Index: /afridex/plugins/fresh-page/RCCWP_Options.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_Options.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_Options.php (revision 21)
@@ -0,0 +1,32 @@
+<?php
+class RCCWP_Options
+{
+	function Delete()
+	{
+		delete_option(RC_CWP_OPTION_KEY);
+	}
+	
+	function Update($hideWritePost, $hideWritePage, $promptEditingPost, $assignToRole, $useSnipshot, $defaultCustomWritePanel)
+	{
+		$options['hide-write-post'] = $hideWritePost;
+		$options['hide-write-page'] = $hideWritePage;
+		$options['prompt-editing-post'] = $promptEditingPost;
+		$options['assign-to-role'] = $assignToRole;
+		$options['use-snipshot'] = $useSnipshot;
+		$options['default-custom-write-panel'] = $defaultCustomWritePanel;
+		
+		$options = serialize($options);
+		update_option(RC_CWP_OPTION_KEY, $options);
+	}
+	
+	function Get($key = null)
+	{
+		$options = unserialize(get_option(RC_CWP_OPTION_KEY));
+		
+		if (!empty($key))
+			return $options[$key];
+		else
+			return $options;
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/phpThumb.config.php
===================================================================
--- /afridex/plugins/fresh-page/phpThumb.config.php (revision 21)
+++ /afridex/plugins/fresh-page/phpThumb.config.php (revision 21)
@@ -0,0 +1,256 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// See: phpthumb.readme.txt for usage instructions          //
+//      NOTE: THIS FILE HAS NO EFFECT IN OBJECT MODE!       //
+//            THIS CONFIG FILE ONLY APPLIES TO phpThumb.php //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+ob_start();
+if (!file_exists(dirname(__FILE__).'/phpthumb.functions.php') || !include_once(dirname(__FILE__).'/phpthumb.functions.php')) {
+	ob_end_flush();
+	die('failed to include_once(phpthumb.functions.php) - realpath="'.realpath(dirname(__FILE__).'/phpthumb.functions.php').'"');
+}
+ob_end_clean();
+
+// START USER CONFIGURATION SECTION:
+
+// * DocumentRoot configuration
+// phpThumb() depends on $_SERVER['DOCUMENT_ROOT'] to resolve path/filenames. This value is usually correct,
+// but has been known to be broken on some servers. This value allows you to override the default value.
+// Do not modify from the auto-detect default value unless you are having problems.
+//$PHPTHUMB_CONFIG['document_root'] = '/home/httpd/httpdocs';
+//$PHPTHUMB_CONFIG['document_root'] = 'c:\\webroot\\example.com\\www';
+//$PHPTHUMB_CONFIG['document_root'] = $_SERVER['DOCUMENT_ROOT'];
+//$PHPTHUMB_CONFIG['document_root'] = realpath((@$_SERVER['DOCUMENT_ROOT'] && file_exists(@$_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF'])) ? $_SERVER['DOCUMENT_ROOT'] : str_replace(dirname(@$_SERVER['PHP_SELF']), '', str_replace(DIRECTORY_SEPARATOR, '/', realpath('.'))));
+$PHPTHUMB_CONFIG['document_root'] = realpath((getenv('DOCUMENT_ROOT') && ereg('^'.preg_quote(realpath(getenv('DOCUMENT_ROOT'))), realpath(__FILE__))) ? getenv('DOCUMENT_ROOT') : str_replace(dirname(@$_SERVER['PHP_SELF']), '', str_replace(DIRECTORY_SEPARATOR, '/', dirname(__FILE__))));
+
+// * Cache directory configuration (choose only one of these - leave the other lines commented-out):
+// Note: this directory must be writable (usually chmod 777 is neccesary) for caching to work.
+// If the directory is not writable no error will be generated but caching will be disabled.
+$PHPTHUMB_CONFIG['cache_directory'] = dirname(__FILE__).'/cache/';                            // set the cache directory relative to the phpThumb() installation
+//$PHPTHUMB_CONFIG['cache_directory'] = $PHPTHUMB_CONFIG['document_root'].'/phpthumb/cache/'; // set the cache directory to an absolute directory for all source images
+//$PHPTHUMB_CONFIG['cache_directory'] = './cache/';                                           // set the cache directory relative to the source image - must start with '.' (will not work to cache URL- or database-sourced images, please use an absolute directory name)
+//$PHPTHUMB_CONFIG['cache_directory'] = null;                                                 // disable thumbnail caching (not recommended)
+//if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
+//	$PHPTHUMB_CONFIG['cache_directory'] = dirname(__FILE__).'/cache/'; // set the cache directory to an absolute directory for all source images
+//} else {
+//	$PHPTHUMB_CONFIG['cache_directory'] = '/tmp/persistent/phpthumb/cache/';
+//}
+
+$PHPTHUMB_CONFIG['cache_disable_warning'] = true; // If [cache_directory] is non-existant or not writable, and [cache_disable_warning] is false, an error image will be generated warning to either set the cache directory or disable the warning (to avoid people not knowing about the cache)
+
+$PHPTHUMB_CONFIG['cache_directory_depth'] = 4; // If this larger than zero, cache structure will be broken into a broad directory structure based on cache filename. For example "cache_src012345..." will be stored in "/0/01/012/0123/cache_src012345..." when (cache_directory_depth = 4)
+
+
+// * Cache culling: phpThumb can automatically limit the contents of the cache directory
+//   based on last-access date and/or number of files and/or total filesize.
+
+//$PHPTHUMB_CONFIG['cache_maxage'] = null;            // never delete cached thumbnails based on last-access time
+$PHPTHUMB_CONFIG['cache_maxage'] = 86400 * 30;        // delete cached thumbnails that haven't been accessed in more than [30 days] (value is maximum time since last access in seconds to avoid deletion)
+
+//$PHPTHUMB_CONFIG['cache_maxsize'] = null;           // never delete cached thumbnails based on byte size of cache directory
+$PHPTHUMB_CONFIG['cache_maxsize'] = 10 * 1024 * 1024; // delete least-recently-accessed cached thumbnails when more than [10MB] of cached files are present (value is maximum bytesize of all cached files)
+
+//$PHPTHUMB_CONFIG['cache_maxfiles'] = null;          // never delete cached thumbnails based on number of cached files
+$PHPTHUMB_CONFIG['cache_maxfiles'] = 200;             // delete least-recently-accessed cached thumbnails when more than [200] cached files are present (value is maximum number of cached files to keep)
+
+
+// * Source image cache configuration
+$PHPTHUMB_CONFIG['cache_source_enabled']   = false;                               // if true, source images obtained via HTTP are cached to $PHPTHUMB_CONFIG['cache_source_directory']
+$PHPTHUMB_CONFIG['cache_source_directory'] = dirname(__FILE__).'/cache/source/';  // set the cache directory for unprocessed source images
+
+// * cache source modification date configuration
+$PHPTHUMB_CONFIG['cache_source_filemtime_ignore_local']  = false; // if true, local source images will not be checked for modification date and cached image will be used if available, even if source image is changed or removed
+$PHPTHUMB_CONFIG['cache_source_filemtime_ignore_remote'] = true;  // if true, remote source images will not be checked for modification date and cached image will be used if available, even if source image is changed or removed. WARNING: cached performance MUCH slower if this is set to false.
+
+
+// * Simplified cache filename configuration
+// Instead of creating unique cache filenames for all parameter combinations, create "simple" cache files (eg: "pic_thumb.jpg")
+// If cache_default_only_suffix is non-empty, GETstring parameters (except 'src') are ignored and only $PHPTHUMB_DEFAULTS
+// parameters (set at the bottom of phpThumb.config.php) are used for processing.
+// The '*' character MUST be used to represent the source image name
+$PHPTHUMB_CONFIG['cache_default_only_suffix'] = '';           // cached in normal phpThumb manner
+//$PHPTHUMB_CONFIG['cache_default_only_suffix'] = '*_thumb';  // cache 'pic.jpg' becomes 'pic_thumb.jpg' (or 'pic_thumb.png' if PNG output is selected, etc)
+//$PHPTHUMB_CONFIG['cache_default_only_suffix'] = 'small-*';  // cache 'pic.jpg' becomes 'small-pic.jpg' (or 'small-pic.png' if PNG output is selected, etc)
+
+$PHPTHUMB_CONFIG['cache_prefix'] = 'phpThumb_cache_'.str_replace('www.', '', @$_SERVER['SERVER_NAME']);
+//$PHPTHUMB_CONFIG['cache_prefix'] = 'phpThumb_cache';                         // allow phpThumb to share 1 set of cached files even if accessed under different servername/domains on same server
+
+$PHPTHUMB_CONFIG['cache_force_passthru'] = true;  // if true, cached image data will always be passed to browser; if false, HTTP redirect will be used instead
+
+
+
+// * Temp directory configuration
+// phpThumb() may need to create temp files. Usually the system temp dir is writable and can be used.
+// Leave this value as NULL in most cases. If you get errors about "failed to open <filename> for writing"
+// you should change this to a full pathname to a directory you do have write access to.
+//$PHPTHUMB_CONFIG['temp_directory'] = null;                               // attempt to auto-detect
+//$PHPTHUMB_CONFIG['temp_directory'] = '/tmp/persistent/phpthumb/cache/';  // set to absolute path
+$PHPTHUMB_CONFIG['temp_directory'] = $PHPTHUMB_CONFIG['cache_directory'];  // set to same as cache directory
+
+
+// NOTE: "max_source_pixels" only affects GD-resized thumbnails. If you have ImageMagick
+//       installed it will bypass most of these limits
+// maximum number of pixels in source image to attempt to process entire image in GD mode.
+// If this is zero then no limit on source image dimensions.
+// If this is nonzero then this is the maximum number of pixels the source image
+// can have to be processed normally, otherwise the embedded EXIF thumbnail will
+// be used (if available) or an "image too large" notice will be displayed.
+// This is to be used for large source images (> 1600x1200) and low PHP memory
+// limits. If PHP runs out of memory the script will usually just die with no output.
+// To calculate this number, multiply the dimensions of the largest image
+// you can process with your memory limitation (e.g. 1600 * 1200 = 1920000)
+// As a general guideline, this number will be about 20% of your PHP memory
+// configuration, so 8M = 1,677,722; 16M = 3,355,443; 32M = 6,710,886; etc.
+if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.2', '>=') && !defined('memory_get_usage') && !@ini_get('memory_limit')) {
+	// memory_get_usage() will only be defined if your PHP is compiled with the --enable-memory-limit configuration option.
+	$PHPTHUMB_CONFIG['max_source_pixels'] = 0;         // no memory limit
+} else {
+	// calculate default max_source_pixels as 1/6 of memory limit configuration
+	$PHPTHUMB_CONFIG['max_source_pixels'] = round(max(intval(ini_get('memory_limit')), intval(get_cfg_var('memory_limit'))) * 1048576 / 6);
+	//$PHPTHUMB_CONFIG['max_source_pixels'] = 0;       // no memory limit
+	//$PHPTHUMB_CONFIG['max_source_pixels'] = 1920000; // allow 1600x1200 images (2Mpx), no larger (about 12MB memory required)
+	//$PHPTHUMB_CONFIG['max_source_pixels'] = 2795000; // 16MB memory limit
+	//$PHPTHUMB_CONFIG['max_source_pixels'] = 3871488; // allow 2272x1704 images (4Mpx), no larger (about 24MB memory required)
+}
+
+
+// ImageMagick configuration
+$PHPTHUMB_CONFIG['prefer_imagemagick']        = true;  // If true, use ImageMagick to resize thumbnails if possible, since it is usually faster than GD functions; if false only use ImageMagick if PHP memory limit is too low.
+$PHPTHUMB_CONFIG['imagemagick_use_thumbnail'] = true;  // If true, use ImageMagick's "-thumbnail" resizing parameter (if available) which removes extra non-image metadata (profiles, EXIF info, etc) resulting in much smaller filesize; if false, use "-resize" paramter which retains this info
+if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
+	// Windows: set absolute pathname
+	$PHPTHUMB_CONFIG['imagemagick_path'] = 'C:/ImageMagick/convert.exe';
+} else {
+	// *nix: set absolute pathname to "convert", or leave as null if "convert" is in the path (location detected with `which`)
+	//$PHPTHUMB_CONFIG['imagemagick_path'] = '/usr/local/bin/convert';
+	$PHPTHUMB_CONFIG['imagemagick_path'] = null;
+}
+
+
+
+// * Default output configuration:
+$PHPTHUMB_CONFIG['output_format']    = 'jpeg'; // default output format ('jpeg', 'png' or 'gif') - thumbnail will be output in this format (if available in your version of GD). This is always overridden by ?f=___ GETstring parameter
+$PHPTHUMB_CONFIG['output_maxwidth']  = 0;      // default maximum thumbnail width.  If this is zero then default width  is the width  of the source image. This is always overridden by ?w=___ GETstring parameter
+$PHPTHUMB_CONFIG['output_maxheight'] = 0;      // default maximum thumbnail height. If this is zero then default height is the height of the source image. This is always overridden by ?h=___ GETstring parameter
+$PHPTHUMB_CONFIG['output_interlace'] = true;   // if true: interlaced output for GIF/PNG, progressive output for JPEG; if false: non-interlaced for GIF/PNG, baseline for JPEG.
+
+// * Error message configuration
+$PHPTHUMB_CONFIG['error_image_width']           = 400;      // default width for error images
+$PHPTHUMB_CONFIG['error_image_height']          = 100;      // default height for error images
+$PHPTHUMB_CONFIG['error_message_image_default'] = '';       // Set this to the name of a generic error image (e.g. '/images/error.png') that you want displayed in place of any error message that may occur. This setting is overridden by the 'err' parameter, which does the same thing.
+$PHPTHUMB_CONFIG['error_bgcolor']               = 'CCCCFF'; // background color of error message images
+$PHPTHUMB_CONFIG['error_textcolor']             = 'FF0000'; // color of text in error messages
+$PHPTHUMB_CONFIG['error_fontsize']              = 1;        // size of text in error messages, from 1 (smallest) to 5 (largest)
+$PHPTHUMB_CONFIG['error_die_on_error']          = true;     // die with error message on any fatal error (recommended with standalone phpThumb.php)
+$PHPTHUMB_CONFIG['error_silent_die_on_error']   = false;    // simply die with no output of any kind on fatal errors (not recommended)
+$PHPTHUMB_CONFIG['error_die_on_source_failure'] = true;     // die with error message if source image cannot be processed by phpThumb() (usually because source image is corrupt in some way). If false the source image will be passed through unprocessed, if true (default) an error message will be displayed.
+
+// * Off-server Thumbnailing Configuration:
+$PHPTHUMB_CONFIG['nohotlink_enabled']           = false;                                    // If false will allow thumbnailing from any source domain
+$PHPTHUMB_CONFIG['nohotlink_valid_domains']     = array(@$_SERVER['HTTP_HOST']);            // This is the list of domains for which thumbnails are allowed to be created. Note: domain only, do not include port numbers. The default value of the current domain should be fine in most cases, but if neccesary you can add more domains in here, in the format "www.example.com"
+$PHPTHUMB_CONFIG['nohotlink_erase_image']       = true;                                     // if true thumbnail is covered up with $PHPTHUMB_CONFIG['nohotlink_fill_color'] before text is applied, if false text is written over top of thumbnail
+$PHPTHUMB_CONFIG['nohotlink_text_message']      = 'Off-server thumbnailing is not allowed'; // text of error message
+
+// * Off-server Linking Configuration:
+$PHPTHUMB_CONFIG['nooffsitelink_enabled']       = false;                                       // If false will allow thumbnails to be linked to from any domain, if true only domains listed below in 'nooffsitelink_valid_domains' will be allowed.
+$PHPTHUMB_CONFIG['nooffsitelink_valid_domains'] = array(@$_SERVER['HTTP_HOST']['www.freshoutmedia.com']);              // This is the list of domains for which thumbnails are allowed to be created. The default value of the current domain should be fine in most cases, but if neccesary you can add more domains in here, in the format 'www.example.com'
+$PHPTHUMB_CONFIG['nooffsitelink_require_refer'] = false;                                      // If false will allow standalone calls to phpThumb(). If true then only requests with a $_SERVER['HTTP_REFERER'] value in 'nooffsitelink_valid_domains' are allowed.
+$PHPTHUMB_CONFIG['nooffsitelink_erase_image']   = false;                                      // if true thumbnail is covered up with $PHPTHUMB_CONFIG['nohotlink_fill_color'] before text is applied, if false text is written over top of thumbnail
+$PHPTHUMB_CONFIG['nooffsitelink_watermark_src'] = '/demo/images/watermark.png';                // webroot-relative image to overlay on hotlinked images
+$PHPTHUMB_CONFIG['nooffsitelink_text_message']  = 'Image taken from '.@$_SERVER['HTTP_HOST']; // text of error message (used if [nooffsitelink_watermark_src] is not a valid image)
+
+
+// * Border & Background default colors
+$PHPTHUMB_CONFIG['border_hexcolor']     = '000000'; // Default border color - usual HTML-style hex color notation (overidden with 'bc' parameter)
+$PHPTHUMB_CONFIG['background_hexcolor'] = 'FFFFFF'; // Default background color when thumbnail aspect ratio does not match fixed-dimension box - usual HTML-style hex color notation (overridden with 'bg' parameter)
+
+// * Watermark configuration
+$PHPTHUMB_CONFIG['ttf_directory'] = dirname(__FILE__).'/fonts'; // Base directory for TTF font files
+//$PHPTHUMB_CONFIG['ttf_directory'] = 'c:/windows/fonts';
+
+
+// * MySQL configuration
+// You may want to pull data from a database rather than a physical file
+// If so, modify the $PHPTHUMB_CONFIG['mysql_query'] line to suit your database structure
+// Note: the data retrieved must be the actual binary data of the image, not a URL or filename
+
+$PHPTHUMB_CONFIG['mysql_query'] = '';
+//$PHPTHUMB_CONFIG['mysql_query'] = 'SELECT `picture` FROM `products` WHERE (`id` = \''.mysql_escape_string(@$_GET['id']).'\')';
+
+// These 4 values must be modified if $PHPTHUMB_CONFIG['mysql_query'] is not empty, but may be ignored if $PHPTHUMB_CONFIG['mysql_query'] is blank.
+$PHPTHUMB_CONFIG['mysql_hostname'] = 'localhost';
+$PHPTHUMB_CONFIG['mysql_username'] = '';
+$PHPTHUMB_CONFIG['mysql_password'] = '';
+$PHPTHUMB_CONFIG['mysql_database'] = '';
+
+
+// * Security configuration
+$PHPTHUMB_CONFIG['high_security_enabled']    = false;  // if enabled, requires 'high_security_password' set to at least 5 characters, and requires the use of phpThumbURL() function (at the bottom of phpThumb.config.php) to generate hashed URLs
+$PHPTHUMB_CONFIG['high_security_password']   = '';     // required if 'high_security_enabled' is true, must be at least 5 characters long
+$PHPTHUMB_CONFIG['disable_debug']            = false;  // prevent phpThumb from displaying any information about your system. If true, phpThumbDebug and error messages will be disabled
+$PHPTHUMB_CONFIG['allow_src_above_docroot']  = false;  // if true, allow src to be anywhere in filesystem; if false (default) only allow src within document_root
+$PHPTHUMB_CONFIG['allow_src_above_phpthumb'] = true;   // if true (default), allow src to be anywhere in filesystem; if false only allow src within sub-directory of phpThumb installation
+$PHPTHUMB_CONFIG['allow_parameter_file']     = false;  // if true, allow use of 'file' parameter; if false (default) the 'file' parameter is disabled/ignored
+$PHPTHUMB_CONFIG['allow_parameter_goto']     = false;  // if true, allow use of 'goto' parameter; if false (default) the 'goto' parameter is disabled/ignored
+
+
+// * HTTP UserAgent configuration
+//$PHPTHUMB_CONFIG['http_user_agent'] = '';                                                                                      // PHP default: none
+//$PHPTHUMB_CONFIG['http_user_agent'] = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)';                                    // Windows XP, Internet Explorer
+$PHPTHUMB_CONFIG['http_user_agent'] = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7'; // Windows XP, Firefox
+
+
+// * Compatability settings
+$PHPTHUMB_CONFIG['disable_pathinfo_parsing']        = false;  // if true, $_SERVER[PATH_INFO] is not parsed. May be needed on some server configurations to allow normal behavior.
+$PHPTHUMB_CONFIG['disable_imagecopyresampled']      = false;  // if true, ImageCopyResampled is replaced with ImageCopyResampleBicubic. May be needed for buggy versions of PHP-GD.
+$PHPTHUMB_CONFIG['disable_onlycreateable_passthru'] = true;   // if true, any image that can be parsed by GetImageSize() can be passed through; if false, only images that can be converted to GD by ImageCreateFrom(JPEG|GIF|PNG) functions are allowed
+
+
+// * HTTP remote file opening settings
+$PHPTHUMB_CONFIG['http_fopen_timeout']              = 10;   // timeout (in seconds) for fopen / curl / fsockopen
+$PHPTHUMB_CONFIG['http_follow_redirect']            = true; // if true (default), follow "302 Found" redirects to new URL; if false, return error message
+
+
+// * Speed optimizations configuration
+$PHPTHUMB_CONFIG['use_exif_thumbnail_for_speed'] = false; // If true, and EXIF thumbnail is available, and is larger or equal to output image dimensions, use EXIF thumbnail rather than actual source image for generating thumbnail. Benefit is only speed, avoiding resizing large image.
+$PHPTHUMB_CONFIG['allow_local_http_src']         = true; // If true, 'src' parameter can be "http://<thishostname>/path/image.ext" instead of just "/path/image.ext"; if false then display warning message to encourage more efficient local-filename calling.
+
+// END USER CONFIGURATION SECTION
+
+///////////////////////////////////////////////////////////////////////////////
+
+// START DEFAULT PARAMETERS SECTION
+// If any parameters are constant across ALL images, you can set them here
+
+$PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE = true;  // if true, any parameters in the URL will override the defaults set here; if false, any parameters set here cannot be overridden in the URL
+$PHPTHUMB_DEFAULTS_DISABLEGETPARAMS  = false; // if true, GETstring parameters will be ignored (except for 'src') and only below default parameters will be used; if false, both default and GETstring parameters will be used (depending on $PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE). Will be auto-set true if !empty($PHPTHUMB_CONFIG['cache_default_only_suffix'])
+
+//$PHPTHUMB_DEFAULTS['w']    = 200;
+//$PHPTHUMB_DEFAULTS['fltr'] = array('blur|10');
+//$PHPTHUMB_DEFAULTS['q']    =  90;
+
+
+// END DEFAULT PARAMETERS SECTION
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Function for generating hashed calls to phpThumb if 'high_security_enabled'
+// example:
+//   require_once($_SERVER['DOCUMENT_ROOT'].'/phpThumb/phpThumb.config.php');
+//   echo '<img src="'.phpThumbURL('src=/images/pic.jpg&w=50').'">';
+
+function phpThumbURL($ParameterString) {
+	global $PHPTHUMB_CONFIG;
+	return str_replace(@$PHPTHUMB_CONFIG['document_root'], '', dirname(__FILE__)).DIRECTORY_SEPARATOR.'phpThumb.php?'.$ParameterString.'&hash='.md5($ParameterString.@$PHPTHUMB_CONFIG['high_security_password']);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+?>
Index: /afridex/plugins/fresh-page/phpthumb.gif.php
===================================================================
--- /afridex/plugins/fresh-page/phpthumb.gif.php (revision 21)
+++ /afridex/plugins/fresh-page/phpthumb.gif.php (revision 21)
@@ -0,0 +1,1168 @@
+<?php
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// GIF Util - (C) 2003 Yamasoft (S/C)
+// http://www.yamasoft.com
+// All Rights Reserved
+// This file can be freely copied, distributed, modified, updated by anyone under the only
+// condition to leave the original address (Yamasoft, http://www.yamasoft.com) and this header.
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// <gif>  = gif_loadFile(filename, [index])
+// <bool> = gif_getSize(<gif> or filename, &width, &height)
+// <bool> = gif_outputAsPng(<gif>, filename, [bgColor])
+// <bool> = gif_outputAsBmp(<gif>, filename, [bgcolor])
+// <bool> = gif_outputAsJpeg(<gif>, filename, [bgcolor]) - use cjpeg if available otherwise uses GD
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Original code by Fabien Ezber
+// Modified by James Heinrich <info@silisoftware.com> for use in phpThumb() - December 10, 2003
+// * Added function gif_loadFileToGDimageResource() - this returns a GD image resource
+// * Modified gif_outputAsJpeg() to check if it's running under Windows, or if cjpeg is not
+//   available, in which case it will attempt to output JPEG using GD functions
+// * added @ error-suppression to two lines where it checks: if ($this->m_img->m_bTrans)
+//   otherwise warnings are generated if error_reporting == E_ALL
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_loadFile($lpszFileName, $iIndex = 0)
+{
+	$gif = new CGIF();
+	if ($gif->loadFile($lpszFileName, $iIndex)) {
+		return $gif;
+	}
+	return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Added by James Heinrich <info@silisoftware.com> - December 10, 2003
+function gif_loadFileToGDimageResource($gifFilename, $bgColor = -1)
+{
+	if ($gif = gif_loadFile($gifFilename)) {
+
+		@set_time_limit(300);
+		// general strategy: convert raw data to PNG then convert PNG data to GD image resource
+		$PNGdata = $gif->getPng($bgColor);
+		if ($img = @ImageCreateFromString($PNGdata)) {
+
+			// excellent - PNG image data successfully converted to GD image
+			return $img;
+
+		} elseif ($img = $gif->getGD_PixelPlotterVersion()) {
+
+			// problem: ImageCreateFromString() didn't like the PNG image data.
+			//   This has been known to happen in PHP v4.0.6
+			// solution: take the raw image data and create a new GD image and plot
+			//   pixel-by-pixel on the GD image. This is extremely slow, but it does
+			//   work and a slow solution is better than no solution, right? :)
+			return $img;
+
+		}
+	}
+	return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_outputAsBmp($gif, $lpszFileName, $bgColor = -1)
+{
+	if (!isSet($gif) || (@get_class($gif) <> 'cgif') || !$gif->loaded() || ($lpszFileName == '')) {
+		return false;
+	}
+
+	$fd = $gif->getBmp($bgColor);
+	if (strlen($fd) <= 0) {
+		return false;
+	}
+
+	if (!($fh = @fopen($lpszFileName, 'wb'))) {
+		return false;
+	}
+	@fwrite($fh, $fd, strlen($fd));
+	@fflush($fh);
+	@fclose($fh);
+	return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_outputAsPng($gif, $lpszFileName, $bgColor = -1)
+{
+	if (!isSet($gif) || (@get_class($gif) <> 'cgif') || !$gif->loaded() || ($lpszFileName == '')) {
+		return false;
+	}
+
+	$fd = $gif->getPng($bgColor);
+	if (strlen($fd) <= 0) {
+		return false;
+	}
+
+	if (!($fh = @fopen($lpszFileName, 'wb'))) {
+		return false;
+	}
+	@fwrite($fh, $fd, strlen($fd));
+	@fflush($fh);
+	@fclose($fh);
+	return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_outputAsJpeg($gif, $lpszFileName, $bgColor = -1)
+{
+	// JPEG output that does not require cjpeg added by James Heinrich <info@silisoftware.com> - December 10, 2003
+	if ((strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') && (file_exists('/usr/local/bin/cjpeg') || `which cjpeg`)) {
+
+		if (gif_outputAsBmp($gif, $lpszFileName.'.bmp', $bgColor)) {
+			exec('cjpeg '.$lpszFileName.'.bmp >'.$lpszFileName.' 2>/dev/null');
+			@unLink($lpszFileName.'.bmp');
+
+			if (@file_exists($lpszFileName)) {
+				if (@fileSize($lpszFileName) > 0) {
+					return true;
+				}
+
+				@unLink($lpszFileName);
+			}
+		}
+
+	} else {
+
+		// either Windows, or cjpeg not found in path
+		if ($img = @ImageCreateFromString($gif->getPng($bgColor))) {
+			if (@ImageJPEG($img, $lpszFileName)) {
+				return true;
+			}
+		}
+
+	}
+
+	return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_getSize($gif, &$width, &$height)
+{
+	if (isSet($gif) && (@get_class($gif) == 'cgif') && $gif->loaded()) {
+		$width  = $gif->width();
+		$height = $gif->height();
+	} elseif (@file_exists($gif)) {
+		$myGIF = new CGIF();
+		if (!$myGIF->getSize($gif, $width, $height)) {
+			return false;
+		}
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFLZW
+{
+	var $MAX_LZW_BITS;
+	var $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode;
+	var $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIFLZW()
+	{
+		$this->MAX_LZW_BITS = 12;
+		unSet($this->Next);
+		unSet($this->Vals);
+		unSet($this->Stack);
+		unSet($this->Buf);
+
+		$this->Next  = range(0, (1 << $this->MAX_LZW_BITS)       - 1);
+		$this->Vals  = range(0, (1 << $this->MAX_LZW_BITS)       - 1);
+		$this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
+		$this->Buf   = range(0, 279);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function deCompress($data, &$datLen)
+	{
+		$stLen  = strlen($data);
+		$datLen = 0;
+		$ret    = '';
+
+		// INITIALIZATION
+		$this->LZWCommand($data, true);
+
+		while (($iIndex = $this->LZWCommand($data, false)) >= 0) {
+			$ret .= chr($iIndex);
+		}
+
+		$datLen = $stLen - strlen($data);
+
+		if ($iIndex != -2) {
+			return false;
+		}
+
+		return $ret;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function LZWCommand(&$data, $bInit)
+	{
+		if ($bInit) {
+			$this->SetCodeSize = ord($data{0});
+			$data = substr($data, 1);
+
+			$this->CodeSize    = $this->SetCodeSize + 1;
+			$this->ClearCode   = 1 << $this->SetCodeSize;
+			$this->EndCode     = $this->ClearCode + 1;
+			$this->MaxCode     = $this->ClearCode + 2;
+			$this->MaxCodeSize = $this->ClearCode << 1;
+
+			$this->GetCode($data, $bInit);
+
+			$this->Fresh = 1;
+			for ($i = 0; $i < $this->ClearCode; $i++) {
+				$this->Next[$i] = 0;
+				$this->Vals[$i] = $i;
+			}
+
+			for (; $i < (1 << $this->MAX_LZW_BITS); $i++) {
+				$this->Next[$i] = 0;
+				$this->Vals[$i] = 0;
+			}
+
+			$this->sp = 0;
+			return 1;
+		}
+
+		if ($this->Fresh) {
+			$this->Fresh = 0;
+			do {
+				$this->FirstCode = $this->GetCode($data, $bInit);
+				$this->OldCode   = $this->FirstCode;
+			}
+			while ($this->FirstCode == $this->ClearCode);
+
+			return $this->FirstCode;
+		}
+
+		if ($this->sp > 0) {
+			$this->sp--;
+			return $this->Stack[$this->sp];
+		}
+
+		while (($Code = $this->GetCode($data, $bInit)) >= 0) {
+			if ($Code == $this->ClearCode) {
+				for ($i = 0; $i < $this->ClearCode; $i++) {
+					$this->Next[$i] = 0;
+					$this->Vals[$i] = $i;
+				}
+
+				for (; $i < (1 << $this->MAX_LZW_BITS); $i++) {
+					$this->Next[$i] = 0;
+					$this->Vals[$i] = 0;
+				}
+
+				$this->CodeSize    = $this->SetCodeSize + 1;
+				$this->MaxCodeSize = $this->ClearCode << 1;
+				$this->MaxCode     = $this->ClearCode + 2;
+				$this->sp          = 0;
+				$this->FirstCode   = $this->GetCode($data, $bInit);
+				$this->OldCode     = $this->FirstCode;
+
+				return $this->FirstCode;
+			}
+
+			if ($Code == $this->EndCode) {
+				return -2;
+			}
+
+			$InCode = $Code;
+			if ($Code >= $this->MaxCode) {
+				$this->Stack[$this->sp] = $this->FirstCode;
+				$this->sp++;
+				$Code = $this->OldCode;
+			}
+
+			while ($Code >= $this->ClearCode) {
+				$this->Stack[$this->sp] = $this->Vals[$Code];
+				$this->sp++;
+
+				if ($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
+					return -1;
+
+				$Code = $this->Next[$Code];
+			}
+
+			$this->FirstCode = $this->Vals[$Code];
+			$this->Stack[$this->sp] = $this->FirstCode;
+			$this->sp++;
+
+			if (($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) {
+				$this->Next[$Code] = $this->OldCode;
+				$this->Vals[$Code] = $this->FirstCode;
+				$this->MaxCode++;
+
+				if (($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) {
+					$this->MaxCodeSize *= 2;
+					$this->CodeSize++;
+				}
+			}
+
+			$this->OldCode = $InCode;
+			if ($this->sp > 0) {
+				$this->sp--;
+				return $this->Stack[$this->sp];
+			}
+		}
+
+		return $Code;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function GetCode(&$data, $bInit)
+	{
+		if ($bInit) {
+			$this->CurBit   = 0;
+			$this->LastBit  = 0;
+			$this->Done     = 0;
+			$this->LastByte = 2;
+			return 1;
+		}
+
+		if (($this->CurBit + $this->CodeSize) >= $this->LastBit) {
+			if ($this->Done) {
+				if ($this->CurBit >= $this->LastBit) {
+					// Ran off the end of my bits
+					return 0;
+				}
+				return -1;
+			}
+
+			$this->Buf[0] = $this->Buf[$this->LastByte - 2];
+			$this->Buf[1] = $this->Buf[$this->LastByte - 1];
+
+			$Count = ord($data{0});
+			$data  = substr($data, 1);
+
+			if ($Count) {
+				for ($i = 0; $i < $Count; $i++) {
+					$this->Buf[2 + $i] = ord($data{$i});
+				}
+				$data = substr($data, $Count);
+			} else {
+				$this->Done = 1;
+			}
+
+			$this->LastByte = 2 + $Count;
+			$this->CurBit   = ($this->CurBit - $this->LastBit) + 16;
+			$this->LastBit  = (2 + $Count) << 3;
+		}
+
+		$iRet = 0;
+		for ($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) {
+			$iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j;
+		}
+
+		$this->CurBit += $this->CodeSize;
+		return $iRet;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFCOLORTABLE
+{
+	var $m_nColors;
+	var $m_arColors;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIFCOLORTABLE()
+	{
+		unSet($this->m_nColors);
+		unSet($this->m_arColors);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function load($lpData, $num)
+	{
+		$this->m_nColors  = 0;
+		$this->m_arColors = array();
+
+		for ($i = 0; $i < $num; $i++) {
+			$rgb = substr($lpData, $i * 3, 3);
+			if (strlen($rgb) < 3) {
+				return false;
+			}
+
+			$this->m_arColors[] = (ord($rgb{2}) << 16) + (ord($rgb{1}) << 8) + ord($rgb{0});
+			$this->m_nColors++;
+		}
+
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function toString()
+	{
+		$ret = '';
+
+		for ($i = 0; $i < $this->m_nColors; $i++) {
+			$ret .=
+				chr(($this->m_arColors[$i] & 0x000000FF))       . // R
+				chr(($this->m_arColors[$i] & 0x0000FF00) >>  8) . // G
+				chr(($this->m_arColors[$i] & 0x00FF0000) >> 16);  // B
+		}
+
+		return $ret;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function toRGBQuad()
+	{
+		$ret = '';
+
+		for ($i = 0; $i < $this->m_nColors; $i++) {
+			$ret .=
+				chr(($this->m_arColors[$i] & 0x00FF0000) >> 16) . // B
+				chr(($this->m_arColors[$i] & 0x0000FF00) >>  8) . // G
+				chr(($this->m_arColors[$i] & 0x000000FF))       . // R
+				"\x00";
+		}
+
+		return $ret;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function colorIndex($rgb)
+	{
+		$rgb  = intval($rgb) & 0xFFFFFF;
+		$r1   = ($rgb & 0x0000FF);
+		$g1   = ($rgb & 0x00FF00) >>  8;
+		$b1   = ($rgb & 0xFF0000) >> 16;
+		$idx  = -1;
+
+		for ($i = 0; $i < $this->m_nColors; $i++) {
+			$r2 = ($this->m_arColors[$i] & 0x000000FF);
+			$g2 = ($this->m_arColors[$i] & 0x0000FF00) >>  8;
+			$b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
+			$d  = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
+
+			if (($idx == -1) || ($d < $dif)) {
+				$idx = $i;
+				$dif = $d;
+			}
+		}
+
+		return $idx;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFFILEHEADER
+{
+	var $m_lpVer;
+	var $m_nWidth;
+	var $m_nHeight;
+	var $m_bGlobalClr;
+	var $m_nColorRes;
+	var $m_bSorted;
+	var $m_nTableSize;
+	var $m_nBgColor;
+	var $m_nPixelRatio;
+	var $m_colorTable;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIFFILEHEADER()
+	{
+		unSet($this->m_lpVer);
+		unSet($this->m_nWidth);
+		unSet($this->m_nHeight);
+		unSet($this->m_bGlobalClr);
+		unSet($this->m_nColorRes);
+		unSet($this->m_bSorted);
+		unSet($this->m_nTableSize);
+		unSet($this->m_nBgColor);
+		unSet($this->m_nPixelRatio);
+		unSet($this->m_colorTable);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function load($lpData, &$hdrLen)
+	{
+		$hdrLen = 0;
+
+		$this->m_lpVer = substr($lpData, 0, 6);
+		if (($this->m_lpVer <> 'GIF87a') && ($this->m_lpVer <> 'GIF89a')) {
+			return false;
+		}
+
+		$this->m_nWidth  = $this->w2i(substr($lpData, 6, 2));
+		$this->m_nHeight = $this->w2i(substr($lpData, 8, 2));
+		if (!$this->m_nWidth || !$this->m_nHeight) {
+			return false;
+		}
+
+		$b = ord(substr($lpData, 10, 1));
+		$this->m_bGlobalClr  = ($b & 0x80) ? true : false;
+		$this->m_nColorRes   = ($b & 0x70) >> 4;
+		$this->m_bSorted     = ($b & 0x08) ? true : false;
+		$this->m_nTableSize  = 2 << ($b & 0x07);
+		$this->m_nBgColor    = ord(substr($lpData, 11, 1));
+		$this->m_nPixelRatio = ord(substr($lpData, 12, 1));
+		$hdrLen = 13;
+
+		if ($this->m_bGlobalClr) {
+			$this->m_colorTable = new CGIFCOLORTABLE();
+			if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
+				return false;
+			}
+			$hdrLen += 3 * $this->m_nTableSize;
+		}
+
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function w2i($str)
+	{
+		return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFIMAGEHEADER
+{
+	var $m_nLeft;
+	var $m_nTop;
+	var $m_nWidth;
+	var $m_nHeight;
+	var $m_bLocalClr;
+	var $m_bInterlace;
+	var $m_bSorted;
+	var $m_nTableSize;
+	var $m_colorTable;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIFIMAGEHEADER()
+	{
+		unSet($this->m_nLeft);
+		unSet($this->m_nTop);
+		unSet($this->m_nWidth);
+		unSet($this->m_nHeight);
+		unSet($this->m_bLocalClr);
+		unSet($this->m_bInterlace);
+		unSet($this->m_bSorted);
+		unSet($this->m_nTableSize);
+		unSet($this->m_colorTable);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function load($lpData, &$hdrLen)
+	{
+		$hdrLen = 0;
+
+		$this->m_nLeft   = $this->w2i(substr($lpData, 0, 2));
+		$this->m_nTop    = $this->w2i(substr($lpData, 2, 2));
+		$this->m_nWidth  = $this->w2i(substr($lpData, 4, 2));
+		$this->m_nHeight = $this->w2i(substr($lpData, 6, 2));
+
+		if (!$this->m_nWidth || !$this->m_nHeight) {
+			return false;
+		}
+
+		$b = ord($lpData{8});
+		$this->m_bLocalClr  = ($b & 0x80) ? true : false;
+		$this->m_bInterlace = ($b & 0x40) ? true : false;
+		$this->m_bSorted    = ($b & 0x20) ? true : false;
+		$this->m_nTableSize = 2 << ($b & 0x07);
+		$hdrLen = 9;
+
+		if ($this->m_bLocalClr) {
+			$this->m_colorTable = new CGIFCOLORTABLE();
+			if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
+				return false;
+			}
+			$hdrLen += 3 * $this->m_nTableSize;
+		}
+
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function w2i($str)
+	{
+		return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFIMAGE
+{
+	var $m_disp;
+	var $m_bUser;
+	var $m_bTrans;
+	var $m_nDelay;
+	var $m_nTrans;
+	var $m_lpComm;
+	var $m_gih;
+	var $m_data;
+	var $m_lzw;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function CGIFIMAGE()
+	{
+		unSet($this->m_disp);
+		unSet($this->m_bUser);
+		unSet($this->m_bTrans);
+		unSet($this->m_nDelay);
+		unSet($this->m_nTrans);
+		unSet($this->m_lpComm);
+		unSet($this->m_data);
+		$this->m_gih = new CGIFIMAGEHEADER();
+		$this->m_lzw = new CGIFLZW();
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function load($data, &$datLen)
+	{
+		$datLen = 0;
+
+		while (true) {
+			$b = ord($data{0});
+			$data = substr($data, 1);
+			$datLen++;
+
+			switch($b) {
+			case 0x21: // Extension
+				if (!$this->skipExt($data, $len = 0)) {
+					return false;
+				}
+				$datLen += $len;
+				break;
+
+			case 0x2C: // Image
+				// LOAD HEADER & COLOR TABLE
+				if (!$this->m_gih->load($data, $len = 0)) {
+					return false;
+				}
+				$data = substr($data, $len);
+				$datLen += $len;
+
+				// ALLOC BUFFER
+				if (!($this->m_data = $this->m_lzw->deCompress($data, $len = 0))) {
+					return false;
+				}
+				$data = substr($data, $len);
+				$datLen += $len;
+
+				if ($this->m_gih->m_bInterlace) {
+					$this->deInterlace();
+				}
+				return true;
+
+			case 0x3B: // EOF
+			default:
+				return false;
+			}
+		}
+		return false;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function skipExt(&$data, &$extLen)
+	{
+		$extLen = 0;
+
+		$b = ord($data{0});
+		$data = substr($data, 1);
+		$extLen++;
+
+		switch($b) {
+		case 0xF9: // Graphic Control
+			$b = ord($data{1});
+			$this->m_disp   = ($b & 0x1C) >> 2;
+			$this->m_bUser  = ($b & 0x02) ? true : false;
+			$this->m_bTrans = ($b & 0x01) ? true : false;
+			$this->m_nDelay = $this->w2i(substr($data, 2, 2));
+			$this->m_nTrans = ord($data{4});
+			break;
+
+		case 0xFE: // Comment
+			$this->m_lpComm = substr($data, 1, ord($data{0}));
+			break;
+
+		case 0x01: // Plain text
+			break;
+
+		case 0xFF: // Application
+			break;
+		}
+
+		// SKIP DEFAULT AS DEFS MAY CHANGE
+		$b = ord($data{0});
+		$data = substr($data, 1);
+		$extLen++;
+		while ($b > 0) {
+			$data = substr($data, $b);
+			$extLen += $b;
+			$b    = ord($data{0});
+			$data = substr($data, 1);
+			$extLen++;
+		}
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function w2i($str)
+	{
+		return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function deInterlace()
+	{
+		$data = $this->m_data;
+
+		for ($i = 0; $i < 4; $i++) {
+			switch($i) {
+			case 0:
+				$s = 8;
+				$y = 0;
+				break;
+
+			case 1:
+				$s = 8;
+				$y = 4;
+				break;
+
+			case 2:
+				$s = 4;
+				$y = 2;
+				break;
+
+			case 3:
+				$s = 2;
+				$y = 1;
+				break;
+			}
+
+			for (; $y < $this->m_gih->m_nHeight; $y += $s) {
+				$lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
+				$this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
+
+				$data =
+					substr($data, 0, $y * $this->m_gih->m_nWidth) .
+					$lne .
+					substr($data, ($y + 1) * $this->m_gih->m_nWidth);
+			}
+		}
+
+		$this->m_data = $data;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIF
+{
+	var $m_gfh;
+	var $m_lpData;
+	var $m_img;
+	var $m_bLoaded;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIF()
+	{
+		$this->m_gfh     = new CGIFFILEHEADER();
+		$this->m_img     = new CGIFIMAGE();
+		$this->m_lpData  = '';
+		$this->m_bLoaded = false;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function loadFile($lpszFileName, $iIndex)
+	{
+		if ($iIndex < 0) {
+			return false;
+		}
+
+		// READ FILE
+		if (!($fh = @fopen($lpszFileName, 'rb'))) {
+			return false;
+		}
+		$this->m_lpData = @fRead($fh, @fileSize($lpszFileName));
+		fclose($fh);
+
+		// GET FILE HEADER
+		if (!$this->m_gfh->load($this->m_lpData, $len = 0)) {
+			return false;
+		}
+		$this->m_lpData = substr($this->m_lpData, $len);
+
+		do {
+			if (!$this->m_img->load($this->m_lpData, $imgLen = 0)) {
+				return false;
+			}
+			$this->m_lpData = substr($this->m_lpData, $imgLen);
+		}
+		while ($iIndex-- > 0);
+
+		$this->m_bLoaded = true;
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function getSize($lpszFileName, &$width, &$height)
+	{
+		if (!($fh = @fopen($lpszFileName, 'rb'))) {
+			return false;
+		}
+		$data = @fRead($fh, @fileSize($lpszFileName));
+		@fclose($fh);
+
+		$gfh = new CGIFFILEHEADER();
+		if (!$gfh->load($data, $len = 0)) {
+			return false;
+		}
+
+		$width  = $gfh->m_nWidth;
+		$height = $gfh->m_nHeight;
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function getBmp($bgColor)
+	{
+		$out = '';
+
+		if (!$this->m_bLoaded) {
+			return false;
+		}
+
+		// PREPARE COLOR TABLE (RGBQUADs)
+		if ($this->m_img->m_gih->m_bLocalClr) {
+			$nColors = $this->m_img->m_gih->m_nTableSize;
+			$rgbq    = $this->m_img->m_gih->m_colorTable->toRGBQuad();
+			if ($bgColor != -1) {
+				$bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
+			}
+		} elseif ($this->m_gfh->m_bGlobalClr) {
+			$nColors = $this->m_gfh->m_nTableSize;
+			$rgbq    = $this->m_gfh->m_colorTable->toRGBQuad();
+			if ($bgColor != -1) {
+				$bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
+			}
+		} else {
+			$nColors =  0;
+			$bgColor = -1;
+		}
+
+		// PREPARE BITMAP BITS
+		$data = $this->m_img->m_data;
+		$nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth;
+		$bmp  = '';
+		$nPad = ($this->m_gfh->m_nWidth % 4) ? 4 - ($this->m_gfh->m_nWidth % 4) : 0;
+		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
+			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
+				if (
+					($x >= $this->m_img->m_gih->m_nLeft) &&
+					($y >= $this->m_img->m_gih->m_nTop) &&
+					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
+					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
+					// PART OF IMAGE
+					if (@$this->m_img->m_bTrans && (ord($data{$nPxl}) == $this->m_img->m_nTrans)) {
+						// TRANSPARENT -> BACKGROUND
+						if ($bgColor == -1) {
+							$bmp .= chr($this->m_gfh->m_nBgColor);
+						} else {
+							$bmp .= chr($bgColor);
+						}
+					} else {
+						$bmp .= $data{$nPxl};
+					}
+				} else {
+					// BACKGROUND
+					if ($bgColor == -1) {
+						$bmp .= chr($this->m_gfh->m_nBgColor);
+					} else {
+						$bmp .= chr($bgColor);
+					}
+				}
+			}
+			$nPxl -= $this->m_gfh->m_nWidth << 1;
+
+			// ADD PADDING
+			for ($x = 0; $x < $nPad; $x++) {
+				$bmp .= "\x00";
+			}
+		}
+
+		// BITMAPFILEHEADER
+		$out .= 'BM';
+		$out .= $this->dword(14 + 40 + ($nColors << 2) + strlen($bmp));
+		$out .= "\x00\x00";
+		$out .= "\x00\x00";
+		$out .= $this->dword(14 + 40 + ($nColors << 2));
+
+		// BITMAPINFOHEADER
+		$out .= $this->dword(40);
+		$out .= $this->dword($this->m_gfh->m_nWidth);
+		$out .= $this->dword($this->m_gfh->m_nHeight);
+		$out .= "\x01\x00";
+		$out .= "\x08\x00";
+		$out .= "\x00\x00\x00\x00";
+		$out .= "\x00\x00\x00\x00";
+		$out .= "\x12\x0B\x00\x00";
+		$out .= "\x12\x0B\x00\x00";
+		$out .= $this->dword($nColors % 256);
+		$out .= "\x00\x00\x00\x00";
+
+		// COLOR TABLE
+		if ($nColors > 0) {
+			$out .= $rgbq;
+		}
+
+		// DATA
+		$out .= $bmp;
+
+		return $out;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function getPng($bgColor)
+	{
+		$out = '';
+
+		if (!$this->m_bLoaded) {
+			return false;
+		}
+
+		// PREPARE COLOR TABLE (RGBQUADs)
+		if ($this->m_img->m_gih->m_bLocalClr) {
+			$nColors = $this->m_img->m_gih->m_nTableSize;
+			$pal     = $this->m_img->m_gih->m_colorTable->toString();
+			if ($bgColor != -1) {
+				$bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
+			}
+		} elseif ($this->m_gfh->m_bGlobalClr) {
+			$nColors = $this->m_gfh->m_nTableSize;
+			$pal     = $this->m_gfh->m_colorTable->toString();
+			if ($bgColor != -1) {
+				$bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
+			}
+		} else {
+			$nColors =  0;
+			$bgColor = -1;
+		}
+
+		// PREPARE BITMAP BITS
+		$data = $this->m_img->m_data;
+		$nPxl = 0;
+		$bmp  = '';
+		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
+			$bmp .= "\x00";
+			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
+				if (
+					($x >= $this->m_img->m_gih->m_nLeft) &&
+					($y >= $this->m_img->m_gih->m_nTop) &&
+					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
+					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
+					// PART OF IMAGE
+					$bmp .= $data{$nPxl};
+				} else {
+					// BACKGROUND
+					if ($bgColor == -1) {
+						$bmp .= chr($this->m_gfh->m_nBgColor);
+					} else {
+						$bmp .= chr($bgColor);
+					}
+				}
+			}
+		}
+		$bmp = gzcompress($bmp, 9);
+
+		///////////////////////////////////////////////////////////////////////
+		// SIGNATURE
+		$out .= "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
+		///////////////////////////////////////////////////////////////////////
+		// HEADER
+		$out .= "\x00\x00\x00\x0D";
+		$tmp  = 'IHDR';
+		$tmp .= $this->ndword($this->m_gfh->m_nWidth);
+		$tmp .= $this->ndword($this->m_gfh->m_nHeight);
+		$tmp .= "\x08\x03\x00\x00\x00";
+		$out .= $tmp;
+		$out .= $this->ndword(crc32($tmp));
+		///////////////////////////////////////////////////////////////////////
+		// PALETTE
+		if ($nColors > 0) {
+			$out .= $this->ndword($nColors * 3);
+			$tmp  = 'PLTE';
+			$tmp .= $pal;
+			$out .= $tmp;
+			$out .= $this->ndword(crc32($tmp));
+		}
+		///////////////////////////////////////////////////////////////////////
+		// TRANSPARENCY
+		if (@$this->m_img->m_bTrans && ($nColors > 0)) {
+			$out .= $this->ndword($nColors);
+			$tmp  = 'tRNS';
+			for ($i = 0; $i < $nColors; $i++) {
+				$tmp .= ($i == $this->m_img->m_nTrans) ? "\x00" : "\xFF";
+			}
+			$out .= $tmp;
+			$out .= $this->ndword(crc32($tmp));
+		}
+		///////////////////////////////////////////////////////////////////////
+		// DATA BITS
+		$out .= $this->ndword(strlen($bmp));
+		$tmp  = 'IDAT';
+		$tmp .= $bmp;
+		$out .= $tmp;
+		$out .= $this->ndword(crc32($tmp));
+		///////////////////////////////////////////////////////////////////////
+		// END OF FILE
+		$out .= "\x00\x00\x00\x00IEND\xAE\x42\x60\x82";
+
+		return $out;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// Added by James Heinrich <info@silisoftware.com> - January 5, 2003
+
+	// Takes raw image data and plots it pixel-by-pixel on a new GD image and returns that
+	// It's extremely slow, but the only solution when ImageCreateFromString() fails
+	function getGD_PixelPlotterVersion()
+	{
+		if (!$this->m_bLoaded) {
+			return false;
+		}
+
+		// PREPARE COLOR TABLE (RGBQUADs)
+		if ($this->m_img->m_gih->m_bLocalClr) {
+			$pal = $this->m_img->m_gih->m_colorTable->toString();
+		} elseif ($this->m_gfh->m_bGlobalClr) {
+			$pal = $this->m_gfh->m_colorTable->toString();
+		} else {
+			die('No color table available in getGD_PixelPlotterVersion()');
+		}
+
+		$PlottingIMG = ImageCreate($this->m_gfh->m_nWidth, $this->m_gfh->m_nHeight);
+		$NumColorsInPal = floor(strlen($pal) / 3);
+		for ($i = 0; $i < $NumColorsInPal; $i++) {
+			$ThisImageColor[$i] = ImageColorAllocate(
+									$PlottingIMG,
+									ord($pal{(($i * 3) + 0)}),
+									ord($pal{(($i * 3) + 1)}),
+									ord($pal{(($i * 3) + 2)}));
+		}
+
+		// PREPARE BITMAP BITS
+		$data = $this->m_img->m_data;
+		$nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth;
+		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
+			set_time_limit(30);
+			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
+				if (
+					($x >= $this->m_img->m_gih->m_nLeft) &&
+					($y >= $this->m_img->m_gih->m_nTop) &&
+					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
+					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
+					// PART OF IMAGE
+					if (@$this->m_img->m_bTrans && (ord($data{$nPxl}) == $this->m_img->m_nTrans)) {
+						ImageSetPixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[$this->m_gfh->m_nBgColor]);
+					} else {
+						ImageSetPixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[ord($data{$nPxl})]);
+					}
+				} else {
+					// BACKGROUND
+					ImageSetPixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[$this->m_gfh->m_nBgColor]);
+				}
+			}
+			$nPxl -= $this->m_gfh->m_nWidth << 1;
+
+		}
+
+		return $PlottingIMG;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function dword($val)
+	{
+		$val = intval($val);
+		return chr($val & 0xFF).chr(($val & 0xFF00) >> 8).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF000000) >> 24);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function ndword($val)
+	{
+		$val = intval($val);
+		return chr(($val & 0xFF000000) >> 24).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF00) >> 8).chr($val & 0xFF);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function width()
+	{
+		return $this->m_gfh->m_nWidth;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function height()
+	{
+		return $this->m_gfh->m_nHeight;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function comment()
+	{
+		return $this->m_img->m_lpComm;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function loaded()
+	{
+		return $this->m_bLoaded;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+?>
Index: /afridex/plugins/fresh-page/phpthumb.filters.php
===================================================================
--- /afridex/plugins/fresh-page/phpthumb.filters.php (revision 21)
+++ /afridex/plugins/fresh-page/phpthumb.filters.php (revision 21)
@@ -0,0 +1,1377 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// phpthumb.filters.php - image processing filter functions //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+class phpthumb_filters {
+
+	var $phpThumbObject = null;
+
+	function phpthumb_filters() {
+		return true;
+	}
+
+	function ApplyMask(&$gdimg_mask, &$gdimg_image) {
+		if (phpthumb_functions::gd_version() < 2) {
+			$this->DebugMessage('Skipping ApplyMask() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+			return false;
+		}
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.2', '>=')) {
+
+			$this->DebugMessage('Using alpha ApplyMask() technique', __FILE__, __LINE__);
+			if ($gdimg_mask_resized = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg_image), ImageSY($gdimg_image))) {
+
+				ImageCopyResampled($gdimg_mask_resized, $gdimg_mask, 0, 0, 0, 0, ImageSX($gdimg_image), ImageSY($gdimg_image), ImageSX($gdimg_mask), ImageSY($gdimg_mask));
+				if ($gdimg_mask_blendtemp = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg_image), ImageSY($gdimg_image))) {
+
+					$color_background = ImageColorAllocate($gdimg_mask_blendtemp, 0, 0, 0);
+					ImageFilledRectangle($gdimg_mask_blendtemp, 0, 0, ImageSX($gdimg_mask_blendtemp), ImageSY($gdimg_mask_blendtemp), $color_background);
+					ImageAlphaBlending($gdimg_mask_blendtemp, false);
+					ImageSaveAlpha($gdimg_mask_blendtemp, true);
+					for ($x = 0; $x < ImageSX($gdimg_image); $x++) {
+						for ($y = 0; $y < ImageSY($gdimg_image); $y++) {
+							//$RealPixel = phpthumb_functions::GetPixelColor($gdimg_mask_blendtemp, $x, $y);
+							$RealPixel = phpthumb_functions::GetPixelColor($gdimg_image, $x, $y);
+							$MaskPixel = phpthumb_functions::GrayscalePixel(phpthumb_functions::GetPixelColor($gdimg_mask_resized, $x, $y));
+							$MaskAlpha = 127 - (floor($MaskPixel['red'] / 2) * (1 - ($RealPixel['alpha'] / 127)));
+							$newcolor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_mask_blendtemp, $RealPixel['red'], $RealPixel['green'], $RealPixel['blue'], $MaskAlpha);
+							ImageSetPixel($gdimg_mask_blendtemp, $x, $y, $newcolor);
+						}
+					}
+					ImageAlphaBlending($gdimg_image, false);
+					ImageSaveAlpha($gdimg_image, true);
+					ImageCopy($gdimg_image, $gdimg_mask_blendtemp, 0, 0, 0, 0, ImageSX($gdimg_mask_blendtemp), ImageSY($gdimg_mask_blendtemp));
+					ImageDestroy($gdimg_mask_blendtemp);
+
+				} else {
+					$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
+				}
+				ImageDestroy($gdimg_mask_resized);
+
+			} else {
+				$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
+			}
+
+		} else {
+			// alpha merging requires PHP v4.3.2+
+			$this->DebugMessage('Skipping ApplyMask() technique because PHP is v"'.phpversion().'"', __FILE__, __LINE__);
+		}
+		return true;
+	}
+
+
+	function Bevel(&$gdimg, $width, $hexcolor1, $hexcolor2) {
+		$width     = ($width     ? $width     : 5);
+		$hexcolor1 = ($hexcolor1 ? $hexcolor1 : 'FFFFFF');
+		$hexcolor2 = ($hexcolor2 ? $hexcolor2 : '000000');
+
+		ImageAlphaBlending($gdimg, true);
+		for ($i = 0; $i < $width; $i++) {
+			$alpha = round(($i / $width) * 127);
+			$color1[$i] = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor1, false, $alpha);
+			$color2[$i] = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor2, false, $alpha);
+
+			ImageLine($gdimg,                   $i,                   $i,                   $i, ImageSY($gdimg) - $i, $color1[$i]); // left
+			ImageLine($gdimg,                   $i,                   $i, ImageSX($gdimg) - $i,                   $i, $color1[$i]); // top
+			ImageLine($gdimg, ImageSX($gdimg) - $i, ImageSY($gdimg) - $i, ImageSX($gdimg) - $i,                   $i, $color2[$i]); // right
+			ImageLine($gdimg, ImageSX($gdimg) - $i, ImageSY($gdimg) - $i,                   $i, ImageSY($gdimg) - $i, $color2[$i]); // bottom
+		}
+		return true;
+	}
+
+
+	function Blur(&$gdimg, $radius=0.5) {
+		// Taken from Torstein Hønsi's phpUnsharpMask (see phpthumb.unsharp.php)
+
+		$radius = round(max(0, min($radius, 50)) * 2);
+		if (!$radius) {
+			return false;
+		}
+
+		$w = ImageSX($gdimg);
+		$h = ImageSY($gdimg);
+		if ($imgBlur = ImageCreateTrueColor($w, $h)) {
+			// Gaussian blur matrix:
+			//	1	2	1
+			//	2	4	2
+			//	1	2	1
+
+			// Move copies of the image around one pixel at the time and merge them with weight
+			// according to the matrix. The same matrix is simply repeated for higher radii.
+			for ($i = 0; $i < $radius; $i++)	{
+				ImageCopy     ($imgBlur, $gdimg, 0, 0, 1, 1, $w - 1, $h - 1);            // up left
+				ImageCopyMerge($imgBlur, $gdimg, 1, 1, 0, 0, $w,     $h,     50.00000);  // down right
+				ImageCopyMerge($imgBlur, $gdimg, 0, 1, 1, 0, $w - 1, $h,     33.33333);  // down left
+				ImageCopyMerge($imgBlur, $gdimg, 1, 0, 0, 1, $w,     $h - 1, 25.00000);  // up right
+				ImageCopyMerge($imgBlur, $gdimg, 0, 0, 1, 0, $w - 1, $h,     33.33333);  // left
+				ImageCopyMerge($imgBlur, $gdimg, 1, 0, 0, 0, $w,     $h,     25.00000);  // right
+				ImageCopyMerge($imgBlur, $gdimg, 0, 0, 0, 1, $w,     $h - 1, 20.00000);  // up
+				ImageCopyMerge($imgBlur, $gdimg, 0, 1, 0, 0, $w,     $h,     16.666667); // down
+				ImageCopyMerge($imgBlur, $gdimg, 0, 0, 0, 0, $w,     $h,     50.000000); // center
+				ImageCopy     ($gdimg, $imgBlur, 0, 0, 0, 0, $w,     $h);
+			}
+			return true;
+		}
+		return false;
+	}
+
+
+	function BlurGaussian(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_GAUSSIAN_BLUR)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_GAUSSIAN_BLUR)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		$this->DebugMessage('FAILED: phpthumb_filters::BlurGaussian($gdimg) [using phpthumb_filters::Blur() instead]', __FILE__, __LINE__);
+		return phpthumb_filters::Blur($gdimg, 0.5);
+	}
+
+
+	function BlurSelective(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_SELECTIVE_BLUR)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_SELECTIVE_BLUR)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::BlurSelective($gdimg) [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Brightness(&$gdimg, $amount=0) {
+		if ($amount == 0) {
+			return true;
+		}
+		$amount = max(-255, min(255, $amount));
+
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_BRIGHTNESS, $amount)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_BRIGHTNESS, '.$amount.')', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+
+		$scaling = (255 - abs($amount)) / 255;
+		$baseamount = (($amount > 0) ? $amount : 0);
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				foreach ($OriginalPixel as $key => $value) {
+					$NewPixel[$key] = round($baseamount + ($OriginalPixel[$key] * $scaling));
+				}
+				$newColor = ImageColorAllocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function Contrast(&$gdimg, $amount=0) {
+		if ($amount == 0) {
+			return true;
+		}
+		$amount = max(-255, min(255, $amount));
+
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_CONTRAST, $amount)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_CONTRAST, '.$amount.')', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+
+		if ($amount > 0) {
+			$scaling = 1 + ($amount / 255);
+		} else {
+			$scaling = (255 - abs($amount)) / 255;
+		}
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				foreach ($OriginalPixel as $key => $value) {
+					$NewPixel[$key] = min(255, max(0, round($OriginalPixel[$key] * $scaling)));
+				}
+				$newColor = ImageColorAllocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+	}
+
+
+	function Colorize(&$gdimg, $amount, $targetColor) {
+		$amount      = (is_numeric($amount)                          ? $amount      : 25);
+		$targetColor = (phpthumb_functions::IsHexColor($targetColor) ? $targetColor : 'gray');
+
+		if ($amount == 0) {
+			return true;
+		}
+
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if ($targetColor == 'gray') {
+				$targetColor = '808080';
+			}
+			$r = substr($targetColor, 0, 2);
+			$g = substr($targetColor, 2, 2);
+			$b = substr($targetColor, 4, 2);
+			if (ImageFilter($gdimg, IMG_FILTER_COLORIZE, $r, $g, $b)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_COLORIZE)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+
+		// overridden below for grayscale
+		if ($targetColor != 'gray') {
+			$TargetPixel['red']   = hexdec(substr($targetColor, 0, 2));
+			$TargetPixel['green'] = hexdec(substr($targetColor, 2, 2));
+			$TargetPixel['blue']  = hexdec(substr($targetColor, 4, 2));
+		}
+
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				if ($targetColor == 'gray') {
+					$TargetPixel = phpthumb_functions::GrayscalePixel($OriginalPixel);
+				}
+				foreach ($TargetPixel as $key => $value) {
+					$NewPixel[$key] = round(max(0, min(255, ($OriginalPixel[$key] * ((100 - $amount) / 100)) + ($TargetPixel[$key] * ($amount / 100)))));
+				}
+				//$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue'], $OriginalPixel['alpha']);
+				$newColor = ImageColorAllocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function Crop(&$gdimg, $left=0, $right=0, $top=0, $bottom=0) {
+		if (!$left && !$right && !$top && !$bottom) {
+			return true;
+		}
+		$oldW = ImageSX($gdimg);
+		$oldH = ImageSY($gdimg);
+		if (($left   > 0) && ($left   < 1)) { $left   = round($left   * $oldW); }
+		if (($right  > 0) && ($right  < 1)) { $right  = round($right  * $oldW); }
+		if (($top    > 0) && ($top    < 1)) { $top    = round($top    * $oldH); }
+		if (($bottom > 0) && ($bottom < 1)) { $bottom = round($bottom * $oldH); }
+		$right  = min($oldW - $left - 1, $right);
+		$bottom = min($oldH - $top  - 1, $bottom);
+		$newW = $oldW - $left - $right;
+		$newH = $oldH - $top  - $bottom;
+
+		if ($imgCropped = ImageCreateTrueColor($newW, $newH)) {
+			ImageCopy($imgCropped, $gdimg, 0, 0, $left, $top, $newW, $newH);
+			if ($gdimg = ImageCreateTrueColor($newW, $newH)) {
+				ImageCopy($gdimg, $imgCropped, 0, 0, 0, 0, $newW, $newH);
+				ImageDestroy($imgCropped);
+				return true;
+			}
+			ImageDestroy($imgCropped);
+		}
+		return false;
+	}
+
+
+	function Desaturate(&$gdimg, $amount, $color='') {
+		if ($amount == 0) {
+			return true;
+		}
+		return phpthumb_filters::Colorize($gdimg, $amount, (phpthumb_functions::IsHexColor($color) ? $color : 'gray'));
+	}
+
+
+	function DropShadow(&$gdimg, $distance, $width, $hexcolor, $angle, $fade) {
+		if (phpthumb_functions::gd_version() < 2) {
+			return false;
+		}
+		$distance = ($distance ? $distance : 10);
+		$width    = ($width    ? $width    : 10);
+		$hexcolor = ($hexcolor ? $hexcolor : '000000');
+		$angle    = ($angle    ? $angle    : 225);
+		$fade     = ($fade     ? $fade     : 1);
+
+		$width_shadow  = cos(deg2rad($angle)) * ($distance + $width);
+		$height_shadow = sin(deg2rad($angle)) * ($distance + $width);
+
+		$scaling = min(ImageSX($gdimg) / (ImageSX($gdimg) + abs($width_shadow)), ImageSY($gdimg) / (ImageSY($gdimg) + abs($height_shadow)));
+
+		for ($i = 0; $i < $width; $i++) {
+			$WidthAlpha[$i] = (abs(($width / 2) - $i) / $width) * $fade;
+			$Offset['x'] = cos(deg2rad($angle)) * ($distance + $i);
+			$Offset['y'] = sin(deg2rad($angle)) * ($distance + $i);
+		}
+
+		$tempImageWidth  = ImageSX($gdimg)  + abs($Offset['x']);
+		$tempImageHeight = ImageSY($gdimg) + abs($Offset['y']);
+
+		if ($gdimg_dropshadow_temp = phpthumb_functions::ImageCreateFunction($tempImageWidth, $tempImageHeight)) {
+
+			ImageAlphaBlending($gdimg_dropshadow_temp, false);
+			ImageSaveAlpha($gdimg_dropshadow_temp, true);
+			$transparent1 = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_dropshadow_temp, 0, 0, 0, 127);
+			ImageFill($gdimg_dropshadow_temp, 0, 0, $transparent1);
+
+			for ($x = 0; $x < ImageSX($gdimg); $x++) {
+				for ($y = 0; $y < ImageSY($gdimg); $y++) {
+					$PixelMap[$x][$y] = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				}
+			}
+			for ($x = 0; $x < $tempImageWidth; $x++) {
+				for ($y = 0; $y < $tempImageHeight; $y++) {
+					//for ($i = 0; $i < $width; $i++) {
+					for ($i = 0; $i < 1; $i++) {
+						if (!isset($PixelMap[$x][$y]['alpha']) || ($PixelMap[$x][$y]['alpha'] > 0)) {
+							if (isset($PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha']) && ($PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha'] < 127)) {
+								$thisColor = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor, false, $PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha']);
+								ImageSetPixel($gdimg_dropshadow_temp, $x, $y, $thisColor);
+							}
+						}
+					}
+				}
+			}
+
+			ImageAlphaBlending($gdimg_dropshadow_temp, true);
+			for ($x = 0; $x < ImageSX($gdimg); $x++) {
+				for ($y = 0; $y < ImageSY($gdimg); $y++) {
+					if ($PixelMap[$x][$y]['alpha'] < 127) {
+						$thisColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_dropshadow_temp, $PixelMap[$x][$y]['red'], $PixelMap[$x][$y]['green'], $PixelMap[$x][$y]['blue'], $PixelMap[$x][$y]['alpha']);
+						ImageSetPixel($gdimg_dropshadow_temp, $x, $y, $thisColor);
+					}
+				}
+			}
+
+			ImageSaveAlpha($gdimg, true);
+			ImageAlphaBlending($gdimg, false);
+			//$this->is_alpha = true;
+			$transparent2 = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0, 0, 0, 127);
+			ImageFilledRectangle($gdimg, 0, 0, ImageSX($gdimg), ImageSY($gdimg), $transparent2);
+			ImageCopyResampled($gdimg, $gdimg_dropshadow_temp, 0, 0, 0, 0, ImageSX($gdimg), ImageSY($gdimg), ImageSX($gdimg_dropshadow_temp), ImageSY($gdimg_dropshadow_temp));
+
+			ImageDestroy($gdimg_dropshadow_temp);
+		}
+		return true;
+	}
+
+
+	function EdgeDetect(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_EDGEDETECT)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_EDGEDETECT)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::EdgeDetect($gdimg) [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Elipse($gdimg) {
+		if (phpthumb_functions::gd_version() < 2) {
+			return false;
+		}
+		// generate mask at twice desired resolution and downsample afterwards for easy antialiasing
+		if ($gdimg_elipsemask_double = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg) * 2, ImageSY($gdimg) * 2)) {
+			if ($gdimg_elipsemask = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg), ImageSY($gdimg))) {
+
+				$color_transparent = ImageColorAllocate($gdimg_elipsemask_double, 255, 255, 255);
+				ImageFilledEllipse($gdimg_elipsemask_double, ImageSX($gdimg), ImageSY($gdimg), (ImageSX($gdimg) - 1) * 2, (ImageSY($gdimg) - 1) * 2, $color_transparent);
+				ImageCopyResampled($gdimg_elipsemask, $gdimg_elipsemask_double, 0, 0, 0, 0, ImageSX($gdimg), ImageSY($gdimg), ImageSX($gdimg) * 2, ImageSY($gdimg) * 2);
+
+				phpthumb_filters::ApplyMask($gdimg_elipsemask, $gdimg);
+				ImageDestroy($gdimg_elipsemask);
+				return true;
+
+			} else {
+				$this->DebugMessage('$gdimg_elipsemask = phpthumb_functions::ImageCreateFunction() failed', __FILE__, __LINE__);
+			}
+			ImageDestroy($gdimg_elipsemask_double);
+		} else {
+			$this->DebugMessage('$gdimg_elipsemask_double = phpthumb_functions::ImageCreateFunction() failed', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	function Emboss(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_EMBOSS)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_EMBOSS)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::Emboss($gdimg) [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Flip(&$gdimg, $x=false, $y=false) {
+		if (!$x && !$y) {
+			return false;
+		}
+		if ($tempImage = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg), ImageSY($gdimg))) {
+			if ($x) {
+				ImageCopy($tempImage, $gdimg, 0, 0, 0, 0, ImageSX($gdimg), ImageSY($gdimg));
+				for ($x = 0; $x < ImageSX($gdimg); $x++) {
+					ImageCopy($gdimg, $tempImage, ImageSX($gdimg) - 1 - $x, 0, $x, 0, 1, ImageSY($gdimg));
+				}
+			}
+			if ($y) {
+				ImageCopy($tempImage, $gdimg, 0, 0, 0, 0, ImageSX($gdimg), ImageSY($gdimg));
+				for ($y = 0; $y < ImageSY($gdimg); $y++) {
+					ImageCopy($gdimg, $tempImage, 0, ImageSY($gdimg) - 1 - $y, 0, $y, ImageSX($gdimg), 1);
+				}
+			}
+			ImageDestroy($tempImage);
+		}
+		return true;
+	}
+
+
+	function Frame(&$gdimg, $frame_width, $edge_width, $hexcolor_frame, $hexcolor1, $hexcolor2) {
+		$frame_width    = ($frame_width    ? $frame_width    : 5);
+		$edge_width     = ($edge_width     ? $edge_width     : 1);
+		$hexcolor_frame = ($hexcolor_frame ? $hexcolor_frame : 'CCCCCC');
+		$hexcolor1      = ($hexcolor1      ? $hexcolor1      : 'FFFFFF');
+		$hexcolor2      = ($hexcolor2      ? $hexcolor2      : '000000');
+
+		$color_frame = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor_frame);
+		$color1      = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor1);
+		$color2      = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor2);
+		for ($i = 0; $i < $edge_width; $i++) {
+			// outer bevel
+			ImageLine($gdimg,                   $i,                   $i,                   $i, ImageSY($gdimg) - $i, $color1); // left
+			ImageLine($gdimg,                   $i,                   $i, ImageSX($gdimg) - $i,                   $i, $color1); // top
+			ImageLine($gdimg, ImageSX($gdimg) - $i, ImageSY($gdimg) - $i, ImageSX($gdimg) - $i,                   $i, $color2); // right
+			ImageLine($gdimg, ImageSX($gdimg) - $i, ImageSY($gdimg) - $i,                   $i, ImageSY($gdimg) - $i, $color2); // bottom
+		}
+		for ($i = 0; $i < $frame_width; $i++) {
+			// actual frame
+			ImageRectangle($gdimg, $edge_width + $i, $edge_width + $i, ImageSX($gdimg) - $edge_width - $i, ImageSY($gdimg) - $edge_width - $i, $color_frame);
+		}
+		for ($i = 0; $i < $edge_width; $i++) {
+			// inner bevel
+			ImageLine($gdimg,                   $frame_width + $edge_width + $i,                   $frame_width + $edge_width + $i,                   $frame_width + $edge_width + $i, ImageSY($gdimg) - $frame_width - $edge_width - $i, $color2); // left
+			ImageLine($gdimg,                   $frame_width + $edge_width + $i,                   $frame_width + $edge_width + $i, ImageSX($gdimg) - $frame_width - $edge_width - $i,                   $frame_width + $edge_width + $i, $color2); // top
+			ImageLine($gdimg, ImageSX($gdimg) - $frame_width - $edge_width - $i, ImageSY($gdimg) - $frame_width - $edge_width - $i, ImageSX($gdimg) - $frame_width - $edge_width - $i,                   $frame_width + $edge_width + $i, $color1); // right
+			ImageLine($gdimg, ImageSX($gdimg) - $frame_width - $edge_width - $i, ImageSY($gdimg) - $frame_width - $edge_width - $i,                   $frame_width + $edge_width + $i, ImageSY($gdimg) - $frame_width - $edge_width - $i, $color1); // bottom
+		}
+		return true;
+	}
+
+
+	function Gamma(&$gdimg, $amount) {
+		if (number_format($amount, 4) == '1.0000') {
+			return true;
+		}
+		return ImageGammaCorrect($gdimg, 1.0, $amount);
+	}
+
+
+	function Grayscale(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_GRAYSCALE)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_GRAYSCALE)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		return phpthumb_filters::Colorize($gdimg, 100, 'gray');
+	}
+
+
+	function HistogramAnalysis(&$gdimg, $calculateGray=false) {
+		$ImageSX = ImageSX($gdimg);
+		$ImageSY = ImageSY($gdimg);
+		for ($x = 0; $x < $ImageSX; $x++) {
+			for ($y = 0; $y < $ImageSY; $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				@$Analysis['red'][$OriginalPixel['red']]++;
+				@$Analysis['green'][$OriginalPixel['green']]++;
+				@$Analysis['blue'][$OriginalPixel['blue']]++;
+				@$Analysis['alpha'][$OriginalPixel['alpha']]++;
+				if ($calculateGray) {
+					$GrayPixel = phpthumb_functions::GrayscalePixel($OriginalPixel);
+					@$Analysis['gray'][$GrayPixel['red']]++;
+				}
+			}
+		}
+		$keys = array('red', 'green', 'blue', 'alpha');
+		if ($calculateGray) {
+			$keys[] = 'gray';
+		}
+		foreach ($keys as $dummy => $key) {
+			ksort($Analysis[$key]);
+		}
+		return $Analysis;
+	}
+
+
+	function HistogramStretch(&$gdimg, $band='*', $method=0, $threshold=0.1) {
+		// equivalent of "Auto Contrast" in Adobe Photoshop
+		// method 0 stretches according to RGB colors. Gives a more conservative stretch.
+		// method 1 band stretches according to grayscale which is color-biased (59% green, 30% red, 11% blue). May give a punchier / more aggressive stretch, possibly appearing over-saturated
+		$Analysis = phpthumb_filters::HistogramAnalysis($gdimg, true);
+		$keys = array('r'=>'red', 'g'=>'green', 'b'=>'blue', 'a'=>'alpha', '*'=>(($method == 0) ? 'all' : 'gray'));
+		$band = substr($band, 0, 1);
+		if (!isset($keys[$band])) {
+			return false;
+		}
+		$key = $keys[$band];
+
+		// If the absolute brightest and darkest pixels are used then one random
+		// pixel in the image could throw off the whole system. Instead, count up/down
+		// from the limit and allow <threshold> (default = 0.1%) of brightest/darkest
+		// pixels to be clipped to min/max
+		$threshold = floatval($threshold) / 100;
+		$clip_threshold = ImageSX($gdimg) * ImageSX($gdimg) * $threshold;
+		//if ($min >= 0) {
+		//	$range_min = min($min, 255);
+		//} else {
+			$countsum = 0;
+			for ($i = 0; $i <= 255; $i++) {
+				if ($method == 0) {
+					$countsum = max(@$Analysis['red'][$i], @$Analysis['green'][$i], @$Analysis['blue'][$i]);
+				} else {
+					$countsum += @$Analysis[$key][$i];
+				}
+				if ($countsum >= $clip_threshold) {
+					$range_min = $i - 1;
+					break;
+				}
+			}
+			$range_min = max($range_min, 0);
+		//}
+		//if ($max > 0) {
+		//	$range_max = max($max, 255);
+		//} else {
+			$countsum = 0;
+			for ($i = 255; $i >= 0; $i--) {
+				if ($method == 0) {
+					$countsum = max(@$Analysis['red'][$i], @$Analysis['green'][$i], @$Analysis['blue'][$i]);
+				} else {
+					$countsum += @$Analysis[$key][$i];
+				}
+				if ($countsum >= $clip_threshold) {
+					$range_max = $i + 1;
+					break;
+				}
+			}
+			$range_max = min($range_max, 255);
+		//}
+		$range_scale = (($range_max == $range_min) ? 1 : (255 / ($range_max - $range_min)));
+		if (($range_min == 0) && ($range_max == 255)) {
+			// no adjustment neccesary - don't waste CPU time!
+			return true;
+		}
+
+		$ImageSX = ImageSX($gdimg);
+		$ImageSY = ImageSY($gdimg);
+		for ($x = 0; $x < $ImageSX; $x++) {
+			for ($y = 0; $y < $ImageSY; $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				if ($band == '*') {
+					$new['red']   = min(255, max(0, ($OriginalPixel['red']   - $range_min) * $range_scale));
+					$new['green'] = min(255, max(0, ($OriginalPixel['green'] - $range_min) * $range_scale));
+					$new['blue']  = min(255, max(0, ($OriginalPixel['blue']  - $range_min) * $range_scale));
+					$new['alpha'] = min(255, max(0, ($OriginalPixel['alpha'] - $range_min) * $range_scale));
+				} else {
+					$new = $OriginalPixel;
+					$new[$key] = min(255, max(0, ($OriginalPixel[$key] - $range_min) * $range_scale));
+				}
+				$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $new['red'], $new['green'], $new['blue'], $new['alpha']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+
+		return true;
+	}
+
+
+	function HistogramOverlay(&$gdimg, $bands='*', $colors='', $width=0.25, $height=0.25, $alignment='BR', $opacity=50, $margin_x=5, $margin_y=null) {
+		$margin_y = (is_null($margin_y) ? $margin_x : $margin_y);
+
+		$Analysis = phpthumb_filters::HistogramAnalysis($gdimg, true);
+		$histW = round(($width > 1) ? min($width, ImageSX($gdimg)) : ImageSX($gdimg) * $width);
+		$histH = round(($width > 1) ? min($width, ImageSX($gdimg)) : ImageSX($gdimg) * $width);
+		if ($gdHist = ImageCreateTrueColor($histW, $histH)) {
+			$color_back = phpthumb_functions::ImageColorAllocateAlphaSafe($gdHist, 0, 0, 0, 127);
+			ImageFilledRectangle($gdHist, 0, 0, $histW, $histH, $color_back);
+			ImageAlphaBlending($gdHist, false);
+			ImageSaveAlpha($gdHist, true);
+
+			$HistogramTempWidth  = 256;
+			$HistogramTempHeight = 100;
+			if ($gdHistTemp = ImageCreateTrueColor($HistogramTempWidth, $HistogramTempHeight)) {
+				$color_back_temp = phpthumb_functions::ImageColorAllocateAlphaSafe($gdHistTemp, 255, 0, 255, 127);
+				ImageAlphaBlending($gdHistTemp, false);
+				ImageSaveAlpha($gdHistTemp, true);
+				ImageFilledRectangle($gdHistTemp, 0, 0, ImageSX($gdHistTemp), ImageSY($gdHistTemp), $color_back_temp);
+
+				$DefaultColors = array('r'=>'FF0000', 'g'=>'00FF00', 'b'=>'0000FF', 'a'=>'999999', '*'=>'FFFFFF');
+				$Colors = explode(';', $colors);
+				$BandsToGraph = array_unique(preg_split('//', $bands));
+				$keys = array('r'=>'red', 'g'=>'green', 'b'=>'blue', 'a'=>'alpha', '*'=>'gray');
+				foreach ($BandsToGraph as $key => $band) {
+					if (!isset($keys[$band])) {
+						continue;
+					}
+					$PeakValue = max($Analysis[$keys[$band]]);
+					$thisColor = phpthumb_functions::ImageHexColorAllocate($gdHistTemp, phpthumb_functions::IsHexColor(@$Colors[$key]) ? $Colors[$key] : $DefaultColors[$band]);
+					for ($x = 0; $x < $HistogramTempWidth; $x++) {
+						ImageLine($gdHistTemp, $x, $HistogramTempHeight - 1, $x, $HistogramTempHeight - 1 - round(@$Analysis[$keys[$band]][$x] / $PeakValue * $HistogramTempHeight), $thisColor);
+					}
+					ImageLine($gdHistTemp, 0, $HistogramTempHeight - 1, $HistogramTempWidth - 1, $HistogramTempHeight - 1, $thisColor);
+					ImageLine($gdHistTemp, 0, $HistogramTempHeight - 2, $HistogramTempWidth - 1, $HistogramTempHeight - 2, $thisColor);
+				}
+				ImageCopyResampled($gdHist, $gdHistTemp, 0, 0, 0, 0, ImageSX($gdHist), ImageSY($gdHist), ImageSX($gdHistTemp), ImageSY($gdHistTemp));
+				ImageDestroy($gdHistTemp);
+			} else {
+				return false;
+			}
+
+			phpthumb_filters::WatermarkOverlay($gdimg, $gdHist, $alignment, $opacity, $margin_x, $margin_y);
+			ImageDestroy($gdHist);
+			return true;
+		}
+		return false;
+	}
+
+
+	function ImageBorder(&$gdimg, $border_width, $radius_x, $radius_y, $hexcolor_border) {
+		$border_width = ($border_width ? $border_width : 1);
+		$radius_x     = ($radius_x     ? $radius_x     : 0);
+		$radius_y     = ($radius_y     ? $radius_y     : 0);
+
+		$output_width  = ImageSX($gdimg);
+		$output_height = ImageSY($gdimg);
+
+		list($new_width, $new_height) = phpthumb_functions::ProportionalResize($output_width, $output_height, $output_width - max($border_width * 2, $radius_x), $output_height - max($border_width * 2, $radius_y));
+		$offset_x = ($radius_x ? $output_width  - $new_width  - $radius_x : 0);
+		$offset_y = ($radius_y ? $output_height - $new_height - $radius_y : 0);
+
+//header('Content-Type: image/png');
+//ImagePNG($gdimg);
+//exit;
+		if ($gd_border_canvas = phpthumb_functions::ImageCreateFunction($output_width, $output_height)) {
+
+			ImageSaveAlpha($gd_border_canvas, true);
+			ImageAlphaBlending($gd_border_canvas, false);
+			$color_background = phpthumb_functions::ImageColorAllocateAlphaSafe($gd_border_canvas, 255, 255, 255, 127);
+			ImageFilledRectangle($gd_border_canvas, 0, 0, $output_width, $output_height, $color_background);
+
+			$color_border = phpthumb_functions::ImageHexColorAllocate($gd_border_canvas, (phpthumb_functions::IsHexColor($hexcolor_border) ? $hexcolor_border : '000000'));
+
+			for ($i = 0; $i < $border_width; $i++) {
+				ImageLine($gd_border_canvas,             floor($offset_x / 2) + $radius_x,                      $i, $output_width - $radius_x - ceil($offset_x / 2),                         $i, $color_border); // top
+				ImageLine($gd_border_canvas,             floor($offset_x / 2) + $radius_x, $output_height - 1 - $i, $output_width - $radius_x - ceil($offset_x / 2),    $output_height - 1 - $i, $color_border); // bottom
+				ImageLine($gd_border_canvas,                    floor($offset_x / 2) + $i,               $radius_y,                      floor($offset_x / 2) +  $i, $output_height - $radius_y, $color_border); // left
+				ImageLine($gd_border_canvas, $output_width - 1 - $i - ceil($offset_x / 2),               $radius_y,    $output_width - 1 - $i - ceil($offset_x / 2), $output_height - $radius_y, $color_border); // right
+			}
+
+			if ($radius_x && $radius_y) {
+
+				// PHP bug: ImageArc() with thicknesses > 1 give bad/undesirable/unpredicatable results
+				// Solution: Draw multiple 1px arcs side-by-side.
+
+				// Problem: parallel arcs give strange/ugly antialiasing problems
+				// Solution: draw non-parallel arcs, from one side of the line thickness at the start angle
+				//   to the opposite edge of the line thickness at the terminating angle
+				for ($thickness_offset = 0; $thickness_offset < $border_width; $thickness_offset++) {
+					ImageArc($gd_border_canvas, floor($offset_x / 2) + 1 +                 $radius_x,              $thickness_offset - 1 + $radius_y, $radius_x * 2, $radius_y * 2, 180, 270, $color_border); // top-left
+					ImageArc($gd_border_canvas,                     $output_width - $radius_x - 1 - ceil($offset_x / 2),              $thickness_offset - 1 + $radius_y, $radius_x * 2, $radius_y * 2, 270, 360, $color_border); // top-right
+					ImageArc($gd_border_canvas,                     $output_width - $radius_x - 1 - ceil($offset_x / 2), $output_height - $thickness_offset - $radius_y, $radius_x * 2, $radius_y * 2,   0,  90, $color_border); // bottom-right
+					ImageArc($gd_border_canvas, floor($offset_x / 2) + 1 +                 $radius_x, $output_height - $thickness_offset - $radius_y, $radius_x * 2, $radius_y * 2,  90, 180, $color_border); // bottom-left
+				}
+				if ($border_width > 1) {
+					for ($thickness_offset = 0; $thickness_offset < $border_width; $thickness_offset++) {
+						ImageArc($gd_border_canvas, floor($offset_x / 2) + $thickness_offset + $radius_x,                                      $radius_y, $radius_x * 2, $radius_y * 2, 180, 270, $color_border); // top-left
+						ImageArc($gd_border_canvas, $output_width - $thickness_offset - $radius_x - 1 - ceil($offset_x / 2),                                      $radius_y, $radius_x * 2, $radius_y * 2, 270, 360, $color_border); // top-right
+						ImageArc($gd_border_canvas, $output_width - $thickness_offset - $radius_x - 1 - ceil($offset_x / 2),                     $output_height - $radius_y, $radius_x * 2, $radius_y * 2,   0,  90, $color_border); // bottom-right
+						ImageArc($gd_border_canvas, floor($offset_x / 2) + $thickness_offset + $radius_x,                     $output_height - $radius_y, $radius_x * 2, $radius_y * 2,  90, 180, $color_border); // bottom-left
+					}
+				}
+
+			}
+			$this->phpThumbObject->ImageResizeFunction($gd_border_canvas, $gdimg, floor(($output_width - $new_width) / 2), round(($output_height - $new_height) / 2), 0, 0, $new_width, $new_height, $output_width, $output_height);
+
+			ImageDestroy($gdimg);
+			$gdimg = phpthumb_functions::ImageCreateFunction($output_width, $output_height);
+			ImageSaveAlpha($gdimg, true);
+			ImageAlphaBlending($gdimg, false);
+			$gdimg_color_background = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 255, 255, 255, 127);
+			ImageFilledRectangle($gdimg, 0, 0, $output_width, $output_height, $gdimg_color_background);
+
+			ImageCopy($gdimg, $gd_border_canvas, 0, 0, 0, 0, $output_width, $output_height);
+			//$gdimg = $gd_border_canvas;
+			ImageDestroy($gd_border_canvas);
+			return true;
+
+
+		} else {
+			$this->DebugMessage('FAILED: $gd_border_canvas = phpthumb_functions::ImageCreateFunction('.$output_width.', '.$output_height.')', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	function ImprovedImageRotate(&$gdimg_source, $rotate_angle=0, $config_background_hexcolor='FFFFFF', $bg=null) {
+		while ($rotate_angle < 0) {
+			$rotate_angle += 360;
+		}
+		$rotate_angle = $rotate_angle % 360;
+		if ($rotate_angle != 0) {
+
+			$background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_source, $config_background_hexcolor);
+
+			if ((phpthumb_functions::gd_version() >= 2) && !$bg && ($rotate_angle % 90)) {
+
+				//$this->DebugMessage('Using alpha rotate', __FILE__, __LINE__);
+				if ($gdimg_rotate_mask = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg_source), ImageSY($gdimg_source))) {
+
+					for ($i = 0; $i <= 255; $i++) {
+						$color_mask[$i] = ImageColorAllocate($gdimg_rotate_mask, $i, $i, $i);
+					}
+					ImageFilledRectangle($gdimg_rotate_mask, 0, 0, ImageSX($gdimg_rotate_mask), ImageSY($gdimg_rotate_mask), $color_mask[255]);
+					$imageX = ImageSX($gdimg_source);
+					$imageY = ImageSY($gdimg_source);
+					for ($x = 0; $x < $imageX; $x++) {
+						for ($y = 0; $y < $imageY; $y++) {
+							$pixelcolor = phpthumb_functions::GetPixelColor($gdimg_source, $x, $y);
+							ImageSetPixel($gdimg_rotate_mask, $x, $y, $color_mask[255 - round($pixelcolor['alpha'] * 255 / 127)]);
+						}
+					}
+					$gdimg_rotate_mask  = ImageRotate($gdimg_rotate_mask,  $rotate_angle, $color_mask[0]);
+					$gdimg_source = ImageRotate($gdimg_source, $rotate_angle, $background_color);
+
+					ImageAlphaBlending($gdimg_source, false);
+					ImageSaveAlpha($gdimg_source, true);
+					//$this->is_alpha = true;
+					$phpThumbFilters = new phpthumb_filters();
+					$phpThumbFilters->phpThumbObject = $this;
+					$phpThumbFilters->ApplyMask($gdimg_rotate_mask, $gdimg_source);
+
+					ImageDestroy($gdimg_rotate_mask);
+
+				} else {
+					//$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
+				}
+
+			} else {
+
+				if (phpthumb_functions::gd_version() < 2) {
+					//$this->DebugMessage('Using non-alpha rotate because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+				} elseif ($bg) {
+					//$this->DebugMessage('Using non-alpha rotate because $this->bg is "'.$bg.'"', __FILE__, __LINE__);
+				} elseif ($rotate_angle % 90) {
+					//$this->DebugMessage('Using non-alpha rotate because ($rotate_angle % 90) = "'.($rotate_angle % 90).'"', __FILE__, __LINE__);
+				} else {
+					//$this->DebugMessage('Using non-alpha rotate because $this->thumbnailFormat is "'.$this->thumbnailFormat.'"', __FILE__, __LINE__);
+				}
+
+				if (ImageColorTransparent($gdimg_source) >= 0) {
+					// ImageRotate() forgets all about an image's transparency and sets the transparent color to black
+					// To compensate, flood-fill the transparent color of the source image with the specified background color first
+					// then rotate and the colors should match
+
+					if (!function_exists('ImageIsTrueColor') || !ImageIsTrueColor($gdimg_source)) {
+						// convert paletted image to true-color before rotating to prevent nasty aliasing artifacts
+
+						//$this->source_width  = ImageSX($gdimg_source);
+						//$this->source_height = ImageSY($gdimg_source);
+						$gdimg_newsrc = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg_source), ImageSY($gdimg_source));
+						$background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_newsrc, $config_background_hexcolor);
+						ImageFilledRectangle($gdimg_newsrc, 0, 0, ImageSX($gdimg_source), ImageSY($gdimg_source), phpthumb_functions::ImageHexColorAllocate($gdimg_newsrc, $config_background_hexcolor));
+						ImageCopy($gdimg_newsrc, $gdimg_source, 0, 0, 0, 0, ImageSX($gdimg_source), ImageSY($gdimg_source));
+						ImageDestroy($gdimg_source);
+						unset($gdimg_source);
+						$gdimg_source = $gdimg_newsrc;
+						unset($gdimg_newsrc);
+
+					} else {
+
+						ImageColorSet(
+							$gdimg_source,
+							ImageColorTransparent($gdimg_source),
+							hexdec(substr($config_background_hexcolor, 0, 2)),
+							hexdec(substr($config_background_hexcolor, 2, 2)),
+							hexdec(substr($config_background_hexcolor, 4, 2)));
+
+						ImageColorTransparent($gdimg_source, -1);
+
+					}
+				}
+
+				$gdimg_source = ImageRotate($gdimg_source, $rotate_angle, $background_color);
+
+			}
+		}
+		return true;
+	}
+
+
+	function MeanRemoval(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_MEAN_REMOVAL)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_MEAN_REMOVAL)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::MeanRemoval($gdimg) [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Negative(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_NEGATE)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_NEGATE)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		$ImageSX = ImageSX($gdimg);
+		$ImageSY = ImageSY($gdimg);
+		for ($x = 0; $x < $ImageSX; $x++) {
+			for ($y = 0; $y < $ImageSY; $y++) {
+				$currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, (~$currentPixel['red'] & 0xFF), (~$currentPixel['green'] & 0xFF), (~$currentPixel['blue'] & 0xFF), $currentPixel['alpha']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function RoundedImageCorners(&$gdimg, $radius_x, $radius_y) {
+		// generate mask at twice desired resolution and downsample afterwards for easy antialiasing
+		// mask is generated as a white double-size elipse on a triple-size black background and copy-paste-resampled
+		// onto a correct-size mask image as 4 corners due to errors when the entire mask is resampled at once (gray edges)
+		if ($gdimg_cornermask_triple = phpthumb_functions::ImageCreateFunction($radius_x * 6, $radius_y * 6)) {
+			if ($gdimg_cornermask = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg), ImageSY($gdimg))) {
+
+				$color_transparent = ImageColorAllocate($gdimg_cornermask_triple, 255, 255, 255);
+				ImageFilledEllipse($gdimg_cornermask_triple, $radius_x * 3, $radius_y * 3, $radius_x * 4, $radius_y * 4, $color_transparent);
+
+				ImageFilledRectangle($gdimg_cornermask, 0, 0, ImageSX($gdimg), ImageSY($gdimg), $color_transparent);
+
+				ImageCopyResampled($gdimg_cornermask, $gdimg_cornermask_triple,                           0,                           0,     $radius_x,     $radius_y, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2);
+				ImageCopyResampled($gdimg_cornermask, $gdimg_cornermask_triple,                           0, ImageSY($gdimg) - $radius_y,     $radius_x, $radius_y * 3, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2);
+				ImageCopyResampled($gdimg_cornermask, $gdimg_cornermask_triple, ImageSX($gdimg) - $radius_x, ImageSY($gdimg) - $radius_y, $radius_x * 3, $radius_y * 3, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2);
+				ImageCopyResampled($gdimg_cornermask, $gdimg_cornermask_triple, ImageSX($gdimg) - $radius_x,                           0, $radius_x * 3,     $radius_y, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2);
+
+				phpthumb_filters::ApplyMask($gdimg_cornermask, $gdimg);
+				ImageDestroy($gdimg_cornermask);
+				$this->DebugMessage('RoundedImageCorners('.$radius_x.', '.$radius_y.') succeeded', __FILE__, __LINE__);
+				return true;
+
+			} else {
+				$this->DebugMessage('FAILED: $gdimg_cornermask = phpthumb_functions::ImageCreateFunction('.ImageSX($gdimg).', '.ImageSY($gdimg).')', __FILE__, __LINE__);
+			}
+			ImageDestroy($gdimg_cornermask_triple);
+
+		} else {
+			$this->DebugMessage('FAILED: $gdimg_cornermask_triple = phpthumb_functions::ImageCreateFunction('.($radius_x * 6).', '.($radius_y * 6).')', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	function Saturation(&$gdimg, $amount, $color='') {
+		if ($amount == 0) {
+			return true;
+		} elseif ($amount > 0) {
+			$amount = 0 - $amount;
+		} else {
+			$amount = abs($amount);
+		}
+		return phpthumb_filters::Desaturate($gdimg, $amount, $color);
+	}
+
+
+	function Sepia(&$gdimg, $amount, $targetColor) {
+		$amount      = (is_numeric($amount) ? max(0, min(100, $amount)) : 50);
+		$targetColor = (phpthumb_functions::IsHexColor($targetColor) ? $targetColor : 'A28065');
+
+		if ($amount == 0) {
+			return true;
+		}
+
+		$TargetPixel['red']   = hexdec(substr($targetColor, 0, 2));
+		$TargetPixel['green'] = hexdec(substr($targetColor, 2, 2));
+		$TargetPixel['blue']  = hexdec(substr($targetColor, 4, 2));
+
+		$ImageSX = ImageSX($gdimg);
+		$ImageSY = ImageSY($gdimg);
+		for ($x = 0; $x < $ImageSX; $x++) {
+			for ($y = 0; $y < $ImageSY; $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				$GrayPixel = phpthumb_functions::GrayscalePixel($OriginalPixel);
+
+				// http://www.gimpguru.org/Tutorials/SepiaToning/
+				// "In the traditional sepia toning process, the tinting occurs most in
+				// the mid-tones: the lighter and darker areas appear to be closer to B&W."
+				$SepiaAmount = ((128 - abs($GrayPixel['red'] - 128)) / 128) * ($amount / 100);
+
+				foreach ($TargetPixel as $key => $value) {
+					$NewPixel[$key] = round(max(0, min(255, $GrayPixel[$key] * (1 - $SepiaAmount) + ($TargetPixel[$key] * $SepiaAmount))));
+				}
+				$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue'], $OriginalPixel['alpha']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function Smooth(&$gdimg, $amount=6) {
+		$amount = min(25, max(0, $amount));
+		if ($amount == 0) {
+			return true;
+		}
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_SMOOTH, $amount)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_SMOOTH, '.$amount.')', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::Smooth($gdimg, '.$amount.') [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Threshold(&$gdimg, $cutoff) {
+		$cutoff = min(255, max(0, ($cutoff ? $cutoff : 128)));
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				$grayPixel = phpthumb_functions::GrayscalePixel($currentPixel);
+				if ($grayPixel['red'] < $cutoff) {
+					$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0x00, 0x00, 0x00, $currentPixel['alpha']);
+				} else {
+					$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0xFF, 0xFF, 0xFF, $currentPixel['alpha']);
+				}
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function ImageTrueColorToPalette2(&$image, $dither, $ncolors) {
+		// http://www.php.net/manual/en/function.imagetruecolortopalette.php
+		// zmorris at zsculpt dot com (17-Aug-2004 06:58)
+		$width  = ImageSX($image);
+		$height = ImageSY($image);
+		$image_copy = ImageCreateTrueColor($width, $height);
+		//ImageCopyMerge($image_copy, $image, 0, 0, 0, 0, $width, $height, 100);
+		ImageCopy($image_copy, $image, 0, 0, 0, 0, $width, $height);
+		ImageTrueColorToPalette($image, $dither, $ncolors);
+		ImageColorMatch($image_copy, $image);
+		ImageDestroy($image_copy);
+		return true;
+	}
+
+	function ReduceColorDepth(&$gdimg, $colors=256, $dither=true) {
+		$colors = max(min($colors, 256), 2);
+		// ImageTrueColorToPalette usually makes ugly colors, the replacement is a bit better
+		//ImageTrueColorToPalette($gdimg, $dither, $colors);
+		phpthumb_filters::ImageTrueColorToPalette2($gdimg, $dither, $colors);
+		return true;
+	}
+
+
+	function WhiteBalance(&$gdimg, $targetColor='') {
+		if (phpthumb_functions::IsHexColor($targetColor)) {
+			$targetPixel = array(
+				'red'   => hexdec(substr($targetColor, 0, 2)),
+				'green' => hexdec(substr($targetColor, 2, 2)),
+				'blue'  => hexdec(substr($targetColor, 4, 2))
+			);
+		} else {
+			$Analysis = phpthumb_filters::HistogramAnalysis($gdimg, false);
+			$targetPixel = array(
+				'red'   => max(array_keys($Analysis['red'])),
+				'green' => max(array_keys($Analysis['green'])),
+				'blue'  => max(array_keys($Analysis['blue']))
+			);
+		}
+		$grayValue = phpthumb_functions::GrayscaleValue($targetPixel['red'], $targetPixel['green'], $targetPixel['blue']);
+		$scaleR = $grayValue / $targetPixel['red'];
+		$scaleG = $grayValue / $targetPixel['green'];
+		$scaleB = $grayValue / $targetPixel['blue'];
+
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe(
+					$gdimg,
+					max(0, min(255, round($currentPixel['red']   * $scaleR))),
+					max(0, min(255, round($currentPixel['green'] * $scaleG))),
+					max(0, min(255, round($currentPixel['blue']  * $scaleB))),
+					$currentPixel['alpha']
+				);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function WatermarkText(&$gdimg, $text, $size, $alignment, $hex_color='000000', $ttffont='', $opacity=100, $margin=5, $angle=0, $bg_color=false, $bg_opacity=0, $fillextend='') {
+		// text watermark requested
+		if (!$text) {
+			return false;
+		}
+		ImageAlphaBlending($gdimg, true);
+
+		$metaTextArray = array(
+			'^Fb' =>       $this->phpThumbObject->getimagesizeinfo['filesize'],
+			'^Fk' => round($this->phpThumbObject->getimagesizeinfo['filesize'] / 1024),
+			'^Fm' => round($this->phpThumbObject->getimagesizeinfo['filesize'] / 1048576),
+			'^X'  => $this->phpThumbObject->getimagesizeinfo[0],
+			'^Y'  => $this->phpThumbObject->getimagesizeinfo[1],
+			'^x'  => ImageSX($gdimg),
+			'^y'  => ImageSY($gdimg),
+			'^^'  => '^',
+		);
+		$text = strtr($text, $metaTextArray);
+
+		$text = str_replace("\r\n", "\n", $text);
+		$text = str_replace("\r",   "\n", $text);
+		$textlines = explode("\n", $text);
+
+		if (@is_readable($ttffont) && is_file($ttffont)) {
+
+			$opacity = 100 - intval(max(min($opacity, 100), 0));
+
+			$this->DebugMessage('Using TTF font "'.$ttffont.'"', __FILE__, __LINE__);
+
+			$TTFbox = ImageTTFbBox($size, $angle, $ttffont, $text);
+
+			$min_x = min($TTFbox[0], $TTFbox[2], $TTFbox[4], $TTFbox[6]);
+			$max_x = max($TTFbox[0], $TTFbox[2], $TTFbox[4], $TTFbox[6]);
+			//$text_width = round($max_x - $min_x + ($size * 0.5));
+			$text_width = round($max_x - $min_x);
+
+			$min_y = min($TTFbox[1], $TTFbox[3], $TTFbox[5], $TTFbox[7]);
+			$max_y = max($TTFbox[1], $TTFbox[3], $TTFbox[5], $TTFbox[7]);
+			//$text_height = round($max_y - $min_y + ($size * 0.5));
+			$text_height = round($max_y - $min_y);
+
+			$TTFboxChar = ImageTTFbBox($size, $angle, $ttffont, 'jH');
+			$char_min_y = min($TTFboxChar[1], $TTFboxChar[3], $TTFboxChar[5], $TTFboxChar[7]);
+			$char_max_y = max($TTFboxChar[1], $TTFboxChar[3], $TTFboxChar[5], $TTFboxChar[7]);
+			$char_height = round($char_max_y - $char_min_y);
+
+			switch ($alignment) {
+				case 'T':
+					$text_origin_x = round((ImageSX($gdimg) - $text_width) / 2);
+					$text_origin_y = $char_height + $margin;
+					break;
+
+				case 'B':
+					$text_origin_x = round((ImageSX($gdimg) - $text_width) / 2);
+					$text_origin_y = ImageSY($gdimg) + $TTFbox[1] - $margin;
+					break;
+
+				case 'L':
+					$text_origin_x = $margin;
+					$text_origin_y = round((ImageSY($gdimg) - $text_height) / 2) + $char_height;
+					break;
+
+				case 'R':
+					$text_origin_x = ImageSX($gdimg) - $text_width  + $TTFbox[0] - $min_x + round($size * 0.25) - $margin;
+					$text_origin_y = round((ImageSY($gdimg) - $text_height) / 2) + $char_height;
+					break;
+
+				case 'C':
+					$text_origin_x = round((ImageSX($gdimg) - $text_width) / 2);
+					$text_origin_y = round((ImageSY($gdimg) - $text_height) / 2) + $char_height;
+					break;
+
+				case 'TL':
+					$text_origin_x = $margin;
+					$text_origin_y = $char_height + $margin;
+					break;
+
+				case 'TR':
+					$text_origin_x = ImageSX($gdimg) - $text_width  + $TTFbox[0] - $min_x + round($size * 0.25) - $margin;
+					$text_origin_y = $char_height + $margin;
+					break;
+
+				case 'BL':
+					$text_origin_x = $margin;
+					$text_origin_y = ImageSY($gdimg) + $TTFbox[1] - $margin;
+					break;
+
+				case 'BR':
+				default:
+					$text_origin_x = ImageSX($gdimg) - $text_width  + $TTFbox[0] - $min_x + round($size * 0.25) - $margin;
+					$text_origin_y = ImageSY($gdimg) + $TTFbox[1] - $margin;
+					break;
+			}
+			$letter_color_text = phpthumb_functions::ImageHexColorAllocate($gdimg, $hex_color, false, $opacity * 1.27);
+
+			if ($alignment == '*') {
+
+				$text_origin_y = $char_height + $margin;
+				while (($text_origin_y - $text_height) < ImageSY($gdimg)) {
+					$text_origin_x = $margin;
+					while ($text_origin_x < ImageSX($gdimg)) {
+						ImageTTFtext($gdimg, $size, $angle, $text_origin_x, $text_origin_y, $letter_color_text, $ttffont, $text);
+						$text_origin_x += ($text_width + $margin);
+					}
+					$text_origin_y += ($text_height + $margin);
+				}
+
+			} else {
+
+				//ImageRectangle($gdimg, $text_origin_x + $min_x, $text_origin_y + $TTFbox[1], $text_origin_x + $min_x + $text_width, $text_origin_y + $TTFbox[1] - $text_height, $letter_color_text);
+				if (phpthumb_functions::IsHexColor($bg_color)) {
+					$text_background_alpha = round(127 * ((100 - min(max(0, $bg_opacity), 100)) / 100));
+					$text_color_background = phpthumb_functions::ImageHexColorAllocate($gdimg, $bg_color, false, $text_background_alpha);
+				} else {
+					$text_color_background = phpthumb_functions::ImageHexColorAllocate($gdimg, 'FFFFFF', false, 127);
+				}
+				$x1 = $text_origin_x + $min_x;
+				$y1 = $text_origin_y + $TTFbox[1];
+				$x2 = $text_origin_x + $min_x + $text_width;
+				$y2 = $text_origin_y + $TTFbox[1] - $text_height;
+				$x_TL = eregi('x', $fillextend) ?               0 : min($x1, $x2);
+				$y_TL = eregi('y', $fillextend) ?               0 : min($y1, $y2);
+				$x_BR = eregi('x', $fillextend) ? ImageSX($gdimg) : max($x1, $x2);
+				$y_BR = eregi('y', $fillextend) ? ImageSY($gdimg) : max($y1, $y2);
+				//while ($y_BR > ImageSY($gdimg)) {
+				//	$y_TL--;
+				//	$y_BR--;
+				//	$text_origin_y--;
+				//}
+				ImageFilledRectangle($gdimg, $x_TL, $y_TL, $x_BR, $y_BR, $text_color_background);
+				ImageTTFtext($gdimg, $size, $angle, $text_origin_x, $text_origin_y, $letter_color_text, $ttffont, $text);
+
+			}
+			return true;
+
+		} else {
+
+			$size = min(5, max(1, $size));
+			$this->DebugMessage('Using built-in font (size='.$size.') for text watermark'.($ttffont ? ' because $ttffont !is_readable('.$ttffont.')' : ''), __FILE__, __LINE__);
+
+			$text_width  = 0;
+			$text_height = 0;
+			foreach ($textlines as $dummy => $line) {
+				$text_width   = max($text_width, ImageFontWidth($size) * strlen($line));
+				$text_height += ImageFontHeight($size);
+			}
+			if ($img_watermark = phpthumb_functions::ImageCreateFunction($text_width, $text_height)) {
+				ImageAlphaBlending($img_watermark, false);
+				if (phpthumb_functions::IsHexColor($bg_color)) {
+					$text_background_alpha = round(127 * ((100 - min(max(0, $bg_opacity), 100)) / 100));
+					$text_color_background = phpthumb_functions::ImageHexColorAllocate($img_watermark, $bg_color, false, $text_background_alpha);
+				} else {
+					$text_color_background = phpthumb_functions::ImageHexColorAllocate($img_watermark, 'FFFFFF', false, 127);
+				}
+				ImageFilledRectangle($img_watermark, 0, 0, ImageSX($img_watermark), ImageSY($img_watermark), $text_color_background);
+
+				if ($angle && function_exists('ImageRotate')) {
+					// using $img_watermark_mask is pointless if ImageRotate function isn't available
+					if ($img_watermark_mask = phpthumb_functions::ImageCreateFunction($text_width, $text_height)) {
+						$mask_color_background = ImageColorAllocate($img_watermark_mask, 0, 0, 0);
+						ImageAlphaBlending($img_watermark_mask, false);
+						ImageFilledRectangle($img_watermark_mask, 0, 0, ImageSX($img_watermark_mask), ImageSY($img_watermark_mask), $mask_color_background);
+						$mask_color_watermark = ImageColorAllocate($img_watermark_mask, 255, 255, 255);
+					}
+				}
+
+				$text_color_watermark = phpthumb_functions::ImageHexColorAllocate($img_watermark, $hex_color);
+				foreach ($textlines as $key => $line) {
+					switch ($alignment) {
+						case 'C':
+						case 'T':
+						case 'B':
+							$x_offset = round(($text_width - (ImageFontWidth($size) * strlen($line))) / 2);
+							break;
+
+						case 'L':
+						case 'TL':
+						case 'BL':
+							$x_offset = 0;
+							break;
+
+						case 'R':
+						case 'TR':
+						case 'BR':
+						default:
+							$x_offset = $text_width - (ImageFontWidth($size) * strlen($line));
+							break;
+					}
+					ImageString($img_watermark, $size, $x_offset, $key * ImageFontHeight($size), $line, $text_color_watermark);
+					if ($angle && $img_watermark_mask) {
+						ImageString($img_watermark_mask, $size, $x_offset, $key * ImageFontHeight($size), $text, $mask_color_watermark);
+					}
+				}
+				if ($angle && $img_watermark_mask) {
+					$img_watermark      = ImageRotate($img_watermark,      $angle, $text_color_background);
+					$img_watermark_mask = ImageRotate($img_watermark_mask, $angle, $mask_color_background);
+					phpthumb_filters::ApplyMask($img_watermark_mask, $img_watermark);
+				}
+				phpthumb_filters::WatermarkOverlay($gdimg, $img_watermark, $alignment, $opacity, $margin);
+				ImageDestroy($img_watermark);
+				return true;
+			}
+
+		}
+		return false;
+	}
+
+
+	function WatermarkOverlay(&$gdimg_dest, &$img_watermark, $alignment='*', $opacity=50, $margin_x=5, $margin_y=null) {
+		if (is_resource($gdimg_dest) && is_resource($img_watermark)) {
+			$watermark_source_x        = 0;
+			$watermark_source_y        = 0;
+			$img_source_width          = ImageSX($gdimg_dest);
+			$img_source_height         = ImageSY($gdimg_dest);
+			$watermark_source_width    = ImageSX($img_watermark);
+			$watermark_source_height   = ImageSY($img_watermark);
+			$watermark_opacity_percent = max(0, min(100, $opacity));
+			$margin_y = (is_null($margin_y) ? $margin_x : $margin_y);
+			$watermark_margin_x = ((($margin_x > 0) && ($margin_x < 1)) ? round((1 - $margin_x) * $img_source_width)  : $margin_x);
+			$watermark_margin_y = ((($margin_y > 0) && ($margin_y < 1)) ? round((1 - $margin_y) * $img_source_height) : $margin_y);
+			switch ($alignment) {
+				case '*':
+					if ($gdimg_tiledwatermark = phpthumb_functions::ImageCreateFunction($img_source_width, $img_source_height)) {
+
+						ImageAlphaBlending($gdimg_tiledwatermark, false);
+						ImageSaveAlpha($gdimg_tiledwatermark, true);
+						$text_color_transparent = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_tiledwatermark, 255, 0, 255, 127);
+						ImageFill($gdimg_tiledwatermark, 0, 0, $text_color_transparent);
+
+						// set the tiled image transparent color to whatever the untiled image transparency index is
+//						ImageColorTransparent($gdimg_tiledwatermark, ImageColorTransparent($img_watermark));
+
+						// a "cleaner" way of doing it, but can't handle the margin feature :(
+//						ImageSetTile($gdimg_tiledwatermark, $img_watermark);
+//						ImageFill($gdimg_tiledwatermark, 0, 0, IMG_COLOR_TILED);
+//						break;
+
+//						ImageFill($gdimg_tiledwatermark, 0, 0, ImageColorTransparent($gdimg_tiledwatermark));
+						// tile the image as many times as can fit
+						for ($x = $watermark_margin_x; $x < ($img_source_width + $watermark_source_width); $x += ($watermark_source_width + $watermark_margin_x)) {
+							for ($y = $watermark_margin_y; $y < ($img_source_height + $watermark_source_height); $y += ($watermark_source_height + $watermark_margin_y)) {
+								ImageCopy(
+									$gdimg_tiledwatermark,
+									$img_watermark,
+									$x,
+									$y,
+									0,
+									0,
+									min($watermark_source_width,  $img_source_width  - $x - $watermark_margin_x),
+									min($watermark_source_height, $img_source_height - $y - $watermark_margin_y)
+								);
+							}
+						}
+
+						$watermark_source_width  = ImageSX($gdimg_tiledwatermark);
+						$watermark_source_height = ImageSY($gdimg_tiledwatermark);
+						$watermark_destination_x = 0;
+						$watermark_destination_y = 0;
+
+						ImageDestroy($img_watermark);
+						$img_watermark = $gdimg_tiledwatermark;
+					}
+					break;
+
+				case 'T':
+					$watermark_destination_x = round((($img_source_width  / 2) - ($watermark_source_width / 2)) + $watermark_margin_x);
+					$watermark_destination_y = $watermark_margin_y;
+					break;
+
+				case 'B':
+					$watermark_destination_x = round((($img_source_width  / 2) - ($watermark_source_width / 2)) + $watermark_margin_x);
+					$watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y;
+					break;
+
+				case 'L':
+					$watermark_destination_x = $watermark_margin_x;
+					$watermark_destination_y = round((($img_source_height / 2) - ($watermark_source_height / 2)) + $watermark_margin_y);
+					break;
+
+				case 'R':
+					$watermark_destination_x = $img_source_width - $watermark_source_width - $watermark_margin_x;
+					$watermark_destination_y = round((($img_source_height / 2) - ($watermark_source_height / 2)) + $watermark_margin_y);
+					break;
+
+				case 'C':
+					$watermark_destination_x = round(($img_source_width  / 2) - ($watermark_source_width  / 2));
+					$watermark_destination_y = round(($img_source_height / 2) - ($watermark_source_height / 2));
+					break;
+
+				case 'TL':
+					$watermark_destination_x = $watermark_margin_x;
+					$watermark_destination_y = $watermark_margin_y;
+					break;
+
+				case 'TR':
+					$watermark_destination_x = $img_source_width - $watermark_source_width - $watermark_margin_x;
+					$watermark_destination_y = $watermark_margin_y;
+					break;
+
+				case 'BL':
+//echo '<pre>';
+////var_dump($watermark_destination_x);
+////var_dump($watermark_destination_y);
+//var_dump($watermark_margin_x);
+//var_dump($img_source_height);
+//var_dump($watermark_source_height);
+//var_dump($watermark_margin_y);
+					$watermark_destination_x = $watermark_margin_x;
+					$watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y;
+					break;
+
+				case 'BR':
+				default:
+					$watermark_destination_x = $img_source_width  - $watermark_source_width  - $watermark_margin_x;
+					$watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y;
+					break;
+			}
+			ImageAlphaBlending($gdimg_dest, false);
+			ImageSaveAlpha($gdimg_dest, true);
+			ImageSaveAlpha($img_watermark, true);
+			phpthumb_functions::ImageCopyRespectAlpha($gdimg_dest, $img_watermark, $watermark_destination_x, $watermark_destination_y, 0, 0, $watermark_source_width, $watermark_source_height, $watermark_opacity_percent);
+
+			return true;
+		}
+		return false;
+	}
+
+
+	function DebugMessage($message, $file='', $line='') {
+		if (is_object($this->phpThumbObject)) {
+			return $this->phpThumbObject->DebugMessage($message, $file, $line);
+		}
+		return false;
+	}
+}
+
+?>
Index: /afridex/plugins/fresh-page/RCCWP_CustomWritePanelPage.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_CustomWritePanelPage.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_CustomWritePanelPage.php (revision 21)
@@ -0,0 +1,361 @@
+<?php
+include_once('RCCWP_CustomWritePanel.php');
+
+class RCCWP_CustomWritePanelPage
+{
+	function Content($customWritePanel = null)
+	{
+		$customWritePanelName = "";
+		$customWritePanelDescription = "";
+		$write_panel_category_ids = array();
+		
+		if ($customWritePanel != null)
+		{
+			$customWritePanelName = $customWritePanel->name;
+			$customWritePanelDescription = $customWritePanel->description;
+			$customWritePanelDisplayOrder = $customWritePanel->display_order;
+			$customWritePanelType = $customWritePanel->type;
+			$customWritePanelCategoryIds = RCCWP_CustomWritePanel::GetAssignedCategoryIds($customWritePanel->id);
+			$customWritePanelStandardFieldIds = RCCWP_CustomWritePanel::GetStandardFieldIds($customWritePanel->id);
+			$customWritePanelHiddenExtFieldCssIds = RCCWP_CustomWritePanel::GetHiddenExternalFieldCssIds($customWritePanel->id);
+		?>
+		<input type="hidden" name="custom-write-panel-id" value="<?=$_POST['custom-write-panel-id']?>" />
+		<?php
+		}
+		
+  		?>
+		<table class="optiontable" border="0">
+		<tbody>
+
+		<tr valign="top">
+			<th scope="row"> </th>
+			<td align="left">
+				<input type="radio" name="radPostPage" id="radPostPage" value="post" <?php if($customWritePanelType == 'post'){?> checked="checked" <?php } ?> onclick='showHide("Post");' /> <strong>Post </strong> &nbsp; &nbsp; &nbsp; 
+				<input type="radio" name="radPostPage" id="radPostPage" value="page" <?php if($customWritePanelType == 'page'){?> checked="checked" <?php } ?> onclick='showHide("Page");' /> <strong>Page</strong>
+			</td>
+		</tr>
+		<tr valign="top">
+			<th scope="row"  align="right">Name:</th>
+			<td><input name="custom-write-panel-name" id="custom-write-panel-name" size="40" type="text" value="<?=$customWritePanelName?>" /></td>
+		</tr>
+<!--
+		<tr valign="top">
+			<th scope="row">Description:</th>
+			<td><textarea name="custom-write-panel-description" id="custom-write-panel-description" rows="2" cols="38"><?=$customWritePanelDescription?></textarea></td>
+		</tr>
+-->
+		<tr valign="top">
+<!--
+			<th scope="row">Assigned Categories:</th>
+			<td>
+				<select multiple size="10" name="custom-write-panel-categories[]" style="width:305px">
+					<?php
+					$cats = RCCWP_Application::GetWpCategories();
+					
+					foreach ($cats as $cat) :
+						$selected = "";
+						if (in_array($cat->cat_ID, $customWritePanelCategoryIds))
+						{
+							$selected = "selected=\"selected\"";
+						}
+					?>
+						<option value="<?=$cat->cat_ID?>" <?=$selected?>><?=$cat->cat_name ?></option>
+					<?php
+					endforeach;
+					?>
+				</select>
+			</td>
+		</tr>
+-->
+				<!-- START :: Javascript for Image/Photo' Css Class -->
+				<script type="text/javascript" language="javascript">
+					function showHide(name)
+					{
+						if(name == "Page")
+						{
+							document.getElementById('catLabel').style.display = 'none';
+							document.getElementById('catText').style.display = 'none';
+						}
+						else
+						{
+							document.getElementById('catLabel').style.display = 'inline';
+							document.getElementById('catText').style.display = 'inline';
+						}
+
+					}
+				</script>
+				<!-- END :: Javascript for Image/Photo' Css Class -->
+			
+			<th scope="row"  align="right"><div id="catLabel" style="display:inline;">Assigned Categories:</div></th>
+			<td>
+				<div id="catText" style="display:inline;">
+					<?php
+					$cats = RCCWP_Application::GetWpCategories();
+		
+					foreach ($cats as $cat) : 
+						$checked = "";
+						if(isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id']))
+						{
+							if (in_array($cat->cat_ID, $customWritePanelCategoryIds))
+							{
+								$checked = "checked=\"checked\"";
+							}
+						}
+					?>
+						<input type="checkbox" name="custom-write-panel-categories[]" value="<?=$cat->cat_ID?>" <?=$checked?> /> <?=$cat->cat_name ?> <br/>
+					<?php
+					endforeach;
+					?>
+				</div>
+			</td>
+		</tr>
+<!--
+		<tr valign="top">
+			<th scope="row">Standard Fields:</th>
+			<td>
+				<select multiple size="10" name="custom-write-panel-standard-fields[]" style="width:305px">
+					<?php
+					$standard_fields = RCCWP_Application::GetWpStandardFields();
+					foreach ($standard_fields as $field) :
+						$selected = "";
+						if ($customWritePanel != null)
+						{
+							if (in_array($field->id, $customWritePanelStandardFieldIds))
+							{
+								$selected = "selected=\"selected\"";
+							}
+						}
+						else
+						{
+							if ($field->default_inclusion == 'true')
+							{
+								$selected = "selected=\"selected\""; 
+							}
+						}
+					?>
+						<option value="<?=$field->id?>" <?=$selected?>><?=$field->name?></option>
+					<?php
+					endforeach;
+					?>
+				</select>
+			</td>
+		</tr>
+	-->
+
+		<tr valign="top">
+			<th scope="row" align="right">Standard Fields:</th>
+			<td>
+					<?php
+					$standard_fields = RCCWP_Application::GetWpStandardFields();
+					foreach ($standard_fields as $field) :
+						$checked = "";
+						if ($customWritePanel != null)
+						{
+							if (in_array($field->id, $customWritePanelStandardFieldIds))
+							{
+								$checked = "checked=\"checked\"";
+							}
+						}
+						else
+						{
+							if ($field->default_inclusion == 'true')
+							{
+								$checked = "checked=\"checked\""; 
+							}
+						}
+					?>
+						<input type="checkbox" name="custom-write-panel-standard-fields[]" value="<?=$field->id?>" <?=$checked?> /> <?=$field->name?> <br />
+					<?php
+					endforeach;
+					?>
+				</select>
+			</td>
+		</tr>
+<!--
+		<tr valign="top">
+			<th scope="row">Hidden External Fields:</th>
+			<td>
+			<?php
+			$extFields = implode("\n", (array)$customWritePanelHiddenExtFieldCssIds);
+			?>
+			<textarea name="custom-write-panel-ext-fields" id="custom-write-panel-ext-fields" rows="2" cols="38"><?=$extFields?></textarea><br />
+			<em>Other plugins may add input fields in write panel. Enter the input fields' css ID here to hide them. Separate each value with a newline.</em>
+			</td>
+		</tr>
+-->
+		<tr valign="top">
+			<th scope="row" align="right">Hidden External Fields:</th>
+			<td>
+			<?php
+//			$extFields = implode("\n", (array)$customWritePanelHiddenExtFieldCssIds);
+			?>
+				<input type="checkbox" name="custom-write-panel-ext-fields[]" id="custom-write-panel-ext-fields[]" value="tagdiv" <?php if (isset($customWritePanelHiddenExtFieldCssIds)) { foreach($customWritePanelHiddenExtFieldCssIds as $item){ if($item == 'tagdiv'){ ?> checked="checked" <?php } } }?> /> Tags
+				<br/>
+				<input type="checkbox" name="custom-write-panel-ext-fields[]" id="custom-write-panel-ext-fields[]" value="wp-bookmarklet" <?php if (isset($customWritePanelHiddenExtFieldCssIds)) { foreach($customWritePanelHiddenExtFieldCssIds as $item) { if($item == 'wp-bookmarklet'){ ?> checked="checked" <?php } } }?> /> WordPress Bookmarklet
+			</td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row" align="right">Order:</th>
+			<td><input name="custom-write-panel-order" id="custom-write-panel-order" size="2" type="text" value="<?=$customWritePanelDisplayOrder?>" /></td>
+		</tr>
+		<?php
+		if (!isset($customWritePanel)) :
+		?>
+		<tr>
+			<th scope="row" align="right">Custom Fields:</th>
+			<td>Add custom fields later by editing this custom write panel.</td>
+		</tr>
+		<?php
+		endif;
+		?>
+		</tbody>
+		</table>
+		
+		<?php
+	}
+	
+	function Edit()
+	{
+		$customWritePanel = RCCWP_Application::GetCustomWritePanels((int)$_REQUEST['custom-write-panel-id']);
+		?>
+		<div class="wrap">
+		
+		<h2>Edit Custom Write Panel</h2>
+		
+		<form action="" method="post" id="edit-custom-write-panel-form">
+		
+		<?php
+		RCCWP_CustomWritePanelPage::Content($customWritePanel);
+		?>
+		
+		<p class="submit" >
+			<input name="cancel-edit-custom-write-panel" type="submit" id="cancel-edit-custom-write-panel" value="<?php _e('Cancel'); ?>" /> 
+			<input name="submit-edit-custom-write-panel" type="submit" id="submit-edit-custom-write-panel" value="<?php _e('Update'); ?>" />
+		</p>
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+	
+	function GetAssignedCategoriesString($customWritePanel)
+	{
+		$results = RCCWP_CustomWritePanel::GetAssignedCategories($customWritePanel);
+		$str = '';
+		foreach ($results as $r)
+		{
+			$str .= $r->cat_name . ', ';	
+		}
+		$str = substr($str, 0, strlen($str) - 2); // deletes last comma and whitespace
+		return $str;
+	}
+	
+	function GetStandardFieldsString($customWritePanel)
+	{
+		$results = RCCWP_CustomWritePanel::GetStandardFields($customWritePanel);
+		foreach ($results as $r)
+		{
+			$str .= $r->name . ', ';	
+		}
+		$str = substr($str, 0, strlen($str) - 2); // deletes last comma and whitespace
+		return $str;
+	}
+	
+	function View($param = 23)
+	{
+		if(isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id']) )
+			$customWritePanelId = (int)$_GET['custom-write-panel-id'];
+		if(isset($_POST['custom-write-panel-id']) && !empty($_POST['custom-write-panel-id']) )
+			$customWritePanelId = (int)$_POST['custom-write-panel-id'];
+
+		$customWritePanel = RCCWP_Application::GetCustomWritePanels($customWritePanelId);
+		?>
+
+		<div class="wrap">
+
+		<h2>Custom Write Panel Info</h2>
+
+		<form action="" method="post" id="view-write-panel-form">
+		
+		<input type="hidden" name="custom-write-panel-id" value="<?=$customWritePanelId?>" />
+			
+  		<fieldset class="options">
+		    	
+  		<table class="optiontable">
+  		<tbody>
+  		<tr>
+			<th scope="row" align="right">Name:</th>
+			<td><?=$customWritePanel->name?></td>
+		</tr>
+<!--
+		<tr>
+			<th scope="row">Description:</th>
+			<td><?=$customWritePanel->description?></td>
+		</tr>
+-->
+		<tr>
+			<th scope="row" align="right">Assigned Categories:</th>
+			<td><?=RCCWP_CustomWritePanelPage::GetAssignedCategoriesString($customWritePanelId)?></td>
+		</tr>
+		<tr>
+			<th scope="row" align="right">Standard Fields:</th>
+			<td><?=RCCWP_CustomWritePanelPage::GetStandardFieldsString($customWritePanelId)?></td>
+		</tr>
+		<tr>
+			<th scope="row" align="right">Type:</th>
+			<td><?=$customWritePanel->type;?></td>
+		</tr>
+  		</tbody>
+  		</table>
+		  
+		</fieldset>
+		
+		<p class="submit" ><input name="edit-custom-write-panel" type="submit" id="edit-custom-write-panel" value="Edit Write Panel" /></p>
+		
+		<fieldset class="options">
+		<legend>Custom Fields</legend>
+		
+  		<table cellpadding="3" cellspacing="3" width="100%" class="widefat">
+  		<thead>
+  		<tr>
+  			<th scope="col">Order</th>
+			<th scope="col">Name</th>
+			<th scope="col">Type</th>
+<!--			<th scope="col">Description</th> -->
+			<th scope="col" colspan="2">Action</th>
+		</tr>
+  		</thead>
+  		<tbody>
+  		<?php
+  		$custom_fields = RCCWP_CustomWritePanel::GetCustomFields($customWritePanelId);
+  		foreach ($custom_fields as $field) :
+  			$class = $class == '' ? 'alternate' : '';
+  		?>
+  			<tr class="<?=$class?>">
+  				<td align="right" width="3"><?=$field->display_order?></td>
+  				<td align="center"><?=$field->name?></td>
+  				<td><?=$field->type?></td>
+<!--  				<td><?=$field->description?></td> -->
+  				<td><a href="<?=RCCWP_ManagementPage::GetCustomFieldEditUrl($customWritePanelId, $field->id)?>" class="edit">Edit</a></td>
+  				<td><a href="<?=RCCWP_ManagementPage::GetCustomFieldDeleteUrl($customWritePanelId, $field->id)?>" class="delete">Delete</a></td>
+  			</tr>
+  		<?php
+  		endforeach;
+  		?>
+  		</tbody>
+  		</table>
+		  
+		</fieldset>
+		
+		<p class="submit" ><input name="create-custom-field" type="submit" id="create-custom-field" value="Create Custom Field" /></p>
+		
+		</form>
+
+		</div>
+		
+		<?php
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/phpthumb.ico.php
===================================================================
--- /afridex/plugins/fresh-page/phpthumb.ico.php (revision 21)
+++ /afridex/plugins/fresh-page/phpthumb.ico.php (revision 21)
@@ -0,0 +1,119 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// phpthumb.ico.php - .ICO output format functions          //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+
+class phpthumb_ico {
+
+	function phpthumb_ico() {
+		return true;
+	}
+
+
+	function GD2ICOstring(&$gd_image_array) {
+		foreach ($gd_image_array as $key => $gd_image) {
+
+			$ImageWidths[$key]  = ImageSX($gd_image);
+			$ImageHeights[$key] = ImageSY($gd_image);
+	    	$bpp[$key]          = ImageIsTrueColor($gd_image) ? 32 : 24;
+	    	$totalcolors[$key]  = ImageColorsTotal($gd_image);
+
+			$icXOR[$key] = '';
+			for ($y = $ImageHeights[$key] - 1; $y >= 0; $y--) {
+				for ($x = 0; $x < $ImageWidths[$key]; $x++) {
+					$argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y);
+					$a = round(255 * ((127 - $argb['alpha']) / 127));
+					$r = $argb['red'];
+					$g = $argb['green'];
+					$b = $argb['blue'];
+
+					if ($bpp[$key] == 32) {
+						$icXOR[$key] .= chr($b).chr($g).chr($r).chr($a);
+					} elseif ($bpp[$key] == 24) {
+						$icXOR[$key] .= chr($b).chr($g).chr($r);
+					}
+
+					if ($a < 128) {
+						@$icANDmask[$key][$y] .= '1';
+					} else {
+						@$icANDmask[$key][$y] .= '0';
+					}
+				}
+				// mask bits are 32-bit aligned per scanline
+				while (strlen($icANDmask[$key][$y]) % 32) {
+					$icANDmask[$key][$y] .= '0';
+				}
+			}
+			$icAND[$key] = '';
+			foreach ($icANDmask[$key] as $y => $scanlinemaskbits) {
+				for ($i = 0; $i < strlen($scanlinemaskbits); $i += 8) {
+					$icAND[$key] .= chr(bindec(str_pad(substr($scanlinemaskbits, $i, 8), 8, '0', STR_PAD_LEFT)));
+				}
+			}
+
+		}
+
+	    foreach ($gd_image_array as $key => $gd_image) {
+			$biSizeImage = $ImageWidths[$key] * $ImageHeights[$key] * ($bpp[$key] / 8);
+
+	    	// BITMAPINFOHEADER - 40 bytes
+			$BitmapInfoHeader[$key]  = '';
+			$BitmapInfoHeader[$key] .= "\x28\x00\x00\x00";                              // DWORD  biSize;
+			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageWidths[$key], 4);      // LONG   biWidth;
+			// The biHeight member specifies the combined
+			// height of the XOR and AND masks.
+			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageHeights[$key] * 2, 4); // LONG   biHeight;
+	    	$BitmapInfoHeader[$key] .= "\x01\x00";                                      // WORD   biPlanes;
+	   		$BitmapInfoHeader[$key] .= chr($bpp[$key])."\x00";                          // wBitCount;
+			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // DWORD  biCompression;
+			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($biSizeImage, 4);            // DWORD  biSizeImage;
+	    	$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // LONG   biXPelsPerMeter;
+	    	$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // LONG   biYPelsPerMeter;
+	    	$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // DWORD  biClrUsed;
+	    	$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // DWORD  biClrImportant;
+		}
+
+
+		$icondata  = "\x00\x00";                                      // idReserved;   // Reserved (must be 0)
+		$icondata .= "\x01\x00";                                      // idType;       // Resource Type (1 for icons)
+		$icondata .= phpthumb_functions::LittleEndian2String(count($gd_image_array), 2);  // idCount;      // How many images?
+
+		$dwImageOffset = 6 + (count($gd_image_array) * 16);
+		foreach ($gd_image_array as $key => $gd_image) {
+	    	// ICONDIRENTRY   idEntries[1]; // An entry for each image (idCount of 'em)
+
+	    	$icondata .= chr($ImageWidths[$key]);                     // bWidth;          // Width, in pixels, of the image
+	    	$icondata .= chr($ImageHeights[$key]);                    // bHeight;         // Height, in pixels, of the image
+	    	$icondata .= chr($totalcolors[$key]);                     // bColorCount;     // Number of colors in image (0 if >=8bpp)
+	    	$icondata .= "\x00";                                      // bReserved;       // Reserved ( must be 0)
+
+	    	$icondata .= "\x01\x00";                                  // wPlanes;         // Color Planes
+			$icondata .= chr($bpp[$key])."\x00";                      // wBitCount;       // Bits per pixel
+
+			$dwBytesInRes = 40 + strlen($icXOR[$key]) + strlen($icAND[$key]);
+			$icondata .= phpthumb_functions::LittleEndian2String($dwBytesInRes, 4);       // dwBytesInRes;    // How many bytes in this resource?
+
+		    $icondata .= phpthumb_functions::LittleEndian2String($dwImageOffset, 4);      // dwImageOffset;   // Where in the file is this image?
+			$dwImageOffset += strlen($BitmapInfoHeader[$key]);
+			$dwImageOffset += strlen($icXOR[$key]);
+			$dwImageOffset += strlen($icAND[$key]);
+	    }
+
+	    foreach ($gd_image_array as $key => $gd_image) {
+			$icondata .= $BitmapInfoHeader[$key];
+			$icondata .= $icXOR[$key];
+			$icondata .= $icAND[$key];
+	    }
+
+	    return $icondata;
+	}
+
+}
+
+?>
Index: /afridex/plugins/fresh-page/phpthumb.bmp.php
===================================================================
--- /afridex/plugins/fresh-page/phpthumb.bmp.php (revision 21)
+++ /afridex/plugins/fresh-page/phpthumb.bmp.php (revision 21)
@@ -0,0 +1,874 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.bmp.php                                      //
+// module for analyzing BMP Image files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// Modified for use in phpThumb() - James Heinrich 2004.07.27  //
+//                                                             //
+/////////////////////////////////////////////////////////////////
+
+
+class phpthumb_bmp {
+
+	function phpthumb_bmp() {
+		return true;
+	}
+
+	function phpthumb_bmp2gd(&$BMPdata, $truecolor=true) {
+		$ThisFileInfo = array();
+		if ($this->getid3_bmp($BMPdata, $ThisFileInfo, true, true)) {
+			$gd = $this->PlotPixelsGD($ThisFileInfo['bmp'], $truecolor);
+			return $gd;
+		}
+		return false;
+	}
+
+	function phpthumb_bmpfile2gd($filename, $truecolor=true) {
+		if ($fp = @fopen($filename, 'rb')) {
+			$BMPdata = fread($fp, filesize($filename));
+			fclose($fp);
+			return $this->phpthumb_bmp2gd($BMPdata, $truecolor);
+		}
+		return false;
+	}
+
+	function GD2BMPstring(&$gd_image) {
+		$imageX = ImageSX($gd_image);
+		$imageY = ImageSY($gd_image);
+
+		$BMP = '';
+		for ($y = ($imageY - 1); $y >= 0; $y--) {
+			$thisline = '';
+			for ($x = 0; $x < $imageX; $x++) {
+				$argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y);
+				$thisline .= chr($argb['blue']).chr($argb['green']).chr($argb['red']);
+			}
+			while (strlen($thisline) % 4) {
+				$thisline .= "\x00";
+			}
+			$BMP .= $thisline;
+		}
+
+		$bmpSize = strlen($BMP) + 14 + 40;
+		// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
+		$BITMAPFILEHEADER  = 'BM';                                                           // WORD    bfType;
+		$BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String($bmpSize, 4); // DWORD   bfSize;
+		$BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String(       0, 2); // WORD    bfReserved1;
+		$BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String(       0, 2); // WORD    bfReserved2;
+		$BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String(      54, 4); // DWORD   bfOffBits;
+
+		// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
+		$BITMAPINFOHEADER  = phpthumb_functions::LittleEndian2String(      40, 4); // DWORD  biSize;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( $imageX, 4); // LONG   biWidth;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( $imageY, 4); // LONG   biHeight;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       1, 2); // WORD   biPlanes;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(      24, 2); // WORD   biBitCount;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       0, 4); // DWORD  biCompression;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       0, 4); // DWORD  biSizeImage;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(    2835, 4); // LONG   biXPelsPerMeter;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(    2835, 4); // LONG   biYPelsPerMeter;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       0, 4); // DWORD  biClrUsed;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       0, 4); // DWORD  biClrImportant;
+
+		return $BITMAPFILEHEADER.$BITMAPINFOHEADER.$BMP;
+	}
+
+	function getid3_bmp(&$BMPdata, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) {
+
+	    // shortcuts
+	    $ThisFileInfo['bmp']['header']['raw'] = array();
+	    $thisfile_bmp                         = &$ThisFileInfo['bmp'];
+	    $thisfile_bmp_header                  = &$thisfile_bmp['header'];
+	    $thisfile_bmp_header_raw              = &$thisfile_bmp_header['raw'];
+
+		// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
+		// all versions
+		// WORD    bfType;
+		// DWORD   bfSize;
+		// WORD    bfReserved1;
+		// WORD    bfReserved2;
+		// DWORD   bfOffBits;
+
+		$offset = 0;
+		$overalloffset = 0;
+		$BMPheader = substr($BMPdata, $overalloffset, 14 + 40);
+		$overalloffset += (14 + 40);
+
+		$thisfile_bmp_header_raw['identifier']  = substr($BMPheader, $offset, 2);
+		$offset += 2;
+
+		if ($thisfile_bmp_header_raw['identifier'] != 'BM') {
+			$ThisFileInfo['error'][] = 'Expecting "BM" at offset '.intval(@$ThisFileInfo['avdataoffset']).', found "'.$thisfile_bmp_header_raw['identifier'].'"';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['bmp']);
+			return false;
+		}
+
+		$thisfile_bmp_header_raw['filesize']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+		$thisfile_bmp_header_raw['reserved1']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+		$offset += 2;
+		$thisfile_bmp_header_raw['reserved2']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+		$offset += 2;
+		$thisfile_bmp_header_raw['data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+		$thisfile_bmp_header_raw['header_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+
+
+		// check if the hardcoded-to-1 "planes" is at offset 22 or 26
+		$planes22 = $this->LittleEndian2Int(substr($BMPheader, 22, 2));
+		$planes26 = $this->LittleEndian2Int(substr($BMPheader, 26, 2));
+		if (($planes22 == 1) && ($planes26 != 1)) {
+			$thisfile_bmp['type_os']      = 'OS/2';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif (($planes26 == 1) && ($planes22 != 1)) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
+			$thisfile_bmp['type_os']      = 'OS/2';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 4;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 5;
+		} else {
+			$ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['bmp']);
+			return false;
+		}
+
+		$ThisFileInfo['fileformat']                  = 'bmp';
+		$ThisFileInfo['video']['dataformat']         = 'bmp';
+		$ThisFileInfo['video']['lossless']           = true;
+		$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+		if ($thisfile_bmp['type_os'] == 'OS/2') {
+
+			// OS/2-format BMP
+			// http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
+
+			// DWORD  Size;             /* Size of this structure in bytes */
+			// DWORD  Width;            /* Bitmap width in pixels */
+			// DWORD  Height;           /* Bitmap height in pixel */
+			// WORD   NumPlanes;        /* Number of bit planes (color depth) */
+			// WORD   BitsPerPixel;     /* Number of bits per pixel per plane */
+
+			$thisfile_bmp_header_raw['width']          = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['height']         = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['planes']         = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['bits_per_pixel'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+
+			$ThisFileInfo['video']['resolution_x']    = $thisfile_bmp_header_raw['width'];
+			$ThisFileInfo['video']['resolution_y']    = $thisfile_bmp_header_raw['height'];
+			$ThisFileInfo['video']['codec']           = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			$ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
+
+			if ($thisfile_bmp['type_version'] >= 2) {
+				// DWORD  Compression;      /* Bitmap compression scheme */
+				// DWORD  ImageDataSize;    /* Size of bitmap data in bytes */
+				// DWORD  XResolution;      /* X resolution of display device */
+				// DWORD  YResolution;      /* Y resolution of display device */
+				// DWORD  ColorsUsed;       /* Number of color table indices used */
+				// DWORD  ColorsImportant;  /* Number of important color indices */
+				// WORD   Units;            /* Type of units used to measure resolution */
+				// WORD   Reserved;         /* Pad structure to 4-byte boundary */
+				// WORD   Recording;        /* Recording algorithm */
+				// WORD   Rendering;        /* Halftoning algorithm used */
+				// DWORD  Size1;            /* Reserved for halftoning algorithm use */
+				// DWORD  Size2;            /* Reserved for halftoning algorithm use */
+				// DWORD  ColorEncoding;    /* Color model used in bitmap */
+				// DWORD  Identifier;       /* Reserved for application use */
+
+				$thisfile_bmp_header_raw['compression']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['bmp_data_size']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_h']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_v']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['colors_used']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_units'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['reserved1']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['recording']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['rendering']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['size1']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['size2']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['color_encoding']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['identifier']       = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+
+				$thisfile_bmp_header['compression']          = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
+
+				$ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			}
+
+		} elseif ($thisfile_bmp['type_os'] == 'Windows') {
+
+			// Windows-format BMP
+
+			// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
+			// all versions
+			// DWORD  biSize;
+			// LONG   biWidth;
+			// LONG   biHeight;
+			// WORD   biPlanes;
+			// WORD   biBitCount;
+			// DWORD  biCompression;
+			// DWORD  biSizeImage;
+			// LONG   biXPelsPerMeter;
+			// LONG   biYPelsPerMeter;
+			// DWORD  biClrUsed;
+			// DWORD  biClrImportant;
+
+			$thisfile_bmp_header_raw['width']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['height']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['planes']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['bits_per_pixel']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['compression']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['bmp_data_size']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['resolution_h']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['resolution_v']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['colors_used']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+
+			$thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
+			$ThisFileInfo['video']['resolution_x']    = $thisfile_bmp_header_raw['width'];
+			$ThisFileInfo['video']['resolution_y']    = $thisfile_bmp_header_raw['height'];
+			$ThisFileInfo['video']['codec']           = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			$ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
+
+			if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
+				// should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
+				$BMPheader .= substr($BMPdata, $overalloffset, 44);
+				$overalloffset += 44;
+
+				// BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
+				// Win95+, WinNT4.0+
+				// DWORD        bV4RedMask;
+				// DWORD        bV4GreenMask;
+				// DWORD        bV4BlueMask;
+				// DWORD        bV4AlphaMask;
+				// DWORD        bV4CSType;
+				// CIEXYZTRIPLE bV4Endpoints;
+				// DWORD        bV4GammaRed;
+				// DWORD        bV4GammaGreen;
+				// DWORD        bV4GammaBlue;
+				$thisfile_bmp_header_raw['red_mask']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['green_mask']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['blue_mask']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['alpha_mask']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['cs_type']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_red']   =                         substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_green'] =                         substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_blue']  =                         substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_red']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_green']  = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_blue']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+
+				$thisfile_bmp_header['ciexyz_red']   = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
+				$thisfile_bmp_header['ciexyz_green'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
+				$thisfile_bmp_header['ciexyz_blue']  = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
+			}
+
+			if ($thisfile_bmp['type_version'] >= 5) {
+				$BMPheader .= substr($BMPdata, $overalloffset, 16);
+				$overalloffset += 16;
+
+				// BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
+				// Win98+, Win2000+
+				// DWORD        bV5Intent;
+				// DWORD        bV5ProfileData;
+				// DWORD        bV5ProfileSize;
+				// DWORD        bV5Reserved;
+				$thisfile_bmp_header_raw['intent']              = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['profile_data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['profile_data_size']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['reserved3']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+			}
+
+		} else {
+
+			$ThisFileInfo['error'][] = 'Unknown BMP format in header.';
+			return false;
+
+		}
+
+		if ($ExtractPalette || $ExtractData) {
+			$PaletteEntries = 0;
+			if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
+				$PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
+			} elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
+				$PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
+			}
+			if ($PaletteEntries > 0) {
+				$BMPpalette = substr($BMPdata, $overalloffset, 4 * $PaletteEntries);
+				$overalloffset += 4 * $PaletteEntries;
+
+				$paletteoffset = 0;
+				for ($i = 0; $i < $PaletteEntries; $i++) {
+					// RGBQUAD          - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
+					// BYTE    rgbBlue;
+					// BYTE    rgbGreen;
+					// BYTE    rgbRed;
+					// BYTE    rgbReserved;
+					$blue  = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					$green = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					$red   = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
+						// no padding byte
+					} else {
+						$paletteoffset++; // padding byte
+					}
+					$thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | ($blue));
+				}
+			}
+		}
+
+		if ($ExtractData) {
+			$RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
+
+			$BMPpixelData = substr($BMPdata, $thisfile_bmp_header_raw['data_offset'], $thisfile_bmp_header_raw['height'] * $RowByteLength);
+			$overalloffset = $thisfile_bmp_header_raw['data_offset'] + ($thisfile_bmp_header_raw['height'] * $RowByteLength);
+
+			$pixeldataoffset = 0;
+			switch (@$thisfile_bmp_header_raw['compression']) {
+
+				case 0: // BI_RGB
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 1:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
+									$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+									for ($i = 7; $i >= 0; $i--) {
+										$paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+										$col++;
+									}
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 4:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
+									$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+									for ($i = 1; $i >= 0; $i--) {
+										$paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+										$col++;
+									}
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 8:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$paletteindex = ord($BMPpixelData{$pixeldataoffset++});
+									$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 24:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+									$pixeldataoffset += 3;
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 32:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+									$pixeldataoffset += 4;
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 16:
+							// ?
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 8:
+							$pixelcounter = 0;
+							while ($pixeldataoffset < strlen($BMPpixelData)) {
+								$firstbyte  = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								$secondbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								if ($firstbyte == 0) {
+
+									// escaped/absolute mode - the first byte of the pair can be set to zero to
+									// indicate an escape character that denotes the end of a line, the end of
+									// a bitmap, or a delta, depending on the value of the second byte.
+									switch ($secondbyte) {
+										case 0:
+											// end of line
+											// no need for special processing, just ignore
+											break;
+
+										case 1:
+											// end of bitmap
+											$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
+											break;
+
+										case 2:
+											// delta - The 2 bytes following the escape contain unsigned values
+											// indicating the horizontal and vertical offsets of the next pixel
+											// from the current position.
+											$colincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$rowincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
+											$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
+											$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
+											break;
+
+										default:
+											// In absolute mode, the first byte is zero and the second byte is a
+											// value in the range 03H through FFH. The second byte represents the
+											// number of bytes that follow, each of which contains the color index
+											// of a single pixel. Each run must be aligned on a word boundary.
+											for ($i = 0; $i < $secondbyte; $i++) {
+												$paletteindex = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+												$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+												$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+												$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+												$pixelcounter++;
+											}
+											while (($pixeldataoffset % 2) != 0) {
+												// Each run must be aligned on a word boundary.
+												$pixeldataoffset++;
+											}
+											break;
+									}
+
+								} else {
+
+									// encoded mode - the first byte specifies the number of consecutive pixels
+									// to be drawn using the color index contained in the second byte.
+									for ($i = 0; $i < $firstbyte; $i++) {
+										$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+										$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
+										$pixelcounter++;
+									}
+
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+
+				case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 4:
+							$pixelcounter = 0;
+							while ($pixeldataoffset < strlen($BMPpixelData)) {
+								$firstbyte  = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								$secondbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								if ($firstbyte == 0) {
+
+									// escaped/absolute mode - the first byte of the pair can be set to zero to
+									// indicate an escape character that denotes the end of a line, the end of
+									// a bitmap, or a delta, depending on the value of the second byte.
+									switch ($secondbyte) {
+										case 0:
+											// end of line
+											// no need for special processing, just ignore
+											break;
+
+										case 1:
+											// end of bitmap
+											$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
+											break;
+
+										case 2:
+											// delta - The 2 bytes following the escape contain unsigned values
+											// indicating the horizontal and vertical offsets of the next pixel
+											// from the current position.
+											$colincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$rowincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
+											$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
+											$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
+											break;
+
+										default:
+											// In absolute mode, the first byte is zero. The second byte contains the number
+											// of color indexes that follow. Subsequent bytes contain color indexes in their
+											// high- and low-order 4 bits, one color index for each pixel. In absolute mode,
+											// each run must be aligned on a word boundary.
+											unset($paletteindexes);
+											for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
+												$paletteindexbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+												$paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
+												$paletteindexes[] = ($paletteindexbyte & 0x0F);
+											}
+											while (($pixeldataoffset % 2) != 0) {
+												// Each run must be aligned on a word boundary.
+												$pixeldataoffset++;
+											}
+
+											foreach ($paletteindexes as $dummy => $paletteindex) {
+												$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+												$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+												$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+												$pixelcounter++;
+											}
+											break;
+									}
+
+								} else {
+
+									// encoded mode - the first byte of the pair contains the number of pixels to be
+									// drawn using the color indexes in the second byte. The second byte contains two
+									// color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
+									// The first of the pixels is drawn using the color specified by the high-order
+									// 4 bits, the second is drawn using the color in the low-order 4 bits, the third
+									// is drawn using the color in the high-order 4 bits, and so on, until all the
+									// pixels specified by the first byte have been drawn.
+									$paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
+									$paletteindexes[1] = ($secondbyte & 0x0F);
+									for ($i = 0; $i < $firstbyte; $i++) {
+										$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+										$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]];
+										$pixelcounter++;
+									}
+
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				case 3: // BI_BITFIELDS
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 16:
+						case 32:
+							$redshift   = 0;
+							$greenshift = 0;
+							$blueshift  = 0;
+							if (!$thisfile_bmp_header_raw['red_mask'] || !$thisfile_bmp_header_raw['green_mask'] || !$thisfile_bmp_header_raw['blue_mask']) {
+								$ThisFileInfo['error'][] = 'missing $thisfile_bmp_header_raw[(red|green|blue)_mask]';
+								return false;
+							}
+							while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
+								$redshift++;
+							}
+							while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
+								$greenshift++;
+							}
+							while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
+								$blueshift++;
+							}
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$pixelvalue = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
+									$pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
+
+									$red   = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask'])   >> $redshift)   / ($thisfile_bmp_header_raw['red_mask']   >> $redshift))   * 255));
+									$green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255));
+									$blue  = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask'])  >> $blueshift)  / ($thisfile_bmp_header_raw['blue_mask']  >> $blueshift))  * 255));
+									$thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue));
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				default: // unhandled compression type
+					$ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data';
+					break;
+			}
+		}
+
+		return true;
+	}
+
+	function IntColor2RGB($color) {
+		$red   = ($color & 0x00FF0000) >> 16;
+		$green = ($color & 0x0000FF00) >> 8;
+		$blue  = ($color & 0x000000FF);
+		return array($red, $green, $blue);
+	}
+
+	function PlotPixelsGD(&$BMPdata, $truecolor=true) {
+		$imagewidth  = $BMPdata['header']['raw']['width'];
+		$imageheight = $BMPdata['header']['raw']['height'];
+
+		if ($truecolor) {
+
+			$gd = @ImageCreateTrueColor($imagewidth, $imageheight);
+
+		} else {
+
+			$gd = @ImageCreate($imagewidth, $imageheight);
+			if (!empty($BMPdata['palette'])) {
+				// create GD palette from BMP palette
+				foreach ($BMPdata['palette'] as $dummy => $color) {
+					list($r, $g, $b) = $this->IntColor2RGB($color);
+					ImageColorAllocate($gd, $r, $g, $b);
+				}
+			} else {
+				// create 216-color websafe palette
+				for ($r = 0x00; $r <= 0xFF; $r += 0x33) {
+					for ($g = 0x00; $g <= 0xFF; $g += 0x33) {
+						for ($b = 0x00; $b <= 0xFF; $b += 0x33) {
+							ImageColorAllocate($gd, $r, $g, $b);
+						}
+					}
+				}
+			}
+
+		}
+		if (!is_resource($gd)) {
+			return false;
+		}
+
+		foreach ($BMPdata['data'] as $row => $colarray) {
+			@set_time_limit(30);
+			foreach ($colarray as $col => $color) {
+				list($red, $green, $blue) = $this->IntColor2RGB($color);
+				if ($truecolor) {
+					$pixelcolor = ImageColorAllocate($gd, $red, $green, $blue);
+				} else {
+					$pixelcolor = ImageColorClosest($gd, $red, $green, $blue);
+				}
+				ImageSetPixel($gd, $col, $row, $pixelcolor);
+			}
+		}
+		return $gd;
+	}
+
+	function PlotBMP(&$BMPinfo) {
+		$starttime = time();
+		if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) {
+			echo 'ERROR: no pixel data<BR>';
+			return false;
+		}
+		set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000)));
+		$im = $this->PlotPixelsGD($BMPinfo['bmp']);
+		if (headers_sent()) {
+			echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds<BR>';
+			ImageDestroy($im);
+			exit;
+		} else {
+			header('Content-Type: image/png');
+			ImagePNG($im);
+			ImageDestroy($im);
+			return true;
+		}
+		return false;
+	}
+
+	function BMPcompressionWindowsLookup($compressionid) {
+		static $BMPcompressionWindowsLookup = array(
+			0 => 'BI_RGB',
+			1 => 'BI_RLE8',
+			2 => 'BI_RLE4',
+			3 => 'BI_BITFIELDS',
+			4 => 'BI_JPEG',
+			5 => 'BI_PNG'
+		);
+		return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid');
+	}
+
+	function BMPcompressionOS2Lookup($compressionid) {
+		static $BMPcompressionOS2Lookup = array(
+			0 => 'BI_RGB',
+			1 => 'BI_RLE8',
+			2 => 'BI_RLE4',
+			3 => 'Huffman 1D',
+			4 => 'BI_RLE24',
+		);
+		return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid');
+	}
+
+
+	// from getid3.lib.php
+
+	function trunc($floatnumber) {
+		// truncates a floating-point number at the decimal point
+		// returns int (if possible, otherwise float)
+		if ($floatnumber >= 1) {
+			$truncatednumber = floor($floatnumber);
+		} elseif ($floatnumber <= -1) {
+			$truncatednumber = ceil($floatnumber);
+		} else {
+			$truncatednumber = 0;
+		}
+		if ($truncatednumber <= 1073741824) { // 2^30
+			$truncatednumber = (int) $truncatednumber;
+		}
+		return $truncatednumber;
+	}
+
+	function LittleEndian2Int($byteword) {
+		$intvalue = 0;
+		$byteword = strrev($byteword);
+		$bytewordlen = strlen($byteword);
+		for ($i = 0; $i < $bytewordlen; $i++) {
+			$intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
+		}
+		return $intvalue;
+	}
+
+	function BigEndian2Int($byteword) {
+		return $this->LittleEndian2Int(strrev($byteword));
+	}
+
+	function BigEndian2Bin($byteword) {
+		$binvalue = '';
+		$bytewordlen = strlen($byteword);
+		for ($i = 0; $i < $bytewordlen; $i++) {
+			$binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
+		}
+		return $binvalue;
+	}
+
+	function FixedPoint2_30($rawdata) {
+		$binarystring = $this->BigEndian2Bin($rawdata);
+		return $this->Bin2Dec(substr($binarystring, 0, 2)) + (float) ($this->Bin2Dec(substr($binarystring, 2, 30)) / 1073741824);
+	}
+
+	function Bin2Dec($binstring, $signed=false) {
+		$signmult = 1;
+		if ($signed) {
+			if ($binstring{0} == '1') {
+				$signmult = -1;
+			}
+			$binstring = substr($binstring, 1);
+		}
+		$decvalue = 0;
+		for ($i = 0; $i < strlen($binstring); $i++) {
+			$decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
+		}
+		return $this->CastAsInt($decvalue * $signmult);
+	}
+
+	function CastAsInt($floatnum) {
+		// convert to float if not already
+		$floatnum = (float) $floatnum;
+
+		// convert a float to type int, only if possible
+		if ($this->trunc($floatnum) == $floatnum) {
+			// it's not floating point
+			if ($floatnum <= 1073741824) { // 2^30
+				// it's within int range
+				$floatnum = (int) $floatnum;
+			}
+		}
+		return $floatnum;
+	}
+
+}
+
+?>
Index: /afridex/plugins/fresh-page/RCCWP_CustomFieldPage.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_CustomFieldPage.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_CustomFieldPage.php (revision 21)
@@ -0,0 +1,200 @@
+<?php
+class RCCWP_CustomFieldPage
+{
+	function Edit()
+	{
+		$custom_field = RCCWP_CustomField::Get((int)$_GET['edit-custom-field']);
+  		?>
+	  	
+  		<div class="wrap">
+<?php
+
+?>
+  		<h2>Edit Custom Field</h2>
+	  	
+  		<form action="" method="post" id="edit-custom-field-form">
+  		<input type="hidden" name="custom-field-id" value="<?=$custom_field->id?>"
+		<fieldset class="options">
+		
+		<table class="optiontable">
+		<tbody>
+		<tr valign="top">
+			<th scope="row">Name:</th>
+			<td><input name="custom-field-name" id="custom-field-name" size="40" type="text" value="<?=$custom_field->name?>" /></td>
+		</tr>
+		<tr valign="top">
+			<th scope="row">Label:</th>
+			<td><input name="custom-field-description" id="custom-field-description" size="40" type="text" value="<?=$custom_field->description?>" /></td>
+		</tr>
+<!--		<tr valign="top">
+			<th scope="row">Description:</th>
+			<td><textarea name="custom-field-description" id="custom-field-description" rows="2" cols="38"><?=$custom_field->description?></textarea></td>
+		</tr>
+-->
+		<tr valign="top">
+			<th scope="row">Order:</th>
+			<td>
+				<input name="custom-field-order" id="custom-field-order" size="2" type="text" value="<?=$custom_field->display_order?>" /></td>
+			</td>
+		</tr>
+		
+		<?php if (in_array($custom_field->type, array('Textbox', 'Listbox'))) : ?>
+		<tr valign="top">
+			<th scope="row">Size:</th>
+			<td><input type="text" name="custom-field-size" id="custom-field-size" size="2" value="<?=$custom_field->properties['size']?>" /></td>
+		</tr>	
+		<?php endif; ?>
+
+		<?php if (in_array($custom_field->type, array('Multiline Textbox'))) : ?>
+		<tr valign="top">
+			<th scope="row">Height:</th>
+			<td><input type="text" name="custom-field-height" id="custom-field-height" size="2" value="<?=$custom_field->properties['height']?>" /></td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row">Width:</th>
+			<td><input type="text" name="custom-field-width" id="custom-field-width" size="2" value="<?=$custom_field->properties['width']?>" /></td>
+		</tr>	
+		<?php endif; ?>
+
+
+<!-- START -->
+		<?php if (in_array($custom_field->type, array('Image'))) : ?>
+	<!--
+		<?php 
+			$size = explode("&",$custom_field->properties['params']);
+				$h = substr($size[1], 2);
+				$w = substr($size[2], 2);
+			$cssVlaue = $custom_field->CSS;
+		?>
+		<tr valign="top">
+			<th scope="row"><span id="lblHeight" style="display:inline;">Height:</span></th>
+			<td><span id="txtHeight" style="display:inline;"><input type="text" name="custom-field-photo-height" id="custom-field-photo-height" size="3" value="<?=$h; ?>" /></span></td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row"><span id="lblWidth" style="display:inline;">Width:</span></th>
+			<td><span id="txtWidth" style="display:inline;"><input type="text" name="custom-field-photo-width" id="custom-field-photo-width" size="3" value="<?=$w; ?>" /></span></td>
+		</tr>
+		-->
+		<?php endif; ?>
+
+<!-- END -->
+
+		<?php
+		if ($custom_field->has_options == "true") :
+			$options = implode("\n", (array)$custom_field->options)
+		?>
+		<tr valign="top">
+			<th scope="row">Options:</th>
+			<td>
+				<textarea name="custom-field-options" id="custom-field-options" rows="2" cols="38"><?=$options?></textarea><br />
+				<em>Separate each option with a newline.</em>
+			</td>
+		</tr>
+		<tr valign="top">
+			<th scope="row">Default Value:</th>
+			<td>
+				<?php
+				$default_value = implode("\n", (array)$custom_field->default_value);
+				if ($custom_field->allow_multiple_values == "true") :
+				?>
+				<textarea name="custom-field-default-value" id="custom-field-default-value" rows="2" cols="38"><?=$default_value?></textarea><br />
+				<em>Separate each value with a newline.</em>
+				<?php
+				else:
+				?>
+				<input type="text" name="custom-field-default-value" id="custom-field-default-value" size="25" value="<?=$default_value?>" />
+				<?php
+				endif;
+				?>
+			</td>
+		</tr>
+		<?php
+		endif;
+		?>
+		
+		<tr valign="top">
+			<th scope="row">Type:</th>
+			<td>
+
+				<!-- START :: Javascript for Image/Photo' Css Class -->
+				<script type="text/javascript" language="javascript">
+					function fun(name)
+					{
+						if(name == "Image")
+						{
+							document.getElementById('divCSS').style.display = 'inline';
+							document.getElementById('divLbl').style.display = 'inline';
+							document.getElementById('lblHeight').style.display = 'inline';
+							document.getElementById('txtHeight').style.display = 'inline';
+							document.getElementById('lblWidth').style.display = 'inline';
+							document.getElementById('txtWidth').style.display = 'inline';
+						}
+						else
+						{
+							document.getElementById('divCSS').style.display = 'none';
+							document.getElementById('divLbl').style.display = 'none';
+							document.getElementById('lblHeight').style.display = 'none';
+							document.getElementById('txtHeight').style.display = 'none';
+							document.getElementById('lblWidth').style.display = 'none';
+							document.getElementById('txtWidth').style.display = 'none';
+						}
+					}
+				</script>
+				<!-- END :: Javascript for Image/Photo' Css Class -->
+
+				<?php
+				$field_types = RCCWP_CustomField::GetCustomFieldTypes();
+				foreach ($field_types as $field) :
+					$checked = 
+						$field->name == $custom_field->type ?
+						'checked="checked"' : '';
+				?>
+					<label><input name="custom-field-type" value="<?=$field->id?>" type="radio" <?=$checked?> onclick='fun("<?=$field->name?>");'/>
+					<?=$field->name?></label><br />
+				<?php
+				endforeach;
+				?>
+			</td>
+		</tr>
+		<!-- START :: For Image/Photo' Css -->
+		<?php
+			$isDisplay = $custom_field->type == "Image" ? 'display:inline;' : 'display:none;';
+		?>
+		<?php 
+			$size = explode("&",$custom_field->properties['params']);
+				$h = substr($size[1], 2);
+				$w = substr($size[2], 2);
+			$cssVlaue = $custom_field->CSS;
+		?>
+		<tr valign="top">
+			<th scope="row"><span id="lblHeight" style="<?php echo $isDisplay;?>">Max Height:</span></th>
+			<td><span id="txtHeight" style="<?php echo $isDisplay;?>"><input type="text" name="custom-field-photo-height" id="custom-field-photo-height" size="3" value="<?=$h; ?>" /></span></td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row"><span id="lblWidth" style="<?php echo $isDisplay;?>">Max Width:</span></th>
+			<td><span id="txtWidth" style="<?php echo $isDisplay;?>"><input type="text" name="custom-field-photo-width" id="custom-field-photo-width" size="3" value="<?=$w; ?>" /></span></td>
+		</tr>
+		<tr valign="top">
+			<th scope="row"><div id="divLbl" style="<?php echo $isDisplay;?>">Css Class:</div></th>
+			<td>
+				<div id="divCSS" style="<?php echo $isDisplay;?>">
+				<input name="custom-field-css" id="custom-field-css" size="40" type="text" value=<?=$cssVlaue?> />
+				</div>
+			</td>
+		</tr>
+
+		<!-- END :: For Image/Photo' Css -->		
+		</tbody>
+		</table>
+		
+		</fieldset>
+  		<p class="submit" ><input name="cancel-edit-custom-field" type="submit" id="cancel-edit-custom-field" value="Cancel" /> <input name="submit-edit-custom-field" type="submit" id="submit-edit-custom-field" value="Update" /></p>
+	  	
+  		</form>
+	  	
+  		</div>
+	  	
+  		<?php
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/cropper.php
===================================================================
--- /afridex/plugins/fresh-page/cropper.php (revision 21)
+++ /afridex/plugins/fresh-page/cropper.php (revision 21)
@@ -0,0 +1,189 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+
+	<title>
+		<?php //bloginfo('name') ?> &rsaquo; 
+		<?php //echo wp_specialchars( strip_tags( $title ) ); ?> 
+		&#8212; WordPress
+	</title>
+
+	<?php //wp_admin_css(); ?>
+    <link href="../../../wp-admin/wp-admin.css" rel="stylesheet" type="text/css" media="all" />
+
+	<script type="text/javascript" src="./js/greybox.js"></script>	
+	<script type="text/javascript" src="./js/lib/prototype.js"></script>	
+ 	<script type="text/javascript" src="./js/lib/scriptaculous.js?load=builder,dragdrop"></script>
+	<script type="text/javascript" src="./js/cropper.js"></script>
+	<script type="text/javascript">
+	function MM_swapImgRestore() { //v3.0
+	  var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
+	}
+	
+	function MM_preloadImages() { //v3.0
+	  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
+		var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
+		if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
+	}
+	
+	function MM_findObj(n, d) { //v4.01
+	  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
+		d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
+	  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
+	  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
+	  if(!x && d.getElementById) x=d.getElementById(n); return x;
+	}
+	
+	function MM_swapImage() { //v3.0
+	  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
+	   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
+	}
+	//-->
+	</script>
+	<script type="text/javascript" charset="utf-8">
+		// setup the callback function
+		function onEndCrop( coords, dimensions ) {
+			$( 'x1' ).value = coords.x1;
+			$( 'y1' ).value = coords.y1;
+			$( 'x2' ).value = coords.x2;
+			$( 'y2' ).value = coords.y2;
+			$( 'width' ).value = dimensions.width;
+			$( 'height' ).value = dimensions.height;
+		}
+		
+		// basic example
+		Event.observe( 
+			window, 
+			'load', 
+			function() { 
+				new Cropper.Img( 
+					'testImage',
+					{
+						onEndCrop: onEndCrop 
+					}
+				) 
+			}
+		); 		
+		
+		
+		if( typeof(dump) != 'function' ) {
+			Debug.init(true, '/');
+			
+			function dump( msg ) {
+				Debug.raise( msg );
+			};
+		} else dump( '---------------------------------------\n' );
+	</script>
+	<script type="text/javascript" charset="utf-8">
+
+		function imageCrop()
+		{
+			if(document.getElementById('x1').value != '0')
+			{
+				x1 = document.getElementById('x1').value;
+				y1 = document.getElementById('y1').value;
+				x2 = document.getElementById('x2').value;
+				y2 = document.getElementById('y2').value;
+
+				w = document.getElementById('width').value;
+				h = document.getElementById('height').value;
+
+				sourceImage = document.getElementById('sourceImage').value;
+
+				if(sourceImage.indexOf("&sw=") != "-1")
+				{
+//					cropValue1 = document.getElementById("cropValues2").value;
+//					cropValue2 = document.getElementById("cropValues3").value;
+					cropValue3 = document.getElementById("cropValues4").value;
+					cropValue4 = document.getElementById("cropValues5").value;
+
+					x1 = parseInt(cropValue3) + parseInt(x1);
+					y1 = parseInt(cropValue4) + parseInt(y1);
+
+					sourceImage = sourceImage.substring(0, sourceImage.indexOf("&sw="));
+				}
+
+				requiredString = sourceImage+"&sw="+w+"&sh="+h+"&sx="+x1+"&sy="+y1;
+			}
+			else
+				requiredString = document.getElementById('sourceImage').value;
+
+			document.getElementById('tempSrc').value = requiredString;
+			parent.exchangeValues(requiredString, document.getElementById('imageThumbId').value); 
+//			parent.$("img_thumb_1").src = requiredString + "&w=150&h=120";
+			parent.$(document.getElementById('imageThumbId').value).src = requiredString + "&w=150&h=120";
+
+			parent.GB_hide();
+			console.log(requiredString);
+		}
+	</script>
+	<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
+	<style type="text/css">
+		label { 
+			clear: left;
+			margin-left: 50px;
+			float: left;
+			width: 5em;
+			}
+		
+		html, body { 
+			margin: 0;
+			}
+		
+		#testWrap {
+			margin:8px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
+			}
+	</style>
+</head>
+<body onload="MM_preloadImages('./images/ajax-loader.gif')">
+	<div align="center" id="freshpostProgressBar" name="freshpostProgressBar" style="position:absolute; bottom:50%; left:30%; z-index:0; display:inline; background:#fff; border:1px solid #666;">
+		Loading...
+		<div><img src="./images/ajax-loader.gif" /></div>
+	</div>
+	<form name="frmCroper" action="" method="post">
+	<?php 
+		if(isset($_GET['sw'])) {
+			$finalSrc = $_GET['id']."&sw=".$_GET['sw']."&sh=".$_GET['sh']."&sx=".$_GET['sx']."&sy=".$_GET['sy'];
+		}
+		else {
+			$finalSrc = $_GET['id'];
+		}
+		if (isset($_GET['url']))
+			$url = $_GET['url']."&post=".$_GET['post'];
+	?>
+		<div id="testWrap" style="z-index:1;">
+			<img src="<?php echo $finalSrc; ?>" alt="test image" id="testImage" onload="javascript: document.getElementById('freshpostProgressBar').style.display = 'none';" />
+		</div>
+
+<?php
+
+$j = 0;
+foreach(explode("&", $finalSrc) as $item)
+{
+	if($j++ == 0)
+		continue;
+	$cropValueArr = explode("=",$item);
+?>
+	<input type="hidden" name="cropValues<?php echo $j; ?>" id="cropValues<?php echo $j; ?>" value="<?php echo $cropValueArr[1]; ?>" />
+<?php } ?>
+
+		<input type="hidden" name="sourceImage" id="sourceImage" value="<?php echo $finalSrc; ?>" />
+		<input type="hidden" name="url" id="url" value="<?php echo $url; ?>" />
+		<input type="hidden" name="tempSrc" id="tempSrc" />
+
+		<input type="hidden" name="imageThumbId" id="imageThumbId" value="<?php echo $_GET['imageThumbId']; ?>"/>
+
+		<p align="right" style="position:fixed; top:-10px; right:0px; z-index:1000;">
+			<input type="button" value="Crop It" onclick="javascript: imageCrop();" />
+		</p>
+		<input type="hidden" name="x1" id="x1" />
+		<input type="hidden" name="y1" id="y1" />
+		<input type="hidden" name="x2" id="x2" />
+		<input type="hidden" name="y2" id="y2" />
+		<input type="hidden" name="width" id="width" />
+		<input type="hidden" name="height" id="height" />
+	</form>
+</body>
+</html>
Index: /afridex/plugins/fresh-page/RC_Format.php
===================================================================
--- /afridex/plugins/fresh-page/RC_Format.php (revision 21)
+++ /afridex/plugins/fresh-page/RC_Format.php (revision 21)
@@ -0,0 +1,37 @@
+<?php
+class RC_Format
+{
+	function BoolToSql($value)
+	{
+		$sql = $value == true ? "'true'" : "'false'";
+		return $sql;
+	}
+	
+	function GetInputName($fieldName)
+	{
+		$name = 'rc_cwp_meta_' . str_replace(' ', '_', $fieldName);
+		$name = attribute_escape(str_replace('.', '$DOT$', $name));
+		return $name;
+	}
+	
+	function GetFieldName($inputName)
+	{
+		$fieldName = str_replace('rc_cwp_meta_', '', $inputName);
+		$fieldName = str_replace('_', ' ', $fieldName);
+		$fieldName = str_replace('$DOT$', '.', $fieldName);
+		return $fieldName;
+	}
+	
+	function TextToSql($value)
+	{
+		$value = trim($value);
+		$sql = $value == '' ? 'NULL' : "'$value'";
+		return $sql;
+	}
+	
+	function TrimArrayValues(&$value, $key)
+	{
+		$value = trim($value);
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/cache/index.php
===================================================================
--- /afridex/plugins/fresh-page/cache/index.php (revision 21)
+++ /afridex/plugins/fresh-page/cache/index.php (revision 21)
@@ -0,0 +1,4 @@
+<?php
+header('Location: /');
+exit;
+?>
Index: /afridex/plugins/fresh-page/RCCWP_Processor.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_Processor.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_Processor.php (revision 21)
@@ -0,0 +1,272 @@
+<?php
+class RCCWP_Processor
+{
+	function Main()
+	{
+		global $CUSTOM_WRITE_PANEL;
+		
+		if (isset($_POST['edit-with-no-custom-write-panel']))
+		{
+			
+			wp_redirect('post.php?action=edit&post=' . $_POST['post-id'] . '&no-custom-write-panel=' . $_POST['custom-write-panel-id']);
+		}
+		else if (isset($_POST['edit-with-custom-write-panel']))
+		{
+			
+			wp_redirect('post.php?action=edit&post=' . $_POST['post-id'] . '&custom-write-panel-id=' . $_POST['custom-write-panel-id']);
+		}
+		
+		if (RCCWP_Application::InWritePostPanel())
+		{
+			include_once('RCCWP_Menu.php');
+			include_once('RCCWP_WritePostPage.php');
+			
+			$CUSTOM_WRITE_PANEL = RCCWP_Post::GetCustomWritePanel();
+			
+			
+			if (isset($CUSTOM_WRITE_PANEL))
+			{
+				include_once('RCCWP_Options.php');
+				$assignToRole = RCCWP_Options::Get('assign-to-role');
+				
+				if ($assignToRole <> 1 || current_user_can($CUSTOM_WRITE_PANEL->capability_name))
+				{
+					
+					ob_start(array('RCCWP_WritePostPage', 'ApplyCustomWritePanelAssignedCategories'));
+					ob_start(array('RCCWP_WritePostPage', 'RelocateWpSubmitButtons'));
+					
+					add_action('admin_head', array('RCCWP_WritePostPage', 'ApplyCustomWritePanelStandardFields'));
+					add_action('admin_head', array('RCCWP_WritePostPage', 'HideCustomWritePanelExternalFields'));
+						
+					add_action('simple_edit_form', array('RCCWP_WritePostPage', 'CustomFieldCollectionInterface'));
+					add_action('edit_form_advanced', array('RCCWP_WritePostPage', 'CustomFieldCollectionInterface'));
+					add_action('edit_page_form', array('RCCWP_WritePostPage', 'CustomFieldCollectionInterface'));
+					
+				}
+			}
+			else if (!isset($_REQUEST['no-custom-write-panel']) && isset($_REQUEST['post']))
+			{
+				include_once('RCCWP_Options.php');
+				$promptEditingPost = RCCWP_Options::Get('prompt-editing-post');
+				if ($promptEditingPost == 1)
+				{
+					wp_redirect('edit.php?page=' . urlencode(RC_CWP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') . '&assign-custom-write-panel=' . (int)$_GET['post']);
+				}
+			}
+		}
+		
+		if (isset($_POST['finish-create-custom-write-panel']))
+		{
+			include_once('RCCWP_CustomWritePanel.php');
+			$hiddenExtFields = $_POST['custom-write-panel-ext-fields'];			
+//			$hiddenExtFields = explode("\n", $_POST['custom-write-panel-ext-fields']);
+			if(!empty($hiddenExtFields))
+				array_walk($hiddenExtFields, array(RC_Format, TrimArrayValues));
+				
+				$customWritePanelId = RCCWP_CustomWritePanel::Create(
+					$_POST['custom-write-panel-name'],
+					$_POST['custom-write-panel-description'],
+					$_POST['custom-write-panel-standard-fields'],
+					$hiddenExtFields,
+					$_POST['custom-write-panel-categories'],
+					$_POST['custom-write-panel-order']);
+
+				RCCWP_CustomWritePanel::AssignToRole($customWritePanelId, 'administrator');
+			wp_redirect('edit.php' . RCCWP_ManagementPage::GetCustomWritePanelEditUrl($customWritePanelId));
+		}
+		else if (isset($_POST['submit-edit-custom-write-panel']))
+		{
+				include_once('RCCWP_CustomWritePanel.php');
+
+	//			$hiddenExtFields = explode("\n", $_POST['custom-write-panel-ext-fields']);
+				$hiddenExtFields = $_POST['custom-write-panel-ext-fields'];
+				if(!empty($hiddenExtFields))
+					array_walk($hiddenExtFields, array(RC_Format, TrimArrayValues));
+
+					RCCWP_CustomWritePanel::Update(
+						$_POST['custom-write-panel-id'],
+						$_POST['custom-write-panel-name'],
+						$_POST['custom-write-panel-description'],
+						$_POST['custom-write-panel-standard-fields'],
+						$hiddenExtFields,
+						$_POST['custom-write-panel-categories'],
+						$_POST['custom-write-panel-order']);
+					
+					RCCWP_CustomWritePanel::AssignToRole($_POST['custom-write-panel-id'], 'administrator');
+		}
+		else if (isset($_POST['finish-create-custom-field']))
+		{
+			include_once('RCCWP_CustomField.php');
+			
+			$current_field = RCCWP_CustomField::GetCustomFieldTypes((int)$_REQUEST['custom-field-type']);
+			
+			if ($current_field->has_properties)
+			{
+				$custom_field_properties = array();
+				if (in_array($current_field->name, array('Textbox', 'Listbox')))
+				{
+					$custom_field_properties['size'] = $_POST['custom-field-size'];
+				}
+				else if (in_array($current_field->name, array('Multiline Textbox')))
+				{
+					$custom_field_properties['height'] = $_POST['custom-field-height'];
+					$custom_field_properties['width'] = $_POST['custom-field-width'];
+				}
+				else if( in_array( $current_field->name, array('Image') ) )
+				{
+					$params = '';
+					if( $_POST['custom-field-photo-height'] != '' && is_numeric( $_POST['custom-field-photo-height']) )
+					{
+						$params .= '&h=' . $_POST['custom-field-photo-height'];
+					}
+
+					if( $_POST['custom-field-photo-width'] != '' && is_numeric( $_POST['custom-field-photo-height']) )
+					{
+						$params .= '&w=' . $_POST['custom-field-photo-width'];
+					}
+					
+					if( $_POST['custom-field-custom-params'] != '' )
+					{
+						$params .= '&' . $_POST['custom-field-custom-params'];
+					}
+
+					if( $params )
+					{
+						$custom_field_properties['params'] = $params;
+					}
+				}
+				else if (in_array($current_field->name, array('Date')))
+				{
+					$custom_field_properties['format'] = $_POST['custom-field-date-format'];
+				}
+			}
+			
+			RCCWP_CustomField::Create(
+				$_POST['custom-write-panel-id'],
+				$_POST['custom-field-name'],
+				$_POST['custom-field-description'],
+				$_POST['custom-field-order'],
+				$_POST['custom-field-type'],
+				$_POST['custom-field-options'],
+				$_POST['custom-field-default-value'],
+				$custom_field_properties
+				);
+		}
+		else if (isset($_POST['submit-edit-custom-field']))
+		{
+			include_once('RCCWP_CustomField.php');
+			
+			$current_field = RCCWP_CustomField::GetCustomFieldTypes((int)$_POST['custom-field-type']);
+			
+			if ($current_field->has_properties)
+			{
+				$custom_field_properties = array();
+				if (in_array($current_field->name, array('Textbox', 'Listbox')))
+				{
+					$custom_field_properties['size'] = $_POST['custom-field-size'];
+				}
+				else if (in_array($current_field->name, array('Multiline Textbox')))
+				{
+					$custom_field_properties['height'] = $_POST['custom-field-height'];
+					$custom_field_properties['width'] = $_POST['custom-field-width'];
+				}
+				else if( in_array( $current_field->name, array('Image') ) )
+				{ 
+					$params = '';
+					
+					if( $_POST['custom-field-photo-height'] != '' && is_numeric( $_POST['custom-field-photo-height']) )
+					{
+						$params = '&h=' . $_POST['custom-field-photo-height'];
+					}
+
+					if( $_POST['custom-field-photo-width'] != '' && is_numeric( $_POST['custom-field-photo-height']) )
+					{
+						$params .= '&w=' . $_POST['custom-field-photo-width'];
+					}
+					
+					if( $_POST['custom-field-custom-params'] != '' )
+					{
+						$params .= '&' . $_POST['custom-field-custom-params'];
+					}
+
+					if( $params )
+					{
+						$custom_field_properties['params'] = '?' . $params;
+					}
+				}
+				else if (in_array($current_field->name, array('Date')))
+				{
+					$custom_field_properties['format'] = $_POST['custom-field-date-format'];
+				}
+			}
+			
+			RCCWP_CustomField::Update(
+				$_POST['custom-field-id'],
+				$_POST['custom-field-name'],
+				$_POST['custom-field-description'],
+				$_POST['custom-field-order'],
+				$_POST['custom-field-type'],
+				$_POST['custom-field-options'],
+				$_POST['custom-field-default-value'],
+				$custom_field_properties
+				);
+		}
+		else if (isset($_POST['update-custom-write-panel-options']))
+		{
+			if ($_POST['uninstall-custom-write-panel'] == 'uninstall')
+			{
+				RCCWP_Application::Uninstall();
+				wp_redirect('options-general.php');
+			}
+			else
+			{
+				include_once('RCCWP_Options.php');
+				RCCWP_Options::Update(
+					$_POST['hide-write-post'],
+					$_POST['hide-write-page'],
+					$_POST['prompt-editing-post'],
+					$_POST['assign-to-role'],
+					$_POST['use-snipshot'],
+					$_POST['default-custom-write-panel']);
+			}
+		}
+		else if (isset($_GET['delete-custom-write-panel']))
+		{
+			include_once('RCCWP_CustomWritePanel.php');
+			RCCWP_CustomWritePanel::Delete($_GET['delete-custom-write-panel']);
+			wp_redirect('edit.php?page=' . urlencode(RC_CWP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php'));
+		}
+		else if (isset($_GET['delete-custom-field']))
+		{
+			include_once('RCCWP_CustomField.php');
+			RCCWP_CustomField::Delete($_REQUEST['delete-custom-field']);
+			wp_redirect('edit.php?page=' . urlencode(RC_CWP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') . '&view-custom-write-panel=' . $_REQUEST['custom-write-panel-id'] . '&custom-write-panel-id=' . $_REQUEST['custom-write-panel-id']);
+		}
+		
+	}
+	
+	function FlushAllOutputBuffer() 
+	{ 
+		
+		while (@ob_end_flush()); 
+		
+	} 
+	
+	function Redirect($location)
+	{
+		global $post_ID;
+		global $page_ID;
+
+		
+		if (!empty($_REQUEST['rc-cwp-custom-write-panel-id']))
+		{
+			if (strstr($location, 'post-new.php?posted=') || strstr($location, 'page-new.php?posted='))
+			{
+				$id = ($post_ID=="")?$page_ID:$post_ID;
+				$location = $_REQUEST['_wp_http_referer'] . '&posted=' . $id;
+			}
+		}
+		return $location;
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/RCCWP_SnipshotCallback.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_SnipshotCallback.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_SnipshotCallback.php (revision 21)
@@ -0,0 +1,112 @@
+
+<?
+
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can('edit_posts')))
+	die("Athentication failed!");
+
+
+	//print_r("Req\n");
+	//print_r($_REQUEST);
+	//print_r("Files\n");
+	//print_r($_FILES);
+	//print_r("Get\n");
+	//print_r($_GET);
+	//print_r("Post\n");
+	//print_r($_POST);
+//$filepath = dirname(__FILE__) . '/files/log.txt';
+//file_put_contents($filepath, , FILE_APPEND);
+
+//
+    //Snipshot callback test
+    //
+    //Install this script on your server to test and debug callbacks from 
+    //www.snipshot.com. Note: this script will need read and write access 
+    //to your image directory.
+
+    //CHANGE THIS TO THE DIRECTORY WHERE YOUR IMAGES WILL BE SAVED
+    $IMG_DIR = './files';
+    chdir($IMG_DIR);
+    
+    //CHANGE THIS TO THE NAME OF THE FIELD THAT CARRIES THE IMAGE DATA OR URL
+    $OUTPUT = 'file';
+
+    function print_pre($r, $desc=''){
+        echo '<pre>' . $desc . ' ' . print_r($r) . '</pre>';
+    }
+    function glob_rsort_modtime( $patt ) {
+        if ( ( $files = @glob($patt, GLOB_BRACE) ) === false )
+            return array(false, 'Glob error.');
+        if ( !count($files) )
+            return array(false, 'No files found.');
+        $rtn = array();
+        foreach ( $files as $filename )
+            $rtn[$filename] = filemtime($filename);
+        arsort($rtn);
+        reset($rtn);
+        foreach ( $rtn as $filename => $t )
+            $rtn[$filename] = date('r', $t);
+        return $rtn;
+    }
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>Snipshot callback</title>
+    
+    <?	
+	$operationSuccess = "false";
+        if (!empty($_REQUEST)){
+            //print_pre($_REQUEST, 'REQUEST');
+            if (!empty($_FILES)){ //snipshot_callback_agent=snipshot
+                //print_pre($_FILES[$OUTPUT], 'FILES');    
+                move_uploaded_file(
+                    $_FILES[$OUTPUT]['tmp_name'], 
+                    $_FILES[$OUTPUT]['name']
+                );//file_put_contents($filepath, time()."trying ".$_FILES[$OUTPUT]['name']."\n", FILE_APPEND);
+		//file_put_contents("log.txt", time()."trying ".basename($_GET[$OUTPUT]));
+            } 
+            else if (!empty($_GET[$OUTPUT])){ //snipshot_callback_agent=user
+                $data = file_get_contents($_GET[$OUTPUT]);
+		//echo "\nfrom ----".$_GET[$OUTPUT]."\n";
+                $fp = fopen(basename($_GET[$OUTPUT]), 'wb');
+		//echo "\nfrom ----".basename($_GET[$OUTPUT])."\n";
+                fwrite($fp, $data);
+                fclose($fp);
+		chmod(basename($_GET[$OUTPUT]), 0644);
+		$filename = basename($_GET[$OUTPUT]);
+
+		//$filename = 'hello_'.time() . '.jpg';
+ 		//rename(basename($_GET[$OUTPUT]), $filename);
+
+		$operationSuccess = "true";
+		//file_put_contents("log.txt", var_export($_REQUEST, true)."\n new name = $filename\n",FILE_APPEND);
+            }
+        }
+        
+        //$file_list = glob_rsort_modtime('{*.jpg,*.png,*.gif,*.tif,*.psd,*.pdf}');
+        //$file_list = array_slice($file_list, 0, 10);
+        //print_pre($file_list, 'LAST 10 IMAGES');
+        //foreach($file_list as $fn => $mt){
+         //   echo '<p><img src="'.$fn.'"/>';
+        //}
+    ?>
+</head>
+<body>
+
+	<script language="javascript">
+
+    		var par = window.parent.document;
+		
+		if (<?=$operationSuccess?>){
+			
+
+
+		}
+		
+	</script>
+	<h3> The file was updated successfuly</h3>
+	<p> Please save the page in order to see the changes. <a href="" onclick="parent.GB_hide();">Close this window</a> </p>
+</body>
+</html>
Index: /afridex/plugins/fresh-page/RCCWP_CreateCustomWritePanelPage.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_CreateCustomWritePanelPage.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_CreateCustomWritePanelPage.php (revision 21)
@@ -0,0 +1,29 @@
+<?php
+include_once('RCCWP_CustomWritePanelPage.php');
+class RCCWP_CreateCustomWritePanelPage
+{
+	function Main()
+	{
+		?>
+
+		<div class="wrap">
+
+		<h2><?php _e('Create Custom Write Panel'); ?></h2>
+		
+		<form action="" method="post" id="create-new-write-panel-form">
+		
+		<?php RCCWP_CustomWritePanelPage::Content(); ?>
+		
+		<p class="submit" >
+			<input name="cancel-create-custom-write-panel" type="submit" id="cancel-create-custom-write-panel" value="<?php _e('Cancel'); ?>" /> 
+			<input name="finish-create-custom-write-panel" type="submit" id="finish-create-custom-write-panel" value="<?php _e('Finish'); ?>" />
+		</p>
+		
+		</form>
+
+		</div>
+
+		<?php
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/RCCWP_OptionsPage.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_OptionsPage.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_OptionsPage.php (revision 21)
@@ -0,0 +1,90 @@
+<?php
+include_once('RCCWP_Options.php');
+
+class RCCWP_OptionsPage
+{
+
+function Main()
+{
+	$customWritePanels = RCCWP_Application::GetCustomWritePanels();
+	$customWritePanelOptions = RCCWP_Options::Get();
+	?>
+	
+	<div class="wrap">
+
+	<h2>Fresh Post Options</h2>
+	
+	<form action="" method="post" id="custom-write-panel-options-form">
+	
+	<p class="submit" ><input name="update-custom-write-panel-options" type="submit" value="Update Options" /></p>
+	
+	<ul> 
+	<li> 
+		<label for="hide-write-post"> 
+		<input name="hide-write-post" id="hide-write-post" value="1" <?=RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['hide-write-post'])?> type="checkbox"> 
+		Hide WordPress' Write Post panel.</label> 
+	</li> 
+	<li> 
+		<label for="hide-write-page"> 
+		<input name="hide-write-page" id="hide-write-page" value="1" <?=RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['hide-write-page'])?> type="checkbox"> 
+		Hide WordPress' Write Page panel.</label> 
+	</li> 
+	<li> 
+		<label for="prompt-editing-post"> 
+		<input name="prompt-editing-post" id="prompt-editing-post" value="1" <?=RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['prompt-editing-post'])?> type="checkbox"> 
+		Prompt when editing a Post not created with Custom Write Panel.</label> 
+	</li>
+	<li> 
+		<label for="assign-to-role"> 
+		<input name="assign-to-role" id="assign-to-role" value="1" <?=RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['assign-to-role'])?> type="checkbox"> 
+		Assign custom write panels to a role. (Requires installing plugin "Role Manager" at http://www.im-web-gefunden.de/wordpress-plugins/role-manager/)</label> 
+	</li>
+	<li> 
+		<label for="use-snipshot"> 
+		<input name="use-snipshot" id="use-snipshot" value="1" <?=RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['use-snipshot'])?> type="checkbox"> 
+		Use Snipshot services instead of cropper to edit photos.</label> 
+	</li>
+	<li>
+		<label for="default-custom-write-panel">Default Custom Write Panel
+		<select name="default-custom-write-panel" id="default-custom-write-panel">
+			<option value="">(None)</option>
+		<?php
+		$defaultCustomWritePanel = $customWritePanelOptions['default-custom-write-panel'];
+		foreach ($customWritePanels as $panel) :
+			$selected = $panel->id == $defaultCustomWritePanel ? 'selected="selected"' : '';
+		?>
+			<option value="<?=$panel->id?>" <?=$selected?>><?=$panel->name?></option>
+		<?php
+		endforeach;
+		?>
+		</select>
+		</label>
+	</li>
+	<li>
+		<label for="uninstall-custom-write-panel">
+		Type <strong>uninstall</strong> into the textbox, click <strong>Update Options</strong>, and all the tables created by this plugin will be deleted
+		<input type="text" id="uninstall-custom-write-panel" name="uninstall-custom-write-panel" size="25" />
+		</label>
+	</li>
+	</ul>
+	
+	<p class="submit" ><input name="update-custom-write-panel-options" type="submit" value="Update Options" /></p>
+	
+	</form>
+
+	</div>
+	
+	<?php
+}
+
+function GetCheckboxState($optionValue)
+{
+	if ($optionValue == '')
+		return '';
+	else 
+		return 'checked="checked"';
+}
+
+}
+
+?>
Index: /afridex/plugins/fresh-page/js/cropper.js
===================================================================
--- /afridex/plugins/fresh-page/js/cropper.js (revision 21)
+++ /afridex/plugins/fresh-page/js/cropper.js (revision 21)
@@ -0,0 +1,567 @@
+/** 
+ * Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * http://www.opensource.org/licenses/bsd-license.php
+ * 
+ * See scriptaculous.js for full scriptaculous licence
+ */
+
+
+var CropDraggable=Class.create();
+Object.extend(Object.extend(CropDraggable.prototype,Draggable.prototype),{initialize:function(_1){
+this.options=Object.extend({drawMethod:function(){
+}},arguments[1]||{});
+this.element=$(_1);
+this.handle=this.element;
+this.delta=this.currentDelta();
+this.dragging=false;
+this.eventMouseDown=this.initDrag.bindAsEventListener(this);
+Event.observe(this.handle,"mousedown",this.eventMouseDown);
+Draggables.register(this);
+},draw:function(_2){
+var _3=Position.cumulativeOffset(this.element);
+var d=this.currentDelta();
+_3[0]-=d[0];
+_3[1]-=d[1];
+var p=[0,1].map(function(i){
+return (_2[i]-_3[i]-this.offset[i]);
+}.bind(this));
+this.options.drawMethod(p);
+}});
+var Cropper={};
+Cropper.Img=Class.create();
+Cropper.Img.prototype={initialize:function(_7,_8){
+this.options=Object.extend({ratioDim:{x:0,y:0},minWidth:0,minHeight:0,displayOnInit:false,onEndCrop:Prototype.emptyFunction,captureKeys:true,onloadCoords:null,maxWidth:0,maxHeight:0},_8||{});
+this.img=$(_7);
+this.clickCoords={x:0,y:0};
+this.dragging=false;
+this.resizing=false;
+this.isWebKit=/Konqueror|Safari|KHTML/.test(navigator.userAgent);
+this.isIE=/MSIE/.test(navigator.userAgent);
+this.isOpera8=/Opera\s[1-8]/.test(navigator.userAgent);
+this.ratioX=0;
+this.ratioY=0;
+this.attached=false;
+this.fixedWidth=(this.options.maxWidth>0&&(this.options.minWidth>=this.options.maxWidth));
+this.fixedHeight=(this.options.maxHeight>0&&(this.options.minHeight>=this.options.maxHeight));
+if(typeof this.img=="undefined"){
+return;
+}
+$A(document.getElementsByTagName("script")).each(function(s){
+if(s.src.match(/cropper\.js/)){
+var _a=s.src.replace(/cropper\.js(.*)?/,"");
+var _b=document.createElement("link");
+_b.rel="stylesheet";
+_b.type="text/css";
+_b.href=_a+"../css/cropper.css";
+_b.media="screen";
+document.getElementsByTagName("head")[0].appendChild(_b);
+}
+});
+if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){
+var _c=this.getGCD(this.options.ratioDim.x,this.options.ratioDim.y);
+this.ratioX=this.options.ratioDim.x/_c;
+this.ratioY=this.options.ratioDim.y/_c;
+}
+this.subInitialize();
+if(this.img.complete||this.isWebKit){
+this.onLoad();
+}else{
+Event.observe(this.img,"load",this.onLoad.bindAsEventListener(this));
+}
+},getGCD:function(a,b){
+if(b==0){
+return a;
+}
+return this.getGCD(b,a%b);
+},onLoad:function(){
+var _f="imgCrop_";
+var _10=this.img.parentNode;
+var _11="";
+if(this.isOpera8){
+_11=" opera8";
+}
+this.imgWrap=Builder.node("div",{"class":_f+"wrap"+_11});
+this.north=Builder.node("div",{"class":_f+"overlay "+_f+"north"},[Builder.node("span")]);
+this.east=Builder.node("div",{"class":_f+"overlay "+_f+"east"},[Builder.node("span")]);
+this.south=Builder.node("div",{"class":_f+"overlay "+_f+"south"},[Builder.node("span")]);
+this.west=Builder.node("div",{"class":_f+"overlay "+_f+"west"},[Builder.node("span")]);
+var _12=[this.north,this.east,this.south,this.west];
+this.dragArea=Builder.node("div",{"class":_f+"dragArea"},_12);
+this.handleN=Builder.node("div",{"class":_f+"handle "+_f+"handleN"});
+this.handleNE=Builder.node("div",{"class":_f+"handle "+_f+"handleNE"});
+this.handleE=Builder.node("div",{"class":_f+"handle "+_f+"handleE"});
+this.handleSE=Builder.node("div",{"class":_f+"handle "+_f+"handleSE"});
+this.handleS=Builder.node("div",{"class":_f+"handle "+_f+"handleS"});
+this.handleSW=Builder.node("div",{"class":_f+"handle "+_f+"handleSW"});
+this.handleW=Builder.node("div",{"class":_f+"handle "+_f+"handleW"});
+this.handleNW=Builder.node("div",{"class":_f+"handle "+_f+"handleNW"});
+this.selArea=Builder.node("div",{"class":_f+"selArea"},[Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeNorth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeEast"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeSouth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeWest"},[Builder.node("span")]),this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW,Builder.node("div",{"class":_f+"clickArea"})]);
+this.imgWrap.appendChild(this.img);
+this.imgWrap.appendChild(this.dragArea);
+this.dragArea.appendChild(this.selArea);
+this.dragArea.appendChild(Builder.node("div",{"class":_f+"clickArea"}));
+_10.appendChild(this.imgWrap);
+this.startDragBind=this.startDrag.bindAsEventListener(this);
+Event.observe(this.dragArea,"mousedown",this.startDragBind);
+this.onDragBind=this.onDrag.bindAsEventListener(this);
+Event.observe(document,"mousemove",this.onDragBind);
+this.endCropBind=this.endCrop.bindAsEventListener(this);
+Event.observe(document,"mouseup",this.endCropBind);
+this.resizeBind=this.startResize.bindAsEventListener(this);
+this.handles=[this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW];
+this.registerHandles(true);
+if(this.options.captureKeys){
+this.keysBind=this.handleKeys.bindAsEventListener(this);
+Event.observe(document,"keypress",this.keysBind);
+}
+new CropDraggable(this.selArea,{drawMethod:this.moveArea.bindAsEventListener(this)});
+this.setParams();
+},registerHandles:function(_13){
+for(var i=0;i<this.handles.length;i++){
+var _15=$(this.handles[i]);
+if(_13){
+var _16=false;
+if(this.fixedWidth&&this.fixedHeight){
+_16=true;
+}else{
+if(this.fixedWidth||this.fixedHeight){
+var _17=_15.className.match(/([S|N][E|W])$/);
+var _18=_15.className.match(/(E|W)$/);
+var _19=_15.className.match(/(N|S)$/);
+if(_17){
+_16=true;
+}else{
+if(this.fixedWidth&&_18){
+_16=true;
+}else{
+if(this.fixedHeight&&_19){
+_16=true;
+}
+}
+}
+}
+}
+if(_16){
+_15.hide();
+}else{
+Event.observe(_15,"mousedown",this.resizeBind);
+}
+}else{
+_15.show();
+Event.stopObserving(_15,"mousedown",this.resizeBind);
+}
+}
+},setParams:function(){
+this.imgW=this.img.width;
+this.imgH=this.img.height;
+$(this.north).setStyle({height:0});
+$(this.east).setStyle({width:0,height:0});
+$(this.south).setStyle({height:0});
+$(this.west).setStyle({width:0,height:0});
+$(this.imgWrap).setStyle({"width":this.imgW+"px","height":this.imgH+"px"});
+$(this.selArea).hide();
+var _1a={x1:0,y1:0,x2:0,y2:0};
+var _1b=false;
+if(this.options.onloadCoords!=null){
+_1a=this.cloneCoords(this.options.onloadCoords);
+_1b=true;
+}else{
+if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){
+_1a.x1=Math.ceil((this.imgW-this.options.ratioDim.x)/2);
+_1a.y1=Math.ceil((this.imgH-this.options.ratioDim.y)/2);
+_1a.x2=_1a.x1+this.options.ratioDim.x;
+_1a.y2=_1a.y1+this.options.ratioDim.y;
+_1b=true;
+}
+}
+this.setAreaCoords(_1a,false,false,1);
+if(this.options.displayOnInit&&_1b){
+this.selArea.show();
+this.drawArea();
+this.endCrop();
+}
+this.attached=true;
+},remove:function(){
+if(this.attached){
+this.attached=false;
+this.imgWrap.parentNode.insertBefore(this.img,this.imgWrap);
+this.imgWrap.parentNode.removeChild(this.imgWrap);
+Event.stopObserving(this.dragArea,"mousedown",this.startDragBind);
+Event.stopObserving(document,"mousemove",this.onDragBind);
+Event.stopObserving(document,"mouseup",this.endCropBind);
+this.registerHandles(false);
+if(this.options.captureKeys){
+Event.stopObserving(document,"keypress",this.keysBind);
+}
+}
+},reset:function(){
+if(!this.attached){
+this.onLoad();
+}else{
+this.setParams();
+}
+this.endCrop();
+},handleKeys:function(e){
+var dir={x:0,y:0};
+if(!this.dragging){
+switch(e.keyCode){
+case (37):
+dir.x=-1;
+break;
+case (38):
+dir.y=-1;
+break;
+case (39):
+dir.x=1;
+break;
+case (40):
+dir.y=1;
+break;
+}
+if(dir.x!=0||dir.y!=0){
+if(e.shiftKey){
+dir.x*=10;
+dir.y*=10;
+}
+this.moveArea([this.areaCoords.x1+dir.x,this.areaCoords.y1+dir.y]);
+Event.stop(e);
+}
+}
+},calcW:function(){
+return (this.areaCoords.x2-this.areaCoords.x1);
+},calcH:function(){
+return (this.areaCoords.y2-this.areaCoords.y1);
+},moveArea:function(_1e){
+this.setAreaCoords({x1:_1e[0],y1:_1e[1],x2:_1e[0]+this.calcW(),y2:_1e[1]+this.calcH()},true,false);
+this.drawArea();
+},cloneCoords:function(_1f){
+return {x1:_1f.x1,y1:_1f.y1,x2:_1f.x2,y2:_1f.y2};
+},setAreaCoords:function(_20,_21,_22,_23,_24){
+if(_21){
+var _25=_20.x2-_20.x1;
+var _26=_20.y2-_20.y1;
+if(_20.x1<0){
+_20.x1=0;
+_20.x2=_25;
+}
+if(_20.y1<0){
+_20.y1=0;
+_20.y2=_26;
+}
+if(_20.x2>this.imgW){
+_20.x2=this.imgW;
+_20.x1=this.imgW-_25;
+}
+if(_20.y2>this.imgH){
+_20.y2=this.imgH;
+_20.y1=this.imgH-_26;
+}
+}else{
+if(_20.x1<0){
+_20.x1=0;
+}
+if(_20.y1<0){
+_20.y1=0;
+}
+if(_20.x2>this.imgW){
+_20.x2=this.imgW;
+}
+if(_20.y2>this.imgH){
+_20.y2=this.imgH;
+}
+if(_23!=null){
+if(this.ratioX>0){
+this.applyRatio(_20,{x:this.ratioX,y:this.ratioY},_23,_24);
+}else{
+if(_22){
+this.applyRatio(_20,{x:1,y:1},_23,_24);
+}
+}
+var _27=[this.options.minWidth,this.options.minHeight];
+var _28=[this.options.maxWidth,this.options.maxHeight];
+if(_27[0]>0||_27[1]>0||_28[0]>0||_28[1]>0){
+var _29={a1:_20.x1,a2:_20.x2};
+var _2a={a1:_20.y1,a2:_20.y2};
+var _2b={min:0,max:this.imgW};
+var _2c={min:0,max:this.imgH};
+if((_27[0]!=0||_27[1]!=0)&&_22){
+if(_27[0]>0){
+_27[1]=_27[0];
+}else{
+if(_27[1]>0){
+_27[0]=_27[1];
+}
+}
+}
+if((_28[0]!=0||_28[0]!=0)&&_22){
+if(_28[0]>0&&_28[0]<=_28[1]){
+_28[1]=_28[0];
+}else{
+if(_28[1]>0&&_28[1]<=_28[0]){
+_28[0]=_28[1];
+}
+}
+}
+if(_27[0]>0){
+this.applyDimRestriction(_29,_27[0],_23.x,_2b,"min");
+}
+if(_27[1]>1){
+this.applyDimRestriction(_2a,_27[1],_23.y,_2c,"min");
+}
+if(_28[0]>0){
+this.applyDimRestriction(_29,_28[0],_23.x,_2b,"max");
+}
+if(_28[1]>1){
+this.applyDimRestriction(_2a,_28[1],_23.y,_2c,"max");
+}
+_20={x1:_29.a1,y1:_2a.a1,x2:_29.a2,y2:_2a.a2};
+}
+}
+}
+this.areaCoords=_20;
+},applyDimRestriction:function(_2d,val,_2f,_30,_31){
+var _32;
+if(_31=="min"){
+_32=((_2d.a2-_2d.a1)<val);
+}else{
+_32=((_2d.a2-_2d.a1)>val);
+}
+if(_32){
+if(_2f==1){
+_2d.a2=_2d.a1+val;
+}else{
+_2d.a1=_2d.a2-val;
+}
+if(_2d.a1<_30.min){
+_2d.a1=_30.min;
+_2d.a2=val;
+}else{
+if(_2d.a2>_30.max){
+_2d.a1=_30.max-val;
+_2d.a2=_30.max;
+}
+}
+}
+},applyRatio:function(_33,_34,_35,_36){
+var _37;
+if(_36=="N"||_36=="S"){
+_37=this.applyRatioToAxis({a1:_33.y1,b1:_33.x1,a2:_33.y2,b2:_33.x2},{a:_34.y,b:_34.x},{a:_35.y,b:_35.x},{min:0,max:this.imgW});
+_33.x1=_37.b1;
+_33.y1=_37.a1;
+_33.x2=_37.b2;
+_33.y2=_37.a2;
+}else{
+_37=this.applyRatioToAxis({a1:_33.x1,b1:_33.y1,a2:_33.x2,b2:_33.y2},{a:_34.x,b:_34.y},{a:_35.x,b:_35.y},{min:0,max:this.imgH});
+_33.x1=_37.a1;
+_33.y1=_37.b1;
+_33.x2=_37.a2;
+_33.y2=_37.b2;
+}
+},applyRatioToAxis:function(_38,_39,_3a,_3b){
+var _3c=Object.extend(_38,{});
+var _3d=_3c.a2-_3c.a1;
+var _3e=Math.floor(_3d*_39.b/_39.a);
+var _3f;
+var _40;
+var _41=null;
+if(_3a.b==1){
+_3f=_3c.b1+_3e;
+if(_3f>_3b.max){
+_3f=_3b.max;
+_41=_3f-_3c.b1;
+}
+_3c.b2=_3f;
+}else{
+_3f=_3c.b2-_3e;
+if(_3f<_3b.min){
+_3f=_3b.min;
+_41=_3f+_3c.b2;
+}
+_3c.b1=_3f;
+}
+if(_41!=null){
+_40=Math.floor(_41*_39.a/_39.b);
+if(_3a.a==1){
+_3c.a2=_3c.a1+_40;
+}else{
+_3c.a1=_3c.a1=_3c.a2-_40;
+}
+}
+return _3c;
+},drawArea:function(){
+var _42=this.calcW();
+var _43=this.calcH();
+var px="px";
+var _45=[this.areaCoords.x1+px,this.areaCoords.y1+px,_42+px,_43+px,this.areaCoords.x2+px,this.areaCoords.y2+px,(this.img.width-this.areaCoords.x2)+px,(this.img.height-this.areaCoords.y2)+px];
+var _46=this.selArea.style;
+_46.left=_45[0];
+_46.top=_45[1];
+_46.width=_45[2];
+_46.height=_45[3];
+var _47=Math.ceil((_42-6)/2)+px;
+var _48=Math.ceil((_43-6)/2)+px;
+this.handleN.style.left=_47;
+this.handleE.style.top=_48;
+this.handleS.style.left=_47;
+this.handleW.style.top=_48;
+this.north.style.height=_45[1];
+var _49=this.east.style;
+_49.top=_45[1];
+_49.height=_45[3];
+_49.left=_45[4];
+_49.width=_45[6];
+var _4a=this.south.style;
+_4a.top=_45[5];
+_4a.height=_45[7];
+var _4b=this.west.style;
+_4b.top=_45[1];
+_4b.height=_45[3];
+_4b.width=_45[0];
+this.subDrawArea();
+this.forceReRender();
+},forceReRender:function(){
+if(this.isIE||this.isWebKit){
+var n=document.createTextNode(" ");
+var d,el,fixEL,i;
+if(this.isIE){
+fixEl=this.selArea;
+}else{
+if(this.isWebKit){
+fixEl=document.getElementsByClassName("imgCrop_marqueeSouth",this.imgWrap)[0];
+d=Builder.node("div","");
+d.style.visibility="hidden";
+var _4e=["SE","S","SW"];
+for(i=0;i<_4e.length;i++){
+el=document.getElementsByClassName("imgCrop_handle"+_4e[i],this.selArea)[0];
+if(el.childNodes.length){
+el.removeChild(el.childNodes[0]);
+}
+el.appendChild(d);
+}
+}
+}
+fixEl.appendChild(n);
+fixEl.removeChild(n);
+}
+},startResize:function(e){
+this.startCoords=this.cloneCoords(this.areaCoords);
+this.resizing=true;
+this.resizeHandle=Event.element(e).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/,"");
+Event.stop(e);
+},startDrag:function(e){
+this.selArea.show();
+this.clickCoords=this.getCurPos(e);
+this.setAreaCoords({x1:this.clickCoords.x,y1:this.clickCoords.y,x2:this.clickCoords.x,y2:this.clickCoords.y},false,false,null);
+this.dragging=true;
+this.onDrag(e);
+Event.stop(e);
+},getCurPos:function(e){
+var el=this.imgWrap,wrapOffsets=Position.cumulativeOffset(el);
+while(el.nodeName!="BODY"){
+wrapOffsets[1]-=el.scrollTop||0;
+wrapOffsets[0]-=el.scrollLeft||0;
+el=el.parentNode;
+}
+return curPos={x:Event.pointerX(e)-wrapOffsets[0],y:Event.pointerY(e)-wrapOffsets[1]};
+},onDrag:function(e){
+if(this.dragging||this.resizing){
+var _54=null;
+var _55=this.getCurPos(e);
+var _56=this.cloneCoords(this.areaCoords);
+var _57={x:1,y:1};
+if(this.dragging){
+if(_55.x<this.clickCoords.x){
+_57.x=-1;
+}
+if(_55.y<this.clickCoords.y){
+_57.y=-1;
+}
+this.transformCoords(_55.x,this.clickCoords.x,_56,"x");
+this.transformCoords(_55.y,this.clickCoords.y,_56,"y");
+}else{
+if(this.resizing){
+_54=this.resizeHandle;
+if(_54.match(/E/)){
+this.transformCoords(_55.x,this.startCoords.x1,_56,"x");
+if(_55.x<this.startCoords.x1){
+_57.x=-1;
+}
+}else{
+if(_54.match(/W/)){
+this.transformCoords(_55.x,this.startCoords.x2,_56,"x");
+if(_55.x<this.startCoords.x2){
+_57.x=-1;
+}
+}
+}
+if(_54.match(/N/)){
+this.transformCoords(_55.y,this.startCoords.y2,_56,"y");
+if(_55.y<this.startCoords.y2){
+_57.y=-1;
+}
+}else{
+if(_54.match(/S/)){
+this.transformCoords(_55.y,this.startCoords.y1,_56,"y");
+if(_55.y<this.startCoords.y1){
+_57.y=-1;
+}
+}
+}
+}
+}
+this.setAreaCoords(_56,false,e.shiftKey,_57,_54);
+this.drawArea();
+Event.stop(e);
+}
+},transformCoords:function(_58,_59,_5a,_5b){
+var _5c=[_58,_59];
+if(_58>_59){
+_5c.reverse();
+}
+_5a[_5b+"1"]=_5c[0];
+_5a[_5b+"2"]=_5c[1];
+},endCrop:function(){
+this.dragging=false;
+this.resizing=false;
+this.options.onEndCrop(this.areaCoords,{width:this.calcW(),height:this.calcH()});
+},subInitialize:function(){
+},subDrawArea:function(){
+}};
+Cropper.ImgWithPreview=Class.create();
+Object.extend(Object.extend(Cropper.ImgWithPreview.prototype,Cropper.Img.prototype),{subInitialize:function(){
+this.hasPreviewImg=false;
+if(typeof (this.options.previewWrap)!="undefined"&&this.options.minWidth>0&&this.options.minHeight>0){
+this.previewWrap=$(this.options.previewWrap);
+this.previewImg=this.img.cloneNode(false);
+this.previewImg.id="imgCrop_"+this.previewImg.id;
+this.options.displayOnInit=true;
+this.hasPreviewImg=true;
+this.previewWrap.addClassName("imgCrop_previewWrap");
+this.previewWrap.setStyle({width:this.options.minWidth+"px",height:this.options.minHeight+"px"});
+this.previewWrap.appendChild(this.previewImg);
+}
+},subDrawArea:function(){
+if(this.hasPreviewImg){
+var _5d=this.calcW();
+var _5e=this.calcH();
+var _5f={x:this.imgW/_5d,y:this.imgH/_5e};
+var _60={x:_5d/this.options.minWidth,y:_5e/this.options.minHeight};
+var _61={w:Math.ceil(this.options.minWidth*_5f.x)+"px",h:Math.ceil(this.options.minHeight*_5f.y)+"px",x:"-"+Math.ceil(this.areaCoords.x1/_60.x)+"px",y:"-"+Math.ceil(this.areaCoords.y1/_60.y)+"px"};
+var _62=this.previewImg.style;
+_62.width=_61.w;
+_62.height=_61.h;
+_62.left=_61.x;
+_62.top=_61.y;
+}
+}});
+
Index: /afridex/plugins/fresh-page/js/epoch_classes.js
===================================================================
--- /afridex/plugins/fresh-page/js/epoch_classes.js (revision 21)
+++ /afridex/plugins/fresh-page/js/epoch_classes.js (revision 21)
@@ -0,0 +1,1089 @@
+/*!!
+Epoch DHTML JavaScript Calendar - Version 2.0.2
+English Edition
+Primary JavaScript File
+(c) 2006-2007 MeanFreePath
+Free for NON-COMMERCIAL use - see website for details and updates
+http://www.meanfreepath.com/javascript_calendar/index.html
+!!*/
+
+/**
+* The main Epoch class.  All publicly-accessible methods and properties are called from this class
+*/
+function Epoch(name,mode,targetelement,multiselect) {
+	var self = this; //workaround due to varying definitions of "this" in variable scopes. see http://www.meanfreepath.com/support/epoch/epoch.html#self for details
+	//DEFINE PRIVATE METHODS
+	//-----------------------------------------------------------------------------
+	/**
+	* Declares and initializes the calendar variables.  All the variables here can be safely changed
+	* (within reason ;) by the developer
+	*/
+	function calConfig() {
+		self.versionNumber = '2.0.2';
+		self.displayYearInitial = self.curDate.getFullYear(); //the initial year to display on load
+		self.displayMonthInitial = self.curDate.getMonth(); //the initial month to display on load (0-11)
+		self.displayYear = self.displayYearInitial;
+		self.displayMonth = self.displayMonthInitial;
+
+		var d = new Date();
+		var curr_year = d.getFullYear();
+		self.minDate = new Date(curr_year,0,1);
+//		self.maxDate = new Date(2012,11,31);
+		self.maxDate = new Date(parseInt(curr_year)+parseInt(10),11,31);
+
+		self.startDay = 0; // the day the week will 'start' on: 0(Sun) to 6(Sat)
+		self.showWeeks = true; //whether the week numbers will be shown
+		self.selCurMonthOnly = true; //allow user to only select dates in the currently displayed month
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* All language settings for Epoch are made here.
+	* Check Date.dateFormat() for the Date object's language settings
+	*/
+	function setLang() {
+		self.daylist = new Array('S','M','T','W','T','F','S','S','M','T','W','T','F','S');
+		self.months_sh = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+		self.monthup_title = 'Go to the next month';
+		self.monthdn_title = 'Go to the previous month';
+		self.clearbtn_caption = 'Clear';
+		self.clearbtn_title = 'Clears any dates selected on the calendar';
+		self.maxrange_caption = 'This is the maximum range';
+		self.closebtn_caption = 'Close';
+		self.closebtn_title = 'Close the calendar';
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Initializes the standard Gregorian Calendar parameters
+	*/
+	function setDays() {
+		self.daynames = new Array();
+		var j=0;
+		for(var i=self.startDay;i<self.startDay + 7;i++) {
+			self.daynames[j++] = self.daylist[i];
+		}
+		self.monthDayCount = new Array(31,((self.curDate.getFullYear() - 2000) % 4 ? 28 : 29),31,30,31,30,31,31,30,31,30,31);
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the full DOM implementation of the calendar
+	*/
+	function createCalendar() {
+		var tbody, tr, td;
+		self.calendar = document.createElement('table');
+		self.calendar.setAttribute('id',self.name+'_calendar');
+		setClass(self.calendar,'calendar');
+		self.calendar.style.display = 'none'; //default to invisible
+		//to prevent IE from selecting text when clicking on the calendar
+		addEventHandler(self.calendar,'selectstart', function() {return false;});
+		addEventHandler(self.calendar,'drag', function() {return false;});
+		tbody = document.createElement('tbody');
+
+		//create the Main Calendar Heading
+		tr = document.createElement('tr');
+		td = document.createElement('td');
+		td.appendChild(createMainHeading());
+		tr.appendChild(td);
+		tbody.appendChild(tr);
+
+		//create the calendar Day Heading & the calendar Day Cells
+		tr = document.createElement('tr');
+		td = document.createElement('td');
+		self.calendar.celltable = document.createElement('table');
+		setClass(self.calendar.celltable,'cells');
+		self.calendar.celltable.appendChild(createDayHeading());
+		self.calendar.celltable.appendChild(createCalCells());
+		td.appendChild(self.calendar.celltable);
+		tr.appendChild(td);
+		tbody.appendChild(tr);
+
+		//create the calendar footer
+		tr = document.createElement('tr');
+		td = document.createElement('td');
+		td.appendChild(createFooter());
+		tr.appendChild(td);
+		tbody.appendChild(tr);
+
+		//add the tbody element to the main calendar table
+		self.calendar.appendChild(tbody);
+
+		//and add the onmouseover events to the calendar table
+		addEventHandler(self.calendar,'mouseover',cal_onmouseover);
+		addEventHandler(self.calendar,'mouseout',cal_onmouseout);
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the primary calendar heading, with months & years
+	*/
+	function createMainHeading() {
+		//create the containing <div> element
+		var container = document.createElement('div');
+		setClass(container,'mainheading');
+		//create the child elements and other variables
+		self.monthSelect = document.createElement('select');
+		self.yearSelect = document.createElement('select');
+		var monthDn = document.createElement('input'), monthUp = document.createElement('input');
+		var opt, i;
+		//fill the month select box
+		for(i=0;i<12;i++) {
+			opt = document.createElement('option');
+			opt.setAttribute('value',i);
+			if(self.displayMonth == i) {
+				opt.setAttribute('selected','selected');
+			}
+			opt.appendChild(document.createTextNode(self.months_sh[i]));
+			self.monthSelect.appendChild(opt);
+		}
+		//and fill the year select box
+		var yrMax = self.maxDate.getFullYear(), yrMin = self.minDate.getFullYear();
+		for(i=yrMin;i<=yrMax;i++) {
+			opt = document.createElement('option');
+			opt.setAttribute('value',i);
+			if(self.displayYear == i) {
+				opt.setAttribute('selected','selected');
+			}
+			opt.appendChild(document.createTextNode(i));
+			self.yearSelect.appendChild(opt);
+		}
+		//add the appropriate children for the month buttons
+		monthUp.setAttribute('type','button');
+		monthUp.setAttribute('value','>');
+		monthUp.setAttribute('title',self.monthup_title);
+		monthDn.setAttribute('type','button');
+		monthDn.setAttribute('value','<');
+		monthDn.setAttribute('title',self.monthdn_title);
+		self.monthSelect.owner = self.yearSelect.owner = monthUp.owner = monthDn.owner = self;  //hack to allow us to access self calendar in the events (<fix>??)
+
+		//assign the event handlers for the controls
+		function selectonchange()	{
+			if(self.goToMonth(self.yearSelect.value,self.monthSelect.value)) {
+				self.displayMonth = self.monthSelect.value;
+				self.displayYear = self.yearSelect.value;
+			}
+			else {
+				self.monthSelect.value = self.displayMonth;
+				self.yearSelect.value = self.displayYear;
+			}
+		}
+		addEventHandler(monthUp,'click',function(){self.nextMonth();});
+		addEventHandler(monthDn,'click',function(){self.prevMonth();});
+		addEventHandler(self.monthSelect,'change',selectonchange);
+		addEventHandler(self.yearSelect,'change',selectonchange);
+
+		//and finally add the elements to the containing div
+		container.appendChild(monthDn);
+		container.appendChild(self.monthSelect);
+		container.appendChild(self.yearSelect);
+		container.appendChild(monthUp);
+		return container;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the footer of the calendar - goes under the calendar cells
+	*/
+	function createFooter() {
+		var container = document.createElement('div');
+		var clearSelected = document.createElement('input');
+		clearSelected.setAttribute('type','button');
+		clearSelected.setAttribute('value',self.clearbtn_caption);
+		clearSelected.setAttribute('title',self.clearbtn_title);
+		clearSelected.owner = self;
+		addEventHandler(clearSelected,'click',function() {self.resetSelections(false);});
+		container.appendChild(clearSelected);
+		if(self.mode == 'popup') {
+			var closeBtn = document.createElement('input');
+			closeBtn.setAttribute('type','button');
+			closeBtn.setAttribute('value',self.closebtn_caption);
+			closeBtn.setAttribute('title',self.closebtn_title);
+			addEventHandler(closeBtn,'click',function(){self.hide();});
+			setClass(closeBtn,'closeBtn');
+			container.appendChild(closeBtn);
+		}
+		return container;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the heading containing the day names
+	*/
+	function createDayHeading() {
+		//create the table element
+		self.calHeading = document.createElement('thead');
+		setClass(self.calHeading,'caldayheading');
+		var tr = document.createElement('tr'), th;
+		self.cols = new Array(false,false,false,false,false,false,false);
+
+		//if we're showing the week headings, create an empty <td> for filler
+		if(self.showWeeks) {
+			th = document.createElement('th');
+			setClass(th,'wkhead');
+			tr.appendChild(th);
+		}
+		//populate the day titles
+		for(var dow=0;dow<7;dow++) {
+			th = document.createElement('th');
+			th.appendChild(document.createTextNode(self.daynames[dow]));
+			if(self.selectMultiple) { //if selectMultiple is true, assign the cell a CalHeading Object to handle all events
+				th.headObj = new CalHeading(self,th,(dow + self.startDay < 7 ? dow + self.startDay : dow + self.startDay - 7));
+			}
+			tr.appendChild(th);
+		}
+		self.calHeading.appendChild(tr);
+		return self.calHeading;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the table containing the calendar day cells
+	*/
+	function createCalCells() {
+		self.rows = new Array(false,false,false,false,false,false);
+		self.cells = new Array();
+		var row = -1, totalCells = (self.showWeeks ? 48 : 42);
+		var beginDate = new Date(self.displayYear,self.displayMonth,1);
+		var endDate = new Date(self.displayYear,self.displayMonth,self.monthDayCount[self.displayMonth]);
+		var sdt = new Date(beginDate);
+		sdt.setDate(sdt.getDate() + (self.startDay - beginDate.getDay()) - (self.startDay - beginDate.getDay() > 0 ? 7 : 0) );
+		//create the table element to hold the cells
+		self.calCells = document.createElement('tbody');
+		var tr,td;
+		var cellIdx = 0, cell, week, dayval;
+
+		for(var i=0;i<totalCells;i++) {
+			if(self.showWeeks) { //if we are showing the week headings
+				if(i % 8 == 0) {
+					row++;
+					week = sdt.getWeek(self.startDay);
+					tr = document.createElement('tr');
+					td = document.createElement('td');
+					if(self.selectMultiple) { //if selectMultiple is enabled, create the associated weekObj objects
+						td.weekObj = new WeekHeading(self,td,week,row)
+					}
+					else {//otherwise just set the class of the td for consistent look
+						setClass(td,'wkhead');
+					}
+					td.appendChild(document.createTextNode(week));
+					tr.appendChild(td);
+					i++;
+				}
+			}
+			else if(i % 7 == 0) { //otherwise, new row every 7 cells
+				row++;
+				week = sdt.getWeek(self.startDay);
+				tr = document.createElement('tr');
+			}
+			//create the day cells
+			dayval = sdt.getDate();
+			td = document.createElement('td');
+			td.appendChild(document.createTextNode(dayval));
+			cell = new CalCell(self,td,sdt,row,week);//,'normal',sdt.getTime() >= self.minDate.getTime() && sdt.getTime() <= self.maxDate.getTime());
+			self.cells[cellIdx] = cell;
+			td.cellObj = cell;
+			tr.appendChild(td);
+			self.calCells.appendChild(tr);
+			self.reDraw(cellIdx++); //and paint the cell according to its properties
+			sdt.setDate(dayval + 1); //increment the date
+		}
+		return self.calCells;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Runs all the operations necessary to change the mode of the calendar
+	* @param HTMLInputElement targetelement
+	*/
+	function setMode(targetelement)	{
+		if(self.mode == 'popup') { //set positioning to absolute for popup
+			self.calendar.style.position = 'absolute';
+		}
+		//if a target element has been set, append the calendar to it
+		if(targetelement) {
+			switch(self.mode) {
+				case 'flat':
+					self.tgt = targetelement;
+					self.tgt.appendChild(self.calendar);
+					self.visible = true;
+					break;
+				case 'popup':
+					self.calendar.style.position = 'absolute';
+					document.body.appendChild(self.calendar);
+					self.setTarget(targetelement,false);
+					break;
+			}
+		}
+		else { //otherwise, add the calendar to the document.body (useful if targetelement will not be defined until after the calendar is initialized)
+			document.body.appendChild(self.calendar);
+			self.visible = false;
+		}
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Removes the calendar table cells from the DOM (does not delete the cell objects associated with them)
+	*/
+	function deleteCells() {
+		self.calendar.celltable.removeChild(self.calendar.celltable.childNodes[1]); //remove the tbody element from the cell table
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the CSS class of the element, W3C & IE
+	* @param HTMLElement element
+	* @param string className
+	*/
+	function setClass(element,className) {
+		element.setAttribute('class',className);
+		element.setAttribute('className',className); //<iehack>
+	}
+	/**
+	* Updates a cell's data, including css class and selection properties
+	* @param int cellindex
+	*/
+	function setCellProperties(cellindex) {
+		var cell = self.cells[cellindex];
+		var date;
+		idx = self.dateInArray(self.dates,cell.date);
+		if(idx > -1) {
+			date = self.dates[idx]; //reduce indirection
+			cell.date.selected = date.selected || false;
+			cell.date.type = date.type;
+			cell.date.canSelect = date.canSelect;
+			cell.setTitle(date.title);
+			cell.setURL(date.href);
+			cell.setHTML(date.cellHTML);
+		}
+		else {
+			cell.date.selected = false; //if the cell's date isn't in the dates array, set it's selected value to false
+		}
+		//make all cells lying outside the min and max dates un-selectable
+		if(cell.date.getTime() < self.minDate.getTime() || cell.date.getTime() > self.maxDate.getTime()) {
+			cell.date.canSelect = false;
+		}
+		cell.setClass();
+	}
+	//-----------------------------------------------------------------------------
+	function cal_onmouseover() {
+		self.mousein = true;
+	}
+	//-----------------------------------------------------------------------------
+	function cal_onmouseout()	{
+		self.mousein = false;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	 * Updates the calendar's selectedDates pointer array
+	 */
+	function updateSelectedDates() {
+		var idx = 0;
+		self.selectedDates = new Array();
+		for(i=0;i<self.dates.length;i++) {
+			if(self.dates[i].selected) {
+				self.selectedDates[idx++] = self.dates[i];
+			}
+		}
+	}
+	//PUBLIC METHODS
+	//-----------------------------------------------------------------------------
+	/**
+	* Find a date in the given array, returning its index if found, -1 if not
+	* @param array arr
+	* @param Date searchVal
+	* @param int startIndex
+	* @return int
+	*/
+	self.dateInArray = function(arr,searchVal,startIndex) {
+		startIndex = (startIndex != null ? startIndex : 0); //default startIndex to 0, if not set
+		for(var i=startIndex;i<arr.length;i++) {
+			if(searchVal.getUeDay() == arr[i].getUeDay()) {
+				return i;
+			}
+		}
+		return -1;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Changes the target element of this calendar to another input.
+	* Many thanks to Jake Olefsky - jake@olefsky.com
+	* @param HTMLInputElement targetelement
+	* @param bool focus
+	*/
+	self.setTarget = function (targetelement, focus)
+	{
+		//if this is a popup calendar
+		if(self.mode == 'popup') {
+			//declare the event handlers for the target element
+			function popupFocus() {
+				self.show();
+			}
+			function popupBlur() {
+				if(!self.mousein){
+					self.hide();
+				}
+			}
+			function popupKeyDown() {
+				self.hide();
+			}
+			//unset old target element event handlers (if there is one yet)
+			if(self.tgt) {
+				removeEventHandler(self.tgt,'focus',popupFocus);
+				removeEventHandler(self.tgt,'blur',popupBlur);
+				removeEventHandler(self.tgt,'keydown',popupKeyDown);
+			}
+			//and set the new target element
+			self.tgt = targetelement;
+			//create a pointer to the INPUT's date object and init the new data array
+			var dto = self.tgt.dateObj,pdateArr = new Array;
+			//if a date is set for the target element
+			if(dto) {
+				if(self.tgt.value.length) { //load it into the calendar...
+					pdateArr[0] = dto;
+				}
+				self.goToMonth(dto.getFullYear(),dto.getMonth()); //...and go to the target's month/year
+			}
+			self.selectDates(pdateArr,true,true,true);
+
+			self.topOffset = self.tgt.offsetHeight; // the vertical distance (in pixels) to display the calendar from the Top of its input element
+			self.leftOffset = 0; 					// the horizontal distance (in pixels) to display the calendar from the Left of its input element
+			self.updatePos(self.tgt);
+			//and add the event handlers to the new element
+			addEventHandler(self.tgt,'focus',popupFocus);
+			addEventHandler(self.tgt,'blur',popupBlur);
+			addEventHandler(self.tgt,'keydown',popupKeyDown);
+			if(focus !== false) { //focus the target element immediately, unless otherwise specified
+				popupFocus();
+			}
+		}
+		else { //if this is a flat or inline calendar
+			//if the target is already set, remove the calendar's DOM representation from it
+			if(self.tgt) {
+				self.tgt.removeChild(self.calendar);
+			}
+			//now, set the calendar's target to the new target element, and show the calendar
+			self.tgt = targetelement;
+			self.tgt.appendChild(self.calendar);
+			self.show();
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Go to the next month.  if the month is December, go to January of the next year
+	* Returns true if the month will be incremented
+	* @return bool
+	*/
+	self.nextMonth = function () {
+		var month = self.displayMonth;
+		var year = self.displayYear;
+		//increment the month/year values, provided they're within the min/max ranges
+		if(self.displayMonth < 11) { //i.e. if currently in the year
+			month++;
+		}
+		else if(self.yearSelect.value < self.maxDate.getFullYear()) { //if not, increment the year as well
+			month = 0;
+			year++;
+		}
+		return self.goToMonth(year,month);
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Go to the previous month - if the month is January, go to December of the previous year.
+	* Returns true if the month will be decremented
+	* @return bool
+	*/
+	self.prevMonth = function () {
+		var month = self.displayMonth;
+		var year = self.displayYear;
+		//increment the month/year values, provided they're within the min/max ranges
+		if(self.displayMonth > 0) { //i.e. if currently in the year
+			month--;
+		}
+		else { //if not, decrement the year as well
+			month = 11;
+			year--;
+		}
+		return self.goToMonth(year,month);
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the calendar to display the requested month/year, returning true if the
+	* date is within the minimum and maximum allowed dates
+	* @param int year
+	* @param int month
+	* @return bool
+	*/
+	self.goToMonth = function (year,month) {
+		var testdatemin = new Date(year, month, 31);
+		var testdatemax = new Date(year, month, 1);
+		if(testdatemin >= self.minDate && testdatemax <= self.maxDate) {
+			self.monthSelect.value = self.displayMonth = month;
+			self.yearSelect.value = self.displayYear = year;
+			//recreate the calendar for the new month
+			createCalCells();
+			deleteCells();
+			self.calendar.celltable.appendChild(self.calCells);
+			return true;
+		}
+		else {
+			alert(self.maxrange_caption);
+			return false;
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Moves the calendar's position to the target element's location (popup mode only)
+	*/
+	self.updatePos = function (target) {
+		if(self.mode == 'popup') {
+			self.calendar.style.top = getTop(target) + self.topOffset + 'px';
+			self.calendar.style.left = getLeft(target) + self.leftOffset + 'px';
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Displays the calendar
+	*/
+	self.show = function ()	{
+		self.updatePos(self.tgt); //update the calendar position, in case the page layout has changed since loading
+		self.calendar.style.display = 'block'; //'table'; //<iehack> 'table' is the W3C-recommended spec, but IE isn't a fan of those
+		self.visible = true;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Hides the calendar
+	*/
+	self.hide = function () {
+		self.calendar.style.display = 'none';
+		self.visible = false;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Toggles (shows/hides) the calendar depending on its current state
+	*/
+	self.toggle = function () {
+		self.visible ? self.hide() : self.show();
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Adds the array "dates" to the calendar's dates array, removing duplicate dates,
+	* and redraws the calendar if redraw is true
+	* @param array dates
+	* @param bool redraw
+	*/
+	self.addDates = function (dates,redraw) {
+		var i;
+		for(i=0;i<dates.length;i++) {
+			if(self.dateInArray(self.dates,dates[i]) == -1) { //if the date isn't already in the array, add it!
+				self.dates[self.dates.length] = dates[i];
+			}
+		}
+		//now rebuild the selectedDates pointer array
+		updateSelectedDates();
+		if(redraw != false) { //redraw  the calendar if "redraw" is false or undefined
+			self.reDraw();
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Removes the dates from the calendar's dates array and redraws the calendar
+	* if redraw is true
+	* @param array dates
+	* @param bool redraw
+	*/
+	self.removeDates = function (dates,redraw) {
+		var idx;
+		for(var i=0;i<dates.length;i++) {
+			idx = self.dateInArray(self.dates,dates[i]);
+			if(idx != -1) { //search for the dates in the dates array, removing them if the dates match
+				self.dates.splice(idx,1);
+			}
+		}
+		updateSelectedDates();
+		if(redraw != false) { //redraw  the calendar if "redraw" is true or undefined
+			self.reDraw();
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Selects or Deselects an array of dates
+	* @param Array inpdates
+	* @param bool selectVal
+	* @param bool redraw
+	* @param bool removeothers
+	*/
+	self.selectDates = function (inpdates,selectVal,redraw,removeothers) {
+		var i, idx;
+		if(removeothers == true) {
+			for(i=0;i<self.dates.length;i++) {
+				self.dates[i].selected = false;
+			}
+		}
+		for(i=0;i<inpdates.length;i++) {
+			idx = self.dateInArray(self.dates,inpdates[i]);
+			if(selectVal == true) {
+				inpdates[i].selected = true;
+				if(idx == -1) { //if the date does not exist in the calendar's dates array, add it
+					self.dates[self.dates.length] = inpdates[i];
+				}
+				else { //if not, just select it
+					self.dates[idx].selected = true;
+				}
+			}
+			else { //if deselecting...
+				if(idx > -1) { //if the date is found, deselect and/or remove it from the calendar's dates array
+					self.dates[idx].selected = inpdates[i].selected = false;
+					if(self.dates[idx].type == 'normal') { //remove 'normal' dates from the dates array, since they're useless unless selected
+						self.dates.splice(idx,1);
+					}
+				}
+			}
+		}
+		//now rebuild the selectedDates pointer array
+		updateSelectedDates();
+		if(redraw != false) { //redraw the calendar if "redraw" is false or undefined
+			self.reDraw();
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Adds the dates in dates as hidden inputs to the form "form".  inputname
+	* is the name of each hidden element. "form" can either be a pointer to the form's
+	* DOM element or its id string.
+	* @param mixed form
+	* @param string inputname
+	*/
+	self.sendForm = function(form,inputname) {
+		var inpname = inputname || 'epochdates', f, inp;
+		f = (typeof(form) == 'string' ? document.getElementById(form) : form);
+		if(!f) {
+			alert('ERROR: Invalid form input');
+			return false;
+		}
+		for(var i=0;i<self.dates.length;i++) {
+			inp = document.createElement('input');
+			inp.setAttribute('type','hidden');
+			inp.setAttribute('name',inpname + '['+i+']');
+			inp.setAttribute('value',encodeURIComponent(self.dates[i].dateFormat('Y-m-d')));  //default to the ISO date format
+			f.appendChild(inp);
+		}
+		return true;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Erases the dates array and resets the calendar's selection variables to defaults.
+	* If retMonth is true, the calendar will return to the initial default month/year
+	* @param bool retMonth
+	*/
+	self.resetSelections = function (retMonth) {
+		var dateArray = new Array();
+		var dt = self.dates;
+		for(var i=0;i<dt.length;i++) {
+			if(dt[i].selected) {
+				dateArray[dateArray.length] = dt[i];
+			}
+		}
+		self.selectDates(dateArray,false,false);
+		self.rows = new Array(false,false,false,false,false,false,false);
+		self.cols = new Array(false,false,false,false,false,false,false);
+		if(self.mode == 'popup') { //hide the calendar and clear the input element if in popup mode
+			self.tgt.value = '';
+			self.hide();
+		}
+		retMonth == true ? self.goToMonth(self.displayYearInitial,self.displayMonthInitial) : self.reDraw();
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Reapplies all the CSS classes for the calendar cells - usually called after changing their state
+	* If index is specified, it will redraw that cell only.
+	* @param int index
+	*/
+	self.reDraw = function (index) {
+		self.state = 1;
+		var len = index ? index + 1 : self.cells.length;
+		for(var i = index || 0;i<len;i++) {
+			setCellProperties(i);
+		}
+		self.state = 2;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Returns the index of the cell whose date value matches "date", or -1 if not found
+	* @param Date date
+	* @return int
+	*/
+	self.getCellIndex = function(date) {
+		for(var i=0;i<self.cells.length;i++) {
+			if(self.cells[i].date.getUeDay() == date.getUeDay()) {
+				return i;
+			}
+		}
+		return -1;
+	};
+	//-----------------------------------------------------------------------------
+	//begin constructor code:
+
+	//PUBLIC VARIABLES
+	self.state = 0;
+	self.name = name;
+	self.curDate = new Date();
+	self.mode = mode;
+	self.selectMultiple = (multiselect == true); //'false' if not true or not set at all
+	//the various calendar variables
+	self.dates = new Array();
+	self.selectedDates = new Array();
+
+	self.calendar;
+	self.calHeading;
+	self.calCells;
+	self.rows;
+	self.cols;
+	self.cells = new Array();
+	//The controls
+	self.monthSelect;
+	self.yearSelect;
+	self.mousein = false;
+
+	//Initialize the calendar and its variables{
+	calConfig();
+	setLang();
+	setDays();
+	createCalendar(); //create the calendar DOM element and its children, and their related objects
+	targetelement = typeof(targetelement) == 'string' ? document.getElementById(targetelement) : targetelement;
+	setMode(targetelement);
+	self.state = 2; //0: initializing, 1: redrawing, 2: finished!
+	self.visible ? self.show() : self.hide();
+}
+//-----------------------------------------------------------------------------
+/*****************************************************************************/
+/**
+* Object that contains the methods and properties for the calendar day headings
+*/
+function CalHeading(owner,tableCell,dayOfWeek) {
+	//-----------------------------------------------------------------------------
+	function DayHeadingonclick() {//selects/deselects the days for this object's day of week
+		//reduce indirection:
+		var sdates = owner.dates;
+		var cells = owner.cells;
+		var dateArray = new Array();
+		owner.cols[dayOfWeek] = !owner.cols[dayOfWeek];
+		for(var i=0;i<cells.length;i++) { //cycle through all the cells in the calendar, selecting all cells with the same dayOfWeek as this heading
+			if(cells[i].dayOfWeek == dayOfWeek && cells[i].date.canSelect && (!owner.selCurMonthOnly || cells[i].date.getMonth() == owner.displayMonth && cells[i].date.getFullYear() == owner.displayYear)) { //if the cell's DoW matches, with other conditions
+				dateArray[dateArray.length] = cells[i].date;
+			}
+		}
+		owner.selectDates(dateArray,owner.cols[dayOfWeek],true);
+	}
+	//-----------------------------------------------------------------------------
+	var self = this;
+	self.dayOfWeek = dayOfWeek;
+	addEventHandler(tableCell,'mouseup',DayHeadingonclick);
+}
+/*****************************************************************************/
+/**
+* Object that contains the methods and properties for the calendar week headings
+*/
+function WeekHeading(owner,tableCell,week,tableRow) {
+	//-----------------------------------------------------------------------------
+	function weekHeadingonclick() {
+		//reduce indirection:
+		var cells = owner.cells;
+		var sdates = owner.dates;
+		var dateArray = new Array();
+		owner.rows[tableRow] = !owner.rows[tableRow];
+		for(var i=0;i<cells.length;i++) {
+			if(cells[i].tableRow == tableRow && cells[i].date.canSelect && (!owner.selCurMonthOnly || cells[i].date.getMonth() == owner.displayMonth && cells[i].date.getFullYear() == owner.displayYear)) { //if the cell's DoW matches, with other conditions)
+				dateArray[dateArray.length] = cells[i].date;
+			}
+		}
+		owner.selectDates(dateArray,owner.rows[tableRow],true);
+	}
+	//-----------------------------------------------------------------------------
+	var self = this;
+	self.week = week;
+	tableCell.setAttribute('class','wkhead');
+	tableCell.setAttribute('className','wkhead'); //<iehack>
+	addEventHandler(tableCell,'mouseup',weekHeadingonclick);
+}
+/*****************************************************************************/
+/**
+* Object that holds all data & code related to a calendar cell
+*/
+/**
+ * The CalCell constructor function
+ * @param Epoch owner
+ * @param HTMLTableCellElement tableCell
+ * @param Date dateObj
+ * @param int row
+ * @param int week
+ */
+function CalCell(owner,tableCell,dateObj,row,week) {
+	var self = this;
+	//-----------------------------------------------------------------------------
+	function calCellonclick() {
+		if(self.date.canSelect) {
+			if(owner.selectMultiple == true) { //if we can select multiple cells simultaneously, add the currently selected self's date to the dates array
+				owner.selectDates(new Array(self.date),!self.date.selected,false);
+				self.setClass(); //update the current cell's style to reflect the changes - a full redraw isn't necessary
+			}
+			else { //if we can only select one date at a time
+				owner.selectDates(new Array(self.date),true,false,true);
+				if(owner.mode == 'popup') { //update the target element's value and hide the calendar if in popup mode
+					owner.tgt.value = self.date.dateFormat(); //use the default date format defined in dateFormat
+					owner.tgt.dateObj = new Date(self.date); //add a Date object to the target element for later reference
+					owner.hide();
+				}
+				owner.reDraw(); //redraw all the calendar cells
+			}
+		}
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Replicate the CSS :hover effect for non-supporting browsers <iehack>
+	*/
+	function calCellonmouseover() {
+		if(self.date.canSelect) {
+			tableCell.setAttribute('class',self.cellClass + ' hover');
+			tableCell.setAttribute('className',self.cellClass + ' hover');
+		}
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Replicate the CSS :hover effect for non-supporting browsers <iehack>
+	*/
+	function calCellonmouseout() {
+		self.setClass();
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the CSS class of the cell based on the specified criteria
+	*/
+	self.setClass = function ()
+	{
+		if(self.date.canSelect !== false) {
+			if(self.date.selected) {
+				self.cellClass = 'cell_selected';
+			}
+			else if(owner.displayMonth != self.date.getMonth() ) {
+				self.cellClass = 'notmnth';
+			}
+			else if(self.date.type == 'holiday') {
+				self.cellClass = 'hlday';
+			}
+			else if(self.dayOfWeek > 0 && self.dayOfWeek < 6) {
+				self.cellClass = 'wkday';
+			}
+			else {
+				self.cellClass = 'wkend';
+			}
+		}
+		else {
+			self.cellClass = 'noselect';
+		}
+		//highlight the current date
+		if(self.date.getUeDay() == owner.curDate.getUeDay()) {
+			self.cellClass = self.cellClass + ' curdate';
+		}
+		tableCell.setAttribute('class',self.cellClass);
+		tableCell.setAttribute('className',self.cellClass); //<iehack>
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the cell's hyperlink, if declared
+	* @param string href
+	* @param string type ('anchor' or 'js' - default 'anchor')
+	*/
+	self.setURL = function(href,type) {
+		if(href) {
+			if(type == 'js') { //Make the WHOLE cell be a clickable link
+				addEventHandler(self.tableCell,'mousedown',function(){window.location.href = href;});
+			}
+			else { //make only the date number of the cell a clickable link:
+				var url = document.createElement('a');
+				url.setAttribute('href',href);
+				url.appendChild(document.createTextNode(self.date.getDate()));
+				self.tableCell.replaceChild(url,self.tableCell.firstChild); //assumes the first child of the cell DOM node is the date text
+			}
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the title (i.e. tooltip) that appears when a user holds their mouse cursor over a cell
+	* @param string titleStr
+	*/
+	self.setTitle = function(titleStr) {
+		if(titleStr && titleStr.length > 0) {
+			self.title = titleStr;
+			self.tableCell.setAttribute('title',titleStr);
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the internal html of the cell, using a string containing html markup
+	* @param string html
+	*/
+	self.setHTML = function(html) {
+		if(html && html.length > 0) {
+			if(self.tableCell.childNodes[1]) {
+				self.tableCell.childNodes[1].innerHTML = html;
+			}
+			else {
+				var htmlCont = document.createElement('div');
+				htmlCont.innerHTML = html;
+				self.tableCell.appendChild(htmlCont);
+			}
+		}
+	};
+	//-----------------------------------------------------------------------------
+	self.cellClass;			//the CSS class of the cell
+	self.tableRow = row;
+	self.tableCell = tableCell;
+	self.date = new Date(dateObj);
+	self.date.canSelect = true; //whether this cell can be selected or not - always true unless set otherwise externally
+	self.date.type = 'normal';  //i.e. normal date, holiday, etc - always true unless set otherwise externally
+	self.date.selected = false;	//whether the cell is selected (and is therefore stored in the owner's dates array)
+	self.date.cellHTML = '';
+	self.dayOfWeek = self.date.getDay();
+	self.week = week;
+	//assign the event handlers for the table cell element
+	addEventHandler(tableCell,'click', calCellonclick);
+	addEventHandler(tableCell,'mouseover', calCellonmouseover);
+	addEventHandler(tableCell,'mouseout', calCellonmouseout);
+	self.setClass();
+}
+/*****************************************************************************/
+Date.prototype.getDayOfYear = function () //returns the day of the year for this date
+{
+	return parseInt((this.getTime() - new Date(this.getFullYear(),0,1).getTime())/86400000 + 1);
+};
+//-----------------------------------------------------------------------------
+/**
+ * Returns the week number for this date.  dowOffset is the day of week the week
+ * "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday),
+ * the week returned is the ISO 8601 week number.
+ * @param int dowOffset
+ * @return int
+ */
+Date.prototype.getWeek = function (dowOffset) {
+	dowOffset = typeof(dowOffset) == 'int' ? dowOffset : 0; //default dowOffset to zero
+	var newYear = new Date(this.getFullYear(),0,1);
+	var day = newYear.getDay() - dowOffset; //the day of week the year begins on
+	day = (day >= 0 ? day : day + 7);
+	var weeknum, daynum = Math.floor((this.getTime() - newYear.getTime() - (this.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1;
+	//if the year starts before the middle of a week
+	if(day < 4) {
+		weeknum = Math.floor((daynum+day-1)/7) + 1;
+		if(weeknum > 52) {
+			nYear = new Date(this.getFullYear() + 1,0,1);
+			nday = nYear.getDay() - dowOffset;
+			nday = nday >= 0 ? nday : nday + 7;
+			weeknum = nday < 4 ? 1 : 53; //if the next year starts before the middle of the week, it is week #1 of that year
+		}
+	}
+	else {
+		weeknum = Math.floor((daynum+day-1)/7);
+	}
+	return weeknum;
+};
+//-----------------------------------------------------------------------------
+Date.prototype.getUeDay = function () //returns the number of DAYS since the UNIX Epoch - good for comparing the date portion
+{
+	return parseInt(Math.floor((this.getTime() - this.getTimezoneOffset() * 60000)/86400000)); //must take into account the local timezone
+};
+//-----------------------------------------------------------------------------
+var __DATE_FORMAT = 'm/d/Y';
+Date.prototype.dateFormat = function(format)
+{
+	if(!format) { // the default date format to use - can be customized to the current locale
+		format = __DATE_FORMAT;
+	}
+	LZ = function(x) {return(x < 0 || x > 9 ? '' : '0') + x};
+	var MONTH_NAMES = new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+	var DAY_NAMES = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+	var result="";
+	var i_format=0;
+	var c="";
+	var token="";
+	var y=this.getFullYear().toString();
+	var M=this.getMonth()+1;
+	var d=this.getDate();
+	var E=this.getDay();
+	var H=this.getHours();
+	var m=this.getMinutes();
+	var s=this.getSeconds();
+	value = {
+		Y: y.toString(),
+		y: y.substring(2),
+		n: M,
+		m: LZ(M),
+		F: MONTH_NAMES[M-1],
+		M: MONTH_NAMES[M+11],
+		j: d,
+		d: LZ(d),
+		D: DAY_NAMES[E+7],
+		l: DAY_NAMES[E],
+		G: H,
+		H: LZ(H)
+	};
+	if (H==0) {value['g']=12;}
+	else if (H>12){value['g']=H-12;}
+	else {value['g']=H;}
+	value['h']=LZ(value['g']);
+	if (H > 11) {value['a']='pm'; value['A'] = 'PM';}
+	else { value['a']='am'; value['A'] = 'AM';}
+	value['i']=LZ(m);
+	value['s']=LZ(s);
+	//construct the result string
+	while (i_format < format.length) {
+		c=format.charAt(i_format);
+		token="";
+		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
+			token += format.charAt(i_format++);
+		}
+		if (value[token] != null) { result=result + value[token]; }
+		else { result=result + token; }
+	}
+	return result;
+};
+/*****************************************************************************/
+//-----------------------------------------------------------------------------
+function addEventHandler(element, type, func) { //unfortunate hack to deal with Internet Explorer's horrible DOM event model <iehack>
+	if(element.addEventListener) {
+		element.addEventListener(type,func,false);
+	}
+	else if (element.attachEvent) {
+		element.attachEvent('on'+type,func);
+	}
+}
+//-----------------------------------------------------------------------------
+function removeEventHandler(element, type, func) { //unfortunate hack to deal with Internet Explorer's horrible DOM event model <iehack>
+	if(element.removeEventListener) {
+		element.removeEventListener(type,func,false);
+	}
+	else if (element.attachEvent) {
+		element.detachEvent('on'+type,func);
+	}
+}
+//-----------------------------------------------------------------------------
+function getTop(element) {//returns the absolute Top value of element, in pixels
+	var oNode = element;
+	var iTop = 0;
+
+	while(oNode.tagName != 'HTML') {
+		iTop += oNode.offsetTop || 0;
+		if(oNode.offsetParent) { //i.e. the parent element is not hidden
+			oNode = oNode.offsetParent;
+		}
+		else {
+			break;
+		}
+	}
+	return iTop;
+}
+//-----------------------------------------------------------------------------
+function getLeft(element) { //returns the absolute Left value of element, in pixels
+	var oNode = element;
+	var iLeft = 0;
+	while(oNode.tagName != 'HTML') {
+		iLeft += oNode.offsetLeft || 0;
+		if(oNode.offsetParent) { //i.e. the parent element is not hidden
+			oNode = oNode.offsetParent;
+		}
+		else {
+			break;
+		}
+	}
+	return iLeft;
+}
+//-----------------------------------------------------------------------------
Index: /afridex/plugins/fresh-page/js/lib/prototype.js
===================================================================
--- /afridex/plugins/fresh-page/js/lib/prototype.js (revision 21)
+++ /afridex/plugins/fresh-page/js/lib/prototype.js (revision 21)
@@ -0,0 +1,2006 @@
+/*  Prototype JavaScript framework, version 1.5.0_rc0
+ *  (c) 2005 Sam Stephenson <sam@conio.net>
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.5.0_rc0',
+  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+  emptyFunction: function() {},
+  K: function(x) {return x}
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.inspect = function(object) {
+  try {
+    if (object == undefined) return 'undefined';
+    if (object == null) return 'null';
+    return object.inspect ? object.inspect() : object.toString();
+  } catch (e) {
+    if (e instanceof RangeError) return '...';
+    throw e;
+  }
+}
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this;
+  return function(event) {
+    return __method.call(object, event || window.event);
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    var digits = this.toString(16);
+    if (this < 16) return '0' + digits;
+    return digits;
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  }
+});
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0; i < arguments.length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += (replacement(match) || '').toString();
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var div = document.createElement('div');
+    var text = document.createTextNode(this);
+    div.appendChild(text);
+    return div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+  },
+
+  toQueryParams: function() {
+    var pairs = this.match(/^\??(.*)$/)[1].split('&');
+    return pairs.inject({}, function(params, pairString) {
+      var pair = pairString.split('=');
+      params[pair[0]] = pair[1];
+      return params;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  camelize: function() {
+    var oStringList = this.split('-');
+    if (oStringList.length == 1) return oStringList[0];
+
+    var camelizedString = this.indexOf('-') == 0
+      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+      : oStringList[0];
+
+    for (var i = 1, len = oStringList.length; i < len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+
+    return camelizedString;
+  },
+
+  inspect: function() {
+    return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + (object[match[3]] || '').toString();
+    });
+  }
+}
+
+var $break    = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        try {
+          iterator(value, index++);
+        } catch (e) {
+          if (e != $continue) throw e;
+        }
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function (iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.collect(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.collect(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.collect(Prototype.K);
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0; i < iterable.length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0; i < this.length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != undefined || value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value && value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0; i < this.length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  }
+});
+var Hash = {
+  _each: function(iterator) {
+    for (var key in this) {
+      var value = this[key];
+      if (typeof value == 'function') continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject($H(this), function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  toQueryString: function() {
+    return this.map(function(pair) {
+      return pair.map(encodeURIComponent).join('=');
+    }).join('&');
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  }
+}
+
+function $H(object) {
+  var hash = Object.extend({}, object || {});
+  Object.extend(hash, Enumerable);
+  Object.extend(hash, Hash);
+  return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    do {
+      iterator(value);
+      value = value.succ();
+    } while (this.include(value));
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responderToAdd) {
+    if (!this.include(responderToAdd))
+      this.responders.push(responderToAdd);
+  },
+
+  unregister: function(responderToRemove) {
+    this.responders = this.responders.without(responderToRemove);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (responder[callback] && typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+  },
+
+  responseIsSuccess: function() {
+    return this.transport.status == undefined
+        || this.transport.status == 0
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  responseIsFailure: function() {
+    return !this.responseIsSuccess();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    var parameters = this.options.parameters || '';
+    if (parameters.length > 0) parameters += '&_=';
+
+    try {
+      this.url = url;
+      if (this.options.method == 'get' && parameters.length > 0)
+        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.options.method, this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) {
+        this.transport.onreadystatechange = this.onStateChange.bind(this);
+        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+      }
+
+      this.setRequestHeaders();
+
+      var body = this.options.postBody ? this.options.postBody : parameters;
+      this.transport.send(this.options.method == 'post' ? body : null);
+
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  setRequestHeaders: function() {
+    var requestHeaders =
+      ['X-Requested-With', 'XMLHttpRequest',
+       'X-Prototype-Version', Prototype.Version,
+       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
+
+    if (this.options.method == 'post') {
+      requestHeaders.push('Content-type', this.options.contentType);
+
+      /* Force "Connection: close" for Mozilla browsers to work around
+       * a bug where XMLHttpReqeuest sends an incorrect Content-length
+       * header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType)
+        requestHeaders.push('Connection', 'close');
+    }
+
+    if (this.options.requestHeaders)
+      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+    for (var i = 0; i < requestHeaders.length; i += 2)
+      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState != 1)
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  header: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) {}
+  },
+
+  evalJSON: function() {
+    try {
+      return eval('(' + this.header('X-JSON') + ')');
+    } catch (e) {}
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  respondToReadyState: function(readyState) {
+    var event = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (event == 'Complete') {
+      try {
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + event, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+    if (event == 'Complete')
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.containers = {
+      success: container.success ? $(container.success) : $(container),
+      failure: container.failure ? $(container.failure) :
+        (container.success ? null : $(container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, object) {
+      this.updateContent();
+      onComplete(transport, object);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.responseIsSuccess() ?
+      this.containers.success : this.containers.failure;
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts)
+      response = response.stripScripts();
+
+    if (receiver) {
+      if (this.options.insertion) {
+        new this.options.insertion(receiver, response);
+      } else {
+        Element.update(receiver, response);
+      }
+    }
+
+    if (this.responseIsSuccess()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $() {
+  var results = [], element;
+  for (var i = 0; i < arguments.length; i++) {
+    element = arguments[i];
+    if (typeof element == 'string')
+      element = document.getElementById(element);
+    results.push(Element.extend(element));
+  }
+  return results.length < 2 ? results[0] : results;
+}
+
+document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  return $A(children).inject([], function(elements, child) {
+    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      elements.push(Element.extend(child));
+    return elements;
+  });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element)
+  var Element = new Object();
+
+Element.extend = function(element) {
+  if (!element) return;
+  if (_nativeExtensions) return element;
+
+  if (!element._extended && element.tagName && element != window) {
+    var methods = Element.Methods, cache = Element.extend.cache;
+    for (property in methods) {
+      var value = methods[property];
+      if (typeof value == 'function')
+        element[property] = cache.findOrStore(value);
+    }
+  }
+
+  element._extended = true;
+  return element;
+}
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+}
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      Element[Element.visible(element) ? 'hide' : 'show'](element);
+    }
+  },
+
+  hide: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = 'none';
+    }
+  },
+
+  show: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = '';
+    }
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+  },
+
+  update: function(element, html) {
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  replace: function(element, html) {
+    element = $(element);
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  getHeight: function(element) {
+    element = $(element);
+    return element.offsetHeight;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).include(className);
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).add(className);
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).remove(className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    for (var i = 0; i < element.childNodes.length; i++) {
+      var node = element.childNodes[i];
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        Element.remove(node);
+    }
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.match(/^\s*$/);
+  },
+
+  childOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var x = element.x ? element.x : element.offsetLeft,
+        y = element.y ? element.y : element.offsetTop;
+    window.scrollTo(x, y);
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    var value = element.style[style.camelize()];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style.camelize()];
+      }
+    }
+
+    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+    return value == 'auto' ? null : value;
+  },
+
+  setStyle: function(element, style) {
+    element = $(element);
+    for (var name in style)
+      element.style[name.camelize()] = style[name];
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    if (Element.getStyle(element, 'display') != 'none')
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = '';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = 'none';
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element._overflow = element.style.overflow;
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element.style.overflow = element._overflow;
+    element._overflow = undefined;
+  }
+}
+
+Object.extend(Element, Element.Methods);
+
+var _nativeExtensions = false;
+
+if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  var HTMLElement = {}
+  HTMLElement.prototype = document.createElement('div').__proto__;
+}
+
+Element.addMethods = function(methods) {
+  Object.extend(Element.Methods, methods || {});
+
+  if(typeof HTMLElement != 'undefined') {
+    var methods = Element.Methods, cache = Element.extend.cache;
+    for (property in methods) {
+      var value = methods[property];
+      if (typeof value == 'function')
+        HTMLElement.prototype[property] = cache.findOrStore(value);
+    }
+    _nativeExtensions = true;
+  }
+}
+
+Element.addMethods();
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        var tagName = this.element.tagName.toLowerCase();
+        if (tagName == 'tbody' || tagName == 'tr') {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set(this.toArray().concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set(this.select(function(className) {
+      return className != classNameToRemove;
+    }).join(' '));
+  },
+
+  toString: function() {
+    return this.toArray().join(' ');
+  }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Selector = Class.create();
+Selector.prototype = {
+  initialize: function(expression) {
+    this.params = {classNames: []};
+    this.expression = expression.toString().strip();
+    this.parseExpression();
+    this.compileMatcher();
+  },
+
+  parseExpression: function() {
+    function abort(message) { throw 'Parse error in selector: ' + message; }
+
+    if (this.expression == '')  abort('empty expression');
+
+    var params = this.params, expr = this.expression, match, modifier, clause, rest;
+    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
+      params.attributes = params.attributes || [];
+      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
+      expr = match[1];
+    }
+
+    if (expr == '*') return this.params.wildcard = true;
+
+    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
+      modifier = match[1], clause = match[2], rest = match[3];
+      switch (modifier) {
+        case '#':       params.id = clause; break;
+        case '.':       params.classNames.push(clause); break;
+        case '':
+        case undefined: params.tagName = clause.toUpperCase(); break;
+        default:        abort(expr.inspect());
+      }
+      expr = rest;
+    }
+
+    if (expr.length > 0) abort(expr.inspect());
+  },
+
+  buildMatchExpression: function() {
+    var params = this.params, conditions = [], clause;
+
+    if (params.wildcard)
+      conditions.push('true');
+    if (clause = params.id)
+      conditions.push('element.id == ' + clause.inspect());
+    if (clause = params.tagName)
+      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
+    if ((clause = params.classNames).length > 0)
+      for (var i = 0; i < clause.length; i++)
+        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
+    if (clause = params.attributes) {
+      clause.each(function(attribute) {
+        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
+        var splitValueBy = function(delimiter) {
+          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
+        }
+
+        switch (attribute.operator) {
+          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
+          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
+          case '|=':      conditions.push(
+                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
+                          ); break;
+          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
+          case '':
+          case undefined: conditions.push(value + ' != null'); break;
+          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
+        }
+      });
+    }
+
+    return conditions.join(' && ');
+  },
+
+  compileMatcher: function() {
+    this.match = new Function('element', 'if (!element.tagName) return false; \
+      return ' + this.buildMatchExpression());
+  },
+
+  findElements: function(scope) {
+    var element;
+
+    if (element = $(this.params.id))
+      if (this.match(element))
+        if (!scope || Element.childOf(element, scope))
+          return [element];
+
+    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
+
+    var results = [];
+    for (var i = 0; i < scope.length; i++)
+      if (this.match(element = scope[i]))
+        results.push(Element.extend(element));
+
+    return results;
+  },
+
+  toString: function() {
+    return this.expression;
+  }
+}
+
+function $$() {
+  return $A(arguments).map(function(expression) {
+    return expression.strip().split(/\s+/).inject([null], function(results, expr) {
+      var selector = new Selector(expr);
+      return results.map(selector.findElements.bind(selector)).flatten();
+    });
+  }).flatten();
+}
+var Field = {
+  clear: function() {
+    for (var i = 0; i < arguments.length; i++)
+      $(arguments[i]).value = '';
+  },
+
+  focus: function(element) {
+    $(element).focus();
+  },
+
+  present: function() {
+    for (var i = 0; i < arguments.length; i++)
+      if ($(arguments[i]).value == '') return false;
+    return true;
+  },
+
+  select: function(element) {
+    $(element).select();
+  },
+
+  activate: function(element) {
+    element = $(element);
+    element.focus();
+    if (element.select)
+      element.select();
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+  serialize: function(form) {
+    var elements = Form.getElements($(form));
+    var queryComponents = new Array();
+
+    for (var i = 0; i < elements.length; i++) {
+      var queryComponent = Form.Element.serialize(elements[i]);
+      if (queryComponent)
+        queryComponents.push(queryComponent);
+    }
+
+    return queryComponents.join('&');
+  },
+
+  getElements: function(form) {
+    form = $(form);
+    var elements = new Array();
+
+    for (var tagName in Form.Element.Serializers) {
+      var tagElements = form.getElementsByTagName(tagName);
+      for (var j = 0; j < tagElements.length; j++)
+        elements.push(tagElements[j]);
+    }
+    return elements;
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name)
+      return inputs;
+
+    var matchingInputs = new Array();
+    for (var i = 0; i < inputs.length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) ||
+          (name && input.name != name))
+        continue;
+      matchingInputs.push(input);
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.blur();
+      element.disabled = 'true';
+    }
+  },
+
+  enable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.disabled = '';
+    }
+  },
+
+  findFirstElement: function(form) {
+    return Form.getElements(form).find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    Field.activate(Form.findFirstElement(form));
+  },
+
+  reset: function(form) {
+    $(form).reset();
+  }
+}
+
+Form.Element = {
+  serialize: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter) {
+      var key = encodeURIComponent(parameter[0]);
+      if (key.length == 0) return;
+
+      if (parameter[1].constructor != Array)
+        parameter[1] = [parameter[1]];
+
+      return parameter[1].map(function(value) {
+        return key + '=' + encodeURIComponent(value);
+      }).join('&');
+    }
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter)
+      return parameter[1];
+  }
+}
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'submit':
+      case 'hidden':
+      case 'password':
+      case 'text':
+        return Form.Element.Serializers.textarea(element);
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+    }
+    return false;
+  },
+
+  inputSelector: function(element) {
+    if (element.checked)
+      return [element.name, element.value];
+  },
+
+  textarea: function(element) {
+    return [element.name, element.value];
+  },
+
+  select: function(element) {
+    return Form.Element.Serializers[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var value = '', opt, index = element.selectedIndex;
+    if (index >= 0) {
+      opt = element.options[index];
+      value = opt.value || opt.text;
+    }
+    return [element.name, value];
+  },
+
+  selectMany: function(element) {
+    var value = [];
+    for (var i = 0; i < element.length; i++) {
+      var opt = element.options[i];
+      if (opt.selected)
+        value.push(opt.value || opt.text);
+    }
+    return [element.name, value];
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    var elements = Form.getElements(this.element);
+    for (var i = 0; i < elements.length; i++)
+      this.registerCallback(elements[i]);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        case 'password':
+        case 'text':
+        case 'textarea':
+        case 'select-one':
+        case 'select-multiple':
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+
+  element: function(event) {
+    return event.target || event.srcElement;
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0; i < Event.observers.length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    var element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.attachEvent))
+      name = 'keydown';
+
+    this._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    var element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.detachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      element.detachEvent('on' + name, observer);
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+if (navigator.appVersion.match(/\bMSIE\b/))
+  Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  clone: function(source, target) {
+    source = $(source);
+    target = $(target);
+    target.style.position = 'absolute';
+    var offsets = this.cumulativeOffset(source);
+    target.style.top    = offsets[1] + 'px';
+    target.style.left   = offsets[0] + 'px';
+    target.style.width  = source.offsetWidth + 'px';
+    target.style.height = source.offsetHeight + 'px';
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent==document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      valueT -= element.scrollTop  || 0;
+      valueL -= element.scrollLeft || 0;
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';;
+    element.style.left   = left + 'px';;
+    element.style.width  = width + 'px';;
+    element.style.height = height + 'px';;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
Index: /afridex/plugins/fresh-page/js/lib/builder.js
===================================================================
--- /afridex/plugins/fresh-page/js/lib/builder.js (revision 21)
+++ /afridex/plugins/fresh-page/js/lib/builder.js (revision 21)
@@ -0,0 +1,101 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// See scriptaculous.js for full license.
+
+var Builder = {
+  NODEMAP: {
+    AREA: 'map',
+    CAPTION: 'table',
+    COL: 'table',
+    COLGROUP: 'table',
+    LEGEND: 'fieldset',
+    OPTGROUP: 'select',
+    OPTION: 'select',
+    PARAM: 'object',
+    TBODY: 'table',
+    TD: 'table',
+    TFOOT: 'table',
+    TH: 'table',
+    THEAD: 'table',
+    TR: 'table'
+  },
+  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+  //       due to a Firefox bug
+  node: function(elementName) {
+    elementName = elementName.toUpperCase();
+    
+    // try innerHTML approach
+    var parentTag = this.NODEMAP[elementName] || 'div';
+    var parentElement = document.createElement(parentTag);
+    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+    } catch(e) {}
+    var element = parentElement.firstChild || null;
+      
+    // see if browser added wrapping tags
+    if(element && (element.tagName != elementName))
+      element = element.getElementsByTagName(elementName)[0];
+    
+    // fallback to createElement approach
+    if(!element) element = document.createElement(elementName);
+    
+    // abort if nothing could be created
+    if(!element) return;
+
+    // attributes (or text)
+    if(arguments[1])
+      if(this._isStringOrNumber(arguments[1]) ||
+        (arguments[1] instanceof Array)) {
+          this._children(element, arguments[1]);
+        } else {
+          var attrs = this._attributes(arguments[1]);
+          if(attrs.length) {
+            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+              parentElement.innerHTML = "<" +elementName + " " +
+                attrs + "></" + elementName + ">";
+            } catch(e) {}
+            element = parentElement.firstChild || null;
+            // workaround firefox 1.0.X bug
+            if(!element) {
+              element = document.createElement(elementName);
+              for(attr in arguments[1]) 
+                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+            }
+            if(element.tagName != elementName)
+              element = parentElement.getElementsByTagName(elementName)[0];
+            }
+        } 
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+  _attributes: function(attributes) {
+    var attrs = [];
+    for(attribute in attributes)
+      attrs.push((attribute=='className' ? 'class' : attribute) +
+          '="' + attributes[attribute].toString().escapeHTML() + '"');
+    return attrs.join(" ");
+  },
+  _children: function(element, children) {
+    if(typeof children=='object') { // array can hold nodes and text
+      children.flatten().each( function(e) {
+        if(typeof e=='object')
+          element.appendChild(e)
+        else
+          if(Builder._isStringOrNumber(e))
+            element.appendChild(Builder._text(e));
+      });
+    } else
+      if(Builder._isStringOrNumber(children)) 
+         element.appendChild(Builder._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  }
+}
Index: /afridex/plugins/fresh-page/js/lib/effects.js
===================================================================
--- /afridex/plugins/fresh-page/js/lib/effects.js (revision 21)
+++ /afridex/plugins/fresh-page/js/lib/effects.js (revision 21)
@@ -0,0 +1,958 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// See scriptaculous.js for full license.  
+
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';  
+  if(this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if(this.slice(0,1) == '#') {  
+      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if(this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return(color.length==7 ? color : (arguments[0] || this));  
+}
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+}
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+}
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);  
+  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
+  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
+
+Element.getOpacity = function(element){  
+  var opacity;
+  if (opacity = Element.getStyle(element, 'opacity'))  
+    return parseFloat(opacity);  
+  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
+    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
+  return 1.0;  
+}
+
+Element.setOpacity = function(element, value){  
+  element= $(element);  
+  if (value == 1){
+    Element.setStyle(element, { opacity: 
+      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
+      0.999999 : null });
+    if(/MSIE/.test(navigator.userAgent))  
+      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
+  } else {  
+    if(value < 0.00001) value = 0;  
+    Element.setStyle(element, {opacity: value});
+    if(/MSIE/.test(navigator.userAgent))  
+     Element.setStyle(element, 
+       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
+                 'alpha(opacity='+value*100+')' });  
+  }
+}  
+ 
+Element.getInlineOpacity = function(element){  
+  return $(element).style.opacity || '';
+}  
+
+Element.childrenWithClassName = function(element, className, findFirst) {
+  var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
+  var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
+    return (c.className && c.className.match(classNameRegExp));
+  });
+  if(!results) results = [];
+  return results;
+}
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Array.prototype.call = function() {
+  var args = arguments;
+  this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if(child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            Builder.node('span',{style: tagifyStyle},
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if(((typeof element == 'object') || 
+        (typeof element == 'function')) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || {});
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || {});
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {}
+
+Effect.Transitions.linear = function(pos) {
+  return pos;
+}
+Effect.Transitions.sinoidal = function(pos) {
+  return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+Effect.Transitions.reverse  = function(pos) {
+  return 1-pos;
+}
+Effect.Transitions.flicker = function(pos) {
+  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+}
+Effect.Transitions.wobble = function(pos) {
+  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+Effect.Transitions.pulse = function(pos) {
+  return (Math.floor(pos*10) % 2 == 0 ? 
+    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+Effect.Transitions.none = function(pos) {
+  return 0;
+}
+Effect.Transitions.full = function(pos) {
+  return 1;
+}
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create();
+Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = (typeof effect.options.queue == 'string') ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if(!this.interval) 
+      this.interval = setInterval(this.loop.bind(this), 40);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if(this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    this.effects.invoke('loop', timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if(typeof queueName != 'string') return queueName;
+    
+    if(!this.instances[queueName])
+      this.instances[queueName] = new Effect.ScopedQueue();
+      
+    return this.instances[queueName];
+  }
+}
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.DefaultOptions = {
+  transition: Effect.Transitions.sinoidal,
+  duration:   1.0,   // seconds
+  fps:        25.0,  // max. 25fps due to Effect.Queue implementation
+  sync:       false, // true for combining
+  from:       0.0,
+  to:         1.0,
+  delay:      0.0,
+  queue:      'parallel'
+}
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+  position: null,
+  start: function(options) {
+    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn + (this.options.duration*1000);
+    this.event('beforeStart');
+    if(!this.options.sync)
+      Effect.Queues.get(typeof this.options.queue == 'string' ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if(timePos >= this.startOn) {
+      if(timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if(this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
+      var frame = Math.round(pos * this.options.fps * this.options.duration);
+      if(frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  render: function(pos) {
+    if(this.state == 'idle') {
+      this.state = 'running';
+      this.event('beforeSetup');
+      if(this.setup) this.setup();
+      this.event('afterSetup');
+    }
+    if(this.state == 'running') {
+      if(this.options.transition) pos = this.options.transition(pos);
+      pos *= (this.options.to-this.options.from);
+      pos += this.options.from;
+      this.position = pos;
+      this.event('beforeUpdate');
+      if(this.update) this.update(pos);
+      this.event('afterUpdate');
+    }
+  },
+  cancel: function() {
+    if(!this.options.sync)
+      Effect.Queues.get(typeof this.options.queue == 'string' ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if(this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if(effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    // make this work on IE on elements without 'layout'
+    if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create();
+Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Bug in Opera: Opera returns the "real" position of a static element or
+    // relative element that does not have top/left explicitly set.
+    // ==> Always set top and left for position relative elements in your stylesheets 
+    // (to 0 if you do not need them) 
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if(this.options.mode == 'absolute') {
+      // absolute movement, so we need to calc deltaX and deltaY
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: this.options.x  * position + this.originalLeft + 'px',
+      top:  this.options.y  * position + this.originalTop  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
+};
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+  initialize: function(element, percent) {
+    this.element = $(element)
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || {});
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = {};
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%'].each( function(fontSizeType) {
+      if(fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if(this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if(/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if(!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if(this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = {};
+    if(this.options.scaleX) d.width = width + 'px';
+    if(this.options.scaleY) d.height = height + 'px';
+    if(this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if(this.elementPositioning == 'absolute') {
+        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if(this.options.scaleY) d.top = -topd + 'px';
+        if(this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = {
+      backgroundImage: this.element.getStyle('background-image') };
+    this.element.setStyle({backgroundImage: 'none'});
+    if(!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if(!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    this.start(arguments[1] || {});
+  },
+  setup: function() {
+    Position.prepare();
+    var offsets = Position.cumulativeOffset(this.element);
+    if(this.options.offset) offsets[1] += this.options.offset;
+    var max = window.innerHeight ? 
+      window.height - window.innerHeight :
+      document.body.scrollHeight - 
+        (document.documentElement.clientHeight ? 
+          document.documentElement.clientHeight : document.body.clientHeight);
+    this.scrollStart = Position.deltaY;
+    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+  },
+  update: function(position) {
+    Position.prepare();
+    window.scrollTo(Position.deltaX, 
+      this.scrollStart + (position*this.delta));
+  }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+  from: element.getOpacity() || 1.0,
+  to:   0.0,
+  afterFinishInternal: function(effect) { 
+    if(effect.options.to!=0) return;
+    effect.element.hide();
+    effect.element.setStyle({opacity: oldOpacity}); 
+  }}, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from);
+    effect.element.show(); 
+  }}, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) {
+        effect.effects[0].element.setStyle({position: 'absolute'}); },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide();
+         effect.effects[0].element.setStyle(oldStyle); }
+     }, arguments[1] || {})
+   );
+}
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide();
+        effect.element.undoClipping();
+      } 
+    }, arguments[1] || {})
+  );
+}
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false,
+      scaleFrom: 0,
+      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+      restoreAfterFinish: true,
+      afterSetup: function(effect) {
+        effect.element.makeClipping();
+        effect.element.setStyle({height: '0px'});
+        effect.element.show(); 
+      },  
+      afterFinishInternal: function(effect) {
+        effect.element.undoClipping();
+      }
+    }, arguments[1] || {})
+  );
+}
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, { 
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned();
+          effect.element.makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide();
+          effect.element.undoClipping();
+          effect.element.undoPositioned();
+          effect.element.setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  });
+}
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide();
+          effect.effects[0].element.undoPositioned();
+          effect.effects[0].element.setStyle(oldStyle);
+        } 
+      }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element, 
+      { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned();
+        effect.element.setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+  element = $(element);
+  element.cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.firstChild.makePositioned();
+      if(window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping();
+      effect.element.setStyle({height: '0px'});
+      effect.element.show(); },
+    afterUpdateInternal: function(effect) {
+      effect.element.firstChild.setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping(); 
+      // IE will crash if child is undoPositioned first
+      if(/MSIE/.test(navigator.userAgent)){
+        effect.element.undoPositioned();
+        effect.element.firstChild.undoPositioned();
+      }else{
+        effect.element.firstChild.undoPositioned();
+        effect.element.undoPositioned();
+      }
+      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || {})
+  );
+}
+  
+Effect.SlideUp = function(element) {
+  element = $(element);
+  element.cleanWhitespace();
+  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    restoreAfterFinish: true,
+    beforeStartInternal: function(effect) {
+      effect.element.makePositioned();
+      effect.element.firstChild.makePositioned();
+      if(window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping();
+      effect.element.show(); },  
+    afterUpdateInternal: function(effect) {
+      effect.element.firstChild.setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+    afterFinishInternal: function(effect) {
+      effect.element.hide();
+      effect.element.undoClipping();
+      effect.element.firstChild.undoPositioned();
+      effect.element.undoPositioned();
+      effect.element.setStyle({bottom: oldInnerBottom}); }
+   }, arguments[1] || {})
+  );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, 
+    { restoreAfterFinish: true,
+      beforeSetup: function(effect) {
+        effect.element.makeClipping(effect.element); },  
+      afterFinishInternal: function(effect) {
+        effect.element.hide(effect.element); 
+        effect.element.undoClipping(effect.element); }
+  });
+}
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide();
+      effect.element.makeClipping();
+      effect.element.makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'});
+               effect.effects[0].element.show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping();
+               effect.effects[0].element.undoPositioned();
+               effect.effects[0].element.setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+}
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned();
+           effect.effects[0].element.makeClipping(); },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide();
+           effect.effects[0].element.undoClipping();
+           effect.effects[0].element.undoPositioned();
+           effect.effects[0].element.setStyle(oldStyle); }
+       }, options)
+  );
+}
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || {};
+  var oldOpacity = element.getInlineOpacity();
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 3.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  Element.makeClipping(element);
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide();
+        effect.element.undoClipping(); 
+        effect.element.setStyle(oldStyle);
+      } });
+  }}, arguments[1] || {}));
+};
+
+['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
+ 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
+  function(f) { Element.Methods[f] = Element[f]; }
+);
+
+Element.Methods.visualEffect = function(element, effect, options) {
+  s = effect.gsub(/_/, '-').camelize();
+  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
+  new Effect[effect_class](element, options);
+  return $(element);
+};
+
+Element.addMethods();
Index: /afridex/plugins/fresh-page/js/lib/unittest.js
===================================================================
--- /afridex/plugins/fresh-page/js/lib/unittest.js (revision 21)
+++ /afridex/plugins/fresh-page/js/lib/unittest.js (revision 21)
@@ -0,0 +1,383 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+//           (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+  var options = Object.extend({
+    pointerX: 0,
+    pointerY: 0,
+    buttons: 0
+  }, arguments[2] || {});
+  var oEvent = document.createEvent("MouseEvents");
+  oEvent.initMouseEvent(eventName, true, true, document.defaultView, 
+    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, 
+    false, false, false, false, 0, $(element));
+  
+  if(this.mark) Element.remove(this.mark);
+  this.mark = document.createElement('div');
+  this.mark.appendChild(document.createTextNode(" "));
+  document.body.appendChild(this.mark);
+  this.mark.style.position = 'absolute';
+  this.mark.style.top = options.pointerY + "px";
+  this.mark.style.left = options.pointerX + "px";
+  this.mark.style.width = "5px";
+  this.mark.style.height = "5px;";
+  this.mark.style.borderTop = "1px solid red;"
+  this.mark.style.borderLeft = "1px solid red;"
+  
+  if(this.step)
+    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+  
+  $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+  var options = Object.extend({
+    ctrlKey: false,
+    altKey: false,
+    shiftKey: false,
+    metaKey: false,
+    keyCode: 0,
+    charCode: 0
+  }, arguments[2] || {});
+
+  var oEvent = document.createEvent("KeyEvents");
+  oEvent.initKeyEvent(eventName, true, true, window, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+    options.keyCode, options.charCode );
+  $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+  for(var i=0; i<command.length; i++) {
+    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+  }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = Object.inspect;
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+  initialize: function(log) {
+    this.log = $(log);
+    if (this.log) {
+      this._createLogTable();
+    }
+  },
+  start: function(testName) {
+    if (!this.log) return;
+    this.testName = testName;
+    this.lastLogLine = document.createElement('tr');
+    this.statusCell = document.createElement('td');
+    this.nameCell = document.createElement('td');
+    this.nameCell.appendChild(document.createTextNode(testName));
+    this.messageCell = document.createElement('td');
+    this.lastLogLine.appendChild(this.statusCell);
+    this.lastLogLine.appendChild(this.nameCell);
+    this.lastLogLine.appendChild(this.messageCell);
+    this.loglines.appendChild(this.lastLogLine);
+  },
+  finish: function(status, summary) {
+    if (!this.log) return;
+    this.lastLogLine.className = status;
+    this.statusCell.innerHTML = status;
+    this.messageCell.innerHTML = this._toHTML(summary);
+  },
+  message: function(message) {
+    if (!this.log) return;
+    this.messageCell.innerHTML = this._toHTML(message);
+  },
+  summary: function(summary) {
+    if (!this.log) return;
+    this.logsummary.innerHTML = this._toHTML(summary);
+  },
+  _createLogTable: function() {
+    this.log.innerHTML =
+    '<div id="logsummary"></div>' +
+    '<table id="logtable">' +
+    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+    '<tbody id="loglines"></tbody>' +
+    '</table>';
+    this.logsummary = $('logsummary')
+    this.loglines = $('loglines');
+  },
+  _toHTML: function(txt) {
+    return txt.escapeHTML().replace(/\n/g,"<br/>");
+  }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+  initialize: function(testcases) {
+    this.options = Object.extend({
+      testLog: 'testlog'
+    }, arguments[1] || {});
+    this.options.resultsURL = this.parseResultsURLQueryParameter();
+    if (this.options.testLog) {
+      this.options.testLog = $(this.options.testLog) || null;
+    }
+    if(this.options.tests) {
+      this.tests = [];
+      for(var i = 0; i < this.options.tests.length; i++) {
+        if(/^test/.test(this.options.tests[i])) {
+          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+        }
+      }
+    } else {
+      if (this.options.test) {
+        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+      } else {
+        this.tests = [];
+        for(var testcase in testcases) {
+          if(/^test/.test(testcase)) {
+            this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
+          }
+        }
+      }
+    }
+    this.currentTest = 0;
+    this.logger = new Test.Unit.Logger(this.options.testLog);
+    setTimeout(this.runTests.bind(this), 1000);
+  },
+  parseResultsURLQueryParameter: function() {
+    return window.location.search.parseQuery()["resultsURL"];
+  },
+  // Returns:
+  //  "ERROR" if there was an error,
+  //  "FAILURE" if there was a failure, or
+  //  "SUCCESS" if there was neither
+  getResult: function() {
+    var hasFailure = false;
+    for(var i=0;i<this.tests.length;i++) {
+      if (this.tests[i].errors > 0) {
+        return "ERROR";
+      }
+      if (this.tests[i].failures > 0) {
+        hasFailure = true;
+      }
+    }
+    if (hasFailure) {
+      return "FAILURE";
+    } else {
+      return "SUCCESS";
+    }
+  },
+  postResults: function() {
+    if (this.options.resultsURL) {
+      new Ajax.Request(this.options.resultsURL, 
+        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+    }
+  },
+  runTests: function() {
+    var test = this.tests[this.currentTest];
+    if (!test) {
+      // finished!
+      this.postResults();
+      this.logger.summary(this.summary());
+      return;
+    }
+    if(!test.isWaiting) {
+      this.logger.start(test.name);
+    }
+    test.run();
+    if(test.isWaiting) {
+      this.logger.message("Waiting for " + test.timeToWait + "ms");
+      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+    } else {
+      this.logger.finish(test.status(), test.summary());
+      this.currentTest++;
+      // tail recursive, hopefully the browser will skip the stackframe
+      this.runTests();
+    }
+  },
+  summary: function() {
+    var assertions = 0;
+    var failures = 0;
+    var errors = 0;
+    var messages = [];
+    for(var i=0;i<this.tests.length;i++) {
+      assertions +=   this.tests[i].assertions;
+      failures   +=   this.tests[i].failures;
+      errors     +=   this.tests[i].errors;
+    }
+    return (
+      this.tests.length + " tests, " + 
+      assertions + " assertions, " + 
+      failures   + " failures, " +
+      errors     + " errors");
+  }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+  initialize: function() {
+    this.assertions = 0;
+    this.failures   = 0;
+    this.errors     = 0;
+    this.messages   = [];
+  },
+  summary: function() {
+    return (
+      this.assertions + " assertions, " + 
+      this.failures   + " failures, " +
+      this.errors     + " errors" + "\n" +
+      this.messages.join("\n"));
+  },
+  pass: function() {
+    this.assertions++;
+  },
+  fail: function(message) {
+    this.failures++;
+    this.messages.push("Failure: " + message);
+  },
+  info: function(message) {
+    this.messages.push("Info: " + message);
+  },
+  error: function(error) {
+    this.errors++;
+    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+  },
+  status: function() {
+    if (this.failures > 0) return 'failed';
+    if (this.errors > 0) return 'error';
+    return 'passed';
+  },
+  assert: function(expression) {
+    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+    try { expression ? this.pass() : 
+      this.fail(message); }
+    catch(e) { this.error(e); }
+  },
+  assertEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEqual";
+    try { (expected == actual) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertEnumEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEnumEqual";
+    try { $A(expected).length == $A(actual).length && 
+      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
+          ', actual ' + Test.Unit.inspect(actual)); }
+    catch(e) { this.error(e); }
+  },
+  assertNotEqual: function(expected, actual) {
+    var message = arguments[2] || "assertNotEqual";
+    try { (expected != actual) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertNull: function(obj) {
+    var message = arguments[1] || 'assertNull'
+    try { (obj==null) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertHidden: function(element) {
+    var message = arguments[1] || 'assertHidden';
+    this.assertEqual("none", element.style.display, message);
+  },
+  assertNotNull: function(object) {
+    var message = arguments[1] || 'assertNotNull';
+    this.assert(object != null, message);
+  },
+  assertInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertInstanceOf';
+    try { 
+      (actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was not an instance of the expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertNotInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertNotInstanceOf';
+    try { 
+      !(actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was an instance of the not expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  _isVisible: function(element) {
+    element = $(element);
+    if(!element.parentNode) return true;
+    this.assertNotNull(element);
+    if(element.style && Element.getStyle(element, 'display') == 'none')
+      return false;
+    
+    return this._isVisible(element.parentNode);
+  },
+  assertNotVisible: function(element) {
+    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+  },
+  assertVisible: function(element) {
+    this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+  initialize: function(name, test, setup, teardown) {
+    Test.Unit.Assertions.prototype.initialize.bind(this)();
+    this.name           = name;
+    this.test           = test || function() {};
+    this.setup          = setup || function() {};
+    this.teardown       = teardown || function() {};
+    this.isWaiting      = false;
+    this.timeToWait     = 1000;
+  },
+  wait: function(time, nextPart) {
+    this.isWaiting = true;
+    this.test = nextPart;
+    this.timeToWait = time;
+  },
+  run: function() {
+    try {
+      try {
+        if (!this.isWaiting) this.setup.bind(this)();
+        this.isWaiting = false;
+        this.test.bind(this)();
+      } finally {
+        if(!this.isWaiting) {
+          this.teardown.bind(this)();
+        }
+      }
+    }
+    catch(e) { this.error(e); }
+  }
+});
Index: /afridex/plugins/fresh-page/js/lib/scriptaculous.js
===================================================================
--- /afridex/plugins/fresh-page/js/lib/scriptaculous.js (revision 21)
+++ /afridex/plugins/fresh-page/js/lib/scriptaculous.js (revision 21)
@@ -0,0 +1,47 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var Scriptaculous = {
+  Version: '1.6.1',
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
+  },
+  load: function() {
+    if((typeof Prototype=='undefined') || 
+       (typeof Element == 'undefined') || 
+       (typeof Element.Methods=='undefined') ||
+       parseFloat(Prototype.Version.split(".")[0] + "." +
+                  Prototype.Version.split(".")[1]) < 1.5)
+       throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
+    
+    $A(document.getElementsByTagName("script")).findAll( function(s) {
+      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+    }).each( function(s) {
+      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+      var includes = s.src.match(/\?.*load=([a-z,]*)/);
+      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
+       function(include) { Scriptaculous.require(path+include+'.js') });
+    });
+  }
+}
+
+Scriptaculous.load();
Index: /afridex/plugins/fresh-page/js/lib/dragdrop.js
===================================================================
--- /afridex/plugins/fresh-page/js/lib/dragdrop.js (revision 21)
+++ /afridex/plugins/fresh-page/js/lib/dragdrop.js (revision 21)
@@ -0,0 +1,915 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+// 
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || {});
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if((typeof containment == 'object') && 
+        (containment.constructor == Array)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var affected = [];
+    
+    if(this.last_active) this.deactivate(this.last_active);
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0) {
+      drop = Droppables.findDeepestChild(affected);
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) 
+        this.last_active.onDrop(element, this.last_active.element, event);
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+    this.activeDraggable = draggable;
+  },
+  
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+  initialize: function(element) {
+    var options = Object.extend({
+      handle: false,
+      starteffect: function(element) {
+        element._opacity = Element.getOpacity(element); 
+        new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+      },
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
+      },
+      endeffect: function(element) {
+        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity}); 
+      },
+      zindex: 1000,
+      revert: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] }
+    }, arguments[1] || {});
+
+    this.element = $(element);
+    
+    if(options.handle && (typeof options.handle == 'string')) {
+      var h = Element.childrenWithClassName(this.element, options.handle, true);
+      if(h.length>0) this.handle = h[0];
+    }
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+    
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
+      options.scroll = $(options.scroll);
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.delta    = this.currentDelta();
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if(src.tagName && (
+        src.tagName=='INPUT' ||
+        src.tagName=='SELECT' ||
+        src.tagName=='OPTION' ||
+        src.tagName=='BUTTON' ||
+        src.tagName=='TEXTAREA')) return;
+        
+      if(this.element._revert) {
+        this.element._revert.cancel();
+        this.element._revert = null;
+      }
+      
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    Position.prepare();
+    Droppables.show(pointer, this.element);
+    Draggables.notify('onDrag', this, event);
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft;
+        p[1] += this.options.scroll.scrollTop;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+
+    if(this.options.ghosting) {
+      Position.relativize(this.element);
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    if(success) Droppables.fire(event, this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && typeof revert == 'function') revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      this.options.reverteffect(this.element, 
+        d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    if(this.options.scroll && (this.options.scroll != window)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(typeof this.options.snap == 'function') {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(this.options.snap instanceof Array) {
+        p = p.map( function(v, i) {
+          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  startScrolling: function(speed) {
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+    Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+    Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+    if (Draggables._lastScrollPointer[0] < 0)
+      Draggables._lastScrollPointer[0] = 0;
+    if (Draggables._lastScrollPointer[1] < 0)
+      Draggables._lastScrollPointer[1] = 0;
+    this.draw(Draggables._lastScrollPointer);
+    
+    if(this.options.change) this.options.change(this);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+}
+
+var Sortable = {
+  sortables: {},
+  
+  _findRootElement: function(element) {
+    while (element.tagName != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    var s = Sortable.options(element);
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      hoverclass:  null,
+      ghosting:    false,
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      /^[^_]*_(.*)$/,
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || {});
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+      //greedy:      !options.dropOnEmpty
+    }
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (this.findElements(element, options) || []).each( function(e) {
+      // handles are per-draggable
+      var handle = options.handle ? 
+        Element.childrenWithClassName(e, options.handle)[0] : e;    
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Element.hide(Sortable._marker);
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = $('dropmarker') || document.createElement('DIV');
+      Element.hide(Sortable._marker);
+      Element.addClassName(Sortable._marker, 'dropmarker');
+      Sortable._marker.style.position = 'absolute';
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.style.left = offsets[0] + 'px';
+    Sortable._marker.style.top = offsets[1] + 'px';
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+      else
+        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+    
+    Element.show(Sortable._marker);
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: new Array,
+        position: parent.children.length,
+        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
+      }
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child)
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  /* Finds the first element of the given tag type within a parent element.
+    Used for finding the first LI[ST] within a L[IST]I[TEM].*/
+  _findChildrenElement: function (element, containerTag) {
+    if (element && element.hasChildNodes)
+      for (var i = 0; i < element.childNodes.length; ++i)
+        if (element.childNodes[i].tagName == containerTag)
+          return element.childNodes[i];
+  
+    return null;
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || {});
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: new Array,
+      container: element,
+      position: 0
+    }
+    
+    return Sortable._tree (element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || {});
+    
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || {});
+    
+    var nodeMap = {};
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || {});
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+}
+
+/* Returns true if child is contained within element */
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+
+  if (child.parentNode == element) return true;
+
+  return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {    
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+  if (type == 'vertical' || type == 'height')
+    return element.offsetHeight;
+  else
+    return element.offsetWidth;
+}
Index: /afridex/plugins/fresh-page/js/lib/slider.js
===================================================================
--- /afridex/plugins/fresh-page/js/lib/slider.js (revision 21)
+++ /afridex/plugins/fresh-page/js/lib/slider.js (revision 21)
@@ -0,0 +1,283 @@
+// Copyright (c) 2005 Marty Haught, Thomas Fuchs 
+//
+// See http://script.aculo.us for more info
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if(!Control) var Control = {};
+Control.Slider = Class.create();
+
+// options:
+//  axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+//  onChange(value)
+//  onSlide(value)
+Control.Slider.prototype = {
+  initialize: function(handle, track, options) {
+    var slider = this;
+    
+    if(handle instanceof Array) {
+      this.handles = handle.collect( function(e) { return $(e) });
+    } else {
+      this.handles = [$(handle)];
+    }
+    
+    this.track   = $(track);
+    this.options = options || {};
+
+    this.axis      = this.options.axis || 'horizontal';
+    this.increment = this.options.increment || 1;
+    this.step      = parseInt(this.options.step || '1');
+    this.range     = this.options.range || $R(0,1);
+    
+    this.value     = 0; // assure backwards compat
+    this.values    = this.handles.map( function() { return 0 });
+    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+    this.options.startSpan = $(this.options.startSpan || null);
+    this.options.endSpan   = $(this.options.endSpan || null);
+
+    this.restricted = this.options.restricted || false;
+
+    this.maximum   = this.options.maximum || this.range.end;
+    this.minimum   = this.options.minimum || this.range.start;
+
+    // Will be used to align the handle onto the track, if necessary
+    this.alignX = parseInt(this.options.alignX || '0');
+    this.alignY = parseInt(this.options.alignY || '0');
+    
+    this.trackLength = this.maximumOffset() - this.minimumOffset();
+    this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
+
+    this.active   = false;
+    this.dragging = false;
+    this.disabled = false;
+
+    if(this.options.disabled) this.setDisabled();
+
+    // Allowed values array
+    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+    if(this.allowedValues) {
+      this.minimum = this.allowedValues.min();
+      this.maximum = this.allowedValues.max();
+    }
+
+    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+    this.eventMouseMove = this.update.bindAsEventListener(this);
+
+    // Initialize handles in reverse (make sure first handle is active)
+    this.handles.each( function(h,i) {
+      i = slider.handles.length-1-i;
+      slider.setValue(parseFloat(
+        (slider.options.sliderValue instanceof Array ? 
+          slider.options.sliderValue[i] : slider.options.sliderValue) || 
+         slider.range.start), i);
+      Element.makePositioned(h); // fix IE
+      Event.observe(h, "mousedown", slider.eventMouseDown);
+    });
+    
+    Event.observe(this.track, "mousedown", this.eventMouseDown);
+    Event.observe(document, "mouseup", this.eventMouseUp);
+    Event.observe(document, "mousemove", this.eventMouseMove);
+    
+    this.initialized = true;
+  },
+  dispose: function() {
+    var slider = this;    
+    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+    Event.stopObserving(document, "mouseup", this.eventMouseUp);
+    Event.stopObserving(document, "mousemove", this.eventMouseMove);
+    this.handles.each( function(h) {
+      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+    });
+  },
+  setDisabled: function(){
+    this.disabled = true;
+  },
+  setEnabled: function(){
+    this.disabled = false;
+  },  
+  getNearestValue: function(value){
+    if(this.allowedValues){
+      if(value >= this.allowedValues.max()) return(this.allowedValues.max());
+      if(value <= this.allowedValues.min()) return(this.allowedValues.min());
+      
+      var offset = Math.abs(this.allowedValues[0] - value);
+      var newValue = this.allowedValues[0];
+      this.allowedValues.each( function(v) {
+        var currentOffset = Math.abs(v - value);
+        if(currentOffset <= offset){
+          newValue = v;
+          offset = currentOffset;
+        } 
+      });
+      return newValue;
+    }
+    if(value > this.range.end) return this.range.end;
+    if(value < this.range.start) return this.range.start;
+    return value;
+  },
+  setValue: function(sliderValue, handleIdx){
+    if(!this.active) {
+      this.activeHandle    = this.handles[handleIdx];
+      this.activeHandleIdx = handleIdx;
+      this.updateStyles();
+    }
+    handleIdx = handleIdx || this.activeHandleIdx || 0;
+    if(this.initialized && this.restricted) {
+      if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+        sliderValue = this.values[handleIdx-1];
+      if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+        sliderValue = this.values[handleIdx+1];
+    }
+    sliderValue = this.getNearestValue(sliderValue);
+    this.values[handleIdx] = sliderValue;
+    this.value = this.values[0]; // assure backwards compat
+    
+    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
+      this.translateToPx(sliderValue);
+    
+    this.drawSpans();
+    if(!this.dragging || !this.event) this.updateFinished();
+  },
+  setValueBy: function(delta, handleIdx) {
+    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
+      handleIdx || this.activeHandleIdx || 0);
+  },
+  translateToPx: function(value) {
+    return Math.round(
+      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
+      (value - this.range.start)) + "px";
+  },
+  translateToValue: function(offset) {
+    return ((offset/(this.trackLength-this.handleLength) * 
+      (this.range.end-this.range.start)) + this.range.start);
+  },
+  getRange: function(range) {
+    var v = this.values.sortBy(Prototype.K); 
+    range = range || 0;
+    return $R(v[range],v[range+1]);
+  },
+  minimumOffset: function(){
+    return(this.isVertical() ? this.alignY : this.alignX);
+  },
+  maximumOffset: function(){
+    return(this.isVertical() ?
+      this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
+  },  
+  isVertical:  function(){
+    return (this.axis == 'vertical');
+  },
+  drawSpans: function() {
+    var slider = this;
+    if(this.spans)
+      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+    if(this.options.startSpan)
+      this.setSpan(this.options.startSpan,
+        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+    if(this.options.endSpan)
+      this.setSpan(this.options.endSpan, 
+        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+  },
+  setSpan: function(span, range) {
+    if(this.isVertical()) {
+      span.style.top = this.translateToPx(range.start);
+      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+    } else {
+      span.style.left = this.translateToPx(range.start);
+      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+    }
+  },
+  updateStyles: function() {
+    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+    Element.addClassName(this.activeHandle, 'selected');
+  },
+  startDrag: function(event) {
+    if(Event.isLeftClick(event)) {
+      if(!this.disabled){
+        this.active = true;
+        
+        var handle = Event.element(event);
+        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
+        if(handle==this.track) {
+          var offsets  = Position.cumulativeOffset(this.track); 
+          this.event = event;
+          this.setValue(this.translateToValue( 
+           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+          ));
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        } else {
+          // find the handle (prevents issues with Safari)
+          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
+            handle = handle.parentNode;
+        
+          this.activeHandle    = handle;
+          this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+          this.updateStyles();
+        
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        }
+      }
+      Event.stop(event);
+    }
+  },
+  update: function(event) {
+   if(this.active) {
+      if(!this.dragging) this.dragging = true;
+      this.draw(event);
+      // fix AppleWebKit rendering
+      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+      Event.stop(event);
+   }
+  },
+  draw: function(event) {
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    var offsets = Position.cumulativeOffset(this.track);
+    pointer[0] -= this.offsetX + offsets[0];
+    pointer[1] -= this.offsetY + offsets[1];
+    this.event = event;
+    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+    if(this.initialized && this.options.onSlide)
+      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+  },
+  endDrag: function(event) {
+    if(this.active && this.dragging) {
+      this.finishDrag(event, true);
+      Event.stop(event);
+    }
+    this.active = false;
+    this.dragging = false;
+  },  
+  finishDrag: function(event, success) {
+    this.active = false;
+    this.dragging = false;
+    this.updateFinished();
+  },
+  updateFinished: function() {
+    if(this.initialized && this.options.onChange) 
+      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+    this.event = null;
+  }
+}
Index: /afridex/plugins/fresh-page/js/lib/controls.js
===================================================================
--- /afridex/plugins/fresh-page/js/lib/controls.js (revision 21)
+++ /afridex/plugins/fresh-page/js/lib/controls.js (revision 21)
@@ -0,0 +1,815 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+  baseInitialize: function(element, update, options) {
+    this.element     = $(element); 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+
+    if (this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || {};
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+    function(element, update){ 
+      if(!update.style.position || update.style.position=='absolute') {
+        update.style.position = 'absolute';
+        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+      }
+      Effect.Appear(update,{duration:0.15});
+    };
+    this.options.onHide = this.options.onHide || 
+    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if (typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (navigator.appVersion.indexOf('MSIE')>0) &&
+      (navigator.userAgent.indexOf('Opera')<0) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix);
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+        
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCount-1;
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var lastTokenPos = this.findLastToken();
+    if (lastTokenPos != -1) {
+      var newValue = this.element.value.substr(0, lastTokenPos + 1);
+      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value;
+    } else {
+      this.element.value = value;
+    }
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.firstChild);
+
+      if(this.update.firstChild && this.update.firstChild.childNodes) {
+        this.entryCount = 
+          this.update.firstChild.childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+
+      this.index = 0;
+      this.render();
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    if(this.getToken().length>=this.options.minChars) {
+      this.startIndicator();
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+
+  getToken: function() {
+    var tokenPos = this.findLastToken();
+    if (tokenPos != -1)
+      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+    else
+      var ret = this.element.value;
+
+    return /\n/.test(ret) ? '' : ret;
+  },
+
+  findLastToken: function() {
+    var lastTokenPos = -1;
+
+    for (var i=0; i<this.options.tokens.length; i++) {
+      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+      if (thisTokenPos > lastTokenPos)
+        lastTokenPos = thisTokenPos;
+    }
+    return lastTokenPos;
+  }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || {});
+  }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = $(element);
+
+    this.options = Object.extend({
+      okButton: true,
+      okText: "ok",
+      cancelLink: true,
+      cancelText: "cancel",
+      savingText: "Saving...",
+      clickToEditText: "Click to edit",
+      okText: "ok",
+      rows: 1,
+      onComplete: function(transport, element) {
+        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+      },
+      onFailure: function(transport) {
+        alert("Error communicating with the server: " + transport.responseText.stripTags());
+      },
+      callback: function(form) {
+        return Form.serialize(form);
+      },
+      handleLineBreaks: true,
+      loadingText: 'Loading...',
+      savingClassName: 'inplaceeditor-saving',
+      loadingClassName: 'inplaceeditor-loading',
+      formClassName: 'inplaceeditor-form',
+      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+      highlightendcolor: "#FFFFFF",
+      externalControl: null,
+      submitOnBlur: false,
+      ajaxOptions: {},
+      evalScripts: false
+    }, options || {});
+
+    if(!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + "-inplaceeditor";
+      if ($(this.options.formId)) {
+        // there's already a form with that name, don't specify an id
+        this.options.formId = null;
+      }
+    }
+    
+    if (this.options.externalControl) {
+      this.options.externalControl = $(this.options.externalControl);
+    }
+    
+    this.originalBackground = Element.getStyle(this.element, 'background-color');
+    if (!this.originalBackground) {
+      this.originalBackground = "transparent";
+    }
+    
+    this.element.title = this.options.clickToEditText;
+    
+    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+    Event.observe(this.element, 'click', this.onclickListener);
+    Event.observe(this.element, 'mouseover', this.mouseoverListener);
+    Event.observe(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.observe(this.options.externalControl, 'click', this.onclickListener);
+      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  },
+  enterEditMode: function(evt) {
+    if (this.saving) return;
+    if (this.editing) return;
+    this.editing = true;
+    this.onEnterEditMode();
+    if (this.options.externalControl) {
+      Element.hide(this.options.externalControl);
+    }
+    Element.hide(this.element);
+    this.createForm();
+    this.element.parentNode.insertBefore(this.form, this.element);
+    Field.scrollFreeActivate(this.editField);
+    // stop the event to avoid a page refresh in Safari
+    if (evt) {
+      Event.stop(evt);
+    }
+    return false;
+  },
+  createForm: function() {
+    this.form = document.createElement("form");
+    this.form.id = this.options.formId;
+    Element.addClassName(this.form, this.options.formClassName)
+    this.form.onsubmit = this.onSubmit.bind(this);
+
+    this.createEditField();
+
+    if (this.options.textarea) {
+      var br = document.createElement("br");
+      this.form.appendChild(br);
+    }
+
+    if (this.options.okButton) {
+      okButton = document.createElement("input");
+      okButton.type = "submit";
+      okButton.value = this.options.okText;
+      okButton.className = 'editor_ok_button';
+      this.form.appendChild(okButton);
+    }
+
+    if (this.options.cancelLink) {
+      cancelLink = document.createElement("a");
+      cancelLink.href = "#";
+      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+      cancelLink.onclick = this.onclickCancel.bind(this);
+      cancelLink.className = 'editor_cancel';      
+      this.form.appendChild(cancelLink);
+    }
+  },
+  hasHTMLLineBreaks: function(string) {
+    if (!this.options.handleLineBreaks) return false;
+    return string.match(/<br/i) || string.match(/<p>/i);
+  },
+  convertHTMLLineBreaks: function(string) {
+    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+  },
+  createEditField: function() {
+    var text;
+    if(this.options.loadTextURL) {
+      text = this.options.loadingText;
+    } else {
+      text = this.getText();
+    }
+
+    var obj = this;
+    
+    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+      this.options.textarea = false;
+      var textField = document.createElement("input");
+      textField.obj = this;
+      textField.type = "text";
+      textField.name = "value";
+      textField.value = text;
+      textField.style.backgroundColor = this.options.highlightcolor;
+      textField.className = 'editor_field';
+      var size = this.options.size || this.options.cols || 0;
+      if (size != 0) textField.size = size;
+      if (this.options.submitOnBlur)
+        textField.onblur = this.onSubmit.bind(this);
+      this.editField = textField;
+    } else {
+      this.options.textarea = true;
+      var textArea = document.createElement("textarea");
+      textArea.obj = this;
+      textArea.name = "value";
+      textArea.value = this.convertHTMLLineBreaks(text);
+      textArea.rows = this.options.rows;
+      textArea.cols = this.options.cols || 40;
+      textArea.className = 'editor_field';      
+      if (this.options.submitOnBlur)
+        textArea.onblur = this.onSubmit.bind(this);
+      this.editField = textArea;
+    }
+    
+    if(this.options.loadTextURL) {
+      this.loadExternalText();
+    }
+    this.form.appendChild(this.editField);
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  loadExternalText: function() {
+    Element.addClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = true;
+    new Ajax.Request(
+      this.options.loadTextURL,
+      Object.extend({
+        asynchronous: true,
+        onComplete: this.onLoadedExternalText.bind(this)
+      }, this.options.ajaxOptions)
+    );
+  },
+  onLoadedExternalText: function(transport) {
+    Element.removeClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = false;
+    this.editField.value = transport.responseText.stripTags();
+  },
+  onclickCancel: function() {
+    this.onComplete();
+    this.leaveEditMode();
+    return false;
+  },
+  onFailure: function(transport) {
+    this.options.onFailure(transport);
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+      this.oldInnerHTML = null;
+    }
+    return false;
+  },
+  onSubmit: function() {
+    // onLoading resets these so we need to save them away for the Ajax call
+    var form = this.form;
+    var value = this.editField.value;
+    
+    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+    // to be displayed indefinitely
+    this.onLoading();
+    
+    if (this.options.evalScripts) {
+      new Ajax.Request(
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this),
+          asynchronous:true, 
+          evalScripts:true
+        }, this.options.ajaxOptions));
+    } else  {
+      new Ajax.Updater(
+        { success: this.element,
+          // don't update on failure (this could be an option)
+          failure: null }, 
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this)
+        }, this.options.ajaxOptions));
+    }
+    // stop the event to avoid a page refresh in Safari
+    if (arguments.length > 1) {
+      Event.stop(arguments[0]);
+    }
+    return false;
+  },
+  onLoading: function() {
+    this.saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  showSaving: function() {
+    this.oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    Element.addClassName(this.element, this.options.savingClassName);
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+  },
+  removeForm: function() {
+    if(this.form) {
+      if (this.form.parentNode) Element.remove(this.form);
+      this.form = null;
+    }
+  },
+  enterHover: function() {
+    if (this.saving) return;
+    this.element.style.backgroundColor = this.options.highlightcolor;
+    if (this.effect) {
+      this.effect.cancel();
+    }
+    Element.addClassName(this.element, this.options.hoverClassName)
+  },
+  leaveHover: function() {
+    if (this.options.backgroundColor) {
+      this.element.style.backgroundColor = this.oldBackground;
+    }
+    Element.removeClassName(this.element, this.options.hoverClassName)
+    if (this.saving) return;
+    this.effect = new Effect.Highlight(this.element, {
+      startcolor: this.options.highlightcolor,
+      endcolor: this.options.highlightendcolor,
+      restorecolor: this.originalBackground
+    });
+  },
+  leaveEditMode: function() {
+    Element.removeClassName(this.element, this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+    if (this.options.externalControl) {
+      Element.show(this.options.externalControl);
+    }
+    this.editing = false;
+    this.saving = false;
+    this.oldInnerHTML = null;
+    this.onLeaveEditMode();
+  },
+  onComplete: function(transport) {
+    this.leaveEditMode();
+    this.options.onComplete.bind(this)(transport, this.element);
+  },
+  onEnterEditMode: function() {},
+  onLeaveEditMode: function() {},
+  dispose: function() {
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+    }
+    this.leaveEditMode();
+    Event.stopObserving(this.element, 'click', this.onclickListener);
+    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  }
+};
+
+Ajax.InPlaceCollectionEditor = Class.create();
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
+  createEditField: function() {
+    if (!this.cached_selectTag) {
+      var selectTag = document.createElement("select");
+      var collection = this.options.collection || [];
+      var optionTag;
+      collection.each(function(e,i) {
+        optionTag = document.createElement("option");
+        optionTag.value = (e instanceof Array) ? e[0] : e;
+        if(this.options.value==optionTag.value) optionTag.selected = true;
+        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
+        selectTag.appendChild(optionTag);
+      }.bind(this));
+      this.cached_selectTag = selectTag;
+    }
+
+    this.editField = this.cached_selectTag;
+    if(this.options.loadTextURL) this.loadExternalText();
+    this.form.appendChild(this.editField);
+    this.options.callback = function(form, value) {
+      return "value=" + encodeURIComponent(value);
+    }
+  }
+});
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+};
Index: /afridex/plugins/fresh-page/js/greybox.js
===================================================================
--- /afridex/plugins/fresh-page/js/greybox.js (revision 21)
+++ /afridex/plugins/fresh-page/js/greybox.js (revision 21)
@@ -0,0 +1,48 @@
+/* Greybox Redux
+ * Required: http://jquery.com/
+ * Written by: John Resig
+ * Based on code by: 4mir Salihefendic (http://amix.dk)
+ * License: LGPL (read more in LGPL.txt)
+ */
+
+var GB_DONE = false;
+var GB_HEIGHT = 400;
+var GB_WIDTH = 400;
+
+function GB_show(caption, url, height, width) {
+  GB_HEIGHT = height || 400;
+  GB_WIDTH = width || 400;
+  if(!GB_DONE) {
+    $(document.body)
+      .append("<div id='GB_overlay'></div><div id='GB_window'><div id='GB_caption'></div>"
+        + "<img src='./../wp-content/plugins/fresh-page/images/close.gif' alt='Close window'/></div>");
+    $("#GB_window img").click(GB_hide);
+    $("#GB_overlay").click(GB_hide);
+    $(window).resize(GB_position);
+    GB_DONE = true;
+  }
+
+  $("#GB_frame").remove();
+  $("#GB_window").append("<iframe id='GB_frame' src='"+url+"'></iframe>");
+
+  $("#GB_caption").html(caption);
+  $("#GB_overlay").show();
+  GB_position();
+
+  if(GB_ANIMATION)
+    $("#GB_window").slideDown("slow");
+  else
+    $("#GB_window").show();
+}
+
+function GB_hide() {
+  $("#GB_window,#GB_overlay").hide();
+}
+
+function GB_position() {
+  var de = document.documentElement;
+  var w = self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
+  $("#GB_window").css({width:GB_WIDTH+"px",height:GB_HEIGHT+"px",
+    left: ((w - GB_WIDTH)/2)+"px" });
+  $("#GB_frame").css("height",GB_HEIGHT - 32 +"px");
+}
Index: /afridex/plugins/fresh-page/js/cropper.uncompressed.js
===================================================================
--- /afridex/plugins/fresh-page/js/cropper.uncompressed.js (revision 21)
+++ /afridex/plugins/fresh-page/js/cropper.uncompressed.js (revision 21)
@@ -0,0 +1,1331 @@
+/**
+ * Image Cropper (v. 1.2.0 - 2006-10-30 )
+ * Copyright (c) 2006 David Spurr (http://www.defusion.org.uk/)
+ * 
+ * The image cropper provides a way to draw a crop area on an image and capture
+ * the coordinates of the drawn crop area.
+ * 
+ * Features include:
+ * 		- Based on Prototype and Scriptaculous
+ * 		- Image editing package styling, the crop area functions and looks 
+ * 		  like those found in popular image editing software
+ * 		- Dynamic inclusion of required styles
+ * 		- Drag to draw areas
+ * 		- Shift drag to draw/resize areas as squares
+ * 		- Selection area can be moved 
+ * 		- Seleciton area can be resized using resize handles
+ * 		- Allows dimension ratio limited crop areas
+ * 		- Allows minimum dimension crop areas
+ * 		- Allows maximum dimesion crop areas
+ * 		- If both min & max dimension options set to the same value for a single axis,then the cropper will not 
+ * 		  display the resize handles as appropriate (when min & max dimensions are passed for both axes this
+ * 		  results in a 'fixed size' crop area)
+ * 		- Allows dynamic preview of resultant crop ( if minimum width & height are provided ), this is
+ * 		  implemented as a subclass so can be excluded when not required
+ * 		- Movement of selection area by arrow keys ( shift + arrow key will move selection area by
+ * 		  10 pixels )
+ *		- All operations stay within bounds of image
+ * 		- All functionality & display compatible with most popular browsers supported by Prototype:
+ * 			PC:	IE 7, 6 & 5.5, Firefox 1.5, Opera 8.5 (see known issues) & 9.0b
+ * 			MAC: Camino 1.0, Firefox 1.5, Safari 2.0
+ * 
+ * Requires:
+ * 		- Prototype v. 1.5.0_rc0 > (as packaged with Scriptaculous 1.6.1)
+ * 		- Scriptaculous v. 1.6.1 > modules: builder, dragdrop 
+ * 		
+ * Known issues:
+ * 		- Safari animated gifs, only one of each will animate, this seems to be a known Safari issue
+ * 
+ * 		- After drawing an area and then clicking to start a new drag in IE 5.5 the rendered height 
+ *        appears as the last height until the user drags, this appears to be the related to the error 
+ *        that the forceReRender() method fixes for IE 6, i.e. IE 5.5 is not redrawing the box properly.
+ * 
+ * 		- Lack of CSS opacity support in Opera before version 9 mean we disable those style rules, these 
+ * 		  could be fixed by using PNGs with transparency if Opera 8.5 support is high priority for you
+ * 
+ * 		- Marching ants keep reloading in IE <6 (not tested in IE7), it is a known issue in IE and I have 
+ *        found no viable workarounds that can be included in the release. If this really is an issue for you
+ *        either try this post: http://mir.aculo.us/articles/2005/08/28/internet-explorer-and-ajax-image-caching-woes
+ *        or uncomment the 'FIX MARCHING ANTS IN IE' rules in the CSS file
+ *		
+ *		- Styling & borders on image, any CSS styling applied directly to the image itself (floats, borders, padding, margin, etc.) will 
+ *		  cause problems with the cropper. The use of a wrapper element to apply these styles to is recommended.
+ * 
+ * 		- overflow: auto or overflow: scroll on parent will cause cropper to burst out of parent in IE and Opera (maybe Mac browsers too)
+ *		  I'm not sure why yet.
+ * 
+ * Usage:
+ * 		See Cropper.Img & Cropper.ImgWithPreview for usage details
+ * 
+ * Changelog:
+ * v1.2.0 - 2006-10-30
+ * 		+ Added id to the preview image element using 'imgCrop_[originalImageID]'
+ *      * #00001 - Fixed bug: Doesn't account for scroll offsets
+ *      * #00009 - Fixed bug: Placing the cropper inside differently positioned elements causes incorrect co-ordinates and display
+ *      * #00013 - Fixed bug: I-bar cursor appears on drag plane
+ *      * #00014 - Fixed bug: If ID for image tag is not found in document script throws error
+ *      * Fixed bug with drag start co-ordinates if wrapper element has moved in browser (e.g. dragged to a new position)
+ *      * Fixed bug with drag start co-ordinates if image contained in a wrapper with scrolling - this may be buggy if image 
+ * 		  has other ancestors with scrolling applied (except the body)
+ *      * #00015 - Fixed bug: When cropper removed and then reapplied onEndCrop callback gets called multiple times, solution suggestion from Bill Smith
+ *      * Various speed increases & code cleanup which meant improved performance in Mac - which allowed removal of different overlay methods for
+ *        IE and all other browsers, which led to a fix for:
+ * 		* #00010 - Fixed bug: Select area doesn't adhere to image size when image resized using img attributes
+ *      - #00006 - Removed default behaviour of automatically setting a ratio when both min width & height passed, the ratioDimensions must be passed in
+ * 		+ #00005 - Added ability to set maximum crop dimensions, if both min & max set as the same value then we'll get a fixed cropper size on the axes as appropriate
+ *        and the resize handles will not be displayed as appropriate
+ * 		* Switched keydown for keypress for moving select area with cursor keys (makes for nicer action) - doesn't appear to work in Safari
+ * 
+ * v1.1.3 - 2006-08-21
+ * 		* Fixed wrong cursor on western handle in CSS
+ * 		+ #00008 & #00003 - Added feature: Allow to set dimensions & position for cropper on load
+ *      * #00002 - Fixed bug: Pressing 'remove cropper' twice removes image in IE
+ * 
+ * v1.1.2 - 2006-06-09
+ * 		* Fixed bugs with ratios when GCD is low (patch submitted by Andy Skelton)
+ * 
+ * v1.1.1 - 2006-06-03
+ * 		* Fixed bug with rendering issues fix in IE 5.5
+ * 		* Fixed bug with endCrop callback issues once cropper had been removed & reset in IE
+ * 
+ * v1.1.0 - 2006-06-02
+ * 		* Fixed bug with IE constantly trying to reload select area background image
+ * 		* Applied more robust fix to Safari & IE rendering issues
+ * 		+ Added method to reset parameters - useful for when dynamically changing img cropper attached to
+ * 		+ Added method to remove cropper from image
+ * 
+ * v1.0.0 - 2006-05-18 
+ * 		+ Initial verison
+ * 
+ * 
+ * Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)
+ * All rights reserved.
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * http://www.opensource.org/licenses/bsd-license.php
+ * 
+ * See scriptaculous.js for full scriptaculous licence
+ */
+ 
+/**
+ * Extend the Draggable class to allow us to pass the rendering
+ * down to the Cropper object.
+ */
+var CropDraggable = Class.create();
+
+Object.extend( Object.extend( CropDraggable.prototype, Draggable.prototype), {
+	
+	initialize: function(element) {
+		this.options = Object.extend(
+			{
+				/**
+				 * The draw method to defer drawing to
+				 */
+				drawMethod: function() {}
+			}, 
+			arguments[1] || {}
+		);
+
+		this.element = $(element);
+
+		this.handle = this.element;
+
+		this.delta    = this.currentDelta();
+		this.dragging = false;   
+
+		this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+		Event.observe(this.handle, "mousedown", this.eventMouseDown);
+
+		Draggables.register(this);
+	},
+	
+	/**
+	 * Defers the drawing of the draggable to the supplied method
+	 */
+	draw: function(point) {
+		var pos = Position.cumulativeOffset(this.element);
+		var d = this.currentDelta();
+		pos[0] -= d[0]; 
+		pos[1] -= d[1];
+				
+		var p = [0,1].map(function(i) { 
+			return (point[i]-pos[i]-this.offset[i]) 
+		}.bind(this));
+				
+		this.options.drawMethod( p );
+	}
+	
+});
+
+
+/**
+ * The Cropper object, this will attach itself to the provided image by wrapping it with 
+ * the generated xHTML structure required by the cropper.
+ * 
+ * Usage:
+ * 	@param obj Image element to attach to
+ * 	@param obj Optional options:
+ * 		- ratioDim obj 
+ * 			The pixel dimensions to apply as a restrictive ratio, with properties x & y
+ * 
+ * 		- minWidth int 
+ * 			The minimum width for the select area in pixels
+ * 
+ * 		- minHeight	int 
+ * 			The mimimum height for the select area in pixels
+ * 
+ * 		- maxWidth int
+ * 			The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)
+ * 
+ * 		- maxHeight int
+ *			The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)
+ * 
+ * 		- displayOnInit int 
+ * 			Whether to display the select area on initialisation, only used when providing minimum width & height or ratio
+ * 
+ * 		- onEndCrop func
+ * 			The callback function to provide the crop details to on end of a crop (see below)
+ * 
+ * 		- captureKeys boolean
+ * 			Whether to capture the keys for moving the select area, as these can cause some problems at the moment
+ * 
+ * 		- onloadCoords obj
+ * 			A coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area to display onload
+ * 	
+ *----------------------------------------------
+ * 
+ * The callback function provided via the onEndCrop option should accept the following parameters:
+ * 		- coords obj
+ * 			The coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area
+ * 
+ * 		- dimensions obj
+ * 			The dimensions object with properites width & height; for the dimensions of the select area
+ * 		
+ *
+ * 		Example:
+ * 			function onEndCrop( coords, dimensions ) {
+ *				$( 'x1' ).value 	= coords.x1;
+ *				$( 'y1' ).value 	= coords.y1;
+ *				$( 'x2' ).value 	= coords.x2;
+ *				$( 'y2' ).value 	= coords.y2;
+ *				$( 'width' ).value 	= dimensions.width;
+ *				$( 'height' ).value	= dimensions.height;
+ *			}
+ * 
+ */
+var Cropper = {};
+Cropper.Img = Class.create();
+Cropper.Img.prototype = {
+	
+	/**
+	 * Initialises the class
+	 * 
+	 * @access public
+	 * @param obj Image element to attach to
+	 * @param obj Options
+	 * @return void
+	 */
+	initialize: function(element, options) {
+		this.options = Object.extend(
+			{
+				/**
+				 * @var obj
+				 * The pixel dimensions to apply as a restrictive ratio
+				 */
+				ratioDim: { x: 0, y: 0 },
+				/**
+				 * @var int
+				 * The minimum pixel width, also used as restrictive ratio if min height passed too
+				 */
+				minWidth:		0,
+				/**
+				 * @var int
+				 * The minimum pixel height, also used as restrictive ratio if min width passed too
+				 */
+				minHeight:		0,
+				/**
+				 * @var boolean
+				 * Whether to display the select area on initialisation, only used when providing minimum width & height or ratio
+				 */
+				displayOnInit:	false,
+				/**
+				 * @var function
+				 * The call back function to pass the final values to
+				 */
+				onEndCrop: Prototype.emptyFunction,
+				/**
+				 * @var boolean
+				 * Whether to capture key presses or not
+				 */
+				captureKeys: true,
+				/**
+				 * @var obj Coordinate object x1, y1, x2, y2
+				 * The coordinates to optionally display the select area at onload
+				 */
+				onloadCoords: null,
+				/**
+				 * @var int
+				 * The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)
+				 */
+				maxWidth: 0,
+				/**
+				 * @var int
+				 * The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)
+				 */
+				maxHeight: 0
+			}, 
+			options || {}
+		);				
+		/**
+		 * @var obj
+		 * The img node to attach to
+		 */
+		this.img			= $( element );
+		/**
+		 * @var obj
+		 * The x & y coordinates of the click point
+		 */
+		this.clickCoords	= { x: 0, y: 0 };
+		/**
+		 * @var boolean
+		 * Whether the user is dragging
+		 */
+		this.dragging		= false;
+		/**
+		 * @var boolean
+		 * Whether the user is resizing
+		 */
+		this.resizing		= false;
+		/**
+		 * @var boolean
+		 * Whether the user is on a webKit browser
+		 */
+		this.isWebKit 		= /Konqueror|Safari|KHTML/.test( navigator.userAgent );
+		/**
+		 * @var boolean
+		 * Whether the user is on IE
+		 */
+		this.isIE 			= /MSIE/.test( navigator.userAgent );
+		/**
+		 * @var boolean
+		 * Whether the user is on Opera below version 9
+		 */
+		this.isOpera8		= /Opera\s[1-8]/.test( navigator.userAgent );
+		/**
+		 * @var int
+		 * The x ratio 
+		 */
+		this.ratioX			= 0;
+		/**
+		 * @var int
+		 * The y ratio
+		 */
+		this.ratioY			= 0;
+		/**
+		 * @var boolean
+		 * Whether we've attached sucessfully
+		 */
+		this.attached		= false;
+		/**
+		 * @var boolean
+		 * Whether we've got a fixed width (if minWidth EQ or GT maxWidth then we have a fixed width
+		 * in the case of minWidth > maxWidth maxWidth wins as the fixed width)
+		 */
+		this.fixedWidth		= ( this.options.maxWidth > 0 && ( this.options.minWidth >= this.options.maxWidth ) );
+		/**
+		 * @var boolean
+		 * Whether we've got a fixed height (if minHeight EQ or GT maxHeight then we have a fixed height
+		 * in the case of minHeight > maxHeight maxHeight wins as the fixed height)
+		 */
+		this.fixedHeight	= ( this.options.maxHeight > 0 && ( this.options.minHeight >= this.options.maxHeight ) );
+		
+		// quit if the image element doesn't exist
+		if( typeof this.img == 'undefined' ) return;
+				
+		// include the stylesheet		
+		$A( document.getElementsByTagName( 'script' ) ).each( 
+			function(s) {
+				if( s.src.match( /cropper\.js/ ) ) {
+					var path 	= s.src.replace( /cropper\.js(.*)?/, '' );
+					// '<link rel="stylesheet" type="text/css" href="' + path + 'cropper.css" media="screen" />';
+					var style 		= document.createElement( 'link' );
+					style.rel 		= 'stylesheet';
+					style.type 		= 'text/css';
+					style.href 		= path + 'cropper.css';
+					style.media 	= 'screen';
+					document.getElementsByTagName( 'head' )[0].appendChild( style );
+				}
+	    	}
+	    );   
+	
+		// calculate the ratio when neccessary
+		if( this.options.ratioDim.x > 0 && this.options.ratioDim.y > 0 ) {
+			var gcd = this.getGCD( this.options.ratioDim.x, this.options.ratioDim.y );
+			this.ratioX = this.options.ratioDim.x / gcd;
+			this.ratioY = this.options.ratioDim.y / gcd;
+			// dump( 'RATIO : ' + this.ratioX + ':' + this.ratioY + '\n' );
+		}
+							
+		// initialise sub classes
+		this.subInitialize();
+
+		// only load the event observers etc. once the image is loaded
+		// this is done after the subInitialize() call just in case the sub class does anything
+		// that will affect the result of the call to onLoad()
+		if( this.img.complete || this.isWebKit ) this.onLoad(); // for some reason Safari seems to support img.complete but returns 'undefined' on the this.img object
+		else Event.observe( this.img, 'load', this.onLoad.bindAsEventListener( this) );		
+	},
+	
+	/**
+	 * The Euclidean algorithm used to find the greatest common divisor
+	 * 
+	 * @acces private
+	 * @param int Value 1
+	 * @param int Value 2
+	 * @return int
+	 */
+	getGCD : function( a , b ) {
+		if( b == 0 ) return a;
+		return this.getGCD(b, a % b );
+	},
+	
+	/**
+	 * Attaches the cropper to the image once it has loaded
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	onLoad: function( ) {
+		/*
+		 * Build the container and all related elements, will result in the following
+		 *
+		 * <div class="imgCrop_wrap">
+		 * 		<img ... this.img ... />
+		 * 		<div class="imgCrop_dragArea">
+		 * 			<!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->
+		 * 			<div class="imgCrop_overlay imageCrop_north"><span></span></div>
+		 * 			<div class="imgCrop_overlay imageCrop_east"><span></span></div>
+		 * 			<div class="imgCrop_overlay imageCrop_south"><span></span></div>
+		 * 			<div class="imgCrop_overlay imageCrop_west"><span></span></div>
+		 * 			<div class="imgCrop_selArea">
+		 * 				<!-- marquees -->
+		 * 				<!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->
+		 * 				<div class="imgCrop_marqueeHoriz imgCrop_marqueeNorth"><span></span></div>
+		 * 				<div class="imgCrop_marqueeVert imgCrop_marqueeEast"><span></span></div>
+		 * 				<div class="imgCrop_marqueeHoriz imgCrop_marqueeSouth"><span></span></div>
+		 * 				<div class="imgCrop_marqueeVert imgCrop_marqueeWest"><span></span></div>			
+		 * 				<!-- handles -->
+		 * 				<div class="imgCrop_handle imgCrop_handleN"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleNE"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleE"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleSE"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleS"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleSW"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleW"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleNW"></div>
+		 * 				<div class="imgCrop_clickArea"></div>
+		 * 			</div>	
+		 * 			<div class="imgCrop_clickArea"></div>
+		 * 		</div>	
+		 * </div>
+		 */
+		var cNamePrefix = 'imgCrop_';
+		
+		// get the point to insert the container
+		var insertPoint = this.img.parentNode;
+		
+		// apply an extra class to the wrapper to fix Opera below version 9
+		var fixOperaClass = '';
+		if( this.isOpera8 ) fixOperaClass = ' opera8';
+		this.imgWrap = Builder.node( 'div', { 'class': cNamePrefix + 'wrap' + fixOperaClass } );
+		
+		this.north		= Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'north' }, [Builder.node( 'span' )] );
+		this.east		= Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'east' } , [Builder.node( 'span' )] );
+		this.south		= Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'south' }, [Builder.node( 'span' )] );
+		this.west		= Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'west' } , [Builder.node( 'span' )] );
+		
+		var overlays	= [ this.north, this.east, this.south, this.west ];
+
+		this.dragArea	= Builder.node( 'div', { 'class': cNamePrefix + 'dragArea' }, overlays );
+						
+		this.handleN	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleN' } );
+		this.handleNE	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleNE' } );
+		this.handleE	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleE' } );
+		this.handleSE	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleSE' } );
+		this.handleS	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleS' } );
+		this.handleSW	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleSW' } );
+		this.handleW	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleW' } );
+		this.handleNW	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleNW' } );
+				
+		this.selArea	= Builder.node( 'div', { 'class': cNamePrefix + 'selArea' },
+			[
+				Builder.node( 'div', { 'class': cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeNorth' }, [Builder.node( 'span' )] ),
+				Builder.node( 'div', { 'class': cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeEast' }  , [Builder.node( 'span' )] ),
+				Builder.node( 'div', { 'class': cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeSouth' }, [Builder.node( 'span' )] ),
+				Builder.node( 'div', { 'class': cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeWest' }  , [Builder.node( 'span' )] ),
+				this.handleN,
+				this.handleNE,
+				this.handleE,
+				this.handleSE,
+				this.handleS,
+				this.handleSW,
+				this.handleW,
+				this.handleNW,
+				Builder.node( 'div', { 'class': cNamePrefix + 'clickArea' } )
+			]
+		);
+				
+		this.imgWrap.appendChild( this.img );
+		this.imgWrap.appendChild( this.dragArea );
+		this.dragArea.appendChild( this.selArea );
+		this.dragArea.appendChild( Builder.node( 'div', { 'class': cNamePrefix + 'clickArea' } ) );
+
+		insertPoint.appendChild( this.imgWrap );
+
+		// add event observers
+		this.startDragBind 	= this.startDrag.bindAsEventListener( this );
+		Event.observe( this.dragArea, 'mousedown', this.startDragBind );
+		
+		this.onDragBind 	= this.onDrag.bindAsEventListener( this );
+		Event.observe( document, 'mousemove', this.onDragBind );
+		
+		this.endCropBind 	= this.endCrop.bindAsEventListener( this );
+		Event.observe( document, 'mouseup', this.endCropBind );
+		
+		this.resizeBind		= this.startResize.bindAsEventListener( this );
+		this.handles = [ this.handleN, this.handleNE, this.handleE, this.handleSE, this.handleS, this.handleSW, this.handleW, this.handleNW ];
+		this.registerHandles( true );
+		
+		if( this.options.captureKeys ) {
+			this.keysBind = this.handleKeys.bindAsEventListener( this );
+			Event.observe( document, 'keypress', this.keysBind );
+		}
+
+		// attach the dragable to the select area
+		new CropDraggable( this.selArea, { drawMethod: this.moveArea.bindAsEventListener( this ) } );
+		
+		this.setParams();
+	},
+	
+	/**
+	 * Manages adding or removing the handle event handler and hiding or displaying them as appropriate
+	 * 
+	 * @access private
+	 * @param boolean registration true = add, false = remove
+	 * @return void
+	 */
+	registerHandles: function( registration ) {	
+		for( var i = 0; i < this.handles.length; i++ ) {
+			var handle = $( this.handles[i] );
+			
+			if( registration ) {
+				var hideHandle	= false;	// whether to hide the handle
+				
+				// disable handles asappropriate if we've got fixed dimensions
+				// if both dimensions are fixed we don't need to do much
+				if( this.fixedWidth && this.fixedHeight ) hideHandle = true;
+				else if( this.fixedWidth || this.fixedHeight ) {
+					// if one of the dimensions is fixed then just hide those handles
+					var isCornerHandle	= handle.className.match( /([S|N][E|W])$/ )
+					var isWidthHandle 	= handle.className.match( /(E|W)$/ );
+					var isHeightHandle 	= handle.className.match( /(N|S)$/ );
+					if( isCornerHandle ) hideHandle = true;
+					else if( this.fixedWidth && isWidthHandle ) hideHandle = true;
+					else if( this.fixedHeight && isHeightHandle ) hideHandle = true;
+				}
+				if( hideHandle ) handle.hide();
+				else Event.observe( handle, 'mousedown', this.resizeBind );
+			} else {
+				handle.show();
+				Event.stopObserving( handle, 'mousedown', this.resizeBind );
+			}
+		}
+	},
+		
+	/**
+	 * Sets up all the cropper parameters, this can be used to reset the cropper when dynamically
+	 * changing the images
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	setParams: function() {
+		/**
+		 * @var int
+		 * The image width
+		 */
+		this.imgW = this.img.width;
+		/**
+		 * @var int
+		 * The image height
+		 */
+		this.imgH = this.img.height;			
+
+		$( this.north ).setStyle( { height: 0 } );
+		$( this.east ).setStyle( { width: 0, height: 0 } );
+		$( this.south ).setStyle( { height: 0 } );
+		$( this.west ).setStyle( { width: 0, height: 0 } );
+		
+		// resize the container to fit the image
+		$( this.imgWrap ).setStyle( { 'width': this.imgW + 'px', 'height': this.imgH + 'px' } );
+		
+		// hide the select area
+		$( this.selArea ).hide();
+						
+		// setup the starting position of the select area
+		var startCoords = { x1: 0, y1: 0, x2: 0, y2: 0 };
+		var validCoordsSet = false;
+		
+		// display the select area 
+		if( this.options.onloadCoords != null ) {
+			// if we've being given some coordinates to 
+			startCoords = this.cloneCoords( this.options.onloadCoords );
+			validCoordsSet = true;
+		} else if( this.options.ratioDim.x > 0 && this.options.ratioDim.y > 0 ) {
+			// if there is a ratio limit applied and the then set it to initial ratio
+			startCoords.x1 = Math.ceil( ( this.imgW - this.options.ratioDim.x ) / 2 );
+			startCoords.y1 = Math.ceil( ( this.imgH - this.options.ratioDim.y ) / 2 );
+			startCoords.x2 = startCoords.x1 + this.options.ratioDim.x;
+			startCoords.y2 = startCoords.y1 + this.options.ratioDim.y;
+			validCoordsSet = true;
+		}
+		
+		this.setAreaCoords( startCoords, false, false, 1 );
+		
+		if( this.options.displayOnInit && validCoordsSet ) {
+			this.selArea.show();
+			this.drawArea();
+			this.endCrop();
+		}
+		
+		this.attached = true;
+	},
+	
+	/**
+	 * Removes the cropper
+	 * 
+	 * @access public
+	 * @return void
+	 */
+	remove: function() {
+		if( this.attached ) {
+			this.attached = false;
+			
+			// remove the elements we inserted
+			this.imgWrap.parentNode.insertBefore( this.img, this.imgWrap );
+			this.imgWrap.parentNode.removeChild( this.imgWrap );
+			
+			// remove the event observers
+			Event.stopObserving( this.dragArea, 'mousedown', this.startDragBind );
+			Event.stopObserving( document, 'mousemove', this.onDragBind );		
+			Event.stopObserving( document, 'mouseup', this.endCropBind );
+			this.registerHandles( false );
+			if( this.options.captureKeys ) Event.stopObserving( document, 'keypress', this.keysBind );
+		}
+	},
+	
+	/**
+	 * Resets the cropper, can be used either after being removed or any time you wish
+	 * 
+	 * @access public
+	 * @return void
+	 */
+	reset: function() {
+		if( !this.attached ) this.onLoad();
+		else this.setParams();
+		this.endCrop();
+	},
+	
+	/**
+	 * Handles the key functionality, currently just using arrow keys to move, if the user
+	 * presses shift then the area will move by 10 pixels
+	 */
+	handleKeys: function( e ) {
+		var dir = { x: 0, y: 0 }; // direction to move it in & the amount in pixels
+		if( !this.dragging ) {
+			
+			// catch the arrow keys
+			switch( e.keyCode ) {
+				case( 37 ) : // left
+					dir.x = -1;
+					break;
+				case( 38 ) : // up
+					dir.y = -1;
+					break;
+				case( 39 ) : // right
+					dir.x = 1;
+					break
+				case( 40 ) : // down
+					dir.y = 1;
+					break;
+			}
+			
+			if( dir.x != 0 || dir.y != 0 ) {
+				// if shift is pressed then move by 10 pixels
+				if( e.shiftKey ) {
+					dir.x *= 10;
+					dir.y *= 10;
+				}
+				
+				this.moveArea( [ this.areaCoords.x1 + dir.x, this.areaCoords.y1 + dir.y ] );
+				Event.stop( e ); 
+			}
+		}
+	},
+	
+	/**
+	 * Calculates the width from the areaCoords
+	 * 
+	 * @access private
+	 * @return int
+	 */
+	calcW: function() {
+		return (this.areaCoords.x2 - this.areaCoords.x1)
+	},
+	
+	/**
+	 * Calculates the height from the areaCoords
+	 * 
+	 * @access private
+	 * @return int
+	 */
+	calcH: function() {
+		return (this.areaCoords.y2 - this.areaCoords.y1)
+	},
+	
+	/**
+	 * Moves the select area to the supplied point (assumes the point is x1 & y1 of the select area)
+	 * 
+	 * @access public
+	 * @param array Point for x1 & y1 to move select area to
+	 * @return void
+	 */
+	moveArea: function( point ) {
+		// dump( 'moveArea        : ' + point[0] + ',' + point[1] + ',' + ( point[0] + ( this.areaCoords.x2 - this.areaCoords.x1 ) ) + ',' + ( point[1] + ( this.areaCoords.y2 - this.areaCoords.y1 ) ) + '\n' );
+		this.setAreaCoords( 
+			{
+				x1: point[0], 
+				y1: point[1],
+				x2: point[0] + this.calcW(),
+				y2: point[1] + this.calcH()
+			},
+			true,
+			false
+		);
+		this.drawArea();
+	},
+
+	/**
+	 * Clones a co-ordinates object, stops problems with handling them by reference
+	 * 
+	 * @access private
+	 * @param obj Coordinate object x1, y1, x2, y2
+	 * @return obj Coordinate object x1, y1, x2, y2
+	 */
+	cloneCoords: function( coords ) {
+		return { x1: coords.x1, y1: coords.y1, x2: coords.x2, y2: coords.y2 };
+	},
+
+	/**
+	 * Sets the select coords to those provided but ensures they don't go
+	 * outside the bounding box
+	 * 
+	 * @access private
+	 * @param obj Coordinates x1, y1, x2, y2
+	 * @param boolean Whether this is a move
+	 * @param boolean Whether to apply squaring
+	 * @param obj Direction of mouse along both axis x, y ( -1 = negative, 1 = positive ) only required when moving etc.
+	 * @param string The current resize handle || null
+	 * @return void
+	 */
+	setAreaCoords: function( coords, moving, square, direction, resizeHandle ) {
+		// dump( 'setAreaCoords (in) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 );
+		if( moving ) {
+			// if moving
+			var targW = coords.x2 - coords.x1;
+			var targH = coords.y2 - coords.y1;
+			
+			// ensure we're within the bounds
+			if( coords.x1 < 0 ) {
+				coords.x1 = 0;
+				coords.x2 = targW;
+			}
+			if( coords.y1 < 0 ) {
+				coords.y1 = 0;
+				coords.y2 = targH;
+			}
+			if( coords.x2 > this.imgW ) {
+				coords.x2 = this.imgW;
+				coords.x1 = this.imgW - targW;
+			}
+			if( coords.y2 > this.imgH ) {
+				coords.y2 = this.imgH;
+				coords.y1 = this.imgH - targH;
+			}			
+		} else {
+			// ensure we're within the bounds
+			if( coords.x1 < 0 ) coords.x1 = 0;
+			if( coords.y1 < 0 ) coords.y1 = 0;
+			if( coords.x2 > this.imgW ) coords.x2 = this.imgW;
+			if( coords.y2 > this.imgH ) coords.y2 = this.imgH;
+			
+			// This is passed as null in onload
+			if( direction != null ) {
+								
+				// apply the ratio or squaring where appropriate
+				if( this.ratioX > 0 ) this.applyRatio( coords, { x: this.ratioX, y: this.ratioY }, direction, resizeHandle );
+				else if( square ) this.applyRatio( coords, { x: 1, y: 1 }, direction, resizeHandle );
+										
+				var mins = [ this.options.minWidth, this.options.minHeight ]; // minimum dimensions [x,y]			
+				var maxs = [ this.options.maxWidth, this.options.maxHeight ]; // maximum dimensions [x,y]
+		
+				// apply dimensions where appropriate
+				if( mins[0] > 0 || mins[1] > 0 || maxs[0] > 0 || maxs[1] > 0) {
+				
+					var coordsTransX 	= { a1: coords.x1, a2: coords.x2 };
+					var coordsTransY 	= { a1: coords.y1, a2: coords.y2 };
+					var boundsX			= { min: 0, max: this.imgW };
+					var boundsY			= { min: 0, max: this.imgH };
+					
+					// handle squaring properly on single axis minimum dimensions
+					if( (mins[0] != 0 || mins[1] != 0) && square ) {
+						if( mins[0] > 0 ) mins[1] = mins[0];
+						else if( mins[1] > 0 ) mins[0] = mins[1];
+					}
+					
+					if( (maxs[0] != 0 || maxs[0] != 0) && square ) {
+						// if we have a max x value & it is less than the max y value then we set the y max to the max x (so we don't go over the minimum maximum of one of the axes - if that makes sense)
+						if( maxs[0] > 0 && maxs[0] <= maxs[1] ) maxs[1] = maxs[0];
+						else if( maxs[1] > 0 && maxs[1] <= maxs[0] ) maxs[0] = maxs[1];
+					}
+					
+					if( mins[0] > 0 ) this.applyDimRestriction( coordsTransX, mins[0], direction.x, boundsX, 'min' );
+					if( mins[1] > 1 ) this.applyDimRestriction( coordsTransY, mins[1], direction.y, boundsY, 'min' );
+					
+					if( maxs[0] > 0 ) this.applyDimRestriction( coordsTransX, maxs[0], direction.x, boundsX, 'max' );
+					if( maxs[1] > 1 ) this.applyDimRestriction( coordsTransY, maxs[1], direction.y, boundsY, 'max' );
+					
+					coords = { x1: coordsTransX.a1, y1: coordsTransY.a1, x2: coordsTransX.a2, y2: coordsTransY.a2 };
+				}
+				
+			}
+		}
+		
+		// dump( 'setAreaCoords (out) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 + '\n' );
+		this.areaCoords = coords;
+	},
+	
+	/**
+	 * Applies the supplied dimension restriction to the supplied coordinates along a single axis
+	 * 
+	 * @access private
+	 * @param obj Single axis coordinates, a1, a2 (e.g. for the x axis a1 = x1 & a2 = x2)
+	 * @param int The restriction value
+	 * @param int The direction ( -1 = negative, 1 = positive )
+	 * @param obj The bounds of the image ( for this axis )
+	 * @param string The dimension restriction type ( 'min' | 'max' )
+	 * @return void
+	 */
+	applyDimRestriction: function( coords, val, direction, bounds, type ) {
+		var check;
+		if( type == 'min' ) check = ( ( coords.a2 - coords.a1 ) < val );
+		else check = ( ( coords.a2 - coords.a1 ) > val );
+		if( check ) {
+			if( direction == 1 ) coords.a2 = coords.a1 + val;
+			else coords.a1 = coords.a2 - val;
+			
+			// make sure we're still in the bounds (not too pretty for the user, but needed)
+			if( coords.a1 < bounds.min ) {
+				coords.a1 = bounds.min;
+				coords.a2 = val;
+			} else if( coords.a2 > bounds.max ) {
+				coords.a1 = bounds.max - val;
+				coords.a2 = bounds.max;
+			}
+		}
+	},
+		
+	/**
+	 * Applies the supplied ratio to the supplied coordinates
+	 * 
+	 * @access private
+	 * @param obj Coordinates, x1, y1, x2, y2
+	 * @param obj Ratio, x, y
+	 * @param obj Direction of mouse, x & y : -1 == negative 1 == positive
+	 * @param string The current resize handle || null
+	 * @return void
+	 */
+	applyRatio : function( coords, ratio, direction, resizeHandle ) {
+		// dump( 'direction.y : ' + direction.y + '\n');
+		var newCoords;
+		if( resizeHandle == 'N' || resizeHandle == 'S' ) {
+			// dump( 'north south \n');
+			// if moving on either the lone north & south handles apply the ratio on the y axis
+			newCoords = this.applyRatioToAxis( 
+				{ a1: coords.y1, b1: coords.x1, a2: coords.y2, b2: coords.x2 },
+				{ a: ratio.y, b: ratio.x },
+				{ a: direction.y, b: direction.x },
+				{ min: 0, max: this.imgW }
+			);
+			coords.x1 = newCoords.b1;
+			coords.y1 = newCoords.a1;
+			coords.x2 = newCoords.b2;
+			coords.y2 = newCoords.a2;
+		} else {
+			// otherwise deal with it as if we're applying the ratio on the x axis
+			newCoords = this.applyRatioToAxis( 
+				{ a1: coords.x1, b1: coords.y1, a2: coords.x2, b2: coords.y2 },
+				{ a: ratio.x, b: ratio.y },
+				{ a: direction.x, b: direction.y },
+				{ min: 0, max: this.imgH }
+			);
+			coords.x1 = newCoords.a1;
+			coords.y1 = newCoords.b1;
+			coords.x2 = newCoords.a2;
+			coords.y2 = newCoords.b2;
+		}
+		
+	},
+	
+	/**
+	 * Applies the provided ratio to the provided coordinates based on provided direction & bounds,
+	 * use to encapsulate functionality to make it easy to apply to either axis. This is probably
+	 * quite hard to visualise so see the x axis example within applyRatio()
+	 * 
+	 * Example in parameter details & comments is for requesting applying ratio to x axis.
+	 * 
+	 * @access private
+	 * @param obj Coords object (a1, b1, a2, b2) where a = x & b = y in example
+	 * @param obj Ratio object (a, b) where a = x & b = y in example
+	 * @param obj Direction object (a, b) where a = x & b = y in example
+	 * @param obj Bounds (min, max)
+	 * @return obj Coords object (a1, b1, a2, b2) where a = x & b = y in example
+	 */
+	applyRatioToAxis: function( coords, ratio, direction, bounds ) {
+		var newCoords = Object.extend( coords, {} );
+		var calcDimA = newCoords.a2 - newCoords.a1;			// calculate dimension a (e.g. width)
+		var targDimB = Math.floor( calcDimA * ratio.b / ratio.a );	// the target dimension b (e.g. height)
+		var targB;											// to hold target b (e.g. y value)
+		var targDimA;                                		// to hold target dimension a (e.g. width)
+		var calcDimB = null;								// to hold calculated dimension b (e.g. height)
+		
+		// dump( 'newCoords[0]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
+				
+		if( direction.b == 1 ) {							// if travelling in a positive direction
+			// make sure we're not going out of bounds
+			targB = newCoords.b1 + targDimB;
+			if( targB > bounds.max ) {
+				targB = bounds.max;
+				calcDimB = targB - newCoords.b1;			// calcuate dimension b (e.g. height)
+			}
+			
+			newCoords.b2 = targB;
+		} else {											// if travelling in a negative direction
+			// make sure we're not going out of bounds
+			targB = newCoords.b2 - targDimB;
+			if( targB < bounds.min ) {
+				targB = bounds.min;
+				calcDimB = targB + newCoords.b2;			// calcuate dimension b (e.g. height)
+			}
+			newCoords.b1 = targB;
+		}
+		
+		// dump( 'newCoords[1]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
+			
+		// apply the calculated dimensions
+		if( calcDimB != null ) {
+			targDimA = Math.floor( calcDimB * ratio.a / ratio.b );
+			
+			if( direction.a == 1 ) newCoords.a2 = newCoords.a1 + targDimA;
+			else newCoords.a1 = newCoords.a1 = newCoords.a2 - targDimA;
+		}
+		
+		// dump( 'newCoords[2]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
+			
+		return newCoords;
+	},
+	
+	/**
+	 * Draws the select area
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	drawArea: function( ) {	
+		/*
+		 * NOTE: I'm not using the Element.setStyle() shortcut as they make it 
+		 * quite sluggish on Mac based browsers
+		 */
+		// dump( 'drawArea        : ' + this.areaCoords.x1 + ',' + this.areaCoords.y1 + ',' + this.areaCoords.x2 + ',' + this.areaCoords.y2 + '\n' );
+		var areaWidth     = this.calcW();
+		var areaHeight    = this.calcH();
+		
+		/*
+		 * Calculate all the style strings before we use them, allows reuse & produces quicker
+		 * rendering (especially noticable in Mac based browsers)
+		 */
+		var px = 'px';
+		var params = [
+			this.areaCoords.x1 + px, 	// the left of the selArea
+			this.areaCoords.y1 + px,		// the top of the selArea
+			areaWidth + px,					// width of the selArea
+			areaHeight + px,					// height of the selArea
+			this.areaCoords.x2 + px,		// bottom of the selArea
+			this.areaCoords.y2 + px,		// right of the selArea
+			(this.img.width - this.areaCoords.x2) + px,	// right edge of selArea
+			(this.img.height - this.areaCoords.y2) + px	// bottom edge of selArea
+		];
+				
+		// do the select area
+		var areaStyle				= this.selArea.style;
+		areaStyle.left				= params[0];
+		areaStyle.top				= params[1];
+		areaStyle.width				= params[2];
+		areaStyle.height			= params[3];
+			  	
+		// position the north, east, south & west handles
+		var horizHandlePos = Math.ceil( (areaWidth - 6) / 2 ) + px;
+		var vertHandlePos = Math.ceil( (areaHeight - 6) / 2 ) + px;
+		
+		this.handleN.style.left 	= horizHandlePos;
+		this.handleE.style.top 		= vertHandlePos;
+		this.handleS.style.left 	= horizHandlePos;
+		this.handleW.style.top		= vertHandlePos;
+		
+		// draw the four overlays
+		this.north.style.height 	= params[1];
+		
+		var eastStyle 				= this.east.style;
+		eastStyle.top				= params[1];
+		eastStyle.height			= params[3];
+		eastStyle.left				= params[4];
+	    eastStyle.width				= params[6];
+	   
+	   	var southStyle 				= this.south.style;
+	   	southStyle.top				= params[5];
+	   	southStyle.height			= params[7];
+	   
+	    var westStyle       		= this.west.style;
+	    westStyle.top				= params[1];
+	    westStyle.height			= params[3];
+	   	westStyle.width				= params[0];
+	   	
+		// call the draw method on sub classes
+		this.subDrawArea();
+		
+		this.forceReRender();
+	},
+	
+	/**
+	 * Force the re-rendering of the selArea element which fixes rendering issues in Safari 
+	 * & IE PC, especially evident when re-sizing perfectly vertical using any of the south handles
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	forceReRender: function() {
+		if( this.isIE || this.isWebKit) {
+			var n = document.createTextNode(' ');
+			var d,el,fixEL,i;
+		
+			if( this.isIE ) fixEl = this.selArea;
+			else if( this.isWebKit ) {
+				fixEl = document.getElementsByClassName( 'imgCrop_marqueeSouth', this.imgWrap )[0];
+				/* we have to be a bit more forceful for Safari, otherwise the the marquee &
+				 * the south handles still don't move
+				 */ 
+				d = Builder.node( 'div', '' );
+				d.style.visibility = 'hidden';
+				
+				var classList = ['SE','S','SW'];
+				for( i = 0; i < classList.length; i++ ) {
+					el = document.getElementsByClassName( 'imgCrop_handle' + classList[i], this.selArea )[0];
+					if( el.childNodes.length ) el.removeChild( el.childNodes[0] );
+					el.appendChild(d);
+				}
+			}
+			fixEl.appendChild(n);
+			fixEl.removeChild(n);
+		}
+	},
+	
+	/**
+	 * Starts the resize
+	 * 
+	 * @access private
+	 * @param obj Event
+	 * @return void
+	 */
+	startResize: function( e ) {
+		this.startCoords = this.cloneCoords( this.areaCoords );
+		
+		this.resizing = true;
+		this.resizeHandle = Event.element( e ).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/, '');
+		// dump( 'this.resizeHandle : ' + this.resizeHandle + '\n' );
+		Event.stop( e );
+	},
+	
+	/**
+	 * Starts the drag
+	 * 
+	 * @access private
+	 * @param obj Event
+	 * @return void
+	 */
+	startDrag: function( e ) {	
+		this.selArea.show();
+		this.clickCoords = this.getCurPos( e );
+     	
+    	this.setAreaCoords( { x1: this.clickCoords.x, y1: this.clickCoords.y, x2: this.clickCoords.x, y2: this.clickCoords.y }, false, false, null );
+    	
+    	this.dragging = true;
+    	this.onDrag( e ); // incase the user just clicks once after already making a selection
+    	Event.stop( e );
+	},
+	
+	/**
+	 * Gets the current cursor position relative to the image
+	 * 
+	 * @access private
+	 * @param obj Event
+	 * @return obj x,y pixels of the cursor
+	 */
+	getCurPos: function( e ) {
+		// get the offsets for the wrapper within the document
+		var el = this.imgWrap, wrapOffsets = Position.cumulativeOffset( el );
+		// remove any scrolling that is applied to the wrapper (this may be buggy) - don't count the scroll on the body as that won't affect us
+		while( el.nodeName != 'BODY' ) {
+			wrapOffsets[1] -= el.scrollTop  || 0;
+			wrapOffsets[0] -= el.scrollLeft || 0;
+			el = el.parentNode;
+	    }		
+		return curPos = { 
+			x: Event.pointerX(e) - wrapOffsets[0],
+			y: Event.pointerY(e) - wrapOffsets[1]
+		}
+	},
+  	
+  	/**
+  	 * Performs the drag for both resize & inital draw dragging
+  	 * 
+  	 * @access private
+	 * @param obj Event
+	 * @return void
+	 */
+  	onDrag: function( e ) {
+  		if( this.dragging || this.resizing ) {	
+  		
+  			var resizeHandle = null;
+  			var curPos = this.getCurPos( e );			
+			var newCoords = this.cloneCoords( this.areaCoords );
+  			var direction = { x: 1, y: 1 };
+  	  					
+		    if( this.dragging ) {
+		    	if( curPos.x < this.clickCoords.x ) direction.x = -1;
+		    	if( curPos.y < this.clickCoords.y ) direction.y = -1;
+		    	
+				this.transformCoords( curPos.x, this.clickCoords.x, newCoords, 'x' );
+				this.transformCoords( curPos.y, this.clickCoords.y, newCoords, 'y' );
+			} else if( this.resizing ) {
+				resizeHandle = this.resizeHandle;			
+				// do x movements first
+				if( resizeHandle.match(/E/) ) {
+					// if we're moving an east handle
+					this.transformCoords( curPos.x, this.startCoords.x1, newCoords, 'x' );	
+					if( curPos.x < this.startCoords.x1 ) direction.x = -1;
+				} else if( resizeHandle.match(/W/) ) {
+					// if we're moving an west handle
+					this.transformCoords( curPos.x, this.startCoords.x2, newCoords, 'x' );
+					if( curPos.x < this.startCoords.x2 ) direction.x = -1;
+				}
+									
+				// do y movements second
+				if( resizeHandle.match(/N/) ) {
+					// if we're moving an north handle	
+					this.transformCoords( curPos.y, this.startCoords.y2, newCoords, 'y' );
+					if( curPos.y < this.startCoords.y2 ) direction.y = -1;
+				} else if( resizeHandle.match(/S/) ) {
+					// if we're moving an south handle
+					this.transformCoords( curPos.y, this.startCoords.y1, newCoords, 'y' );	
+					if( curPos.y < this.startCoords.y1 ) direction.y = -1;
+				}	
+							
+			}
+		
+			this.setAreaCoords( newCoords, false, e.shiftKey, direction, resizeHandle );
+			this.drawArea();
+			Event.stop( e ); // stop the default event (selecting images & text) in Safari & IE PC
+		}
+	},
+	
+	/**
+	 * Applies the appropriate transform to supplied co-ordinates, on the
+	 * defined axis, depending on the relationship of the supplied values
+	 * 
+	 * @access private
+	 * @param int Current value of pointer
+	 * @param int Base value to compare current pointer val to
+	 * @param obj Coordinates to apply transformation on x1, x2, y1, y2
+	 * @param string Axis to apply transformation on 'x' || 'y'
+	 * @return void
+	 */
+	transformCoords : function( curVal, baseVal, coords, axis ) {
+		var newVals = [ curVal, baseVal ];
+		if( curVal > baseVal ) newVals.reverse();
+		coords[ axis + '1' ] = newVals[0];
+		coords[ axis + '2' ] = newVals[1];		
+	},
+	
+	/**
+	 * Ends the crop & passes the values of the select area on to the appropriate 
+	 * callback function on completion of a crop
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	endCrop : function() {
+		this.dragging = false;
+		this.resizing = false;
+		
+		this.options.onEndCrop(
+			this.areaCoords,
+			{
+				width: this.calcW(), 
+				height: this.calcH() 
+			}
+		);
+	},
+	
+	/**
+	 * Abstract method called on the end of initialization
+	 * 
+	 * @access private
+	 * @abstract
+	 * @return void
+	 */
+	subInitialize: function() {},
+	
+	/**
+	 * Abstract method called on the end of drawArea()
+	 * 
+	 * @access private
+	 * @abstract
+	 * @return void
+	 */
+	subDrawArea: function() {}
+};
+
+
+
+
+/**
+ * Extend the Cropper.Img class to allow for presentation of a preview image of the resulting crop,
+ * the option for displayOnInit is always overridden to true when displaying a preview image
+ * 
+ * Usage:
+ * 	@param obj Image element to attach to
+ * 	@param obj Optional options:
+ * 		- see Cropper.Img for base options
+ * 		
+ * 		- previewWrap obj
+ * 			HTML element that will be used as a container for the preview image		
+ */
+Cropper.ImgWithPreview = Class.create();
+
+Object.extend( Object.extend( Cropper.ImgWithPreview.prototype, Cropper.Img.prototype ), {
+	
+	/**
+	 * Implements the abstract method from Cropper.Img to initialize preview image settings.
+	 * Will only attach a preview image is the previewWrap element is defined and the minWidth
+	 * & minHeight options are set.
+	 * 
+	 * @see Croper.Img.subInitialize
+	 */
+	subInitialize: function() {
+		/**
+		 * Whether or not we've attached a preview image
+		 * @var boolean
+		 */
+		this.hasPreviewImg = false;
+		if( typeof(this.options.previewWrap) != 'undefined' 
+			&& this.options.minWidth > 0 
+			&& this.options.minHeight > 0
+		) {
+			/**
+			 * The preview image wrapper element
+			 * @var obj HTML element
+			 */
+			this.previewWrap 	= $( this.options.previewWrap );
+			/**
+			 * The preview image element
+			 * @var obj HTML IMG element
+			 */
+			this.previewImg 	= this.img.cloneNode( false );
+			// set the ID of the preview image to be unique
+			this.previewImg.id	= 'imgCrop_' + this.previewImg.id;
+			
+						
+			// set the displayOnInit option to true so we display the select area at the same time as the thumbnail
+			this.options.displayOnInit = true;
+
+			this.hasPreviewImg 	= true;
+			
+			this.previewWrap.addClassName( 'imgCrop_previewWrap' );
+			
+			this.previewWrap.setStyle(
+			 { 
+			 	width: this.options.minWidth + 'px',
+			 	height: this.options.minHeight + 'px'
+			 }
+			);
+			
+			this.previewWrap.appendChild( this.previewImg );
+		}
+	},
+	
+	/**
+	 * Implements the abstract method from Cropper.Img to draw the preview image
+	 * 
+	 * @see Croper.Img.subDrawArea
+	 */
+	subDrawArea: function() {
+		if( this.hasPreviewImg ) {
+			// get the ratio of the select area to the src image
+			var calcWidth = this.calcW();
+			var calcHeight = this.calcH();
+			// ratios for the dimensions of the preview image
+			var dimRatio = { 
+				x: this.imgW / calcWidth, 
+				y: this.imgH / calcHeight 
+			}; 
+			//ratios for the positions within the preview
+			var posRatio = { 
+				x: calcWidth / this.options.minWidth, 
+				y: calcHeight / this.options.minHeight 
+			};
+			
+			// setting the positions in an obj before apply styles for rendering speed increase
+			var calcPos	= {
+				w: Math.ceil( this.options.minWidth * dimRatio.x ) + 'px',
+				h: Math.ceil( this.options.minHeight * dimRatio.y ) + 'px',
+				x: '-' + Math.ceil( this.areaCoords.x1 / posRatio.x )  + 'px',
+				y: '-' + Math.ceil( this.areaCoords.y1 / posRatio.y ) + 'px'
+			}
+			
+			var previewStyle 	= this.previewImg.style;
+			previewStyle.width 	= calcPos.w;
+			previewStyle.height	= calcPos.h;
+			previewStyle.left	= calcPos.x;
+			previewStyle.top	= calcPos.y;
+		}
+	}
+	
+});
Index: /afridex/plugins/fresh-page/RCCWP_Constant.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_Constant.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_Constant.php (revision 21)
@@ -0,0 +1,26 @@
+<?php
+global $wpdb;
+
+define('RC_CWP_DEBUG_MODE', TRUE);
+define('RC_CWP_PLUGIN_DIR', dirname(plugin_basename(__FILE__)));
+define('RC_CWP_POST_WRITE_PANEL_ID_META_KEY', '_rc_cwp_write_panel_id');
+define('RC_CWP_OPTION_KEY', 'rc_custom_write_panel');
+
+define('RC_CWP_TABLE_WRITE_PANELS', $wpdb->prefix  . 'rc_cwp_write_panels');
+define('RC_CWP_TABLE_CUSTOM_FIELD_TYPES', $wpdb->prefix  . 'rc_cwp_custom_field_types');
+define('RC_CWP_TABLE_PANEL_CUSTOM_FIELD', $wpdb->prefix  . 'rc_cwp_panel_custom_field');
+define('RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS', $wpdb->prefix  . 'rc_cwp_custom_field_options');
+define('RC_CWP_TABLE_PANEL_CATEGORY', $wpdb->prefix  . 'rc_cwp_panel_category');
+define('RC_CWP_TABLE_STANDARD_FIELDS', $wpdb->prefix  . 'rc_cwp_standard_fields');
+define('RC_CWP_TABLE_PANEL_STANDARD_FIELD', $wpdb->prefix  . 'rc_cwp_panel_standard_field');
+define('RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES', $wpdb->prefix  . 'rc_cwp_custom_field_properties');
+define('RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD', $wpdb->prefix  . 'rc_cwp_panel_hidden_external_field');
+
+if (!defined('DIRECTORY_SEPARATOR'))
+{
+	if (strpos(php_uname('s'), 'Win') !== false )
+		define('DIRECTORY_SEPARATOR', '\\');
+	else 
+		define('DIRECTORY_SEPARATOR', '/');
+}
+?>
Index: /afridex/plugins/fresh-page/phpThumb.php
===================================================================
--- /afridex/plugins/fresh-page/phpThumb.php (revision 21)
+++ /afridex/plugins/fresh-page/phpThumb.php (revision 21)
@@ -0,0 +1,592 @@
+<?php
+
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// See: phpthumb.changelog.txt for recent changes           //
+// See: phpthumb.readme.txt for usage instructions          //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+error_reporting(E_ALL);
+ini_set('display_errors', '1');
+if (!@ini_get('safe_mode')) {
+	set_time_limit(60);  // shouldn't take nearly this long in most cases, but with many filter and/or a slow server...
+}
+ini_set('magic_quotes_runtime', '0');
+if (@ini_get('magic_quotes_runtime')) {
+	die('"magic_quotes_runtime" is set in php.ini, cannot run phpThumb with this enabled');
+}
+$starttime = array_sum(explode(' ', microtime()));
+
+// this script relies on the superglobal arrays, fake it here for old PHP versions
+if (phpversion() < '4.1.0') {
+	$_SERVER = $HTTP_SERVER_VARS;
+	$_GET    = $HTTP_GET_VARS;
+}
+
+// instantiate a new phpThumb() object
+ob_start();
+if (!include_once(dirname(__FILE__).'/phpthumb.class.php')) {
+	ob_end_flush();
+	die('failed to include_once("'.realpath(dirname(__FILE__).'/phpthumb.class.php').'")');
+}
+ob_end_clean();
+$phpThumb = new phpThumb();
+$phpThumb->DebugTimingMessage('phpThumb.php start', __FILE__, __LINE__, $starttime);
+$phpThumb->SetParameter('config_error_die_on_error', true);
+
+// phpThumbDebug[0] used to be here, but may reveal too much
+// info when high_security_mode should be enabled (not set yet)
+
+if (file_exists(dirname(__FILE__).'/phpThumb.config.php')) {
+	ob_start();
+	if (include_once(dirname(__FILE__).'/phpThumb.config.php')) {
+		// great
+	} else {
+		ob_end_flush();
+		$phpThumb->ErrorImage('failed to include_once('.dirname(__FILE__).'/phpThumb.config.php) - realpath="'.realpath(dirname(__FILE__).'/phpThumb.config.php').'"');
+	}
+	ob_end_clean();
+} elseif (file_exists(dirname(__FILE__).'/phpThumb.config.php.default')) {
+	$phpThumb->ErrorImage('Please rename "phpThumb.config.php.default" to "phpThumb.config.php"');
+} else {
+	$phpThumb->ErrorImage('failed to include_once('.dirname(__FILE__).'/phpThumb.config.php) - realpath="'.realpath(dirname(__FILE__).'/phpThumb.config.php').'"');
+}
+
+if (!@$PHPTHUMB_CONFIG['disable_pathinfo_parsing'] && (empty($_GET) || isset($_GET['phpThumbDebug'])) && !empty($_SERVER['PATH_INFO'])) {
+	$_SERVER['PHP_SELF'] = str_replace($_SERVER['PATH_INFO'], '', @$_SERVER['PHP_SELF']);
+
+	$args = explode(';', substr($_SERVER['PATH_INFO'], 1));
+	$phpThumb->DebugMessage('PATH_INFO.$args set to ('.implode(')(', $args).')', __FILE__, __LINE__);
+	if (!empty($args)) {
+		$_GET['src'] = @$args[count($args) - 1];
+		if (eregi('^new\=([a-z0-9]+)', $_GET['src'], $matches)) {
+			unset($_GET['src']);
+			$_GET['new'] = $matches[1];
+		}
+	}
+	if (eregi('^([0-9]*)x?([0-9]*)$', @$args[count($args) - 2], $matches)) {
+		$_GET['w'] = $matches[1];
+		$_GET['h'] = $matches[2];
+		$phpThumb->DebugMessage('PATH_INFO."w"x"h" set to "'.$_GET['w'].'"x"'.$_GET['h'].'"', __FILE__, __LINE__);
+	}
+	for ($i = 0; $i < count($args) - 2; $i++) {
+		@list($key, $value) = explode('=', @$args[$i]);
+		if (substr($key, -2) == '[]') {
+			$array_key_name = substr($key, 0, -2);
+			$_GET[$array_key_name][] = $value;
+			$phpThumb->DebugMessage('PATH_INFO."'.$array_key_name.'[]" = "'.$value.'"', __FILE__, __LINE__);
+		} else {
+			$_GET[$key] = $value;
+			$phpThumb->DebugMessage('PATH_INFO."'.$key.'" = "'.$value.'"', __FILE__, __LINE__);
+		}
+	}
+}
+
+if (@$PHPTHUMB_CONFIG['high_security_enabled']) {
+	if (!@$_GET['hash']) {
+		$phpThumb->ErrorImage('ERROR: missing hash');
+	} elseif (strlen($PHPTHUMB_CONFIG['high_security_password']) < 5) {
+		$phpThumb->ErrorImage('ERROR: strlen($PHPTHUMB_CONFIG[high_security_password]) < 5');
+	} elseif ($_GET['hash'] != md5(str_replace('&hash='.$_GET['hash'], '', $_SERVER['QUERY_STRING']).$PHPTHUMB_CONFIG['high_security_password'])) {
+		$phpThumb->ErrorImage('ERROR: invalid hash');
+	}
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[0]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '0') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+// returned the fixed string if the evil "magic_quotes_gpc" setting is on
+if (get_magic_quotes_gpc()) {
+	$RequestVarsToStripSlashes = array('src', 'wmf', 'file', 'err', 'goto', 'down');
+	foreach ($RequestVarsToStripSlashes as $key) {
+		if (isset($_GET[$key])) {
+			$_GET[$key] = stripslashes($_GET[$key]);
+		}
+	}
+}
+
+if (!@$_SERVER['PATH_INFO'] && !@$_SERVER['QUERY_STRING']) {
+	$phpThumb->ErrorImage('phpThumb() v'.$phpThumb->phpthumb_version.'<br><a href="http://phpthumb.sourceforge.net">http://phpthumb.sourceforge.net</a><br><br>ERROR: no parameters specified');
+}
+
+if (@$_GET['src'] && isset($_GET['md5s']) && empty($_GET['md5s'])) {
+	if (eregi('^(f|ht)tps?://', $_GET['src'])) {
+		if ($rawImageData = phpthumb_functions::SafeURLread($_GET['src'], $error, $phpThumb->config_http_fopen_timeout, $phpThumb->config_http_follow_redirect)) {
+			$md5s = md5($rawImageData);
+		}
+	} else {
+		$SourceFilename = $phpThumb->ResolveFilenameToAbsolute($_GET['src']);
+		if (is_readable($SourceFilename)) {
+			$md5s = phpthumb_functions::md5_file_safe($SourceFilename);
+		} else {
+			$phpThumb->ErrorImage('ERROR: "'.$SourceFilename.'" cannot be read');
+		}
+	}
+	if (@$_SERVER['HTTP_REFERER']) {
+		$phpThumb->ErrorImage('&md5s='.$md5s);
+	} else {
+		die('&md5s='.$md5s);
+	}
+}
+
+if (!empty($PHPTHUMB_CONFIG)) {
+	foreach ($PHPTHUMB_CONFIG as $key => $value) {
+		$keyname = 'config_'.$key;
+		$phpThumb->setParameter($keyname, $value);
+		if (!eregi('password', $key)) {
+			$phpThumb->DebugMessage('setParameter('.$keyname.', '.$phpThumb->phpThumbDebugVarDump($value).')', __FILE__, __LINE__);
+		}
+	}
+} else {
+	$phpThumb->DebugMessage('$PHPTHUMB_CONFIG is empty', __FILE__, __LINE__);
+}
+
+if (@$_GET['src'] && !@$PHPTHUMB_CONFIG['allow_local_http_src'] && eregi('^http://'.@$_SERVER['HTTP_HOST'].'(.+)', @$_GET['src'], $matches)) {
+	$phpThumb->ErrorImage('It is MUCH better to specify the "src" parameter as "'.$matches[1].'" instead of "'.$matches[0].'".'."\n\n".'If you really must do it this way, enable "allow_local_http_src" in phpThumb.config.php');
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[1]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '1') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+$parsed_url_referer = parse_url(@$_SERVER['HTTP_REFERER']);
+if ($phpThumb->config_nooffsitelink_require_refer && !in_array(@$parsed_url_referer['host'], $phpThumb->config_nohotlink_valid_domains)) {
+	$phpThumb->ErrorImage('config_nooffsitelink_require_refer enabled and '.(@$parsed_url_referer['host'] ? '"'.$parsed_url_referer['host'].'" is not an allowed referer' : 'no HTTP_REFERER exists'));
+}
+$parsed_url_src = parse_url(@$_GET['src']);
+if ($phpThumb->config_nohotlink_enabled && $phpThumb->config_nohotlink_erase_image && eregi('^(f|ht)tps?://', @$_GET['src']) && !in_array(@$parsed_url_src['host'], $phpThumb->config_nohotlink_valid_domains)) {
+	$phpThumb->ErrorImage($phpThumb->config_nohotlink_text_message);
+}
+
+if ($phpThumb->config_mysql_query) {
+	if ($cid = @mysql_connect($phpThumb->config_mysql_hostname, $phpThumb->config_mysql_username, $phpThumb->config_mysql_password)) {
+		if (@mysql_select_db($phpThumb->config_mysql_database, $cid)) {
+			if ($result = @mysql_query($phpThumb->config_mysql_query, $cid)) {
+				if ($row = @mysql_fetch_array($result)) {
+
+					mysql_free_result($result);
+					mysql_close($cid);
+					$phpThumb->setSourceData($row[0]);
+					unset($row);
+
+				} else {
+					mysql_free_result($result);
+					mysql_close($cid);
+					$phpThumb->ErrorImage('no matching data in database.');
+				}
+			} else {
+				mysql_close($cid);
+				$phpThumb->ErrorImage('Error in MySQL query: "'.mysql_error($cid).'"');
+			}
+		} else {
+			mysql_close($cid);
+			$phpThumb->ErrorImage('cannot select MySQL database: "'.mysql_error($cid).'"');
+		}
+	} else {
+		$phpThumb->ErrorImage('cannot connect to MySQL server');
+	}
+	unset($_GET['id']);
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[2]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '2') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+if (@$PHPTHUMB_CONFIG['cache_default_only_suffix'] && (strpos($PHPTHUMB_CONFIG['cache_default_only_suffix'], '*') !== false)) {
+	$PHPTHUMB_DEFAULTS_DISABLEGETPARAMS = true;
+}
+// deprecated: 'err', 'file', 'goto',
+$allowedGETparameters = array('src', 'new', 'w', 'h', 'wp', 'hp', 'wl', 'hl', 'ws', 'hs', 'f', 'q', 'sx', 'sy', 'sw', 'sh', 'zc', 'bc', 'bg', 'bgt', 'fltr', 'xto', 'ra', 'ar', 'aoe', 'far', 'iar', 'maxb', 'down', 'phpThumbDebug', 'hash', 'md5s', 'sfn', 'dpi', 'sia');
+if (!empty($PHPTHUMB_DEFAULTS) && is_array($PHPTHUMB_DEFAULTS)) {
+	$phpThumb->DebugMessage('setting $PHPTHUMB_DEFAULTS['.implode(';', array_keys($PHPTHUMB_DEFAULTS)).']', __FILE__, __LINE__);
+	foreach ($PHPTHUMB_DEFAULTS as $key => $value) {
+		if ($PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE || !isset($_GET[$key])) {
+			$_GET[$key] = $value;
+			$phpThumb->DebugMessage('PHPTHUMB_DEFAULTS assigning ('.$value.') to $_GET['.$key.']', __FILE__, __LINE__);
+			//$phpThumb->DebugMessage('PHPTHUMB_DEFAULTS.setParameter('.$key.', '.$phpThumb->phpThumbDebugVarDump($value).')', __FILE__, __LINE__);
+			//$phpThumb->setParameter($key, $value);
+		}
+	}
+}
+foreach ($_GET as $key => $value) {
+	if (@$PHPTHUMB_DEFAULTS_DISABLEGETPARAMS && ($key != 'src')) {
+		// disabled, do not set parameter
+		$phpThumb->DebugMessage('ignoring $_GET['.$key.'] because of $PHPTHUMB_DEFAULTS_DISABLEGETPARAMS', __FILE__, __LINE__);
+	} elseif (in_array($key, $allowedGETparameters)) {
+		$phpThumb->DebugMessage('setParameter('.$key.', '.$phpThumb->phpThumbDebugVarDump($value).')', __FILE__, __LINE__);
+		$phpThumb->setParameter($key, $value);
+	} else {
+		$phpThumb->ErrorImage('Forbidden parameter: '.$key);
+	}
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[3]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '3') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+//if (!@$_GET['phpThumbDebug'] && !$phpThumb->sourceFilename && !function_exists('ImageJPEG') && !function_exists('ImagePNG') && !function_exists('ImageGIF')) {
+if (!@$_GET['phpThumbDebug'] && !is_file($phpThumb->sourceFilename) && !phpthumb_functions::gd_version()) {
+	if (!headers_sent()) {
+		// base64-encoded error image in GIF format
+		$ERROR_NOGD = 'R0lGODlhIAAgALMAAAAAABQUFCQkJDY2NkZGRldXV2ZmZnJycoaGhpSUlKWlpbe3t8XFxdXV1eTk5P7+/iwAAAAAIAAgAAAE/vDJSau9WILtTAACUinDNijZtAHfCojS4W5H+qxD8xibIDE9h0OwWaRWDIljJSkUJYsN4bihMB8th3IToAKs1VtYM75cyV8sZ8vygtOE5yMKmGbO4jRdICQCjHdlZzwzNW4qZSQmKDaNjhUMBX4BBAlmMywFSRWEmAI6b5gAlhNxokGhooAIK5o/pi9vEw4Lfj4OLTAUpj6IabMtCwlSFw0DCKBoFqwAB04AjI54PyZ+yY3TD0ss2YcVmN/gvpcu4TOyFivWqYJlbAHPpOntvxNAACcmGHjZzAZqzSzcq5fNjxFmAFw9iFRunD1epU6tsIPmFCAJnWYE0FURk7wJDA0MTKpEzoWAAskiAAA7';
+		header('Content-Type: image/gif');
+		echo base64_decode($ERROR_NOGD);
+	} else {
+		echo '*** ERROR: No PHP-GD support available ***';
+	}
+	exit;
+}
+
+// check to see if file can be output from source with no processing or caching
+$CanPassThroughDirectly = true;
+if ($phpThumb->rawImageData) {
+	// data from SQL, should be fine
+} elseif (eregi('^(f|ht)tp\://', $phpThumb->src)) {
+	$phpThumb->DebugMessage('$CanPassThroughDirectly=false because eregi("^(f|ht)tp\://", '.$phpThumb->src.')', __FILE__, __LINE__);
+	$CanPassThroughDirectly = false;
+} elseif (!@is_file($phpThumb->sourceFilename)) {
+	$phpThumb->DebugMessage('$CanPassThroughDirectly=false because !@is_file('.$phpThumb->sourceFilename.')', __FILE__, __LINE__);
+	$CanPassThroughDirectly = false;
+} elseif (!@is_readable($phpThumb->sourceFilename)) {
+	$phpThumb->DebugMessage('$CanPassThroughDirectly=false because !@is_readable('.$phpThumb->sourceFilename.')', __FILE__, __LINE__);
+	$CanPassThroughDirectly = false;
+}
+foreach ($_GET as $key => $value) {
+	switch ($key) {
+		case 'src':
+			// allowed
+			break;
+
+		case 'w':
+		case 'h':
+			// might be OK if exactly matches original
+			break;
+
+		case 'phpThumbDebug':
+			// handled in direct-passthru code
+			break;
+
+		default:
+			// all other parameters will cause some processing,
+			// therefore cannot pass through original image unmodified
+			$CanPassThroughDirectly = false;
+			$UnAllowedGET[] = $key;
+			break;
+	}
+}
+if (!empty($UnAllowedGET)) {
+	$phpThumb->DebugMessage('$CanPassThroughDirectly=false because $_GET['.implode(';', array_unique($UnAllowedGET)).'] are set', __FILE__, __LINE__);
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[4]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '4') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+function SendSaveAsFileHeaderIfNeeded() {
+	if (headers_sent()) {
+		return false;
+	}
+	global $phpThumb;
+	$downloadfilename = phpthumb_functions::SanitizeFilename(@$_GET['sia'] ? $_GET['sia'] : (@$_GET['down'] ? $_GET['down'] : 'phpThumb_generated_thumbnail'.(@$_GET['f'] ? $_GET['f'] : 'jpg')));
+	if (@$downloadfilename) {
+		$phpThumb->DebugMessage('SendSaveAsFileHeaderIfNeeded() sending header: Content-Disposition: '.(@$_GET['down'] ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"', __FILE__, __LINE__);
+		header('Content-Disposition: '.(@$_GET['down'] ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"');
+	}
+	return true;
+}
+
+$phpThumb->DebugMessage('$CanPassThroughDirectly="'.intval($CanPassThroughDirectly).'" && $phpThumb->src="'.$phpThumb->src.'"', __FILE__, __LINE__);
+while ($CanPassThroughDirectly && $phpThumb->src) {
+	// no parameters set, passthru
+	$SourceFilename = $phpThumb->ResolveFilenameToAbsolute($phpThumb->src);
+
+	// security and size checks
+	if ($phpThumb->getimagesizeinfo = @GetImageSize($SourceFilename)) {
+		$phpThumb->DebugMessage('Direct passthru GetImageSize() returned [w='.$phpThumb->getimagesizeinfo[0].';h='.$phpThumb->getimagesizeinfo[1].';t='.$phpThumb->getimagesizeinfo[2].']', __FILE__, __LINE__);
+
+		if (!@$_GET['w'] && !@$_GET['wp'] && !@$_GET['wl'] && !@$_GET['ws'] && !@$_GET['h'] && !@$_GET['hp'] && !@$_GET['hl'] && !@$_GET['hs']) {
+			// no resizing needed
+			$phpThumb->DebugMessage('Passing "'.$SourceFilename.'" through directly, no resizing required ("'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'")', __FILE__, __LINE__);
+		} elseif (($phpThumb->getimagesizeinfo[0] <= @$_GET['w']) && ($phpThumb->getimagesizeinfo[1] <= @$_GET['h']) && ((@$_GET['w'] == $phpThumb->getimagesizeinfo[0]) || (@$_GET['h'] == $phpThumb->getimagesizeinfo[1]))) {
+			// image fits into 'w'x'h' box, and at least one dimension matches exactly, therefore no resizing needed
+			$phpThumb->DebugMessage('Passing "'.$SourceFilename.'" through directly, no resizing required ("'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'" fits inside "'.@$_GET['w'].'"x"'.@$_GET['h'].'")', __FILE__, __LINE__);
+		} else {
+			$phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because resizing required (from "'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'" to "'.@$_GET['w'].'"x"'.@$_GET['h'].'")', __FILE__, __LINE__);
+			break;
+		}
+		switch ($phpThumb->getimagesizeinfo[2]) {
+			case 1: // GIF
+			case 2: // JPG
+			case 3: // PNG
+				// great, let it through
+				break;
+			default:
+				// browser probably can't handle format, remangle it to JPEG/PNG/GIF
+				$phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because $phpThumb->getimagesizeinfo[2] = "'.$phpThumb->getimagesizeinfo[2].'"', __FILE__, __LINE__);
+				break 2;
+		}
+
+		$ImageCreateFunctions = array(1=>'ImageCreateFromGIF', 2=>'ImageCreateFromJPEG', 3=>'ImageCreateFromPNG');
+		$theImageCreateFunction = @$ImageCreateFunctions[$phpThumb->getimagesizeinfo[2]];
+		if ($phpThumb->config_disable_onlycreateable_passthru || (function_exists($theImageCreateFunction) && ($dummyImage = @$theImageCreateFunction($SourceFilename)))) {
+
+			// great
+			if (@is_resource($dummyImage)) {
+				unset($dummyImage);
+			}
+
+			if (headers_sent()) {
+				$phpThumb->ErrorImage('Headers already sent ('.basename(__FILE__).' line '.__LINE__.')');
+				exit;
+			}
+			if (@$_GET['phpThumbDebug']) {
+				$phpThumb->DebugTimingMessage('skipped direct $SourceFilename passthru', __FILE__, __LINE__);
+				$phpThumb->DebugMessage('Would have passed "'.$SourceFilename.'" through directly, but skipping due to phpThumbDebug', __FILE__, __LINE__);
+				break;
+			}
+
+			SendSaveAsFileHeaderIfNeeded();
+			header('Last-Modified: '.gmdate('D, d M Y H:i:s', @filemtime($SourceFilename)).' GMT');
+			if ($contentType = phpthumb_functions::ImageTypeToMIMEtype(@$phpThumb->getimagesizeinfo[2])) {
+				header('Content-Type: '.$contentType);
+			}
+			@readfile($SourceFilename);
+			exit;
+
+		} else {
+			$phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because ($phpThumb->config_disable_onlycreateable_passthru = "'.$phpThumb->config_disable_onlycreateable_passthru.'") and '.$theImageCreateFunction.'() failed', __FILE__, __LINE__);
+			break;
+		}
+
+	} else {
+		$phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because GetImageSize() failed', __FILE__, __LINE__);
+		break;
+	}
+	break;
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[5]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '5') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+function RedirectToCachedFile() {
+	global $phpThumb, $PHPTHUMB_CONFIG;
+
+	$nice_cachefile = str_replace(DIRECTORY_SEPARATOR, '/', $phpThumb->cache_filename);
+	$nice_docroot   = str_replace(DIRECTORY_SEPARATOR, '/', rtrim($PHPTHUMB_CONFIG['document_root'], '/\\'));
+
+	$parsed_url = @parse_url(@$_SERVER['HTTP_REFERER']);
+
+	$nModified  = filemtime($phpThumb->cache_filename);
+
+	if ($phpThumb->config_nooffsitelink_enabled && @$_SERVER['HTTP_REFERER'] && !in_array(@$parsed_url['host'], $phpThumb->config_nooffsitelink_valid_domains)) {
+
+		$phpThumb->DebugMessage('Would have used cached (image/'.$phpThumb->thumbnailFormat.') file "'.$phpThumb->cache_filename.'" (Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT), but skipping because $_SERVER[HTTP_REFERER] ('.@$_SERVER['HTTP_REFERER'].') is not in $phpThumb->config_nooffsitelink_valid_domains ('.implode(';', $phpThumb->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__);
+
+	} elseif ($phpThumb->phpThumbDebug) {
+
+		$phpThumb->DebugTimingMessage('skipped using cached image', __FILE__, __LINE__);
+		$phpThumb->DebugMessage('Would have used cached file, but skipping due to phpThumbDebug', __FILE__, __LINE__);
+		$phpThumb->DebugMessage('* Would have sent headers (1): Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT', __FILE__, __LINE__);
+		if ($getimagesize = @GetImageSize($phpThumb->cache_filename)) {
+			$phpThumb->DebugMessage('* Would have sent headers (2): Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($getimagesize[2]), __FILE__, __LINE__);
+		}
+		if (ereg('^'.preg_quote($nice_docroot).'(.*)$', $nice_cachefile, $matches)) {
+			$phpThumb->DebugMessage('* Would have sent headers (3): Location: '.dirname($matches[1]).'/'.urlencode(basename($matches[1])), __FILE__, __LINE__);
+		} else {
+			$phpThumb->DebugMessage('* Would have sent data: readfile('.$phpThumb->cache_filename.')', __FILE__, __LINE__);
+		}
+
+	} else {
+
+		if (headers_sent()) {
+			$phpThumb->ErrorImage('Headers already sent ('.basename(__FILE__).' line '.__LINE__.')');
+			exit;
+		}
+		SendSaveAsFileHeaderIfNeeded();
+
+		header('Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT');
+		if (@$_SERVER['HTTP_IF_MODIFIED_SINCE'] && ($nModified == strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) && @$_SERVER['SERVER_PROTOCOL']) {
+			header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
+			exit;
+		}
+
+		if ($getimagesize = @GetImageSize($phpThumb->cache_filename)) {
+			header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($getimagesize[2]));
+		} elseif (eregi('\.ico$', $phpThumb->cache_filename)) {
+			header('Content-Type: image/x-icon');
+		}
+		if (!@$PHPTHUMB_CONFIG['cache_force_passthru'] && ereg('^'.preg_quote($nice_docroot).'(.*)$', $nice_cachefile, $matches)) {
+			header('Location: '.dirname($matches[1]).'/'.urlencode(basename($matches[1])));
+		} else {
+			@readfile($phpThumb->cache_filename);
+		}
+		exit;
+
+	}
+	return true;
+}
+
+// check to see if file already exists in cache, and output it with no processing if it does
+$phpThumb->SetCacheFilename();
+if (@is_file($phpThumb->cache_filename)) {
+	RedirectToCachedFile();
+} else {
+	$phpThumb->DebugMessage('Cached file "'.$phpThumb->cache_filename.'" does not exist, processing as normal', __FILE__, __LINE__);
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[6]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '6') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+if ($phpThumb->rawImageData) {
+
+	// great
+
+} elseif (@$_GET['new']) {
+
+	// generate a blank image resource of the specified size/background color/opacity
+	if (($phpThumb->w <= 0) || ($phpThumb->h <= 0)) {
+		$phpThumb->ErrorImage('"w" and "h" parameters required for "new"');
+	}
+	@list($bghexcolor, $opacity) = explode('|', $_GET['new']);
+	if (!phpthumb_functions::IsHexColor($bghexcolor)) {
+		$phpThumb->ErrorImage('BGcolor parameter for "new" is not valid');
+	}
+	$opacity = (strlen($opacity) ? $opacity : 100);
+	if ($phpThumb->gdimg_source = phpthumb_functions::ImageCreateFunction($phpThumb->w, $phpThumb->h)) {
+		$alpha = (100 - min(100, max(0, $opacity))) * 1.27;
+		if ($alpha) {
+			$phpThumb->setParameter('is_alpha', true);
+			ImageAlphaBlending($phpThumb->gdimg_source, false);
+			ImageSaveAlpha($phpThumb->gdimg_source, true);
+		}
+		$new_background_color = phpthumb_functions::ImageHexColorAllocate($phpThumb->gdimg_source, $bghexcolor, false, $alpha);
+		ImageFilledRectangle($phpThumb->gdimg_source, 0, 0, $phpThumb->w, $phpThumb->h, $new_background_color);
+	} else {
+		$phpThumb->ErrorImage('failed to create "new" image ('.$phpThumb->w.'x'.$phpThumb->h.')');
+	}
+
+} elseif (!$phpThumb->src) {
+
+	$phpThumb->ErrorImage('Usage: '.$_SERVER['PHP_SELF'].'?src=/path/and/filename.jpg'."\n".'read Usage comments for details');
+
+} elseif (eregi('^(f|ht)tp\://', $phpThumb->src)) {
+
+	if ($phpThumb->config_http_user_agent) {
+		ini_set('user_agent', $phpThumb->config_http_user_agent);
+	}
+
+	if ($rawImageData = phpthumb_functions::SafeURLread(phpthumb_functions::CleanUpURLencoding($phpThumb->src), $error, $phpThumb->config_http_fopen_timeout, $phpThumb->config_http_follow_redirect)) {
+		$phpThumb->setSourceData($rawImageData, urlencode($phpThumb->src));
+	} else {
+		$phpThumb->ErrorImage($error);
+	}
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[7]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '7') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+$phpThumb->GenerateThumbnail();
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[8]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '8') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+if ($phpThumb->config_allow_parameter_file && $phpThumb->file) {
+
+	$phpThumb->RenderToFile($phpThumb->ResolveFilenameToAbsolute($phpThumb->file));
+	if ($phpThumb->config_allow_parameter_goto && $phpThumb->goto && eregi('^(f|ht)tps?://', $phpThumb->goto)) {
+		// redirect to another URL after image has been rendered to file
+		header('Location: '.$phpThumb->goto);
+		exit;
+	}
+
+} elseif (@$PHPTHUMB_CONFIG['high_security_enabled'] && @$_GET['nocache']) {
+
+	// cache disabled, don't write cachefile
+
+} else {
+
+	phpthumb_functions::EnsureDirectoryExists(dirname($phpThumb->cache_filename));
+	if ((file_exists($phpThumb->cache_filename) && is_writable($phpThumb->cache_filename)) || is_writable(dirname($phpThumb->cache_filename))) {
+
+		$phpThumb->CleanUpCacheDirectory();
+		if ($phpThumb->RenderToFile($phpThumb->cache_filename) && is_readable($phpThumb->cache_filename)) {
+			chmod($phpThumb->cache_filename, 0644);
+			RedirectToCachedFile();
+		} else {
+			$phpThumb->DebugMessage('Failed: RenderToFile('.$phpThumb->cache_filename.')', __FILE__, __LINE__);
+		}
+
+	} else {
+
+		$phpThumb->DebugMessage('Cannot write to $phpThumb->cache_filename ('.$phpThumb->cache_filename.') because that directory ('.dirname($phpThumb->cache_filename).') is not writable', __FILE__, __LINE__);
+
+	}
+
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[9]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '9') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+if (!$phpThumb->OutputThumbnail()) {
+	$phpThumb->ErrorImage('Error in OutputThumbnail():'."\n".$phpThumb->debugmessages[(count($phpThumb->debugmessages) - 1)]);
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[10]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '10') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+?>
Index: /afridex/plugins/fresh-page/RCCWP_WritePostPage.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_WritePostPage.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_WritePostPage.php (revision 21)
@@ -0,0 +1,841 @@
+<?php
+class RCCWP_WritePostPage
+{
+	function ApplyCustomWritePanelAssignedCategories($content)
+	{
+		global $CUSTOM_WRITE_PANEL;
+		
+		$assignedCategoryIds = RCCWP_CustomWritePanel::GetAssignedCategoryIds($CUSTOM_WRITE_PANEL->id);
+		foreach ($assignedCategoryIds as $categoryId)
+		{
+			$toReplace = 'id="in-category-' . $categoryId . '"';
+			$replacement = $toReplace . ' checked="checked"';
+			$content = str_replace($toReplace, $replacement, $content);
+		}
+		
+		return $content;
+	}
+	
+	function ApplyCustomWritePanelStandardFields()
+	{
+		global $CUSTOM_WRITE_PANEL;
+		
+		$displayCssIds = RCCWP_CustomWritePanel::GetStandardFieldCssIds($CUSTOM_WRITE_PANEL->id);
+		$wpCssIds = RCCWP_Application::GetWpStandardFieldCssIds();
+		
+		$hideCssIds = array_diff($wpCssIds, $displayCssIds);
+		
+		if (empty($hideCssIds))
+			return;
+		
+		if (in_array('postdiv', $hideCssIds))
+		{
+			array_push($hideCssIds, 'postdivrich');
+		}
+		
+		array_walk($hideCssIds, create_function('&$item1, $key', '$item1 = "#" . $item1;'));
+		$hideCssIdString = implode(', ', $hideCssIds);
+		?>
+		
+		<style type="text/css"><?=$hideCssIdString?> {display: none !important;}</style>
+		
+		<?php
+	}
+	
+	function HideCustomWritePanelExternalFields()
+	{
+		global $CUSTOM_WRITE_PANEL;
+		
+		$hideCssIds = RCCWP_CustomWritePanel::GetHiddenExternalFieldCssIds($CUSTOM_WRITE_PANEL->id);
+		if (empty($hideCssIds))
+			return;
+			
+		array_walk($hideCssIds, create_function('&$item1, $key', '$item1 = "#" . $item1;'));
+		$hideCssIdString = implode(', ', $hideCssIds);
+		?>
+		
+		<style type="text/css"><?=$hideCssIdString?> {display: none !important;}</style>
+		
+		<?php	
+	}
+	
+	function CustomFieldCollectionInterface()
+	{
+		global $CUSTOM_WRITE_PANEL;
+		
+		if (!isset($CUSTOM_WRITE_PANEL))
+			return;
+		
+		$customFields = array();
+		$customFields = RCCWP_CustomWritePanel::GetCustomFields($CUSTOM_WRITE_PANEL->id);
+		?>
+		<style type="text/css">.form-table td{background-color:#fff;margin-bottom:0;border:none;} .form-table input, .form-table textarea{border-color:#ccc;}</style>
+		<div id="freshpagediv" class="postbox">
+			<h3><a class="togbox">+</a> Freshpage Panel</h3>
+			<div class="inside">
+			<table class="form-table" border="0">
+		
+		<?php
+		foreach ($customFields as $field)
+		{
+			RCCWP_WritePostPage::CustomFieldInterface($field);
+		}
+		?>
+		
+			</table>
+		</div>
+		</div>
+		<input type="hidden" name="rc-custom-write-panel-verify-key" id="rc-custom-write-panel-verify-key" value="<?=wp_create_nonce('rc-custom-write-panel')?>" />
+		<input type="hidden" name="rc-cwp-custom-write-panel-id" value="<?=$CUSTOM_WRITE_PANEL->id?>" />
+		<!-- rc_cwp_submit_buttons -->
+		
+		<?php
+	}
+	
+	function CustomFieldInterface($customField)
+	{
+		switch ($customField->type)
+		{
+			case 'Textbox' :
+				RCCWP_WritePostPage::TextboxInterface($customField);
+				break;
+			case 'Multiline Textbox' :
+				RCCWP_WritePostPage::MultilineTextboxInterface($customField);
+				break;
+			case 'Checkbox' :
+				RCCWP_WritePostPage::CheckboxInterface($customField);
+				break;
+			case 'Checkbox List' :
+				RCCWP_WritePostPage::CheckboxListInterface($customField);
+				break;
+			case 'Radiobutton List' :
+				RCCWP_WritePostPage::RadiobuttonListInterface($customField);
+				break;
+			case 'Dropdown List' :
+				RCCWP_WritePostPage::DropdownListInterface($customField);
+				break;
+			case 'Listbox' :
+				RCCWP_WritePostPage::ListboxInterface($customField);
+				break;
+			case 'File' :
+				RCCWP_WritePostPage::FileInterface($customField);
+				break;
+			case 'Image' :
+				RCCWP_WritePostPage::PhotoInterface($customField);
+				break;
+			case 'Date' :
+				RCCWP_WritePostPage::DateInterface($customField);
+				break;
+			case 'Audio' :
+				RCCWP_WritePostPage::AudioInterface($customField);
+				break;
+			default:
+				;
+		}
+	}
+	
+	function CheckboxInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name);
+			$checked = $value == 'true' ? 'checked="checked"' : '';
+		}
+		?>
+		
+		<tr>
+		<td>
+		<label for="<?=$inputName?>">
+		<input type="hidden" name="<?=$inputName?>" value="false" />
+		<input tabindex="3" class="checkbox" name="<?=$inputName?>" value="true" id="<?=$inputName?>" type="checkbox" <?=$checked?> />
+		<?=$customFieldTitle?>
+		<input type="hidden" name="rc_cwp_meta_ids[]" value="<?=$customFieldId?>" />
+		<input type="hidden" name="rc_cwp_meta_keys[]" value="<?=$inputName?>" />
+		</label>
+		</td>
+		</tr>
+		
+		<?php
+	}
+	
+	function CheckboxListInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		
+		$values = array();
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$values = RCCWP_CustomField::GetCustomFieldValues($_REQUEST['post'], $customField->name);
+		}
+		else
+		{
+			$values = $customField->default_value;
+		}
+		?>
+		
+		<tr>
+		<td>
+		<fieldset style="border:1px solid #ddd;padding:10px;">
+		<legend style="padding:3px;font-weight:bold;"><?=$customFieldTitle?></legend>
+		
+		<?php
+		foreach ($customField->options as $option) :
+			$option = attribute_escape(trim($option));
+			$checked = in_array($option, (array)$values) ? 'checked="checked"' : '';
+		?>
+		
+			<label for="" class="selectit">
+				<input tabindex="3" id="<?=$option?>" name="<?=$inputName?>[]" value="<?=$option?>" type="checkbox" <?=$checked?>/>
+				<?=attribute_escape($option)?>
+			</label><br />
+		
+		<?php
+		endforeach;
+		?>
+			</fieldset>
+			<input type="hidden" name="rc_cwp_meta_ids[]" value="<?=$customFieldName?>" />
+			<input type="hidden" name="rc_cwp_meta_keys[]" value="<?=$inputName?>" />
+		</td>
+		</tr>
+		
+		<?php
+	}
+	
+	function DropdownListInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name));
+		}
+		else
+		{
+			$value = $customField->default_value[0];
+		}
+		?>
+		
+		<tr>
+		<td>
+		<label for="<?=$inputName?>"><?=$customFieldTitle?></label><br />
+		<select tabindex="3" name="<?=$inputName?>">
+			<option value="">--Select--</option>
+		
+		<?php
+		foreach ($customField->options as $option) :
+			$option = attribute_escape(trim($option));
+			$selected = $option == $value ? 'selected="selected"' : '';
+		?>
+		
+			<option value="<?=$option?>" <?=$selected?>><?=$option?></option>
+		
+		<?php
+		endforeach;
+		?>
+		
+		</select>	
+		<input type="hidden" name="rc_cwp_meta_ids[]" value="<?=$customFieldId?>" />
+		<input type="hidden" name="rc_cwp_meta_keys[]" value="<?=$inputName?>" />
+		</td>
+		</tr>
+		
+		<?php
+	}
+	
+	function ListboxInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		$inputSize = (int)$customField->properties['size'];
+		
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$values = RCCWP_CustomField::GetCustomFieldValues($_REQUEST['post'], $customField->name);
+		}
+		else
+		{
+			$values = $customField->default_value;
+		}
+		?>
+		
+		<tr>
+		<td>
+		<label for="<?=$inputName?>"><?=$customFieldTitle?></label><br />
+		<select tabindex="3" id="<?=$inputName?>" name="<?=$inputName?>[]" multiple size="<?=$inputSize?>">
+		
+		<?php
+		foreach ($customField->options as $option) :
+			$option = attribute_escape(trim($option));
+			$selected = in_array($option, (array)$values) ? 'selected="selected"' : '';
+		?>
+			
+			<option value="<?=$option?>" <?=$selected?>><?=$option?></option>
+			
+		<?php
+		endforeach;
+		?>
+		
+		</select>
+		<input type="hidden" name="rc_cwp_meta_ids[]" value="<?=$customFieldId?>" />
+		<input type="hidden" name="rc_cwp_meta_keys[]" value="<?=$inputName?>" />
+		</td>
+		</tr>
+		
+		<?php
+	}
+	
+	function MultilineTextboxInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		$inputHeight = (int)$customField->properties['height'];
+		$inputWidth = (int)$customField->properties['width'];
+		
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name));
+		}
+		?>
+		
+		<tr>
+		<td>
+		<label for="<?=$inputName?>"><?=$customFieldTitle?></label><br />
+		<textarea tabindex="3" id="<?=$inputName?>" name="<?=$inputName?>" rows="<?=$inputHeight?>" cols="<?=$inputWidth?>"><?=$value?></textarea>
+		<input type="hidden" name="rc_cwp_meta_ids[]" value="<?=$customFieldId?>" />
+		<input type="hidden" name="rc_cwp_meta_keys[]" value="<?=$inputName?>" />
+		</td>
+		</tr>
+		
+		<?php
+	}
+	
+	function TextboxInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		$inputSize = (int)$customField->properties['size'];
+		
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name));
+		}
+		?>
+		
+		<tr>
+		<td>
+		<label for="<?=$inputName?>"><?=$customFieldTitle?></label><br />
+		<input tabindex="3" id="<?=$inputName?>" name="<?=$inputName?>" value="<?=$value?>" type="text" size="<?=$inputSize?>" />
+		<input type="hidden" name="rc_cwp_meta_ids[]" value="<?=$customFieldId?>" />
+		<input type="hidden" name="rc_cwp_meta_keys[]" value="<?=$inputName?>" />
+		</td>
+		</tr>
+		
+		<?php
+	}
+	
+
+	function FileInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		$urlName = RC_Format::GetInputName( $customField->name . '_url' );
+		$filepath = RC_Format::GetInputName( $customField->name . '_filepath' );
+
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name));
+		}
+
+		?>
+		
+		<tr>
+		<td>
+		<label for="<?=$inputName?>"><?=$customFieldTitle?></label>
+		<?php if( $value ){ echo '(<a href="' . $value . '" target="_blank">View Current</a>)'; } ?>
+		</td>
+		</tr>
+		<tr>
+		<td>
+		<input tabindex="3" 
+			id="<?=$inputName?>" 
+			name="<?=$inputName?>" 
+			type="file"
+			style="visibility:hidden;height:0px"
+			/>
+		
+		<input tabindex="3" 
+			id="<?=$filepath?>" 
+			name="<?=$filepath?>" 
+			type="hidden" 
+			size="46"
+			onchange="var postForm=document.getElementById('post');postForm.encoding='multipart/form-data';"
+			/>
+
+
+
+		<div id='upload_iframe_<?=$filepath?>'>
+		<iframe id='upload_internal_iframe_<?=$filepath?>' src='./../wp-content/plugins/fresh-page/RCCWP_upload.php?input_name=<?=$filepath?>&type=0' frameborder='' scrolling='0' style="border-width: 0px; height: 75px;width: 800px;vertical-align:top;"></iframe>
+		</div>
+		<div id="upload_progress_<?=$filepath?>" style="visibility:hidden;height:0px"></div>
+
+
+
+
+
+		<input type="hidden" name="rc_cwp_meta_ids[]" 	 value="<?=$customFieldId?>" 	/>
+		<input type="hidden" name="rc_cwp_meta_keys[]" 	 value="<?=$inputName?>" 	/>
+		<input type="hidden" name="rc_cwp_meta_files[]"	 value="<?=$inputName?>" 	/>
+		<input type="hidden" name="<?=$inputName?>_last" value="<?=$value?>" 		/>
+		</td>
+		</tr>
+		
+		<?php
+	}
+
+
+	function PhotoInterface($customField)
+	{
+		$customFieldId 	= '';
+		$customFieldName= attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName 		= RC_Format::GetInputName($customField->name);
+		$urlName 		= RC_Format::GetInputName( $customField->name . '_url' );
+		$filepath 		= RC_Format::GetInputName( $customField->name . '_filepath' );
+		$params 		= $customField->properties['params'];
+		$noimage 		= "";
+
+		global $countImageThumbID;
+		$imageThumbID = "";
+		$imageThumbID = "img_thumb_".++$countImageThumbID;
+
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+//			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name));
+			$value = RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name);
+			
+			if(!(strpos($value, 'http') === FALSE))
+				$hidValue = str_replace('"', "'", $value);
+			$value = stripslashes(trim("\<img src=\'".$value."\' class=\"freshout\" \/\>"));
+		}
+		else
+		{
+			$noimage = "<img src='./../wp-content/plugins/fresh-page/images/noimage.jpg' />";
+		}
+		if($hidValue == '')
+		{
+			$noimage = "<img src='./../wp-content/plugins/fresh-page/images/noimage.jpg' />";
+		}
+
+		include_once('RCCWP_Options.php');
+		$useSnipshot = RCCWP_Options::Get('use-snipshot');
+	
+		?>
+		
+		<tr>
+			<td><label for="<?=$inputName?>"><?=$customFieldTitle?></label> <span id="spanImageSize"></span></td>
+		</tr>
+		<tr>
+			<td>
+				<table width="650px" border="0">
+					<tr>
+						<td rowspan="3" width="25%">
+							<?php
+								if($value != "")
+								{ 
+									if(!(strpos($value, '<img src') === FALSE))
+									{
+										$valueLinkArr = explode("'", $value);
+										$valueLink = $valueLinkArr[1];
+										//$valueLink = $value;
+		
+										if(!(strpos($value, '&sw') === FALSE))
+										{
+											// Calculating Image Width/Height
+											$arrSize = explode("=",$value);
+											$arrSize1 = explode("&",$arrSize[3]);
+											$arrSize2 = explode("&",$arrSize[4]);
+		
+											$imageWidth = $arrSize1[0];
+											$imageHeight = $arrSize2[0];
+											// END
+		
+											$valueArr = explode("&sw", $value);
+											$valueArr = explode("'", $valueArr[1]);
+											$value = str_replace("&sw".$valueArr[0]."'", "&sw".$valueArr[0]."&w=150&h=120' align='left' id='".$imageThumbID."'", $value);
+										}
+										else if(!(strpos($value, '&w') === FALSE))
+										{
+											// Calculating Image Width/Height
+											$arrSize = explode("=",$value);
+											$arrSize1 = explode("&",$arrSize[3]);
+											$arrSize2 = explode("'",$arrSize[4]);
+		
+											$imageWidth = $arrSize1[0];
+											$imageHeight = $arrSize2[0];
+											// END
+		
+											$valueArr = explode("&", $value);
+											$valueArr = explode("'", $valueArr[2]);
+											$value = str_replace($valueArr[0], "&w=150&h=120' align='left' id='".$imageThumbID."'", $value);
+										}
+										else
+										{
+											// Calculating Image Width/Height
+											$arrSize = explode("&",$params);
+											$arrSize1 = explode("=",$arrSize[1]);
+											$arrSize2 = explode("=",$arrSize[2]);
+		
+											$imageWidth = $arrSize1[1];
+											$imageHeight = $arrSize2[1];
+											// END
+		
+											$valueArr = explode("'", $value);
+											$value = str_replace($valueArr[1], $valueArr[1]."&w=150' id='".$imageThumbID."' align='left", $value);
+										}
+										if(!empty($imageWidth))
+										{
+										?>
+										<script language="javascript">
+											document.getElementById('spanImageSize').innerHTML = " &nbsp; (<?php echo $imageWidth; echo ' x '; echo $imageHeight; ?>)";
+										</script>
+
+										<?php
+										}
+											echo '<a href="' . $valueLink . '" target="_blank">' . $value .'</a>';
+									 }
+								 }
+								 echo $noimage;
+								 $arrSize = explode("phpThumb.php?src=",$valueLink);
+								 $arrSizeext = explode("&",$arrSize[1]);
+								 if (sizeof($arrSizeext) > 1){
+								 	$ext = substr($arrSizeext[0],-4, -1);
+									$fileLink = substr($arrSizeext[0],0, -1);
+								 }
+								 else	{
+									$ext = substr($arrSizeext[0],-3);
+									$fileLink = $arrSizeext[0];
+									}
+							 ?>
+						</td>
+					</tr>
+					<tr>
+						<td>
+							<input tabindex="3" 
+								id="<?=$inputName?>" 
+								name="<?=$inputName?>" 
+								type="file"
+								style="visibility:hidden;height:0px"
+								/>
+							
+							<input tabindex="3" 
+								id="<?=$filepath?>" 
+								name="<?=$filepath?>" 
+								type="hidden" 
+								size="46"
+								onchange="var postForm=document.getElementById('post');postForm.encoding='multipart/form-data';"
+								/>
+
+							
+					
+								<div id='upload_iframe_<?=$filepath?>'>
+								<iframe id='upload_internal_iframe_<?=$filepath?>' src='./../wp-content/plugins/fresh-page/RCCWP_upload.php?input_name=<?=$filepath?>&type=1' frameborder='' scrolling='0' style="border-width: 0px; height: 75px;width: 800px;vertical-align:top;"></iframe>
+								</div>
+								<div id="upload_progress_<?=$filepath?>" style="visibility:hidden;height:0px"></div>
+					
+							
+
+							
+						</td>
+					</tr>
+
+				</table>
+				<input type="hidden" name="rc_cwp_meta_ids[]" 	 value="<?=$customFieldId?>" 	/>
+				<input type="hidden" name="rc_cwp_meta_keys[]" 	 value="<?=$inputName?>" 	/>
+				<input type="hidden" name="rc_cwp_meta_files[]"	 value="<?=$inputName?>" 	/>
+				<input type="hidden" name="rc_cwp_meta_photos[]" value="<?=$inputName?>" 	/>
+				<input type="hidden" name="<?=$inputName?>_last" id="<?=$inputName?>_last" value="<?=$hidValue?>"		/>
+				<input type="hidden" name="<?=$inputName?>_params" value="<?=$params?>"		/>
+				<script type="text/javascript" src="http://jquery.com/src/latest/"></script>
+				<script type="text/javascript" src="<?php bloginfo("url"); ?>/wp-content/plugins/fresh-page/js/greybox.js"></script>
+				<link href="<?php bloginfo("url"); ?>/wp-content/plugins/fresh-page/css/greybox.css" rel="stylesheet" type="text/css" media="all" />
+				<script type="text/javascript">
+				  var GB_ANIMATION = true;
+				  $(document).ready(function(){
+					$("a.greybox").click(function(){
+					  var t = this.title || $(this).text() || this.href;
+					  GB_show(t,this.href,470,600);
+					  return false;
+					});
+				  });
+	
+				  $(document).ready(function(){
+					$("a.greybox1").click(function(){
+					  var t = this.title || $(this).text() || this.href;
+					  var myWidth = 0, myHeight = 0;
+					  if( typeof( window.innerWidth ) == 'number' ) {
+						//Non-IE
+						myWidth = window.innerWidth;
+						myHeight = window.innerHeight;
+					   } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
+						//IE 6+ in 'standards compliant mode'
+						myWidth = document.documentElement.clientWidth;
+						myHeight = document.documentElement.clientHeight;
+					   }
+
+					  GB_show(t,this.href,myHeight- 55,myWidth-55);
+					  return false;
+					});
+				  });
+				
+				function setCookie(c_name,value,expiredays)
+				{
+					var exdate=new Date();
+					exdate.setDate(exdate.getDate()+expiredays);
+					document.cookie=c_name+ "=" +escape(value)+
+					((expiredays==null) ? "" : ";expires="+exdate.toGMTString());
+				}
+				function prepareUpdatePhoto(){	
+					document.getElementById('<?=$filepath?>').value = '<?=basename($fileLink)?>';	
+					document.getElementById('<?=$filepath?>').onchange();
+					document.getElementById("<?=$inputName?>_last").value = '0';
+					return true;
+				}	
+				</script>
+				<?php
+					if(isset($_REQUEST['post']) && $hidValue != '')
+					{
+						if ($useSnipshot){ 
+							
+							
+							//echo "<br/> Snipshot selected <br />";
+				?>
+							<div align="left"> 
+								<ul>
+									<li style="list-style:none;"><a target="_blank" href="http://services.snipshot.com/?snipshot_input=<?php echo urlencode($arrSize[1])."&snipshot_callback=".urlencode(get_bloginfo("url")."/wp-content/plugins/fresh-page/RCCWP_SnipshotCallback.php");?>&snipshot_output=file&snipshot_callback_agent=user&test=hello&snipshot_output_options=<?php echo urlencode("{\"filetype\":\"$ext\"}")?>" title="Fresh Post" class="greybox1" id="lnkCropper" onclick="prepareUpdatePhoto()"> <strong>Edit</strong> </a></li>
+								</ul>
+							</div>
+
+				<?php
+						}else{
+				?>
+							<div align=="left"> 
+								<ul>
+									<li style="list-style:none;"><a href="<?php bloginfo("url");?>/wp-content/plugins/fresh-page/cropper.php?id=<?php echo $valueLink; ?>&url=<?php echo $_SERVER['REQUEST_URI']; ?>&imageThumbId=<?php echo $imageThumbID;?>" title="Fresh Post" class="greybox" id="lnkCropper"> <strong>Crop</strong> </a></li>
+								</ul>
+							</div>
+				<?php 	
+						} 
+					}
+				?>
+				<script type="text/javascript">
+				function exchangeValues(e, id)
+				{
+					document.getElementById(document.getElementById('parent_text_'+id.substring(10)).value).value = e;
+					document.getElementById(document.getElementById('hidImgValue'+id.substring(10)).value).value = e;
+				}
+				</script>
+				<!-- Used to store name of URL Field -->
+				<input type="hidden" name="parent_text_<?php echo $countImageThumbID; ?>" id="parent_text_<?php echo $countImageThumbID; ?>" value="<?php echo $filepath; ?>"/>
+				<input type="hidden" name="hidImgValue<?php echo $countImageThumbID; ?>" id="hidImgValue<?php echo $countImageThumbID; ?>" value="<?php echo $inputName; ?>_last" />
+			</td>
+		</tr>
+		<?php
+	}
+	
+	function RadiobuttonListInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		
+		if (isset($_REQUEST['post']))
+		{
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name));
+		}
+		else
+		{
+			$value = $customField->default_value[0];
+		}
+		?>
+		
+		<tr>
+		<td>
+		<fieldset style="border:1px solid #ddd;padding:10px;">
+		<legend style="padding:3px;font-weight:bold;"><?=$customFieldTitle?></legend>
+		
+		<?php
+		foreach ($customField->options as $option) :
+			$option = attribute_escape(trim($option));
+			$checked = $option == $value ? 'checked="checked"' : '';
+		?>
+			<label for="" class="selectit">
+				<input tabindex="3" id="<?=$option?>" name="<?=$inputName?>" value="<?=$option?>" type="radio" <?=$checked?>/>
+				<?=$option?>
+			</label><br />
+		<?php
+		endforeach;
+		?>
+		</fieldset>
+		<input type="hidden" name="rc_cwp_meta_ids[]" value="<?=$customFieldId?>" />
+		<input type="hidden" name="rc_cwp_meta_keys[]" value="<?=$inputName?>" />
+		</td>
+		</tr>
+		
+		<?php
+	}
+
+	function DateInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+
+		global $wpdb;
+
+		$sQuery = "SELECT * FROM wp_rc_cwp_custom_field_properties WHERE custom_field_id='".$customField->id."'";
+		$result = $wpdb->get_results($sQuery);
+
+		$arrDateFormat = explode('"', $result[0]->properties);
+		$dateFormat = $arrDateFormat[3];
+
+?>
+		<!-- Calendar Control -->
+		<link rel="stylesheet" type="text/css" href="<?php bloginfo('url');?>/wp-content/plugins/fresh-page/css/epoch_styles.css" /> <!--Epoch's styles-->
+		<script type="text/javascript" src="<?php bloginfo('url');?>/wp-content/plugins/fresh-page/js/epoch_classes.js"></script> <!--Epoch's Code-->
+		<!-- Calendar Control -->
+
+		<script type="text/javascript">
+			var bas_cal, dp_cal, ms_cal; // declare the calendars as global variables
+			window.onload = function () {
+				__DATE_FORMAT = "<?php echo $dateFormat; ?>";
+				dp_cal  = new Epoch('dp_cal','popup',document.getElementById('date_field'));
+			}; 
+		</script>	
+
+<?php
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name));
+		}
+		?>
+		
+		<tr>
+		<td>
+
+	<!--Datepicker container. The <input> text element is required, the button element is optional-->
+
+		<label for="<?=$inputName?>"><?=$customFieldTitle?></label><br />
+		<input tabindex="3" id="date_field" name="<?=$inputName?>" value="<?=$value?>" type="text" size="<?=$inputSize?>" />
+		<input type="button" value="..." onclick="dp_cal.toggle();" />
+
+		<input type="hidden" name="rc_cwp_meta_ids[]" value="<?=$customFieldId?>" />
+		<input type="hidden" name="rc_cwp_meta_keys[]" value="<?=$inputName?>" />
+		</td>
+		</tr>
+		
+		<?php
+	}
+
+	function AudioInterface($customField)
+	{
+		$customFieldId = '';
+		$customFieldName = attribute_escape($customField->name);
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = RC_Format::GetInputName($customField->name);
+		$urlName = RC_Format::GetInputName( $customField->name . '_url' );
+		$filepath = RC_Format::GetInputName( $customField->name . '_filepath' );
+		
+
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$valueOriginal = RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name);
+			if (!empty($valueOriginal))
+				$value = stripslashes(trim("\<div style=\'padding-top:3px;\'\>\<object classid=\'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\' codebase='\http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0\' width=\'95%\' height=\'20\' wmode=\'transparent\' \>\<param name=\'movie\' value=\'http://www.freshoutmedia.com/singlemp3player.swf?file=".$valueOriginal."\' wmode=\'transparent\' /\>\<param name=\'quality\' value=\'high\' wmode=\'transparent\' /\>\<embed src=\'http://www.freshoutmedia.com/singlemp3player.swf?file=".$valueOriginal."' width=\'50\%\' height=\'20\' quality=\'high\' pluginspage=\'http://www.macromedia.com/go/getflashplayer\' type=\'application/x-shockwave-flash\' wmode=\'transparent\' \>\</embed\>\</object\>\</div\>"));
+//			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValue($_REQUEST['post'], $customField->name));
+		}
+
+		?>
+		
+		<tr>
+		<td><br/>
+		<label for="<?=$inputName?>"><?=$customFieldTitle?></label>
+		<?php if( $value ){ echo $value; } ?><br/>
+		</td>
+		</tr>
+		<tr>
+		<td>
+		<input tabindex="3" 
+			id="<?=$inputName?>" 
+			name="<?=$inputName?>" 
+			type="file"
+			style="visibility:hidden;height:0px"
+			/>
+		
+		<input tabindex="3" 
+			id="<?=$filepath?>" 
+			name="<?=$filepath?>" 
+			type="hidden" 
+			size="46"
+			onchange="var postForm=document.getElementById('post');postForm.encoding='multipart/form-data';"
+			/>
+		
+		
+		
+			<div id='upload_iframe_<?=$filepath?>'>
+			<iframe id='upload_internal_iframe_<?=$filepath?>' src='./../wp-content/plugins/fresh-page/RCCWP_upload.php?input_name=<?=$filepath?>&type=2' frameborder='' scrolling='0' style="border-width: 0px; height: 75px;width: 800px;vertical-align:top;"></iframe>
+			</div>
+			<div id="upload_progress_<?=$filepath?>" style="visibility:hidden;height:0px"></div>
+		
+	
+
+
+		<input type="hidden" name="rc_cwp_meta_ids[]" 	 value="<?=$customFieldId?>" 	/>
+		<input type="hidden" name="rc_cwp_meta_keys[]" 	 value="<?=$inputName?>" 	/>
+		<input type="hidden" name="rc_cwp_meta_files[]"	 value="<?=$inputName?>" 	/>
+		<input type="hidden" name="<?=$inputName?>_last" value="<?=$valueOriginal?>" 		/>
+		</td>
+		</tr>
+		
+		<?php
+	}
+
+	function RelocateWpSubmitButtons($content)
+	{
+		$pattern = '(<p class="submit".*?\/p>)(.*?)(<!-- rc_cwp_submit_buttons -->)';  // all those Save, Publish, etc buttons
+		$replacement = '$2$1';
+		$content = preg_replace('/' . $pattern . '/s', $replacement, $content);
+		return $content;
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/phpthumb.class.php
===================================================================
--- /afridex/plugins/fresh-page/phpthumb.class.php (revision 21)
+++ /afridex/plugins/fresh-page/phpthumb.class.php (revision 21)
@@ -0,0 +1,3624 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// See: phpthumb.readme.txt for usage instructions          //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+ob_start();
+if (!include_once(dirname(__FILE__).'/phpthumb.functions.php')) {
+	ob_end_flush();
+	die('failed to include_once("'.realpath(dirname(__FILE__).'/phpthumb.functions.php').'")');
+}
+ob_end_clean();
+
+class phpthumb {
+
+	// public:
+	// START PARAMETERS (for object mode and phpThumb.php)
+	// See phpthumb.readme.txt for descriptions of what each of these values are
+	var $src  = null;     // SouRCe filename
+	var $new  = null;     // NEW image (phpThumb.php only)
+	var $w    = null;     // Width
+	var $h    = null;     // Height
+	var $wp   = null;     // Width  (Portrait Images Only)
+	var $hp   = null;     // Height (Portrait Images Only)
+	var $wl   = null;     // Width  (Landscape Images Only)
+	var $hl   = null;     // Height (Landscape Images Only)
+	var $ws   = null;     // Width  (Square Images Only)
+	var $hs   = null;     // Height (Square Images Only)
+	var $f    = null;     // Format
+	var $q    = 75;       // jpeg output Quality
+	var $sx   = null;     // Source crop top-left X position
+	var $sy   = null;     // Source crop top-left Y position
+	var $sw   = null;     // Source crop Width
+	var $sh   = null;     // Source crop Height
+	var $zc   = null;     // Zoom Crop
+	var $bc   = null;     // Border Color
+	var $bg   = null;     // BackGround color
+	var $fltr = array();  // FiLTeRs
+	var $goto = null;     // GO TO url after processing
+	var $err  = null;     // default ERRor image filename
+	var $xto  = null;     // extract eXif Thumbnail Only
+	var $ra   = null;     // Rotate by Angle
+	var $ar   = null;     // Auto Rotate
+	var $aoe  = null;     // Allow Output Enlargement
+	var $far  = null;     // Fixed Aspect Ratio
+	var $iar  = null;     // Ignore Aspect Ratio
+	var $maxb = null;     // MAXimum Bytes
+	var $down = null;     // DOWNload thumbnail filename
+	var $md5s = null;     // MD5 hash of Source image
+	var $sfn  = 0;        // Source Frame Number
+	var $dpi  = 150;      // Dots Per Inch for vector source formats
+	var $sia  = null;     // Save Image As filename
+
+	var $file = null;     // >>>deprecated, DO NOT USE, will be removed in future versions<<<
+
+	var $phpThumbDebug = null;
+	// END PARAMETERS
+
+
+	// public:
+	// START CONFIGURATION OPTIONS (for object mode only)
+	// See phpThumb.config.php for descriptions of what each of these settings do
+
+	// * Directory Configuration
+	var $config_cache_directory                      = null;
+	var $config_cache_directory_depth                = 0;
+	var $config_cache_disable_warning                = true;
+	var $config_cache_source_enabled                 = false;
+	var $config_cache_source_directory               = null;
+	var $config_temp_directory                       = null;
+	var $config_document_root                        = null;
+
+	// * Default output configuration:
+	var $config_output_format                        = 'jpeg';
+	var $config_output_maxwidth                      = 0;
+	var $config_output_maxheight                     = 0;
+	var $config_output_interlace                     = true;
+
+	// * Error message configuration
+	var $config_error_image_width                    = 400;
+	var $config_error_image_height                   = 100;
+	var $config_error_message_image_default          = '';
+	var $config_error_bgcolor                        = 'CCCCFF';
+	var $config_error_textcolor                      = 'FF0000';
+	var $config_error_fontsize                       = 1;
+	var $config_error_die_on_error                   = false;
+	var $config_error_silent_die_on_error            = false;
+	var $config_error_die_on_source_failure          = true;
+
+	// * Anti-Hotlink Configuration:
+	var $config_nohotlink_enabled                    = true;
+	var $config_nohotlink_valid_domains              = array();
+	var $config_nohotlink_erase_image                = true;
+	var $config_nohotlink_text_message               = 'Off-server thumbnailing is not allowed';
+	// * Off-server Linking Configuration:
+	var $config_nooffsitelink_enabled                = false;
+	var $config_nooffsitelink_valid_domains          = array();
+	var $config_nooffsitelink_require_refer          = false;
+	var $config_nooffsitelink_erase_image            = true;
+	var $config_nooffsitelink_watermark_src          = '';
+	var $config_nooffsitelink_text_message           = 'Off-server linking is not allowed';
+
+	// * Border & Background default colors
+	var $config_border_hexcolor                      = '000000';
+	var $config_background_hexcolor                  = 'FFFFFF';
+
+	// * TrueType Fonts
+	var $config_ttf_directory                        = '.';
+
+	var $config_max_source_pixels                    = null;
+	var $config_use_exif_thumbnail_for_speed         = false;
+	var $allow_local_http_src                        = false;
+
+	var $config_imagemagick_path                     = null;
+	var $config_prefer_imagemagick                   = true;
+	var $config_imagemagick_use_thumbnail            = true;
+
+	var $config_cache_maxage                         = null;
+	var $config_cache_maxsize                        = null;
+	var $config_cache_maxfiles                       = null;
+	var $config_cache_source_filemtime_ignore_local  = false;
+	var $config_cache_source_filemtime_ignore_remote = true;
+	var $config_cache_default_only_suffix            = false;
+	var $config_cache_force_passthru                 = true;
+	var $config_cache_prefix                         = '';    // default value set in the constructor below
+
+	// * MySQL
+	var $config_mysql_query                          = null;
+	var $config_mysql_hostname                       = null;
+	var $config_mysql_username                       = null;
+	var $config_mysql_password                       = null;
+	var $config_mysql_database                       = null;
+
+	// * Security
+	var $config_high_security_enabled                = false;
+	var $config_high_security_password               = null;
+	var $config_disable_debug                        = false;
+	var $config_allow_src_above_docroot              = false;
+	var $config_allow_src_above_phpthumb             = true;
+	var $config_allow_parameter_file                 = false;
+	var $config_allow_parameter_goto                 = false;
+
+	// * HTTP fopen
+	var $config_http_fopen_timeout                   = 10;
+	var $config_http_follow_redirect                 = true;
+
+	// * Compatability
+	var $config_disable_pathinfo_parsing             = false;
+	var $config_disable_imagecopyresampled           = false;
+	var $config_disable_onlycreateable_passthru      = false;
+
+	var $config_http_user_agent                      = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7';
+
+	// END CONFIGURATION OPTIONS
+
+
+	// public: error messages (read-only)
+	var $debugmessages = array();
+	var $debugtiming   = array();
+	var $fatalerror    = null;
+
+
+	// private: (should not be modified directly)
+	var $thumbnailQuality = 75;
+	var $thumbnailFormat  = null;
+
+	var $sourceFilename   = null;
+	var $rawImageData     = null;
+	var $IMresizedData    = null;
+	var $outputImageData  = null;
+
+	var $useRawIMoutput   = false;
+
+	var $gdimg_output     = null;
+	var $gdimg_source     = null;
+
+	var $getimagesizeinfo = null;
+
+	var $source_width  = null;
+	var $source_height = null;
+
+	var $thumbnailCropX = null;
+	var $thumbnailCropY = null;
+	var $thumbnailCropW = null;
+	var $thumbnailCropH = null;
+
+	var $exif_thumbnail_width  = null;
+	var $exif_thumbnail_height = null;
+	var $exif_thumbnail_type   = null;
+	var $exif_thumbnail_data   = null;
+	var $exif_raw_data         = null;
+
+	var $thumbnail_width        = null;
+	var $thumbnail_height       = null;
+	var $thumbnail_image_width  = null;
+	var $thumbnail_image_height = null;
+
+	var $cache_filename         = null;
+
+	var $AlphaCapableFormats = array('png', 'ico', 'gif');
+	var $is_alpha = false;
+
+	var $iswindows = null;
+
+	var $phpthumb_version = '1.7.7-200612252156';
+
+	//////////////////////////////////////////////////////////////////////
+
+	// public: constructor
+	function phpThumb() {
+		$this->DebugTimingMessage('phpThumb() constructor', __FILE__, __LINE__);
+		$this->DebugMessage('phpThumb() v'.$this->phpthumb_version, __FILE__, __LINE__);
+		$this->config_max_source_pixels = round(max(intval(ini_get('memory_limit')), intval(get_cfg_var('memory_limit'))) * 1048576 * 0.20); // 20% of memory_limit
+		$this->iswindows = (bool) (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN');
+		$this->config_temp_directory = realpath($this->config_temp_directory ? $this->config_temp_directory : (getenv('TMPDIR') ? getenv('TMPDIR') : getenv('TMP')));
+		$this->config_document_root = (@$_SERVER['DOCUMENT_ROOT'] ? $_SERVER['DOCUMENT_ROOT'] : $this->config_document_root);
+		$this->config_cache_prefix = 'phpThumb_cache_'.@$_SERVER['SERVER_NAME'];
+
+		$php_sapi_name = strtolower(function_exists('php_sapi_name') ? php_sapi_name() : '');
+		if ($php_sapi_name == 'cli') {
+			$this->config_allow_src_above_docroot = true;
+		}
+	}
+
+	// public:
+	function setSourceFilename($sourceFilename) {
+		$this->rawImageData   = null;
+		$this->sourceFilename = $sourceFilename;
+		$this->src            = $sourceFilename;
+		$this->DebugMessage('setSourceFilename('.$sourceFilename.') set $this->sourceFilename to "'.$this->sourceFilename.'"', __FILE__, __LINE__);
+		return true;
+	}
+
+	// public:
+	function setSourceData($rawImageData, $sourceFilename='') {
+		$this->sourceFilename = null;
+		$this->rawImageData   = $rawImageData;
+		$this->DebugMessage('setSourceData() setting $this->rawImageData ('.strlen($this->rawImageData).' bytes)', __FILE__, __LINE__);
+		if ($this->config_cache_source_enabled) {
+			$sourceFilename = ($sourceFilename ? $sourceFilename : md5($rawImageData));
+			if (!is_dir($this->config_cache_source_directory)) {
+				$this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not a directory');
+			} elseif (!@is_writable($this->config_cache_source_directory)) {
+				$this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not writable');
+			}
+			$this->DebugMessage('setSourceData() attempting to save source image to "'.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).'"', __FILE__, __LINE__);
+			if ($fp = @fopen($this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename), 'wb')) {
+				fwrite($fp, $rawImageData);
+				fclose($fp);
+			} elseif (!$this->phpThumbDebug) {
+				$this->ErrorImage('setSourceData() failed to write to source cache ('.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).')');
+			}
+		}
+		return true;
+	}
+
+	// public:
+	function setSourceImageResource($gdimg) {
+		$this->gdimg_source = $gdimg;
+		return true;
+	}
+
+	// public:
+	function setParameter($param, $value) {
+		if ($param == 'src') {
+			$this->setSourceFilename($this->ResolveFilenameToAbsolute($value));
+		} elseif (@is_array($this->$param)) {
+			if (is_array($value)) {
+				foreach ($value as $arraykey => $arrayvalue) {
+					array_push($this->$param, $arrayvalue);
+				}
+			} else {
+				array_push($this->$param, $value);
+			}
+		} else {
+			$this->$param = $value;
+		}
+		return true;
+	}
+
+	// public:
+	function getParameter($param) {
+		//if (property_exists('phpThumb', $param)) {
+			return $this->$param;
+		//}
+		//$this->DebugMessage('setParameter() attempting to get non-existant parameter "'.$param.'"', __FILE__, __LINE__);
+		//return false;
+	}
+
+
+	// public:
+	function GenerateThumbnail() {
+
+		$this->setOutputFormat();
+			$this->phpThumbDebug('8a');
+		$this->ResolveSource();
+			$this->phpThumbDebug('8b');
+		$this->SetCacheFilename();
+			$this->phpThumbDebug('8c');
+		$this->ExtractEXIFgetImageSize();
+			$this->phpThumbDebug('8d');
+		if ($this->useRawIMoutput) {
+			$this->DebugMessage('Skipping rest of GenerateThumbnail() because $this->useRawIMoutput', __FILE__, __LINE__);
+			return true;
+		}
+			$this->phpThumbDebug('8e');
+		if (!$this->SourceImageToGD()) {
+			$this->DebugMessage('SourceImageToGD() failed', __FILE__, __LINE__);
+			return false;
+		}
+			$this->phpThumbDebug('8f');
+		$this->Rotate();
+			$this->phpThumbDebug('8g');
+		$this->CreateGDoutput();
+			$this->phpThumbDebug('8h');
+
+		switch ($this->far) {
+			case 'L':
+			case 'TL':
+			case 'BL':
+				$destination_offset_x = 0;
+				$destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2);
+				break;
+			case 'R':
+			case 'TR':
+			case 'BR':
+				$destination_offset_x =  round($this->thumbnail_width  - $this->thumbnail_image_width);
+				$destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2);
+				break;
+			case 'T':
+			case 'TL':
+			case 'TR':
+				$destination_offset_x = round(($this->thumbnail_width  - $this->thumbnail_image_width)  / 2);
+				$destination_offset_y = 0;
+				break;
+			case 'B':
+			case 'BL':
+			case 'BR':
+				$destination_offset_x = round(($this->thumbnail_width  - $this->thumbnail_image_width)  / 2);
+				$destination_offset_y =  round($this->thumbnail_height - $this->thumbnail_image_height);
+				break;
+			case 'C':
+			default:
+				$destination_offset_x = round(($this->thumbnail_width  - $this->thumbnail_image_width)  / 2);
+				$destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2);
+		}
+
+//		// copy/resize image to appropriate dimensions
+//		$borderThickness = 0;
+//		if (!empty($this->fltr)) {
+//			foreach ($this->fltr as $key => $value) {
+//				if (ereg('^bord\|([0-9]+)', $value, $matches)) {
+//					$borderThickness = $matches[1];
+//					break;
+//				}
+//			}
+//		}
+//		if ($borderThickness > 0) {
+//			//$this->DebugMessage('Skipping ImageResizeFunction() because BorderThickness="'.$borderThickness.'"', __FILE__, __LINE__);
+//			$this->thumbnail_image_height /= 2;
+//		}
+		$this->ImageResizeFunction(
+			$this->gdimg_output,
+			$this->gdimg_source,
+			$destination_offset_x,
+			$destination_offset_y,
+			$this->thumbnailCropX,
+			$this->thumbnailCropY,
+			$this->thumbnail_image_width,
+			$this->thumbnail_image_height,
+			$this->thumbnailCropW,
+			$this->thumbnailCropH
+		);
+
+		$this->DebugMessage('memory_get_usage() after copy-resize = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__);
+		ImageDestroy($this->gdimg_source);
+		$this->DebugMessage('memory_get_usage() after ImageDestroy = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__);
+
+			$this->phpThumbDebug('8i');
+		$this->AntiOffsiteLinking();
+			$this->phpThumbDebug('8j');
+		$this->ApplyFilters();
+			$this->phpThumbDebug('8k');
+		$this->AlphaChannelFlatten();
+			$this->phpThumbDebug('8l');
+		$this->MaxFileSize();
+			$this->phpThumbDebug('8m');
+
+		$this->DebugMessage('GenerateThumbnail() completed successfully', __FILE__, __LINE__);
+		return true;
+	}
+
+
+	// public:
+	function RenderOutput() {
+		if (!$this->useRawIMoutput && !is_resource($this->gdimg_output)) {
+			$this->DebugMessage('RenderOutput() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__);
+			return false;
+		}
+		if (!$this->thumbnailFormat) {
+			$this->DebugMessage('RenderOutput() failed because $this->thumbnailFormat is empty', __FILE__, __LINE__);
+			return false;
+		}
+		if ($this->useRawIMoutput) {
+			$this->DebugMessage('RenderOutput copying $this->IMresizedData ('.strlen($this->IMresizedData).' bytes) to $this->outputImage', __FILE__, __LINE__);
+			$this->outputImageData = $this->IMresizedData;
+			return true;
+		}
+
+		$builtin_formats = array();
+		if (function_exists('ImageTypes')) {
+			$imagetypes = ImageTypes();
+			$builtin_formats['wbmp'] = (bool) ($imagetypes & IMG_WBMP);
+			$builtin_formats['jpg']  = (bool) ($imagetypes & IMG_JPG);
+			$builtin_formats['gif']  = (bool) ($imagetypes & IMG_GIF);
+			$builtin_formats['png']  = (bool) ($imagetypes & IMG_PNG);
+		}
+		$this->DebugMessage('RenderOutput() attempting Image'.strtoupper(@$this->thumbnailFormat).'($this->gdimg_output)', __FILE__, __LINE__);
+		ob_start();
+		switch ($this->thumbnailFormat) {
+			case 'wbmp':
+				if (!@$builtin_formats['wbmp']) {
+					$this->DebugMessage('GD does not have required built-in support for WBMP output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				ImageJPEG($this->gdimg_output, null, $this->thumbnailQuality);
+				$this->outputImageData = ob_get_contents();
+				break;
+
+			case 'jpeg':
+			case 'jpg':  // should be "jpeg" not "jpg" but just in case...
+				if (!@$builtin_formats['jpg']) {
+					$this->DebugMessage('GD does not have required built-in support for JPEG output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				ImageJPEG($this->gdimg_output, null, $this->thumbnailQuality);
+				$this->outputImageData = ob_get_contents();
+				break;
+
+			case 'png':
+				if (!@$builtin_formats['png']) {
+					$this->DebugMessage('GD does not have required built-in support for PNG output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				ImagePNG($this->gdimg_output);
+				$this->outputImageData = ob_get_contents();
+				break;
+
+			case 'gif':
+				if (!@$builtin_formats['gif']) {
+					$this->DebugMessage('GD does not have required built-in support for GIF output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				ImageGIF($this->gdimg_output);
+				$this->outputImageData = ob_get_contents();
+				break;
+
+			case 'bmp':
+				$ImageOutFunction = '"builtin BMP output"';
+				if (!@include_once(dirname(__FILE__).'/phpthumb.bmp.php')) {
+					$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				$phpthumb_bmp = new phpthumb_bmp();
+				$this->outputImageData = $phpthumb_bmp->GD2BMPstring($this->gdimg_output);
+				unset($phpthumb_bmp);
+				break;
+
+			case 'ico':
+				$ImageOutFunction = '"builtin ICO output"';
+				if (!@include_once(dirname(__FILE__).'/phpthumb.ico.php')) {
+					$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				$phpthumb_ico = new phpthumb_ico();
+				$arrayOfOutputImages = array($this->gdimg_output);
+				$this->outputImageData = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages);
+				unset($phpthumb_ico);
+				break;
+
+			default:
+				$this->DebugMessage('RenderOutput failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__);
+				ob_end_clean();
+				return false;
+		}
+		ob_end_clean();
+		if (!$this->outputImageData) {
+			$this->DebugMessage('RenderOutput() for "'.$this->thumbnailFormat.'" failed', __FILE__, __LINE__);
+			ob_end_clean();
+			return false;
+		}
+		$this->DebugMessage('RenderOutput() completing with $this->outputImageData = '.strlen($this->outputImageData).' bytes', __FILE__, __LINE__);
+		return true;
+	}
+
+	function RenderToFile($filename) {
+		if (eregi('^(f|ht)tps?\://', $filename)) {
+			$this->DebugMessage('RenderToFile() failed because $filename ('.$filename.') is a URL', __FILE__, __LINE__);
+			return false;
+		}
+		// render thumbnail to this file only, do not cache, do not output to browser
+		//$renderfilename = $this->ResolveFilenameToAbsolute(dirname($filename)).DIRECTORY_SEPARATOR.basename($filename);
+		$renderfilename = $filename;
+		if (($filename{0} != '/') && ($filename{0} != '\\') && ($filename{1} != ':')) {
+			$renderfilename = $this->ResolveFilenameToAbsolute($renderfilename);
+		}
+		if (!@is_writable(dirname($renderfilename))) {
+			$this->DebugMessage('RenderToFile() failed because "'.dirname($renderfilename).'/" is not writable', __FILE__, __LINE__);
+			return false;
+		}
+		if (@is_file($renderfilename) && !@is_writable($renderfilename)) {
+			$this->DebugMessage('RenderToFile() failed because "'.$renderfilename.'" is not writable', __FILE__, __LINE__);
+			return false;
+		}
+
+		if ($this->RenderOutput()) {
+			if (file_put_contents($renderfilename, $this->outputImageData)) {
+				$this->DebugMessage('RenderToFile('.$renderfilename.') succeeded', __FILE__, __LINE__);
+				return true;
+			}
+			if (!@file_exists($renderfilename)) {
+				$this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] did not appear to fail, but the output image does not exist either...', __FILE__, __LINE__);
+			}
+		} else {
+			$this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] failed', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	// public:
+	function OutputThumbnail() {
+		if (!$this->useRawIMoutput && !is_resource($this->gdimg_output)) {
+			$this->DebugMessage('OutputThumbnail() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__);
+			return false;
+		}
+		if (headers_sent()) {
+			return $this->ErrorImage('OutputThumbnail() failed - headers already sent');
+			exit;
+		}
+
+		$downloadfilename = phpthumb_functions::SanitizeFilename(is_string($this->sia) ? $this->sia : ($this->down ? $this->down : 'phpThumb_generated_thumbnail'.'.'.$this->thumbnailFormat));
+		$this->DebugMessage('Content-Disposition header filename set to "'.$downloadfilename.'"', __FILE__, __LINE__);
+		if ($downloadfilename) {
+			header('Content-Disposition: '.($this->down ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"');
+		} else {
+			$this->DebugMessage('failed to send Content-Disposition header because $downloadfilename is empty', __FILE__, __LINE__);
+		}
+
+		if ($this->useRawIMoutput) {
+
+			header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+			echo $this->IMresizedData;
+
+		} else {
+
+			$this->DebugMessage('ImageInterlace($this->gdimg_output, '.intval($this->config_output_interlace).')', __FILE__, __LINE__);
+			ImageInterlace($this->gdimg_output, intval($this->config_output_interlace));
+			switch ($this->thumbnailFormat) {
+				case 'jpeg':
+					header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+					$ImageOutFunction = 'image'.$this->thumbnailFormat;
+					@$ImageOutFunction($this->gdimg_output, '', $this->thumbnailQuality);
+					break;
+
+				case 'png':
+				case 'gif':
+					header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+					$ImageOutFunction = 'image'.$this->thumbnailFormat;
+					@$ImageOutFunction($this->gdimg_output);
+					break;
+
+				case 'bmp':
+					if (!@include_once(dirname(__FILE__).'/phpthumb.bmp.php')) {
+						$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__);
+						return false;
+					}
+					$phpthumb_bmp = new phpthumb_bmp();
+					if (is_object($phpthumb_bmp)) {
+						$bmp_data = $phpthumb_bmp->GD2BMPstring($this->gdimg_output);
+						unset($phpthumb_bmp);
+						if (!$bmp_data) {
+							$this->DebugMessage('$phpthumb_bmp->GD2BMPstring() failed', __FILE__, __LINE__);
+							return false;
+						}
+						header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+						echo $bmp_data;
+					} else {
+						$this->DebugMessage('new phpthumb_bmp() failed', __FILE__, __LINE__);
+						return false;
+					}
+					break;
+
+				case 'ico':
+					if (!@include_once(dirname(__FILE__).'/phpthumb.ico.php')) {
+						$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__);
+						return false;
+					}
+					$phpthumb_ico = new phpthumb_ico();
+					if (is_object($phpthumb_ico)) {
+						$arrayOfOutputImages = array($this->gdimg_output);
+						$ico_data = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages);
+						unset($phpthumb_ico);
+						if (!$ico_data) {
+							$this->DebugMessage('$phpthumb_ico->GD2ICOstring() failed', __FILE__, __LINE__);
+							return false;
+						}
+						header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+						echo $ico_data;
+					} else {
+						$this->DebugMessage('new phpthumb_ico() failed', __FILE__, __LINE__);
+						return false;
+					}
+					break;
+
+				default:
+					$this->DebugMessage('OutputThumbnail failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__);
+					return false;
+					break;
+			}
+
+		}
+		return true;
+	}
+
+
+	// public:
+	function CleanUpCacheDirectory() {
+		if (($this->config_cache_maxage > 0) || ($this->config_cache_maxsize > 0) || ($this->config_cache_maxfiles > 0)) {
+			$CacheDirOldFilesAge  = array();
+			$CacheDirOldFilesSize = array();
+			$AllFilesInCacheDirectory = phpthumb_functions::GetAllFilesInSubfolders($this->config_cache_directory);
+			foreach ($AllFilesInCacheDirectory as $fullfilename) {
+				if (eregi('^phpThumb_cache_', $fullfilename) && file_exists($fullfilename)) {
+					$CacheDirOldFilesAge[$fullfilename] = @fileatime($fullfilename);
+					if ($CacheDirOldFilesAge[$fullfilename] == 0) {
+						$CacheDirOldFilesAge[$fullfilename] = @filemtime($fullfilename);
+					}
+					$CacheDirOldFilesSize[$fullfilename] = @filesize($fullfilename);
+				}
+			}
+			if (empty($CacheDirOldFilesSize)) {
+				return true;
+			}
+			$DeletedKeys = array();
+			foreach ($CacheDirOldFilesSize as $fullfilename => $filesize) {
+				// purge all zero-size files more than an hour old (to prevent trying to delete just-created and/or in-use files)
+				$cutofftime = time() - 3600;
+				if (($filesize == 0) && ($CacheDirOldFilesAge[$fullfilename] < $cutofftime)) {
+					if (@unlink($fullfilename)) {
+						$DeletedKeys[] = $fullfilename;
+						unset($CacheDirOldFilesSize[$fullfilename]);
+						unset($CacheDirOldFilesAge[$fullfilename]);
+					}
+				}
+			}
+			$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys).' zero-byte files', __FILE__, __LINE__);
+			asort($CacheDirOldFilesAge);
+
+			if ($this->config_cache_maxfiles > 0) {
+				$TotalCachedFiles = count($CacheDirOldFilesAge);
+				$DeletedKeys = array();
+				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
+					if ($TotalCachedFiles > $this->config_cache_maxfiles) {
+						if (@unlink($fullfilename)) {
+							$TotalCachedFiles--;
+							$DeletedKeys[] = $fullfilename;
+						}
+					} else {
+						// there are few enough files to keep the rest
+						break;
+					}
+				}
+				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys).' files based on (config_cache_maxfiles='.$this->config_cache_maxfiles.')', __FILE__, __LINE__);
+				foreach ($DeletedKeys as $fullfilename) {
+					unset($CacheDirOldFilesAge[$fullfilename]);
+					unset($CacheDirOldFilesSize[$fullfilename]);
+				}
+			}
+
+			if ($this->config_cache_maxage > 0) {
+				$mindate = time() - $this->config_cache_maxage;
+				$DeletedKeys = array();
+				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
+					if ($filedate > 0) {
+						if ($filedate < $mindate) {
+							if (@unlink($fullfilename)) {
+								$DeletedKeys[] = $fullfilename;
+							}
+						} else {
+							// the rest of the files are new enough to keep
+							break;
+						}
+					}
+				}
+				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys).' files based on (config_cache_maxage='.$this->config_cache_maxage.')', __FILE__, __LINE__);
+				foreach ($DeletedKeys as $fullfilename) {
+					unset($CacheDirOldFilesAge[$fullfilename]);
+					unset($CacheDirOldFilesSize[$fullfilename]);
+				}
+			}
+
+			if ($this->config_cache_maxsize > 0) {
+				$TotalCachedFileSize = array_sum($CacheDirOldFilesSize);
+				$DeletedKeys = array();
+				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
+					if ($TotalCachedFileSize > $this->config_cache_maxsize) {
+						if (@unlink($fullfilename)) {
+							$TotalCachedFileSize -= $CacheDirOldFilesSize[$fullfilename];
+							$DeletedKeys[] = $fullfilename;
+						}
+					} else {
+						// the total filesizes are small enough to keep the rest of the files
+						break;
+					}
+				}
+				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys).' files based on (config_cache_maxsize='.$this->config_cache_maxsize.')', __FILE__, __LINE__);
+				foreach ($DeletedKeys as $fullfilename) {
+					unset($CacheDirOldFilesAge[$fullfilename]);
+					unset($CacheDirOldFilesSize[$fullfilename]);
+				}
+			}
+
+		} else {
+			$this->DebugMessage('skipping CleanUpCacheDirectory() because config set to not use it', __FILE__, __LINE__);
+		}
+		return true;
+	}
+
+	//////////////////////////////////////////////////////////////////////
+
+	function ResolveSource() {
+		if (is_resource($this->gdimg_source)) {
+			$this->DebugMessage('ResolveSource() exiting because is_resource($this->gdimg_source)', __FILE__, __LINE__);
+			return true;
+		}
+		if ($this->rawImageData) {
+			$this->sourceFilename = null;
+			$this->DebugMessage('ResolveSource() exiting because $this->rawImageData is set ('.number_format(strlen($this->rawImageData)).' bytes)', __FILE__, __LINE__);
+			return true;
+		}
+		if ($this->sourceFilename) {
+			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->sourceFilename);
+			$this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'"', __FILE__, __LINE__);
+		} elseif ($this->src) {
+			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src);
+			$this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'" from $this->src ('.$this->src.')', __FILE__, __LINE__);
+		} else {
+			return $this->ErrorImage('$this->sourceFilename and $this->src are both empty');
+		}
+		if ($this->iswindows && ((substr($this->sourceFilename, 0, 2) == '//') || (substr($this->sourceFilename, 0, 2) == '\\\\'))) {
+			// Windows \\share\filename.ext
+		} elseif (eregi('^(f|ht)tps?\://', $this->sourceFilename)) {
+			// URL
+			if ($this->config_http_user_agent) {
+				ini_set('user_agent', $this->config_http_user_agent);
+			}
+		} elseif (!@file_exists($this->sourceFilename)) {
+			return $this->ErrorImage('"'.$this->sourceFilename.'" does not exist');
+		} elseif (!@is_file($this->sourceFilename)) {
+			return $this->ErrorImage('"'.$this->sourceFilename.'" is not a file');
+		}
+		return true;
+	}
+
+	function setOutputFormat() {
+		static $alreadyCalled = false;
+		if ($this->thumbnailFormat && $alreadyCalled) {
+			return true;
+		}
+		$alreadyCalled = true;
+
+		$AvailableImageOutputFormats = array();
+		$AvailableImageOutputFormats[] = 'text';
+		if (@is_readable(dirname(__FILE__).'/phpthumb.ico.php')) {
+			$AvailableImageOutputFormats[] = 'ico';
+		}
+		if (@is_readable(dirname(__FILE__).'/phpthumb.bmp.php')) {
+			$AvailableImageOutputFormats[] = 'bmp';
+		}
+
+		$this->thumbnailFormat = 'ico';
+
+		// Set default output format based on what image types are available
+		if (function_exists('ImageTypes')) {
+			$imagetypes = ImageTypes();
+			if ($imagetypes & IMG_WBMP) {
+				$this->thumbnailFormat         = 'wbmp';
+				$AvailableImageOutputFormats[] = 'wbmp';
+			}
+			if ($imagetypes & IMG_GIF) {
+				$this->thumbnailFormat         = 'gif';
+				$AvailableImageOutputFormats[] = 'gif';
+			}
+			if ($imagetypes & IMG_PNG) {
+				$this->thumbnailFormat         = 'png';
+				$AvailableImageOutputFormats[] = 'png';
+			}
+			if ($imagetypes & IMG_JPG) {
+				$this->thumbnailFormat         = 'jpeg';
+				$AvailableImageOutputFormats[] = 'jpeg';
+			}
+		} else {
+			//return $this->ErrorImage('ImageTypes() does not exist - GD support might not be enabled?');
+			$this->DebugMessage('ImageTypes() does not exist - GD support might not be enabled?',  __FILE__, __LINE__);
+		}
+		if ($this->ImageMagickVersion()) {
+			$IMformats = array('jpeg', 'png', 'gif', 'bmp', 'ico', 'wbmp');
+			$this->DebugMessage('Addding ImageMagick formats to $AvailableImageOutputFormats ('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__);
+			foreach ($IMformats as $key => $format) {
+				$AvailableImageOutputFormats[] = $format;
+			}
+		}
+		$AvailableImageOutputFormats = array_unique($AvailableImageOutputFormats);
+		$this->DebugMessage('$AvailableImageOutputFormats = array('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__);
+
+		if (strtolower($this->config_output_format) == 'jpg') {
+			$this->config_output_format = 'jpeg';
+		}
+		if (strtolower($this->f) == 'jpg') {
+			$this->f = 'jpeg';
+		}
+		if (phpthumb_functions::CaseInsensitiveInArray($this->config_output_format, $AvailableImageOutputFormats)) {
+			// set output format to config default if that format is available
+			$this->DebugMessage('$this->thumbnailFormat set to $this->config_output_format "'.strtolower($this->config_output_format).'"', __FILE__, __LINE__);
+			$this->thumbnailFormat = strtolower($this->config_output_format);
+		} elseif ($this->config_output_format) {
+			$this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->config_output_format ('.strtolower($this->config_output_format).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__);
+		}
+		if ($this->f && (phpthumb_functions::CaseInsensitiveInArray($this->f, $AvailableImageOutputFormats))) {
+			// override output format if $this->f is set and that format is available
+			$this->DebugMessage('$this->thumbnailFormat set to $this->f "'.strtolower($this->f).'"', __FILE__, __LINE__);
+			$this->thumbnailFormat = strtolower($this->f);
+		} elseif ($this->f) {
+			$this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->f ('.strtolower($this->f).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__);
+		}
+
+		// for JPEG images, quality 1 (worst) to 99 (best)
+		// quality < 25 is nasty, with not much size savings - not recommended
+		// problems with 100 - invalid JPEG?
+		$this->thumbnailQuality = max(1, min(99, ($this->q ? $this->q : 75)));
+		$this->DebugMessage('$this->thumbnailQuality set to "'.$this->thumbnailQuality.'"', __FILE__, __LINE__);
+
+		return true;
+	}
+
+	function setCacheDirectory() {
+		// resolve cache directory to absolute pathname
+		$this->DebugMessage('setCacheDirectory() starting with config_cache_directory = "'.$this->config_cache_directory.'"', __FILE__, __LINE__);
+		if (substr($this->config_cache_directory, 0, 1) == '.') {
+			if (eregi('^(f|ht)tps?\://', $this->src)) {
+				if (!$this->config_cache_disable_warning) {
+					$this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') cannot be used for remote images. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php');
+				}
+			} elseif ($this->src) {
+				// resolve relative cache directory to source image
+				$this->config_cache_directory = dirname($this->ResolveFilenameToAbsolute($this->src)).DIRECTORY_SEPARATOR.$this->config_cache_directory;
+			} else {
+				// $this->new is probably set
+			}
+		}
+		if (substr($this->config_cache_directory, -1) == '/') {
+			$this->config_cache_directory = substr($this->config_cache_directory, 0, -1);
+		}
+		if ($this->iswindows) {
+			$this->config_cache_directory = str_replace('/', DIRECTORY_SEPARATOR, $this->config_cache_directory);
+		}
+		if ($this->config_cache_directory) {
+			$real_cache_path = realpath($this->config_cache_directory);
+			if (!$real_cache_path) {
+				$this->DebugMessage('realpath($this->config_cache_directory) failed for "'.$this->config_cache_directory.'"', __FILE__, __LINE__);
+				if (!is_dir($this->config_cache_directory)) {
+					$this->DebugMessage('!is_dir('.$this->config_cache_directory.')', __FILE__, __LINE__);
+				}
+			}
+			if ($real_cache_path) {
+				$this->DebugMessage('setting config_cache_directory to realpath('.$this->config_cache_directory.') = "'.$real_cache_path.'"', __FILE__, __LINE__);
+				$this->config_cache_directory = $real_cache_path;
+			}
+		}
+		if (!is_dir($this->config_cache_directory)) {
+			if (!$this->config_cache_disable_warning) {
+				$this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') does not exist. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php');
+			}
+			$this->DebugMessage('$this->config_cache_directory ('.$this->config_cache_directory.') is not a directory', __FILE__, __LINE__);
+			$this->config_cache_directory = null;
+		} elseif (!@is_writable($this->config_cache_directory)) {
+			$this->DebugMessage('$this->config_cache_directory is not writable ('.$this->config_cache_directory.')', __FILE__, __LINE__);
+		}
+
+		if (!@is_dir($this->config_temp_directory) && !@is_writable($this->config_temp_directory) && @is_dir($this->config_cache_directory) && @is_writable($this->config_cache_directory)) {
+			$this->DebugMessage('setting $this->config_temp_directory = $this->config_cache_directory ('.$this->config_cache_directory.')', __FILE__, __LINE__);
+			$this->config_temp_directory = $this->config_cache_directory;
+		}
+		return true;
+	}
+
+
+	function ResolveFilenameToAbsolute($filename) {
+		if (!$filename) {
+			return false;
+		}
+
+		//if (eregi('^(f|ht)tps?\://', $filename)) {
+		if (eregi('^[a-z0-9]+\:/{1,2}', $filename)) {
+			// eg: http://host/path/file.jpg (HTTP URL)
+			// eg: ftp://host/path/file.jpg  (FTP URL)
+			// eg: data1:/path/file.jpg      (Netware path)
+
+			//$AbsoluteFilename = $filename;
+			return $filename;
+
+		} elseif ($this->iswindows && ($filename{1} == ':')) {
+
+			// absolute pathname (Windows)
+			$AbsoluteFilename = $filename;
+
+		} elseif ($this->iswindows && ((substr($filename, 0, 2) == '//') || (substr($filename, 0, 2) == '\\\\'))) {
+
+			// absolute pathname (Windows)
+			$AbsoluteFilename = $filename;
+
+		} elseif ($filename{0} == '/') {
+
+			if (@is_readable($filename) && !@is_readable($this->config_document_root.$filename)) {
+
+				// absolute filename (*nix)
+				$AbsoluteFilename = $filename;
+
+			} elseif ($filename{1} == '~') {
+
+				// /~user/path
+				if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray($filename)) {
+					$AbsoluteFilename = $ApacheLookupURIarray['filename'];
+				} else {
+					$AbsoluteFilename = realpath($filename);
+					if (@is_readable($AbsoluteFilename)) {
+						$this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.$filename.'", but the correct filename ('.$AbsoluteFilename.') seems to have been resolved with realpath($filename)', __FILE__, __LINE__);
+					} else {
+						return $this->ErrorImage('phpthumb_functions::ApacheLookupURIarray() failed for "'.$filename.'". This has been known to fail on Apache2 - try using the absolute filename for the source image (ex: "/home/user/httpdocs/image.jpg" instead of "/~user/image.jpg")');
+					}
+				}
+
+			} else {
+
+				// relative filename (any OS)
+				if (ereg('^'.preg_quote($this->config_document_root), $filename)) {
+					$AbsoluteFilename = $filename;
+					$this->DebugMessage('ResolveFilenameToAbsolute() NOT prepending $this->config_document_root ('.$this->config_document_root.') to $filename ('.$filename.') resulting in ($AbsoluteFilename = "'.$AbsoluteFilename.'")', __FILE__, __LINE__);
+				} else {
+					$AbsoluteFilename = $this->config_document_root.$filename;
+					$this->DebugMessage('ResolveFilenameToAbsolute() prepending $this->config_document_root ('.$this->config_document_root.') to $filename ('.$filename.') resulting in ($AbsoluteFilename = "'.$AbsoluteFilename.'")', __FILE__, __LINE__);
+				}
+
+			}
+
+		} else {
+
+			// relative to current directory (any OS)
+			$AbsoluteFilename = $this->config_document_root.dirname(@$_SERVER['PHP_SELF']).DIRECTORY_SEPARATOR.$filename;
+			//if (!@file_exists($AbsoluteFilename) && @file_exists(realpath($this->DotPadRelativeDirectoryPath($filename)))) {
+			//	$AbsoluteFilename = realpath($this->DotPadRelativeDirectoryPath($filename));
+			//}
+
+			if (substr(dirname(@$_SERVER['PHP_SELF']), 0, 2) == '/~') {
+				if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray(dirname(@$_SERVER['PHP_SELF']))) {
+					$AbsoluteFilename = $ApacheLookupURIarray['filename'].DIRECTORY_SEPARATOR.$filename;
+				} else {
+					$AbsoluteFilename = realpath('.').DIRECTORY_SEPARATOR.$filename;
+					if (@is_readable($AbsoluteFilename)) {
+						$this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'", but the correct filename ('.$AbsoluteFilename.') seems to have been resolved with realpath(.)/$filename', __FILE__, __LINE__);
+					} else {
+						return $this->ErrorImage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'". This has been known to fail on Apache2 - try using the absolute filename for the source image');
+					}
+				}
+			}
+
+		}
+		if (is_link($AbsoluteFilename)) {
+			$this->DebugMessage('is_link()==true, changing "'.$AbsoluteFilename.'" to "'.readlink($AbsoluteFilename).'"', __FILE__, __LINE__);
+			$AbsoluteFilename = readlink($AbsoluteFilename);
+		}
+		if (realpath($AbsoluteFilename)) {
+			$AbsoluteFilename = realpath($AbsoluteFilename);
+		}
+		if ($this->iswindows) {
+			$AbsoluteFilename = eregi_replace('^'.preg_quote(realpath($this->config_document_root)), realpath($this->config_document_root), $AbsoluteFilename);
+			$AbsoluteFilename = str_replace(DIRECTORY_SEPARATOR, '/', $AbsoluteFilename);
+		}
+		if (!$this->config_allow_src_above_docroot && !ereg('^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', realpath($this->config_document_root))), $AbsoluteFilename)) {
+			$this->DebugMessage('!$this->config_allow_src_above_docroot therefore setting "'.$AbsoluteFilename.'" (outside "'.realpath($this->config_document_root).'") to null', __FILE__, __LINE__);
+			return false;
+		}
+		if (!$this->config_allow_src_above_phpthumb && !ereg('^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', dirname(__FILE__))), $AbsoluteFilename)) {
+			$this->DebugMessage('!$this->config_allow_src_above_phpthumb therefore setting "'.$AbsoluteFilename.'" (outside "'.dirname(__FILE__).'") to null', __FILE__, __LINE__);
+			return false;
+		}
+		return $AbsoluteFilename;
+	}
+
+	function ImageMagickWhichConvert() {
+		static $WhichConvert = null;
+		if (is_null($WhichConvert)) {
+			if ($this->iswindows) {
+				$WhichConvert = false;
+			} else {
+				$WhichConvert = trim(phpthumb_functions::SafeExec('which convert'));
+			}
+		}
+		return $WhichConvert;
+	}
+
+	function ImageMagickCommandlineBase() {
+		static $commandline = null;
+		if (is_null($commandline)) {
+			$commandline = (!is_null($this->config_imagemagick_path) ? $this->config_imagemagick_path : '');
+
+			if ($this->config_imagemagick_path && ($this->config_imagemagick_path != realpath($this->config_imagemagick_path))) {
+				if (@is_executable(realpath($this->config_imagemagick_path))) {
+					$this->DebugMessage('Changing $this->config_imagemagick_path ('.$this->config_imagemagick_path.') to realpath($this->config_imagemagick_path) ('.realpath($this->config_imagemagick_path).')', __FILE__, __LINE__);
+					$this->config_imagemagick_path = realpath($this->config_imagemagick_path);
+				} else {
+					$this->DebugMessage('Leaving $this->config_imagemagick_path as ('.$this->config_imagemagick_path.') because !is_execuatable(realpath($this->config_imagemagick_path)) ('.realpath($this->config_imagemagick_path).')', __FILE__, __LINE__);
+				}
+			}
+			$this->DebugMessage('  file_exists('.$this->config_imagemagick_path.') = '.intval(  @file_exists($this->config_imagemagick_path)), __FILE__, __LINE__);
+			$this->DebugMessage('is_executable('.$this->config_imagemagick_path.') = '.intval(@is_executable($this->config_imagemagick_path)), __FILE__, __LINE__);
+			if (@file_exists($this->config_imagemagick_path)) {
+				$this->DebugMessage('using ImageMagick path from $this->config_imagemagick_path ('.$this->config_imagemagick_path.')', __FILE__, __LINE__);
+				if ($this->iswindows) {
+					$commandline = substr($this->config_imagemagick_path, 0, 2).' && cd "'.str_replace('/', DIRECTORY_SEPARATOR, substr(dirname($this->config_imagemagick_path), 2)).'" && '.basename($this->config_imagemagick_path);
+				} else {
+					$commandline = '"'.$this->config_imagemagick_path.'"';
+				}
+				return $commandline;
+			}
+
+			$which_convert = $this->ImageMagickWhichConvert();
+			$IMversion     = $this->ImageMagickVersion();
+
+			if ($which_convert && ($which_convert{0} == '/') && @file_exists($which_convert)) {
+
+				// `which convert` *should* return the path if "convert" exist, or nothing if it doesn't
+				// other things *may* get returned, like "sh: convert: not found" or "no convert in /usr/local/bin /usr/sbin /usr/bin /usr/ccs/bin"
+				// so only do this if the value returned exists as a file
+				$this->DebugMessage('using ImageMagick path from `which convert` ('.$which_convert.')', __FILE__, __LINE__);
+				$commandline = 'convert';
+
+			} elseif ($IMversion) {
+
+				$this->DebugMessage('setting ImageMagick path to $this->config_imagemagick_path ('.$this->config_imagemagick_path.') ['.$IMversion.']', __FILE__, __LINE__);
+				$commandline = $this->config_imagemagick_path;
+
+			} else {
+
+				$this->DebugMessage('ImageMagickThumbnailToGD() aborting because cannot find convert in $this->config_imagemagick_path ('.$this->config_imagemagick_path.'), and `which convert` returned ('.$which_convert.')', __FILE__, __LINE__);
+				$commandline = '';
+
+			}
+		}
+		return $commandline;
+	}
+
+	function ImageMagickVersion($returnRAW=false) {
+		static $versionstring = null;
+		if (is_null($versionstring)) {
+			$commandline = $this->ImageMagickCommandlineBase();
+			$commandline = (!is_null($commandline) ? $commandline : '');
+
+			$versionstring = array(0=>'', 1=>'');
+			if ($commandline) {
+				$commandline .= ' --version';
+				$this->DebugMessage('ImageMagick version checked with "'.$commandline.'"', __FILE__, __LINE__);
+				$versionstring[1] = trim(phpthumb_functions::SafeExec($commandline));
+				if (eregi('^Version: [^0-9]*([ 0-9\\.\\:Q/]+) (http|file)\:', $versionstring[1], $matches)) {
+					$versionstring[0] = $matches[1];
+				} else {
+					$versionstring[0] = false;
+					$this->DebugMessage('ImageMagick did not return recognized version string ('.$versionstring[1].')', __FILE__, __LINE__);
+				}
+				$this->DebugMessage('ImageMagick convert --version says "'.$matches[0].'"', __FILE__, __LINE__);
+			}
+		}
+		return @$versionstring[intval($returnRAW)];
+	}
+
+	function ImageMagickSwitchAvailable($switchname) {
+		static $IMoptions = null;
+		if (is_null($IMoptions)) {
+			$IMoptions = array();
+			$commandline = $this->ImageMagickCommandlineBase();
+			if (!is_null($commandline)) {
+				$commandline .= ' -help';
+				$IMhelp_lines = explode("\n", phpthumb_functions::SafeExec($commandline));
+				foreach ($IMhelp_lines as $line) {
+					if (ereg('^[\+\-]([a-z\-]+) ', trim($line), $matches)) {
+						$IMoptions[$matches[1]] = true;
+					}
+				}
+			}
+		}
+		if (is_array($switchname)) {
+			$allOK = true;
+			foreach ($switchname as $key => $value) {
+				if (!isset($IMoptions[$value])) {
+					$allOK = false;
+					break;
+				}
+			}
+			$this->DebugMessage('ImageMagickSwitchAvailable('.implode(';', $switchname).') = '.intval($allOK).'', __FILE__, __LINE__);
+		} else {
+			$allOK = isset($IMoptions[$switchname]);
+			$this->DebugMessage('ImageMagickSwitchAvailable('.$switchname.') = '.intval($allOK).'', __FILE__, __LINE__);
+		}
+		return $allOK;
+	}
+
+	function ImageMagickFormatsList() {
+		static $IMformatsList = null;
+		if (is_null($IMformatsList)) {
+			$IMformatsList = '';
+			$commandline = $this->ImageMagickCommandlineBase();
+			if (!is_null($commandline)) {
+				$commandline = dirname($commandline).DIRECTORY_SEPARATOR.str_replace('convert', 'identify', basename($commandline));
+				$commandline .= ' -list format';
+				$IMformatsList = phpthumb_functions::SafeExec($commandline);
+			}
+		}
+		return $IMformatsList;
+	}
+
+	function ImageMagickThumbnailToGD() {
+		// http://www.imagemagick.org/script/command-line-options.php
+
+		$this->useRawIMoutput = true;
+		if (phpthumb_functions::gd_version()) {
+			//$UnAllowedParameters = array('sx', 'sy', 'sw', 'sh', 'xto', 'ra', 'ar', 'bg', 'bc', 'fltr');
+			$UnAllowedParameters = array('xto', 'ra', 'ar', 'bg', 'bc', 'fltr');
+			foreach ($UnAllowedParameters as $parameter) {
+				if (isset($this->$parameter)) {
+					$this->DebugMessage('$this->useRawIMoutput=false because "'.$parameter.'" is set', __FILE__, __LINE__);
+					$this->useRawIMoutput = false;
+					break;
+				}
+			}
+		}
+		$outputFormat = $this->thumbnailFormat;
+		if (phpthumb_functions::gd_version()) {
+			if ($this->useRawIMoutput) {
+				switch ($this->thumbnailFormat) {
+					case 'gif':
+						$ImageCreateFunction = 'ImageCreateFromGIF';
+						$this->is_alpha = true;
+						break;
+					case 'png':
+						$ImageCreateFunction = 'ImageCreateFromPNG';
+						$this->is_alpha = true;
+						break;
+					case 'jpg':
+					case 'jpeg':
+						$ImageCreateFunction = 'ImageCreateFromJPEG';
+						break;
+					default:
+						$outputFormat = 'png';
+						$ImageCreateFunction = 'ImageCreateFromPNG';
+						$this->is_alpha = true;
+						$this->useRawIMoutput = false;
+						break;
+				}
+				if (!function_exists(@$ImageCreateFunction)) {
+					// ImageMagickThumbnailToGD() depends on ImageCreateFromPNG/ImageCreateFromGIF
+					//$this->DebugMessage('ImageMagickThumbnailToGD() aborting because '.@$ImageCreateFunction.'() is not available', __FILE__, __LINE__);
+					$this->useRawIMoutput = true;
+					//return false;
+				}
+			} else {
+				$outputFormat = 'png';
+				$ImageCreateFunction = 'ImageCreateFromPNG';
+				$this->is_alpha = true;
+				$this->useRawIMoutput = false;
+			}
+		}
+
+		// http://freealter.org/doc_distrib/ImageMagick-5.1.1/www/convert.html
+		if (!$this->sourceFilename) {
+			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because $this->sourceFilename is empty', __FILE__, __LINE__);
+			$this->useRawIMoutput = false;
+			return false;
+		}
+		if (ini_get('safe_mode')) {
+			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because safe_mode is enabled', __FILE__, __LINE__);
+			$this->useRawIMoutput = false;
+			return false;
+		}
+
+		$commandline = $this->ImageMagickCommandlineBase();
+		if ($commandline) {
+			if ($IMtempfilename = $this->phpThumb_tempnam()) {
+				$IMtempfilename = realpath($IMtempfilename);
+
+				$IMuseExplicitImageOutputDimensions = false;
+				if ($this->ImageMagickSwitchAvailable('thumbnail') && $this->config_imagemagick_use_thumbnail) {
+					$IMresizeParameter = 'thumbnail';
+				} else {
+					$IMresizeParameter = 'resize';
+
+					// some (older? around 2002) versions of IM won't accept "-resize 100x" but require "-resize 100x100"
+					$commandline_test = $this->ImageMagickCommandlineBase().' logo: -resize 1x "'.$IMtempfilename.'" 2>&1';
+					$IMresult_test = phpthumb_functions::SafeExec($commandline_test);
+					$IMuseExplicitImageOutputDimensions = eregi('image dimensions are zero', $IMresult_test);
+					$this->DebugMessage('IMuseExplicitImageOutputDimensions = '.intval($IMuseExplicitImageOutputDimensions), __FILE__, __LINE__);
+					if ($fp_im_temp = @fopen($IMtempfilename, 'wb')) {
+						// erase temp image so ImageMagick logo doesn't get output if other processing fails
+						fclose($fp_im_temp);
+					}
+				}
+
+
+				if (!is_null($this->dpi) && $this->ImageMagickSwitchAvailable('modulate')) {
+					// for raster source formats only (WMF, PDF, etc)
+					$commandline .= ' -density '.$this->dpi;
+				}
+				ob_start();
+				$getimagesize = GetImageSize($this->sourceFilename);
+				$GetImageSizeError = ob_get_contents();
+				ob_end_clean();
+				if (is_array($getimagesize)) {
+					$this->DebugMessage('GetImageSize('.$this->sourceFilename.') SUCCEEDED: '.serialize($getimagesize), __FILE__, __LINE__);
+				} else {
+					$this->DebugMessage('GetImageSize('.$this->sourceFilename.') FAILED with error "'.$GetImageSizeError.'"', __FILE__, __LINE__);
+				}
+				if (is_array($getimagesize)) {
+					$this->DebugMessage('GetImageSize('.$this->sourceFilename.') returned [w='.$getimagesize[0].';h='.$getimagesize[1].';f='.$getimagesize[2].']', __FILE__, __LINE__);
+					$this->source_width  = $getimagesize[0];
+					$this->source_height = $getimagesize[1];
+					$this->DebugMessage('source dimensions set to '.$this->source_width.'x'.$this->source_height, __FILE__, __LINE__);
+					$this->SetOrientationDependantWidthHeight();
+
+					if (!eregi('('.implode('|', $this->AlphaCapableFormats).')', $outputFormat)) {
+						// not a transparency-capable format
+						$commandline .= ' -background "#'.($this->bg ? $this->bg : 'FFFFFF').'"';
+						if ($getimagesize[2] == 1) {
+							$commandline .= ' -flatten';
+						}
+					}
+					if ($getimagesize[2] == 1) {
+						$commandline .= ' -coalesce'; // may be needed for animated GIFs
+					}
+					if ($this->source_width || $this->source_height) {
+						if ($this->zc) {
+
+							$borderThickness = 0;
+							if (!empty($this->fltr)) {
+								foreach ($this->fltr as $key => $value) {
+									if (ereg('^bord\|([0-9]+)', $value, $matches)) {
+										$borderThickness = $matches[1];
+										break;
+									}
+								}
+							}
+							$wAll = intval(max($this->w, $this->wp, $this->wl, $this->ws)) - (2 * $borderThickness);
+							$hAll = intval(max($this->h, $this->hp, $this->hl, $this->hs)) - (2 * $borderThickness);
+							$imAR = $this->source_width / $this->source_height;
+							//$zcAR = (($wAll && $hAll) ? $wAll / $hAll : $imAR);
+							$zcAR = (($wAll && $hAll) ? $wAll / $hAll : 1);
+//echo '<pre>';
+//var_dump($wAll);
+//var_dump($hAll);
+//var_dump($zcAR);
+							//if (($wAll > $borderThickness) && ($wAll > $borderThickness)) {
+							//	$zcAR = ($wAll - (2 * $borderThickness)) / ($hAll - (2 * $borderThickness));
+							//}
+//echo ($wAll - (2 * $borderThickness))."\n";
+//echo ($hAll - (2 * $borderThickness))."\n";
+//var_dump($zcAR);
+							$side  = phpthumb_functions::nonempty_min($this->source_width, $this->source_height, max($wAll, $hAll));
+							$sideX = phpthumb_functions::nonempty_min($this->source_width,                       $wAll, round($hAll * $zcAR));
+							$sideY = phpthumb_functions::nonempty_min(                     $this->source_height, $hAll, round($wAll / $zcAR));
+
+							$thumbnailH = round(max($sideY, ($sideY * $zcAR) / $imAR));
+							if ($IMuseExplicitImageOutputDimensions) {
+								$commandline .= ' -'.$IMresizeParameter.' '.$thumbnailH.'x'.$thumbnailH;
+							} else {
+								$commandline .= ' -'.$IMresizeParameter.' x'.$thumbnailH;
+							}
+//echo '<pre>';
+//var_dump($this->w);
+//var_dump($this->wp);
+//var_dump($this->wl);
+//var_dump($this->ws);
+//var_dump($wAll);
+//var_dump($side);
+//var_dump($sideX);
+//var_dump($sideY);
+//var_dump($zcAR);
+//var_dump($thumbnailH);
+//print_r($getimagesize);
+//echo '</pre>';
+
+							$commandline .= ' -gravity center';
+
+							if (($wAll > 0) && ($hAll > 0)) {
+								$commandline .= ' -crop '.$wAll.'x'.$hAll.'+0+0';
+							} else {
+								$commandline .= ' -crop '.$side.'x'.$side.'+0+0';
+							}
+							if ($this->ImageMagickSwitchAvailable('repage')) {
+								$commandline .= ' +repage';
+							} else {
+								$this->DebugMessage('Skipping "+repage" because ImageMagick (v'.$this->ImageMagickVersion().') does not support it', __FILE__, __LINE__);
+							}
+
+						} elseif ($this->sw || $this->sh || $this->sx || $this->sy) {
+
+							$commandline .= ' -crop '.($this->sw ? $this->sw : $this->source_width).'x'.($this->sh ? $this->sh : $this->source_height).'+'.$this->sx.'+'.$this->sy;
+							// this is broken for aoe=1, but unsure how to fix. Send advice to info@silisoftware.com
+							if ($this->w || $this->h) {
+								if ($this->ImageMagickSwitchAvailable('repage')) {
+									$commandline .= ' -repage';
+								} else {
+									$this->DebugMessage('Skipping "-repage" because ImageMagick (v'.$this->ImageMagickVersion().') does not support it', __FILE__, __LINE__);
+								}
+								if ($IMuseExplicitImageOutputDimensions) {
+									if ($this->w && !$this->h) {
+										$this->h = ceil($this->w / ($this->source_width / $this->source_height));
+									} elseif ($this->h && !$this->w) {
+										$this->w = ceil($this->h * ($this->source_width / $this->source_height));
+									}
+								}
+								$commandline .= ' -'.$IMresizeParameter.' '.$this->w.'x'.$this->h;
+							}
+
+						} else {
+
+							if ($this->iar && (intval($this->w) > 0) && (intval($this->h) > 0)) {
+								$commandline .= ' -'.$IMresizeParameter.' '.$this->w.'x'.$this->h.'!';
+							} else {
+//echo '<pre>';
+//print_r($getimagesize);
+//echo '</pre>';
+//echo $this->w.'x'.$this->h.'<br>';
+								$this->w = ((($this->aoe || $this->far) && $this->w) ? $this->w : ($this->w ? phpthumb_functions::nonempty_min($this->w, $getimagesize[0]) : ''));
+								$this->h = ((($this->aoe || $this->far) && $this->h) ? $this->h : ($this->h ? phpthumb_functions::nonempty_min($this->h, $getimagesize[1]) : ''));
+//echo $this->w.'x'.$this->h.'<br>';
+								if ($this->w || $this->h) {
+									if ($IMuseExplicitImageOutputDimensions) {
+										if ($this->w && !$this->h) {
+											$this->h = ceil($this->w / ($this->source_width / $this->source_height));
+										} elseif ($this->h && !$this->w) {
+											$this->w = ceil($this->h * ($this->source_width / $this->source_height));
+										}
+									}
+									$commandline .= ' -'.$IMresizeParameter.' '.$this->w.'x'.$this->h;
+								}
+							}
+						}
+					}
+
+				} else {
+
+					$this->DebugMessage('GetImageSize('.$this->sourceFilename.') failed', __FILE__, __LINE__);
+					if ($this->w || $this->h) {
+						if ($IMuseExplicitImageOutputDimensions) {
+							// unknown source aspect ration, just put large number and hope IM figures it out
+							$commandline .= ' -'.$IMresizeParameter.' '.($this->w ? $this->w : '9999').'x'.($this->h ? $this->h : '9999');
+						} else {
+							$commandline .= ' -'.$IMresizeParameter.' '.$this->w.'x'.$this->h;
+						}
+						if ($this->iar && (intval($this->w) > 0) && (intval($this->h) > 0)) {
+							$commandline .= '!';
+						}
+					}
+
+				}
+				foreach ($this->fltr as $filterkey => $filtercommand) {
+					@list($command, $parameter) = explode('|', $filtercommand, 2);
+					switch ($command) {
+						case 'brit':
+							if ($this->ImageMagickSwitchAvailable('modulate')) {
+								$commandline .= ' -modulate '.(100 + $parameter).',100,100';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'cont':
+							if ($this->ImageMagickSwitchAvailable('contrast')) {
+								$contDiv10 = round($parameter / 10);
+								if ($contDiv10 > 0) {
+									for ($i = 0; $i < $contDiv10; $i++) {
+										$commandline .= ' -contrast'; // increase contrast by 10%
+									}
+								} elseif ($contDiv10 < 0) {
+									for ($i = $contDiv10; $i < 0; $i++) {
+										$commandline .= ' +contrast'; // decrease contrast by 10%
+									}
+								} else {
+									// do nothing
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'ds':
+							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
+								if ($parameter == 100) {
+									$commandline .= ' -colorspace GRAY -modulate 100,0,100';
+								} else {
+									$commandline .= ' -modulate 100,'.(100 - $parameter).',100';
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'sat':
+							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
+								if ($parameter == -100) {
+									$commandline .= ' -colorspace GRAY -modulate 100,0,100';
+								} else {
+									$commandline .= ' -modulate 100,'.(100 + $parameter).',100';
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'gray':
+							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
+								$commandline .= ' -colorspace GRAY -modulate 100,0,100';
+								//$commandline .= ' -colorspace GRAY';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'clr':
+							if ($this->ImageMagickSwitchAvailable(array('fill', 'colorize'))) {
+								@list($amount, $color) = explode('|', $parameter);
+								$commandline .= ' -fill #'.$color.' -colorize '.$amount;
+							}
+							break;
+
+						case 'sep':
+							if ($this->ImageMagickSwitchAvailable('sepia-tone')) {
+								@list($amount, $color) = explode('|', $parameter);
+								$amount = ($amount ? $amount : 80);
+								if (!$color) {
+									$commandline .= ' -sepia-tone '.$amount.'%';
+									unset($this->fltr[$filterkey]);
+								}
+							}
+							break;
+
+						case 'gam':
+							if ($this->ImageMagickSwitchAvailable('gamma')) {
+								$commandline .= ' -gamma '.$parameter;
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'neg':
+							if ($this->ImageMagickSwitchAvailable('negate')) {
+								$commandline .= ' -negate';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'th':
+							if ($this->ImageMagickSwitchAvailable(array('threshold', 'dither', 'monochrome'))) {
+								$commandline .= ' -threshold '.round($parameter / 2.55).'% -dither -monochrome';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'rcd':
+							if ($this->ImageMagickSwitchAvailable(array('colors', 'dither'))) {
+								@list($colors, $dither) = explode('|', $parameter);
+								$colors = ($colors                ?  (int) $colors : 256);
+								$dither  = ((strlen($dither) > 0) ? (bool) $dither : true);
+								$commandline .= ' -colors '.max($colors, 8); // ImageMagick will otherwise fail with "cannot quantize to fewer than 8 colors"
+								$commandline .= ($dither ? ' -dither' : ' +dither');
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'flip':
+							if ($this->ImageMagickSwitchAvailable(array('flip', 'flop'))) {
+								if (strpos(strtolower($parameter), 'x') !== false) {
+									$commandline .= ' -flop';
+								}
+								if (strpos(strtolower($parameter), 'y') !== false) {
+									$commandline .= ' -flip';
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'edge':
+							if ($this->ImageMagickSwitchAvailable('edge')) {
+								$parameter = ($parameter ? $parameter : 2);
+								$commandline .= ' -edge '.($parameter ? $parameter : 1);
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'emb':
+							if ($this->ImageMagickSwitchAvailable(array('emboss', 'negate'))) {
+								$parameter = ($parameter ? $parameter : 2);
+								$commandline .= ' -emboss '.$parameter;
+								if ($parameter < 2) {
+									$commandline .= ' -negate'; // ImageMagick negates the image for some reason with '-emboss 1';
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'lvl':
+							@list($band, $method, $threshold) = explode('|', $parameter);
+							$band      = ($band ? ereg_replace('[^RGBA\\*]', '', strtoupper($band)) : '*');
+							$method    = ((strlen($method) > 0)    ? intval($method)                :   2);
+							$threshold = ((strlen($threshold) > 0) ? floatval($threshold)           : 0.1);
+
+							$band = ereg_replace('[^RGBA\\*]', '', strtoupper($band));
+
+							if (($method > 1) && !$this->ImageMagickSwitchAvailable(array('channel', 'contrast-stretch'))) {
+								// Because ImageMagick processing happens before PHP-GD filters, and because some
+								// clipping is involved in the "lvl" filter, if "lvl" happens before "wb" then the
+								// "wb" filter will have (almost) no effect. Therefore, if "wb" is enabled then
+								// force the "lvl" filter to be processed by GD, not ImageMagick.
+								foreach ($this->fltr as $fltr_key => $fltr_value) {
+									list($fltr_cmd) = explode('|', $fltr_value);
+									if ($fltr_cmd == 'wb') {
+										$this->DebugMessage('Setting "lvl" filter method to "0" (from "'.$method.'") because white-balance filter also enabled', __FILE__, __LINE__);
+										$method = 0;
+									}
+								}
+							}
+
+							switch ($method) {
+								case 0: // internal RGB
+								case 1: // internal grayscale
+									break;
+								case 2: // ImageMagick "contrast-stretch"
+									if ($this->ImageMagickSwitchAvailable('contrast-stretch')) {
+										$thiscommand = ' -contrast-stretch '.$threshold.'%';
+										$commandline .= (($band == '*') ? $thiscommand : ' -channel '.strtoupper($band).$thiscommand.' +channel');
+										unset($this->fltr[$filterkey]);
+									}
+									break;
+								case 3: // ImageMagick "normalize"
+									if ($this->ImageMagickSwitchAvailable('normalize')) {
+										$thiscommand = ' -normalize';
+										$commandline .= (($band == '*') ? $thiscommand : ' -channel '.strtoupper($band).$thiscommand.' +channel');
+										unset($this->fltr[$filterkey]);
+									}
+									break;
+								default:
+									$this->DebugMessage('unsupported method ('.$method.') for "lvl" filter', __FILE__, __LINE__);
+									break;
+							}
+							if (isset($this->fltr[$filterkey]) && ($method > 1)) {
+								$this->fltr[$filterkey] = $command.'|'.$band.'|0|'.$threshold;
+								$this->DebugMessage('filter "lvl" remapped from method "'.$method.'" to method "0" because ImageMagick support is missing', __FILE__, __LINE__);
+							}
+							break;
+
+						case 'wb':
+							if ($this->ImageMagickSwitchAvailable(array('channel', 'contrast-stretch'))) {
+								@list($threshold) = explode('|', $parameter);
+								$threshold = (is_float($threshold) ? $threshold : 0.1);
+								$commandline .= ' -channel R -contrast-stretch '.$threshold.'%';
+								$commandline .= ' -channel G -contrast-stretch '.$threshold.'%';
+								$commandline .= ' -channel B -contrast-stretch '.$threshold.'%';
+								$commandline .= ' +channel';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'blur':
+							if ($this->ImageMagickSwitchAvailable('blur')) {
+								@list($radius) = explode('|', $parameter);
+								$radius = ($radius ? $radius : 1);
+								$commandline .= ' -blur '.$radius;
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'gblr':
+							if ($this->ImageMagickSwitchAvailable('gaussian')) {
+								@list($radius) = explode('|', $parameter);
+								$radius = ($radius ? $radius : 1);
+								$commandline .= ' -gaussian '.$radius;
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'usm':
+							if ($this->ImageMagickSwitchAvailable('unsharp')) {
+								@list($amount, $radius, $threshold) = explode('|', $parameter);
+								$amount    = ($amount            ? $amount    : 80);
+								$radius    = ($radius            ? $radius    : 0.5);
+								$threshold = (strlen($threshold) ? $threshold : 3);
+								$commandline .= ' -unsharp '.number_format(($radius * 2) - 1, 2).'x1+'.number_format($amount / 100, 2).'+'.number_format($threshold / 100, 2);
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'bord':
+							if ($this->ImageMagickSwitchAvailable(array('border', 'bordercolor', 'thumbnail', 'crop'))) {
+								if (!$this->zc) {
+									@list($width, $rX, $rY, $color) = explode('|', $parameter);
+									if ($width && !$rX && !$rY) {
+										if (!phpthumb_functions::IsHexColor($color)) {
+											$color = ($this->bc ? $this->bc : '000000');
+										}
+										$commandline .= ' -border '.$width.' -bordercolor "#'.$color.'"';
+										if (ereg(' \-crop ([0-9]+)x([0-9]+)\+0\+0 ', $commandline, $matches)) {
+											$commandline = str_replace(' -crop '.$matches[1].'x'.$matches[2].'+0+0 ', ' -crop '.($matches[1] - (2 * $width)).'x'.($matches[2] - (2 * $width)).'+0+0 ', $commandline);
+										} elseif (ereg(' \-'.$IMresizeParameter.' ([0-9]+)x([0-9]+) ', $commandline, $matches)) {
+											$commandline = str_replace(' -'.$IMresizeParameter.' '.$matches[1].'x'.$matches[2].' ', ' -'.$IMresizeParameter.' '.($matches[1] - (2 * $width)).'x'.($matches[2] - (2 * $width)).' ', $commandline);
+										}
+										unset($this->fltr[$filterkey]);
+									}
+								}
+							}
+							break;
+
+						case 'crop':
+							break;
+
+						case 'sblr':
+							break;
+
+						case 'mean':
+							break;
+
+						case 'smth':
+							break;
+
+						case 'bvl':
+							break;
+
+						case 'wmi':
+							break;
+
+						case 'wmt':
+							break;
+
+						case 'over':
+							break;
+
+						case 'hist':
+							break;
+
+						case 'fram':
+							break;
+
+						case 'drop':
+							break;
+
+						case 'mask':
+							break;
+
+						case 'elip':
+							break;
+
+						case 'ric':
+							break;
+
+					}
+					if (!isset($this->fltr[$filterkey])) {
+						$this->DebugMessage('Processed $this->fltr['.$filterkey.'] ('.$filtercommand.') with ImageMagick', __FILE__, __LINE__);
+					} else {
+						$this->DebugMessage('Skipping $this->fltr['.$filterkey.'] ('.$filtercommand.') with ImageMagick', __FILE__, __LINE__);
+					}
+				}
+				$this->DebugMessage('Remaining $this->fltr after ImageMagick: ('.$this->phpThumbDebugVarDump($this->fltr).')', __FILE__, __LINE__);
+
+				if (eregi('jpe?g', $outputFormat) && $this->q) {
+					if ($this->ImageMagickSwitchAvailable(array('quality', 'interlace'))) {
+						$commandline .= ' -quality '.$this->thumbnailQuality;
+						if ($this->config_output_interlace) {
+							// causes weird things with animated GIF... leave for JPEG only
+							$commandline .= ' -interlace line '; // Use Line or Plane to create an interlaced PNG or GIF or progressive JPEG image
+						}
+					}
+				}
+				$commandline .= ' "'.str_replace('/', DIRECTORY_SEPARATOR, $this->sourceFilename).(($outputFormat == 'gif') ? '' : '['.intval($this->sfn).']').'"'; // [0] means first frame of (GIF) animation, can be ignored
+				$commandline .= ' '.$outputFormat.':"'.$IMtempfilename.'"';
+				if (!$this->iswindows) {
+					$commandline .= ' 2>&1';
+				}
+				$this->DebugMessage('ImageMagick called as ('.$commandline.')', __FILE__, __LINE__);
+				$IMresult = phpthumb_functions::SafeExec($commandline);
+				clearstatcache();
+				if (!@file_exists($IMtempfilename) || !@filesize($IMtempfilename)) {
+					$this->fatalerror = 'ImageMagick failed with message ('.trim($IMresult).')';
+					$this->DebugMessage('ImageMagick failed with message ('.trim($IMresult).')', __FILE__, __LINE__);
+					if ($this->iswindows && !$IMresult) {
+						$this->DebugMessage('Check to make sure that PHP has read+write permissions to "'.dirname($IMtempfilename).'"', __FILE__, __LINE__);
+					}
+
+				} else {
+
+					$this->IMresizedData = file_get_contents($IMtempfilename);
+					$getimagesize_imresized = @GetImageSize($IMtempfilename);
+					if (($this->config_max_source_pixels > 0) && (($getimagesize_imresized[0] * $getimagesize_imresized[1]) > $this->config_max_source_pixels)) {
+						$this->DebugMessage('skipping ImageMagickThumbnailToGD::'.$ImageCreateFunction.'() because IM output is too large ('.$getimagesize_imresized[0].'x'.$getimagesize_imresized[0].' = '.($getimagesize_imresized[0] * $getimagesize_imresized[1]).' > '.$this->config_max_source_pixels.')', __FILE__, __LINE__);
+					} elseif (function_exists(@$ImageCreateFunction) && ($this->gdimg_source = @$ImageCreateFunction($IMtempfilename))) {
+//header('Content-Type: image/png');
+//ImageSaveAlpha($this->gdimg_source, true);
+//ImagePNG($this->gdimg_source);
+//exit;
+						$this->source_width  = ImageSX($this->gdimg_source);
+						$this->source_height = ImageSY($this->gdimg_source);
+						$this->DebugMessage('ImageMagickThumbnailToGD::'.$ImageCreateFunction.'() succeeded, $this->gdimg_source is now ('.$this->source_width.'x'.$this->source_height.')', __FILE__, __LINE__);
+						$this->DebugMessage('ImageMagickThumbnailToGD() returning $IMresizedData ('.strlen($this->IMresizedData).' bytes)', __FILE__, __LINE__);
+					} else {
+						$this->useRawIMoutput = true;
+						$this->DebugMessage('$this->useRawIMoutput set to TRUE because '.@$ImageCreateFunction.'('.$IMtempfilename.') failed', __FILE__, __LINE__);
+					}
+					@unlink($IMtempfilename);
+					return true;
+
+				}
+				unlink($IMtempfilename);
+
+			} else {
+				$this->DebugMessage('ImageMagickThumbnailToGD() aborting, phpThumb_tempnam() failed', __FILE__, __LINE__);
+			}
+		} else {
+			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because ImageMagickCommandlineBase() failed', __FILE__, __LINE__);
+		}
+		$this->useRawIMoutput = false;
+		return false;
+	}
+
+
+	function Rotate() {
+		if ($this->ra || $this->ar) {
+			if (!function_exists('ImageRotate')) {
+				$this->DebugMessage('!function_exists(ImageRotate)', __FILE__, __LINE__);
+				return false;
+			}
+			if (!include_once(dirname(__FILE__).'/phpthumb.filters.php')) {
+				$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.filters.php" which is required for applying filters ('.implode(';', $this->fltr).')', __FILE__, __LINE__);
+				return false;
+			}
+
+			$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
+			if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
+				return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
+			}
+
+			$rotate_angle = 0;
+			if ($this->ra) {
+
+				$rotate_angle = floatval($this->ra);
+
+			} else {
+
+				if ($this->ar == 'x') {
+					if (phpthumb_functions::version_compare_replacement(phpversion(), '4.2.0', '>=')) {
+						if ($this->sourceFilename) {
+							if (function_exists('exif_read_data')) {
+								if ($exif_data = @exif_read_data($this->sourceFilename, 'IFD0')) {
+									// http://sylvana.net/jpegcrop/exif_orientation.html
+									switch (@$exif_data['Orientation']) {
+										case 1:
+											$rotate_angle = 0;
+											break;
+										case 3:
+											$rotate_angle = 180;
+											break;
+										case 6:
+											$rotate_angle = 270;
+											break;
+										case 8:
+											$rotate_angle = 90;
+											break;
+
+										default:
+											$this->DebugMessage('EXIF auto-rotate failed because unknown $exif_data[Orientation] "'.@$exif_data['Orientation'].'"', __FILE__, __LINE__);
+											return false;
+											break;
+									}
+									$this->DebugMessage('EXIF auto-rotate set to '.$rotate_angle.' degrees ($exif_data[Orientation] = "'.@$exif_data['Orientation'].'")', __FILE__, __LINE__);
+								} else {
+									$this->DebugMessage('failed: exif_read_data('.$this->sourceFilename.')', __FILE__, __LINE__);
+									return false;
+								}
+							} else {
+								$this->DebugMessage('!function_exists(exif_read_data)', __FILE__, __LINE__);
+								return false;
+							}
+						} else {
+							$this->DebugMessage('Cannot auto-rotate from EXIF data because $this->sourceFilename is empty', __FILE__, __LINE__);
+							return false;
+						}
+					} else {
+						$this->DebugMessage('Cannot auto-rotate from EXIF data because PHP is less than v4.2.0 ('.phpversion().')', __FILE__, __LINE__);
+						return false;
+					}
+				} elseif (($this->ar == 'l') && ($this->source_height > $this->source_width)) {
+					$rotate_angle = 270;
+				} elseif (($this->ar == 'L') && ($this->source_height > $this->source_width)) {
+					$rotate_angle = 90;
+				} elseif (($this->ar == 'p') && ($this->source_width > $this->source_height)) {
+					$rotate_angle = 90;
+				} elseif (($this->ar == 'P') && ($this->source_width > $this->source_height)) {
+					$rotate_angle = 270;
+				}
+
+			}
+			if ($rotate_angle % 90) {
+				$this->is_alpha = true;
+			}
+			phpthumb_filters::ImprovedImageRotate($this->gdimg_source, $rotate_angle, $this->config_background_hexcolor, $this->bg);
+			$this->source_width  = ImageSX($this->gdimg_source);
+			$this->source_height = ImageSY($this->gdimg_source);
+		}
+		return true;
+	}
+
+
+	function FixedAspectRatio() {
+		// optional fixed-dimension images (regardless of aspect ratio)
+
+		if (!$this->far) {
+			// do nothing
+			return true;
+		}
+
+		if (!$this->w || !$this->h) {
+			return false;
+		}
+		$this->thumbnail_width  = $this->w;
+		$this->thumbnail_height = $this->h;
+		$this->is_alpha = true;
+		if ($this->thumbnail_image_width >= $this->thumbnail_width) {
+
+			if ($this->w) {
+				$aspectratio = $this->thumbnail_image_height / $this->thumbnail_image_width;
+				$this->thumbnail_image_height = round($this->thumbnail_image_width * $aspectratio);
+				$this->thumbnail_height = ($this->h ? $this->h : $this->thumbnail_image_height);
+			} elseif ($this->thumbnail_image_height < $this->thumbnail_height) {
+				$this->thumbnail_image_height = $this->thumbnail_height;
+				$this->thumbnail_image_width  = round($this->thumbnail_image_height / $aspectratio);
+			}
+
+		} else {
+			if ($this->h) {
+				$aspectratio = $this->thumbnail_image_width / $this->thumbnail_image_height;
+				$this->thumbnail_image_width = round($this->thumbnail_image_height * $aspectratio);
+			} elseif ($this->thumbnail_image_width < $this->thumbnail_width) {
+				$this->thumbnail_image_width = $this->thumbnail_width;
+				$this->thumbnail_image_height  = round($this->thumbnail_image_width / $aspectratio);
+			}
+
+		}
+		return true;
+	}
+
+
+	function AntiOffsiteLinking() {
+		// Optional anti-offsite hijacking of the thumbnail script
+		$allow = true;
+		if ($allow && $this->config_nooffsitelink_enabled && (@$_SERVER['HTTP_REFERER'] || $this->config_nooffsitelink_require_refer)) {
+			$this->DebugMessage('AntiOffsiteLinking() checking $_SERVER[HTTP_REFERER] "'.@$_SERVER['HTTP_REFERER'].'"', __FILE__, __LINE__);
+			foreach ($this->config_nooffsitelink_valid_domains as $key => $valid_domain) {
+				// $_SERVER['HTTP_HOST'] contains the port number, so strip it out here to make default configuration work
+				list($clean_domain) = explode(':', $valid_domain);
+				$this->config_nooffsitelink_valid_domains[$key] = $clean_domain;
+			}
+			$parsed_url = parse_url(@$_SERVER['HTTP_REFERER']);
+			if (!phpthumb_functions::CaseInsensitiveInArray(@$parsed_url['host'], $this->config_nooffsitelink_valid_domains)) {
+				$allow = false;
+				$erase   = $this->config_nooffsitelink_erase_image;
+				$message = $this->config_nooffsitelink_text_message;
+$this->ErrorImage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is NOT in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')');
+exit;
+				$this->DebugMessage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is NOT in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__);
+			} else {
+				$this->DebugMessage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__);
+			}
+		}
+
+		if ($allow && $this->config_nohotlink_enabled && eregi('^(f|ht)tps?\://', $this->src)) {
+			$parsed_url = parse_url($this->src);
+			if (!phpthumb_functions::CaseInsensitiveInArray(@$parsed_url['host'], $this->config_nohotlink_valid_domains)) {
+				// This domain is not allowed
+				$allow = false;
+				$erase   = $this->config_nohotlink_erase_image;
+				$message = $this->config_nohotlink_text_message;
+				$this->DebugMessage('AntiOffsiteLinking() - "'.$parsed_url['host'].'" is NOT in $this->config_nohotlink_valid_domains ('.implode(';', $this->config_nohotlink_valid_domains).')', __FILE__, __LINE__);
+			} else {
+				$this->DebugMessage('AntiOffsiteLinking() - "'.$parsed_url['host'].'" is in $this->config_nohotlink_valid_domains ('.implode(';', $this->config_nohotlink_valid_domains).')', __FILE__, __LINE__);
+			}
+		}
+
+		if ($allow) {
+			$this->DebugMessage('AntiOffsiteLinking() says this is allowed', __FILE__, __LINE__);
+			return true;
+		}
+
+		if (!phpthumb_functions::IsHexColor($this->config_error_bgcolor)) {
+			return $this->ErrorImage('Invalid hex color string "'.$this->config_error_bgcolor.'" for $this->config_error_bgcolor');
+		}
+		if (!phpthumb_functions::IsHexColor($this->config_error_textcolor)) {
+			return $this->ErrorImage('Invalid hex color string "'.$this->config_error_textcolor.'" for $this->config_error_textcolor');
+		}
+		if ($erase) {
+
+			return $this->ErrorImage($message, $this->thumbnail_width, $this->thumbnail_height, $this->config_error_bgcolor, $this->config_error_textcolor, $this->config_error_fontsize);
+
+		} else {
+
+			$this->config_nooffsitelink_watermark_src = $this->ResolveFilenameToAbsolute($this->config_nooffsitelink_watermark_src);
+			if (is_file($this->config_nooffsitelink_watermark_src)) {
+
+				if (!include_once(dirname(__FILE__).'/phpthumb.filters.php')) {
+					$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.filters.php" which is required for applying watermark', __FILE__, __LINE__);
+					return false;
+				}
+				$watermark_img = $this->ImageCreateFromStringReplacement(file_get_contents($this->config_nooffsitelink_watermark_src));
+				$phpthumbFilters = new phpthumb_filters();
+				$phpthumbFilters->phpThumbObject = $this;
+				$opacity = 50;
+				$margin  = 5;
+				$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $watermark_img, '*', $opacity, $margin);
+				ImageDestroy($watermark_img);
+				unset($phpthumbFilters);
+
+			} else {
+
+				$nohotlink_text_array = explode("\n", wordwrap($message, floor($this->thumbnail_width / ImageFontWidth($this->config_error_fontsize)), "\n"));
+				$nohotlink_text_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_error_textcolor);
+
+				$topoffset = round(($this->thumbnail_height - (count($nohotlink_text_array) * ImageFontHeight($this->config_error_fontsize))) / 2);
+
+				$rowcounter = 0;
+				$this->DebugMessage('AntiOffsiteLinking() writing '.count($nohotlink_text_array).' lines of text "'.$message.'" (in #'.$this->config_error_textcolor.') on top of image', __FILE__, __LINE__);
+				foreach ($nohotlink_text_array as $textline) {
+					$leftoffset = max(0, round(($this->thumbnail_width - (strlen($textline) * ImageFontWidth($this->config_error_fontsize))) / 2));
+					ImageString($this->gdimg_output, $this->config_error_fontsize, $leftoffset, $topoffset + ($rowcounter++ * ImageFontHeight($this->config_error_fontsize)), $textline, $nohotlink_text_color);
+				}
+
+			}
+
+		}
+		return true;
+	}
+
+
+	function AlphaChannelFlatten() {
+		if (!$this->is_alpha) {
+			// image doesn't have alpha transparency, no need to flatten
+			$this->DebugMessage('skipping AlphaChannelFlatten() because !$this->is_alpha', __FILE__, __LINE__);
+			return false;
+		}
+		switch ($this->thumbnailFormat) {
+			case 'png':
+			case 'ico':
+				// image has alpha transparency, but output as PNG or ICO which can handle it
+				$this->DebugMessage('skipping AlphaChannelFlatten() because ($this->thumbnailFormat == "'.$this->thumbnailFormat.'")', __FILE__, __LINE__);
+				return false;
+				break;
+
+			case 'gif':
+				// image has alpha transparency, but output as GIF which can handle only single-color transparency
+				$CurrentImageColorTransparent = ImageColorTransparent($this->gdimg_output);
+				if ($CurrentImageColorTransparent == -1) {
+					// no transparent color defined
+
+					if (phpthumb_functions::gd_version() < 2.0) {
+						$this->DebugMessage('AlphaChannelFlatten() failed because GD version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+						return false;
+					}
+
+					if ($img_alpha_mixdown_dither = @ImageCreateTrueColor(ImageSX($this->gdimg_output), ImageSY($this->gdimg_output))) {
+
+						for ($i = 0; $i <= 255; $i++) {
+							$dither_color[$i] = ImageColorAllocate($img_alpha_mixdown_dither, $i, $i, $i);
+						}
+
+						// scan through current truecolor image copy alpha channel to temp image as grayscale
+						for ($x = 0; $x < $this->thumbnail_width; $x++) {
+							for ($y = 0; $y < $this->thumbnail_height; $y++) {
+								$PixelColor = phpthumb_functions::GetPixelColor($this->gdimg_output, $x, $y);
+								ImageSetPixel($img_alpha_mixdown_dither, $x, $y, $dither_color[($PixelColor['alpha'] * 2)]);
+							}
+						}
+
+						// dither alpha channel grayscale version down to 2 colors
+						ImageTrueColorToPalette($img_alpha_mixdown_dither, true, 2);
+
+						// reduce color palette to 256-1 colors (leave one palette position for transparent color)
+						ImageTrueColorToPalette($this->gdimg_output, true, 255);
+
+						// allocate a new color for transparent color index
+						$TransparentColor = ImageColorAllocate($this->gdimg_output, 1, 254, 253);
+						ImageColorTransparent($this->gdimg_output, $TransparentColor);
+
+						// scan through alpha channel image and note pixels with >50% transparency
+						$TransparentPixels = array();
+						for ($x = 0; $x < $this->thumbnail_width; $x++) {
+							for ($y = 0; $y < $this->thumbnail_height; $y++) {
+								$AlphaChannelPixel = phpthumb_functions::GetPixelColor($img_alpha_mixdown_dither, $x, $y);
+								if ($AlphaChannelPixel['red'] > 127) {
+									ImageSetPixel($this->gdimg_output, $x, $y, $TransparentColor);
+								}
+							}
+						}
+						ImageDestroy($img_alpha_mixdown_dither);
+
+						$this->DebugMessage('AlphaChannelFlatten() set image to 255+1 colors with transparency for GIF output', __FILE__, __LINE__);
+						return true;
+
+					} else {
+						$this->DebugMessage('AlphaChannelFlatten() failed ImageCreate('.ImageSX($this->gdimg_output).', '.ImageSY($this->gdimg_output).')', __FILE__, __LINE__);
+						return false;
+					}
+
+				} else {
+					// a single transparent color already defined, leave as-is
+					$this->DebugMessage('skipping AlphaChannelFlatten() because ($this->thumbnailFormat == "'.$this->thumbnailFormat.'") and ImageColorTransparent returned "'.$CurrentImageColorTransparent.'"', __FILE__, __LINE__);
+					return true;
+				}
+				break;
+		}
+		$this->DebugMessage('continuing AlphaChannelFlatten() for output format "'.$this->thumbnailFormat.'"', __FILE__, __LINE__);
+		// image has alpha transparency, and is being output in a format that doesn't support it -- flatten
+		if ($gdimg_flatten_temp = phpthumb_functions::ImageCreateFunction($this->thumbnail_width, $this->thumbnail_height)) {
+
+			$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
+			if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
+				return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
+			}
+			$background_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_background_hexcolor);
+			ImageFilledRectangle($gdimg_flatten_temp, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $background_color);
+			ImageCopy($gdimg_flatten_temp, $this->gdimg_output, 0, 0, 0, 0, $this->thumbnail_width, $this->thumbnail_height);
+
+			ImageAlphaBlending($this->gdimg_output, true);
+			ImageSaveAlpha($this->gdimg_output, false);
+			ImageColorTransparent($this->gdimg_output, -1);
+			ImageCopy($this->gdimg_output, $gdimg_flatten_temp, 0, 0, 0, 0, $this->thumbnail_width, $this->thumbnail_height);
+
+			ImageDestroy($gdimg_flatten_temp);
+			return true;
+
+		} else {
+			$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	function ApplyFilters() {
+		if ($this->fltr && is_array($this->fltr)) {
+			if (!include_once(dirname(__FILE__).'/phpthumb.filters.php')) {
+				$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.filters.php" which is required for applying filters ('.implode(';', $this->fltr).')', __FILE__, __LINE__);
+				return false;
+			}
+			$phpthumbFilters = new phpthumb_filters();
+			$phpthumbFilters->phpThumbObject = $this;
+			foreach ($this->fltr as $filtercommand) {
+				@list($command, $parameter) = explode('|', $filtercommand, 2);
+				$this->DebugMessage('Attempting to process filter command "'.$command.'('.$parameter.')"', __FILE__, __LINE__);
+				switch ($command) {
+					case 'brit': // Brightness
+						$phpthumbFilters->Brightness($this->gdimg_output, $parameter);
+						break;
+
+					case 'cont': // Contrast
+						$phpthumbFilters->Contrast($this->gdimg_output, $parameter);
+						break;
+
+					case 'ds': // Desaturation
+						$phpthumbFilters->Desaturate($this->gdimg_output, $parameter, '');
+						break;
+
+					case 'sat': // Saturation
+						$phpthumbFilters->Saturation($this->gdimg_output, $parameter, '');
+						break;
+
+					case 'gray': // Grayscale
+						$phpthumbFilters->Grayscale($this->gdimg_output);
+						break;
+
+					case 'clr': // Colorize
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Colorize() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							break;
+						}
+						@list($amount, $color) = explode('|', $parameter);
+						$phpthumbFilters->Colorize($this->gdimg_output, $amount, $color);
+						break;
+
+					case 'sep': // Sepia
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Sepia() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							break;
+						}
+						@list($amount, $color) = explode('|', $parameter);
+						$phpthumbFilters->Sepia($this->gdimg_output, $amount, $color);
+						break;
+
+					case 'gam': // Gamma correction
+						$phpthumbFilters->Gamma($this->gdimg_output, $parameter);
+						break;
+
+					case 'neg': // Negative colors
+						$phpthumbFilters->Negative($this->gdimg_output);
+						break;
+
+					case 'th': // Threshold
+						$phpthumbFilters->Threshold($this->gdimg_output, $parameter);
+						break;
+
+					case 'rcd': // ReduceColorDepth
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping ReduceColorDepth() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							break;
+						}
+						@list($colors, $dither) = explode('|', $parameter);
+						$colors = ($colors                ?  (int) $colors : 256);
+						$dither  = ((strlen($dither) > 0) ? (bool) $dither : true);
+						$phpthumbFilters->ReduceColorDepth($this->gdimg_output, $colors, $dither);
+						break;
+
+					case 'flip': // Flip
+						$phpthumbFilters->Flip($this->gdimg_output, (strpos(strtolower($parameter), 'x') !== false), (strpos(strtolower($parameter), 'y') !== false));
+						break;
+
+					case 'edge': // EdgeDetect
+						$phpthumbFilters->EdgeDetect($this->gdimg_output);
+						break;
+
+					case 'emb': // Emboss
+						$phpthumbFilters->Emboss($this->gdimg_output);
+						break;
+
+					case 'bvl': // Bevel
+						@list($width, $color1, $color2) = explode('|', $parameter);
+						$phpthumbFilters->Bevel($this->gdimg_output, $width, $color1, $color2);
+						break;
+
+					case 'lvl': // autoLevels
+						@list($band, $method, $threshold) = explode('|', $parameter);
+						$band      = ($band ? ereg_replace('[^RGBA\\*]', '', strtoupper($band)) : '*');
+						$method    = ((strlen($method) > 0)    ? intval($method)                :   2);
+						$threshold = ((strlen($threshold) > 0) ? floatval($threshold)           : 0.1);
+
+						$phpthumbFilters->HistogramStretch($this->gdimg_output, $band, $method, $threshold);
+						break;
+
+					case 'wb': // WhiteBalance
+						$phpthumbFilters->WhiteBalance($this->gdimg_output, $parameter);
+						break;
+
+					case 'hist': // Histogram overlay
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping HistogramOverlay() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							break;
+						}
+						@list($bands, $colors, $width, $height, $alignment, $opacity, $margin_x, $margin_y) = explode('|', $parameter);
+						$bands     = ($bands     ? $bands     :  '*');
+						$colors    = ($colors    ? $colors    :   '');
+						$width     = ($width     ? $width     : 0.25);
+						$height    = ($height    ? $height    : 0.25);
+						$alignment = ($alignment ? $alignment : 'BR');
+						$opacity   = ($opacity   ? $opacity   :   50);
+						$margin_x  = ($margin_x  ? $margin_x  :    5);
+						$margin_y  = $margin_y; // just to note it wasn't forgotten, but let the value always pass unchanged
+						$phpthumbFilters->HistogramOverlay($this->gdimg_output, $bands, $colors, $width, $height, $alignment, $opacity, $margin_x, $margin_y);
+						break;
+
+					case 'fram': // Frame
+						@list($frame_width, $edge_width, $color_frame, $color1, $color2) = explode('|', $parameter);
+						$phpthumbFilters->Frame($this->gdimg_output, $frame_width, $edge_width, $color_frame, $color1, $color2);
+						break;
+
+					case 'drop': // DropShadow
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping DropShadow() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						$this->is_alpha = true;
+						@list($distance, $width, $color, $angle, $fade) = explode('|', $parameter);
+						$phpthumbFilters->DropShadow($this->gdimg_output, $distance, $width, $color, $angle, $fade);
+						break;
+
+					case 'mask': // Mask cropping
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Mask() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						$mask_filename = $this->ResolveFilenameToAbsolute($parameter);
+						if (@is_readable($mask_filename) && ($fp_mask = @fopen($mask_filename, 'rb'))) {
+							$MaskImageData = '';
+							do {
+								$buffer = fread($fp_mask, 8192);
+								$MaskImageData .= $buffer;
+							} while (strlen($buffer) > 0);
+							fclose($fp_mask);
+							if ($gdimg_mask = $this->ImageCreateFromStringReplacement($MaskImageData)) {
+								$this->is_alpha = true;
+								$phpthumbFilters->ApplyMask($gdimg_mask, $this->gdimg_output);
+								ImageDestroy($gdimg_mask);
+							} else {
+								$this->DebugMessage('ImageCreateFromStringReplacement() failed for "'.$mask_filename.'"', __FILE__, __LINE__);
+							}
+						} else {
+							$this->DebugMessage('Cannot open mask file "'.$mask_filename.'"', __FILE__, __LINE__);
+						}
+						break;
+
+					case 'elip': // Elipse cropping
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Elipse() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						$this->is_alpha = true;
+						$phpthumbFilters->Elipse($this->gdimg_output);
+						break;
+
+					case 'ric': // RoundedImageCorners
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping RoundedImageCorners() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						@list($radius_x, $radius_y) = explode('|', $parameter);
+						if (($radius_x < 1) || ($radius_y < 1)) {
+							$this->DebugMessage('Skipping RoundedImageCorners('.$radius_x.', '.$radius_y.') because x/y radius is less than 1', __FILE__, __LINE__);
+							break;
+						}
+						$this->is_alpha = true;
+						$phpthumbFilters->RoundedImageCorners($this->gdimg_output, $radius_x, $radius_y);
+						break;
+
+					case 'crop': // Crop
+						@list($left, $right, $top, $bottom) = explode('|', $parameter);
+						$phpthumbFilters->Crop($this->gdimg_output, $left, $right, $top, $bottom);
+						break;
+
+					case 'bord': // Border
+						@list($border_width, $radius_x, $radius_y, $hexcolor_border) = explode('|', $parameter);
+						$this->is_alpha = true;
+						$phpthumbFilters->ImageBorder($this->gdimg_output, $border_width, $radius_x, $radius_y, $hexcolor_border);
+						break;
+
+					case 'over': // Overlay
+						@list($filename, $underlay, $margin, $opacity) = explode('|', $parameter);
+						$underlay = (bool) ($underlay              ? $underlay : false);
+						$margin   =        ((strlen($margin)  > 0) ? $margin   : ($underlay ? 0.1 : 0.0));
+						$opacity  =        ((strlen($opacity) > 0) ? $opacity  : 100);
+						if (($margin > 0) && ($margin < 1)) {
+							$margin = min(0.499, $margin);
+						} elseif (($margin > -1) && ($margin < 0)) {
+							$margin = max(-0.499, $margin);
+						}
+
+						$filename = $this->ResolveFilenameToAbsolute($filename);
+						if (@is_readable($filename) && ($fp_watermark = @fopen($filename, 'rb'))) {
+							$WatermarkImageData = '';
+							do {
+								$buffer = fread($fp_watermark, 8192);
+								$WatermarkImageData .= $buffer;
+							} while (strlen($buffer) > 0);
+							fclose($fp_watermark);
+							if ($img_watermark = $this->ImageCreateFromStringReplacement($WatermarkImageData)) {
+								if ($margin < 1) {
+									$resized_x = max(1, ImageSX($this->gdimg_output) - round(2 * (ImageSX($this->gdimg_output) * $margin)));
+									$resized_y = max(1, ImageSY($this->gdimg_output) - round(2 * (ImageSY($this->gdimg_output) * $margin)));
+								} else {
+									$resized_x = max(1, ImageSX($this->gdimg_output) - round(2 * $margin));
+									$resized_y = max(1, ImageSY($this->gdimg_output) - round(2 * $margin));
+								}
+
+								if ($underlay) {
+
+									if ($img_watermark_resized = phpthumb_functions::ImageCreateFunction(ImageSX($this->gdimg_output), ImageSY($this->gdimg_output))) {
+										ImageAlphaBlending($img_watermark_resized, false);
+										ImageSaveAlpha($img_watermark_resized, true);
+										$this->ImageResizeFunction($img_watermark_resized, $img_watermark, 0, 0, 0, 0, ImageSX($img_watermark_resized), ImageSY($img_watermark_resized), ImageSX($img_watermark), ImageSY($img_watermark));
+										if ($img_source_resized = phpthumb_functions::ImageCreateFunction($resized_x, $resized_y)) {
+											ImageAlphaBlending($img_source_resized, false);
+											ImageSaveAlpha($img_source_resized, true);
+											$this->ImageResizeFunction($img_source_resized, $this->gdimg_output, 0, 0, 0, 0, ImageSX($img_source_resized), ImageSY($img_source_resized), ImageSX($this->gdimg_output), ImageSY($this->gdimg_output));
+											$phpthumbFilters->WatermarkOverlay($img_watermark_resized, $img_source_resized, 'C', $opacity, $margin);
+											ImageCopy($this->gdimg_output, $img_watermark_resized, 0, 0, 0, 0, ImageSX($this->gdimg_output), ImageSY($this->gdimg_output));
+										} else {
+											$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.$resized_x.', '.$resized_y.')', __FILE__, __LINE__);
+										}
+										ImageDestroy($img_watermark_resized);
+									} else {
+										$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.ImageSX($this->gdimg_output).', '.ImageSY($this->gdimg_output).')', __FILE__, __LINE__);
+									}
+
+								} else { // overlay
+
+									if ($img_watermark_resized = phpthumb_functions::ImageCreateFunction($resized_x, $resized_y)) {
+										ImageAlphaBlending($img_watermark_resized, false);
+										ImageSaveAlpha($img_watermark_resized, true);
+										$this->ImageResizeFunction($img_watermark_resized, $img_watermark, 0, 0, 0, 0, ImageSX($img_watermark_resized), ImageSY($img_watermark_resized), ImageSX($img_watermark), ImageSY($img_watermark));
+										$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $img_watermark_resized, 'C', $opacity, $margin);
+										ImageDestroy($img_watermark_resized);
+									} else {
+										$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.$resized_x.', '.$resized_y.')', __FILE__, __LINE__);
+									}
+
+								}
+								ImageDestroy($img_watermark);
+
+							} else {
+								$this->DebugMessage('ImageCreateFromStringReplacement() failed for "'.$filename.'"', __FILE__, __LINE__);
+							}
+						} else {
+							$this->DebugMessage('Cannot open overlay file "'.$filename.'"', __FILE__, __LINE__);
+						}
+						break;
+
+					case 'wmi': // WaterMarkImage
+						@list($filename, $alignment, $opacity, $margin['x'], $margin['y']) = explode('|', $parameter);
+						$alignment   = ($alignment           ? $alignment   : 'BR');
+						$opacity     = (strlen($opacity)     ? $opacity     : 50);
+						$margins = array('x', 'y');
+						foreach ($margins as $xy) {
+							$margin[$xy] = (strlen($margin[$xy]) ? $margin[$xy] : 5);
+							if (($margin[$xy] > 0) && ($margin[$xy] < 1)) {
+								$margin[$xy] = min(0.499, $margin[$xy]);
+							} elseif (($margin[$xy] > -1) && ($margin[$xy] < 0)) {
+								$margin[$xy] = max(-0.499, $margin[$xy]);
+							}
+						}
+
+						$filename = $this->ResolveFilenameToAbsolute($filename);
+						if (@is_readable($filename)) {
+							if ($img_watermark = $this->ImageCreateFromFilename($filename)) {
+								// great
+								$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $img_watermark, $alignment, $opacity, $margin['x'], $margin['y']);
+								ImageDestroy($img_watermark);
+							} else {
+								$this->DebugMessage('ImageCreateFromFilename() failed for "'.$filename.'"', __FILE__, __LINE__);
+							}
+						} else {
+							$this->DebugMessage('!is_readable('.$filename.')', __FILE__, __LINE__);
+						}
+						break;
+
+					case 'wmt': // WaterMarkText
+						@list($text, $size, $alignment, $hex_color, $ttffont, $opacity, $margin, $angle, $bg_color, $bg_opacity, $fillextend) = explode('|', $parameter);
+						$text       = ($text            ? $text       : '');
+						$size       = ($size            ? $size       : 3);
+						$alignment  = ($alignment       ? $alignment  : 'BR');
+						$hex_color  = ($hex_color       ? $hex_color  : '000000');
+						$ttffont    = ($ttffont         ? $ttffont    : '');
+						$opacity    = (strlen($opacity) ? $opacity    : 50);
+						$margin     = (strlen($margin)  ? $margin     : 5);
+						$angle      = (strlen($angle)   ? $angle      : 0);
+						$bg_color   = ($bg_color        ? $bg_color   : false);
+						$bg_opacity = ($bg_opacity      ? $bg_opacity : 0);
+						$fillextend = ($fillextend      ? $fillextend : '');
+
+						if (basename($ttffont) == $ttffont) {
+							$ttffont = realpath($this->config_ttf_directory.DIRECTORY_SEPARATOR.$ttffont);
+						} else {
+							$ttffont = $this->ResolveFilenameToAbsolute($ttffont);
+						}
+						$phpthumbFilters->WatermarkText($this->gdimg_output, $text, $size, $alignment, $hex_color, $ttffont, $opacity, $margin, $angle, $bg_color, $bg_opacity, $fillextend);
+						break;
+
+					case 'blur': // Blur
+						@list($radius) = explode('|', $parameter);
+						$radius = ($radius ? $radius : 1);
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Blur() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						$phpthumbFilters->Blur($this->gdimg_output, $radius);
+						break;
+
+					case 'gblr': // Gaussian Blur
+						$phpthumbFilters->BlurGaussian($this->gdimg_output);
+						break;
+
+					case 'sblr': // Selective Blur
+						$phpthumbFilters->BlurSelective($this->gdimg_output);
+						break;
+
+					case 'mean': // MeanRemoval blur
+						$phpthumbFilters->MeanRemoval($this->gdimg_output);
+						break;
+
+					case 'smth': // Smooth blur
+						$phpthumbFilters->Smooth($this->gdimg_output, $parameter);
+						break;
+
+					case 'usm': // UnSharpMask sharpening
+						@list($amount, $radius, $threshold) = explode('|', $parameter);
+						$amount    = ($amount            ? $amount    : 80);
+						$radius    = ($radius            ? $radius    : 0.5);
+						$threshold = (strlen($threshold) ? $threshold : 3);
+						if (phpthumb_functions::gd_version() >= 2.0) {
+							ob_start();
+							if (!@include_once(dirname(__FILE__).'/phpthumb.unsharp.php')) {
+								$include_error = ob_get_contents();
+								if ($include_error) {
+									$this->DebugMessage('include_once("'.dirname(__FILE__).'/phpthumb.unsharp.php") generated message: "'.$include_error.'"', __FILE__, __LINE__);
+								}
+								$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.unsharp.php" which is required for unsharp masking', __FILE__, __LINE__);
+								ob_end_clean();
+								return false;
+							}
+							ob_end_clean();
+							phpUnsharpMask::applyUnsharpMask($this->gdimg_output, $amount, $radius, $threshold);
+						} else {
+							$this->DebugMessage('Skipping unsharp mask because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						break;
+
+					case 'rot': // ROTate
+						@list($angle, $bgcolor) = explode('|', $parameter);
+						$phpthumbFilters->ImprovedImageRotate($this->gdimg_output, $angle, $bgcolor);
+						break;
+				}
+				$this->DebugMessage('Finished processing filter command "'.$command.'('.$parameter.')"', __FILE__, __LINE__);
+			}
+		}
+		return true;
+	}
+
+
+	function MaxFileSize() {
+		if (phpthumb_functions::gd_version() < 2) {
+			$this->DebugMessage('Skipping MaxFileSize() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+			return false;
+		}
+		if ($this->maxb > 0) {
+			switch ($this->thumbnailFormat) {
+				case 'png':
+				case 'gif':
+					$imgRenderFunction = 'image'.$this->thumbnailFormat;
+
+					ob_start();
+					$imgRenderFunction($this->gdimg_output);
+					$imgdata = ob_get_contents();
+					ob_end_clean();
+
+					if (strlen($imgdata) > $this->maxb) {
+						for ($i = 8; $i >= 1; $i--) {
+							$tempIMG = ImageCreateTrueColor(ImageSX($this->gdimg_output), ImageSY($this->gdimg_output));
+							ImageCopy($tempIMG, $this->gdimg_output, 0, 0, 0, 0, ImageSX($this->gdimg_output), ImageSY($this->gdimg_output));
+							ImageTrueColorToPalette($tempIMG, true, pow(2, $i));
+							ob_start();
+							$imgRenderFunction($tempIMG);
+							$imgdata = ob_get_contents();
+							ob_end_clean();
+
+							if (strlen($imgdata) <= $this->maxb) {
+								ImageTrueColorToPalette($this->gdimg_output, true, pow(2, $i));
+								break;
+							}
+						}
+					}
+					if (strlen($imgdata) > $this->maxb) {
+						ImageTrueColorToPalette($this->gdimg_output, true, pow(2, $i));
+						return false;
+					}
+					break;
+
+				case 'jpeg':
+					ob_start();
+					ImageJPEG($this->gdimg_output);
+					$imgdata = ob_get_contents();
+					ob_end_clean();
+
+					$OriginalJPEGquality = $this->thumbnailQuality;
+					if (strlen($imgdata) > $this->maxb) {
+						for ($i = 3; $i < 20; $i++) {
+							$q = round(100 * (1 - log10($i / 2)));
+							ob_start();
+							ImageJPEG($this->gdimg_output, '', $q);
+							$imgdata = ob_get_contents();
+							ob_end_clean();
+
+							$this->thumbnailQuality = $q;
+							if (strlen($imgdata) <= $this->maxb) {
+								break;
+							}
+						}
+					}
+					if (strlen($imgdata) > $this->maxb) {
+						return false;
+					}
+					break;
+
+				default:
+					return false;
+					break;
+			}
+		}
+		return true;
+	}
+
+
+	function CalculateThumbnailDimensions() {
+//echo $this->source_width.'x'.$this->source_height.'<hr>';
+		$this->thumbnailCropX = ($this->sx ? (($this->sx >= 1) ? $this->sx : round($this->sx * $this->source_width))  : 0);
+//echo $this->thumbnailCropX.'<br>';
+		$this->thumbnailCropY = ($this->sy ? (($this->sy >= 1) ? $this->sy : round($this->sy * $this->source_height)) : 0);
+//echo $this->thumbnailCropY.'<br>';
+		$this->thumbnailCropW = ($this->sw ? (($this->sw >= 1) ? $this->sw : round($this->sw * $this->source_width))  : $this->source_width);
+//echo $this->thumbnailCropW.'<br>';
+		$this->thumbnailCropH = ($this->sh ? (($this->sh >= 1) ? $this->sh : round($this->sh * $this->source_height)) : $this->source_height);
+//echo $this->thumbnailCropH.'<hr>';
+
+		// limit source area to original image area
+		$this->thumbnailCropW = max(1, min($this->thumbnailCropW, $this->source_width  - $this->thumbnailCropX));
+		$this->thumbnailCropH = max(1, min($this->thumbnailCropH, $this->source_height - $this->thumbnailCropY));
+
+		$this->DebugMessage('CalculateThumbnailDimensions() [x,y,w,h] initially set to ['.$this->thumbnailCropX.','.$this->thumbnailCropY.','.$this->thumbnailCropW.','.$this->thumbnailCropH.']', __FILE__, __LINE__);
+
+
+		if ($this->zc && $this->w && $this->h) {
+			// Zoom Crop
+			// retain proportional resizing we did above, but crop off larger dimension so smaller
+			// dimension fully fits available space
+
+			$scaling_X = $this->source_width  / $this->w;
+			$scaling_Y = $this->source_height / $this->h;
+			if ($scaling_X > $scaling_Y) {
+				// some of the width will need to be cropped
+				$allowable_width = $this->source_width / $scaling_X * $scaling_Y;
+				$this->thumbnailCropW = round($allowable_width);
+				$this->thumbnailCropX = round(($this->source_width - $allowable_width) / 2);
+
+			} elseif ($scaling_Y > $scaling_X) {
+				// some of the height will need to be cropped
+				$allowable_height = $this->source_height / $scaling_Y * $scaling_X;
+				$this->thumbnailCropH = round($allowable_height);
+				$this->thumbnailCropY = round(($this->source_height - $allowable_height) / 2);
+
+			} else {
+				// image fits perfectly, no cropping needed
+			}
+			$this->thumbnail_width  = $this->w;
+			$this->thumbnail_height = $this->h;
+			$this->thumbnail_image_width  = $this->thumbnail_width;
+			$this->thumbnail_image_height = $this->thumbnail_height;
+
+		} elseif ($this->iar && $this->w && $this->h) {
+
+			// Ignore Aspect Ratio
+			// stretch image to fit exactly 'w' x 'h'
+			$this->thumbnail_width  = $this->w;
+			$this->thumbnail_height = $this->h;
+			$this->thumbnail_image_width  = $this->thumbnail_width;
+			$this->thumbnail_image_height = $this->thumbnail_height;
+
+		} else {
+
+			$original_aspect_ratio = $this->thumbnailCropW / $this->thumbnailCropH;
+			if ($this->aoe) {
+				if ($this->w && $this->h) {
+					$maxwidth  = min($this->w, $this->h * $original_aspect_ratio);
+					$maxheight = min($this->h, $this->w / $original_aspect_ratio);
+				} elseif ($this->w) {
+					$maxwidth  = $this->w;
+					$maxheight = $this->w / $original_aspect_ratio;
+				} elseif ($this->h) {
+					$maxwidth  = $this->h * $original_aspect_ratio;
+					$maxheight = $this->h;
+				} else {
+					$maxwidth  = $this->thumbnailCropW;
+					$maxheight = $this->thumbnailCropH;
+				}
+			} else {
+				$maxwidth  = phpthumb_functions::nonempty_min($this->w, $this->thumbnailCropW, $this->config_output_maxwidth);
+				$maxheight = phpthumb_functions::nonempty_min($this->h, $this->thumbnailCropH, $this->config_output_maxheight);
+//echo $maxwidth.'x'.$maxheight.'<br>';
+				$maxwidth  = min($maxwidth, $maxheight * $original_aspect_ratio);
+				$maxheight = min($maxheight, $maxwidth / $original_aspect_ratio);
+//echo $maxwidth.'x'.$maxheight.'<hr>';
+			}
+
+			$this->thumbnail_image_width  = $maxwidth;
+			$this->thumbnail_image_height = $maxheight;
+			$this->thumbnail_width  = $maxwidth;
+			$this->thumbnail_height = $maxheight;
+
+			$this->FixedAspectRatio();
+		}
+
+		$this->thumbnail_width  = max(1, floor($this->thumbnail_width));
+		$this->thumbnail_height = max(1, floor($this->thumbnail_height));
+		return true;
+	}
+
+
+	function CreateGDoutput() {
+		$this->CalculateThumbnailDimensions();
+
+		// Create the GD image (either true-color or 256-color, depending on GD version)
+		$this->gdimg_output = phpthumb_functions::ImageCreateFunction($this->thumbnail_width, $this->thumbnail_height);
+
+		// Images that have transparency must have the background filled with the configured 'bg' color
+		// otherwise the transparent color will appear as black
+		ImageSaveAlpha($this->gdimg_output, true);
+		if ($this->is_alpha && phpthumb_functions::gd_version() >= 2) {
+
+			ImageAlphaBlending($this->gdimg_output, false);
+			$output_full_alpha = phpthumb_functions::ImageColorAllocateAlphaSafe($this->gdimg_output, 255, 255, 255, 127);
+			ImageFilledRectangle($this->gdimg_output, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $output_full_alpha);
+
+		} else {
+
+			$current_transparent_color = ImageColorTransparent($this->gdimg_source);
+			if ($this->bg || (@$current_transparent_color >= 0)) {
+
+				$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
+				if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
+					return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
+				}
+				$background_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_background_hexcolor);
+				ImageFilledRectangle($this->gdimg_output, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $background_color);
+
+			}
+
+		}
+		$this->DebugMessage('CreateGDoutput() returning canvas "'.$this->thumbnail_width.'x'.$this->thumbnail_height.'"', __FILE__, __LINE__);
+		return true;
+	}
+
+	function SetOrientationDependantWidthHeight() {
+		$this->DebugMessage('SetOrientationDependantWidthHeight() starting with "'.$this->source_width.'"x"'.$this->source_height.'"', __FILE__, __LINE__);
+		if ($this->source_height > $this->source_width) {
+			// portrait
+			$this->w = phpthumb_functions::OneOfThese($this->wp, $this->w, $this->ws, $this->wl);
+			$this->h = phpthumb_functions::OneOfThese($this->hp, $this->h, $this->hs, $this->hl);
+		} elseif ($this->source_height < $this->source_width) {
+			// landscape
+			$this->w = phpthumb_functions::OneOfThese($this->wl, $this->w, $this->ws, $this->wp);
+			$this->h = phpthumb_functions::OneOfThese($this->hl, $this->h, $this->hs, $this->hp);
+		} else {
+			// square
+			$this->w = phpthumb_functions::OneOfThese($this->ws, $this->w, $this->wl, $this->wp);
+			$this->h = phpthumb_functions::OneOfThese($this->hs, $this->h, $this->hl, $this->hp);
+		}
+		//$this->w = round($this->w ? $this->w : (($this->h && $this->source_height) ? $this->h * $this->source_width  / $this->source_height : $this->w));
+		//$this->h = round($this->h ? $this->h : (($this->w && $this->source_width)  ? $this->w * $this->source_height / $this->source_width  : $this->h));
+		$this->DebugMessage('SetOrientationDependantWidthHeight() setting w="'.intval($this->w).'", h="'.intval($this->h).'"', __FILE__, __LINE__);
+		return true;
+	}
+
+	function ExtractEXIFgetImageSize() {
+		$this->DebugMessage('starting ExtractEXIFgetImageSize()', __FILE__, __LINE__);
+
+		if (is_resource($this->gdimg_source)) {
+
+			$this->source_width  = ImageSX($this->gdimg_source);
+			$this->source_height = ImageSY($this->gdimg_source);
+
+			$this->SetOrientationDependantWidthHeight();
+
+		} elseif ($this->rawImageData && !$this->sourceFilename) {
+
+			$this->DebugMessage('bypassing EXIF and GetImageSize sections because $this->rawImageData is set and $this->sourceFilename is not set', __FILE__, __LINE__);
+
+		}
+
+		if (is_null($this->getimagesizeinfo)) {
+			$this->getimagesizeinfo = @GetImageSize($this->sourceFilename);
+		}
+
+		if (!empty($this->getimagesizeinfo)) {
+			// great
+			$this->getimagesizeinfo['filesize'] = @filesize($this->sourceFilename);
+		} elseif (!$this->rawImageData) {
+			$this->DebugMessage('GetImageSize("'.$this->sourceFilename.'") failed', __FILE__, __LINE__);
+		}
+
+		if ($this->config_prefer_imagemagick) {
+			if ($this->ImageMagickThumbnailToGD()) {
+				return true;
+			}
+			$this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__);
+		}
+
+		$this->source_width  = $this->getimagesizeinfo[0];
+		$this->source_height = $this->getimagesizeinfo[1];
+
+		$this->SetOrientationDependantWidthHeight();
+
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.2.0', '>=') && function_exists('exif_read_data')) {
+			$this->exif_raw_data = @exif_read_data($this->sourceFilename, 0, true);
+		}
+		if (function_exists('exif_thumbnail') && ($this->getimagesizeinfo[2] == 2)) {
+			// Extract EXIF info from JPEGs
+
+			$this->exif_thumbnail_width  = '';
+			$this->exif_thumbnail_height = '';
+			$this->exif_thumbnail_type   = '';
+
+			// The parameters width, height and imagetype are available since PHP v4.3.0
+			if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=')) {
+
+				$this->exif_thumbnail_data = @exif_thumbnail($this->sourceFilename, $this->exif_thumbnail_width, $this->exif_thumbnail_height, $this->exif_thumbnail_type);
+
+			} else {
+
+				// older versions of exif_thumbnail output an error message but NOT return false on failure
+				ob_start();
+				$this->exif_thumbnail_data = exif_thumbnail($this->sourceFilename);
+				$exit_thumbnail_error = ob_get_contents();
+				ob_end_clean();
+				if (!$exit_thumbnail_error && $this->exif_thumbnail_data) {
+
+					if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
+						$this->exif_thumbnail_width  = ImageSX($gdimg_exif_temp);
+						$this->exif_thumbnail_height = ImageSY($gdimg_exif_temp);
+						$this->exif_thumbnail_type   = 2; // (2 == JPEG) before PHP v4.3.0 only JPEG format EXIF thumbnails are returned
+						unset($gdimg_exif_temp);
+					} else {
+						return $this->ErrorImage('Failed - $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data) in '.__FILE__.' on line '.__LINE__);
+					}
+
+				}
+
+			}
+
+		} elseif (!function_exists('exif_thumbnail')) {
+
+			$this->DebugMessage('exif_thumbnail() does not exist, cannot extract EXIF thumbnail', __FILE__, __LINE__);
+			return false;
+
+		}
+
+		$this->DebugMessage('EXIF thumbnail extraction: (size='.strlen($this->exif_thumbnail_data).'; type="'.$this->exif_thumbnail_type.'"; '.intval($this->exif_thumbnail_width).'x'.intval($this->exif_thumbnail_height).')', __FILE__, __LINE__);
+
+		// see if EXIF thumbnail can be used directly with no processing
+		if ($this->config_use_exif_thumbnail_for_speed && $this->exif_thumbnail_data) {
+			while (true) {
+				if (!$this->xto) {
+					$source_ar = $this->source_width / $this->source_height;
+					$exif_ar = $this->exif_thumbnail_width / $this->exif_thumbnail_height;
+					if (number_format($source_ar, 2) != number_format($exif_ar, 2)) {
+						$this->DebugMessage('not using EXIF thumbnail because $source_ar != $exif_ar ('.$source_ar.' != '.$exif_ar.')', __FILE__, __LINE__);
+						break;
+					}
+					if ($this->w && ($this->w != $this->exif_thumbnail_width)) {
+						$this->DebugMessage('not using EXIF thumbnail because $this->w != $this->exif_thumbnail_width ('.$this->w.' != '.$this->exif_thumbnail_width.')', __FILE__, __LINE__);
+						break;
+					}
+					if ($this->h && ($this->h != $this->exif_thumbnail_height)) {
+						$this->DebugMessage('not using EXIF thumbnail because $this->h != $this->exif_thumbnail_height ('.$this->h.' != '.$this->exif_thumbnail_height.')', __FILE__, __LINE__);
+						break;
+					}
+					$CannotBeSetParameters = array('sx', 'sy', 'sh', 'sw', 'far', 'bg', 'bc', 'fltr', 'phpThumbDebug');
+					foreach ($CannotBeSetParameters as $parameter) {
+						if ($this->$parameter) {
+							break 2;
+						}
+					}
+				}
+
+				$this->DebugMessage('setting $this->gdimg_source = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data)', __FILE__, __LINE__);
+				$this->gdimg_source = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data);
+				$this->source_width  = ImageSX($this->gdimg_source);
+				$this->source_height = ImageSY($this->gdimg_source);
+				return true;
+			}
+		}
+
+		if (($this->config_max_source_pixels > 0) && (($this->source_width * $this->source_height) > $this->config_max_source_pixels)) {
+
+			// Source image is larger than would fit in available PHP memory.
+			// If ImageMagick is installed, use it to generate the thumbnail.
+			// Else, if an EXIF thumbnail is available, use that as the source image.
+			// Otherwise, no choice but to fail with an error message
+			$this->DebugMessage('image is '.$this->source_width.'x'.$this->source_height.' and therefore contains more pixels ('.($this->source_width * $this->source_height).') than $this->config_max_source_pixels setting ('.$this->config_max_source_pixels.')', __FILE__, __LINE__);
+			if (!$this->config_prefer_imagemagick && $this->ImageMagickThumbnailToGD()) {
+				// excellent, we have a thumbnailed source image
+				return true;
+			}
+
+		}
+		return true;
+	}
+
+
+	function SetCacheFilename() {
+		if (!is_null($this->cache_filename)) {
+			$this->DebugMessage('$this->cache_filename already set, skipping SetCacheFilename()', __FILE__, __LINE__);
+			return true;
+		}
+		$this->setOutputFormat();
+		$this->setCacheDirectory();
+		if (!$this->config_cache_directory) {
+			$this->DebugMessage('SetCacheFilename() failed because $this->config_cache_directory is empty', __FILE__, __LINE__);
+			return false;
+		}
+
+		if (!$this->sourceFilename && !$this->rawImageData && $this->src) {
+			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src);
+		}
+
+		if ($this->config_cache_default_only_suffix && $this->sourceFilename) {
+			// simplified cache filenames:
+			// only use default parameters in phpThumb.config.php
+			// substitute source filename into * in $this->config_cache_default_only_suffix
+			// (eg: '*_thumb' becomes 'picture_thumb.jpg')
+			if (strpos($this->config_cache_default_only_suffix, '*') === false) {
+				$this->DebugMessage('aborting simplified caching filename because no * in "'.$this->config_cache_default_only_suffix.'"', __FILE__, __LINE__);
+			} else {
+				eregi('(.+)(\.[a-z0-9]+)?$', basename($this->sourceFilename), $matches);
+				$this->cache_filename = $this->config_cache_directory.DIRECTORY_SEPARATOR.rawurlencode(str_replace('*', @$matches[1], $this->config_cache_default_only_suffix)).'.'.strtolower($this->thumbnailFormat);
+				return true;
+			}
+		}
+
+		$this->cache_filename = '';
+		$broad_directory_name = '';
+		if ($this->new) {
+			$broad_directory_name = strtolower(md5($this->new));
+			$this->cache_filename .= '_new'.$broad_directory_name;
+		} elseif ($this->md5s) {
+			// source image MD5 hash provided
+			$this->DebugMessage('SetCacheFilename() _raw set from $this->md5s = "'.$this->md5s.'"', __FILE__, __LINE__);
+			$broad_directory_name = $this->md5s;
+			$this->cache_filename .= '_raw'.$this->md5s;
+		} elseif (!$this->src && $this->rawImageData) {
+			$this->DebugMessage('SetCacheFilename() _raw set from md5($this->rawImageData) = "'.md5($this->rawImageData).'"', __FILE__, __LINE__);
+			$broad_directory_name = strtolower(md5($this->rawImageData));
+			$this->cache_filename .= '_raw'.$broad_directory_name;
+		} else {
+			$this->DebugMessage('SetCacheFilename() _src set from md5($this->sourceFilename) "'.$this->sourceFilename.'" = "'.md5($this->sourceFilename).'"', __FILE__, __LINE__);
+			$broad_directory_name = strtolower(md5($this->sourceFilename));
+			$this->cache_filename .= '_src'.$broad_directory_name;
+		}
+		if (@$_SERVER['HTTP_REFERER'] && $this->config_nooffsitelink_enabled) {
+			$parsed_url1 = @parse_url(@$_SERVER['HTTP_REFERER']);
+			$parsed_url2 = @parse_url('http://'.@$_SERVER['HTTP_HOST']);
+			if (@$parsed_url1['host'] && @$parsed_url2['host'] && ($parsed_url1['host'] != $parsed_url2['host'])) {
+				// include "_offsite" only if nooffsitelink_enabled and if referrer doesn't match the domain of the current server
+				$this->cache_filename .= '_offsite';
+			}
+		}
+
+		$ParametersString = '';
+		if ($this->fltr && is_array($this->fltr)) {
+			$ParametersString .= '_fltr'.implode('_fltr', $this->fltr);
+		}
+		$FilenameParameters1 = array('ar', 'bg', 'bc', 'far', 'sx', 'sy', 'sw', 'sh', 'zc');
+		foreach ($FilenameParameters1 as $key) {
+			if ($this->$key) {
+				$ParametersString .= '_'.$key.$this->$key;
+			}
+		}
+		$FilenameParameters2 = array('h', 'w', 'wl', 'wp', 'ws', 'hp', 'hs', 'xto', 'ra', 'iar', 'aoe', 'maxb', 'sfn', 'dpi');
+		foreach ($FilenameParameters2 as $key) {
+			if ($this->$key) {
+				$ParametersString .= '_'.$key.intval($this->$key);
+			}
+		}
+		if ($this->thumbnailFormat == 'jpeg') {
+			// only JPEG output has variable quality option
+			$ParametersString .= '_q'.intval($this->thumbnailQuality);
+		}
+		$this->DebugMessage('SetCacheFilename() _par set from md5('.$ParametersString.')', __FILE__, __LINE__);
+		$this->cache_filename .= '_par'.strtolower(md5($ParametersString));
+
+		if ($this->md5s) {
+			// source image MD5 hash provided
+			// do not source image modification date --
+			// cached image will be used even if file was modified or removed
+		} elseif (!$this->config_cache_source_filemtime_ignore_remote && eregi('^(f|ht)tps?\://', $this->src)) {
+			$this->cache_filename .= '_dat'.intval(phpthumb_functions::filedate_remote($this->src));
+		} elseif (!$this->config_cache_source_filemtime_ignore_local && $this->src && !$this->rawImageData) {
+			$this->cache_filename .= '_dat'.intval(@filemtime($this->sourceFilename));
+		}
+
+		$this->cache_filename .= '.'.strtolower($this->thumbnailFormat);
+		$broad_directories = '';
+		for ($i = 0; $i < $this->config_cache_directory_depth; $i++) {
+			$broad_directories .= DIRECTORY_SEPARATOR.substr($broad_directory_name, 0, $i + 1);
+		}
+
+		$this->cache_filename = $this->config_cache_directory.$broad_directories.DIRECTORY_SEPARATOR.$this->config_cache_prefix.rawurlencode($this->cache_filename);
+		return true;
+	}
+
+
+	function SourceImageIsTooLarge($width, $height) {
+		if (!$this->config_max_source_pixels) {
+			return false;
+		}
+		if (function_exists('memory_get_usage')) {
+			$available_memory = max(intval(ini_get('memory_limit')), intval(get_cfg_var('memory_limit'))) * 1048576;
+			$available_memory -= memory_get_usage();
+			return (bool) (($width * $height * 5) > $available_memory);
+		}
+		return (bool) (($width * $height) > $this->config_max_source_pixels);
+	}
+
+	function ImageCreateFromFilename($filename) {
+		// try to create GD image source directly via GD, if possible,
+		// rather than buffering to memory and creating with ImageCreateFromString
+		$ImageCreateWasAttempted = false;
+		$gd_image = false;
+
+		$this->DebugMessage('starting ImageCreateFromFilename('.$filename.')', __FILE__, __LINE__);
+		if ($filename && ($getimagesizeinfo = @GetImageSize($filename))) {
+			if (!$this->SourceImageIsTooLarge($getimagesizeinfo[0], $getimagesizeinfo[1])) {
+				$ImageCreateFromFunction = array(
+					1  => 'ImageCreateFromGIF',
+					2  => 'ImageCreateFromJPEG',
+					3  => 'ImageCreateFromPNG',
+					15 => 'ImageCreateFromWBMP',
+				);
+				$this->DebugMessage('ImageCreateFromFilename found ($getimagesizeinfo[2]=='.@$getimagesizeinfo[2].')', __FILE__, __LINE__);
+				switch (@$getimagesizeinfo[2]) {
+					case 1:  // GIF
+					case 2:  // JPEG
+					case 3:  // PNG
+					case 15: // WBMP
+						$ImageCreateFromFunctionName = $ImageCreateFromFunction[$getimagesizeinfo[2]];
+						if (function_exists($ImageCreateFromFunctionName)) {
+							$this->DebugMessage('Calling '.$ImageCreateFromFunctionName.'('.$filename.')', __FILE__, __LINE__);
+							$ImageCreateWasAttempted = true;
+							$gd_image = $ImageCreateFromFunctionName($filename);
+						} else {
+							$this->DebugMessage('NOT calling '.$ImageCreateFromFunctionName.'('.$filename.') because !function_exists('.$ImageCreateFromFunctionName.')', __FILE__, __LINE__);
+						}
+						break;
+
+					case 4:  // SWF
+					case 5:  // PSD
+					case 6:  // BMP
+					case 7:  // TIFF (LE)
+					case 8:  // TIFF (BE)
+					case 9:  // JPC
+					case 10: // JP2
+					case 11: // JPX
+					case 12: // JB2
+					case 13: // SWC
+					case 14: // IFF
+					case 16: // XBM
+						$this->DebugMessage('No built-in image creation function for image type "'.@$getimagesizeinfo[2].'" ($getimagesizeinfo[2])', __FILE__, __LINE__);
+						break;
+
+					default:
+						$this->DebugMessage('Unknown value for $getimagesizeinfo[2]: "'.@$getimagesizeinfo[2].'"', __FILE__, __LINE__);
+						break;
+				}
+			} else {
+				$this->DebugMessage('image is '.$getimagesizeinfo[0].'x'.$getimagesizeinfo[1].' and therefore contains more pixels ('.($getimagesizeinfo[0] * $getimagesizeinfo[1]).') than $this->config_max_source_pixels setting ('.$this->config_max_source_pixels.')', __FILE__, __LINE__);
+				return false;
+			}
+		} else {
+			$this->DebugMessage('empty $filename or GetImageSize('.$filename.') failed', __FILE__, __LINE__);
+		}
+
+		if (!$gd_image) {
+			// cannot create from filename, attempt to create source image with ImageCreateFromString, if possible
+			if ($ImageCreateWasAttempted) {
+				$this->DebugMessage(@$ImageCreateFromFunctionName.'() was attempted but FAILED', __FILE__, __LINE__);
+			}
+			$this->DebugMessage('Populating $rawimagedata', __FILE__, __LINE__);
+			$rawimagedata = '';
+			if ($fp = @fopen($filename, 'rb')) {
+				$filesize = filesize($filename);
+				$blocksize = 8192;
+				$blockreads = ceil($filesize / $blocksize);
+				for ($i = 0; $i < $blockreads; $i++) {
+					$rawimagedata .= fread($fp, $blocksize);
+				}
+				fclose($fp);
+			} else {
+				$this->DebugMessage('cannot fopen('.$filename.')', __FILE__, __LINE__);
+			}
+			if ($rawimagedata) {
+				$this->DebugMessage('attempting ImageCreateFromStringReplacement($rawimagedata ('.strlen($rawimagedata).' bytes), true)', __FILE__, __LINE__);
+				$gd_image = $this->ImageCreateFromStringReplacement($rawimagedata, true);
+			}
+		}
+		return $gd_image;
+	}
+
+	function SourceImageToGD() {
+		if (is_resource($this->gdimg_source)) {
+			$this->source_width  = ImageSX($this->gdimg_source);
+			$this->source_height = ImageSY($this->gdimg_source);
+			$this->DebugMessage('skipping SourceImageToGD() because $this->gdimg_source is already a resource ('.$this->source_width.'x'.$this->source_height.')', __FILE__, __LINE__);
+			return true;
+		}
+		$this->DebugMessage('starting SourceImageToGD()', __FILE__, __LINE__);
+
+		if ($this->ImageMagickThumbnailToGD()) {
+
+			// excellent, we have a thumbnailed source image
+			$this->DebugMessage('ImageMagickThumbnailToGD() succeeded', __FILE__, __LINE__);
+
+		} elseif (!$this->gdimg_source && $this->rawImageData) {
+
+			if ($this->md5s && ($this->md5s != md5($this->rawImageData))) {
+				return $this->ErrorImage('$this->md5s != md5($this->rawImageData)'."\n".'"'.$this->md5s.'" != '."\n".'"'.md5($this->rawImageData).'"');
+			}
+			$this->gdimg_source = $this->ImageCreateFromStringReplacement($this->rawImageData);
+			if (!$this->gdimg_source) {
+				return $this->ErrorImage('Unknown image type identified by "'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).') in SourceImageToGD()['.__LINE__.']');
+			}
+
+		} elseif (!$this->gdimg_source && $this->sourceFilename) {
+
+			if ($this->md5s && ($this->md5s != phpthumb_functions::md5_file_safe($this->sourceFilename))) {
+				return $this->ErrorImage('$this->md5s != md5(sourceFilename)'."\n".'"'.$this->md5s.'" != '."\n".'"'.phpthumb_functions::md5_file_safe($this->sourceFilename).'"');
+			}
+			switch (@$this->getimagesizeinfo[2]) {
+				case 1:
+				case 3:
+					// GIF or PNG input file may have transparency
+					$this->is_alpha = true;
+					break;
+			}
+			if (!$this->SourceImageIsTooLarge($this->source_width, $this->source_height)) {
+			$this->gdimg_source = $this->ImageCreateFromFilename($this->sourceFilename);
+			}
+
+		}
+
+		while (true) {
+			if ($this->gdimg_source) {
+				$this->DebugMessage('Not using EXIF thumbnail data because $this->gdimg_source is already set', __FILE__, __LINE__);
+				break;
+			}
+			if (!$this->exif_thumbnail_data) {
+				$this->DebugMessage('Not using EXIF thumbnail data because $this->exif_thumbnail_data is empty', __FILE__, __LINE__);
+				break;
+			}
+			if (ini_get('safe_mode')) {
+				if (!$this->SourceImageIsTooLarge($this->source_width, $this->source_height)) {
+					$this->DebugMessage('Using EXIF thumbnail data because source image too large and safe_mode enabled', __FILE__, __LINE__);
+					$this->aoe = true;
+				} else {
+					break;
+				}
+			} else {
+				if (!$this->config_use_exif_thumbnail_for_speed) {
+					$this->DebugMessage('Not using EXIF thumbnail data because $this->config_use_exif_thumbnail_for_speed is FALSE', __FILE__, __LINE__);
+					break;
+				}
+				if (($this->thumbnailCropX != 0) || ($this->thumbnailCropY != 0)) {
+					$this->DebugMessage('Not using EXIF thumbnail data because source cropping is enabled ('.$this->thumbnailCropX.','.$this->thumbnailCropY.')', __FILE__, __LINE__);
+					break;
+				}
+				if (($this->w > $this->exif_thumbnail_width) || ($this->h > $this->exif_thumbnail_height)) {
+					$this->DebugMessage('Not using EXIF thumbnail data because EXIF thumbnail is too small ('.$this->exif_thumbnail_width.'x'.$this->exif_thumbnail_height.' vs '.$this->w.'x'.$this->h.')', __FILE__, __LINE__);
+					break;
+				}
+				$source_ar = $this->source_width / $this->source_height;
+				$exif_ar   = $this->exif_thumbnail_width / $this->exif_thumbnail_height;
+				if (number_format($source_ar, 2) != number_format($exif_ar, 2)) {
+					$this->DebugMessage('not using EXIF thumbnail because $source_ar != $exif_ar ('.$source_ar.' != '.$exif_ar.')', __FILE__, __LINE__);
+					break;
+				}
+			}
+
+			// EXIF thumbnail exists, and is equal to or larger than destination thumbnail, and will be use as source image
+			$this->DebugMessage('Trying to use EXIF thumbnail as source image', __FILE__, __LINE__);
+
+			if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
+
+				$this->DebugMessage('Successfully using EXIF thumbnail as source image', __FILE__, __LINE__);
+				$this->gdimg_source   = $gdimg_exif_temp;
+				$this->source_width   = $this->exif_thumbnail_width;
+				$this->source_height  = $this->exif_thumbnail_height;
+				$this->thumbnailCropW = $this->source_width;
+				$this->thumbnailCropH = $this->source_height;
+				return true;
+
+			} else {
+				$this->DebugMessage('$this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false) failed', __FILE__, __LINE__);
+			}
+
+			break;
+		}
+
+		if (!$this->gdimg_source) {
+			$this->DebugMessage('$this->gdimg_source is still empty', __FILE__, __LINE__);
+
+			$this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__);
+
+			$imageHeader = '';
+			$gd_info = gd_info();
+			$GDreadSupport = false;
+			switch (@$this->getimagesizeinfo[2]) {
+				case 1:
+					$imageHeader = 'Content-Type: image/gif';
+					$GDreadSupport = (bool) @$gd_info['GIF Read Support'];
+					break;
+				case 2:
+					$imageHeader = 'Content-Type: image/jpeg';
+					$GDreadSupport = (bool) @$gd_info['JPG Support'];
+					break;
+				case 3:
+					$imageHeader = 'Content-Type: image/png';
+					$GDreadSupport = (bool) @$gd_info['PNG Support'];
+					break;
+			}
+			if ($imageHeader) {
+				// cannot create image for whatever reason (maybe ImageCreateFromJPEG et al are not available?)
+				// and ImageMagick is not available either, no choice but to output original (not resized/modified) data and exit
+				if ($this->config_error_die_on_source_failure) {
+					$errormessages = array();
+					$errormessages[] = 'All attempts to create GD image source failed.';
+					if ($this->fatalerror) {
+						$errormessages[] = $this->fatalerror;
+					}
+					if (ini_get('safe_mode')) {
+						$errormessages[] = 'Safe Mode enabled, therefore ImageMagick is unavailable. (disable Safe Mode if possible)';
+					} elseif (!$this->ImageMagickVersion()) {
+						$errormessages[] = 'ImageMagick is not installed (it is highly recommended that you install it).';
+					}
+					if ($this->SourceImageIsTooLarge($this->getimagesizeinfo[0], $this->getimagesizeinfo[1])) {
+						$memory_get_usage = (function_exists('memory_get_usage') ? memory_get_usage() : 0);
+						$errormessages[] = 'Source image is too large ('.$this->getimagesizeinfo[0].'x'.$this->getimagesizeinfo[1].' = '.number_format($this->getimagesizeinfo[0] * $this->getimagesizeinfo[1] / 1000000, 1).'Mpx, max='.number_format($this->config_max_source_pixels / 1000000, 1).'Mpx) for GD creation (either install ImageMagick or increase PHP memory_limit to at least '.ceil(($memory_get_usage + (5 * $this->getimagesizeinfo[0] * $this->getimagesizeinfo[1])) / 1000000).'M).';
+					} elseif (!$GDreadSupport) {
+						$errormessages[] = 'GD does not have read support for "'.$imageHeader.'".';
+					} else {
+						$errormessages[] = 'Source image probably corrupt.';
+					}
+					$this->ErrorImage(implode("\n", $errormessages));
+
+				} else {
+					$this->DebugMessage('All attempts to create GD image source failed ('.(ini_get('safe_mode') ? 'Safe Mode enabled, ImageMagick unavailable and source image probably too large for GD': ($GDreadSupport ? 'source image probably corrupt' : 'GD does not have read support for "'.$imageHeader.'"')).'), cannot generate thumbnail');
+					//$this->DebugMessage('All attempts to create GD image source failed ('.($GDreadSupport ? 'source image probably corrupt' : 'GD does not have read support for "'.$imageHeader.'"').'), outputing raw image', __FILE__, __LINE__);
+					//if (!$this->phpThumbDebug) {
+					//	header($imageHeader);
+					//	echo $this->rawImageData;
+					//	exit;
+					//}
+					return false;
+				}
+			}
+
+			//switch (substr($this->rawImageData, 0, 2)) {
+			//	case 'BM':
+			switch (@$this->getimagesizeinfo[2]) {
+				case 6:
+					ob_start();
+					if (!@include_once(dirname(__FILE__).'/phpthumb.bmp.php')) {
+						ob_end_clean();
+						return $this->ErrorImage('include_once('.dirname(__FILE__).'/phpthumb.bmp.php) failed');
+					}
+					ob_end_clean();
+					if ($fp = @fopen($this->sourceFilename, 'rb')) {
+						$this->rawImageData = '';
+						while (!feof($fp)) {
+							$this->rawImageData .= fread($fp, 32768);
+						}
+						fclose($fp);
+					}
+					$phpthumb_bmp = new phpthumb_bmp();
+					if ($this->gdimg_source = $phpthumb_bmp->phpthumb_bmp2gd($this->rawImageData, (phpthumb_functions::gd_version() >= 2.0))) {
+						$this->DebugMessage('$phpthumb_bmp->phpthumb_bmp2gd() succeeded', __FILE__, __LINE__);
+						break;
+					}
+					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on BMP source conversion' : 'phpthumb_bmp2gd() failed');
+					break;
+			//}
+			//switch (substr($this->rawImageData, 0, 4)) {
+			//	case 'II'."\x2A\x00":
+			//	case 'MM'."\x00\x2A":
+				case 7:
+				case 8:
+					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on TIFF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support TIFF source images without it');
+					break;
+
+				//case "\xD7\xCD\xC6\x9A":
+				//	return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on WMF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support WMF source images without it');
+				//	break;
+			}
+
+			if (!$this->gdimg_source) {
+				$HeaderFourBytes = '';
+				if ($fp = @fopen($this->sourceFilename, 'rb')) {
+					$HeaderFourBytes = fread($fp, 4);
+					fclose($fp);
+				}
+				if ($HeaderFourBytes == "\xD7\xCD\xC6\x9A") { // WMF
+					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on WMF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support WMF source images without it');
+				} elseif ($HeaderFourBytes == '%PDF') { // "%PDF"
+					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick and GhostScript are both required for PDF source images; GhostScript may not be properly configured' : 'ImageMagick and/or GhostScript are unavailable and phpThumb() does not support PDF source images without them');
+				} elseif (substr($HeaderFourBytes, 0, 3) == "\xFF\xD8\xFF") { // JPEG
+					return $this->ErrorImage('Image is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
+				} elseif ($HeaderFourBytes == '%PNG') { // "%PNG"
+					return $this->ErrorImage('Image is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
+				} elseif (substr($HeaderFourBytes, 0, 3) == 'GIF') { // GIF
+					return $this->ErrorImage('Image is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
+				}
+				return $this->ErrorImage('Unknown image type identified by "'.substr($HeaderFourBytes, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($HeaderFourBytes, 0, 4)).') in SourceImageToGD()['.__LINE__.']');
+
+			}
+		}
+
+		if (!$this->gdimg_source) {
+			if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
+				$this->DebugMessage('All other attempts failed, but successfully using EXIF thumbnail as source image', __FILE__, __LINE__);
+				$this->gdimg_source   = $gdimg_exif_temp;
+				// override allow-enlarging setting if EXIF thumbnail is the only source available
+				// otherwise thumbnails larger than the EXIF thumbnail will be created at EXIF size
+				$this->aoe = true;
+				return true;
+			}
+			return false;
+		}
+
+		$this->source_width  = ImageSX($this->gdimg_source);
+		$this->source_height = ImageSY($this->gdimg_source);
+		return true;
+	}
+
+
+	function phpThumbDebugVarDump($var) {
+		if (is_null($var)) {
+			return 'NULL';
+		} elseif (is_bool($var)) {
+			return ($var ? 'TRUE' : 'FALSE');
+		} elseif (is_string($var)) {
+			return 'string('.strlen($var).')'.str_repeat(' ', max(0, 3 - strlen(strlen($var)))).' "'.$var.'"';
+		} elseif (is_int($var)) {
+			return 'integer     '.$var;
+		} elseif (is_float($var)) {
+			return 'float       '.$var;
+		} elseif (is_array($var)) {
+			ob_start();
+			var_dump($var);
+			$vardumpoutput = ob_get_contents();
+			ob_end_clean();
+			return strtr($vardumpoutput, "\n\r\t", '   ');
+		}
+		return gettype($var);
+	}
+
+	function phpThumbDebug($level='') {
+		if ($level && ($this->phpThumbDebug !== $level)) {
+			return true;
+		}
+		if ($this->config_disable_debug) {
+			return $this->ErrorImage('phpThumbDebug disabled');
+		}
+
+		$FunctionsExistance  = array('exif_thumbnail', 'gd_info', 'image_type_to_mime_type', 'ImageCopyResampled', 'ImageCopyResized', 'ImageCreate', 'ImageCreateFromString', 'ImageCreateTrueColor', 'ImageIsTrueColor', 'ImageRotate', 'ImageTypes', 'version_compare', 'ImageCreateFromGIF', 'ImageCreateFromJPEG', 'ImageCreateFromPNG', 'ImageCreateFromWBMP', 'ImageCreateFromXBM', 'ImageCreateFromXPM', 'ImageCreateFromString', 'ImageCreateFromGD', 'ImageCreateFromGD2', 'ImageCreateFromGD2Part', 'ImageJPEG', 'ImageGIF', 'ImagePNG', 'ImageWBMP');
+		$ParameterNames      = array('src', 'new', 'w', 'h', 'f', 'q', 'sx', 'sy', 'sw', 'sh', 'far', 'bg', 'bc', 'file', 'goto', 'err', 'xto', 'ra', 'ar', 'aoe', 'iar', 'maxb');
+		$ConfigVariableNames = array('document_root', 'temp_directory', 'output_format', 'output_maxwidth', 'output_maxheight', 'error_message_image_default', 'error_bgcolor', 'error_textcolor', 'error_fontsize', 'error_die_on_error', 'error_silent_die_on_error', 'error_die_on_source_failure', 'nohotlink_enabled', 'nohotlink_valid_domains', 'nohotlink_erase_image', 'nohotlink_text_message', 'nooffsitelink_enabled', 'nooffsitelink_valid_domains', 'nooffsitelink_require_refer', 'nooffsitelink_erase_image', 'nooffsitelink_text_message', 'high_security_enabled', 'allow_src_above_docroot', 'allow_src_above_phpthumb', 'allow_parameter_file', 'allow_parameter_goto', 'max_source_pixels', 'use_exif_thumbnail_for_speed', 'border_hexcolor', 'background_hexcolor', 'ttf_directory', 'disable_pathinfo_parsing', 'disable_imagecopyresampled');
+		$OtherVariableNames  = array('phpThumbDebug', 'thumbnailQuality', 'thumbnailFormat', 'gdimg_output', 'gdimg_source', 'sourceFilename', 'source_width', 'source_height', 'thumbnailCropX', 'thumbnailCropY', 'thumbnailCropW', 'thumbnailCropH', 'exif_thumbnail_width', 'exif_thumbnail_height', 'exif_thumbnail_type', 'thumbnail_width', 'thumbnail_height', 'thumbnail_image_width', 'thumbnail_image_height');
+
+		$DebugOutput = array();
+		$DebugOutput[] = 'phpThumb() version          = '.$this->phpthumb_version;
+		$DebugOutput[] = 'phpversion()                = '.@phpversion();
+		$DebugOutput[] = 'PHP_OS                      = '.PHP_OS;
+		$DebugOutput[] = '__FILE__                    = '.__FILE__;
+		$DebugOutput[] = 'realpath(.)                 = '.@realpath('.');
+		$DebugOutput[] = '$_SERVER[PHP_SELF]          = '.@$_SERVER['PHP_SELF'];
+		$DebugOutput[] = '$_SERVER[HOST_NAME]         = '.@$_SERVER['HOST_NAME'];
+		$DebugOutput[] = '$_SERVER[HTTP_REFERER]      = '.@$_SERVER['HTTP_REFERER'];
+		$DebugOutput[] = '$_SERVER[QUERY_STRING]      = '.@$_SERVER['QUERY_STRING'];
+		$DebugOutput[] = '$_SERVER[PATH_INFO]         = '.@$_SERVER['PATH_INFO'];
+		$DebugOutput[] = '$_SERVER[DOCUMENT_ROOT]     = '.@$_SERVER['DOCUMENT_ROOT'];
+		$DebugOutput[] = 'getenv(DOCUMENT_ROOT)       = '.@getenv('DOCUMENT_ROOT');
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = 'get_magic_quotes_gpc()      = '.$this->phpThumbDebugVarDump(@get_magic_quotes_gpc());
+		$DebugOutput[] = 'get_magic_quotes_runtime()  = '.$this->phpThumbDebugVarDump(@get_magic_quotes_runtime());
+		$DebugOutput[] = 'error_reporting()           = '.$this->phpThumbDebugVarDump(error_reporting());
+		$DebugOutput[] = 'ini_get(error_reporting)    = '.$this->phpThumbDebugVarDump(@ini_get('error_reporting'));
+		$DebugOutput[] = 'ini_get(display_errors)     = '.$this->phpThumbDebugVarDump(@ini_get('display_errors'));
+		$DebugOutput[] = 'ini_get(allow_url_fopen)    = '.$this->phpThumbDebugVarDump(@ini_get('allow_url_fopen'));
+		$DebugOutput[] = 'ini_get(disable_functions)  = '.$this->phpThumbDebugVarDump(@ini_get('disable_functions'));
+		$DebugOutput[] = 'ini_get(safe_mode)          = '.$this->phpThumbDebugVarDump(@ini_get('safe_mode'));
+		$DebugOutput[] = 'ini_get(open_basedir)       = '.$this->phpThumbDebugVarDump(@ini_get('open_basedir'));
+		$DebugOutput[] = 'ini_get(max_execution_time) = '.$this->phpThumbDebugVarDump(@ini_get('max_execution_time'));
+		$DebugOutput[] = 'ini_get(memory_limit)       = '.$this->phpThumbDebugVarDump(@ini_get('memory_limit'));
+		$DebugOutput[] = 'get_cfg_var(memory_limit)   = '.$this->phpThumbDebugVarDump(@get_cfg_var('memory_limit'));
+		$DebugOutput[] = 'memory_get_usage()          = '.(function_exists('memory_get_usage') ? $this->phpThumbDebugVarDump(@memory_get_usage()) : 'n/a');
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = '$this->config_prefer_imagemagick            = '.$this->phpThumbDebugVarDump($this->config_prefer_imagemagick);
+		$DebugOutput[] = '$this->config_imagemagick_path              = '.$this->phpThumbDebugVarDump($this->config_imagemagick_path);
+		$DebugOutput[] = '$this->ImageMagickWhichConvert()            = '.$this->ImageMagickWhichConvert();
+		$IMpathUsed = ($this->config_imagemagick_path ? $this->config_imagemagick_path : $this->ImageMagickWhichConvert());
+		$DebugOutput[] = '[actual ImageMagick path used]              = '.$this->phpThumbDebugVarDump($IMpathUsed);
+		$DebugOutput[] = 'file_exists([actual ImageMagick path used]) = '.$this->phpThumbDebugVarDump(@file_exists($IMpathUsed));
+		$DebugOutput[] = 'ImageMagickVersion(false)                   = '.$this->ImageMagickVersion(false);
+		$DebugOutput[] = 'ImageMagickVersion(true)                    = '.$this->ImageMagickVersion(true);
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = '$this->config_cache_directory               = '.$this->phpThumbDebugVarDump($this->config_cache_directory);
+		$DebugOutput[] = '$this->config_cache_directory_depth         = '.$this->phpThumbDebugVarDump($this->config_cache_directory_depth);
+		$DebugOutput[] = '$this->config_cache_disable_warning         = '.$this->phpThumbDebugVarDump($this->config_cache_disable_warning);
+		$DebugOutput[] = '$this->config_cache_maxage                  = '.$this->phpThumbDebugVarDump($this->config_cache_maxage);
+		$DebugOutput[] = '$this->config_cache_maxsize                 = '.$this->phpThumbDebugVarDump($this->config_cache_maxsize);
+		$DebugOutput[] = '$this->config_cache_maxfiles                = '.$this->phpThumbDebugVarDump($this->config_cache_maxfiles);
+		$DebugOutput[] = '$this->config_cache_force_passthru          = '.$this->phpThumbDebugVarDump($this->config_cache_force_passthru);
+		$DebugOutput[] = '$this->cache_filename                       = '.$this->phpThumbDebugVarDump($this->cache_filename);
+		$DebugOutput[] = 'is_readable($this->config_cache_directory)  = '.$this->phpThumbDebugVarDump(@is_readable($this->config_cache_directory));
+		$DebugOutput[] = 'is_writable($this->config_cache_directory)  = '.$this->phpThumbDebugVarDump(@is_writable($this->config_cache_directory));
+		$DebugOutput[] = 'is_readable($this->cache_filename)          = '.$this->phpThumbDebugVarDump(@is_readable($this->cache_filename));
+		$DebugOutput[] = 'is_writable($this->cache_filename)          = '.(@file_exists($this->cache_filename) ? $this->phpThumbDebugVarDump(@is_writable($this->cache_filename)) : 'n/a');
+		$DebugOutput[] = '';
+
+		foreach ($ConfigVariableNames as $varname) {
+			$varname = 'config_'.$varname;
+			$value = $this->$varname;
+			$DebugOutput[] = '$this->'.str_pad($varname, 37, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = '';
+		foreach ($OtherVariableNames as $varname) {
+			$value = $this->$varname;
+			$DebugOutput[] = '$this->'.str_pad($varname, 27, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = 'strlen($this->rawImageData)        = '.strlen(@$this->rawImageData);
+		$DebugOutput[] = 'strlen($this->exif_thumbnail_data) = '.strlen(@$this->exif_thumbnail_data);
+		$DebugOutput[] = '';
+
+		foreach ($ParameterNames as $varname) {
+			$value = $this->$varname;
+			$DebugOutput[] = '$this->'.str_pad($varname, 4, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = '';
+
+		foreach ($FunctionsExistance as $functionname) {
+			$DebugOutput[] = 'builtin_function_exists('.$functionname.')'.str_repeat(' ', 23 - strlen($functionname)).' = '.$this->phpThumbDebugVarDump(phpthumb_functions::builtin_function_exists($functionname));
+		}
+		$DebugOutput[] = '';
+
+		$gd_info = gd_info();
+		foreach ($gd_info as $key => $value) {
+			$DebugOutput[] = 'gd_info.'.str_pad($key, 34, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = '';
+
+		$exif_info = phpthumb_functions::exif_info();
+		foreach ($exif_info as $key => $value) {
+			$DebugOutput[] = 'exif_info.'.str_pad($key, 26, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = '';
+
+		if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray(dirname(@$_SERVER['PHP_SELF']))) {
+			foreach ($ApacheLookupURIarray as $key => $value) {
+				$DebugOutput[] = 'ApacheLookupURIarray.'.str_pad($key, 15, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+			}
+		} else {
+				$DebugOutput[] = 'ApacheLookupURIarray() -- FAILED';
+		}
+		$DebugOutput[] = '';
+
+		if (isset($_GET) && is_array($_GET)) {
+			foreach ($_GET as $key => $value) {
+				$DebugOutput[] = '$_GET['.$key.']'.str_repeat(' ', 30 - strlen($key)).'= '.$this->phpThumbDebugVarDump($value);
+			}
+		}
+		if (isset($_POST) && is_array($_POST)) {
+			foreach ($_POST as $key => $value) {
+				$DebugOutput[] = '$_POST['.$key.']'.str_repeat(' ', 29 - strlen($key)).'= '.$this->phpThumbDebugVarDump($value);
+			}
+		}
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = '$this->debugmessages:';
+		foreach ($this->debugmessages as $errorstring) {
+			$DebugOutput[] = '  * '.$errorstring;
+		}
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = '$this->debugtiming:';
+		foreach ($this->debugtiming as $timestamp => $timingstring) {
+			$DebugOutput[] = '  * '.$timestamp.' '.$timingstring;
+		}
+		$DebugOutput[] = '  * Total processing time: '.number_format(max(array_keys($this->debugtiming)) - min(array_keys($this->debugtiming)), 6);
+
+		$this->f = (isset($_GET['f']) ? $_GET['f'] : $this->f); // debug modes 0-2 don't recognize text mode otherwise
+		return $this->ErrorImage(implode("\n", $DebugOutput), 700, 500, true);
+	}
+
+	function ErrorImage($text, $width=0, $height=0, $forcedisplay=false) {
+		$width  = ($width  ? $width  : $this->config_error_image_width);
+		$height = ($height ? $height : $this->config_error_image_height);
+
+		$text = 'phpThumb() v'.$this->phpthumb_version."\n\n".$text;
+		if ($this->config_disable_debug) {
+			$text = 'Error messages disabled';
+		}
+
+		$this->DebugMessage($text, __FILE__, __LINE__);
+		if ($this->phpThumbDebug && !$forcedisplay) {
+			return false;
+		}
+		if (!$this->config_error_die_on_error && !$forcedisplay) {
+			$this->fatalerror = $text;
+			return false;
+		}
+		if ($this->config_error_silent_die_on_error) {
+			exit;
+		}
+		if ($this->err || $this->config_error_message_image_default) {
+			// Show generic custom error image instead of error message
+			// for use on production sites where you don't want debug messages
+			if ($this->err == 'showerror') {
+				// fall through and actually show error message even if default error image is set
+			} else {
+				header('Location: '.($this->err ? $this->err : $this->config_error_message_image_default));
+				exit;
+			}
+		}
+		$this->setOutputFormat();
+		if (!$this->thumbnailFormat || (phpthumb_functions::gd_version() < 1)) {
+			$this->thumbnailFormat = 'text';
+		}
+		if (@$this->thumbnailFormat == 'text') {
+			// bypass all GD functions and output text error message
+			die('<pre>'.$text.'</pre>');
+		}
+
+		$FontWidth  = ImageFontWidth($this->config_error_fontsize);
+		$FontHeight = ImageFontHeight($this->config_error_fontsize);
+
+		$LinesOfText = explode("\n", @wordwrap($text, floor($width / $FontWidth), "\n", true));
+		$height = max($height, count($LinesOfText) * $FontHeight);
+
+		$headers_file = '';
+		$headers_line = '';
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=') && headers_sent($headers_file, $headers_line)) {
+
+			echo "\n".'**Headers already sent in file "'.$headers_file.'" on line "'.$headers_line.'", dumping error message as text:**<br><pre>'."\n\n".$text."\n".'</pre>';
+
+		} elseif (headers_sent()) {
+
+			echo "\n".'**Headers already sent, dumping error message as text:**<br><pre>'."\n\n".$text."\n".'</pre>';
+
+		} elseif ($gdimg_error = ImageCreate($width, $height)) {
+
+			$background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_error, $this->config_error_bgcolor,   true);
+			$text_color       = phpthumb_functions::ImageHexColorAllocate($gdimg_error, $this->config_error_textcolor, true);
+			ImageFilledRectangle($gdimg_error, 0, 0, $width, $height, $background_color);
+			$lineYoffset = 0;
+			foreach ($LinesOfText as $line) {
+				ImageString($gdimg_error, $this->config_error_fontsize, 2, $lineYoffset, $line, $text_color);
+				$lineYoffset += $FontHeight;
+			}
+			if (function_exists('ImageTypes')) {
+				$imagetypes = ImageTypes();
+				if ($imagetypes & IMG_PNG) {
+					header('Content-Type: image/png');
+					ImagePNG($gdimg_error);
+				} elseif ($imagetypes & IMG_GIF) {
+					header('Content-Type: image/gif');
+					ImageGIF($gdimg_error);
+				} elseif ($imagetypes & IMG_JPG) {
+					header('Content-Type: image/jpeg');
+					ImageJPEG($gdimg_error);
+				} elseif ($imagetypes & IMG_WBMP) {
+					header('Content-Type: image/vnd.wap.wbmp');
+					ImageWBMP($gdimg_error);
+				}
+			}
+			ImageDestroy($gdimg_error);
+
+		}
+		if (!headers_sent()) {
+			echo "\n".'**Failed to send graphical error image, dumping error message as text:**<br>'."\n\n".$text;
+		}
+		exit;
+		return true;
+	}
+
+	function ImageCreateFromStringReplacement(&$RawImageData, $DieOnErrors=false) {
+		// there are serious bugs in the non-bundled versions of GD which may cause
+		// PHP to segfault when calling ImageCreateFromString() - avoid if at all possible
+		// when not using a bundled version of GD2
+		if (!phpthumb_functions::gd_version()) {
+			if ($DieOnErrors) {
+				if (!headers_sent()) {
+					// base64-encoded error image in GIF format
+					$ERROR_NOGD = 'R0lGODlhIAAgALMAAAAAABQUFCQkJDY2NkZGRldXV2ZmZnJycoaGhpSUlKWlpbe3t8XFxdXV1eTk5P7+/iwAAAAAIAAgAAAE/vDJSau9WILtTAACUinDNijZtAHfCojS4W5H+qxD8xibIDE9h0OwWaRWDIljJSkUJYsN4bihMB8th3IToAKs1VtYM75cyV8sZ8vygtOE5yMKmGbO4jRdICQCjHdlZzwzNW4qZSQmKDaNjhUMBX4BBAlmMywFSRWEmAI6b5gAlhNxokGhooAIK5o/pi9vEw4Lfj4OLTAUpj6IabMtCwlSFw0DCKBoFqwAB04AjI54PyZ+yY3TD0ss2YcVmN/gvpcu4TOyFivWqYJlbAHPpOntvxNAACcmGHjZzAZqzSzcq5fNjxFmAFw9iFRunD1epU6tsIPmFCAJnWYE0FURk7wJDA0MTKpEzoWAAskiAAA7';
+					header('Content-Type: image/gif');
+					echo base64_decode($ERROR_NOGD);
+				} else {
+					echo '*** ERROR: No PHP-GD support available ***';
+				}
+				exit;
+			} else {
+				$this->DebugMessage('ImageCreateFromStringReplacement() failed: gd_version says "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+				return false;
+			}
+		}
+		if (phpthumb_functions::gd_is_bundled()) {
+			$this->DebugMessage('ImageCreateFromStringReplacement() calling built-in ImageCreateFromString()', __FILE__, __LINE__);
+			return @ImageCreateFromString($RawImageData);
+		}
+		if (ini_get('safe_mode')) {
+			$this->DebugMessage('ImageCreateFromStringReplacement() failed: cannot create temp file in SAFE_MODE', __FILE__, __LINE__);
+			return false;
+		}
+
+		switch (substr($RawImageData, 0, 3)) {
+			case 'GIF':
+				$ICFSreplacementFunctionName = 'ImageCreateFromGIF';
+				break;
+			case "\xFF\xD8\xFF":
+				$ICFSreplacementFunctionName = 'ImageCreateFromJPEG';
+				break;
+			case "\x89".'PN':
+				$ICFSreplacementFunctionName = 'ImageCreateFromPNG';
+				break;
+			default:
+				$this->DebugMessage('ImageCreateFromStringReplacement() failed: unknown fileformat signature "'.phpthumb_functions::HexCharDisplay(substr($RawImageData, 0, 3)).'"', __FILE__, __LINE__);
+				return false;
+				break;
+		}
+		if ($tempnam = $this->phpThumb_tempnam()) {
+			if ($fp_tempnam = @fopen($tempnam, 'wb')) {
+				fwrite($fp_tempnam, $RawImageData);
+				fclose($fp_tempnam);
+				if (($ICFSreplacementFunctionName == 'ImageCreateFromGIF') && !function_exists($ICFSreplacementFunctionName)) {
+
+					// Need to create from GIF file, but ImageCreateFromGIF does not exist
+					ob_start();
+					if (!@include_once(dirname(__FILE__).'/phpthumb.gif.php')) {
+						$ErrorMessage = 'Failed to include required file "'.dirname(__FILE__).'/phpthumb.gif.php" in '.__FILE__.' on line '.__LINE__;
+						$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
+					}
+					ob_end_clean();
+					// gif_loadFileToGDimageResource() cannot read from raw data, write to file first
+					if ($tempfilename = $this->phpThumb_tempnam()) {
+						if ($fp_tempfile = @fopen($tempfilename, 'wb')) {
+							fwrite($fp_tempfile, $RawImageData);
+							fclose($fp_tempfile);
+							$gdimg_source = gif_loadFileToGDimageResource($tempfilename);
+							$this->DebugMessage('gif_loadFileToGDimageResource('.$tempfilename.') completed', __FILE__, __LINE__);
+							unlink($tempfilename);
+							return $gdimg_source;
+							break;
+						} else {
+							$ErrorMessage = 'Failed to open tempfile in '.__FILE__.' on line '.__LINE__;
+							$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
+						}
+					} else {
+						$ErrorMessage = 'Failed to open generate tempfile name in '.__FILE__.' on line '.__LINE__;
+						$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
+					}
+
+				} elseif (function_exists($ICFSreplacementFunctionName) && ($gdimg_source = @$ICFSreplacementFunctionName($tempnam))) {
+
+					// great
+					$this->DebugMessage($ICFSreplacementFunctionName.'('.$tempnam.') succeeded', __FILE__, __LINE__);
+					unlink($tempnam);
+					return $gdimg_source;
+
+				} else {
+
+					// GD functions not available, or failed to create image
+					$this->DebugMessage($ICFSreplacementFunctionName.'('.$tempnam.') '.(function_exists($ICFSreplacementFunctionName) ? 'failed' : 'does not exist'), __FILE__, __LINE__);
+					if (isset($_GET['phpThumbDebug'])) {
+						$this->phpThumbDebug();
+					}
+
+				}
+			} else {
+				$ErrorMessage = 'Failed to fopen('.$tempnam.', "wb") in '.__FILE__.' on line '.__LINE__."\n".'You may need to set $PHPTHUMB_CONFIG[temp_directory] in phpThumb.config.php';
+				$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
+			}
+			@unlink($tempnam);
+		} else {
+			$ErrorMessage = 'Failed to generate phpThumb_tempnam() in '.__FILE__.' on line '.__LINE__."\n".'You may need to set $PHPTHUMB_CONFIG[temp_directory] in phpThumb.config.php';
+		}
+		if ($DieOnErrors && $ErrorMessage) {
+			return $this->ErrorImage($ErrorMessage);
+		}
+		return false;
+	}
+
+	function ImageResizeFunction(&$dst_im, &$src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH) {
+		$this->DebugMessage('ImageResizeFunction($o, $s, '.$dstX.', '.$dstY.', '.$srcX.', '.$srcY.', '.$dstW.', '.$dstH.', '.$srcW.', '.$srcH.')', __FILE__, __LINE__);
+		if (phpthumb_functions::gd_version() >= 2.0) {
+			if ($this->config_disable_imagecopyresampled) {
+				return phpthumb_functions::ImageCopyResampleBicubic($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
+			}
+			return ImageCopyResampled($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
+		}
+		return ImageCopyResized($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
+	}
+
+	function phpThumb_tempnam() {
+		$tempnam = realpath(tempnam($this->config_temp_directory, 'pThumb'));
+		$this->DebugMessage('phpThumb_tempnam() returning "'.$tempnam.'"', __FILE__, __LINE__);
+		return $tempnam;
+	}
+
+	function DebugMessage($message, $file='', $line='') {
+		$this->debugmessages[] = $message.($file ? ' in file "'.(basename($file) ? basename($file) : $file).'"' : '').($line ? ' on line '.$line : '');
+		return true;
+	}
+
+	function DebugTimingMessage($message, $file='', $line='', $timestamp=0) {
+		if (!$timestamp) {
+			$timestamp = array_sum(explode(' ', microtime()));
+		}
+		$this->debugtiming[number_format($timestamp, 6, '.', '')] = ': '.$message.($file ? ' in file "'.(basename($file) ? basename($file) : $file).'"' : '').($line ? ' on line '.$line : '');
+		return true;
+	}
+
+}
+
+?>
Index: /afridex/plugins/fresh-page/RCCWP_CreateCustomFieldPage.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_CreateCustomFieldPage.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_CreateCustomFieldPage.php (revision 21)
@@ -0,0 +1,599 @@
+<?php
+include_once('RCCWP_CustomField.php');
+
+class RCCWP_CreateCustomFieldPage
+{
+	function Main()
+	{
+		?>
+  	
+  		<div class="wrap">
+	  	
+  		<h2>Create Custom Field</h2>
+	  
+		<?php
+			// if can't write, display notification
+
+			if( !is_writable( dirname(__FILE__) . '/files/poopoopeepee.txt' ) )
+			{
+				//echo 'PHP CANNOT SAVE FILES -- Please change permissions on wp-content/plugins/fresh_page/files/';
+			}
+
+		?>
+
+	
+  		<form action="" method="post" id="create-custom-field-form">
+
+		<?php if(isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id'])) { ?>
+  			<input type="hidden" name="custom-write-panel-id" value="<?=$_GET['custom-write-panel-id']?>"
+		<?php } ?>
+		<?php if(isset($_POST['custom-write-panel-id']) && !empty($_POST['custom-write-panel-id'])) { ?>
+  			<input type="hidden" name="custom-write-panel-id" value="<?=$_POST['custom-write-panel-id']?>"
+
+		<?php } ?>
+		<fieldset class="options">
+		
+		<table class="optiontable">
+		<tbody>
+		<tr valign="top">
+			<th scope="row">Name:</th>
+			<td><input name="custom-field-name" id="custom-field-name" size="40" type="text" /></td>
+		</tr>
+<!--
+		<tr valign="top">
+			<th scope="row">Description:</th>
+			<td><textarea name="custom-field-description" id="custom-field-description" rows="2" cols="38"></textarea></td>
+		</tr>
+-->
+		<tr valign="top">
+			<th scope="row">Label:</th>
+			<td><input name="custom-field-description" id="custom-field-description" size="40" type="text" /></td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row">Order:</th>
+			<td><input type="text" name="custom-field-order" id="custom-field-order" size="2" value="0" /></td>
+		</tr>
+		<tr valign="top">
+			<th scope="row">Type:</th>
+			<td>
+
+				<!-- START :: Javascript for Image/Photo' Css Class -->
+				<script type="text/javascript" language="javascript">
+					function fun(name)
+					{
+						if(name == "Image")
+						{
+							document.getElementById('divLbl').style.display = 'inline';
+							document.getElementById('divCSS').style.display = 'inline';
+						}
+						else
+						{
+							document.getElementById('divLbl').style.display = 'none';
+							document.getElementById('divCSS').style.display = 'none';
+						}
+					}
+				</script>
+				<!-- END :: Javascript for Image/Photo' Css Class -->
+
+				<?php
+				$field_types = RCCWP_CustomField::GetCustomFieldTypes();
+				foreach ($field_types as $field) :
+					$checked = 
+						$field->name == RCCWP_CustomField::GetDefaultCustomFieldType() ?
+						'checked="checked"' : '';
+				?>
+					<label><input name="custom-field-type" value="<?=$field->id?>" type="radio" <?=$checked?> onclick='fun("<?=$field->name?>");' /> <!-- Calling Javascript Function -->
+					<?=$field->name?></label><br />
+				<?php
+				endforeach;
+				?>
+			</td>
+		</tr>
+		<!-- START :: For Image/Photo' Css -->
+		<tr valign="top">
+			<th scope="row"><div id="divLbl" style="display:none">Css Class:</div></th>
+			<td>
+				<div id="divCSS" style="display:none">
+				<input name="custom-field-css" id="custom-field-css" size="40" type="text" value="freshout" />
+				</div>
+			</td>
+		</tr>
+		<!-- END :: For Image/Photo' Css -->
+		</tbody>
+		</table>
+		
+		</fieldset>
+	  	
+  		<p class="submit" ><input name="cancel-create-custom-field" type="submit" id="cancel-create-custom-field" value="Cancel" /> <input name="continue-create-custom-field" type="submit" id="continue-create-custom-field" value="Continue" /></p>
+	  	
+  		</form>
+	  	
+  		</div>
+	  	
+  		<?php	
+	}
+	
+	function SetOptions()
+	{
+		$current_field = RCCWP_CustomField::GetCustomFieldTypes($_POST['custom-field-type']);
+		?>
+		
+		<div class="wrap">
+		
+		<h2>Create Custom Field</h2>
+		
+		<form action="" method="post" id="continue-create-new-field-form">
+		
+		<input type="hidden" name="custom-write-panel-id" 	value="<?=$_POST['custom-write-panel-id']?>" />
+		<input type="hidden" name="custom-field-name" 		value="<?=$_POST['custom-field-name']?>" />
+		<input type="hidden" name="custom-field-description" 	value="<?=$_POST['custom-field-description']?>" />
+		<input type="hidden" name="custom-field-order" 		value="<?=$_POST['custom-field-order']?>" />
+		<input type="hidden" name="custom-field-type" 		value="<?=$_POST['custom-field-type']?>" />
+
+		<!-- Hidden value for Image/Photo' Css Class-->
+		<input type="hidden" name="custom-field-css" value="<?=$_POST['custom-field-css']?>" />
+
+		<fieldset class="options">
+		<legend><?=$current_field->name?></legend>
+		
+		<table class="optiontable">
+		<tbody>
+		
+		<?php
+		if ($current_field->has_properties == "true") :
+		?>
+		
+		<?php 
+		if (in_array($current_field->name, array('Textbox', 'Listbox'))) : 
+			if ($current_field->name == 'Textbox')
+				$size = 25;
+			else if ($current_field->name == 'Listbox')
+				$size = 3;
+		?>
+		<tr valign="top">
+			<th scope="row">Size:</th>
+			<td><input type="text" name="custom-field-size" id="custom-field-size" size="2" value="<?=$size?>" /></td>
+		</tr>	
+		<?php endif; ?>
+		
+		<?php 
+		if (in_array($current_field->name, array('Multiline Textbox'))) : 
+			$height = 3;
+			$width = 23;
+		?>
+		<tr valign="top">
+			<th scope="row">Height:</th>
+			<td><input type="text" name="custom-field-height" id="custom-field-height" size="2" value="<?=$height?>" /></td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row">Width:</th>
+			<td><input type="text" name="custom-field-width" id="custom-field-width" size="2" value="<?=$width?>" /></td>
+		</tr>	
+		<?php endif; ?>
+		
+		<?php
+		endif; // has_properties
+		?>
+		
+		<?php
+		if ($current_field->has_options == "true") :
+		?>		
+		<tr valign="top">
+			<th scope="row">Options:</th>
+			<td>
+				<textarea name="custom-field-options" id="custom-field-options" rows="2" cols="38"></textarea><br />
+				<em>Separate each option with a newline.</em>
+			</td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row">Default Value:</th>
+			<td>
+				<?php
+				if ($current_field->allow_multiple_values == "true") :
+				?>
+				<textarea name="custom-field-default-value" id="custom-field-default-value" rows="2" cols="38"></textarea><br />
+				<em>Separate each value with a newline.</em>
+				<?php
+				else :
+				?>				
+				<input type="text" name="custom-field-default-value" id="custom-field-default-value" size="25" />
+				<?php
+				endif;
+				?>
+			</td>
+		</tr>
+		<?php endif; ?>
+
+
+		<?php if( $current_field->has_properties && $current_field->name == 'Image' ) : ?>
+
+
+			Max Height:	<input type="text" name="custom-field-photo-height" id="custom-field-photo-height"/>
+			Max Width: <input type="text" name="custom-field-photo-width" id="custom-field-photo-width" />
+			Custom: <input type="text" name="custom-field-custom-params" id="custom-field-custom-params" />
+			<div style="color:blue;text-decoration:underline;"
+				onclick="div=document.getElementById('params');div.style.display='';"
+				>
+				Custom Options List
+			</div>
+			<div id="params"
+				style="display:none;"
+				onclick="this.style.display='none';">
+				<pre><?php echo param_list();  ?></pre>
+			</div>
+		<?php endif; ?>
+
+		<!-- Date Custom Field -->
+		<?php if( $current_field->has_properties && $current_field->name == 'Date' ) : ?>
+			Format:	<input type="text" name="custom-field-date-format" id="custom-field-date-format" /> (e.q. &nbsp; m/d/Y)
+		<?php endif; ?>
+		<!-- Date Custom Field -->
+
+		</tbody>
+		</table>
+		
+		</fieldset>
+		
+			<p class="submit" ><input name="cancel-create-custom-field" type="submit" id="cancel-custom-new-field" value="Cancel" /> <input name="finish-create-custom-field" type="submit" id="finish-create-custom-field" value="Finish" /></p>
+			
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+
+} //end class	
+
+	function param_list()
+	{
+		return ' 
+		 src = filename of source image
+		 new = create new image, not thumbnail of existing image.
+		       Requires "w" and "h" parameters set.
+		       [ex: &new=FF0000|75] - red background, 75% opacity
+		       Set to hex color string of background. Opacity is
+		       optional (defaults to 100% opaque).
+		   w = max width of output thumbnail in pixels
+		   h = max height of output thumbnail in pixels
+		  wp = max width for portrait images
+		  hp = max height for portrait images
+		  wl = max width for landscape images
+		  hl = max height for landscape images
+		  ws = max width for square images
+		  hs = max height for square images
+		   f = output image format ("jpeg", "png", or "gif")
+		   q = JPEG compression (1=worst, 95=best, 75=default)
+		  sx = left side of source rectangle (default = 0)
+		       (values 0 &lt; sx &lt; 1 represent percentage)
+		  sy = top side of source rectangle (default = 0)
+		       (values 0 &lt; sy &lt; 1 represent percentage)
+		  sw = width of source rectangle (default = fullwidth)
+		       (values 0 &lt; sw &lt; 1 represent percentage)
+		  sh = height of source rectangle (default = fullheight)
+		       (values 0 &lt; sh &lt; 1 represent percentage)
+		  zc = zoom-crop. Will auto-crop off the larger dimension
+		       so that the image will fill the smaller dimension
+		       (requires both "w" and "h", overrides "iar", "far")
+		       Set to "1" or "C" to zoom-crop towards the center,
+		       or set to "T", "B", "L", "R", "TL", "TR", "BL", "BR"
+		       to gravitate towards top/left/bottom/right directions
+		       (requies ImageMagick for values other than "C" or "1")
+		  bg = background hex color (default = FFFFFF)
+		  bc = border hex color (default = 000000)
+		fltr = filter system. Call as an array as follows:
+		       - "brit" (Brightness) [ex: &fltr[]=brit|&lt;value]
+		         where &lt;value is the amount +/- to adjust brightness
+		         (range -255 to 255)
+		         Availble in PHP5 with bundled GD only.
+		       - "cont" (Constrast) [ex: &fltr[]=cont|&lt;value&gt;]
+		         where &lt;value is the amount +/- to adjust contrast
+		         (range -255 to 255)
+		         Availble in PHP5 with bundled GD only.
+		       - "gam" (Gamma Correction) [ex:
+		         &fltr[]=gam|&lt;value&gt;]
+		         where &lt;value&gt; can be a number &gt;0 to 10+ (default 1.0)
+		         Must be &gt;0 (zero gives no effect). There is no max,
+		         although beyond 10 is pretty useless. Negative
+		         numbers actually do something, maybe not quite the
+		         desired effect, but interesting nonetheless.
+		       - "sat" (SATuration) [ex: &fltr[]=sat|&lt;value&gt;]
+		         where &lt;value&gt; is a number between zero (no change)
+		         and -100 (complete desaturation = grayscale), or it
+		         can be any positive number for increased saturation.
+		       - "ds" (DeSaturate) [ex: &fltr[]=ds|&lt;value&gt;]
+		         is an alias for "sat" except values are inverted
+		         (positive values remove color, negative values boost
+		         saturation)
+		       - "gray" (Grayscale) [ex: &fltr[]=gray]
+		         remove all color from image, make it grayscale
+		       - "th" (Threshold) [ex: &fltr[]=th|&lt;value&gt;]
+		         makes image greyscale, then sets all pixels brighter
+		         than &lt;value&gt; (range 0-255) to white, and all pixels
+		         darker than &lt;value&gt; to black
+		       - "rcd" (Reduce Color Depth) [ex:
+		         &fltr[]=rcd|&lt;c&gt;|&lt;d&gt;]
+		         where &lt;c&gt; is the number of colors (2-256) you want
+		         in the output image, and &lt;d&gt; is "1" for dithering
+		         (deault) or "0" for no dithering
+		       - "clr" (Colorize) [ex:
+		         &fltr[]=clr|&lt;value&gt;|&lt;color&gt;]
+		         where &lt;value&gt; is a number between 0 and 100 for the
+		         amount of colorization, and &lt;color&gt; is the hex color
+		         to colorize to.
+		       - "sep" (Sepia) [ex:
+		         &fltr[]=sep|&lt;value&gt;|&lt;color&gt;]
+		         where &lt;value&gt; is a number between 0 and 100 for the
+		         amount of colorization (default=50), and &lt;color&gt; is
+		         the hex color to colorize to (default=A28065).
+		         Note: this behaves differently when applied by
+		         ImageMagick, in which case 80 is default, and lower
+		         values give brighter/yellower images and higher
+		         values give darker/bluer images
+		       - "usm" (UnSharpMask) [ex:
+		         &fltr[]=usm|&lt;a&gt;|&lt;r&gt;|&lt;t&gt;]
+		         where &lt;a&gt; is the amount (default = 80), &lt;r&gt; is the
+		         radius (default = 0.5), &lt;t&gt; is the threshold
+		         (default = 3).
+		       - "blur" (Blur) [ex: &fltr[]=blur|&lt;radius&gt;]
+		         where (0 &lt; &lt;radius&gt; &lt; 25) (default = 1)
+		       - "gblr" (Gaussian Blur) [ex: &fltr[]=gblr]
+		         Availble in PHP5 with bundled GD only.
+		       - "sblr" (Selective Blur) [ex: &fltr[]=gblr]
+		         Availble in PHP5 with bundled GD only.
+		       - "smth" (Smooth) [ex: &fltr[]=smth|&lt;value&gt;]
+		         where &lt;value&gt; is the weighting value for the matrix
+		         (range -10 to 10, default 6)
+		         Availble in PHP5 with bundled GD only.
+		       - "lvl" (Levels)
+		         [ex: &fltr[]=lvl|&lt;channel&gt;|&lt;method&gt;|&lt;threshol&gt;d
+		         where &lt;channel&gt; can be one of "r", "g", "b", "a" (for
+		         Red, Green, Blue, Alpha respectively), or "*" for all
+		         RGB channels (default) based on grayscale average.
+		         ImageMagick methods can support multiple channels
+		         (eg "lvl|rg|3") but internal methods cannot (they will
+		         use first character of channel string as channel)
+		         &lt;method&gt; can be one of:
+		         0=Internal RGB;
+		         1=Internal Grayscale;
+		         2=ImageMagick Contrast-Stretch (default)
+		         3=ImageMagick Normalize (may appear over-saturated)
+		         &lt;threshold&gt; is how much of brightest/darkest pixels
+		         will be clipped in percent (default = 0.1%)
+		         Using default parameters (&fltr[]=lvl) is similar to
+		         Auto Contrast in Adobe Photoshop.
+		       - "wb" (White Balance) [ex: &fltr[]=wb|&lt;c&gt;]
+		         where &lt;c&gt; is the target hex color to white balance
+		         on, this color is what "should be" white, or light
+		         gray. The filter attempts to maintain brightness so
+		         any gray color can theoretically be used. If &lt;c&gt; is
+		         omitted the filter guesses based on brightest pixels
+		         in each of RGB
+		         OR &lt;c&gt; can be the percent of white clipping used
+		         to calculate auto-white-balance (default = 0.1%)
+		         NOTE: "wb" in default settings already gives an effect
+		         similar to "lvl", there is usually no need to use "lvl"
+		         if "wb" is already used.
+		       - "hist" (Histogram)
+		         [ex: &fltr[]=hist|&lt;b&gt;|&lt;c&gt;|&lt;w&gt;|&lt;h&gt;|&lt;a&gt;|&lt;o&gt;|&lt;x&gt;|&lt;y&gt;]
+		         Where &lt;b&gt; is the color band(s) to display, from back
+		         to front (one or more of "rgba*" for Red Green Blue
+		         Alpha and Grayscale respectively);
+		         &lt;c&gt; is a semicolon-seperated list of hex colors to
+		         use for each graph band (defaults to FF0000, 00FF00,
+		         0000FF, 999999, FFFFFF respectively);
+		         &lt;w&gt; and &lt;h&gt; are the width and height of the overlaid
+		         histogram in pixels, or if &lt;= 1 then percentage of
+		         source image width/height;
+		         &lt;a&gt; is the alignment (same as for "wmi" and "wmt");
+		         &lt;o&gt; is opacity from 0 (transparent) to 100 (opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         &lt;x&gt; and &lt;y&gt; are the edge margin in pixels (or percent
+		             if 0 &lt; (x|y) &lt; 1)
+		       - "over" (OVERlay/underlay image) overlays an image on
+		         the thumbnail, or overlays the thumbnail on another
+		         image (to create a picture frame for example)
+		         [ex: &fltr[]=over|&lt;i&gt;|&lt;u&gt;|&lt;m&gt;|&lt;o&gt;]
+		         where &lt;i&gt; is the image filename; &lt;u&gt; is "0" (default)
+		         for overlay the image on top of the thumbnail or "1"
+		         for overlay the thumbnail on top of the image; &lt;m&gt; is
+		         the margin - can be absolute pixels, or if &lt; 1 is a
+		         percentage of the thumbnail size [must be &lt; 0.5]
+		         (default is 0 for overlay and 10% for underlay);
+		         &lt;o&gt; is opacity (0 = transparent, 100 = opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         (thanks raynerapeï¿œgmail*com, shabazz3ï¿œmsu*edu)
+		       - "wmi" (WaterMarkImage)
+		         [ex: &fltr[]=wmi|&lt;f&gt;|&lt;a&gt;|&lt;o&gt;|&lt;x&gt;|&lt;y&gt;|&lt;r&gt;] where
+		         &lt;f&gt; is the filename of the image to overlay;
+		         &lt;a&gt; is the alignment (one of BR, BL, TR, TL, C,
+		             R, L, T, B, *) where B=bottom, T=top, L=left,
+		             R=right, C=centre, *=tile)
+		             *or*
+		             an absolute position in pixels (from top-left
+		             corner of canvas to top-left corner of overlay)
+		             in format {xoffset}x{yoffset} (eg: "10x20")
+		             note: this is center position of image if &lt;&gt;x
+		             and &lt;y&gt; are set
+		         &lt;o&gt; is opacity from 0 (transparent) to 100 (opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         &lt;x&gt; and &lt;y&gt; are the edge (and inter-tile) margin in
+		             pixels (or percent if 0 &lt; (x|y) &lt; 1)
+		             *or*
+		             if &lt;a&gt; is absolute-position format then &lt;x&gt; and
+		             &lt;y&gt; represent maximum width and height that the
+		             watermark image will be scaled to fit inside
+		         &lt;r&gt; is rotation angle of overlaid watermark
+		       - "wmt" (WaterMarkText)
+		         [ex: &fltr[]=wmt|&lt;t&gt;|&lt;s&gt;|&lt;a&gt;|&lt;c&gt;|&lt;f&gt;|&lt;o&gt;|&lt;m&gt;|&lt;n&gt;|&lt;b&gt;|&lt;O&gt;|&lt;x&gt;]
+		         where:
+		         &lt;t&gt; is the text to use as a watermark;
+		             URLencoded Unicode HTMLentities must be used for
+		               characters beyond chr(127). For example, the
+		               "eighth note" character (U+266A) is represented
+		               as "&#9834;" and then urlencoded to "%26%239834%3B"
+		             Any instance of metacharacters will be replaced
+		             with their calculated value. Currently supported:
+		               ^Fb = source image filesize in bytes
+		               ^Fk = source image filesize in kilobytes
+		               ^Fm = source image filesize in megabytes
+		               ^X  = source image width in pixels
+		               ^Y  = source image height in pixels
+		               ^x  = thumbnail width in pixels
+		               ^y  = thumbnail height in pixels
+		               ^^  = the character ^
+		         &lt;s&gt; is the font size (1-5 for built-in font, or point
+		             size for TrueType fonts);
+		         &lt;a&gt; is the alignment (one of BR, BL, TR, TL, C, R, L,
+		             T, B, * where B=bottom, T=top, L=left, R=right,
+		             C=centre, *=tile);
+		             *or*
+		             an absolute position in pixels (from top-left
+		             corner of canvas to top-left corner of overlay)
+		             in format {xoffset}x{yoffset} (eg: "10x20")
+		         &lt;c&gt; is the hex color of the text;
+		         &lt;f&gt; is the filename of the TTF file (optional, if
+		             omitted a built-in font will be used);
+		         &lt;o&gt; is opacity from 0 (transparent) to 100 (opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         &lt;m&gt; is the edge (and inter-tile) margin in percent;
+		         &lt;n&gt; is the angle
+		         &lt;b&gt; is the hex color of the background;
+		         &lt;O&gt; is background opacity from 0 (transparent) to
+		             100 (opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         &lt;x&gt; is the direction(s) in which the background is
+		             extended (either "x" or "y" (or both, but both
+		             will obscure entire image))
+		             Note: works with TTF fonts only, not built-in
+		       - "flip" [ex: &fltr[]=flip|x   or   &fltr[]=flip|y]
+		         flip image on X or Y axis
+		       - "ric" [ex: &fltr[]=ric|&lt;x&gt;|&lt;y&gt;]
+		         rounds off the corners of the image (to transparent
+		         for PNG output), where &lt;x&gt; is the horizontal radius
+		         of the curve and &lt;y&gt; is the vertical radius
+		       - "elip" [ex: &fltr[]=elip]
+		         similar to rounded corners but more extreme
+		       - "mask" [ex: &fltr[]=mask|filename.png]
+		         greyscale values of mask are applied as the alpha
+		         channel to the main image. White is opaque, black
+		         is transparent.
+		       - "bvl" (BeVeL) [ex:
+		         &fltr[]=bvl|&lt;w&gt;|&lt;c1&gt;|&lt;c2&gt;]
+		         where &lt;w&gt; is the bevel width, &lt;c1&gt; is the hex color
+		         for the top and left shading, &lt;c2&gt; is the hex color
+		         for the bottom and right shading
+		       - "bord" (BORDer) [ex:
+		         &fltr[]=bord|&lt;w&gt;|&lt;rx&gt;|&lt;ry&gt;|&lt;&gt;c
+		         where &lt;w&gt; is the width in pixels, &lt;rx&gt;
+			 and &lt;ry&gt; are
+		         horizontal and vertical radii for rounded corners,
+		         and &lt;c&gt; is the hex color of the border
+		       - "fram" (FRAMe) draws a frame, similar to "bord" but
+		         more configurable
+		         [ex: &fltr[]=fram|&lt;w1&gt;|&lt;w2&gt;|&lt;c1&gt;|&lt;c2&gt;|&lt;c3&gt;]
+		         where &lt;w1&gt; is the width of the main border,
+			 &lt;w2&gt; is
+		         the width of each side of the bevel part, &lt;c1&gt; is the
+		         hex color of the main border, &lt;c2&gt; is the highlight
+		         bevel color, &lt;c3&gt; is the shadow bevel color
+		       - "drop" (DROP shadow)
+		         [ex: &fltr[]=drop|&lt;d&gt;|&lt;w&gt;|&lt;clr&gt;|&lt;a&gt;]
+		         where &lt;d&gt; is distance from image to shadow,
+			 &lt;w&gt; is
+		         width of shadow fade (not yet implemented),
+			 &lt;clr&gt; is
+		         the hex color of the shadow, and &lt;a&gt; is the angle of
+		         the shadow (default=225)
+		       - "crop" (CROP image)
+		         [ex:
+			 &fltr[]=crop|&lt;l&gt;|&lt;r&gt;|&lt;t&gt;|&lt;b&gt;]
+		         where &lt;l&gt; is the number of pixels to crop from the left
+		         side of the resized image; &lt;r&gt;, &lt;t&gt;,
+			 &lt;b&gt; are for right,
+		         top and bottom respectively. Where (0 &lt; x &lt; 1) the
+		         value will be used as a percentage of width/height.
+		         Left and top crops take precedence over right and
+		         bottom values. Cropping will be limited such that at
+		         least 1 pixel of width and height always remains.
+		       - "rot" (ROTate)
+		         [ex: &fltr[]=rot|&lt;a&gt;|&lt;b&gt;]
+		         where &lt;a&gt; is the rotation angle in degrees;
+			 &lt;b&gt; is the
+		         background hex color. Similar to regular "ra" parameter
+		         but is applied in filter order after regular processing
+		         so you can rotate output of other filters.
+		       - "size" (reSIZE)
+		         [ex: &fltr[]=size|&lt;x&gt;|&lt;y&gt;|&lt;s&gt;]
+		         where &lt;x&gt; is the horizontal dimension in pixels,
+			 &lt;y&gt; is
+		         the vertical dimension in pixels, &lt;s&gt; is boolean whether
+		         to stretch (if 1) or resize proportionately (0, default)
+		         &lt;x&gt; and &lt;y&gt; will be interpreted as percentage of current
+		         output image size if values are (0 &lt; X &lt; 1)
+		         NOTE: do NOT use this filter unless absolutely neccesary.
+		         It is only provided for cases where other filters need to
+		         have absolute positioning based on source image and the
+		         resultant image should be resized after other filters are
+		         applied. This filter is less efficient than the standard
+		         resizing procedures.
+		       - "stc" (Source Transparent Color)
+		         [ex: &fltr[]=stc|&lt;c&gt;|&lt;n&gt;|<x&gt;]
+		         where <c&gt; is the hex color of the target color to be made
+		         transparent; <n&gt; is the minimum threshold in percent (all
+		         pixels within <n&gt;% of the target color will be 100%
+		         transparent, default <n&gt;=5); <x&gt; is the maximum threshold
+		         in percent (all pixels more than <x&gt;% from the target
+		         color will be 100% opaque, default <x&gt;=10); pixels between
+		         the two thresholds will be partially transparent.
+		md5s = MD5 hash of the source image -- if this parameter is
+		       passed with the hash of the source image then the
+		       source image is not checked for existance or
+		       modification and the cached file is used (if
+		       available). If "md5s" is passed an empty string then
+		       phpThumb.php dies and outputs the correct MD5 hash
+		       value.  This parameter is the single-file equivalent
+		       of "cache_source_filemtime_ignore_*" configuration
+		       paramters
+		 xto = EXIF Thumbnail Only - set to only extract EXIF
+		       thumbnail and not do any additional processing
+		  ra = Rotate by Angle: angle of rotation in degrees
+		       positive = counterclockwise, negative = clockwise
+		  ar = Auto Rotate: set to "x" to use EXIF orientation
+		       stored by camera. Can also be set to "l" or "L"
+		       for landscape, or "p" or "P" for portrait. "l"
+		       and "P" rotate the image clockwise, "L" and "p"
+		       rotate the image counter-clockwise.
+		 sfn = Source Frame Number - use this frame/page number for
+		       multi-frame/multi-page source images (GIF, TIFF, etc)
+		 aoe = Output Allow Enlarging - override the setting for
+		       $CONFIG["output_allow_enlarging"] (1=on, 0=off)
+		       ("far" and "iar" both override this and allow output
+		       larger than input)
+		 iar = Ignore Aspect Ratio - disable proportional resizing
+		       and stretch image to fit "h" & "w" (which must both
+		       be set).  (1=on, 0=off)  (overrides "far")
+		 far = Force Aspect Ratio - image will be created at size
+		       specified by "w" and "h" (which must both be set).
+		       Alignment: L=left,R=right,T=top,B=bottom,C=center
+		       BL,BR,TL,TR use the appropriate direction if the
+		       image is landscape or portrait.
+		 dpi = Dots Per Inch - input DPI setting when importing from
+		       vector image format such as PDF, WMF, etc
+		 sia = Save Image As - default filename to save generated
+		       image as. Specify the base filename, the extension
+		       (eg: ".png") will be automatically added
+		maxb = MAXimum Byte size - output quality is auto-set to
+		       fit thumbnail into "maxb" bytes  (compression
+		       quality is adjusted for JPEG, bit depth is adjusted
+		       for PNG and GIF)
+		down = filename to save image to. If this is set the
+		       browser will prompt to save to this filename rather
+		       than display the image
+		
+			';
+		}
+
+		
+?>
Index: /afridex/plugins/fresh-page/phpthumb.functions.php
===================================================================
--- /afridex/plugins/fresh-page/phpthumb.functions.php (revision 21)
+++ /afridex/plugins/fresh-page/phpthumb.functions.php (revision 21)
@@ -0,0 +1,963 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// phpthumb.functions.php - general support functions       //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+class phpthumb_functions {
+
+	function user_function_exists($functionname) {
+		if (function_exists('get_defined_functions')) {
+			static $get_defined_functions = array();
+			if (empty($get_defined_functions)) {
+				$get_defined_functions = get_defined_functions();
+			}
+			return in_array(strtolower($functionname), $get_defined_functions['user']);
+		}
+		return function_exists($functionname);
+	}
+
+
+	function builtin_function_exists($functionname) {
+		if (function_exists('get_defined_functions')) {
+			static $get_defined_functions = array();
+			if (empty($get_defined_functions)) {
+				$get_defined_functions = get_defined_functions();
+			}
+			return in_array(strtolower($functionname), $get_defined_functions['internal']);
+		}
+		return function_exists($functionname);
+	}
+
+
+	function version_compare_replacement_sub($version1, $version2, $operator='') {
+		// If you specify the third optional operator argument, you can test for a particular relationship.
+		// The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively.
+		// Using this argument, the function will return 1 if the relationship is the one specified by the operator, 0 otherwise.
+
+		// If a part contains special version strings these are handled in the following order: dev < (alpha = a) < (beta = b) < RC < pl
+		static $versiontype_lookup = array();
+		if (empty($versiontype_lookup)) {
+			$versiontype_lookup['dev']   = 10001;
+			$versiontype_lookup['a']     = 10002;
+			$versiontype_lookup['alpha'] = 10002;
+			$versiontype_lookup['b']     = 10003;
+			$versiontype_lookup['beta']  = 10003;
+			$versiontype_lookup['RC']    = 10004;
+			$versiontype_lookup['pl']    = 10005;
+		}
+		if (isset($versiontype_lookup[$version1])) {
+			$version1 = $versiontype_lookup[$version1];
+		}
+		if (isset($versiontype_lookup[$version2])) {
+			$version2 = $versiontype_lookup[$version2];
+		}
+
+		switch ($operator) {
+			case '<':
+			case 'lt':
+				return intval($version1 < $version2);
+				break;
+			case '<=':
+			case 'le':
+				return intval($version1 <= $version2);
+				break;
+			case '>':
+			case 'gt':
+				return intval($version1 > $version2);
+				break;
+			case '>=':
+			case 'ge':
+				return intval($version1 >= $version2);
+				break;
+			case '==':
+			case '=':
+			case 'eq':
+				return intval($version1 == $version2);
+				break;
+			case '!=':
+			case '<>':
+			case 'ne':
+				return intval($version1 != $version2);
+				break;
+		}
+		if ($version1 == $version2) {
+			return 0;
+		} elseif ($version1 < $version2) {
+			return -1;
+		}
+		return 1;
+	}
+
+
+	function version_compare_replacement($version1, $version2, $operator='') {
+		if (function_exists('version_compare')) {
+			// built into PHP v4.1.0+
+			return version_compare($version1, $version2, $operator);
+		}
+
+		// The function first replaces _, - and + with a dot . in the version strings
+		$version1 = strtr($version1, '_-+', '...');
+		$version2 = strtr($version2, '_-+', '...');
+
+		// and also inserts dots . before and after any non number so that for example '4.3.2RC1' becomes '4.3.2.RC.1'.
+		// Then it splits the results like if you were using explode('.',$ver). Then it compares the parts starting from left to right.
+		$version1 = eregi_replace('([0-9]+)([A-Z]+)([0-9]+)', '\\1.\\2.\\3', $version1);
+		$version2 = eregi_replace('([0-9]+)([A-Z]+)([0-9]+)', '\\1.\\2.\\3', $version2);
+
+		$parts1 = explode('.', $version1);
+		$parts2 = explode('.', $version1);
+		$parts_count = max(count($parts1), count($parts2));
+		for ($i = 0; $i < $parts_count; $i++) {
+			$comparison = phpthumb_functions::version_compare_replacement_sub($version1, $version2, $operator);
+			if ($comparison != 0) {
+				return $comparison;
+			}
+		}
+		return 0;
+	}
+
+
+	function phpinfo_array() {
+		static $phpinfo_array = array();
+		if (empty($phpinfo_array)) {
+			ob_start();
+			phpinfo();
+			$phpinfo = ob_get_contents();
+			ob_end_clean();
+			$phpinfo_array = explode("\n", $phpinfo);
+		}
+		return $phpinfo_array;
+	}
+
+
+	function exif_info() {
+		static $exif_info = array();
+		if (empty($exif_info)) {
+			// based on code by johnschaefer at gmx dot de
+			// from PHP help on gd_info()
+			$exif_info = array(
+				'EXIF Support'           => '',
+				'EXIF Version'           => '',
+				'Supported EXIF Version' => '',
+				'Supported filetypes'    => ''
+			);
+			$phpinfo_array = phpthumb_functions::phpinfo_array();
+			foreach ($phpinfo_array as $line) {
+				$line = trim(strip_tags($line));
+				foreach ($exif_info as $key => $value) {
+					if (strpos($line, $key) === 0) {
+						$newvalue = trim(str_replace($key, '', $line));
+						$exif_info[$key] = $newvalue;
+					}
+				}
+			}
+		}
+		return $exif_info;
+	}
+
+
+	function ImageTypeToMIMEtype($imagetype) {
+		if (function_exists('image_type_to_mime_type') && ($imagetype >= 1) && ($imagetype <= 16)) {
+			// PHP v4.3.0+
+			return image_type_to_mime_type($imagetype);
+		}
+		static $image_type_to_mime_type = array(
+			1  => 'image/gif',                     // IMAGETYPE_GIF
+			2  => 'image/jpeg',                    // IMAGETYPE_JPEG
+			3  => 'image/png',                     // IMAGETYPE_PNG
+			4  => 'application/x-shockwave-flash', // IMAGETYPE_SWF
+			5  => 'image/psd',                     // IMAGETYPE_PSD
+			6  => 'image/bmp',                     // IMAGETYPE_BMP
+			7  => 'image/tiff',                    // IMAGETYPE_TIFF_II (intel byte order)
+			8  => 'image/tiff',                    // IMAGETYPE_TIFF_MM (motorola byte order)
+			9  => 'application/octet-stream',      // IMAGETYPE_JPC
+			10 => 'image/jp2',                     // IMAGETYPE_JP2
+			11 => 'application/octet-stream',      // IMAGETYPE_JPX
+			12 => 'application/octet-stream',      // IMAGETYPE_JB2
+			13 => 'application/x-shockwave-flash', // IMAGETYPE_SWC
+			14 => 'image/iff',                     // IMAGETYPE_IFF
+			15 => 'image/vnd.wap.wbmp',            // IMAGETYPE_WBMP
+			16 => 'image/xbm',                     // IMAGETYPE_XBM
+
+			'gif'  => 'image/gif',                 // IMAGETYPE_GIF
+			'jpg'  => 'image/jpeg',                // IMAGETYPE_JPEG
+			'jpeg' => 'image/jpeg',                // IMAGETYPE_JPEG
+			'png'  => 'image/png',                 // IMAGETYPE_PNG
+			'bmp'  => 'image/bmp',                 // IMAGETYPE_BMP
+			'ico'  => 'image/x-icon',
+		);
+
+		return (isset($image_type_to_mime_type[$imagetype]) ? $image_type_to_mime_type[$imagetype] : false);
+	}
+
+
+	function HexCharDisplay($string) {
+		$len = strlen($string);
+		$output = '';
+		for ($i = 0; $i < $len; $i++) {
+			$output .= ' 0x'.str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
+		}
+		return $output;
+	}
+
+
+	function IsHexColor($HexColorString) {
+		return eregi('^[0-9A-F]{6}$', $HexColorString);
+	}
+
+
+	function ImageColorAllocateAlphaSafe(&$gdimg_hexcolorallocate, $R, $G, $B, $alpha=false) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.2', '>=') && ($alpha !== false)) {
+			return ImageColorAllocateAlpha($gdimg_hexcolorallocate, $R, $G, $B, intval($alpha));
+		} else {
+			return ImageColorAllocate($gdimg_hexcolorallocate, $R, $G, $B);
+		}
+	}
+
+	function ImageHexColorAllocate(&$gdimg_hexcolorallocate, $HexColorString, $dieOnInvalid=false, $alpha=false) {
+		if (!is_resource($gdimg_hexcolorallocate)) {
+			die('$gdimg_hexcolorallocate is not a GD resource in ImageHexColorAllocate()');
+		}
+		if (phpthumb_functions::IsHexColor($HexColorString)) {
+			$R = hexdec(substr($HexColorString, 0, 2));
+			$G = hexdec(substr($HexColorString, 2, 2));
+			$B = hexdec(substr($HexColorString, 4, 2));
+			return phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_hexcolorallocate, $R, $G, $B, $alpha);
+		}
+		if ($dieOnInvalid) {
+			die('Invalid hex color string: "'.$HexColorString.'"');
+		}
+		return ImageColorAllocate($gdimg_hexcolorallocate, 0x00, 0x00, 0x00);
+	}
+
+
+	function HexColorXOR($hexcolor) {
+		return strtoupper(str_pad(dechex(~hexdec($hexcolor) & 0xFFFFFF), 6, '0', STR_PAD_LEFT));
+	}
+
+
+	function GetPixelColor(&$img, $x, $y) {
+		if (!is_resource($img)) {
+			return false;
+		}
+		return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y));
+	}
+
+
+	function GrayscaleValue($r, $g, $b) {
+		return round(($r * 0.30) + ($g * 0.59) + ($b * 0.11));
+	}
+
+
+	function GrayscalePixel($OriginalPixel) {
+		$gray = phpthumb_functions::GrayscaleValue($OriginalPixel['red'], $OriginalPixel['green'], $OriginalPixel['blue']);
+		return array('red'=>$gray, 'green'=>$gray, 'blue'=>$gray);
+	}
+
+
+	function GrayscalePixelRGB($rgb) {
+		$r = ($rgb >> 16) & 0xFF;
+		$g = ($rgb >>  8) & 0xFF;
+		$b =  $rgb        & 0xFF;
+		return ($r * 0.299) + ($g * 0.587) + ($b * 0.114);
+	}
+
+
+	function ImageCopyResampleBicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) {
+		// ron at korving dot demon dot nl
+		// http://www.php.net/imagecopyresampled
+
+		$scaleX = ($src_w - 1) / $dst_w;
+		$scaleY = ($src_h - 1) / $dst_h;
+
+		$scaleX2 = $scaleX / 2.0;
+		$scaleY2 = $scaleY / 2.0;
+
+		$isTrueColor = ImageIsTrueColor($src_img);
+
+		for ($y = $src_y; $y < $src_y + $dst_h; $y++) {
+			$sY   = $y * $scaleY;
+			$siY  = (int) $sY;
+			$siY2 = (int) $sY + $scaleY2;
+
+			for ($x = $src_x; $x < $src_x + $dst_w; $x++) {
+				$sX   = $x * $scaleX;
+				$siX  = (int) $sX;
+				$siX2 = (int) $sX + $scaleX2;
+
+				if ($isTrueColor) {
+
+					$c1 = ImageColorAt($src_img, $siX, $siY2);
+					$c2 = ImageColorAt($src_img, $siX, $siY);
+					$c3 = ImageColorAt($src_img, $siX2, $siY2);
+					$c4 = ImageColorAt($src_img, $siX2, $siY);
+
+					$r = (( $c1             +  $c2             +  $c3             +  $c4            ) >> 2) & 0xFF0000;
+					$g = ((($c1 & 0x00FF00) + ($c2 & 0x00FF00) + ($c3 & 0x00FF00) + ($c4 & 0x00FF00)) >> 2) & 0x00FF00;
+					$b = ((($c1 & 0x0000FF) + ($c2 & 0x0000FF) + ($c3 & 0x0000FF) + ($c4 & 0x0000FF)) >> 2);
+
+				} else {
+
+					$c1 = ImageColorsForIndex($src_img, ImageColorAt($src_img, $siX, $siY2));
+					$c2 = ImageColorsForIndex($src_img, ImageColorAt($src_img, $siX, $siY));
+					$c3 = ImageColorsForIndex($src_img, ImageColorAt($src_img, $siX2, $siY2));
+					$c4 = ImageColorsForIndex($src_img, ImageColorAt($src_img, $siX2, $siY));
+
+					$r = ($c1['red']   + $c2['red']   + $c3['red']   + $c4['red'] )  << 14;
+					$g = ($c1['green'] + $c2['green'] + $c3['green'] + $c4['green']) <<  6;
+					$b = ($c1['blue']  + $c2['blue']  + $c3['blue']  + $c4['blue'] ) >>  2;
+
+				}
+				ImageSetPixel($dst_img, $dst_x + $x - $src_x, $dst_y + $y - $src_y, $r+$g+$b);
+			}
+		}
+		return true;
+	}
+
+
+	function ImageCreateFunction($x_size, $y_size) {
+		$ImageCreateFunction = 'ImageCreate';
+		if (phpthumb_functions::gd_version() >= 2.0) {
+			$ImageCreateFunction = 'ImageCreateTrueColor';
+		}
+		if (!function_exists($ImageCreateFunction)) {
+			return phpthumb::ErrorImage($ImageCreateFunction.'() does not exist - no GD support?');
+		}
+		if (($x_size <= 0) || ($y_size <= 0)) {
+			return phpthumb::ErrorImage('Invalid image dimensions: '.$ImageCreateFunction.'('.$x_size.', '.$y_size.')');
+		}
+		return $ImageCreateFunction($x_size, $y_size);
+	}
+
+
+	function ImageCopyRespectAlpha(&$dst_im, &$src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct=100) {
+		for ($x = $src_x; $x < $src_w; $x++) {
+			for ($y = $src_y; $y < $src_h; $y++) {
+				$RealPixel    = phpthumb_functions::GetPixelColor($dst_im, $dst_x + $x, $dst_y + $y);
+				$OverlayPixel = phpthumb_functions::GetPixelColor($src_im, $x, $y);
+				$alphapct = $OverlayPixel['alpha'] / 127;
+				$opacipct = $pct / 100;
+				$overlaypct = (1 - $alphapct) * $opacipct;
+
+				$newcolor = phpthumb_functions::ImageColorAllocateAlphaSafe(
+					$dst_im,
+					round($RealPixel['red']   * (1 - $overlaypct)) + ($OverlayPixel['red']   * $overlaypct),
+					round($RealPixel['green'] * (1 - $overlaypct)) + ($OverlayPixel['green'] * $overlaypct),
+					round($RealPixel['blue']  * (1 - $overlaypct)) + ($OverlayPixel['blue']  * $overlaypct),
+					//$RealPixel['alpha']);
+					0);
+
+				ImageSetPixel($dst_im, $dst_x + $x, $dst_y + $y, $newcolor);
+			}
+		}
+		return true;
+	}
+
+
+	function ProportionalResize($old_width, $old_height, $new_width=false, $new_height=false) {
+		$old_aspect_ratio = $old_width / $old_height;
+		if (($new_width === false) && ($new_height === false)) {
+			return false;
+		} elseif ($new_width === false) {
+			$new_width = $new_height * $old_aspect_ratio;
+		} elseif ($new_height === false) {
+			$new_height = $new_width / $old_aspect_ratio;
+		}
+		$new_aspect_ratio = $new_width / $new_height;
+		if ($new_aspect_ratio == $old_aspect_ratio) {
+			// great, done
+		} elseif ($new_aspect_ratio < $old_aspect_ratio) {
+			// limited by width
+			$new_height = $new_width / $old_aspect_ratio;
+		} elseif ($new_aspect_ratio > $old_aspect_ratio) {
+			// limited by height
+			$new_width = $new_height * $old_aspect_ratio;
+		}
+		return array(round($new_width), round($new_height));
+	}
+
+
+	function FunctionIsDisabled($function) {
+		static $DisabledFunctions = null;
+		if (is_null($DisabledFunctions)) {
+			$disable_functions_local  = explode(',',     @ini_get('disable_functions'));
+			$disable_functions_global = explode(',', @get_cfg_var('disable_functions'));
+			foreach ($disable_functions_local as $key => $value) {
+				$DisabledFunctions[$value] = 'local';
+			}
+			foreach ($disable_functions_global as $key => $value) {
+				$DisabledFunctions[$value] = 'global';
+			}
+			if (@ini_get('safe_mode')) {
+				$DisabledFunctions['shell_exec'] = 'local';
+			}
+		}
+		return isset($DisabledFunctions[$function]);
+	}
+
+
+	function SafeExec($command) {
+		static $AllowedExecFunctions = array();
+		if (empty($AllowedExecFunctions)) {
+			$AllowedExecFunctions = array('shell_exec'=>true, 'passthru'=>true, 'system'=>true, 'exec'=>true);
+			foreach ($AllowedExecFunctions as $key => $value) {
+				$AllowedExecFunctions[$key] = !phpthumb_functions::FunctionIsDisabled($key);
+			}
+		}
+		foreach ($AllowedExecFunctions as $execfunction => $is_allowed) {
+			if (!$is_allowed) {
+				continue;
+			}
+			switch ($execfunction) {
+				case 'passthru':
+					ob_start();
+					$execfunction($command);
+					$returnvalue = ob_get_contents();
+					ob_end_clean();
+					break;
+
+				case 'shell_exec':
+				case 'system':
+				case 'exec':
+				default:
+					ob_start();
+					$returnvalue = $execfunction($command);
+					ob_end_clean();
+					break;
+			}
+			return $returnvalue;
+		}
+		return false;
+	}
+
+
+	function ApacheLookupURIarray($filename) {
+		// apache_lookup_uri() only works when PHP is installed as an Apache module.
+		if (php_sapi_name() == 'apache') {
+			$keys = array('status', 'the_request', 'status_line', 'method', 'content_type', 'handler', 'uri', 'filename', 'path_info', 'args', 'boundary', 'no_cache', 'no_local_copy', 'allowed', 'send_bodyct', 'bytes_sent', 'byterange', 'clength', 'unparsed_uri', 'mtime', 'request_time');
+			if ($apacheLookupURIobject = @apache_lookup_uri($filename)) {
+				$apacheLookupURIarray = array();
+				foreach ($keys as $key) {
+					$apacheLookupURIarray[$key] = @$apacheLookupURIobject->$key;
+				}
+				return $apacheLookupURIarray;
+			}
+		}
+		return false;
+	}
+
+
+	function gd_is_bundled() {
+		static $isbundled = null;
+		if (is_null($isbundled)) {
+			$gd_info = gd_info();
+			$isbundled = (strpos($gd_info['GD Version'], 'bundled') !== false);
+		}
+		return $isbundled;
+	}
+
+
+	function gd_version($fullstring=false) {
+		static $cache_gd_version = array();
+		if (empty($cache_gd_version)) {
+			$gd_info = gd_info();
+			if (eregi('bundled \((.+)\)$', $gd_info['GD Version'], $matches)) {
+				$cache_gd_version[1] = $gd_info['GD Version'];  // e.g. "bundled (2.0.15 compatible)"
+				$cache_gd_version[0] = (float) $matches[1];     // e.g. "2.0" (not "bundled (2.0.15 compatible)")
+			} else {
+				$cache_gd_version[1] = $gd_info['GD Version'];                       // e.g. "1.6.2 or higher"
+				$cache_gd_version[0] = (float) substr($gd_info['GD Version'], 0, 3); // e.g. "1.6" (not "1.6.2 or higher")
+			}
+		}
+		return $cache_gd_version[intval($fullstring)];
+	}
+
+
+	function filesize_remote($remotefile, $timeout=10) {
+		$size = false;
+		$url = parse_url($remotefile);
+		if ($fp = @fsockopen($url['host'], ($url['port'] ? $url['port'] : 80), $errno, $errstr, $timeout)) {
+			fwrite($fp, 'HEAD '.@$url['path'].@$url['query'].' HTTP/1.0'."\r\n".'Host: '.@$url['host']."\r\n\r\n");
+			if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=')) {
+				stream_set_timeout($fp, $timeout);
+			}
+			while (!feof($fp)) {
+				$headerline = fgets($fp, 4096);
+				if (eregi('^Content-Length: (.*)', $headerline, $matches)) {
+					$size = intval($matches[1]);
+					break;
+				}
+			}
+			fclose ($fp);
+		}
+		return $size;
+	}
+
+
+	function filedate_remote($remotefile, $timeout=10) {
+		$date = false;
+		$url = parse_url($remotefile);
+		if ($fp = @fsockopen($url['host'], ($url['port'] ? $url['port'] : 80), $errno, $errstr, $timeout)) {
+			fwrite($fp, 'HEAD '.@$url['path'].@$url['query'].' HTTP/1.0'."\r\n".'Host: '.@$url['host']."\r\n\r\n");
+			if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=')) {
+				stream_set_timeout($fp, $timeout);
+			}
+			while (!feof($fp)) {
+				$headerline = fgets($fp, 4096);
+				if (eregi('^Last-Modified: (.*)', $headerline, $matches)) {
+					$date = strtotime($matches[1]) - date('Z');
+					break;
+				}
+			}
+			fclose ($fp);
+		}
+		return $date;
+	}
+
+
+	function md5_file_safe($filename) {
+		// md5_file() doesn't exist in PHP < 4.2.0
+		if (function_exists('md5_file')) {
+			return md5_file($filename);
+		}
+		if ($fp = @fopen($filename, 'rb')) {
+			$rawData = '';
+			do {
+				$buffer = fread($fp, 8192);
+				$rawData .= $buffer;
+			} while (strlen($buffer) > 0);
+			fclose($fp);
+			return md5($rawData);
+		}
+		return false;
+	}
+
+
+	function nonempty_min() {
+		$arg_list = func_get_args();
+		$acceptable = array();
+		foreach ($arg_list as $arg) {
+			if ($arg) {
+				$acceptable[] = $arg;
+			}
+		}
+		return min($acceptable);
+	}
+
+
+	function LittleEndian2String($number, $minbytes=1) {
+		$intstring = '';
+		while ($number > 0) {
+			$intstring = $intstring.chr($number & 255);
+			$number >>= 8;
+		}
+		return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
+	}
+
+	function OneOfThese() {
+		// return the first useful (non-empty/non-zero/non-false) value from those passed
+		$arg_list = func_get_args();
+		foreach ($arg_list as $key => $value) {
+			if ($value) {
+				return $value;
+			}
+		}
+		return false;
+	}
+
+	function CaseInsensitiveInArray($needle, $haystack) {
+		$needle = strtolower($needle);
+		foreach ($haystack as $key => $value) {
+			if (is_array($value)) {
+				// skip?
+			} elseif ($needle == strtolower($value)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function URLreadFsock($host, $file, &$errstr, $successonly=true, $port=80, $timeout=10) {
+		if (!function_exists('fsockopen') || phpthumb_functions::FunctionIsDisabled('fsockopen')) {
+			$errstr = 'fsockopen() unavailable';
+			return false;
+		}
+		if ($fp = @fsockopen($host, 80, $errno, $errstr, $timeout)) {
+			$out  = 'GET '.$file.' HTTP/1.0'."\r\n";
+			$out .= 'Host: '.$host."\r\n";
+			$out .= 'Connection: Close'."\r\n\r\n";
+			fwrite($fp, $out);
+
+			$isHeader = true;
+			$Data_header = '';
+			$Data_body   = '';
+			$header_newlocation = '';
+			while (!feof($fp)) {
+				$line = fgets($fp, 1024);
+				if ($isHeader) {
+					$Data_header .= $line;
+				} else {
+					$Data_body .= $line;
+				}
+				if (eregi('^HTTP/[\\.0-9]+ ([0-9]+) (.+)$', rtrim($line), $matches)) {
+					list($dummy, $errno, $errstr) = $matches;
+					$errno = intval($errno);
+				} elseif (eregi('^Location: (.*)$', rtrim($line), $matches)) {
+					$header_newlocation = $matches[1];
+				}
+				if ($isHeader && ($line == "\r\n")) {
+					$isHeader = false;
+					if ($successonly) {
+						switch ($errno) {
+							case 200:
+								// great, continue
+								break;
+
+							default:
+								$errstr = $errno.' '.$errstr.($header_newlocation ? '; Location: '.$header_newlocation : '');
+								fclose($fp);
+								return false;
+								break;
+						}
+					}
+				}
+			}
+			fclose($fp);
+			return $Data_body;
+		}
+		return null;
+	}
+
+	function CleanUpURLencoding($url, $queryseperator='&') {
+		if (!eregi('^http', $url)) {
+			return $url;
+		}
+		$parse_url = @parse_url($url);
+		$pathelements = explode('/', $parse_url['path']);
+		$CleanPathElements = array();
+		$TranslationMatrix = array(' '=>'%20');
+		foreach ($pathelements as $key => $pathelement) {
+			$CleanPathElements[] = strtr($pathelement, $TranslationMatrix);
+		}
+		foreach ($CleanPathElements as $key => $value) {
+			if (!$value) {
+				unset($CleanPathElements[$key]);
+			}
+		}
+
+		$queries = explode($queryseperator, @$parse_url['query']);
+		$CleanQueries = array();
+		foreach ($queries as $key => $query) {
+			@list($param, $value) = explode('=', $query);
+			$CleanQueries[] = strtr($param, $TranslationMatrix).($value ? '='.strtr($value, $TranslationMatrix) : '');
+		}
+		foreach ($CleanQueries as $key => $value) {
+			if (!$value) {
+				unset($CleanQueries[$key]);
+			}
+		}
+
+		$cleaned_url  = $parse_url['scheme'].'://';
+		$cleaned_url .= (@$parse_url['username'] ? $parse_url['host'].(@$parse_url['password'] ? ':'.$parse_url['password'] : '').'@' : '');
+		$cleaned_url .= $parse_url['host'];
+		$cleaned_url .= '/'.implode('/', $CleanPathElements);
+		$cleaned_url .= (@$CleanQueries ? '?'.implode($queryseperator, $CleanQueries) : '');
+		return $cleaned_url;
+	}
+
+	function SafeURLread($url, &$error, $timeout=10, $followredirects=true) {
+		$error = '';
+
+		$parsed_url = @parse_url($url);
+		$alreadyLookedAtURLs[trim($url)] = true;
+		do {
+			$rawData = phpthumb_functions::URLreadFsock(@$parsed_url['host'], @$parsed_url['path'].'?'.@$parsed_url['query'], $errstr, true, (@$parsed_url['port'] ? @$parsed_url['port'] : 80), $timeout);
+			if (eregi('302 Found; Location\\: (http.*)', $errstr, $matches)) {
+				$matches[1] = trim(@$matches[1]);
+				if ($alreadyLookedAtURLs[$matches[1]]) {
+					break;
+				}
+				$alreadyLookedAtURLs[$matches[1]] = true;
+				$parsed_url = @parse_url($matches[1]);
+			} else {
+				break;
+			}
+		} while (true);
+		$error .= 'Error opening "'.$url.'":'."\n\n".$errstr;
+		if ($rawData === false) {
+			return false;
+		} elseif ($rawData === null) {
+			// fall through
+		} else {
+			return $rawData;
+		}
+
+		if (function_exists('curl_version') && !phpthumb_functions::FunctionIsDisabled('curl_exec')) {
+			$ch = curl_init();
+			curl_setopt($ch, CURLOPT_URL, $url);
+			curl_setopt($ch, CURLOPT_HEADER, false);
+			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+			curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
+			curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+			$rawData = curl_exec($ch);
+			curl_close($ch);
+			if (strlen($rawData) > 0) {
+				return $rawData;
+			}
+			$error .= 'CURL available but returned no data; ';
+		} else {
+			$error .= 'CURL unavailable; ';
+		}
+
+		$BrokenURLfopenPHPversions = array('4.4.2');
+		if (in_array(phpversion(), $BrokenURLfopenPHPversions)) {
+			$error .= 'fopen(URL) broken in PHP v'.phpversion().'; ';
+		} elseif (@ini_get('allow_url_fopen')) {
+			$rawData = '';
+			ob_start();
+			if ($fp = fopen($url, 'rb')) {
+				do {
+					$buffer = fread($fp, 8192);
+					$rawData .= $buffer;
+				} while (strlen($buffer) > 0);
+				fclose($fp);
+			} else {
+				$error .= trim(strip_tags(ob_get_contents()));
+			}
+			ob_end_clean();
+			if (!$error) {
+				return $rawData;
+			}
+			$error .= '; "allow_url_fopen" enabled but returned no data; ';
+		} else {
+			$error .= '"allow_url_fopen" disabled; ';
+		}
+
+		return false;
+	}
+
+	function EnsureDirectoryExists($dirname) {
+		$directory_elements = explode(DIRECTORY_SEPARATOR, $dirname);
+		$startoffset = (!$directory_elements[0] ? 2 : 1);  // unix with leading "/" then start with 2nd element; Windows with leading "c:\" then start with 1st element
+		$open_basedirs = explode(':', ini_get('open_basedir'));
+		foreach ($open_basedirs as $key => $open_basedir) {
+			if (ereg('^'.preg_quote($open_basedir), $dirname) && (strlen($dirname) > strlen($open_basedir))) {
+				$startoffset = count(explode(DIRECTORY_SEPARATOR, $open_basedir));
+				break;
+			}
+		}
+		$i = $startoffset;
+		$endoffset = count($directory_elements);
+		for ($i = $startoffset; $i <= $endoffset; $i++) {
+			$test_directory = implode(DIRECTORY_SEPARATOR, array_slice($directory_elements, 0, $i));
+			if (!$test_directory) {
+				continue;
+			}
+			if (!@is_dir($test_directory)) {
+				if (@file_exists($test_directory)) {
+					// directory name already exists as a file
+					return false;
+				}
+				@mkdir($test_directory, 0755);
+				@chmod($test_directory, 0755);
+				if (!@is_dir($test_directory) || !@is_writeable($test_directory)) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+
+	function GetAllFilesInSubfolders($dirname) {
+		$AllFiles = array();
+		$dirname = rtrim(realpath($dirname), '/\\');
+		if ($dirhandle = @opendir($dirname)) {
+			while ($file = readdir($dirhandle)) {
+				$fullfilename = $dirname.DIRECTORY_SEPARATOR.$file;
+				if (is_file($fullfilename)) {
+					$AllFiles[] = $fullfilename;
+				} elseif (is_dir($fullfilename)) {
+					if (($file == '.') || ($file == '..')) {
+						continue;
+					}
+					$subfiles = phpthumb_functions::GetAllFilesInSubfolders($fullfilename);
+					foreach ($subfiles as $filename) {
+						$AllFiles[] = $filename;
+					}
+				} else {
+					// ignore?
+				}
+			}
+			closedir($dirhandle);
+		}
+		sort($AllFiles);
+		return array_unique($AllFiles);
+	}
+
+
+	function SanitizeFilename($filename) {
+		$filename = ereg_replace('[^'.preg_quote(' !#$%^()+,-.;<>=@[]_{}').'a-zA-Z0-9]', '_', $filename);
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.1.0', '>=')) {
+			$filename = trim($filename, '.');
+		}
+		return $filename;
+	}
+
+}
+
+
+////////////// END: class phpthumb_functions //////////////
+
+
+if (!function_exists('gd_info')) {
+	// built into PHP v4.3.0+ (with bundled GD2 library)
+	function gd_info() {
+		static $gd_info = array();
+		if (empty($gd_info)) {
+			// based on code by johnschaefer at gmx dot de
+			// from PHP help on gd_info()
+			$gd_info = array(
+				'GD Version'         => '',
+				'FreeType Support'   => false,
+				'FreeType Linkage'   => '',
+				'T1Lib Support'      => false,
+				'GIF Read Support'   => false,
+				'GIF Create Support' => false,
+				'JPG Support'        => false,
+				'PNG Support'        => false,
+				'WBMP Support'       => false,
+				'XBM Support'        => false
+			);
+			$phpinfo_array = phpthumb_functions::phpinfo_array();
+			foreach ($phpinfo_array as $line) {
+				$line = trim(strip_tags($line));
+				foreach ($gd_info as $key => $value) {
+					//if (strpos($line, $key) !== false) {
+					if (strpos($line, $key) === 0) {
+						$newvalue = trim(str_replace($key, '', $line));
+						$gd_info[$key] = $newvalue;
+					}
+				}
+			}
+			if (empty($gd_info['GD Version'])) {
+				// probable cause: "phpinfo() disabled for security reasons"
+				if (function_exists('ImageTypes')) {
+					$imagetypes = ImageTypes();
+					if ($imagetypes & IMG_PNG) {
+						$gd_info['PNG Support'] = true;
+					}
+					if ($imagetypes & IMG_GIF) {
+						$gd_info['GIF Create Support'] = true;
+					}
+					if ($imagetypes & IMG_JPG) {
+						$gd_info['JPG Support'] = true;
+					}
+					if ($imagetypes & IMG_WBMP) {
+						$gd_info['WBMP Support'] = true;
+					}
+				}
+				// to determine capability of GIF creation, try to use ImageCreateFromGIF on a 1px GIF
+				if (function_exists('ImageCreateFromGIF')) {
+					if ($tempfilename = phpthumb::phpThumb_tempnam()) {
+						if ($fp_tempfile = @fopen($tempfilename, 'wb')) {
+							fwrite($fp_tempfile, base64_decode('R0lGODlhAQABAIAAAH//AP///ywAAAAAAQABAAACAUQAOw==')); // very simple 1px GIF file base64-encoded as string
+							fclose($fp_tempfile);
+
+							// if we can convert the GIF file to a GD image then GIF create support must be enabled, otherwise it's not
+							$gd_info['GIF Read Support'] = (bool) @ImageCreateFromGIF($tempfilename);
+						}
+						unlink($tempfilename);
+					}
+				}
+				if (function_exists('ImageCreateTrueColor') && @ImageCreateTrueColor(1, 1)) {
+					$gd_info['GD Version'] = '2.0.1 or higher (assumed)';
+				} elseif (function_exists('ImageCreate') && @ImageCreate(1, 1)) {
+					$gd_info['GD Version'] = '1.6.0 or higher (assumed)';
+				}
+			}
+		}
+		return $gd_info;
+	}
+}
+
+
+if (!function_exists('is_executable')) {
+	// in PHP v3+, but v5.0+ for Windows
+	function is_executable($filename) {
+		// poor substitute, but better than nothing
+		return file_exists($filename);
+	}
+}
+
+
+if (!function_exists('preg_quote')) {
+	// included in PHP v3.0.9+, but may be unavailable if not compiled in
+	function preg_quote($string, $delimiter='\\') {
+		static $preg_quote_array = array();
+		if (empty($preg_quote_array)) {
+			$escapeables = '.\\+*?[^]$(){}=!<>|:';
+			for ($i = 0; $i < strlen($escapeables); $i++) {
+				$strtr_preg_quote[$escapeables{$i}] = $delimiter.$escapeables{$i};
+			}
+		}
+		return strtr($string, $strtr_preg_quote);
+	}
+}
+
+if (!function_exists('file_get_contents')) {
+	// included in PHP v4.3.0+
+	function file_get_contents($filename) {
+		if (eregi('^(f|ht)tp\://', $filename)) {
+			return SafeURLread($filename, $error);
+		}
+		if ($fp = @fopen($filename, 'rb')) {
+			$rawData = '';
+			do {
+				$buffer = fread($fp, 8192);
+				$rawData .= $buffer;
+			} while (strlen($buffer) > 0);
+			fclose($fp);
+			return $rawData;
+		}
+		return false;
+	}
+}
+
+
+if (!function_exists('file_put_contents')) {
+	// included in PHP v5.0.0+
+	function file_put_contents($filename, $filedata) {
+		if ($fp = @fopen($filename, 'wb')) {
+			fwrite($fp, $filedata);
+			fclose($fp);
+			return true;
+		}
+		return false;
+	}
+}
+
+if (!function_exists('imagealphablending')) {
+	// built-in function requires PHP v4.0.6+ *and* GD v2.0.1+
+	function imagealphablending(&$img, $blendmode=true) {
+		// do nothing, this function is declared here just to
+		// prevent runtime errors if GD2 is not available
+		return true;
+	}
+}
+
+if (!function_exists('imagesavealpha')) {
+	// built-in function requires PHP v4.3.2+ *and* GD v2.0.1+
+	function imagesavealpha(&$img, $blendmode=true) {
+		// do nothing, this function is declared here just to
+		// prevent runtime errors if GD2 is not available
+		return true;
+	}
+}
+
+?>
Index: /afridex/plugins/fresh-page/RCCWP_Application.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_Application.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_Application.php (revision 21)
@@ -0,0 +1,302 @@
+<?php
+class RCCWP_Application
+{
+	function GetCustomWritePanels($writePanelId = null)
+	{
+		global $wpdb;
+	
+		$sql = "SELECT id, name, description, display_order, capability_name, type FROM " . RC_CWP_TABLE_WRITE_PANELS;
+		
+		if (isset($writePanelId))
+		{
+			$sql .=	" WHERE id = " . (int)$writePanelId;
+			$results = $wpdb->get_row($sql);
+		}
+		else
+		{
+			$sql .= " ORDER BY display_order ASC";
+			$results = $wpdb->get_results($sql);
+			if (!isset($results)) 
+				$results = array();
+		}
+		
+		return $results;
+	}
+
+	function GetWpCategories()
+	{
+		global $wpdb;
+		//$sql = "SELECT cat_ID, cat_name FROM $wpdb->categories ORDER BY cat_name";
+
+		if( $wpdb->terms != '' )
+		{
+			$sql = "SELECT t.term_id AS cat_ID, t.name AS cat_name FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id WHERE tt.taxonomy = 'category' ORDER BY t.name";
+		}
+		else
+		{
+			$sql = "SELECT cat_ID, cat_name FROM $wpdb->categories ORDER BY cat_name";
+		}
+
+		$results = $wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		return $results;
+	}
+
+	function GetWpDefaultCategory()
+	{
+
+	}
+
+	function GetWpStandardFields($standardFieldId = null)
+	{
+		global $wpdb;
+	
+		if (isset($standardFieldId))
+		{
+			$sql = "SELECT id, name, css_id, default_inclusion FROM " . RC_CWP_TABLE_STANDARD_FIELDS .
+				" WHERE id = " . (int)$standardFieldId;
+			$results = $wpdb->get_row($sql);	
+		}
+		else
+		{
+			$sql = "SELECT id, name, css_id, default_inclusion FROM " . RC_CWP_TABLE_STANDARD_FIELDS . " ORDER BY name";
+			$results = $wpdb->get_results($sql);
+			if (!isset($results))
+				$results = array();
+		}
+		return $results;
+	}
+	
+	function GetWpStandardFieldCssIds()
+	{
+		$results = RCCWP_Application::GetWpStandardFields();
+		$ids = array();
+		foreach ($results as $r)
+		{
+			$ids[] = $r->css_id;
+		}
+		return $ids;
+	}
+
+	function Install()
+	{
+		global $wpdb;
+
+		// Giving full rights to "files" folder. 
+		chmod("files", 777);
+
+		include_once('RCCWP_Options.php');
+		
+		if (get_option(RC_CWP_OPTION_KEY) === false)
+			RCCWP_Options::Update(0, 0, 0, 0, 0, 0);
+		
+		// include upgrade-functions for maybe_create_table;
+		if (!function_exists('maybe_create_table')) {
+			require_once(ABSPATH . '/wp-admin/upgrade-functions.php');
+		}
+
+		$sql1 = "CREATE TABLE " . RC_CWP_TABLE_WRITE_PANELS . " (
+			id int(11) NOT NULL auto_increment,
+			name varchar(50) NOT NULL,
+			description varchar(255),
+			display_order tinyint,
+			capability_name varchar(50) NOT NULL,
+			type varchar(50) NOT NULL,
+			PRIMARY KEY (id) )";
+		
+		$sql2 = "CREATE TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " (
+			id tinyint(11) NOT NULL auto_increment,
+			name varchar(50) NOT NULL,
+			description varchar(100),
+			has_options enum('true', 'false') NOT NULL,
+			has_properties enum('true', 'false') NOT NULL,
+			allow_multiple_values enum('true', 'false') NOT NULL,
+			PRIMARY KEY (id) )";
+			
+		$sql3 = "CREATE TABLE " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD . " (
+			id int(11) NOT NULL auto_increment,
+			panel_id int(11) NOT NULL,
+			name varchar(50) NOT NULL,
+			description varchar(255),
+			display_order tinyint,
+			display_name enum('true', 'false') NOT NULL,
+			display_description enum('true', 'false') NOT NULL,
+			type tinyint NOT NULL,
+			CSS varchar(100),
+			PRIMARY KEY (id) )";
+			
+		$sql4 = "CREATE TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS . " (
+			custom_field_id int(11) NOT NULL,
+			options text,
+			default_option text,
+			PRIMARY KEY (custom_field_id) )";
+		
+		$sql5 = "CREATE TABLE " . RC_CWP_TABLE_PANEL_CATEGORY . "(
+			panel_id int(11) NOT NULL,
+			cat_id int(11) NOT NULL,
+			PRIMARY KEY (panel_id, cat_id) )";
+			
+		$sql6 = "CREATE TABLE " . RC_CWP_TABLE_STANDARD_FIELDS . "(
+			id int(11) NOT NULL,
+			name varchar(50) NOT NULL,
+			css_id varchar(50) NOT NULL,
+			default_inclusion enum('true', 'false') NOT NULL,
+			PRIMARY KEY (id) )";
+		
+		$sql7 = "CREATE TABLE " . RC_CWP_TABLE_PANEL_STANDARD_FIELD . " (
+			panel_id int(11) NOT NULL,
+			standard_field_id int(11) NOT NULL,
+			PRIMARY KEY (panel_id, standard_field_id) )";
+		
+		$sql8 = "CREATE TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " (
+			custom_field_id int(11) NOT NULL,
+			properties text,
+			PRIMARY KEY (custom_field_id) )";
+		
+		$sql9 = "CREATE TABLE " . RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD . " (
+			panel_id int(11) NOT NULL,
+			css_id varchar(50) NOT NULL,
+			PRIMARY KEY (panel_id, css_id) )";
+		
+		// create the table, as needed
+		maybe_create_table(RC_CWP_TABLE_WRITE_PANELS, $sql1);
+		maybe_create_table(RC_CWP_TABLE_CUSTOM_FIELD_TYPES, $sql2);
+		maybe_create_table(RC_CWP_TABLE_PANEL_CUSTOM_FIELD, $sql3);
+		maybe_create_table(RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS, $sql4);	
+		maybe_create_table(RC_CWP_TABLE_PANEL_CATEGORY, $sql5);
+		maybe_create_table(RC_CWP_TABLE_STANDARD_FIELDS, $sql6);
+		maybe_create_table(RC_CWP_TABLE_PANEL_STANDARD_FIELD, $sql7);
+		maybe_create_table(RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES, $sql8);
+		maybe_create_table(RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD, $sql9);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (1, 'Textbox', NULL, 'false', 'true', 'false')";
+		$wpdb->query($sql6);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (2, 'Multiline Textbox', NULL, 'false', 'true', 'false')";
+		$wpdb->query($sql6);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (3, 'Checkbox', NULL, 'false', 'false', 'false')";
+		$wpdb->query($sql6);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (4, 'Checkbox List', NULL, 'true', 'false', 'true')";
+		$wpdb->query($sql6);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (5, 'Radiobutton List', NULL, 'true', 'false', 'false')";
+		$wpdb->query($sql6);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (6, 'Dropdown List', NULL, 'true', 'false', 'false')";
+		$wpdb->query($sql6);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (7, 'Listbox', NULL, 'true', 'true', 'true')";
+		$wpdb->query($sql6);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (8, 'File', NULL, 'false', 'false', 'false')";
+		$wpdb->query($sql6);
+		
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (9, 'Image', NULL, 'false', 'true', 'false')";
+		$wpdb->query($sql6);
+
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (10, 'Date', NULL, 'false', 'true', 'false')";
+		$wpdb->query($sql6);
+
+		$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (11, 'Audio', NULL, 'false', 'false', 'false')";
+		$wpdb->query($sql6);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (1, 'Title', 'titlediv', 'true')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (2, 'Categories', 'categorydiv', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (3, 'Discussion', 'commentstatusdiv', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (4, 'Post Password', 'passworddiv', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (5, 'Post Slug', 'slugdiv', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (6, 'Post Status', 'poststatusdiv', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (7, 'Post Timestamp', 'posttimestampdiv', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (8, 'Upload', 'uploading', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (9, 'Optional Excerpt', 'postexcerpt', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (10, 'Trackbacks', 'trackbacksdiv', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (11, 'Custom Fields', 'postcustom', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (12, 'Post', 'postdiv', 'false')";
+		$wpdb->query($sql9);
+		
+		$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (13, 'Post Author', 'authordiv', 'false')";
+		$wpdb->query($sql9);
+
+		$options['hide-write-post'] = 0;
+		$options['hide-write-page'] = 0;
+		$options['prompt-editing-post'] = 0;
+		$options['assign-to-role'] = 0;
+		$options['use-snipshot'] = 1;
+		$options['default-custom-write-panel'] = "";
+		
+		$options = serialize($options);
+		update_option(RC_CWP_OPTION_KEY, $options);
+	}
+
+	function Uninstall()
+	{
+ 		global $wpdb;
+ 		$sql = "DELETE FROM $wpdb->postmeta WHERE meta_key = '" . RC_CWP_POST_WRITE_PANEL_ID_META_KEY . "'";
+ 		$wpdb->query($sql);
+	 	
+		$sql = "DROP TABLE " . RC_CWP_TABLE_WRITE_PANELS;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANEL_CATEGORY;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_STANDARD_FIELDS;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANEL_STANDARD_FIELD;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES;
+		$wpdb->query($sql);
+		
+		delete_option(RC_CWP_OPTION_KEY);
+	}
+	
+	function InCustomWritePanel()
+	{
+		return RCCWP_Application::InWritePostPanel() && isset($_REQUEST['custom-write-panel-id']);
+	}
+	
+	function InWritePostPanel()
+	{
+		return (strstr($_SERVER['REQUEST_URI'], '/wp-admin/post-new.php') ||
+			strstr($_SERVER['REQUEST_URI'], '/wp-admin/post.php') ||
+			strstr($_SERVER['REQUEST_URI'], '/wp-admin/page-new.php') ||
+			strstr($_SERVER['REQUEST_URI'], '/wp-admin/page.php'));
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/Main.php.svn-base
===================================================================
--- /afridex/plugins/fresh-page/Main.php.svn-base (revision 21)
+++ /afridex/plugins/fresh-page/Main.php.svn-base (revision 21)
@@ -0,0 +1,86 @@
+<?php
+/*
+Plugin Name: Fresh Post
+Plugin URI: http://freshoutcreative.com/goodies
+Description: Create page templates with custom file uploads and integrated photo resizing.
+Author: Freshout
+Version: .11
+Author URI: http://freshoutcreative.com
+*/
+
+if (is_admin())
+{
+	include_once('RCCWP_Constant.php');
+	include_once('RCCWP_Application.php');
+	
+	register_activation_hook(dirname(__FILE__) . '/Main.php', array('RCCWP_Application', 'Install'));
+
+	if (get_option(RC_CWP_OPTION_KEY) !== false)
+	{
+		include_once('RCCWP_Menu.php');
+
+		//register_deactivation_hook(dirname(__FILE__) . '/Main.php', array('RCCWP_Application', 'Uninstall'));
+
+
+		add_action('admin_menu', array('RCCWP_Post', 'save_files') );
+
+		add_action('admin_menu', array('RCCWP_Menu', 'AttachCustomWritePanelMenuItems'));
+		add_action('admin_menu', array('RCCWP_Menu', 'DetachWpWritePanelMenuItems'));
+
+		include_once('RCCWP_Processor.php');
+		add_action('init', array('RCCWP_Processor', 'Main'));
+		add_action('admin_menu', array('RCCWP_Menu', 'AttachManagementMenuItem'));
+		add_action('admin_menu', array('RCCWP_Menu', 'AttachOptionsMenuItem'));
+
+		include_once('RCCWP_Post.php');	
+		add_action('edit_post', array('RCCWP_Post', 'SetCustomWritePanel'));
+		add_action('save_post', array('RCCWP_Post', 'SetCustomWritePanel'));
+		add_action('publish_post', array('RCCWP_Post', 'SetCustomWritePanel'));
+
+		add_action('edit_post', array('RCCWP_Post', 'SetMetaValue'));
+		add_action('save_post', array('RCCWP_Post', 'SetMetaValue'));
+		add_action('publish_post', array('RCCWP_Post', 'SetMetaValue'));
+
+		add_filter('wp_redirect', array('RCCWP_Processor', 'Redirect'));
+
+		add_action('shutdown', array('RCCWP_Processor', 'FlushAllOutputBuffer')); 
+	}
+}
+
+require_once 'get-custom.php';
+
+function cwp_add_type_identifier(){ 
+	global $wpdb;
+	global $post;
+	
+	if( isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id']))
+	{
+		$getPostID = $wpdb->get_results("SELECT id, type FROM". RC_CWP_TABLE_WRITE_PANELS ." WHERE id='".$_GET['custom-write-panel-id']."'");
+		echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"". $getPostID[0]->type ."\" />";
+
+	}
+	else{
+		if($post->post_type == 'page') { 
+			echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"page\" />";
+			echo "<div id=\"message\" class=\"updated fade\"><p><strong>Page saved.</strong></p></div>";
+ 		} else {
+			echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"post\" />";
+ 		}
+
+ 	}
+} 
+
+function cwp_add_pages_identifiers(){
+
+	$key = wp_create_nonce('rc-custom-write-panel');
+	$id = $_GET['custom-write-panel-id'];
+	echo 
+<<<EOF
+		<input type="hidden" name="rc-custom-write-panel-verify-key" id="rc-custom-write-panel-verify-key" value="$key" />
+			<input type="hidden" name="rc-cwp-custom-write-panel-id" value="$id" />
+EOF;
+}
+
+add_action('edit_page_form','cwp_add_pages_identifiers');
+add_action('edit_form_advanced','cwp_add_type_identifier');
+?>
Index: /afridex/plugins/fresh-page/backup_RCCWP_CustomField.php
===================================================================
--- /afridex/plugins/fresh-page/backup_RCCWP_CustomField.php (revision 21)
+++ /afridex/plugins/fresh-page/backup_RCCWP_CustomField.php (revision 21)
@@ -0,0 +1,252 @@
+<?php
+include_once('RC_Format.php');
+
+class RCCWP_CustomField
+{
+	function Create($customWritePanelId, $name, $description, $order = 1, $type, $options = null, $default_value = null, $properties = null)
+	{
+		global $wpdb;
+	
+		$sql = sprintf(
+			"INSERT INTO " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" (panel_id, name, description, display_order, type) values (%d, %s, %s, %d, %d)",
+			$customWritePanelId,
+			RC_Format::TextToSql($name),
+			RC_Format::TextToSql($description),
+			$order,
+			$type
+			);
+		$wpdb->query($sql);
+		
+		$customFieldId = $wpdb->insert_id;
+		
+		$field_type = RCCWP_CustomField::GetCustomFieldTypes($type);
+		if ($field_type->has_options == "true")
+		{
+			$options = explode("\n", $options);
+			array_walk($options, array(RC_Format, TrimArrayValues));
+			
+			$default_value = explode("\n", $default_value);
+			array_walk($default_value, array(RC_Format, TrimArrayValues));
+			
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" (custom_field_id, options, default_option) values (%d, %s, %s)",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($options)),
+				RC_Format::TextToSql(serialize($default_value))
+				);	
+			$wpdb->query($sql);	
+		}
+		
+		if ($field_type->has_properties == "true")
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" (custom_field_id, properties) values (%d, %s)",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($properties))
+				);	
+			$wpdb->query($sql);	
+		}
+	}
+	
+	function Delete($customFieldId = null)
+	{
+		global $wpdb;
+		
+		$customField = RCCWP_CustomField::Get($customFieldId);
+		
+		$sql = sprintf(
+			"DELETE FROM " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" WHERE id = %d",
+			$customFieldId
+			);
+		$wpdb->query($sql);
+		
+		if ($customField->has_options == "true")
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);	
+			$wpdb->query($sql);	
+		}
+	}
+	
+	function Get($customFieldId)
+	{
+		global $wpdb;
+		$sql = "SELECT cf.id, cf.name, tt.name AS type, cf.description, cf.display_order, co.options, co.default_option AS default_value, tt.has_options, cp.properties, tt.has_properties, tt.allow_multiple_values FROM " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" cf LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS . " co ON cf.id = co.custom_field_id" .
+			" LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " cp ON cf.id = cp.custom_field_id" .
+			" JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " tt ON cf.type = tt.id" . 
+			" WHERE cf.id = " . $customFieldId;
+		$results = $wpdb->get_row($sql);
+			
+		$results->options = unserialize($results->options);
+		$results->properties = unserialize($results->properties);
+		$results->default_value = unserialize($results->default_value);
+		return $results;
+	}
+	
+	function GetCustomFieldNames($customFieldTypeId = null)
+	{
+		$customFieldNames = array();
+		$customFields = RCCWP_CustomField::GetCustomFieldTypes($customFieldTypeId);
+		foreach ($customFields as $field)
+		{
+			$customFieldNames[] = $field->name;
+		}
+		
+		return $customFieldNames;
+	}
+	
+	function GetCustomFieldTypes($customFieldTypeId = null)
+	{
+		global $wpdb;
+	
+		if (isset($customFieldTypeId))
+		{
+			$sql = "SELECT id, name, description, has_options, has_properties, allow_multiple_values FROM " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES .
+				" WHERE id = " . (int)$customFieldTypeId;
+			$results = $wpdb->get_row($sql);	
+		}
+		else
+		{
+			$sql = "SELECT id, name, description, has_options, has_properties, allow_multiple_values FROM " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES;
+			$results = $wpdb->get_results($sql);
+			if (!isset($results))
+				$results = array();
+		}
+		return $results;
+	}
+	
+	function GetCustomFieldValue($postId, $customFieldName)
+	{
+		return get_post_meta($postId, $customFieldName, true);
+	}
+	
+	function GetCustomFieldValues($postId, $customFieldName)
+	{
+		return get_post_meta($postId, $customFieldName, false);
+	}
+	
+	function GetDefaultCustomFieldType()
+	{
+		return 'Textbox';
+	}
+	
+	function GetOptions()
+	{
+
+	}
+
+	function GetProperties()
+	{
+
+	}
+	
+	function Update($customFieldId, $name, $description, $order = 1, $type, $options = null, $default_value = null, $properties = null)
+	{
+		global $wpdb;
+		
+		$oldCustomField = RCCWP_CustomField::Get($customFieldId);
+		
+		if ($oldCustomField->name != $name)
+		{
+			$sql = sprintf(
+				"UPDATE $wpdb->postmeta" .
+				" SET meta_key = %s" .
+				" WHERE meta_key = %s",
+				RC_Format::TextToSql($name),
+				RC_Format::TextToSql($oldCustomField->name)
+				);
+			
+			$wpdb->query($sql);
+		}
+		
+		$sql = sprintf(
+			"UPDATE " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" SET name = %s" .
+			" , description = %s" .
+			" , display_order = %d" .
+			" , type = %d" .
+			" WHERE id = %d",
+			RC_Format::TextToSql($name),
+			RC_Format::TextToSql($description),
+			$order,
+			$type,
+			$customFieldId
+			);
+		$wpdb->query($sql);
+
+
+		$field_type = RCCWP_CustomField::GetCustomFieldTypes($type);
+		if ($field_type->has_options == "true")
+		{
+			$options = explode("\n", $options);
+			array_walk($options, array(RC_Format, TrimArrayValues));
+			
+			$default_value = explode("\n", $default_value);
+			array_walk($default_value, array(RC_Format, TrimArrayValues));
+			
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" (custom_field_id, options, default_option) values (%d, %s, %s)" . 
+				" ON DUPLICATE KEY UPDATE options = %s, default_option = %s",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($options)),
+				RC_Format::TextToSql(serialize($default_value)),
+				RC_Format::TextToSql(serialize($options)),
+				RC_Format::TextToSql(serialize($default_value))
+				);	
+			$wpdb->query($sql);	
+		}
+		else
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);
+			$wpdb->query($sql);	
+		}
+		
+		if ($field_type->has_properties == "true")
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" (custom_field_id, properties) values (%d, %s)" .
+				" ON DUPLICATE KEY UPDATE properties = %s",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($properties)),
+				RC_Format::TextToSql(serialize($properties))
+				);	
+			$wpdb->query($sql);	
+		}
+		else
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);
+			$wpdb->query($sql);	
+		}
+/*
+		if(isset($_POST['custom-photo-height']) && !empty($_POST['custom-photo-height']) && isset($_POST['custom-photo-width']) && !empty($_POST['custom-photo-width']))
+		{
+			$prop = 'a:1:{s:6:"params";s:12:"&h='.$_POST['custom-photo-height'].'&w='.$_POST['custom-photo-width'].'";}';
+			$sql = "UPDATE wp_rc_cwp_custom_field_properties SET properties='".$prop."' WHERE custom_field_id='".$_POST['custom-field-id']."'";
+			$wpdb->query($sql);	
+		
+//echo "<pre>";
+//print_r($_POST);
+//die();
+
+		}*/
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/RCCWP_Menu.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_Menu.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_Menu.php (revision 21)
@@ -0,0 +1,267 @@
+<?php
+include_once('RCCWP_ManagementPage.php');
+include_once('RCCWP_CreateCustomWritePanelPage.php');
+include_once('RCCWP_CreateCustomFieldPage.php');
+include_once('RCCWP_CustomFieldPage.php');
+
+class RCCWP_Menu
+{
+	function AttachManagementMenuItem()
+	{
+		$page_group = 'RCCWP_ManagementPage';
+		$page_type = 'View';
+		
+		if (isset($_REQUEST['create-custom-write-panel']))
+		{
+			$page_group = 'RCCWP_CreateCustomWritePanelPage';
+			$page_type = 'Main';
+		}
+		else if (isset($_REQUEST['create-custom-field']))
+		{
+			$page_group = 'RCCWP_CreateCustomFieldPage';
+			$page_type = 'Main';
+		}
+		else if (isset($_REQUEST['continue-create-custom-field']))
+		{
+			$current_field = RCCWP_CustomField::GetCustomFieldTypes((int)$_REQUEST['custom-field-type']);
+			if ($current_field->has_options == "true" || $current_field->has_properties == "true")
+			{
+				$page_group = 'RCCWP_CreateCustomFieldPage';
+				$page_type = 'SetOptions';
+			}
+			else if ($current_field->has_options == "false")
+			{
+				RCCWP_CustomField::Create(
+					$_POST['custom-write-panel-id'],
+					$_POST['custom-field-name'],
+					$_POST['custom-field-description'],
+					$_POST['custom-field-order'],
+					$_POST['custom-field-type'],
+					$_POST['custom-field-options']);	
+				
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'View';
+			}
+		}
+		else if (isset($_REQUEST['finish-create-custom-field']))
+		{
+			$page_group = 'RCCWP_CustomWritePanelPage';
+			$page_type = 'View';
+		}
+		else if (isset($_REQUEST['finish-create-custom-write-panel']))
+		{
+			$page_group = 'RCCWP_ManagementPage';
+			$page_type = 'View';
+		}
+		else if (isset($_REQUEST['edit-custom-write-panel']))
+		{
+			$page_group = 'RCCWP_CustomWritePanelPage';
+			$page_type = 'Edit';
+		}
+		else if (isset($_REQUEST['cancel-edit-custom-write-panel']))
+		{
+			$page_group = 'RCCWP_CustomWritePanelPage';
+			$page_type = 'View';
+		}
+		else if (isset($_REQUEST['submit-edit-custom-write-panel']))
+		{
+			$page_group = 'RCCWP_CustomWritePanelPage';
+			$page_type = 'View';
+		}
+		else if (isset($_REQUEST['cancel-edit-custom-field']))
+		{
+			$page_group = 'RCCWP_CustomWritePanelPage';
+			$page_type = 'View';
+		}
+		else if (isset($_REQUEST['submit-edit-custom-field']))
+		{
+			$page_group = 'RCCWP_CustomWritePanelPage';
+			$page_type = 'View';
+		}
+		else if (isset($_REQUEST['edit-custom-field']))
+		{
+			$page_group = 'RCCWP_CustomFieldPage';
+			$page_type = 'Edit';
+		}
+		else if (isset($_REQUEST['view-custom-write-panel']))
+		{
+			$page_group = 'RCCWP_CustomWritePanelPage';
+			$page_type = 'View';
+		}
+		else if (isset($_REQUEST['assign-custom-write-panel']))
+		{
+			$page_group = 'RCCWP_ManagementPage';
+			$page_type = 'AssignCustomWritePanel';
+		}
+		
+		add_management_page(__('Custom Write Panel'), __('Write Panels'), 'manage_options', __FILE__, array($page_group, $page_type));	
+	}
+	
+	function AttachOptionsMenuItem()
+	{
+		include_once('RCCWP_OptionsPage.php');
+		add_options_page(__('Fresh Post Options'), __('Fresh Post'), 'manage_options', 'RCCWP_OptionsPage.php', array('RCCWP_OptionsPage', 'Main'));	
+	}
+	
+	function AttachCustomWritePanelMenuItems()
+	{
+		include_once('RCCWP_Options.php');
+		$assignToRole = RCCWP_Options::Get('assign-to-role');
+
+		$customWritePanels = RCCWP_Application::GetCustomWritePanels();
+
+		foreach ($customWritePanels as $panel)
+		{
+			if ($assignToRole <> 1 || current_user_can($panel->capability_name))
+			{
+				if ($panel->type == "post"){
+					add_submenu_page('edit.php', __($panel->name), __($panel->name), 'publish_posts', 'edit.php?filter-posts=1&custom-write-panel-id=' . $panel->id);
+				}
+				else {
+					add_submenu_page('edit.php', __($panel->name), __($panel->name), 'publish_posts', 'edit-pages.php?filter-posts=1&custom-write-panel-id=' . $panel->id);
+				}
+			}
+			
+		}
+		
+		if (isset($_REQUEST['post']))
+		{
+			global $submenu;
+			//print_r($submenu);
+			global $CUSTOM_WRITE_PANEL;
+			/*if (isset($CUSTOM_WRITE_PANEL))
+				if ($CUSTOM_WRITE_PANEL->type == "post")
+					$submenu['edit.php'][5] = array(__($CUSTOM_WRITE_PANEL->name), 'edit_posts', 'edit.php');
+				else
+					$submenu['edit.php'][10] = array(__($CUSTOM_WRITE_PANEL->name), 'edit_pages', 'edit-pages.php');
+			*/
+		}
+		else
+		{
+			
+				
+			foreach ($customWritePanels as $panel)
+			{
+				if ($assignToRole <> 1 || current_user_can($panel->capability_name))
+				{
+					if ($panel->type == "post"){
+						add_submenu_page('post-new.php', __($panel->name), __($panel->name), 'publish_posts', 'post-new.php?custom-write-panel-id=' . $panel->id);
+						//add_submenu_page('edit.php', __($panel->name), __($panel->name), 'publish_posts', 'edit.php?filter-posts=1&custom-write-panel-id=' . $panel->id);
+					}
+					else {
+						add_submenu_page('post-new.php', __($panel->name), __($panel->name), 'publish_posts', 'page-new.php?custom-write-panel-id=' . $panel->id);
+						//add_submenu_page('edit.php', __($panel->name), __($panel->name), 'publish_posts', 'edit-pages.php?filter-posts=1&custom-write-panel-id=' . $panel->id);
+					}
+				}
+				
+			}
+		}
+		
+		RCCWP_Menu::SetCurrentCustomWritePanelMenuItem();
+		
+	}
+
+	function HighlightCustomPanel(){
+		global $wpdb, $submenu_file, $post; 
+		
+		$result = $wpdb->get_results( " SELECT meta_value
+						FROM $wpdb->postmeta
+						WHERE post_id = '".$post->ID."' and meta_key = '_rc_cwp_write_panel_id'", ARRAY_A );
+		$currPage = basename($_SERVER['SCRIPT_NAME']);
+		if (count($result) > 0 && $currPage =="post.php" ){
+			$id = $result[0]['meta_value'];
+			$submenu_file = "edit.php?filter-posts=1&custom-write-panel-id=$id";
+		}
+		elseif (count($result) > 0 && $currPage == "page.php" ){
+			$id = $result[0]['meta_value'];
+			$submenu_file = "edit-pages.php?filter-posts=1&custom-write-panel-id=$id";
+		}
+		
+		
+	}
+
+	function FilterPostsPagesList($where){
+		global $wpdb;
+		if (isset($_GET['filter-posts'])) {
+			$panel_id = $_GET['custom-write-panel-id'];
+			$where = $where . " AND 0 < (SELECT count($wpdb->postmeta.meta_value)
+					FROM $wpdb->postmeta
+					WHERE $wpdb->postmeta.post_id = $wpdb->posts.ID and $wpdb->postmeta.meta_key = '_rc_cwp_write_panel_id' and $wpdb->postmeta.meta_value = '$panel_id') ";
+		}
+		return $where;
+		/*$i = 0;
+		if (isset($_GET['filter-posts']) && (!empty($posts))) {
+			$panel_id = $_GET['custom-write-panel-id'];
+			foreach($posts as $my_post){
+				$result = $wpdb->get_results( " SELECT meta_value
+					FROM $wpdb->postmeta
+					WHERE post_id = '$my_post->ID' and meta_key = '_rc_cwp_write_panel_id' and meta_value = '$panel_id'", ARRAY_A );
+				if (count($result) == 0 )
+					array_splice($posts, $i ,1);
+				else
+					$i++;
+			}
+
+		}
+		return $posts;*/
+	}
+	
+	function DetachWpWritePanelMenuItems()
+	{
+		include_once('RCCWP_Options.php');
+		global $submenu;
+
+		$options = RCCWP_Options::Get();
+		
+		if ($options['hide-write-post'] == '1')
+			unset($submenu['post-new.php'][5]);
+		
+		if ($options['hide-write-page'] == '1')
+			unset($submenu['post-new.php'][10]);	
+	}
+	
+	function SetCurrentCustomWritePanelMenuItem()
+	{
+		// wp-admin/menu.php
+		global $submenu_file;
+		global $menu;
+		
+		include_once('RCCWP_Options.php');
+		$options = RCCWP_Options::Get();
+		
+		if ($options['default-custom-write-panel'] != '')
+		{
+			include_once('RCCWP_CustomWritePanel.php');
+			
+			$customWritePanel = RCCWP_CustomWritePanel::Get((int)$options['default-custom-write-panel']);
+			if ($options['assign-to-role'] <> 1 || current_user_can($customWritePanel->capability_name))
+			{
+				if ($customWritePanel->type == "post")
+					$menu[5][2] = 'post-new.php?custom-write-panel-id=' . (int)$options['default-custom-write-panel'];
+				else
+					$menu[5][2] = 'page-new.php?custom-write-panel-id=' . (int)$options['default-custom-write-panel'];
+			}
+		}
+		
+		if ($_REQUEST['custom-write-panel-id'])
+		{
+			$customWritePanel = RCCWP_CustomWritePanel::Get((int)$_REQUEST['custom-write-panel-id']);
+			if ($_REQUEST['filter-posts']){
+				if ($customWritePanel->type == "post")
+					$submenu_file = 'edit.php?filter-posts=1&custom-write-panel-id=' . (int)$_REQUEST['custom-write-panel-id'];
+				else
+					$submenu_file = 'edit-pages.php?filter-posts=1&custom-write-panel-id=' . (int)$_REQUEST['custom-write-panel-id'];
+			}
+			else{
+				if ($customWritePanel->type == "post")
+					$submenu_file = 'post-new.php?custom-write-panel-id=' . (int)$_REQUEST['custom-write-panel-id'];
+				else
+					$submenu_file = 'page-new.php?custom-write-panel-id=' . (int)$_REQUEST['custom-write-panel-id'];
+			}
+		}
+		else if (isset($_REQUEST['post']))
+		{
+		}
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/css/cropper.css
===================================================================
--- /afridex/plugins/fresh-page/css/cropper.css (revision 21)
+++ /afridex/plugins/fresh-page/css/cropper.css (revision 21)
@@ -0,0 +1,182 @@
+.imgCrop_wrap {
+	/* width: 500px;   @done_in_js */
+	/* height: 375px;  @done_in_js */
+	position: relative;
+	cursor: crosshair;
+}
+
+/* an extra classname is applied for Opera < 9.0 to fix it's lack of opacity support */
+.imgCrop_wrap.opera8 .imgCrop_overlay,
+.imgCrop_wrap.opera8 .imgCrop_clickArea { 
+	background-color: transparent;
+}
+
+/* fix for IE displaying all boxes at line-height by default, although they are still 1 pixel high until we combine them with the pointless span */
+.imgCrop_wrap,
+.imgCrop_wrap * {
+	font-size: 0;
+}
+
+.imgCrop_overlay {
+	background-color: #000;
+	opacity: 0.5;
+	filter:alpha(opacity=50);
+	position: absolute;
+	width: 100%;
+	height: 100%;
+}
+
+.imgCrop_selArea {
+	position: absolute;
+	/* @done_in_js 
+	top: 20px;
+	left: 20px;
+	width: 200px;
+	height: 200px;
+	background: transparent url(castle.jpg) no-repeat  -210px -110px;
+	*/
+	cursor: move;
+	z-index: 2;
+}
+
+/* clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */
+.imgCrop_clickArea {
+	width: 100%;
+	height: 100%;
+	background-color: #FFF;
+	opacity: 0.01;
+	filter:alpha(opacity=01);
+}
+
+.imgCrop_marqueeHoriz {
+	position: absolute;
+	width: 100%;
+	height: 1px;
+	background: transparent url(../images/marqueeHoriz.gif) repeat-x 0 0;
+	z-index: 3;
+}
+
+.imgCrop_marqueeVert {
+	position: absolute;
+	height: 100%;
+	width: 1px;
+	background: transparent url(../images/marqueeVert.gif) repeat-y 0 0;
+	z-index: 3;
+}
+
+/* 
+ *  FIX MARCHING ANTS IN IE
+ *	As IE <6 tries to load background images we can uncomment the follwoing hack 
+ *  to remove that issue, not as pretty - but is anything in IE?
+ *  And yes I do know that 'filter' is evil, but it will make it look semi decent in IE
+ *
+* html .imgCrop_marqueeHoriz,
+* html .imgCrop_marqueeVert {
+	background: transparent;
+	filter: Invert; 
+}
+* html .imgCrop_marqueeNorth { border-top: 1px dashed #000; }
+* html .imgCrop_marqueeEast  { border-right: 1px dashed #000; }
+* html .imgCrop_marqueeSouth { border-bottom: 1px dashed #000; }
+* html .imgCrop_marqueeWest  { border-left: 1px dashed #000; }
+*/
+
+.imgCrop_marqueeNorth { top: 0; left: 0; }
+.imgCrop_marqueeEast  { top: 0; right: 0; }
+.imgCrop_marqueeSouth { bottom: 0px; left: 0; }
+.imgCrop_marqueeWest  { top: 0; left: 0; }
+
+
+.imgCrop_handle {
+	position: absolute;
+	border: 1px solid #333;
+	width: 6px;
+	height: 6px;
+	background: #FFF;
+	opacity: 0.5;
+	filter:alpha(opacity=50);
+	z-index: 4;
+}
+
+/* fix IE 5 box model */
+* html .imgCrop_handle {
+	width: 8px;
+	height: 8px;
+	wid\th: 6px;
+	hei\ght: 6px;
+}
+
+.imgCrop_handleN {
+	top: -3px;
+	left: 0;
+	/* margin-left: 49%;    @done_in_js */
+	cursor: n-resize;
+}
+
+.imgCrop_handleNE { 
+	top: -3px;
+	right: -3px;
+	cursor: ne-resize;
+}
+
+.imgCrop_handleE {
+	top: 0;
+	right: -3px;
+	/* margin-top: 49%;    @done_in_js */
+	cursor: e-resize;
+}
+
+.imgCrop_handleSE {
+	right: -3px;
+	bottom: -3px;
+	cursor: se-resize;
+}
+
+.imgCrop_handleS {
+	right: 0;
+	bottom: -3px;
+	/* margin-right: 49%; @done_in_js */
+	cursor: s-resize;
+}
+
+.imgCrop_handleSW {
+	left: -3px;
+	bottom: -3px;
+	cursor: sw-resize;
+}
+
+.imgCrop_handleW {
+	top: 0;
+	left: -3px;
+	/* margin-top: 49%;  @done_in_js */
+	cursor: w-resize;
+}
+
+.imgCrop_handleNW {
+	top: -3px;
+	left: -3px;
+	cursor: nw-resize;
+}
+
+/**
+ * Create an area to click & drag around on as the default browser behaviour is to let you drag the image 
+ */
+.imgCrop_dragArea {
+	width: 100%;
+	height: 100%;
+	z-index: 200;
+	position: absolute;
+	top: 0;
+	left: 0;
+}
+
+.imgCrop_previewWrap {
+	/* width: 200px;  @done_in_js */
+	/* height: 200px; @done_in_js */
+	overflow: hidden;
+	position: relative;
+}
+
+.imgCrop_previewWrap img {
+	position: absolute;
+}
Index: /afridex/plugins/fresh-page/css/greybox.css
===================================================================
--- /afridex/plugins/fresh-page/css/greybox.css (revision 21)
+++ /afridex/plugins/fresh-page/css/greybox.css (revision 21)
@@ -0,0 +1,54 @@
+#GB_overlay {
+  background-image: url(../images/overlay.png); 
+  position: absolute;
+  margin: auto;
+  top: 0;
+  left: 0;
+  z-index: 100;
+  width:  100%;
+  height: 100%;
+}
+
+* html #GB_overlay {
+  background-color: #000;
+  background-color: transparent;
+  background-image: url(../images/blank.gif);
+  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../images/overlay.png", sizingMethod="scale");
+}
+
+#GB_window {
+  top: 10px;
+  left: 0px;
+  position: absolute;
+  background: #fff;
+  border: 5px solid #aaa;
+  overflow: auto;
+  width: 400px;
+  height: 400px;
+  z-index: 150;
+}
+
+#GB_frame {
+  border: 0;
+  overflow: auto;
+  width: 100%;
+  height: 378px;
+}
+
+#GB_caption {
+  font: 12px bold helvetica, verdana, sans-serif;
+  color: #fff;
+  background: #888;
+  padding: 2px 0 2px 5px;
+  margin: 0;
+  text-align: left;
+}
+
+#GB_window img {
+  position: absolute;
+  top: 2px;
+  right: 5px;
+  cursor: pointer;
+  cursor: hand;
+}
+
Index: /afridex/plugins/fresh-page/css/epoch_styles.css
===================================================================
--- /afridex/plugins/fresh-page/css/epoch_styles.css (revision 21)
+++ /afridex/plugins/fresh-page/css/epoch_styles.css (revision 21)
@@ -0,0 +1,120 @@
+/*!!
+Epoch DHTML JavaScript Calendar - Version 2.0.2
+English Edition
+CSS Style File
+(c) 2006-2007 MeanFreePath
+Free for NON-COMMERCIAL use - see website for details and updates
+http://www.meanfreepath.com/javascript_calendar/index.html
+!!*/
+
+table.calendar {
+	font-family: Helvetica, Arial, sans-serif;
+	font-size: 0.8em;
+	border-collapse: collapse;
+	background-color: white;
+	border: solid #999999 1px;
+	background-color: white;
+	width: 215px;
+	text-align: center;
+	/*prevent user from selecting text in Mozilla & Safari - check calendar constructor for IE code)*/
+	-moz-user-select: none;
+    /*-khtml-user-select: none;*/
+}
+table.calendar a {
+}
+table.calendar a:hover {
+}
+table.calendar input, table.calendar select {
+	font-size: 10px;
+}
+table.calendar td, table.calendar th {
+	border: 0;
+	font-size: 10px;
+	text-align: center;
+}
+div.mainheading {
+	margin: 2px;
+}
+.closeBtn {
+	/*float: right;
+	width: 15px;
+	/*font-size: 1.5em;
+	height: 13px;
+
+	padding: 0 0 3px 0;
+	margin: 1px 8px 0 0;
+	border: solid black 1px;*/
+}
+/*all styles related to the main calendar grid*/
+table.cells {
+	border-collapse: collapse;
+	border: solid #CCCCCC 1px;
+	cursor: pointer;
+	empty-cells: show;
+	margin: 0 6px 0 6px;
+}
+/*the day headings*/
+table.cells th {
+	border: solid #CCCCCC 1px;
+	text-align: left;
+	font-weight: bold;
+	color: #0054E3;
+	width: 22px;
+}
+table.cells th.wkhead {
+	border-right: double #CCCCCC 3px;
+	cursor: default;
+	width: 22px;
+}
+/*The date cells*/
+table.cells td {
+	border: solid #CCCCCC 1px;
+	vertical-align: top;
+	text-align: left;
+	font-weight: bold;
+	height: 20px; /*IE doesn't like ems*/
+}
+table.cells td.wkhead {
+	background-color: white;
+	text-align: center;
+	border-right: double #CCCCCC 3px;
+	color: #0054E3;
+}
+table.cells td.noselect {
+	background-color: #EEEEEE;
+	color: #BBBBBB;
+	text-decoration: line-through;
+	cursor: default;
+}
+table.cells td.hlday {
+	background-color: #99FF99;
+}
+table.cells td.wkday {
+	background-color: #DDDDDD;
+}
+table.cells td.wkend {
+	background-color: #DDDDDD;
+}
+table.cells td.curdate {
+
+}
+table.cells td.cell_selected {
+	background-color: #99CCFF;
+	color: black;
+}
+table.cells td.notmnth {
+	background-color: #FFFFFF;
+	color: #CCCCCC;
+}
+table.cells td.notallowed {
+	background-color: white;
+	color: #EEEEEE;
+	font-style: italic;
+}
+table.cells td.hover {
+	background-color: #999999;
+}
+table.cells td div {
+	padding: 1px;
+	margin: 0;
+}
Index: /afridex/plugins/fresh-page/css/iframe.css
===================================================================
--- /afridex/plugins/fresh-page/css/iframe.css (revision 21)
+++ /afridex/plugins/fresh-page/css/iframe.css (revision 21)
@@ -0,0 +1,132 @@
+* {
+	margin: 0;
+	padding: 0;
+}
+
+body {
+	background: #FFF;
+	padding: 18px;
+}
+
+a {
+	color: #09719D;
+	cursor: pointer;
+}
+a:hover {
+	color: #0FB8FF;
+}
+a.kbd {
+	background-color: #E5E5E5;
+	border-color: #ECECEC #ACACAC #ACACAC #ECECEC;
+	border-style: solid;
+	border-width: 2px;
+	color: #858585;
+	font: normal 12px/18px Arial, sans-serif;
+	margin-right: 6px;
+	padding: 1px 3px;
+}
+a:hover.kbd {
+	background-color: #0FB8FF;
+	border-color: #0FB8FF;
+	color: #FFF;
+}
+
+code {
+	background-color: #E5E5E5;
+	color: #666;
+	display: block;
+	font: normal 10px/14px "Lucida Console", Monaco, monospace;
+	float: left;
+	margin: 9px 0 18px 0;
+	width: 592px;
+	padding: 9px 18px;
+}
+
+em {
+	color: #998000;
+	font-style: normal;
+}
+
+form div {
+	clear: both;
+	margin-bottom: 18px;
+	overflow: hidden;
+}
+form fieldset {
+	border: #E5E5E5 2px solid;
+	clear: both;
+	margin-bottom: 9px;
+	overflow: hidden;
+	padding: 9px 18px;
+}
+form input,
+form select {
+	border-color: #ACACAC #E1E1E1 #E1E1E1 #ACACAC;
+	border-style: solid;
+	border-width: 2px;
+	float: left;
+	font: normal 12px Arial, sans-serif;
+	margin-right: 6px;
+	width: 125px;
+}
+
+form.long input, 
+form.long select {
+	width: 175px;
+}
+
+form.short input, 
+form.short select {
+	width: 75px;
+}
+
+form.long input.alternate,
+form.long select.alternate,
+form.long input.calendar,
+form.long select.calendar {
+	width: 149px;
+}
+
+form.short input.alternate,
+form.short select.alternate,
+form.short input.calendar,
+form.short select.calendar {
+	width: 49px;
+}
+
+form input {
+	padding: 1px 3px;
+}
+
+form label {
+	color: #262626;
+	font: normal 12px/18px Arial, sans-serif;
+	float: left;
+	margin-right: 6px;
+	text-align: right;
+	width: 50px;
+}
+form legend {
+	background: #E5E5E5;
+	color: #262626;
+	font: normal 12px/18px Arial, sans-serif;
+	margin-bottom: 9px;
+	padding: 2px 11px;
+}
+
+h1 {
+	color: #262626;
+	font: normal 14px/18px Arial, sans-serif;
+	margin-bottom: 18px;
+}
+
+p {
+	color: #666;
+	font: normal 12px/18px Arial, sans-serif;
+	margin-bottom: 9px;
+}
+
+strong {
+	color: #262626;
+	font-weight: normal;
+}
Index: /afridex/plugins/fresh-page/RCCWP_CustomWritePanel.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_CustomWritePanel.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_CustomWritePanel.php (revision 21)
@@ -0,0 +1,428 @@
+<?php
+class RCCWP_CustomWritePanel
+{
+	function AssignToRole($customWritePanelId, $roleName)
+	{
+		$customWritePanel = RCCWP_CustomWritePanel::Get($customWritePanelId);
+		$capabilityName = $customWritePanel->capability_name;
+		$role = get_role($roleName);
+		$role->add_cap($capabilityName);	
+	}
+	
+	function Create($name, $description = '', $standardFields = array(), $hiddenExtFields = array(), $categories = array(), $display_order = 1)
+	{
+		include_once('RC_Format.php');
+		global $wpdb;
+		
+		$capabilityName = RCCWP_CustomWritePanel::GetCapabilityName($name);
+
+		$sql = sprintf(
+			"INSERT INTO " . RC_CWP_TABLE_WRITE_PANELS .
+			" (name, description, display_order, capability_name, type)" .
+			" values" .
+			" (%s, %s, %d, %s, %s)", 
+			RC_Format::TextToSql($name), 
+			RC_Format::TextToSql($description),
+			$display_order,
+			RC_Format::TextToSql($capabilityName),
+			RC_Format::TextToSql($_POST['radPostPage'])	
+		);
+			
+		$wpdb->query($sql);
+		$customWritePanelId = $wpdb->insert_id;
+		
+		if (!isset($categories))
+			$categories = array();
+		foreach ($categories as $cat_id)
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_PANEL_CATEGORY .
+				" (panel_id, cat_id)" .
+				" values (%d, %d)",
+				$customWritePanelId,
+				$cat_id 
+				);
+			$wpdb->query($sql);
+		}
+		
+		if (!isset($standardFields))
+			$standardFields = array();
+		foreach ($standardFields as $standard_field_id)
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+				" (panel_id, standard_field_id)" .
+				" values (%d, %d)",
+				$customWritePanelId,
+				$standard_field_id 
+				);
+			$wpdb->query($sql);
+		}
+		
+		if (!empty($hiddenExtFields))
+		{
+			foreach ($hiddenExtFields as $css_id)
+			{
+				if ($css_id != '')
+				{
+					$sql = sprintf(
+						"INSERT INTO " . RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD .
+						" (panel_id, css_id)" .
+						" values (%d, %s)",
+						$customWritePanelId,
+						RC_Format::TextToSql($css_id)
+						);
+					
+					$wpdb->query($sql);
+				}
+			}
+		}
+		
+		return $customWritePanelId;
+	}
+	
+	function Delete($customWritePanelId = null)
+	{
+		include_once ('RCCWP_CustomField.php');
+		if (isset($customWritePanelId))
+		{
+			global $wpdb;
+			
+			$customWritePanel = RCCWP_Application::GetCustomWritePanels($customWritePanelId);
+			$customFields = RCCWP_CustomWritePanel::GetCustomFields($customWritePanel->id);
+			foreach ($customFields as $field) 
+			{
+				RCCWP_CustomField::Delete($field->id);
+  			}
+		  	
+  			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_WRITE_PANELS .
+				" WHERE id = %d",
+				$customWritePanel->id
+				);
+			$wpdb->query($sql);
+			
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_CATEGORY .
+				" WHERE panel_id = %d",
+				$customWritePanel->id
+				);
+			$wpdb->query($sql);
+			
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+				" WHERE panel_id = %d",
+				$customWritePanelId
+				);
+			$wpdb->query($sql);
+			
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD .
+				" WHERE panel_id = %d",
+				$customWritePanelId
+				);
+			$wpdb->query($sql);
+		}
+	}
+	
+	function Get($customWritePanelId)
+	{
+		global $wpdb;
+	
+		$sql = "SELECT id, name, description, display_order, capability_name, type FROM " . RC_CWP_TABLE_WRITE_PANELS .
+			" WHERE id = " . (int)$customWritePanelId;
+		
+		$results = $wpdb->get_row($sql);
+		
+		return $results;
+	}
+	
+	function GetAssignedCategoryIds($customWritePanelId)
+	{
+		$results = RCCWP_CustomWritePanel::GetAssignedCategories($customWritePanelId);
+		$ids = array();
+		foreach ($results as $r)
+		{
+			$ids[] = $r->cat_id;
+		}
+		
+		return $ids;
+	}
+	
+	function GetAssignedCategories($customWritePanelId)
+	{
+		global $wpdb;
+		/*
+			$sql = "SELECT rc.cat_id, cat_name FROM " . RC_CWP_TABLE_PANEL_CATEGORY . 
+			" rc JOIN $wpdb->categories wp ON rc.cat_ID = wp.cat_ID" . 
+			" WHERE panel_id = " . $customWritePanelId;
+		*/
+		
+		if( $wpdb->terms != '' )
+		{
+			$sql = "SELECT rc.cat_id, wp.name AS cat_name FROM " . 
+				RC_CWP_TABLE_PANEL_CATEGORY . "
+				rc JOIN $wpdb->terms wp ON rc.cat_ID = wp.term_id" . "
+				WHERE panel_id = " . $customWritePanelId;
+		}
+		else
+		{
+			$sql = "SELECT rc.cat_id, cat_name FROM " . 
+				RC_CWP_TABLE_PANEL_CATEGORY . "
+				rc JOIN $wpdb->categories wp ON rc.cat_ID = wp.cat_ID 
+				WHERE panel_id = " . $customWritePanelId;
+		}
+		
+
+		$results = $wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		
+		return $results;
+	}
+	
+	function GetCapabilityName($customWritePanelName)
+	{
+		// copied from WP's sanitize_title_with_dashes($title) (formatting.php)
+		$capabilityName = strip_tags($customWritePanelName);
+		// Preserve escaped octets.
+		$capabilityName = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $capabilityName);
+		// Remove percent signs that are not part of an octet.
+		$capabilityName = str_replace('%', '', $capabilityName);
+		// Restore octets.
+		$capabilityName = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $capabilityName);
+   
+		$capabilityName = remove_accents($capabilityName);
+		if (seems_utf8($capabilityName)) 
+		{
+			if (function_exists('mb_strtolower')) 
+			{
+				$capabilityName = mb_strtolower($capabilityName, 'UTF-8');
+			}
+			$capabilityName = utf8_uri_encode($capabilityName, 200);
+		}
+   
+		$capabilityName = strtolower($capabilityName);
+		$capabilityName = preg_replace('/&.+?;/', '', $capabilityName); // kill entities
+		$capabilityName = preg_replace('/[^%a-z0-9 _-]/', '', $capabilityName);
+		$capabilityName = preg_replace('/\s+/', '_', $capabilityName);
+		$capabilityName = preg_replace('|-+|', '_', $capabilityName);
+		$capabilityName = trim($capabilityName, '_');
+   
+		return $capabilityName;
+	}
+
+	function GetCustomFields($customWritePanelId)
+	{
+		global $wpdb;
+		$sql = "SELECT cf.id, cf.name, tt.name AS type, cf.description, cf.display_order, co.options, co.default_option AS default_value, tt.has_options, cp.properties, tt.has_properties, tt.allow_multiple_values FROM " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" cf LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS . " co ON cf.id = co.custom_field_id" .
+			" LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " cp ON cf.id = cp.custom_field_id" .
+			" JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " tt ON cf.type = tt.id" . 
+			" WHERE panel_id = " . $customWritePanelId .
+			" ORDER BY cf.display_order";
+		$results =$wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		
+		for ($i = 0; $i < $wpdb->num_rows; ++$i)
+		{
+			$results[$i]->options = unserialize($results[$i]->options);
+			$results[$i]->properties = unserialize($results[$i]->properties);
+			$results[$i]->default_value = unserialize($results[$i]->default_value);
+		}
+		
+		return $results;
+	}
+	
+	function GetHiddenExternalFieldCssIds($customWritePanelId)
+	{
+		global $wpdb;
+		$sql = "SELECT css_id FROM " . RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD . 
+			" WHERE panel_id = " . $customWritePanelId;
+		$results = $wpdb->get_col($sql);
+		if (!isset($results))
+			$results = array();
+		
+		return $results;
+	}
+	
+	function GetStandardFieldCssIds($customWritePanelId)
+	{
+		$results = RCCWP_CustomWritePanel::GetStandardFields($customWritePanelId);
+		$ids = array();
+		foreach ($results as $r)
+		{
+			$ids[] = $r->css_id; 
+		}
+		
+		return $ids; 
+	}
+	
+	function GetStandardFieldIds($customWritePanelId)
+	{
+		$results = RCCWP_CustomWritePanel::GetStandardFields($customWritePanelId);
+		$ids = array();
+		foreach ($results as $r)
+		{
+			$ids[] = $r->standard_field_id;
+		}
+		
+		return $ids;
+	}
+
+	function GetStandardFields($customWritePanelId)
+	{
+		global $wpdb;
+		$sql = "SELECT ps.standard_field_id, name, css_id FROM " . RC_CWP_TABLE_PANEL_STANDARD_FIELD . 
+			" ps JOIN " . RC_CWP_TABLE_STANDARD_FIELDS . " sf ON ps.standard_field_id = sf.id" . 
+			" WHERE panel_id = " . $customWritePanelId;
+		$results = $wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		
+		return $results;
+	}
+	
+	function Update($customWritePanelId, $name, $description = '', $standardFields = array(), $hiddenExtFields = array(), $categories = array(), $display_order = 1)
+	{
+		include_once('RC_Format.php');
+		global $wpdb;
+		
+		$capabilityName = RCCWP_CustomWritePanel::GetCapabilityName($name);
+	
+		$sql = sprintf(
+			"UPDATE " . RC_CWP_TABLE_WRITE_PANELS .
+			" SET name = %s" .
+			" , description = %s" .
+			" , display_order = %d" .
+			" , capability_name = %s" .
+			" , type = %s" .
+			" where id = %d",
+			RC_Format::TextToSql($name), 
+			RC_Format::TextToSql($description),
+			$display_order,
+			RC_Format::TextToSql($capabilityName),
+			RC_Format::TextToSql($_POST['radPostPage']),
+			$customWritePanelId );
+		
+		$wpdb->query($sql);
+		
+		if (!isset($categories) || empty($categories))
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_CATEGORY .
+				" WHERE panel_id = %d",
+				$customWritePanelId
+				);
+			
+			$wpdb->query($sql);
+		}
+		else
+		{
+			$currentCategoryIds = array();
+			$currentCategoryIds = RCCWP_CustomWritePanel::GetAssignedCategoryIds($customWritePanelId);
+			
+			$keepCategoryIds = array_intersect($currentCategoryIds, $categories);
+			$deleteCategoryIds = array_diff($currentCategoryIds, $keepCategoryIds);
+			$insertCategoryIds = array_diff($categories, $keepCategoryIds);
+			
+			foreach ($insertCategoryIds as $cat_id)
+			{
+				$sql = sprintf(
+					"INSERT INTO " . RC_CWP_TABLE_PANEL_CATEGORY .
+					" (panel_id, cat_id)" .
+					" values (%d, %d)",
+					$customWritePanelId,
+					$cat_id 
+					);
+				$wpdb->query($sql);
+			}
+			
+			if (!empty($deleteCategoryIds))
+			{
+				$sql = sprintf(
+					"DELETE FROM " . RC_CWP_TABLE_PANEL_CATEGORY .
+					" WHERE panel_id = %d" .
+					" AND cat_id IN (%s)",
+					$customWritePanelId,
+					implode(',', $deleteCategoryIds)
+					);
+				
+				$wpdb->query($sql);
+			}
+		}
+		
+		if (!isset($standardFields) || empty($standardFields))
+		{			
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+				" WHERE panel_id = %d",
+				$customWritePanelId
+				);
+			$wpdb->query($sql);
+		}
+		else
+		{
+			$currentStandardFieldIds = array();
+			$currentStandardFieldIds = RCCWP_CustomWritePanel::GetStandardFieldIds($customWritePanelId);
+			
+			$keepStandardFieldIds = array_intersect($currentStandardFieldIds, $standardFields);
+			$deleteStandardFieldIds = array_diff($currentStandardFieldIds, $keepStandardFieldIds);
+			$insertStandardFieldIds = array_diff($standardFields, $keepStandardFieldIds);
+			
+			foreach ($insertStandardFieldIds as $standard_field_id)
+			{
+				$sql = sprintf(
+					"INSERT INTO " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+					" (panel_id, standard_field_id)" .
+					" values (%d, %d)",
+					$customWritePanelId,
+					$standard_field_id 
+					);
+				$wpdb->query($sql);
+			}
+			
+			if (!empty($deleteStandardFieldIds))
+			{
+				$sql = sprintf(
+					"DELETE FROM " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+					" WHERE panel_id = %d" .
+					" AND standard_field_id IN (%s)",
+					$customWritePanelId,
+					implode(',', $deleteStandardFieldIds)
+					);
+				
+				$wpdb->query($sql);
+			}
+		}
+		
+		$sql = sprintf(
+			"DELETE FROM " . RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD .
+			" WHERE panel_id = %d",
+			$customWritePanelId
+			);
+		
+		$wpdb->query($sql);
+		
+		if (!empty($hiddenExtFields))
+		{
+			foreach ($hiddenExtFields as $css_id)
+			{
+				if ($css_id != '')
+				{
+					$sql = sprintf(
+						"INSERT INTO " . RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD .
+						" (panel_id, css_id)" .
+						" values (%d, %s)",
+						$customWritePanelId,
+						RC_Format::TextToSql($css_id)
+						);
+					
+					$wpdb->query($sql);
+				}
+			}
+		}
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/phpthumb.unsharp.php
===================================================================
--- /afridex/plugins/fresh-page/phpthumb.unsharp.php (revision 21)
+++ /afridex/plugins/fresh-page/phpthumb.unsharp.php (revision 21)
@@ -0,0 +1,162 @@
+<?php
+//////////////////////////////////////////////////////////////
+////                                                      ////
+////              p h p U n s h a r p M a s k             ////
+////                                                      ////
+////    Unsharp mask algorithm by Torstein Hønsi 2003.    ////
+////               thoensi_at_netcom_dot_no               ////
+////               Please leave this notice.              ////
+////                                                      ////
+//////////////////////////////////////////////////////////////
+/// From: http://vikjavev.no/hovudsida/umtestside.php       //
+//                                                          //
+//  Reformatted by James Heinrich <info@silisoftware.com>   //
+//  for use in phpThumb() on 3 February 2003.               //
+//                                                          //
+//  phpThumb() is found at http://phpthumb.sourceforge.net ///
+//////////////////////////////////////////////////////////////
+
+/*
+WARNING! Due to a known bug in PHP 4.3.2 this script is not working well in this version.
+The sharpened images get too dark. The bug is fixed in version 4.3.3.
+
+Unsharp masking is a traditional darkroom technique that has proven very suitable for
+digital imaging. The principle of unsharp masking is to create a blurred copy of the image
+and compare it to the underlying original. The difference in colour values
+between the two images is greatest for the pixels near sharp edges. When this
+difference is subtracted from the original image, the edges will be
+accentuated.
+
+The Amount parameter simply says how much of the effect you want. 100 is 'normal'.
+Radius is the radius of the blurring circle of the mask. 'Threshold' is the least
+difference in colour values that is allowed between the original and the mask. In practice
+this means that low-contrast areas of the picture are left unrendered whereas edges
+are treated normally. This is good for pictures of e.g. skin or blue skies.
+
+Any suggenstions for improvement of the algorithm, expecially regarding the speed
+and the roundoff errors in the Gaussian blur process, are welcome.
+*/
+
+class phpUnsharpMask {
+
+	function applyUnsharpMask(&$img, $amount, $radius, $threshold) {
+
+		// $img is an image that is already created within php using
+		// imgcreatetruecolor. No url! $img must be a truecolor image.
+
+		// Attempt to calibrate the parameters to Photoshop:
+		$amount = min($amount, 500);
+		$amount = $amount * 0.016;
+		if ($amount == 0) {
+			return true;
+		}
+
+		$radius = min($radius, 50);
+		$radius = $radius * 2;
+
+		$threshold = min($threshold, 255);
+
+		$radius = abs(round($radius)); 	// Only integers make sense.
+		if ($radius == 0) {
+			return true;
+		}
+
+		$w = ImageSX($img);
+		$h = ImageSY($img);
+		$imgCanvas  = ImageCreateTrueColor($w, $h);
+		$imgCanvas2 = ImageCreateTrueColor($w, $h);
+		ImageCopy($imgCanvas,  $img, 0, 0, 0, 0, $w, $h);
+		ImageCopy($imgCanvas2, $img, 0, 0, 0, 0, $w, $h);
+
+
+		$builtinFilterSucceeded = false;
+		if ($radius == 1) {
+			if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+				if (ImageFilter($imgCanvas, IMG_FILTER_GAUSSIAN_BLUR) && ImageFilter($imgCanvas2, IMG_FILTER_GAUSSIAN_BLUR)) {
+					$builtinFilterSucceeded = true;
+				}
+			}
+		}
+
+		if (!$builtinFilterSucceeded) {
+			$imgBlur  = ImageCreateTrueColor($w, $h);
+			$imgBlur2 = ImageCreateTrueColor($w, $h);
+
+			///////////////////////////
+			//
+			// Gaussian blur matrix:
+			//	1  2  1
+			//	2  4  2
+			//	1  2  1
+			//
+			///////////////////////////
+
+			// Move copies of the image around one pixel at the time and merge them with weight
+			// according to the matrix. The same matrix is simply repeated for higher radii.
+			for ($i = 0; $i < $radius; $i++)	{
+				ImageCopy     ($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1);            // up left
+				ImageCopyMerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w,     $h,     50);        // down right
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h,     33.33333);  // down left
+				ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w,     $h - 1, 25);        // up right
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h,     33.33333);  // left
+				ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w,     $h,     25);        // right
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w,     $h - 1, 20 );       // up
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w,     $h,     16.666667); // down
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w,     $h,     50);        // center
+				ImageCopy     ($imgCanvas, $imgBlur, 0, 0, 0, 0, $w,     $h);
+
+				// During the loop above the blurred copy darkens, possibly due to a roundoff
+				// error. Therefore the sharp picture has to go through the same loop to
+				// produce a similar image for comparison. This is not a good thing, as processing
+				// time increases heavily.
+				ImageCopy     ($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 20 );
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 16.666667);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
+				ImageCopy     ($imgCanvas2, $imgBlur2, 0, 0, 0, 0, $w, $h);
+			}
+			ImageDestroy($imgBlur);
+			ImageDestroy($imgBlur2);
+		}
+
+		// Calculate the difference between the blurred pixels and the original
+		// and set the pixels
+		for ($x = 0; $x < $w; $x++)	{ // each row
+			for ($y = 0; $y < $h; $y++)	{ // each pixel
+
+				$rgbOrig = ImageColorAt($imgCanvas2, $x, $y);
+				$rOrig = (($rgbOrig >> 16) & 0xFF);
+				$gOrig = (($rgbOrig >>  8) & 0xFF);
+				$bOrig =  ($rgbOrig        & 0xFF);
+
+				$rgbBlur = ImageColorAt($imgCanvas, $x, $y);
+				$rBlur = (($rgbBlur >> 16) & 0xFF);
+				$gBlur = (($rgbBlur >>  8) & 0xFF);
+				$bBlur =  ($rgbBlur        & 0xFF);
+
+				// When the masked pixels differ less from the original
+				// than the threshold specifies, they are set to their original value.
+				$rNew = (abs($rOrig - $rBlur) >= $threshold) ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) : $rOrig;
+				$gNew = (abs($gOrig - $gBlur) >= $threshold) ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) : $gOrig;
+				$bNew = (abs($bOrig - $bBlur) >= $threshold) ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) : $bOrig;
+
+				if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
+					$pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
+					ImageSetPixel($img, $x, $y, $pixCol);
+				}
+			}
+		}
+		ImageDestroy($imgCanvas);
+		ImageDestroy($imgCanvas2);
+
+		return true;
+	}
+
+}
+
+?>
Index: /afridex/plugins/fresh-page/RCCWP_ManagementPage.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_ManagementPage.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_ManagementPage.php (revision 21)
@@ -0,0 +1,148 @@
+<?php
+include_once('RCCWP_Application.php');
+
+class RCCWP_ManagementPage
+{
+	function AssignCustomWritePanel()
+	{
+		$postId = (int)$_GET['assign-custom-write-panel'];
+		$customWritePanels = RCCWP_Application::GetCustomWritePanels();
+		$customWritePanelOptions = RCCWP_Options::Get();
+		$message = 'The Post that you\'re about to edit is not associated with any Custom Write Panel.';
+		?>
+		
+		<div id="message" class="updated"><p><?php _e($message); ?></p></div>
+		
+		<div class="wrap">
+		<h2><?php _e('Assign Custom Write Panel'); ?></h2>
+		
+		<form action="" method="post" id="assign-custom-write-panel-form">
+		
+		<table class="optiontable">
+		<tbody>
+		<tr valign="top">
+			<th scope="row">Custom Write Panel:</th>
+			<td>
+				<select name="custom-write-panel-id" id="custom-write-panel-id">
+					<option value="">(None)</option>
+				<?php
+				$defaultCustomWritePanel = $customWritePanelOptions['default-custom-write-panel'];
+				foreach ($customWritePanels as $panel) :
+					$selected = $panel->id == $defaultCustomWritePanel ? 'selected="selected"' : '';
+				?>
+					<option value="<?=$panel->id?>" <?=$selected?>><?=$panel->name?></option>
+				<?php
+				endforeach;
+				?>
+				</select>
+			</td>
+		</tr>
+		</tbody>
+		</table>
+		
+		<input type="hidden" name="post-id" value="<?=$postId?>" />
+		<p class="submit" >
+			<input name="edit-with-no-custom-write-panel" type="submit" value="Don't Assign Custom Write Panel" />
+			<input name="edit-with-custom-write-panel" type="submit" value="Edit with Custom Write Panel" />
+		</p>
+		
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+	
+	function GetCustomFieldEditUrl($customWritePanelId, $customFieldId)
+	{
+		$url = '?page=' . RC_CWP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php&edit-custom-field=' . $customFieldId . '&custom-write-panel-id=' . $customWritePanelId;
+		return $url;
+	}
+	
+	function GetCustomFieldDeleteUrl($customWritePanelId, $customFieldId)
+	{
+		$url = '?page=' . RC_CWP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php&delete-custom-field=' . $customFieldId . '&custom-write-panel-id=' . $customWritePanelId;
+		return $url;
+	}
+	
+	function GetCustomWritePanelEditUrl($customWritePanelId)
+	{
+		$url = '?page=' . urlencode( RC_CWP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') .'&view-custom-write-panel=' . $customWritePanelId . '&custom-write-panel-id=' . $customWritePanelId;
+		return $url;
+	}
+	
+	function GetCustomWritePanelDeleteUrl($customWritePanelId)
+	{
+		$url = '?page=' . RC_CWP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php&delete-custom-write-panel=' . $customWritePanelId . '&custom-write-panel-id=' . $customWritePanelId;
+		return $url;
+	}
+	
+	function View()
+	{
+		$customWritePanels = RCCWP_Application::GetCustomWritePanels();
+		?>
+
+		<div class="wrap">
+		<h2><?php _e('Custom Write Panel'); ?></h2>
+		<table cellpadding="3" cellspacing="3" width="100%" class="widefat">
+		<thead>
+		<tr>
+			<th scope="col" width="7%"><?php _e('Order'); ?></th>
+			<th scope="col" width="50%"><?php _e('Name'); ?></th>
+<!--			<th scope="col"><?php _e('Description'); ?></th> -->
+			<th colspan="3" style="text-align:center"><?php _e('Action'); ?></th>
+		</tr>
+		</thead>
+		<tbody>
+		<?php
+		foreach ($customWritePanels as $panel) :
+			$class = $class == '' ? 'alternate' : '';
+		?>
+		<tr class="<?=$class?>">
+			<td align="right"><?=$panel->display_order ?></td>
+			<td><?=$panel->name ?></td>
+<!--			<td><?=$panel->description ?></td></td> -->
+
+			<td width="15%" align="center">
+			<?php $buttonBGColor = $buttonBGColor == '#F1F1F1' ? '#fff' : '#F1F1F1';  ?>
+			<form action="" method="post" id="view-write-panel-form">
+				<input type="hidden" name="custom-write-panel-id" value="<?=$panel->id?>" />
+				 <input name="create-custom-field" type="submit" id="create-custom-field" value="Create Custom Field" style="cursor:pointer; border:0px; background:<?php echo $buttonBGColor?>; color:#00019B;" onmouseover="this.style.background='#DFDFDF';" onmouseout="this.style.background='<?php echo $buttonBGColor?>'"/>
+			</form>
+			</td>
+
+<!--
+			<td width="20%">
+			<form action="" method="post" id="view-write-panel-form" name="view-write-panel-form">
+				<input type="text" name="custom-write-panel-id" value="<?=$panel->id?>" />
+				<a href="javascript:void(0);" onclick="document.form.view-write-panel-form.submit();" class="edit"><?php _e('Create Custom Field'); ?></a>
+			</form>
+			</td>
+-->
+			<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelEditUrl($panel->id); ?>" class="edit"><?php _e('View') ?></a></td>
+			<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelDeleteUrl($panel->id); ?>" class="delete"><?php _e('Delete'); ?></a></td>
+		
+		</tr>
+		<?php
+		endforeach;
+		?>
+		</tbody>
+		</table>
+
+		</div>
+
+		<div class="wrap">
+		<form action="" method="post" id="main-management-form">
+		<p class="submit">
+			<!--
+			<input name="custom-write-panel-options" type="submit" id="custom-write-panel-options" value="<?php _e('Go to Options'); ?>" />
+			-->
+			<input name="create-custom-write-panel" type="submit" id="create-custom-write-panel" value="<?php _e('Create Custom Write Panel'); ?>" />
+		</p>
+		</form>
+		</div>
+
+		<?php 
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/RCCWP_Post_bu.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_Post_bu.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_Post_bu.php (revision 21)
@@ -0,0 +1,288 @@
+<?php
+class RCCWP_Post
+{	
+	function save_files()
+	{
+
+
+### IF NOT CALLED FROM A CWP PAGE, IGNORE IT ###
+
+		if( ! isset( $_REQUEST['rc_cwp_meta_files'] ) )
+		{
+			return;
+		}
+
+
+
+		### IF NO NEW FILES ARE GIVEN, USE THE OLD FILES ###
+
+		if( count( $_FILES ) < 1 && isset( $_REQUEST['rc_cwp_meta_files'] ) )
+		{
+			foreach( $_REQUEST['rc_cwp_meta_files'] as $meta_name )
+			{
+				$_POST[$meta_name] = $_REQUEST[ $meta_name . '_last' ];
+			}
+		}
+
+		
+		### DEAL WITH SPECIFIED FILES ###
+
+		
+
+		foreach( $_FILES as $meta_name => $file )
+		{
+			$upload_filepath = $_POST[$meta_name . '_filepath']; 
+			if ($upload_filepath != ""){
+				$oldfilename = $upload_filepath; 
+				$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/files/';
+
+				$filename = time().substr($oldfilename, 10);
+				//rename the file to avoid image caching
+				rename(dirname(__FILE__) . '/files/'.$oldfilename, dirname(__FILE__) . '/files/'.$filename);
+				$_POST[$meta_name] = $path . $filename;
+
+				
+			}
+			else{
+				$_POST[$meta_name] = $_POST[$meta_name . '_last'];
+				continue;
+			}
+
+			/*if( file_exists( $file['tmp_name'] ) )
+			{
+				 //Old Upload code:
+				$filename = time() . $file['name'];
+
+				$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/files/';
+
+				$_POST[$meta_name] = $path . $filename;
+
+				move_uploaded_file( $file['tmp_name'], dirname(__FILE__) . '/files/' . $filename );
+				*/
+			
+			/*else // try to get web file
+			{
+				$url = $_REQUEST[$meta_name . '_url'];
+				
+
+				if ($fp_source = @fopen($url, 'rb')) {
+					//Get target filename
+					$exploded_url = explode( '.', $url );
+	
+					$ext = array_pop( $exploded_url );
+	
+					$filename = time() . '_' . str_replace( 'rc_cwp_meta_', '', $meta_name ) . '.' . $ext;
+					
+					$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/files/';
+		
+					$_POST[$meta_name] = $path . $filename;
+	
+					$directory = dirname(__FILE__) . '/files/';
+
+					$fp_dest = @fopen($directory . $filename,"wb");
+					if ($fp_dest == false) return;
+
+					while(!feof($fp_source)) {
+						set_time_limit(30);
+
+						if (connection_status()!=0) return;
+
+						$readData = fread($fp_source, 1024*8);
+						if ($readData == false) return;
+						
+						if (fwrite($fp_dest,$readData) == false) return;
+						
+					}
+					if (fclose($fp_source) == false) return;
+					if (fclose($fp_dest) == false) return;
+				}
+				else{
+					$_POST[$meta_name] = $_POST[$meta_name . '_last'];
+					continue;
+				}
+
+				
+
+				
+				if( ! $file_string = file_get_contents( $url ) ) // if we fail to get web file, use old value
+				{
+					$_POST[$meta_name] = $_POST[$meta_name . '_last'];
+
+					continue;
+				}
+
+				$exploded_url = explode( '.', $url );
+
+				$ext = array_pop( $exploded_url );
+
+				$filename = time() . '_' . str_replace( 'rc_cwp_meta_', '', $meta_name ) . '.' . $ext;
+				
+				$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/files/';
+	
+				$_POST[$meta_name] = $path . $filename;
+
+				$directory = dirname(__FILE__) . '/files/';
+
+				file_put_contents( $directory . $filename, $file_string );
+			}*/
+		}
+
+
+
+		### ADD THUMBNAILER PATH AND PARAMS TO THE PHOTO FILE NAME ###
+
+		if( isset( $_REQUEST['rc_cwp_meta_photos'] ) )
+		{
+			foreach( $_REQUEST['rc_cwp_meta_photos'] as $meta_name )
+			{
+				// if photo is new, add thumbnailer path
+
+				if( $_POST[ $meta_name ] != $_POST[ $meta_name . '_last' ] )
+				{
+					$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/phpThumb.php?src=';
+					$_POST[ $meta_name ] = $path . $_POST[ $meta_name ];
+				}
+				
+
+				// if photo is new, add params
+
+				if( isset( $_REQUEST[ $meta_name . '_params' ] ) && $_REQUEST[ $meta_name . '_params' ] )
+				{
+					if( ! strpos( $_POST[$meta_name], $_REQUEST[ $meta_name . '_params' ] ) )
+					{
+						$_POST[$meta_name] .= $_REQUEST[$meta_name . '_params'];
+					}
+				}
+			}
+		}
+	}
+
+
+	function GetCustomWritePanel()
+	{
+		if (isset($_GET['post']))
+		{
+
+			$customWritePanelId = get_post_meta((int)$_GET['post'], RC_CWP_POST_WRITE_PANEL_ID_META_KEY, true);
+			
+			if (empty($customWritePanelId))
+			{
+				$customWritePanelId = (int)$_REQUEST['custom-write-panel-id'];
+			}
+		}
+		else if (isset($_REQUEST['custom-write-panel-id']))
+		{
+			$customWritePanelId = (int)$_REQUEST['custom-write-panel-id'];
+		}
+		
+		if (isset($customWritePanelId))
+		{
+			include_once('RCCWP_Application.php');
+			$customWritePanel = RCCWP_Application::GetCustomWritePanels($customWritePanelId);
+		}
+		return $customWritePanel;
+	}
+	
+	function SetCustomWritePanel($postId)
+	{
+		if(!wp_verify_nonce($_REQUEST['rc-custom-write-panel-verify-key'], 'rc-custom-write-panel'))
+			return $postId;
+        		
+		if (!current_user_can('edit_post', $postId))
+			return $postId;
+		
+		$customWritePanelId = $_POST['rc-cwp-custom-write-panel-id'];
+		if (isset($customWritePanelId))
+		{
+			if (!empty($customWritePanelId))
+			{
+				if (!update_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY, $customWritePanelId))
+				{
+					add_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY, $customWritePanelId);
+				}
+			}
+			else
+			{
+				delete_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY);
+			}
+		}
+	}
+	
+	function SetMetaValue($postId)
+	{
+		global $wpdb;
+
+		if(!wp_verify_nonce($_REQUEST['rc-custom-write-panel-verify-key'], 'rc-custom-write-panel'))
+			return $postId;
+		
+		if (!current_user_can('edit_post', $postId))
+			return $postId;
+		
+		$customWritePanelId = $_POST['rc-cwp-custom-write-panel-id'];
+		if (isset($customWritePanelId))
+		{
+			if (!empty($customWritePanelId))
+			{
+				$customFieldCount = count($customFieldKeys);
+				$customFieldKeys = (array)$_POST['rc_cwp_meta_keys'];
+				foreach ($customFieldKeys as $key)
+				{
+					$rawCustomFieldName = $key;
+					
+					if (isset($rawCustomFieldName))
+					{
+						if (!empty($rawCustomFieldName))
+						{
+							$customFieldName = $wpdb->escape(stripslashes(trim(RC_Format::GetFieldName($rawCustomFieldName))));
+
+							$customFieldValue = $_POST[$rawCustomFieldName];
+							$audioExt = substr($customFieldValue, -4);
+
+							/*if($audioExt == '.mp3' || $audioExt == '.MP3' || $audioExt == '.avg' || $audioExt == '.AVG' || $audioExt == 'mpeg' || $audioExt == 'MPEG')
+							{
+								$customFieldValue = "\<div style=\'padding-top:3px;\'\>\<object classid=\'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\' codebase='\http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0\' width=\'95%\' height=\'20\' wmode=\'transparent\' \>\<param name=\'movie\' value=\'http://www.freshoutmedia.com/singlemp3player.swf?file=".$customFieldValue."\' wmode=\'transparent\' /\>\<param name=\'quality\' value=\'high\' wmode=\'transparent\' /\>\<embed src=\'http://www.freshoutmedia.com/singlemp3player.swf?file=".$customFieldValue."' width=\'50\%\' height=\'20\' quality=\'high\' pluginspage=\'http://www.macromedia.com/go/getflashplayer\' type=\'application/x-shockwave-flash\' wmode=\'transparent\' \>\</embed\>\</object\>\</div\>";
+							}
+
+							if(!(strpos($customFieldValue, 'phpThumb.php') === FALSE))
+							{
+								$_POST['rc_cwp_meta_Image_url'];
+
+								if((strpos($customFieldValue, 'img src') === FALSE))
+								{
+									$cssClass = $wpdb->get_results("SELECT CSS FROM wp_rc_cwp_panel_custom_field WHERE panel_id='".$_POST['rc-cwp-custom-write-panel-id']."' and name='".$customFieldName."'");
+
+									$customFieldValue = "\<img src=\'".$customFieldValue."\' class=\"".$cssClass[0]->CSS."\" \/\>";
+									//$customFieldValue = "\<img src=\'".$customFieldValue."\' \/\>";
+								}
+							}*/
+
+							delete_post_meta($postId, $customFieldName);
+							if (is_array($customFieldValue))
+							{
+								foreach ($customFieldValue as $value)
+								{
+									$value = stripslashes(trim($value));
+									add_post_meta($postId, $customFieldName, $value);
+								}
+							}
+							else
+							{
+								$value = stripslashes(trim($customFieldValue));
+								add_post_meta($postId, $customFieldName, $value);
+							}
+						}
+						else
+						{
+							delete_post_meta($postId, $customFieldName);
+						}
+					}
+				}
+			}
+			else
+			{
+				
+			}
+		}
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/RCCWP_CustomField.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_CustomField.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_CustomField.php (revision 21)
@@ -0,0 +1,243 @@
+<?php
+include_once('RC_Format.php');
+
+class RCCWP_CustomField
+{
+	function Create($customWritePanelId, $name, $description, $order = 1, $type, $options = null, $default_value = null, $properties = null)
+	{
+		global $wpdb;
+	
+		$sql = sprintf(
+			"INSERT INTO " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" (panel_id, name, description, display_order, type, CSS) values (%d, %s, %s, %d, %d, %s)",
+			$customWritePanelId,
+			RC_Format::TextToSql($name),
+			RC_Format::TextToSql($description),
+			$order,
+			$type,
+			"'".$_POST['custom-field-css']."'"
+			);
+		$wpdb->query($sql);
+		
+		$customFieldId = $wpdb->insert_id;
+		
+		$field_type = RCCWP_CustomField::GetCustomFieldTypes($type);
+		if ($field_type->has_options == "true")
+		{
+			$options = explode("\n", $options);
+			array_walk($options, array(RC_Format, TrimArrayValues));
+			
+			$default_value = explode("\n", $default_value);
+			array_walk($default_value, array(RC_Format, TrimArrayValues));
+			
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" (custom_field_id, options, default_option) values (%d, %s, %s)",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($options)),
+				RC_Format::TextToSql(serialize($default_value))
+				);	
+			$wpdb->query($sql);	
+		}
+		
+		if ($field_type->has_properties == "true")
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" (custom_field_id, properties) values (%d, %s)",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($properties))
+				);	
+			$wpdb->query($sql);	
+		}
+	}
+	
+	function Delete($customFieldId = null)
+	{
+		global $wpdb;
+		
+		$customField = RCCWP_CustomField::Get($customFieldId);
+		
+		$sql = sprintf(
+			"DELETE FROM " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" WHERE id = %d",
+			$customFieldId
+			);
+		$wpdb->query($sql);
+		
+		if ($customField->has_options == "true")
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);	
+			$wpdb->query($sql);	
+		}
+	}
+	
+	function Get($customFieldId)
+	{
+		global $wpdb;
+		$sql = "SELECT cf.id, cf.name, cf.CSS, tt.name AS type, cf.description, cf.display_order, co.options, co.default_option AS default_value, tt.has_options, cp.properties, tt.has_properties, tt.allow_multiple_values FROM " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" cf LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS . " co ON cf.id = co.custom_field_id" .
+			" LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " cp ON cf.id = cp.custom_field_id" .
+			" JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " tt ON cf.type = tt.id" . 
+			" WHERE cf.id = " . $customFieldId;
+		$results = $wpdb->get_row($sql);
+			
+		$results->options = unserialize($results->options);
+		$results->properties = unserialize($results->properties);
+		$results->default_value = unserialize($results->default_value);
+		return $results;
+	}
+	
+	function GetCustomFieldNames($customFieldTypeId = null)
+	{
+		$customFieldNames = array();
+		$customFields = RCCWP_CustomField::GetCustomFieldTypes($customFieldTypeId);
+		foreach ($customFields as $field)
+		{
+			$customFieldNames[] = $field->name;
+		}
+		
+		return $customFieldNames;
+	}
+	
+	function GetCustomFieldTypes($customFieldTypeId = null)
+	{
+		global $wpdb;
+	
+		if (isset($customFieldTypeId))
+		{
+			$sql = "SELECT id, name, description, has_options, has_properties, allow_multiple_values FROM " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES .
+				" WHERE id = " . (int)$customFieldTypeId;
+			$results = $wpdb->get_row($sql);	
+		}
+		else
+		{
+			$sql = "SELECT id, name, description, has_options, has_properties, allow_multiple_values FROM " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES;
+			$results = $wpdb->get_results($sql);
+			if (!isset($results))
+				$results = array();
+		}
+		return $results;
+	}
+	
+	function GetCustomFieldValue($postId, $customFieldName)
+	{
+		return get_post_meta($postId, $customFieldName, true);
+	}
+	
+	function GetCustomFieldValues($postId, $customFieldName)
+	{
+		return get_post_meta($postId, $customFieldName, false);
+	}
+	
+	function GetDefaultCustomFieldType()
+	{
+		return 'Textbox';
+	}
+	
+	function GetOptions()
+	{
+
+	}
+
+	function GetProperties()
+	{
+
+	}
+	
+	function Update($customFieldId, $name, $description, $order = 1, $type, $options = null, $default_value = null, $properties = null)
+	{
+		global $wpdb;
+		
+		$oldCustomField = RCCWP_CustomField::Get($customFieldId);
+		
+		if ($oldCustomField->name != $name)
+		{
+			$sql = sprintf(
+				"UPDATE $wpdb->postmeta" .
+				" SET meta_key = %s" .
+				" WHERE meta_key = %s",
+				RC_Format::TextToSql($name),
+				RC_Format::TextToSql($oldCustomField->name)
+				);
+			
+			$wpdb->query($sql);
+		}
+		
+		$sql = sprintf(
+			"UPDATE " . RC_CWP_TABLE_PANEL_CUSTOM_FIELD .
+			" SET name = %s" .
+			" , description = %s" .
+			" , display_order = %d" .
+			" , type = %d" .
+			" , CSS = '%s'" .
+			" WHERE id = %d",
+			RC_Format::TextToSql($name),
+			RC_Format::TextToSql($description),
+			$order,
+			$type,
+			$_POST['custom-field-css'],
+			$customFieldId
+			);
+		$wpdb->query($sql);
+
+
+		$field_type = RCCWP_CustomField::GetCustomFieldTypes($type);
+		if ($field_type->has_options == "true")
+		{
+			$options = explode("\n", $options);
+			array_walk($options, array(RC_Format, TrimArrayValues));
+			
+			$default_value = explode("\n", $default_value);
+			array_walk($default_value, array(RC_Format, TrimArrayValues));
+			
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" (custom_field_id, options, default_option) values (%d, %s, %s)" . 
+				" ON DUPLICATE KEY UPDATE options = %s, default_option = %s",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($options)),
+				RC_Format::TextToSql(serialize($default_value)),
+				RC_Format::TextToSql(serialize($options)),
+				RC_Format::TextToSql(serialize($default_value))
+				);	
+			$wpdb->query($sql);	
+		}
+		else
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);
+			$wpdb->query($sql);	
+		}
+		
+		if ($field_type->has_properties == "true")
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" (custom_field_id, properties) values (%d, %s)" .
+				" ON DUPLICATE KEY UPDATE properties = %s",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($properties)),
+				RC_Format::TextToSql(serialize($properties))
+				);	
+			$wpdb->query($sql);	
+		}
+		else
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);
+			$wpdb->query($sql);	
+		}
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/RCCWP_upload.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_upload.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_upload.php (revision 21)
@@ -0,0 +1,217 @@
+<html><head>
+<?php
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can('edit_posts')))
+	die("Athentication failed!");
+
+
+$imagesExts = array('gif','jpg','png');
+$audiosExts = array('wav','mp3');
+
+$acceptedExtsString = "";
+
+function DownloadFile(){
+	global $acceptedExtsString, $imagesExts, $audiosExts;
+
+	$url = $_POST['upload_url'];
+
+	// Prepare accpeted extensions
+	$acceptedExts = array();
+
+	if ('1' == $_POST['type'])
+		$acceptedExts = $imagesExts;
+	elseif ('2' == $_POST['type']) 	
+		$acceptedExts = $audiosExts;
+
+
+	//Retrieve file
+	if ($fp_source = @fopen($url, 'rb')) {
+		//Get target filename
+		$exploded_url = explode( '.', $url );
+
+		$ext = array_pop( $exploded_url );
+
+		// Check extension
+		if (false != $acceptedExts)
+			if (false === array_search(strtolower($ext), $acceptedExts)){
+				foreach($acceptedExts as $acceptedExt)
+					if ($acceptedExtsString == "")
+						$acceptedExtsString = $acceptedExt;
+					else
+						$acceptedExtsString = $acceptedExtsString." - ".$acceptedExt;
+				return false;
+			}
+		
+
+		$filename = time() . '_' . str_replace( 'rc_cwp_meta_', '', $_POST["input_name"]) . '.' . $ext;
+		
+		$directory = dirname(__FILE__) . '/files/';
+
+		$fp_dest = @fopen($directory . $filename,"wb");
+		if ($fp_dest == false) return false;
+
+		while(!feof($fp_source)) {
+			set_time_limit(30);
+
+			//if (connection_status()!=0) return false;
+
+			$readData = fread($fp_source, 1024*2);
+			//if ($readData == false) return false;
+			
+			fwrite($fp_dest,$readData);
+			
+		}
+		fclose($fp_source) ;
+		fclose($fp_dest) ;
+		//chmod($directory . $filename, 0644);
+
+		return $filename;
+	}
+	return false;
+
+}
+
+
+if (isset($_POST['fileframe'])) 
+{
+	$operationSuccess = "false";
+	// A file is uploaded
+	if (isset($_FILES['file']) && (!empty($_FILES['file']['tmp_name'])))  // file was send from browser
+	{
+		
+		if ($_FILES['file']['error'] == UPLOAD_ERR_OK)  // no error
+		{
+			$filename = time() . $_FILES['file']['name'];
+			@move_uploaded_file( $_FILES['file']['tmp_name'], dirname(__FILE__) . '/files/' . $filename );
+			@chmod(dirname(__FILE__) . '/files/' . $filename, 0644);
+
+			$result_msg = '<p>The file '.$_FILES['file']['name'].' was uploaded successfuly. Please remember to click the submit button to save the post</p>';
+			$operationSuccess = "true";
+		}
+		elseif ($_FILES['file']['error'] == UPLOAD_ERR_INI_SIZE)
+			$result_msg = '<p>The uploaded file exceeds the maximum upload limit</p>';
+		else 
+			$result_msg = '<p>The upload failed</p>';
+	
+	}
+	elseif (isset($_POST['upload_url']) && (!empty($_POST['upload_url'])))  // file was send from browser
+	{
+		if ( (substr($_POST['upload_url'],0,4) != "http") && (substr($_POST['upload_url'],0,3) != "ftp"))
+			$_POST['upload_url'] = "http://".$_POST['upload_url'];
+
+		$filename = DownloadFile();
+		
+
+		if (false == $filename) {
+			if ($acceptedExtsString != "") $infoStr = ". Make sure the file ends with: $acceptedExtsString";
+			$result_msg = "Error downloading file: ".$_POST['upload_url'].$infoStr;
+		}
+		else{
+			$result_msg = '<p>The URL '.$_POST['upload_url'].' was downloaded successfuly. Please remember to click the submit button to save the post</p>';
+			$operationSuccess = "true";
+		}
+		
+	}	
+
+	// If operation is success, make sure the file was created properly
+	if ($operationSuccess == "true"){
+		if ($fp_check_file = @fopen(dirname(__FILE__) . '/files/' . $filename, 'rb')) {
+			fclose($fp_check_file);
+		}
+		else{
+			$operationSuccess = "false";
+			$result_msg = "Failed to upload the file!";
+		}
+		
+	}
+	
+
+?>
+
+	<script language="javascript">
+
+    		var par = window.parent.document;
+		var iframe = par.getElementById('upload_internal_iframe_<?=$_POST["input_name"]?>');
+		par.getElementById('upload_progress_<?=$_POST["input_name"]?>').innerHTML = '<?=$result_msg?>';
+
+		if (<?=$operationSuccess?>){
+			
+			iframe.style.visibility = "visible";
+			iframe.style.height = "75px";
+			
+			par.getElementById('<?=$_POST["input_name"]?>').value = '<?=$filename?>';
+			par.getElementById('<?=$_POST["input_name"]?>').onchange();
+		}
+		else{
+			iframe.style.visibility = "visible";
+			iframe.style.height = "75px";
+		}
+		
+	</script>
+
+
+<?php
+	//exit();
+}
+?>
+
+<script language="javascript">
+function upload(){
+	// hide old iframe
+    	var par = window.parent.document;
+
+	var iframe = par.getElementById('upload_internal_iframe_<?=$_GET["input_name"]?>');
+	iframe.style.height = '0px';
+		
+	// update progress
+	par.getElementById('upload_progress_<?=$_GET["input_name"]?>').style.visibility = "visible";
+	par.getElementById('upload_progress_<?=$_GET["input_name"]?>').style.height = "auto";
+	par.getElementById('upload_progress_<?=$_GET["input_name"]?>').innerHTML = "Transferring ";
+
+	setTimeout("transferring(0)",1000) ;
+	
+	// send
+	document.iform.submit();
+	//iframe.style.visibility = 'hidden';
+	//par.getElementById('upload_progress').style.visibility = "hidden";
+}
+
+function transferring(dots){
+	
+	newString = "Transferring ";
+	for (var x=1; x<=dots; x++) {
+        	newString = newString + ".";
+    	} 
+	
+	var par = window.parent.document;
+
+	// update progress
+	if (par.getElementById('upload_progress_<?=$_GET["input_name"]?>').innerHTML.substring(0,5) != "Trans") return;
+	par.getElementById('upload_progress_<?=$_GET["input_name"]?>').innerHTML = newString;
+	if (dots == 4) dots = 0; else dots = dots + 1;
+	setTimeout("transferring("+dots+")",1000) ;
+	
+}
+
+</script>
+<style>
+body {vertical-align:top;}
+</style>
+<link type="text/css" href="http://localhost/wp/wp-admin/wp-admin.css?version=2.3.3" rel="stylesheet">
+</head>
+<body>
+<form name="iform" action="" method="post" enctype="multipart/form-data">
+<input id="file" type="file" name="file"  />
+<input type="hidden" name="fileframe" value="true" /></br>
+<input id="upload_url"
+			name="upload_url"
+			type="text"
+			size="40"
+			/> :or URL 
+<input type="button" onclick="upload()" value="Upload" />
+<input type="hidden" name="fileframe" value="true" />
+<input type="hidden" name="imgnum" />
+<input type="hidden" name="input_name" value="<?=$_GET["input_name"]?>" />
+<input type="hidden" name="type" value="<?=$_GET["type"]?>" />
+</form></body>
+</html>
Index: /afridex/plugins/fresh-page/RCCWP_Post.php
===================================================================
--- /afridex/plugins/fresh-page/RCCWP_Post.php (revision 21)
+++ /afridex/plugins/fresh-page/RCCWP_Post.php (revision 21)
@@ -0,0 +1,295 @@
+<?php
+class RCCWP_Post
+{	
+	function save_files()
+	{
+
+
+### IF NOT CALLED FROM A CWP PAGE, IGNORE IT ###
+
+		if( ! isset( $_REQUEST['rc_cwp_meta_files'] ) )
+		{
+			return;
+		}
+
+
+
+		### IF NO NEW FILES ARE GIVEN, USE THE OLD FILES ###
+
+		if( count( $_FILES ) < 1 && isset( $_REQUEST['rc_cwp_meta_files'] ) )
+		{
+			foreach( $_REQUEST['rc_cwp_meta_files'] as $meta_name )
+			{
+				$_POST[$meta_name] = $_REQUEST[ $meta_name . '_last' ];
+			}
+		}
+
+		
+		### DEAL WITH SPECIFIED FILES ###
+
+		
+
+		foreach( $_FILES as $meta_name => $file )
+		{
+			$upload_filepath = $_POST[$meta_name . '_filepath']; 
+			if ($upload_filepath != ""){
+				$oldfilename = $upload_filepath; 
+				$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/files/';
+
+				$filename = time().substr($oldfilename, 10);
+				//rename the file to avoid image caching
+				rename(dirname(__FILE__) . '/files/'.$oldfilename, dirname(__FILE__) . '/files/'.$filename);
+				$_POST[$meta_name] = $path . $filename;
+
+				
+			}
+			else{
+				$_POST[$meta_name] = $_POST[$meta_name . '_last'];
+				continue;
+			}
+
+			/*if( file_exists( $file['tmp_name'] ) )
+			{
+				 //Old Upload code:
+				$filename = time() . $file['name'];
+
+				$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/files/';
+
+				$_POST[$meta_name] = $path . $filename;
+
+				move_uploaded_file( $file['tmp_name'], dirname(__FILE__) . '/files/' . $filename );
+				*/
+			
+			/*else // try to get web file
+			{
+				$url = $_REQUEST[$meta_name . '_url'];
+				
+
+				if ($fp_source = @fopen($url, 'rb')) {
+					//Get target filename
+					$exploded_url = explode( '.', $url );
+	
+					$ext = array_pop( $exploded_url );
+	
+					$filename = time() . '_' . str_replace( 'rc_cwp_meta_', '', $meta_name ) . '.' . $ext;
+					
+					$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/files/';
+		
+					$_POST[$meta_name] = $path . $filename;
+	
+					$directory = dirname(__FILE__) . '/files/';
+
+					$fp_dest = @fopen($directory . $filename,"wb");
+					if ($fp_dest == false) return;
+
+					while(!feof($fp_source)) {
+						set_time_limit(30);
+
+						if (connection_status()!=0) return;
+
+						$readData = fread($fp_source, 1024*8);
+						if ($readData == false) return;
+						
+						if (fwrite($fp_dest,$readData) == false) return;
+						
+					}
+					if (fclose($fp_source) == false) return;
+					if (fclose($fp_dest) == false) return;
+				}
+				else{
+					$_POST[$meta_name] = $_POST[$meta_name . '_last'];
+					continue;
+				}
+
+				
+
+				
+				if( ! $file_string = file_get_contents( $url ) ) // if we fail to get web file, use old value
+				{
+					$_POST[$meta_name] = $_POST[$meta_name . '_last'];
+
+					continue;
+				}
+
+				$exploded_url = explode( '.', $url );
+
+				$ext = array_pop( $exploded_url );
+
+				$filename = time() . '_' . str_replace( 'rc_cwp_meta_', '', $meta_name ) . '.' . $ext;
+				
+				$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/files/';
+	
+				$_POST[$meta_name] = $path . $filename;
+
+				$directory = dirname(__FILE__) . '/files/';
+
+				file_put_contents( $directory . $filename, $file_string );
+			}*/
+		}
+
+
+
+		### ADD THUMBNAILER PATH AND PARAMS TO THE PHOTO FILE NAME ###
+
+		if( isset( $_REQUEST['rc_cwp_meta_photos'] ) )
+		{
+			foreach( $_REQUEST['rc_cwp_meta_photos'] as $meta_name )
+			{
+				// if photo is new, add thumbnailer path
+
+				if( $_POST[ $meta_name ] != $_POST[ $meta_name . '_last' ] )
+				{
+					$path = get_bloginfo('url') . '/wp-content/plugins/fresh-page/phpThumb.php?src=';
+					$_POST[ $meta_name ] = $path . $_POST[ $meta_name ];
+				}
+				
+
+				// if photo is new, add params
+
+				if( isset( $_REQUEST[ $meta_name . '_params' ] ) && $_REQUEST[ $meta_name . '_params' ] )
+				{
+					if( ! strpos( $_POST[$meta_name], $_REQUEST[ $meta_name . '_params' ] ) )
+					{
+						$_POST[$meta_name] .= $_REQUEST[$meta_name . '_params'];
+					}
+				}
+			}
+		}
+	}
+
+
+	function GetCustomWritePanel()
+	{
+		
+		
+		if (isset($_GET['post']))
+		{
+
+			$customWritePanelId = get_post_meta((int)$_GET['post'], RC_CWP_POST_WRITE_PANEL_ID_META_KEY, true);
+		
+		
+			if (empty($customWritePanelId))
+			{
+				$customWritePanelId = (int)$_REQUEST['custom-write-panel-id'];
+			}
+		}
+		else if (isset($_REQUEST['custom-write-panel-id']))
+		{
+			$customWritePanelId = (int)$_REQUEST['custom-write-panel-id'];
+		}
+		
+		if (isset($customWritePanelId))
+		{
+			include_once('RCCWP_Application.php');
+			$customWritePanel = RCCWP_Application::GetCustomWritePanels($customWritePanelId);
+		}
+		
+		return $customWritePanel;
+	}
+	
+	function SetCustomWritePanel($postId)
+	{
+		
+		if(!wp_verify_nonce($_REQUEST['rc-custom-write-panel-verify-key'], 'rc-custom-write-panel'))
+			return $postId;
+        		
+		if (!current_user_can('edit_post', $postId))
+			return $postId;
+		
+		$customWritePanelId = $_POST['rc-cwp-custom-write-panel-id'];
+		if (isset($customWritePanelId))
+		{
+			if (!empty($customWritePanelId))
+			{
+				
+				if (!update_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY, $customWritePanelId))
+				{
+
+					add_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY, $customWritePanelId);
+				}
+			}
+			else
+			{
+				delete_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY);
+			}
+		}
+	}
+	
+	function SetMetaValue($postId)
+	{
+		global $wpdb;
+
+		if(!wp_verify_nonce($_REQUEST['rc-custom-write-panel-verify-key'], 'rc-custom-write-panel'))
+			return $postId;
+		
+		if (!current_user_can('edit_post', $postId))
+			return $postId;
+		
+		$customWritePanelId = $_POST['rc-cwp-custom-write-panel-id'];
+		if (isset($customWritePanelId))
+		{
+			if (!empty($customWritePanelId))
+			{
+				$customFieldCount = count($customFieldKeys);
+				$customFieldKeys = (array)$_POST['rc_cwp_meta_keys'];
+				foreach ($customFieldKeys as $key)
+				{
+					$rawCustomFieldName = $key;
+					
+					if (isset($rawCustomFieldName))
+					{
+						if (!empty($rawCustomFieldName))
+						{
+							$customFieldName = $wpdb->escape(stripslashes(trim(RC_Format::GetFieldName($rawCustomFieldName))));
+
+							$customFieldValue = $_POST[$rawCustomFieldName];
+							$audioExt = substr($customFieldValue, -4);
+
+							/*if($audioExt == '.mp3' || $audioExt == '.MP3' || $audioExt == '.avg' || $audioExt == '.AVG' || $audioExt == 'mpeg' || $audioExt == 'MPEG')
+							{
+								$customFieldValue = "\<div style=\'padding-top:3px;\'\>\<object classid=\'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\' codebase='\http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0\' width=\'95%\' height=\'20\' wmode=\'transparent\' \>\<param name=\'movie\' value=\'http://www.freshoutmedia.com/singlemp3player.swf?file=".$customFieldValue."\' wmode=\'transparent\' /\>\<param name=\'quality\' value=\'high\' wmode=\'transparent\' /\>\<embed src=\'http://www.freshoutmedia.com/singlemp3player.swf?file=".$customFieldValue."' width=\'50\%\' height=\'20\' quality=\'high\' pluginspage=\'http://www.macromedia.com/go/getflashplayer\' type=\'application/x-shockwave-flash\' wmode=\'transparent\' \>\</embed\>\</object\>\</div\>";
+							}
+
+							if(!(strpos($customFieldValue, 'phpThumb.php') === FALSE))
+							{
+								$_POST['rc_cwp_meta_Image_url'];
+
+								if((strpos($customFieldValue, 'img src') === FALSE))
+								{
+									$cssClass = $wpdb->get_results("SELECT CSS FROM wp_rc_cwp_panel_custom_field WHERE panel_id='".$_POST['rc-cwp-custom-write-panel-id']."' and name='".$customFieldName."'");
+
+									$customFieldValue = "\<img src=\'".$customFieldValue."\' class=\"".$cssClass[0]->CSS."\" \/\>";
+									//$customFieldValue = "\<img src=\'".$customFieldValue."\' \/\>";
+								}
+							}*/
+
+							delete_post_meta($postId, $customFieldName);
+							if (is_array($customFieldValue))
+							{
+								foreach ($customFieldValue as $value)
+								{
+									$value = stripslashes(trim($value));
+									add_post_meta($postId, $customFieldName, $value);
+								}
+							}
+							else
+							{
+								$value = stripslashes(trim($customFieldValue));
+								add_post_meta($postId, $customFieldName, $value);
+							}
+						}
+						else
+						{
+							delete_post_meta($postId, $customFieldName);
+						}
+					}
+				}
+			}
+			else
+			{
+				
+			}
+		}
+	}
+}
+?>
Index: /afridex/plugins/fresh-page/Main.php
===================================================================
--- /afridex/plugins/fresh-page/Main.php (revision 21)
+++ /afridex/plugins/fresh-page/Main.php (revision 21)
@@ -0,0 +1,103 @@
+<?php
+/*
+Plugin Name: FreshPost
+Plugin URI: http://freshout.us/goodies
+Description: Turn WordPress into a CMS.
+Author: Freshout
+Version: .13
+Author URI: http://freshout.us/
+*/
+global $wpdb;
+global $post;
+
+
+
+if (is_admin())
+{
+	include_once('RCCWP_Constant.php');
+	include_once('RCCWP_Application.php');
+	
+	register_activation_hook(dirname(__FILE__) . '/Main.php', array('RCCWP_Application', 'Install'));
+
+	if (get_option(RC_CWP_OPTION_KEY) !== false)
+	{
+		include_once('RCCWP_Menu.php');
+
+		//register_deactivation_hook(dirname(__FILE__) . '/Main.php', array('RCCWP_Application', 'Uninstall'));
+
+
+		add_action('admin_menu', array('RCCWP_Post', 'save_files') );
+
+		add_action('admin_menu', array('RCCWP_Menu', 'AttachCustomWritePanelMenuItems'));
+		add_action('admin_menu', array('RCCWP_Menu', 'DetachWpWritePanelMenuItems'));
+
+		include_once('RCCWP_Processor.php');
+		add_action('init', array('RCCWP_Processor', 'Main'));
+		add_action('admin_menu', array('RCCWP_Menu', 'AttachManagementMenuItem'));
+		add_action('admin_menu', array('RCCWP_Menu', 'AttachOptionsMenuItem'));
+		add_filter('posts_where', array('RCCWP_Menu', 'FilterPostsPagesList'));
+		add_action('admin_head', array('RCCWP_Menu', 'HighlightCustomPanel'));
+
+
+		//add_filter('the_posts', array('RCCWP_Menu', 'FilterPostsPagesList'));
+		//add_filter('get_pages', array('RCCWP_Menu', 'FilterPostsPagesList'));
+
+		include_once('RCCWP_Post.php');	
+		add_action('edit_post', array('RCCWP_Post', 'SetCustomWritePanel'));
+		add_action('save_post', array('RCCWP_Post', 'SetCustomWritePanel'));
+		add_action('publish_post', array('RCCWP_Post', 'SetCustomWritePanel'));
+
+		add_action('edit_post', array('RCCWP_Post', 'SetMetaValue'));
+		add_action('save_post', array('RCCWP_Post', 'SetMetaValue'));
+		add_action('publish_post', array('RCCWP_Post', 'SetMetaValue'));
+
+		add_filter('wp_redirect', array('RCCWP_Processor', 'Redirect'));
+
+		add_action('shutdown', array('RCCWP_Processor', 'FlushAllOutputBuffer')); 
+	}
+}
+
+require_once 'get-custom.php';
+
+function cwp_add_type_identifier(){ 
+	global $wpdb;
+	global $post;
+	
+	if( isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id']))
+	{
+		$getPostID = $wpdb->get_results("SELECT id, type FROM". RC_CWP_TABLE_WRITE_PANELS ." WHERE id='".$_GET['custom-write-panel-id']."'");
+		echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"". $getPostID[0]->type ."\" />";
+
+	}
+	else{
+		if($post->post_type == 'page') { 
+			echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"page\" />";
+ 		} else {
+			echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"post\" />";
+ 		}
+
+ 	}
+} 
+
+function cwp_add_pages_identifiers(){
+	global $post;
+	global $wpdb;
+
+	$key = wp_create_nonce('rc-custom-write-panel');
+	$id = "";
+	$result = $wpdb->get_results( " SELECT meta_value
+					FROM $wpdb->postmeta
+					WHERE post_id = '$post->ID' and meta_key = '_rc_cwp_write_panel_id'", ARRAY_A );
+	
+	if (count($result) > 0)
+		$id = $result[0]['meta_value'];
+	echo 
+<<<EOF
+		<input type="hidden" name="rc-custom-write-panel-verify-key" id="rc-custom-write-panel-verify-key" value="$key" />
+		<input type="hidden" name="rc-cwp-custom-write-panel-id" value="$id" />
+EOF;
+}
+
+add_action('edit_page_form','cwp_add_pages_identifiers');
+add_action('edit_form_advanced','cwp_add_type_identifier');
+?>
Index: /afridex/plugins/fresh-page/readme.txt
===================================================================
--- /afridex/plugins/fresh-page/readme.txt (revision 21)
+++ /afridex/plugins/fresh-page/readme.txt (revision 21)
@@ -0,0 +1,61 @@
+=== FreshPost ===
+Contributors: alphaoide, coffee2code, freshout
+Tags: custom write panel, custom, write panel, freshout, freshpost, freshpage
+Requires at least: 2.3
+Tested up to: 2.5.1
+Stable tag: .13
+
+FreshPost is for creating customized write panels with file and photo uploads.  Includes automatic photo resizing via phpThumb and freshout.
+
+
+== Description ==
+
+WordPress gets pretty close to becoming a practical content management solution with this plugin. Create multiple custom write panels, add custom fields (images, drop downs, file upload, images, checkboxes, textareas, etc..), photos can be automatically resized, cropped, watermarked, drop shadowed, and modified in many other ways (this plugin comes with php_thumb).In addition to being able to upload files from your computers, you can also retrieve Images from 3rd party sites on the fly. Even-though weÕve worked long and hard to get this plugin to where it is nowÉ we owe the success of it to the efforts of Scott Reiley, Joshua Siguar, and Eric Pujol.
+
+    * Retrieve images from any URL and store/manipulate them on your server.
+    * Specify height, width, and numerous other requirements on your images
+    * Create multiple custom write panels
+    * Integrates with Role Manager to hide panels from users with the specified roles
+    * Hide obtrusive options in the write panel
+    * Field Labels
+    * Create any kind of custom field - Textbox, Multiline Textbox, Checkbox, Checkbox List, Radiobutton List, Dropdown List, Listbox, File, Photo
+    * Custom Write Pages
+    * Snipshot Integration and Native Cropper
+    * Image Thumbnail Preview
+    * Date Picker
+    * Audio Player
+    * CSS Class for Images
+    * Add Custom Write Panels for Pages
+    * Large File Uploads and Retrievals
+    * Sort Posts (Under Manage) by Write Panel Used to Compose
+
+
+
+== Installation ==
+
+Follow the following steps to install this plugin.
+
+1. Download plugin to the `/wp-content/plugins/` folder
+2. CHMOD 777 the cache folder
+3. Activate the plugin through the 'Plugins' menu in WordPress
+
+
+== Frequently Asked Questions ==
+
+= Please go to the URL below for examples, questions, and more =
+
+http://freshout.us/goodies/fresh-post-for-wordpress-wordpress-cms/
+
+
+
+= Is this Fresh Page? =
+
+Yes, this is the same plugin as Fresh Page.
+
+
+
+== Credits ==
+
+* ["Joshua Sigar"](http://rhymedcode.net) The creator of Custom Write Panel
+* ["James Heinrich"](info@silisoftware.com) The creator of phpThumb
+* ["Scott Reilly"](http://www.coffee2code.com) The creator of get-custom plugin
Index: /afridex/plugins/fresh-page/get-custom.php
===================================================================
--- /afridex/plugins/fresh-page/get-custom.php (revision 21)
+++ /afridex/plugins/fresh-page/get-custom.php (revision 21)
@@ -0,0 +1,181 @@
+<?php
+/*
+
+=>> Visit the plugin's homepage for more information and latest updates  <<=
+                http://www.coffee2code.com/wp-plugins/
+
+2. Optional: Add filters for 'the_meta' to filter custom field data (see the end of the file for 
+commented out samples you may wish to include).  *NEW*: Add per-meta filters by hooking 'the_meta_$field'
+
+4. Give a post a custom field with a value.
+
+5. Use the function freshout somewhere inside "the loop" and/or use the function c2c_get_recent_custom
+outside "the loop"; use 'echo' to display the contents of the custom field; or use as an argument to 
+another function
+
+
+Function arguments:
+    $field	: This is the name of the custom field you wish to display
+    $before	: The text to display before all field value(s)
+    $after	: The text to display after all field value(s)
+    $none	: The text to display in place of the field value should no field value exists; if defined as ''
+    		and no field value exists, then nothing (including no $before and $after) gets displayed
+    $between 	: The text to display between multiple occurrences of the custom field; if defined as '', then
+    		only the first instance will be used
+    $before_last: The text to display between the next-to-last and last items listed when multiple occurrences of
+    		the custom field; $between MUST be set to something other than '' for this to take effect
+    
+Additional arguments used by c2c_get_recent_custom():
+   $limit	: The limit to the number of 
+   $unique	: Boolean ('true' or 'false') to indicate if each custom field value in the results should be unique
+   $order	: Indicates if the results should be sorted in chronological order ('ASC') (the earliest custom field value
+   		listed first), or reverse chronological order ('DESC') (the most recent custom field value listed first)
+   $include_static : Boolean ('true' or 'false') to indicate if static posts (i.e. "pages) should be included when
+   		retrieving recent custom values; default is 'true'
+   $show_pass_post : Boolean ('true' or 'false') to indicate if password protected posts should be included when 
+   		retrieving recent custom values; default is 'false'
+		
+Examples: (visit the plugin's homepage for more examples)
+
+	<?php echo freshout('mymood'); ?>  // with this simple invocation, you can echo the value of any metadata field
+	
+	<?php echo freshout('mymood', 'Today's moods: ', '', ', '); ?>
+	
+	<?php echo c2c_get_recent_custom('mymood', 'Most recent mood: '); ?>
+	
+	<?php echo freshout('mymood', '(Current mood: ', ')', ''); ?>
+	
+	<?php echo freshout('mylisten', 'Listening to : ', '', 'No one at the moment.'); ?>
+	
+	<?php echo freshout('myread', 'I\'ve been reading ', ', if you must know.', 'nothing'); ?>
+	
+	<?php echo freshout('todays_link', '<a class="tlink" href="', '" >Today\'s Link</a>'); ?>
+	
+	<?php echo freshout('related_offsite_links', 
+		   'Here\'s a list of offsite links related to this post:<ol><li><a href="',
+		   '">Related</a></li></ol>',
+		   '',
+		   '">Related</a></li><li><a href="'); ?>
+	
+	<?php echo freshout('more_pictures',
+		   'Pictures I\'ve taken today:<br /><div class="more_pictures"><img alt="[photo]" src="',
+		   '" /></div>',
+		   '',
+		   '" /> : <img alt="[photo]" src="'); ?>
+
+	Custom 'more...' link text, by replacing <?php the_content(); ?> in index.php with this:
+	<?php the_content(freshout('more', '<span class="morelink">', '</span>', '(more...)')); ?>
+	
+*/
+
+/*
+Copyright (c) 2004-2005 by Scott Reilly (aka coffee2code)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, 
+modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+if (!isset($wpdb->posts)) {	// For WP 1.2 compatibility
+	global $tableposts, $tablepostmeta;
+	$wpdb->posts = $tableposts;
+	$wpdb->postmeta = $tablepostmeta;
+}
+
+// Get Variable
+function get ($field, $before='', $after='', $none='', $between='', $before_last='') {
+	return c2c__format_custom($field, (array)get_post_custom_values($field), $before, $after, $none, $between, $before_last);
+}
+
+// Get Image. 
+function get_image ($field, $before='', $after='', $none='', $between='', $before_last='') {
+	global $wpdb;
+	$fieldValue = c2c__format_custom($field, (array)get_post_custom_values($field), $before, $after, $none, $between, $before_last);
+	$cssClass = $wpdb->get_results("SELECT CSS FROM wp_rc_cwp_panel_custom_field WHERE name='".$field."'");
+	$finalString = stripslashes(trim("\<img src=\'".$fieldValue."\' class=\"".$cssClass[0]->CSS."\" \/\>"));
+	return $finalString;
+}
+
+// Get Audio. 
+function get_audio ($field, $before='', $after='', $none='', $between='', $before_last='') {
+	$fieldValue = c2c__format_custom($field, (array)get_post_custom_values($field), $before, $after, $none, $between, $before_last);
+	$finalString = stripslashes(trim("\<div style=\'padding-top:3px;\'\>\<object classid=\'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\' codebase='\http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0\' width=\'95%\' height=\'20\' wmode=\'transparent\' \>\<param name=\'movie\' value=\'http://www.freshoutmedia.com/singlemp3player.swf?file=".$fieldValue."\' wmode=\'transparent\' /\>\<param name=\'quality\' value=\'high\' wmode=\'transparent\' /\>\<embed src=\'http://www.freshoutmedia.com/singlemp3player.swf?file=".$fieldValue."' width=\'50\%\' height=\'20\' quality=\'high\' pluginspage=\'http://www.macromedia.com/go/getflashplayer\' type=\'application/x-shockwave-flash\' wmode=\'transparent\' \>\</embed\>\</object\>\</div\>"));
+	return $finalString;
+}
+
+// This works outside "the loop"
+function c2c_get_recent_custom ($field, $before='', $after='', $none='', $between=', ', $before_last='', $limit=1, $unique=false, $order='DESC', $include_static=true, $show_pass_post=false) {
+	global $wpdb;
+	if (empty($between)) $limit = 1;
+	if ($order != 'ASC') $order = 'DESC';
+	$now = current_time('mysql');
+
+	$sql = "SELECT ";
+	if ($unique) $sql .= "DISTINCT ";
+	$sql .= "meta_value FROM $wpdb->posts AS posts, $wpdb->postmeta AS postmeta ";
+	$sql .= "WHERE posts.ID = postmeta.post_id AND postmeta.meta_key = '$field' ";
+	$sql .= "AND ( posts.post_status = 'publish' ";
+	if ($include_static) $sql .= " OR posts.post_status = 'static' ";
+	$sql .= " ) AND posts.post_date < '$now' ";
+	if (!$show_pass_post) $sql .= "AND posts.post_password = '' ";
+	$sql .= "AND postmeta.meta_value != '' ";
+	$sql .= "ORDER BY posts.post_date $order LIMIT $limit";
+	$results = array(); $values = array();
+	$results = $wpdb->get_results($sql);
+	if (!empty($results))
+		foreach ($results as $result) { $values[] = $result->meta_value; };
+	return c2c__format_custom($field, $values, $before, $after, $none, $between, $before_last);
+} //end c2c_get_recent_custom()
+
+/* Helper function */
+function c2c__format_custom ($field, $meta_values, $before='', $after='', $none='', $between='', $before_last='') {
+	$values = array();
+	if (empty($between)) $meta_values = array_slice($meta_values,0,1);
+	if (!empty($meta_values))
+		foreach ($meta_values as $meta) {
+			$meta = apply_filters("the_meta_$field", $meta);
+			$values[] = apply_filters('the_meta', $meta);
+		}
+
+	if (empty($values)) $value = '';
+	else {
+		$values = array_map('trim', $values);
+		if (empty($before_last)) $value = implode($values, $between);
+		else {
+			switch ($size = sizeof($values)) {
+				case 1:
+					$value = $values[0];
+					break;
+				case 2:
+					$value = $values[0] . $before_last . $values[1];
+					break;
+				default:
+					$value = implode(array_slice($values,0,$size-1), $between) . $before_last . $values[$size-1];
+			}
+		}
+	}
+	if (empty($value)) {
+		if (empty($none)) return;
+		$value = $none;
+	}
+	return $before . $value . $after;
+} //end c2c__format_custom()
+
+// Some filters you may wish to perform: (these are filters typically done to 'the_content' (post content))
+//add_filter('the_meta', 'convert_chars');
+//add_filter('the_meta', 'wptexturize');
+
+// Other optional filters (you would need to obtain and activate these plugins before trying to use these)
+//add_filter('the_meta', 'c2c_hyperlink_urls', 9);
+//add_filter('the_meta', 'text_replace', 2);
+//add_filter('the_meta', 'textile', 6);
+
+?>
Index: /afridex/plugins/get-recent-comments 2/changelog.txt
===================================================================
--- /afridex/plugins/get-recent-comments 2/changelog.txt (revision 21)
+++ /afridex/plugins/get-recent-comments 2/changelog.txt (revision 21)
@@ -0,0 +1,143 @@
+Changelog
+
+Version Date       Changes
+2.0.2   2007/09/25 Fix: Plugin was not compatible to WordPress
+                   2.0.11 any more. Thank you to Stephan for
+                   reporting the bug.
+2.0.1   2007/09/24 Added switch on the categories page, which
+                   reverses the selection. It is now possible to
+                   include or exclude categories.
+2.0     2007/09/24 New code for fetching the data: 1. Instead of
+                   one expensive database query we now use two
+                   or more cheap queries. Thanks to mirra, who
+                   reported the problem. And again thank you to
+                   the people mentioned in changlog 1.4, where
+                   the cache was introduced for the same
+                   (performance-) problems on big blogs. 2. This
+                   also fixed a bug, which lead to too less than
+                   requested comments in lists, ordered by post.
+                   Thanks to Johanna and FrÃ©dÃ©ric for reporting
+                   and documenting this. Changed the css in the
+                   admin gui, to work around a display issue
+                   with Tiger Admin. Thank you, Andi, for
+                   finding this. Added %time_since macro, which
+                   displays the time since the comment was
+                   posted. Thanks to Imran and Keith for
+                   sugesting (something like) this (very long
+                   ago). Admin interface: Added switch fpr
+                   turning on and off smileys. Thank you, panos,
+                   for requesting this feature. Support for
+                   Custom Smileys Plugin. Thanks to Henry for
+                   suggesting this. Fix: Username was not
+                   displayed as "Anonymous", if commentor left
+                   no name. Thanks to Pixelation for reporting
+                   this. Added support for WordPress 2.3. It
+                   will drop the post2cat table. Changed plugin
+                   to new taxonomy scheme. A *great* thank you
+                   goes to Lakatos Zsolt, who provided a
+                   complete patch for get-recent-comments-1.5.6,
+                   which made it very easy for me to understand
+                   how 2.0-beta10 had to be changed. Thank you
+                   also to xelios, Ville and Kretzschmar who
+                   warned me, that WordPress 2.3 will break the
+                   old plugin code.
+1.5.5   2007/03/26 Added support for malyfred's Polyglot Plugin.
+                   Requested by Torben.
+1.5.4   2007/02/01 Use full pingback_author as %comment_author
+                   (instead of 'Unknown', if the pingback parser
+                   fails to recognize the pingback_author.
+                   Thanks again to Gant who found this in his
+                   blog. Added %author_url_href macros, which
+                   allows to generate inactive links, if the
+                   commentator did not leave an url. This was
+                   wished (in part long ago) by beej, carl,
+                   FilSchiesty and SwB.
+                   Added %profile_picture macro, which supports
+                   Hannah Gray's Profile Pics Plugin. Thank you
+                   for the idea and your help, Markus
+1.5.3   2007/01/15 Refresh cache, when a comment is approved by
+                   moderator. Problem found by Gant. Thank you!
+1.5.2   2007/01/05 Added option for excluding comments from blog
+                   authors. Suggested by This is Zimbabwe, Slim,
+                   marilyn's shampoo and Igor M.
+1.5.1   2006/12/29 Store the cache base64 encoded. There seems
+                   to be a problem with the unserialization of
+                   multibyte characters. Thanks to priv, who
+                   reported the problem and suggested the
+                   encoding.
+                   After upgrading to this version you should
+                   trigger a regeneration of the cache by adding
+                   a comment somewhere.
+1.5     2006/12/27 New pingback parser
+                   Stop losing html entities and tags in the
+                   post titles and comments by using
+                   wptexturize. Thanks to ejm (again!) and
+                   mobius for reporting the problem and making
+                   suggestions.
+                   Bugfix in widget code: Error, when trackbacks
+                   came before comments
+1.4     2006/12/24 The plugin is a widget now. Thanks to
+                   herrmueller and Thomas de Klein for
+                   suggesting this feature.
+                   Cache the output in order to reduce the
+                   database impact of the plugin. Thanks to the
+                   following people for reporting the poor
+                   performance and making suggestions to solve
+                   the problem: Brandon Stone, King of Fools,
+                   Robert Basic and especially CountZero.
+                   Option to combine comments and trackbacks in
+                   one list (requested by Maniac and die
+                   produzentin)
+                   Allow to Group comments by their posting
+                   (requested by eyolf)
+                   Allow limit of comments per post (suggested
+                   by Thomas)
+                   Use Wordpress 2.1 compatible database
+                   variables. Thanks to spencerp, for reporting
+                   and fixing.
+                   Bugfix: Wrong key used in gravatar hash
+                   (Thank you, Hamzeh N., for finding and fixing
+                   this).
+                   Updated the stylesheets to the look of
+                   wordpress 2.x.
+                   Added two macros: %comment_type and
+                   %post_counter.
+                   Use less option variables in db.
+                   Updated instructions page.
+                   Dropped support for Wordpress 1.2
+1.3.1   2006/12/11 Fixes for problems with wordpress running
+                   under windows.
+1.3     2006/11/26 Fixes for problems with php5.
+1.2     2005/09/15 Prevent pingbacks from own blog. Thanks to
+                   Matt for the idea and support!
+                   To use the feature, go to the trackbacks
+                   configuration and enter the address of your
+                   webserver.
+1.0     2005/03/21 Also show comments to static pages. (They are
+                   new in WP 1.5). Thanks to maza for the hint.
+0.9     2005/03/20 Introduced admin gui. Handle trackbacks
+                   different than comments. Replaced most
+                   regular expressions with basic string
+                   operations. Dedicated macro for posting time.
+                   Requested by Zonekiller
+0.8     2005/02/04 Readjusted sequence of arguments to the one
+                   described in the documentation. Thanks to
+                   Thomas
+0.7     2005/02/03 Renamed plugin to get-recent-comments, to
+                   make it possible to use the subversion system
+                   at www.wp-plugins.org
+                   Allow to specify your own formatting in the
+                   function call
+0.5     2005/01/02 Removed superfluous </p>
+0.4     2004/12/19 Use function arguments for displaying HTML
+                   before and after the comment
+                   Make number of comments and number of
+                   characters also function arguments
+0.3     2004/12/08 Link to permalink of comment
+                   Wrap very long strings
+0.2                Donât show comments that are not approved
+0.1     2004/11/03 Initial release
+
+Thanks to all who sent bug reports and ideas for improvements.
+Please send me a mail if I forgot you to mention here.
+
Index: /afridex/plugins/get-recent-comments 2/todo
===================================================================
--- /afridex/plugins/get-recent-comments 2/todo (revision 21)
+++ /afridex/plugins/get-recent-comments 2/todo (revision 21)
@@ -0,0 +1,130 @@
+# 2.0 panos: Is it possible to enable smilies in recent_comments?
+
+
+### Maniac: I needed comments and pings displayed in one list combined 
+
+###produzentin: ich wollte mal vorsichtig nachfragen, ob es eine version geben
+###wird, in der man trackbacks und comments zusammen abfragen und unter ein
+###ÃŒberschrift darstellen kann (siehe comment 213). oder geht das jetzt schon?
+
+# 2.0 Keith: One enhancement would be to provide %comment_age as well, so that you could show a comment as â14 minutes agoâ or something, but that gets complicated when handling multiple languages. Oh, wait, I just saw thereâs a human_time_diff() function in WordPress that handles the language issue, so %comment_age is easy to add.
+
+<a href="http://www.cricket.mailliw.com" rel="external nofollow">Will</a>: Is it possible to call the plugin and provide the parameters directly, over-riding the âOptionsâ page?
+
+###DONE Zonekiller: Iâd like to insert âcomment_timeâ after comment_authorâ to abtain something like this:
+###Recent Comments
+###zonekiller: 18:00
+###samred: 17:50
+###jack234: 15:50
+
+###eyolf: Is there a way to customize the output so that all comments to the same
+###post are grouped together under one heading
+
+touchan: Iâm trying to translate your recent comments plugin into Japanese, and make langauge failes (po/mo failes) for it.
+And this is a tiny favor. Could you put
+
+ãload_plugin_textdomain(âget-recent-commentsâ)
+
+on your next release?
+
+Matt: Since the tagboard plugin sets up the tagboard as a static page, I was wondering whether it would be possible for your recent comments plugin to have an option exclude not only certain categories, but certain static pages.
+
+# done in 1.5.4 
+#beej: I just noticed that if the commenter does not supply a url, the link
+#points to the current post the viewer is on. Is there a way for the link to
+#point to the post the comment was made?
+#
+#carl: I have the commenters name link to their url. But if they did not supply
+#a url (or i deleted it) it links to my home page. Is there a way to make no
+#link appear if their isn o link?
+#
+#
+#FilSchiesty: I think that if a comment author does not type a url in the form,
+#then it should not link back to my website, it should just be author name
+#without the link.
+
+Fair Tax: This worked wonderfully. THANK YOU. Looked for a PayPal lnk to send you a tip, but couldnât find one. Feel free to e-mail me, though.  Joshua
+
+ Chele in [dot] LA: was wondering can a favatar be used instead of a gravatar..
+
+### 2006-12-03 done:
+###Brandon Stone: Plugin laeuft langsam, wegen DESTINCT
+###King of Fools: dito - vorschlag fuer workaround
+###CountZero: dito - dito
+
+# 2.0 Imran: I was just wondering if there is a way of integrating Dunstanâs Time Since Plugin in thisâŠ
+
+
+
+
+# Eingbaut in 1.5.2
+#Slim: Exclude authorâs comments YES or NO
+#Exclude these authors, to prevent certain comments to be showing on the main page
+#This is Zimbabwe: weâd like to exclude links to all our comments, and prioritise those left by visitors
+#
+
+### War kein bug ### Tobi: %post_title funktioniert leider nicht. Es wird nur der Autor und der gekÃŒrzte Kommentar ausgegeben.
+
+# herrmueller: widget
+
+Johan: But how do I change the chop-off-dots? Typographically anal, Iâd like them to be a proper ellipsis [âŠ, âŠ] instead of three dots [âŠ].
+
+
+Daniel: The plugin seems to work fine but I try to make it appear in a page instead of in the sidebar
+
+liseur: if it can be possible to display the most recent posts in a static page, not in the sidebar.
+
+
+# done in 1.5
+#ejm Says:
+#September 25th, 2006 at 19:21 e
+#Excuse me for replying to myself. I believe that I found the answer at php.net. I changed trim(htmlspecialchars(stripslashes($post[âTitleâ]))), in get-recent-comments.php to the following:
+#âââââââââââââ
+#$output = str_replace(â%post_titleâ,
+#html_entity_decode($post[âTitleâ]), $output);
+#
+#mobius: html tags in posts are not rendered as html, so you wind up getting what you see above instead of italicized type
+
+
+
+
+oliver: I wonder if there is an easy way to show additional recent comment results in categories or monthly archives, filtered by that category or month. May be this could be a feature for the next plugin version?
+
+Ville SÃ€Ã€vuori | Edit This
+
+Hi! This is a great plugin.
+Just upgraded to version 1.2 and found a little bug. (Or maybe a feature?) The %comment_excerpt does not work expectedly when placed in the title-attribute of the link. The following, for example, outputs full (not shortened) comment:
+%comment_author: %post_title
+As a quick workaround, I found out that for some reason, this works:
+%comment_author: %post_title%comment_excerpt
+Dang. HTML-tags were ripped of. Maybe this will show up:
+<li>%comment_author: <a href="%comment_link" title="%comment_excerpt">%post_title</a></li>
+and
+<li>%comment_author: <a href="%comment_link" title="%comment_excerpt">%post_title</a><span style="display: none;">%comment_excerpt</span></li>
+
+Abel Cheung - utf8
+Tommaso Tessarolo - seltsames char problem
+2ndboy - utf8
+
+# Johanna http://comicsworthreading.com/ nr of comments in order by post wrong
+# FrÃ©dÃ©ric http://skyfall.free.fr/ nr of comments in order by post wrong
+
+julius http://julescubtree.dyn-o-saur.com/blog openid
+
+Marcel van Leeuwen http://www.amvanleeuwen.nl/ Only the line breaks disappeared. Now the text is show in one long lineâŠ
+
+Jessica http://www.schattendings.de/ disclose secret support http://bluesome.net/post/2006/01/01/121/
+
+Ceglie Messapica http://www.ceglieplurale.it/ FeedWordPress wrong link for comments and authors
+
+ prinzzess http://www.prinzzess.biz/ DE uebersetzung
+
+# Mirra: DB to slow
+
+# Andi http://gantenbein.ws/ Tiger Admin
+
+# Henry (nolink) custom smileys plugin
+
+# Pixelation Says:  http://pixcapacitor.com/ Anonymous
+
+
Index: /afridex/plugins/get-recent-comments 2/get-recent-comments.php
===================================================================
--- /afridex/plugins/get-recent-comments 2/get-recent-comments.php (revision 21)
+++ /afridex/plugins/get-recent-comments 2/get-recent-comments.php (revision 21)
@@ -0,0 +1,1571 @@
+<?php
+/*
+Plugin Name: Get Recent Comments
+Version: 2.0.2
+Plugin URI: http://blog.jodies.de/archiv/2004/11/13/recent-comments/
+Author: Krischan Jodies
+Author URI: http://blog.jodies.de
+Description: Display the most recent comments or trackbacks with your own formatting in the sidebar. Visit <a href="options-general.php?page=get-recent-comments.php">Options/Recent Comments</a> after activation of the plugin.
+
+
+
+*/
+
+if ( function_exists("is_plugin_page") && is_plugin_page() ) {
+	kjgrc_options_page(); 
+	return;
+}
+
+function kjgrc_subpage_misc()
+{
+?>
+<h2><?php _e('Miscellaneous Options') ?></h2>
+<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=6&amp;updated=true">
+<input type="hidden" name="function" value="misc">
+<?php wp_nonce_field('update-options') ?>
+
+<p class="submit"><input type="submit" name="Submit" value="<?php _e('Update Options &raquo;') ?>" /></p>
+
+<fieldset class="options"> 
+<legend><?php _e('Smileys') ?></legend> 
+<?php
+$converter = "WordPress";
+if (function_exists("csm_convert") ) {
+?>
+The Custom Smileys plugin is active.
+<?php $converter = "Custom Smileys";
+} else { ?>
+WordPress offers conversion of emoticons like :-) and :-P to graphics on display. At the moment WordPress is set to: <a href="options-writing.php"><?php if (get_settings('use_smilies')) echo 'convert to graphics'; else echo 'don\'t convert to graphics'; ?></a>.
+<?php
+}
+?>
+
+<table class="optiontable"> 
+
+<th scope="row"><?php _e('The plugin should:') ?> </th>
+<td>
+<label><input type="radio" name="convert_smileys" value="1" <?php if (kjgrc_get_option("misc","convert_smileys") == 1) echo 'checked="checked"' ?>> do it like <?php echo $converter ?>.</label>
+<br>
+<label><input type="radio" name="convert_smileys" value="0" <?php if (kjgrc_get_option("misc","convert_smileys") == 0) echo 'checked="checked"' ?>> never convert emoticons to graphics (even if <?php echo $converter ?> does it elsewhere).</label>
+</td>
+
+</tr>
+</table>
+</fieldset> 
+
+<fieldset class="options"> 
+<legend><?php _e('Cache') ?></legend> 
+If there are no new comments, the plugin fetches the output from the cache,
+instead of querying the database. If you want the plugin to ask the database
+every time, a web page is generated, you can disable this feature.
+
+<table class="optiontable"> 
+
+<th scope="row"><?php _e('The plugin should:') ?> </th>
+<td>
+<label><input type="checkbox" name="use_cache_checkbox" <?php if (kjgrc_use_cache()) echo 'checked="checked"'?>> cache the output (recommended).</label>
+</td>
+
+</tr>
+</table>
+</fieldset> 
+
+<p class="submit"><input type="submit" name="Submit" value="<?php _e('Update Options &raquo;') ?>" />
+<input type="hidden" name="action" value="update" /> 
+<input type="hidden" name="page_options" value="blogname,blogdescription,siteurl,admin_email,users_can_register,gmt_offset,date_format,time_format,home,start_of_week,comment_registration,default_role" /> 
+</p>
+</form>
+
+</div> 
+<?php
+}
+
+function kjgrc_subpage_gravatar() 
+{
+	$gravatar_checked[0] = '';
+	$gravatar_checked[1] = '';
+	$gravatar_checked[2] = '';
+	$gravatar_checked[3] = '';
+	$gravatar_checked[kjgrc_get_option('gravatar','rating')] = "checked=\"checked\" ";
+	
+?>
+<form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=5&amp;updated=true">
+<input type="hidden" name="function" value="gravatar">
+
+<h2>Settings for %gravatar</h2>
+<fieldset class="options">
+<table width="100%" cellspacing="2" cellpadding="5" class="editform">
+<tr valign="top">
+<th width="33%" scope="row"><?php _e('Size of Gravatars:') ?></th>
+<td nowrap><input name="gravatar_size" type="text" value="<?php echo kjgrc_get_option("gravatar","size"); ?>" size="3" /> <?php _e('Pixel') ?><br />
+Valid values are between 1 and 80 pixels.
+</td>
+</tr>
+<tr valign="top"> 
+        <th scope="row">Alternative URL:</th> 
+        <td><input name="gravatar_alt_url" type="text" style="width: 95%" value="<?php echo kjgrc_get_option("gravatar","alt_url"); ?>" size="45" />
+        <br />
+This is an <strong>optional</strong> image that will be displayed if no gravatar is found. Enter the full URL (with http://). If left empty, gravatar.com returns a transparent pixel.</td> 
+</tr> 
+<tr>
+        <th scope="row">Display gravatars up to this rating:</th> 
+        <td> <label for="gravatar_rating0"><input name="gravatar_rating" id="gravatar_rating0" type="radio" value="0" <?php echo $gravatar_checked[0]; ?>/> G (All audiences)</label><br />
+<label for="gravatar_rating1"><input name="gravatar_rating" id="gravatar_rating1" type="radio" value="1" <?php echo $gravatar_checked[1]; ?>/> PG</label><br />
+<label for="gravatar_rating2"><input name="gravatar_rating" id="gravatar_rating2" type="radio" value="2" <?php echo $gravatar_checked[2]; ?>/> R</label><br />
+<label for="gravatar_rating3"><input name="gravatar_rating" id="gravatar_rating3" type="radio" value="3" <?php echo $gravatar_checked[3]; ?>/> X</label></td> 
+</tr> 
+
+</table>
+
+<p class="submit">
+<input type="submit" name="Submit" value="<?php _e('Update Options') ?> &raquo;" />
+</p>
+</form> 
+<?php
+} // kjgrc_subpage_gravatar
+
+function kjgrc_subpage_exclude_cat() 
+{
+	global $wpdb;
+	if (function_exists("get_categories")) {
+		$categories = get_categories('&hide_empty=0');
+	} else {
+		// be still compatible to 2.0.11
+		$categories = $wpdb->get_results("SELECT * FROM $wpdb->categories ORDER BY cat_name");
+	}
+	$exclude_cat = kjgrc_get_exclude_cat();
+?>
+<form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=4&amp;updated=true">
+
+<input type="hidden" name="function" value="exclude_cat">
+<h2>Categories</h2>
+<label><input type="radio" name="exclude_categories_reverse" value="1" <?php if (kjgrc_get_option("misc","exclude_cat_reverse") == 1) echo 'checked="checked"' ?>>Show only comments to articles of the following categories:</label>
+<br>
+<label><input type="radio" name="exclude_categories_reverse" value="0" <?php if (kjgrc_get_option("misc","exclude_cat_reverse") == 0) echo 'checked="checked"' ?>>Show no comments to articles of the following categories:</label>
+<p>
+
+<?php
+
+	if ($categories) {
+		foreach ($categories as $category) {
+			$checked = '';
+			if ($exclude_cat && in_array($category->cat_ID,$exclude_cat)) {
+				$checked = 'checked="checked" ';
+			}
+			echo "<label for=\"\">\n";
+			echo "<input name=\"exclude_category[]\" type=\"checkbox\" value=\"$category->cat_ID\" $checked/>";
+			echo " $category->cat_name</label><br />\n";
+		}
+	}
+?>
+<p class="submit">
+<input type="submit" name="Submit" value="<?php _e('Update Options') ?> &raquo;" />
+</p>
+</form> 
+<?php
+} // kjgrc_subpage_exclude_cat
+
+function kjgrc_subpage_grc() 
+{
+?>
+<script type="text/javascript">
+<!--
+function toggle_grouped_titles()
+{
+        if (document.get_recent_comments_form.grouped_by_post_checkbox.checked == false) {
+		document.getElementById('grouped_by_post_cell_a').style.display = "none";
+		document.getElementById('grouped_by_post_cell_b').style.display = "none";
+        } else {
+		document.getElementById('grouped_by_post_cell_a').style.display = "";
+		document.getElementById('grouped_by_post_cell_b').style.display = "";
+        }
+}
+function toggle_exclude_blog_owner2()
+{
+	if (document.get_recent_comments_form.grc_exclude_blog_owner_checkbox.checked == false) {
+		document.getElementById('grc_exclude_blog_owner_checkbox2').style.display = "none";
+	} else {
+		document.getElementById('grc_exclude_blog_owner_checkbox2').style.display = "";
+	}
+}
+-->
+</script>
+
+<form name="get_recent_comments_form" method=post action="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;updated=true">
+<input type="hidden" name="function" value="grc">
+<h2><?php _e('Recent Comments') ?></h2>
+<fieldset class="options"> 
+<table width="100%" cellspacing="2" cellpadding="5" class="editform">
+<tr valign="top">
+<th width="33%" scope="row"><?php _e('Show the most recent:') ?></th>
+<td><input name="max_comments" type="text" id="max_comments" value="<?php echo kjgrc_get_option("grc","max_comments"); ?>" size="3" /> <?php _e('comments') ?></td>
+<td rowspan="6"><pre><div style='font-size: 10px; border-left: 1px solid; margin: 0px;'> %comment_excerpt - Shortened comment.
+ %comment_link    - Link to the comment. 
+ %comment_author  - Name left by the commenter
+ %comment_date    - Date of comment
+ %comment_time    - Time of comment
+ %comment_type    - Comment, Trackback or Pingback
+ %time_since      - Time since comment was posted
+ %userid          - UserID of the commenter
+ %gravatar        - Gravatar of the commenter, full img tag
+ %gravatar_url    - Gravatar of the commenter, only url
+ %profile_picture - URL of profile picture
+ %author_url      - URL of author or trackback
+ %author_url_href - href="%author_url" or empty
+ %post_title      - Title of the posting
+ %post_link       - Link to the posting 
+ %post_date       - Date of the posting
+ %post_counter    - Number of comments to this post</pre></div></td>
+</tr>
+<tr valign="top">
+<th width="33%" scope="row"><?php _e('Long comments are chopped off at:') ?></th>
+<td nowrap><input name="chars_per_comment" type="text" id="chars_per_comment" value="<?php echo kjgrc_get_option("grc","chars_per_comment"); ?>" size="3" /> <?php _e('characters') ?></td>
+</tr>
+<tr valign="top">
+<th width="33%" scope="row"><?php _e('Wrap long words at:') ?></th>
+<td nowrap><input name="chars_per_word" type="text" id="chars_per_word" value="<?php echo kjgrc_get_option("grc","chars_per_word"); ?>" size="3" /> <?php _e('characters') ?></td>
+</tr>
+<tr valign="top">
+<th width="33%" scope="row">Template:
+<td>&nbsp;</td>
+</tr>
+
+<tr>
+<td colspan=2>
+<label for="grc_exclude_blog_owner_checkbox">
+<input type="checkbox" name="grc_exclude_blog_owner_checkbox" id="grc_exclude_blog_owner_checkbox" onclick="toggle_exclude_blog_owner2();" <?php if (kjgrc_get_option("grc","exclude_blog_owner") == 1) echo "checked=\"checked\""; ?>> Exclude comments by blog authors (your own comments)</label>
+</td>
+</tr>
+
+<tr id="grc_exclude_blog_owner_checkbox2" style="display: <?php echo ((kjgrc_get_option("grc","exclude_blog_owner") == 0) ? "none" : "table-row") ?>;">
+<td colspan=2>
+<label for="grc_exclude_blog_owner2_checkbox">&nbsp;&nbsp;&nbsp;
+<input type="checkbox" name="grc_exclude_blog_owner2_checkbox" id="grc_exclude_blog_owner2_checkbox" <?php if (kjgrc_get_option("grc","exclude_blog_owner2") == 1) echo "checked=\"checked\""; ?>> Also consider usernames and e-mail addresses, to recognize blog authors</label>
+</td>
+</tr>
+
+
+<tr>
+<td colspan=2>
+<label for="grc_show_trackbacks_checkbox">
+<input type="checkbox" name="grc_show_trackbacks_checkbox" id="grc_show_trackbacks_checkbox" <?php if (kjgrc_get_option("grc","show_trackbacks") == 1) echo "checked=\"checked\""; ?>> Show Comments and Trackbacks/Pingbacks together</label>
+</td>
+</tr>
+</tr>
+<tr>
+<td colspan=2>
+<label for="grouped_by_post_checkbox">
+<input type="checkbox" name="grouped_by_post_checkbox" id="grouped_by_post_checkbox" onclick="toggle_grouped_titles();" <?php if (kjgrc_get_option("grc","grouped_by_post") == 1) echo "checked=\"checked\""; ?>> Group comments by Posting</label>
+</td>
+</tr>
+<tr id="grouped_by_post_cell_a" style="display: <?php echo ((kjgrc_get_option("grc","grouped_by_post") == 0) ? "none" : "table-row") ?>;">
+<td colspan=3>
+<label for="grc_limit_comments_per_post_checkbox">
+<input type="checkbox" name="grc_limit_comments_per_post_checkbox" id="grc_limit_comments_per_post_checkbox" onclick="toggle_grouped_titles();" <?php if (kjgrc_get_option("grc","limit_comments_per_post") == 1) echo "checked=\"checked\""; ?>> Limit number of comments per post: <!-- aka de klein limit --></label> <input type="text" name="grc_comments_per_post" size=3 value="<?php echo kjgrc_get_option("grc","comments_per_post");?>"><br /><br />
+
+<textarea name="grouped_by_post_a" cols="60" rows="2" id="grouped_by_post_a" style="width: 98%; font-size: 12px;" class="code"><?php echo stripslashes(htmlspecialchars(kjgrc_get_option("grc","grouped_by_post_a"))); ?></textarea><br /><span style="font-size: 10px;"><strong>Template for the post</strong>. It should start with &lt;li&gt; and end with &lt;ul&gt;<span>
+</td>
+</tr>
+
+<tr>
+<td colspan=3 style="padding-left: 30px;"><textarea name="format" cols="60" rows="2" id="format" style="width: 98%; font-size: 12px;" class="code"><?php echo stripslashes(htmlspecialchars(kjgrc_get_option("grc","format"))); ?></textarea><br /><span style="font-size: 10px;"><strong>Template for the comments</strong>. If you want them as a list, It should start with &lt;li&gt; and end with &lt;/li&gt;<span></td>
+</tr>
+<tr>
+
+<tr id="grouped_by_post_cell_b" style="display: <?php echo ((kjgrc_get_option("grc","grouped_by_post") == 0) ? "none" : "table-row") ?>;">
+<td colspan=3>
+
+<textarea name="grouped_by_post_b" cols="60" rows="2" id="grouped_by_post_b" style="width: 98%; font-size: 12px;" class="code"><?php echo stripslashes(htmlspecialchars(kjgrc_get_option("grc","grouped_by_post_b"))); ?></textarea><br /><span style="font-size: 10px;"><strong>Template for the closing tags of the post template</strong>. Usally &lt;/ul&gt;&lt;/li&gt;</span></td>
+
+</tr>
+
+<tr>
+<td colspan=3>
+<strong>Result</strong>
+<?php $result=kjgrc_create_recent_comments('grc_sample');  substr_count($result, "\n");?>
+<textarea cols="60" rows="<?php $result=kjgrc_create_recent_comments('grc_sample');  substr_count($result, "\n"); echo substr_count($result, "\n")+1;?>" style="width: 98%; font-size: 12px; left-margin: 30;" class="code" wrap="off" readonly><?php echo trim($result); ?></textarea>
+</td>         
+</tr>
+
+</table>
+<p class="submit">
+<input type="submit" id="deletepost" name="reset_template" value="<?php _e('Reset template to default') ?> &raquo;" onclick="return confirm('You are about to reset your template for \'Recent Comments\'.\n  \'Cancel\' to stop, \'OK\' to delete.')" />
+<input type="submit" name="Submit" value="<?php _e('Update Recent Comments Options') ?> &raquo;" />
+</p>
+</fieldset>
+</form>
+
+<?php
+} // kjgrc_subpage_grc 
+
+function kjgrc_subpage_grt () 
+{
+?>
+
+<form name="trackback_form" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;updated=true&amp;subpage=2">
+<input type="hidden" name="function" value="grt">
+<h2><?php _e('Recent Trackbacks') ?></h2>
+<fieldset class="options"> 
+<table width="100%" cellspacing="2" cellpadding="5" class="editform">
+<tr valign="top">
+<th width="33%" scope="row"><?php _e('Show the most recent:') ?></th>
+<td nowrap><input name="max_comments" type="text" id="max_comments" value="<?php echo kjgrc_get_option("grt","max_comments"); ?>" size="3" /> <?php _e('Trackbacks') ?></td>
+<td rowspan="3"><pre><div style='font-size: 10px; border-left: 1px solid; margin: 0px;'> %comment_excerpt - Shortened comment.
+ %comment_link    - Link to the comment.
+ %comment_author  - Name left by the commenter
+ %comment_date    - Date of comment
+ %comment_time    - Time of comment
+ %comment_type    - Pingback or Trackback
+ %time_since      - Time since trackback was posted
+ %author_url      - URL of author or trackback
+ %author_url_href - href="%author_url" or empty
+ %trackback_title - Title of trackback
+ %post_title      - Title of the posting
+ %post_link       - Link to the posting
+ %post_date       - Date of the posting</pre></div></td>
+</tr>
+<tr valign="top">
+<th width="33%" scope="row"><?php _e('Long trackbacks are chopped off at:') ?></th>
+<td nowrap><input name="chars_per_comment" type="text" id="chars_per_comment" value="<?php echo kjgrc_get_option("grt","chars_per_comment"); ?>" size="3" /> <?php _e('characters') ?></td>
+</tr>
+<tr valign="top">
+<th width="33%" scope="row"><?php _e('Wrap long words at:') ?></th>
+<td><input name="chars_per_word" type="text" id="chars_per_word" value="<?php echo kjgrc_get_option("grt","chars_per_word"); ?>" size="3" /> <?php _e('characters') ?></td>
+</tr>
+<tr valign="top">
+<th width="33%" scope="row"><?php _e('Ignore trackbacks originating from this ip address:') ?></th>
+<td><input name="ignore_ip" type="text" id="ignore_ip" value="<?php echo kjgrc_get_option("grt","ignore_ip"); ?>" size="16" /><br><span style='font-size: 10px;'>Insert the <a href="javascript:;" onmousedown="document.trackback_form.ignore_ip.value='<?php global $_SERVER; echo $_SERVER['SERVER_ADDR']; ?>';">address of your webserver</a> to filter pingbacks from your own posts</span></td>
+</tr>
+<tr valign="top">
+<th width="33%" scope="row">Template:
+<td>&nbsp;</td>
+</tr>
+<tr valign="top">
+<td colspan="3">
+       <textarea name="format" cols="60" rows="2" id="format" style="width: 98%; font-size: 12px;" class="code"><?php echo stripslashes(htmlspecialchars(kjgrc_get_option("grt","format"))); ?></textarea><br /><span style="font-size: 10px;"><strong>Template for the trackbacks and pingbacks.</strong> Usually starts with &lt;li&gt; and ends with &lt;/li&gt;.</span>
+</td>
+</tr>
+
+<tr>
+<td colspan=3>
+<strong>Result</strong>
+<?php $result=kjgrc_create_recent_comments('grc_sample');  substr_count($result, "\n");?>
+<textarea cols="60" rows="<?php $result=kjgrc_create_recent_trackbacks('grt_sample');  substr_count($result, "\n"); echo substr_count($result, "\n")+1;?>" style="width: 98%; font-size: 12px; left-margin: 30;" class="code" wrap="off" readonly><?php echo trim($result); ?></textarea>
+</td>         
+</tr>
+
+</table>
+
+
+<p class="submit">
+<input type="submit" id="deletepost" name="reset_template" value="<?php _e('Reset template to default') ?> &raquo;" onclick="return confirm('You are about to reset your template for \'Recent Trackbacks\'.\n  \'Cancel\' to stop, \'OK\' to delete.')" />
+<input type="submit" name="Submit" value="<?php _e('Update Recent Trackbacks Options') ?> &raquo;" />
+</p>
+</form>   
+</fieldset>
+
+<?php 
+} //kjgrc_subpage_grt
+
+function kjgrc_subpage_instructions () 
+{
+?>   
+<h2><?php _e('Instructions') ?></h2>
+<p><strong>1. What this plugin does</strong></p>
+It shows excerpts of the latest comments and/or trackbacks in your sidebar. You
+have comprehensive control about their appearance. This ranges from the number
+of comments, the length of the excerpts up to the html layout. You can let the
+plugin order the comments by the corresponding post, or simply order them by
+date. The plugin can (optionally) separate the trackbacks/pingbacks from the
+comments. It can ignore comments to certain categories, and it offers support
+for gravatars. It only gives extra work to the database, when actually a new
+comment arrived. And you can filter out unwanted pingbacks, which originate
+from your own blog. And it is a widget.
+
+<p><strong>2. Installation</strong></p>
+Since you are reading this text, you already uploaded and activated the plugin.
+Now you want to add the plugin to your theme. There are two options to do this:
+
+<p><strong>2.1 Modern Theme with widget support</strong></p>
+The plugin is a <a href="http://automattic.com/code/widgets/">widget</a>. If
+your theme supports widgets, and you have installed the widget plugin, adding
+the plugin to the sidebar is easy: Go to the <a href="themes.php">presentation menu</a> and drag
+and drop the widget into the sidebar. Don't forget the Get Recent Trackbacks
+box. And you might want to change the title. All done.
+
+<p><strong>2.2 Old school theme without widget support</strong></p>
+
+<p>
+You need to insert the following code snippet into the <a href="theme-editor.php">sidebar template</a>. 
+</p>
+
+<span class="code">wp-content/themes/default/sidebar.php</span>
+<div style="border: 1px solid; border-color: #ccc; margin: 15px; background: #eee;">
+
+<pre class="code" style='color:#000000;'><span style='color:#7f0055; background:#ffffe8; '>&lt;?php</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; font-weight:bold; '>if</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#000000; background:#ffffe8; '>(</span><span style='color:#7f0055; background:#ffffe8; font-weight:bold; '>function_exists</span><span style='color:#000000; background:#ffffe8; '>(</span><span style='color:#2a00ff; background:#ffffe8; '>'get_recent_comments'</span><span style='color:#000000; background:#ffffe8; '>)</span><span style='color:#000000; background:#ffffe8; '>)</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#000000; background:#ffffe8; '>{</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; '>?></span><span style='color:#000000;  '></span>
+<span style='color:#000000;  '>   </span><span style='color:#7f0055;  '>&lt;</span><span style='color:#7f0055;  font-weight:bold; '>li</span><span style='color:#7f0055;  '>></span><span style='color:#7f0055;  '>&lt;</span><span style='color:#7f0055;  font-weight:bold; '>h2</span><span style='color:#7f0055;  '>></span><span style='color:#7f0055; background:#ffffe8; '>&lt;?php</span><span style='color:#000000; background:#ffffe8; '> _e</span><span style='color:#000000; background:#ffffe8; '>(</span><span style='color:#2a00ff; background:#ffffe8; '>'Recent Comments:'</span><span style='color:#000000; background:#ffffe8; '>)</span><span style='color:#000000; background:#ffffe8; '>;</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; '>?></span><span style='color:#7f0055;'>&lt;/</span><span style='color:#7f0055;  font-weight:bold; '>h2</span><span style='color:#7f0055;  '>></span><span style='color:#000000;  '></span>
+<span style='color:#000000;  '>   </span><span style='color:#7f0055;  '>&lt;</span><span style='color:#7f0055;  font-weight:bold; '>ul</span><span style='color:#7f0055;  '>></span><span style='color:#7f0055; background:#ffffe8; '>&lt;?php</span><span style='color:#000000; background:#ffffe8; '> get_recent_comments</span><span style='color:#000000; background:#ffffe8; '>(</span><span style='color:#000000; background:#ffffe8; '>)</span><span style='color:#000000; background:#ffffe8; '>;</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; '>?></span><span style='color:#7f0055;  '>&lt;/</span><span style='color:#7f0055;  font-weight:bold; '>ul</span><span style='color:#7f0055;  '>></span><span style='color:#000000;  '></span>
+<span style='color:#000000;  '>   </span><span style='color:#7f0055;  '>&lt;/</span><span style='color:#7f0055;  font-weight:bold; '>li</span><span style='color:#7f0055;  '>></span><span style='color:#000000;  '></span>
+<span style='color:#7f0055; background:#ffffe8; '>&lt;?php</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#000000; background:#ffffe8; '>}</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; '>?></span>   
+
+<span style='color:#7f0055; background:#ffffe8; '>&lt;?php</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; font-weight:bold; '>if</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#000000; background:#ffffe8; '>(</span><span style='color:#7f0055; background:#ffffe8; font-weight:bold; '>function_exists</span><span style='color:#000000; background:#ffffe8; '>(</span><span style='color:#2a00ff; background:#ffffe8; '>'get_recent_trackbacks'</span><span style='color:#000000; background:#ffffe8; '>)</span><span style='color:#000000; background:#ffffe8; '>)</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#000000; background:#ffffe8; '>{</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; '>?></span><span style='color:#000000;  '></span>
+<span style='color:#000000;  '>   </span><span style='color:#7f0055;  '>&lt;</span><span style='color:#7f0055;  font-weight:bold; '>li</span><span style='color:#7f0055;  '>></span><span style='color:#7f0055;  '>&lt;</span><span style='color:#7f0055;  font-weight:bold; '>h2</span><span style='color:#7f0055;  '>></span><span style='color:#7f0055; background:#ffffe8; '>&lt;?php</span><span style='color:#000000; background:#ffffe8; '> _e</span><span style='color:#000000; background:#ffffe8; '>(</span><span style='color:#2a00ff; background:#ffffe8; '>'Recent Trackbacks:'</span><span style='color:#000000; background:#ffffe8; '>)</span><span style='color:#000000; background:#ffffe8; '>;</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; '>?></span><span style='color:#7f0055;  '>&lt;/</span><span style='color:#7f0055;  font-weight:bold; '>h2</span><span style='color:#7f0055;  '>></span><span style='color:#000000;  '></span>
+<span style='color:#000000;  '>   </span><span style='color:#7f0055;  '>&lt;</span><span style='color:#7f0055;  font-weight:bold; '>ul</span><span style='color:#7f0055;  '>></span><span style='color:#7f0055; background:#ffffe8; '>&lt;?php</span><span style='color:#000000; background:#ffffe8; '> get_recent_trackbacks</span><span style='color:#000000; background:#ffffe8; '>(</span><span style='color:#000000; background:#ffffe8; '>)</span><span style='color:#000000; background:#ffffe8; '>;</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; '>?></span><span style='color:#7f0055;  '>&lt;/</span><span style='color:#7f0055;  font-weight:bold; '>ul</span><span style='color:#7f0055;  '>></span><span style='color:#000000;  '></span>
+<span style='color:#000000;  '>   </span><span style='color:#7f0055;  '>&lt;/</span><span style='color:#7f0055;  font-weight:bold; '>li</span><span style='color:#7f0055;  '>></span><span style='color:#000000;  '></span>
+<span style='color:#7f0055; background:#ffffe8; '>&lt;?php</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#000000; background:#ffffe8; '>}</span><span style='color:#000000; background:#ffffe8; '> </span><span style='color:#7f0055; background:#ffffe8; '>?></span>
+</pre>
+
+</div>
+
+<p><strong>3. Customizing</strong></p>
+The strongest feature of the plugin is that you can change the html layout in the admin interface, by filling in templates. In the templates you make use of macros, that are later replaced by the actual data.
+
+<p><strong>3.1 Using the Macros</strong></p>
+<table>
+<tr><td valign="top" nowrap>%comment_excerpt</td><td>The text of the comment. It might get shorted to the number of characters you entered in <em>"Long comments are chopped off at..."</em></td></tr>
+<tr><td valign="top" nowrap>%comment_link</td><td>The URL to the cited comment.</td></tr> 
+<tr><td valign="top" nowrap>%comment_author</td><td>The name, the commenter entered in the comment form. If she left the field empty, the name is "Anonymous".</td></tr>
+<tr><td valign="top" nowrap>%comment_date</td><td>The date, when the comment was posted in the style you configured as <a href="options-general.php">default date format</a>.</td></tr>
+<tr><td valign="top" nowrap>%comment_time</td><td>The time, when the comment was posted</td></tr>
+<tr><td valign="top" nowrap>%time_since</td><td>Time since the comment was posted. For example: &quot;9 hours 16 minutes&quot;.</td></tr>
+<tr><td valign="top" nowrap>%comment_type</td><td>&quot;Comment&quot;, &quot;Trackback&quot; or &quot;Pingback&quot;.</td></tr>
+<tr><td valign="top" nowrap>%gravatar</td><td>This macro becomes a complete image tag. If the comment author registered a gravatar with <a href="http://www.gravatar.com">gravatar.com</a>. Example:<br />&lt;img src=&quot;http://www.gravatar.com/avatar.php?gravatar_id=1ebbd34d4e45cac&amp;size=25&amp;rating=X&quot;/&gt;  </td></tr>
+<tr><td valign="top" nowrap>%gravatar_url</td><td>This macro becomes only the URL to the gravatar. Example:<br />http://www.gravatar.com/avatar.php?gravatar_id=1ebbd34d4e45cac&amp;size=25&amp;rating=X</td></tr>
+<tr><td valign="top" nowrap>%profile_picture</td><td>Becomes to URL of a profile picture. Use it like this: &lt;img src="%profile_picture" width=16 height=16&gt;. This only works after activation of <a href="http://geekgrl.net">Hannah Gray's</a> <a href="http://geekgrl.net/2007/01/02/profile-pics-plugin-release/">Profile Pics Plugin</a>.</td></tr>
+<tr><td valign="top" nowrap>%userid</td><td>If the comment author is registered with your wordpress, and was logged in, when she wrote the comment this is replaced with the user id, she has in WordPress. The user id's are listed here: <a href="users.php">users.php</a>. You can do fancyful things with this macro. For example you may construct an image url, that points to pictures of all the authors of your blog: &lt;img src=&quot;/images/user%userid.jpg&quot;&gt;</td></tr>
+<tr><td valign="top" nowrap>%author_url</td><td>The URL, the comment author left in the comment form, or if the comment is a trackback, the URL of the site that issued the trackback.</td></tr>
+<tr><td valign="top" nowrap>%author_url_href</td><td>The same like %author_url but inside a href statement. If there is no URL, the whole macro is empty. Without a href="" statement the link looks like a link, but is inactive. Use it like this: &lt;a %author_url_href title="%comment_date"&gt;comment_author&lt;/a&gt;</td></tr>
+<tr><td valign="top" nowrap>%post_title</td><td>The title of the posting that was commented.</td></tr>
+<tr><td valign="top" nowrap>%post_link</td><td>The URL of the posting that was commented.</td></tr> 
+<tr><td valign="top" nowrap>%post_date</td><td>The date when the commented posting was published.</td></tr>
+<tr><td valign="top" nowrap>%post_counter</td><td>The number of comments this post has..</td></tr>
+<tr><td valign="top" nowrap>%trackback_title</td><td>Only applicable in trackbacks: The title of the trackback. It  might get shorted to the number of characters you entered in <em>"Long trackbacks are chopped off at..."</em></td></tr>
+</table>
+
+<p><strong>3.2 Group by comments</strong></p>
+
+If you want to order the comments by their posting, you will have not one but
+three templates. The middle one is just the same as in the normal order. The
+first and the last template are used to generate headers for the grouped
+comments. Usually you will show the %post_title in this header. This is the
+html markup that is generated by the templates:
+
+<style type="text/css">
+<!--
+.top-example { vertical-align:top; }
+.top-ital { vertical-align:top; font-style:italic; padding-left: 10px; }
+#example { font-family:monospace; 
+           font-weight:bold; 
+           border: 1px solid;
+           border-color: #ccc;
+           margin: 15px;
+           background: #eee;
+}	
+#example pre {
+	margin: 0;
+}
+#example td {
+	
+}
+-->
+</style>
+<div id="example">
+<table style="border-spacing:0px; ">
+<tr>
+<td class="top-example"><pre><span style="color:green">&lt;li&gt;&lt;h2&gt;Recent Comments:&lt;/h2&gt;
+   &lt;ul&gt;</pre></span></td>
+<td class="top-ital"><span style="color:green">Start sequence.</span> Automatically provided when the plugin is used as widget. Otherwise you have to enter this by manually into the <a href="theme-editor.php">sidebar.php template</a>.</td>
+</tr>
+<tr>
+<td class="top-example"><pre>
+      <span style="color:blue">&lt;li&gt;Post 1&lt;/li&gt;
+         &lt;ul&gt;
+</pre></span></td>
+<td class="top-ital"><span style="color:blue">Template for the post. This is the header for a list of comments, that all belong to the same posting.</span></td>
+</tr>
+<tr>
+<td class="top-example"><pre>
+            <span style="color:olive">&lt;li&gt;Comment1 to Post1&lt;/li&gt;
+            &lt;li&gt;Comment2 to Post1&lt;/li&gt;
+</pre></span></td>
+<td class="top-ital"><span style="color:olive">Template for the comments<br />It is repeated for every single comment</span></td>
+</tr>
+<tr class="top-example">
+<td class="top-example"><pre>
+         <span style="color:red">&lt;/ul&gt;
+      &lt;/li&gt;
+</pre></span></td>
+<td class="top-ital"><span style="color:red">Template for the closing tags of the post template</span></td>
+</tr>
+<tr class="top-example">
+<td class="top-example"><pre>
+      <span style="color:blue">&lt;li&gt;Post 2&lt;/li&gt;
+         &lt;ul&gt;
+</pre></span></td>
+<td class="top-ital"><span style="color:blue">The next posting.</span></td>
+</tr>
+<tr class="top-example">
+<td class="top-example"><pre>
+            <span style="color:olive">&lt;li&gt;Comment1 to Post2&lt;/li&gt;
+            &lt;li&gt;Comment2 to Post2&lt;/li&gt;
+</pre></span></td>
+<td class="top-ital"><span style="color:olive">The comments to the next posting</span></td>
+</tr>
+<tr class="top-example">
+<td class="top-example"><pre>
+         <span style="color:red">&lt;/ul&gt;
+      &lt;/li&gt;
+</pre></span></td>
+<td class="top-ital"><span style="color:red">Again close the tags</span></td>
+</tr>
+<tr class="top-example">
+<td class="top-example"><pre>
+   <span style="color:green">&lt;/ul&gt;
+&lt;/li&gt;
+</pre></span></td>
+<td class="top-ital"><span style="color:green">End sequence.</span> Automatically provided when the plugin is used as widget. Otherwise you have to enter this by manually into the <a href="theme-editor.php">sidebar.php template</a>.</td>
+</tr>
+</table>
+</div>
+
+
+<p><strong>4. Miscellaneous</strong></p>
+<ul>
+<li>Don't worry if you screwed up the template, reset the template to default and try again.</li>
+<li><em>"Wrap long words at..."</em> means: words, that exceed this length are split into fragments to prevent damage to the layout of your blog.</li>
+<li>"<em>Ignore trackbacks originating from this ip address</em>" on the
+configuration page for recent trackbacks is useful for filtering out pingbacks
+that occur when you have a link to your own site in a post.</li>
+<li>If you select to exclude comments made by blog authors (your own comments) from  the list, then the comments which you made, when you were logged in, are ignored. If you also want to exclude comments you made, when you were not logged in, you can select to also consider the username and e-mail address.</li>
+</ul>
+<p><strong>5. Interoperation with other plugins</strong></p>
+<ul>
+<li>If <a href="http://geekgrl.net">Hannah Gray's</a> <a href="http://geekgrl.net/2007/01/02/profile-pics-plugin-release/
+">Profile Pics Plugin</a> is present, you may use to macro <em>%profile_picture</em> to obtain an URL to the profile picture of the commentator (read in the macro section for a working example).</li>
+<li>If <a href="http://fredfred.net/skriker/index.php/polyglot">malyfred's</a> <a href="http://fredfred.net/skriker/index.php/polyglot">Polyglot</a> Plugin is present, additional filters are applied to comments, titles, dates and times, which select the right language for the user.</li>
+</ul>
+</div>  
+
+<?php  
+}
+
+function kjgrc_subpage_header ($kjgrc_selected_tab) {
+	$current_tab[$kjgrc_selected_tab] = "class=\"current\"";
+?>
+<style>
+<!--
+#adminmenu3 li {
+        display: inline;
+        line-height: 200%;
+        list-style: none;
+        text-align: center;
+}
+
+#adminmenu3 {
+        background: #0d324f;
+        border-bottom: none;
+        margin: 0;
+	height: 25px;
+        padding: 3px 2em 0 1em;
+	paddi/ng: 0 0 0 0;
+}
+                                                                                     
+#adminmenu3 .current {
+        background: #f9fcfe;
+        border-top: 1px solid #045290;
+        border-right: 2px solid #045290;
+        color: #000;
+}
+                                                                                     
+#adminmenu3 a {
+        border: none;
+        color: #fff;
+        font-size: 12px;
+        padding: .3em .4em .33em;
+}
+                                                                                     
+#adminmenu3 a:hover {
+        background: #ddeaf4;
+        color: #393939;
+}
+                                                                                     
+-->
+</style>
+<ul id="submenu">
+   <li><a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=1" <?php echo $current_tab[1] ?>>Comments</a></li>
+   <li><a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=2" <?php echo $current_tab[2] ?>>Trackbacks</a></li>
+   <li><a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=4" <?php echo $current_tab[4] ?>>Categories</a></li>
+   <li><a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=5" <?php echo $current_tab[5] ?>>Gravatars</a></li>
+   <li><a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=6" <?php echo $current_tab[6] ?>>Misc</a></li>
+   <li><a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=3" <?php echo $current_tab[3] ?>>Instructions</a></li>
+</ul>
+<div class="wrap">
+<?php
+}
+
+// function kjgrc_log ($msg="")
+// {
+// 	$handle = @fopen ("/home/krischan/debug.log", "a");
+// 	if (is_writable("/home/krischan/debug.log")) {
+// 	  	fwrite($handle,date("r").": $msg\n");
+// 		fclose($handle);
+// 	}
+// }	
+
+function kjgrc_set_option($key,$value) 
+{
+	global $kjgrc_options_loaded;
+	// Fetch dummy value just to enforce migration if needed
+	kjgrc_get_option("grc","max_comments");
+	$options = get_option("kjgrc_options");
+	$options[$key] = $value;
+	update_option("kjgrc_options",$options);
+	kjgrc_invalidate_cache();
+}
+
+function kjgrc_get_option($section,$option_name)
+{
+	global $wpdb,$kjgrc_options_loaded;
+	$version = 11; // If version differs from saved value -> insert new defaults
+	$kjgrc_options = get_option("kjgrc_options");
+	if ($kjgrc_options == NULL || $kjgrc_options['version'] != $version) 
+	{
+		$default_options = array (
+			"grc_max_comments" => 5,
+			"grc_chars_per_comment" => 120,
+			"grc_chars_per_word" => 30,
+			"grc_format" => "<li><a href=\"%comment_link\" title=\"%post_title, %post_date\">%comment_author</a>: %comment_excerpt</li>",
+			"grc_grouped_by_post" => 0,
+			"grc_grouped_by_post_a" => '<li><strong><a href="%post_link" title="%post_title was posted on %post_date">%post_title</a>&nbsp;(<a href="#" title="There are %post_counter comments to this posting">%post_counter</a>)</strong><ul>',
+			"grc_grouped_by_post_b" => '</ul></li>',
+			"grc_show_trackbacks" => 0,
+			"grc_comments_per_post" => 5,
+			"grc_limit_comments_per_post" => 0,
+			"grc_exclude_blog_owner" => 0,
+			"grc_exclude_blog_owner2" => 0,
+			"grt_max_comments" => 5,
+			"grt_chars_per_comment" => 120,
+			"grt_chars_per_word" => 30,
+			"grt_format" => "<li><a href=\"%comment_link\" title=\"Trackback to &quot;%post_title&quot;: %comment_excerpt\">%comment_author</a>: %trackback_title</li>",
+			"misc_exclude_cat" => "",
+			"misc_exclude_cat_reverse" => 0,
+			"misc_convert_smileys" => 1,
+			"gravatar_size" => 20,
+			"gravatar_alt_url" => "",
+			"grt_ignore_ip" => "",
+			"gravatar_rating" => 0,
+			"grc_sidebar_title" => "Recent Comments",
+			"grt_sidebar_title" => "Recent Trackbacks"
+		);
+		$old_keys = array("grc_max_comments","grc_chars_per_comment","grc_chars_per_word","grc_format","grt_max_comments","grt_chars_per_comment","grt_chars_per_word","grt_format","misc_exclude_cat","gravatar_size","gravatar_alt_url","gravatar_rating","grt_ignore_ip"); 
+		// This might be an upgrade from < 1.4 to 1.4 or newer version. If there are old keys, migrate them to the new array:
+		add_option('gravatar_alt_url','');
+		if ($kjgrc_options == NULL) {
+			foreach ($old_keys as $key) {
+				$sql = "SELECT option_value,option_id FROM $wpdb->options WHERE option_name = 'kjgrc_$key'";
+				$old_value = $wpdb->get_row($sql,ARRAY_N); // $old_value[0] = old value, $old_value[1] = option id
+				if ($old_value[1] != NULL) { 
+					$kjgrc_options[$key] = $old_value[0];
+					delete_option("kjgrc_$key");
+				} 
+			}
+		}
+		// Whether it's a new installation or an introduction of new options: Fill in default values
+		foreach (array_keys($default_options) as $key) {
+			if (($kjgrc_options == NULL) || ! array_key_exists($key,$kjgrc_options) ) {
+				$kjgrc_options[$key] = $default_options[$key];
+			}
+		}
+		// Delete keys that are not in use any more
+		foreach (array_keys($default_options) as $key) {
+			$tmp[$key] = $kjgrc_options[$key];
+		}
+		$kjgrc_options = $tmp;
+		$kjgrc_options["version"] = $version;
+		add_option("kjgrc_options",$options,$description = '', $autoload = 'no');
+		update_option('kjgrc_options',$kjgrc_options);
+	}
+	return $kjgrc_options[$section . "_" . "$option_name"];
+}
+
+function kjgrc_use_cache()
+{
+	add_option('kjgrc_use_cache',1);
+	return get_option("kjgrc_use_cache");
+}
+
+function kjgrc_options_page ()
+{
+?>
+<?php
+	$function = $_POST['function'];
+	if (isset($_GET['updated']) && ($_GET['updated'] == 'true') && 
+		(!empty($_POST['max_comments'])) && 
+		(!empty($_POST['chars_per_comment'])) &&         
+		(!empty($_POST['chars_per_word'])) && 
+		(!empty($_POST['function'])) )
+		// todo: check new params
+	{
+		if (($function == 'grc') ||
+		    ($function == 'grt'))
+		{
+			if (!empty($_POST['reset_template'])) {
+				if ($function == 'grc') {
+					kjgrc_set_option('grc_format','<li><a href="%comment_link" title="%post_title, %post_date">%comment_author</a>: %comment_excerpt</li>');
+					kjgrc_set_option('grc_grouped_by_post_a','<li><strong><a href="%post_link" title="%post_title was posted on %post_date">%post_title</a> (<a href="#" title="There are %post_counter comments to this posting">%post_counter</a>)</strong><ul>');
+					kjgrc_set_option('grc_grouped_by_post_b','</ul></li>');
+				}
+				if ($function == 'grt') {
+					kjgrc_set_option('grt_format','<li><a href="%comment_link" title="Trackback to &quot;%post_title&quot;: %comment_excerpt">%comment_author</a>: %trackback_title</li>');
+				}
+				//delete_option('kjgrc_'.$function.'_format');
+			} else {
+				kjgrc_set_option($function.'_max_comments', (int)$_POST['max_comments']);
+				kjgrc_set_option($function.'_chars_per_comment', (int)$_POST['chars_per_comment']);
+				kjgrc_set_option($function.'_chars_per_word', (int)$_POST['chars_per_word']);
+				kjgrc_set_option($function.'_format', $_POST['format']);
+				kjgrc_set_option($function.'_grouped_by_post_a', $_POST['grouped_by_post_a']);
+				kjgrc_set_option($function.'_grouped_by_post_b', $_POST['grouped_by_post_b']);
+			}
+		}
+		if ($function == 'grc') {
+
+			if ($_POST['grc_exclude_blog_owner_checkbox'] == 'on') 
+				kjgrc_set_option('grc_exclude_blog_owner',1);
+			else 
+				kjgrc_set_option('grc_exclude_blog_owner',0);
+			if ($_POST['grc_exclude_blog_owner2_checkbox'] == 'on') 
+				kjgrc_set_option('grc_exclude_blog_owner2',1);
+			else
+				kjgrc_set_option('grc_exclude_blog_owner2',0);
+			if ($_POST['grouped_by_post_checkbox'] == 'on') {
+				kjgrc_set_option('grc_grouped_by_post',1);
+			} else {
+				kjgrc_set_option('grc_grouped_by_post',0);
+			}
+			if ($_POST['grc_show_trackbacks_checkbox'] == 'on') {
+				kjgrc_set_option('grc_show_trackbacks',1);
+			} else {
+				kjgrc_set_option('grc_show_trackbacks',0);
+			}
+			if (! empty($_POST['grc_comments_per_post'])) {
+				kjgrc_set_option('grc_comments_per_post',(int)$_POST['grc_comments_per_post']);
+				if (kjgrc_get_option("grc","comments_per_post") == 0)
+					kjgrc_set_option("grc_comments_per_post",1);
+			}
+			if ($_POST['grc_limit_comments_per_post_checkbox'] == 'on') {
+				kjgrc_set_option('grc_limit_comments_per_post',1);
+			} else {
+				kjgrc_set_option('grc_limit_comments_per_post',0);
+			}
+		}
+	}
+	if (isset($_GET['updated']) && ($_GET['updated'] == 'true'))
+	{
+	   	if ($function == 'exclude_cat') 
+        	{
+			if (count($_POST['exclude_category']) == 0) {
+				kjgrc_set_option('misc_exclude_cat','');
+			} else {
+				kjgrc_set_option('misc_exclude_cat',implode(" ",$_POST['exclude_category']));
+			}
+			if (isset($_POST['exclude_categories_reverse'])) {
+				if ($_POST['exclude_categories_reverse'] == 0 || $_POST['exclude_categories_reverse'] == 1) {
+					kjgrc_set_option("misc_exclude_cat_reverse",$_POST['exclude_categories_reverse']);
+				}
+			}
+        	}
+		if ($function == 'gravatar') {
+			if ($_POST['gravatar_size'] > 0 && $_POST['gravatar_size'] < 81) {
+				kjgrc_set_option('gravatar_size',$_POST['gravatar_size']);
+			}
+			kjgrc_set_option('gravatar_alt_url',$_POST['gravatar_alt_url']);
+			kjgrc_set_option('gravatar_rating',$_POST['gravatar_rating']);
+		}
+		if ($function == 'grt' && isset($_POST['ignore_ip']))
+		{
+			kjgrc_set_option('grt_ignore_ip',trim($_POST['ignore_ip']));
+		}
+		if ($function == 'misc' && isset($_POST['convert_smileys'])) {
+			if ($_POST['convert_smileys'] == 0 || $_POST['convert_smileys'] == 1) {
+				kjgrc_set_option("misc_convert_smileys",$_POST['convert_smileys']);
+			}
+		}
+		if ($function == 'misc') {
+			if ($_POST['use_cache_checkbox'] == 'on') {
+				update_option("kjgrc_use_cache",1);
+			} else {
+				update_option("kjgrc_use_cache",0);
+			}
+		}
+	}
+	$kjgrc_subpage = 1;
+	if (isset($_GET['subpage'])) {
+		$kjgrc_subpage = $_GET['subpage'];
+	}
+	kjgrc_subpage_header($kjgrc_subpage);
+	if ($kjgrc_subpage == 1) {
+		kjgrc_subpage_grc(); 
+	} elseif ($kjgrc_subpage == 2) {
+		kjgrc_subpage_grt();
+	} elseif ($kjgrc_subpage == 3) {
+		kjgrc_subpage_instructions();
+	} elseif ($kjgrc_subpage == 4) {
+		kjgrc_subpage_exclude_cat();
+	} elseif ($kjgrc_subpage == 5) {
+		kjgrc_subpage_gravatar();
+	} elseif ($kjgrc_subpage == 6) {
+		kjgrc_subpage_misc();
+	}
+}
+
+function kjgrc_add_options_page() 
+{
+	add_options_page('Get Recent Comments Plugin', 'Recent Comments', 8, 'get-recent-comments.php','kjgrc_options_page');
+}
+
+function kjgrc_get_exclude_cat ()
+{
+	$exclude_cat = kjgrc_get_option('misc','exclude_cat');
+	if ($exclude_cat == '') {
+		return FALSE;
+	}
+	#echo "cats: '". kjgrc_get_option('misc','exclude_cat') ."' ";
+	return explode(" ",kjgrc_get_option('misc','exclude_cat'));
+}
+
+#function kjgrc_suicide ()
+#{
+#	$plugins = get_settings('active_plugins');
+#	if (in_array('get-recent-comments.php',$plugins)) {
+#		unset($plugins['get-recent-comments.php']);
+#		update_option('active_plugins',$plugins);
+#	}
+#	delete_option('kjgrc_options');
+#	kjgrc_get_option('grc','max_comments');
+#	delete_option('kjgrc_options');
+#	delete_option('kjgrc_cache');
+#	echo 'you are now dead';
+#}
+
+function get_recent_comments()
+{
+	echo kjgrc_cache("comments");
+}
+
+function get_recent_trackbacks()
+{
+	echo kjgrc_cache("trackbacks");
+}
+
+function kjgrc_invalidate_cache() {
+	delete_option('kjgrc_cache');
+}
+function kjgrc_handle_new_comment($args) {
+	if (wp_get_comment_status($args) == 'approved') {
+		kjgrc_invalidate_cache();
+	}
+}
+
+function kjgrc_prepare_cache($cache)
+{
+	// return $cache;
+	$last = 0;
+	$start = strpos($cache,'<%time_since:');
+	if ($start === false) {
+		return $cache;
+	}
+	while ($start !== false)
+	{
+		//kjgrc_log("start: $start");
+		$end = strpos($cache,'>',$start);
+		//kjgrc_log("end $end");
+		//kjgrc_log("cut $last - $start");
+		$newcache = $newcache . substr($cache,$last,$start-$last);
+		$tmp = substr($cache,$start+13,$end-$start-13);
+		$ago = gmdate('U')-$tmp;
+		//kjgrc_log("--> $tmp <--");
+		$newcache = $newcache . kjgrc_format_seconds($ago);
+		#kjgrc_log("$newcache");
+		$last = $end + 1;
+		$start = strpos($cache,'<%time_since:',$last);
+	}
+	$newcache = $newcache . substr($cache,$last);
+	return $newcache;	
+}
+
+function kjgrc_cache($type)
+{
+	global $wpdb,$kjgrc_cache;
+	$use_cache = FALSE;
+	if (kjgrc_use_cache()) {
+		$use_cache = TRUE;
+	}
+	if ($use_cache == FALSE) {
+		// kjgrc_log("return cache without caching");
+		$kjgrc_cache[comments] = kjgrc_prepare_cache(kjgrc_create_recent_comments());
+		$kjgrc_cache[trackbacks] = kjgrc_prepare_cache(kjgrc_create_recent_trackbacks());
+		return $kjgrc_cache[$type];
+	}
+	if ($kjgrc_cache == NULL) {
+		// kjgrc_log("return cache WITH caching");
+		$kjgrc_cache = get_option('kjgrc_cache');
+		if ($kjgrc_cache == NULL) {
+			// kjgrc_log("write new cache");
+			$kjgrc_cache[comments] =  base64_encode(kjgrc_create_recent_comments());
+			$kjgrc_cache[trackbacks] = base64_encode(kjgrc_create_recent_trackbacks());
+			update_option('kjgrc_cache',$kjgrc_cache);
+		}
+		$kjgrc_cache[comments] = kjgrc_prepare_cache(base64_decode($kjgrc_cache[comments]));
+		$kjgrc_cache[trackbacks] = kjgrc_prepare_cache(base64_decode($kjgrc_cache[trackbacks]));
+	}
+	// search and replace up to date information
+	return $kjgrc_cache[$type];
+}
+
+
+function kjgrc_create_recent_trackbacks( $caller = '')
+{
+	global $kjgrc_we_are_a_widget,$kjgrc_widget_args;;
+
+	// $entries = kjgrc_get_entries($max_comments,$chars_per_comment,$chars_per_word,$format,$query,0,$caller);
+	$entries = kjgrc_get_comments2('grt',$caller);
+	// If we are a widget: Also care for the title
+	if ($kjgrc_we_are_a_widget == TRUE) {
+        	extract($kjgrc_widget_args);
+        	return $before_widget . $before_title . kjgrc_get_option("grt","sidebar_title") . $after_title .
+        	'<div id="get_recent_comments_wrap"><ul>' .
+		$entries .
+		'</ul></div>' .
+		$after_widget;
+        }
+	return $entries;
+}
+
+function kjgrc_create_recent_comments ($caller='')
+{
+	global $kjgrc_we_are_a_widget,$kjgrc_widget_args;
+
+	// $entries = kjgrc_get_entries($max_comments,$chars_per_comment,$chars_per_word,$format,$query,kjgrc_get_option("grc","grouped_by_post"),$caller);
+	// If we are a widget: Also care for the title
+	// $entries = kjgrc_get_comments2($max_comments,$chars_per_comment,$chars_per_word,$format,kjgrc_get_option("grc","grouped_by_post"),$caller);
+	$entries = kjgrc_get_comments2('grc',$caller);
+	if ($kjgrc_we_are_a_widget == TRUE) {
+        	extract($kjgrc_widget_args);
+        	return $before_widget . $before_title . kjgrc_get_option("grc","sidebar_title") . $after_title .
+        	'<div id="get_recent_comments_wrap"><ul>' .
+		$entries .
+		'</ul></div>' .
+		$after_widget;
+        } 
+	return $entries;
+}
+
+function kjgrc_parse_pingback($pingback_author)
+{
+	$workstring = trim($pingback_author);
+	/* most common syntax
+	1. author &raquo; title
+	2. author &raquo; category &raquo; title
+	3. title at author
+	4. title - author (too insignificant)
+	5. [&raquo;] title &laquo; author
+	*/
+	$first_delimiter = strpos($workstring,'&raquo;');
+	while ($first_delimiter !== false && $first_delimiter == 0) {
+		$workstring = trim(substr($workstring,7));
+		$first_delimiter = strpos($workstring,'&raquo;');
+	}
+	if ($first_delimiter !== false) {
+		$comment_author = substr($workstring,0,$first_delimiter-1);
+		$workstring = trim(substr($workstring,$first_delimiter+7));
+		$first_delimiter = strpos($workstring,'&raquo;');
+		if ($first_delimiter !== false) {
+			$workstring = trim(substr($workstring,$first_delimiter+7));
+		}
+		return array($comment_author,$workstring);
+	}
+	foreach (array(' at ','&laquo;',' - ',' auf ',' by ',' // ',' | ',' : ',' @ ',' / ') as $delimiter)
+	{
+		$first_delimiter = strpos($workstring,$delimiter);
+		if ($first_delimiter !== false) {
+			$trackback_title = trim(substr($workstring,0,$first_delimiter));
+			$comment_author = trim(substr($workstring,$first_delimiter+strlen($delimiter)));
+			// kjgrc_log("delimiter match [$delimiter]: $workstring -> a: '$comment_author' t: '$trackback_title' ");
+			return array($comment_author,$trackback_title);
+		}
+	}
+	// $comment_author = 'Unknown';
+	$comment_author = $pingback_author;
+	$trackback_title = '';
+	return array($comment_author,$trackback_title);
+}
+
+// original function from wordpress 2.3 for backwards compatibility to wordpress 2.0.11
+function kjgrc_get_users_of_blog( $id = '' ) {
+        global $wpdb, $blog_id;
+        if ( empty($id) )
+                $id = (int) $blog_id;
+        $users = $wpdb->get_results( "SELECT user_id, user_login, display_name, user_email, meta_value FROM $wpdb->users, $wpdb->usermeta WHERE " . $wpdb->users . 
+".ID = " . $wpdb->usermeta . ".user_id AND meta_key = '" . $wpdb->prefix . "capabilities' ORDER BY {$wpdb->usermeta}.user_id" );
+        return $users;
+}
+
+
+/* This function is dedicated to Mike */
+function kjgrc_is_wordpress_user($comment)
+{
+	global $kjgrc_wordpress_users;
+	if ($kjgrc_wordpress_users == NULL) {
+		// be still compatible to wordpress 2.0.11
+		if (function_exists("get_users_of_blog")) {
+			$kjgrc_wordpress_users = get_users_of_blog();
+		} else {
+			$kjgrc_wordpress_users = kjgrc_get_users_of_blog();
+		}
+	}
+	foreach ($kjgrc_wordpress_users as $user) {
+		if (strcasecmp($comment->comment_author_email,$user->user_email) == 0 ) {
+			if ( (strcasecmp($comment->comment_author,$user->user_login) == 0) ||
+			     (strcasecmp($comment->comment_author,$user->display_name) == 0) ) 
+			{
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+// function kjgrc_get_comments2 ($max_comments,$chars_per_comment,$chars_per_word,$format,$grouped_by_post,$caller)
+function kjgrc_get_comments2 ($requested_comment_type,$caller)
+{
+	// kjgrc_log("kjgrc_get_comments2 $caller");
+	// echo "kjgrc_get_comments2 $requested_comment_type $caller<br>";
+	global $wpdb;
+	$batch_number = 0;
+	$number_of_comments = 0;
+
+	$requested_number_of_comments = kjgrc_get_option($requested_comment_type,"max_comments");
+	$chars_per_comment = kjgrc_get_option($requested_comment_type,"chars_per_comment");
+	$chars_per_word = kjgrc_get_option($requested_comment_type,"chars_per_word");
+	$format = stripslashes(kjgrc_get_option($requested_comment_type,"format"));
+	$batch_size = $requested_number_of_comments * 3;
+	// grc
+	//x// $max_comments = kjgrc_get_option("grc","max_comments");
+	//x// $chars_per_comment = kjgrc_get_option("grc","chars_per_comment");
+	//x// $chars_per_word = kjgrc_get_option("grc","chars_per_word");
+	//x// $format = stripslashes(kjgrc_get_option("grc","format"));
+	//x// $sql_comment_type = "AND comment_type = '' ";
+	//x// if (kjgrc_get_option("grc","show_trackbacks") == 1) {
+	//x// 	$sql_comment_type = '';
+	//x// }
+	
+	// grt
+	//// $max_comments = kjgrc_get_option("grt","max_comments");
+	//// $chars_per_comment = kjgrc_get_option("grt","chars_per_comment");
+	//// $chars_per_word = kjgrc_get_option("grt","chars_per_word");
+	//// $format = stripslashes(kjgrc_get_option("grt","format"));
+
+	//// $query = "SELECT DISTINCT $wpdb->comments.* FROM $wpdb->comments ".
+	//// 	"LEFT JOIN $wpdb->posts ON $wpdb->posts.ID=$wpdb->comments.comment_post_ID ".
+	//// 	$sql_join_post2cat .
+	//// 	"WHERE (post_status = 'publish' OR post_status = 'static') AND comment_approved= '1' AND  post_password = '' ".
+	//// 	$sql_exlude_cat .
+	//// 	"AND ( comment_type = 'trackback' OR comment_type = 'pingback' ) ".
+        ////         $sql_ignore_ip .
+	//// 	"ORDER BY comment_date DESC LIMIT $max_comments";
+	
+	$sql_ignore_ip = '';
+	$sql_exclude_owner = '';
+	$grouped_by_post = 0;
+	if ($requested_comment_type == 'grt')
+	{	
+		if (kjgrc_get_option("grt","ignore_ip") != '') {
+			$sql_ignore_ip = "AND comment_author_IP != '". kjgrc_get_option("grt","ignore_ip") ."' ";
+		}
+		$sql_comment_type = "( comment_type = 'trackback' OR comment_type = 'pingback' ) ";
+	}
+	if ($requested_comment_type == 'grc')
+	{
+		$sql_comment_type = "comment_type = '' ";
+		if (kjgrc_get_option("grc","show_trackbacks") == 1) {
+			$sql_comment_type = '1 ';
+		}
+		if (kjgrc_get_option("grc","exclude_blog_owner") == 1) {
+		 	$sql_exclude_owner = "AND user_id = '0' ";
+		}
+		if (kjgrc_get_option("grc","grouped_by_post")) {
+			$grouped_by_post = 1;
+		}
+	}
+
+	while ($comments_number < $requested_number_of_comments) 
+	{
+		$query = "SELECT * from $wpdb->comments WHERE comment_approved= '1' AND " . 
+			 $sql_comment_type .
+			 $sql_exclude_owner .
+			 $sql_ignore_ip .
+			 "ORDER BY comment_date DESC LIMIT " . ($batch_number*$batch_size) . ",$batch_size"; 
+		// echo "$query<br><br>";
+		$comments = $wpdb->get_results($query);
+		if (!$comments) {
+			$result .= "none";
+			break;
+		}
+		unset($missing_post);
+		foreach ($comments as $comment) {
+			if ($post_cache == NULL || ! array_key_exists($comment->comment_post_ID,$post_cache)) {
+				$missing_post[$comment->comment_post_ID] = 1;
+			}
+		}
+		if ($missing_post != NULL)
+		{
+			unset($comma_separated);
+			$comma_separated = implode(",", array_keys($missing_post));
+			if (empty($wpdb->term_relationships)) {
+				$query = "SELECT * from $wpdb->posts JOIN $wpdb->post2cat ON ID = post_id WHERE ID IN ($comma_separated);";
+			} else { 
+				$query = "SELECT * from $wpdb->posts JOIN $wpdb->term_relationships ON ($wpdb->term_relationships.object_id=ID)  WHERE ID IN ($comma_separated);"; 
+			}
+			// echo "$query<br>";
+			$posts = $wpdb->get_results($query);
+			foreach ($posts as $post) {
+				// echo "p: $post->ID ";
+				$post_cache[$post->ID] = $post;
+				if (empty($wpdb->term_relationships)) {
+					$cat_cache[$post->ID][$post->category_id] = 1;
+				} else {
+					$cat_cache[$post->ID][$post->term_taxonomy_id] = 1;
+				}	
+			}
+		}
+		// echo "-------<br>";
+		// drop comments:
+		// 1. comment_approved = 1 -> in select
+        	// 2. post_status = 'publish' OR post_status = 'static'
+		// 3. post_password = ''
+		// 4. AND user_id = '0' -> in select
+		// 5. AND comment_author_IP != '1.2.3.4' -> in select	
+		// 6. "AND category_id  != '$cat'
+    		// 7. limit_comments_per_post
+
+		foreach ($comments as $comment) 
+		{
+			if ($post_cache[$comment->comment_post_ID]->post_status != 'publish' &&
+                            $post_cache[$comment->comment_post_ID]->post_status != 'static' ) 
+			{	
+				// echo "drop $comment->comment_ID (". $post_cache[$comment->comment_post_ID]->post_status .")<br>";
+				continue;
+			}	
+			if ($post_cache[$comment->comment_post_ID]->post_password != '') {
+				// echo "drop $comment->comment_ID (protected)<br>";
+				continue;
+			}
+			/* Optional additional check for wordpress users who are not logged in */
+			if ((kjgrc_get_option("grc","exclude_blog_owner") == 1) && 
+			    (kjgrc_get_option("grc","exclude_blog_owner2") == 1)	
+			   ) 
+			{
+				if (kjgrc_is_wordpress_user($comment)) {
+					// echo "drop $comment->comment_ID (is_wordpress_user)<br>";
+					continue;
+				}
+			}
+			$exclude_cat = kjgrc_get_exclude_cat();
+			$is_in_excluded_cat = 0;
+			if (kjgrc_get_option("misc","exclude_cat_reverse") == 1) {
+				$is_in_excluded_cat = 1;
+			}
+			if ($exclude_cat) {
+				foreach ($exclude_cat as $cat) {
+					if ($cat_cache[$comment->comment_post_ID][$cat] == 1) {
+						$is_in_excluded_cat = 1;
+						if (kjgrc_get_option("misc","exclude_cat_reverse") == 1) {
+							$is_in_excluded_cat = 0;
+						}
+						// echo "drop $comment->comment_ID (is in excluded cat $cat)<br>";
+						
+					}
+				}
+				if ($is_in_excluded_cat) {
+					continue;
+				}
+			}
+			// nur !trackbacks?
+			if ($grouped_by_post && kjgrc_get_option("grc","limit_comments_per_post") == 1) {
+				if ($comments_per_post_counter[$comment->comment_post_ID] >= kjgrc_get_option("grc","comments_per_post")) {
+					// echo "drop $comment->comment_ID (max nr cmt/post reached)<br>";
+					continue;
+				}
+			}
+			$comments_per_post_counter[$comment->comment_post_ID]++;
+			$comments_number++;
+			// $result .= "$comments_number [$batch_number] $comment->comment_ID ($comment->comment_content)<br>";
+			$fetched_comments[] = $comment;
+			if ($comments_number >= $requested_number_of_comments) {
+				break;
+			}
+		}
+		$batch_number++;
+		
+	} 
+        // comments are selected. now format them
+
+	if (!(strpos($format,"%gravatar") !== false))
+		$has_gravatar = 0;
+	else {
+		$has_gravatar = 1;
+		$gravatar_alt_url = kjgrc_get_option('gravatar','alt_url');
+		$gravatar_size    = kjgrc_get_option('gravatar','size');
+		$gravatar_rating  = kjgrc_get_option('gravatar','rating');
+		$gravatar_mpaa[0] = 'G';
+		$gravatar_mpaa[1] = 'PG';
+		$gravatar_mpaa[2] = 'R';
+		$gravatar_mpaa[3] = 'X';
+		$gravatar_options .= "&amp;size=$gravatar_size";
+		$gravatar_options .= "&amp;rating=" . $gravatar_mpaa[$gravatar_rating];
+		if (kjgrc_get_option('gravatar','alt_url') != '') {
+			$gravatar_options .= "&amp;default=" . urlencode($gravatar_alt_url);
+		}
+	}
+
+        if (! $fetched_comments) {
+		return "<li><!-- no comments yet --></li>";
+	}
+	foreach ($fetched_comments as $comment)
+	{
+		$trackback_title = '';
+		if (function_exists("polyglot_init")) {
+			// This looks like the wrong filter, but the_content deletes smileys when called from here
+			$comment_excerpt = apply_filters('single_post_title',$comment->comment_content);
+		} else {
+			$comment_excerpt = $comment->comment_content;
+		}
+		// comment_author, 
+		$comment_type = "Comment";
+		if ($comment->comment_type == 'pingback') 
+		{
+			
+			$comment_type = "Pingback";
+			list($comment_author,$trackback_title) = kjgrc_parse_pingback($comment->comment_author);
+			if(strpos($comment_excerpt,'[...]') == 0) 
+				$comment_excerpt = trim(substr($comment_excerpt,5));
+			if(strpos($comment_excerpt,'[...]') == strlen($comment_excerpt)-5)
+				$comment_excerpt = trim(substr($comment_excerpt,0,strlen($comment_excerpt)-5));
+		}
+		elseif ($comment->comment_type == 'trackback') 
+		{
+			$comment_type = "Trackback";
+			$trackback_title = preg_replace("/^<strong>(.+?)<\/strong>.*/s","$1",$comment->comment_content);
+			$trackback_title = strip_tags($trackback_title);
+			$trackback_title = preg_replace("/[\n\t\r]/"," ",$trackback_title);
+               		$trackback_title = preg_replace("/\s{2,}/"," ",$trackback_title);
+               		$trackback_title = wordwrap($trackback_title,$chars_per_word,' ',1);
+										
+			$comment_excerpt = preg_replace("/^<strong>.+?<\/strong>/","",$comment->comment_content,1);
+			$comment_author = $comment->comment_author;
+		}
+		else 
+		{
+			$comment_author = $comment->comment_author;
+			if (!$comment_author)
+				$comment_author = "Anonymous";
+		}	
+		$comment_excerpt = strip_tags(wptexturize($comment_excerpt));
+
+		$comment_excerpt = preg_replace("/[\n\t\r]/"," ",$comment_excerpt); // whitespace into 1 blank
+		$comment_excerpt = preg_replace("/\s{2,}/"," ",$comment_excerpt); // whitespace into 1 blank
+                $comment_excerpt = wordwrap($comment_excerpt,$chars_per_word,' ',1);
+
+		if ($trackback_title == '')
+			$trackback_title = $comment_excerpt;
+
+		$post_link    = get_permalink($comment->comment_post_ID);
+		$comment_link = $post_link .
+				"#comment-$comment->comment_ID";
+		/* Does not work - polyglot uses global variables to access the comment/post data
+		if (function_exists("polyglot_init")) {
+			$comment_date = apply_filters('the_date',$comment->comment_date);
+		} else {
+			$comment_date = mysql2date(get_settings('date_format'),$comment->comment_date);
+		}
+		if (function_exists("polyglot_init")) {
+			$comment_time = apply_filters('the_time',$comment->comment_date);
+		} else {
+			//$comment_time = substr($comment->comment_date,11,5); // 2005-03-09 22:23:53
+			$comment_time = mysql2date(get_settings('time_format'),$comment->comment_date); // Thanks to Keith
+		}
+		*/
+		$comment_date = mysql2date(get_settings('date_format'),$comment->comment_date);
+		$comment_time = mysql2date(get_settings('time_format'),$comment->comment_date); // Thanks to Keith
+
+		if ($has_gravatar && $comment_author != '') 
+		{
+			if ($md5_cache && array_key_exists($comment->comment_author,$md5_cache)) {
+				$gravatar_md5 = $md5_cache[$comment->comment_author];
+			} else {
+				$gravatar_md5 = md5($comment->comment_author_email);
+				$md5_cache[$comment->comment_author_email] = $gravatar_md5; 
+			}
+			$comment_gravatar_url = "http://www.gravatar.com/avatar.php?" .
+				"gravatar_id=$gravatar_md5" .
+				$gravatar_options;
+				
+			$comment_gravatar = "<img src=\"" . $comment_gravatar_url .
+				"\" alt=\"\" width=\"$gravatar_size\" height=\"$gravatar_size\" class=\"kjgrcGravatar\" />";
+		}
+		#$post = get_postdata($comment->comment_post_ID);
+		#$post_date = mysql2date(get_settings('date_format'),$post['Date']);
+		#$post_title = trim(htmlspecialchars(stripslashes($post['Title'])));
+		#$post = get_postdata($comment->comment_post_ID);
+		// *** insert cache for post data here
+		// $post = $wpdb->get_row("SELECT * from $wpdb->posts WHERE ID = $comment->comment_post_ID");
+		$post = $post_cache[$comment->comment_post_ID];
+		if (function_exists("polyglot_init")) {
+			$post_date = apply_filters('the_date',$post->post_date);
+		} else {
+			$post_date = mysql2date(get_settings('date_format'),$post->post_date);
+		}
+		// $post_title = trim(htmlspecialchars(stripslashes($post->post_title)));
+		$post_title = strip_tags(wptexturize($post->post_title));
+		if (function_exists("polyglot_init")) {
+			$post_title = apply_filters('single_post_title',$post_title);
+		}
+		$post_counter = $post->comment_count;
+
+		$author_url = $comment->comment_author_url;
+		if ($author_url == "http://")
+			$author_url = "";
+		if (empty($author_url) || $author_url == "http://")
+			$author_url_href = "";
+
+		$output = $format;
+		// Replace tags by values
+		$output = str_replace("%comment_link",    $comment_link,     $output);
+		$output = str_replace("%author_url_href", $author_url_href,  $output);
+		$output = str_replace("%author_url",      $author_url,       $output);
+		$output = str_replace("%userid",  	  $comment->user_id, $output);
+		
+		$output = str_replace("%gravatar_url",    $comment_gravatar_url, $output);
+		$output = str_replace("%gravatar",        $comment_gravatar, $output);
+
+		// function author_image_path($authorID, $display = true, $type = 'url') 
+		if (function_exists("author_image_path")) {
+			$profile_pict = author_image_path($comment->user_id,false,'url');
+			$output = str_replace("%profile_picture",  $profile_pict,   $output);
+		} else {
+			$output = str_replace("%profile_picture",  '',   $output);
+		}
+
+		$output = str_replace("%comment_author",  $comment_author,   $output);
+		$output = str_replace("%comment_date",    $comment_date,     $output);
+		$output = str_replace("%comment_time",    $comment_time,     $output);
+
+		
+		//$output = str_replace("%time_since",    'time_since_' . $comment->unixdate . ' - ' . gmdate('U') .' = '. ($comment->unixdate-gmdate('U')),    $output);
+		$utc_time = kjgrc_utc2unixtime($comment->comment_date_gmt); //2006-12-30 17:05:59
+		$output = str_replace("%time_since",    "<%time_since:$utc_time>",    $output);
+		
+		$output = str_replace("%comment_type",    $comment_type,     $output);
+		$output = str_replace("%post_title",      $post_title,       $output);
+		$output = str_replace("%post_link",       $post_link,        $output);
+		$output = str_replace("%post_date",       $post_date,        $output);
+		$output = str_replace("%post_counter",    $post_counter,     $output);
+
+		/*
+		// Nice idea, but confuses users
+		//strip title or content?
+		$visible = strip_tags($output);
+		if (strpos($visible,'%comment_excerpt') !== false) {
+			$comment_excerpt = kjgrc_excerpt($comment_excerpt,$chars_per_comment,$chars_per_word,'%comment_excerpt',$output);
+		} 
+		elseif (strpos($visible,'%trackback_title') !== false) {
+			$trackback_title = kjgrc_excerpt($trackback_title,$chars_per_comment,$chars_per_word,'%trackback_title',$output);
+		}
+		*/
+		$comment_excerpt = kjgrc_excerpt($comment_excerpt,$chars_per_comment,$chars_per_word,'%comment_excerpt',$output);
+		if (kjgrc_get_option("misc","convert_smileys")) {
+			if (function_exists("csm_convert") ) {
+				$comment_excerpt = csm_convert($comment_excerpt);
+			} 
+			else {
+				if (get_settings('use_smilies')) {
+					$comment_excerpt = convert_smilies($comment_excerpt);
+				}
+			}
+		}
+
+		$trackback_title = kjgrc_excerpt($trackback_title,$chars_per_comment,$chars_per_word,'%trackback_title',$output);
+		
+		$output = str_replace("%comment_excerpt", $comment_excerpt, $output);
+		$output = str_replace("%trackback_title", $trackback_title, $output);
+		// Replacement done
+
+		//$len = strlen(strip_tags($output));
+		//$output .= " [$comment_time]";
+		// . " (" . time_since(strtotime($comment->comment_date_gmt." GMT")) ." ago)";
+		// *** Das aber nur bei recent comments, nicht bei trackbacks!
+		// if (kjgrc_get_option("grc","limit_comments_per_post") == 1) {
+		// 	if (count($comment_list[$comment->comment_post_ID]) < kjgrc_get_option("grc","comments_per_post")) { 
+		// 		$comment_list[$comment->comment_post_ID][] = $output;
+		// 	}
+		// } else {
+		// 		$comment_list[$comment->comment_post_ID][] = $output;
+		// }
+		$comment_list[$comment->comment_post_ID][] = $output;
+
+		if (($post_list == NULL) || ! array_key_exists($comment->comment_post_ID,$post_list)) {
+			$post_output = stripslashes(kjgrc_get_option("grc","grouped_by_post_a"));
+			$post_output = str_replace("%post_title",         $post_title,         $post_output);
+			$post_output = str_replace("%post_link",          $post_link,          $post_output);
+			$post_output = str_replace("%post_date",          $post_date,          $post_output);
+			$post_output = str_replace("%post_counter",       $post_counter, $post_output);
+			$post_list[$comment->comment_post_ID] = $post_output;
+		}
+
+		$all_entries .= "\t$output\n";
+		if ($caller == 'grc_sample' || $caller ==  'grt_sample') 
+			break;
+	} // foreach comments
+
+	if ($grouped_by_post == 1)
+	{	
+		$all_entries = '';
+		foreach (array_keys($post_list) as $post_id) {
+			$all_entries .= $post_list[$post_id] . "\n";
+			foreach ($comment_list[$post_id] as $tmp) {
+				$all_entries .= $tmp ."\n";
+			} 
+			$all_entries .= kjgrc_get_option("grc","grouped_by_post_b") ."\n";
+		}
+	}
+		
+	return $all_entries;
+}
+
+function kjgrc_utc2unixtime($utc_time)
+{
+	$y = substr($utc_time,0,4);
+	$m = substr($utc_time,5,2);
+	$d = substr($utc_time,8,2);
+	$h = substr($utc_time,11,2);
+	$min = substr($utc_time,14,2);
+	$s   = substr($utc_time,17,2);
+	//mktime ( [int hour [, int minute [, int second [, int month [, int day [, int year [, int is_dst]]]]]]])
+	//$ago = gmdate('U')-$utc_time;
+	return gmmktime($h,$min,$s,$m,$d,$y);
+}
+
+function kjgrc_format_seconds($seconds)
+{
+	$d_str = "days";
+	$h_str = "hours";
+	$m_str = "minutes";
+	$s_str = "seconds";
+
+	$d = floor($seconds / (24 * 3600));
+	if ($d == 1) $d_str = "day";
+	$seconds = $seconds - ($d * 24 * 3600);
+	
+	$h = floor($seconds / 3600);
+	if ($h == 1) $h_str = "hour";
+	$seconds = $seconds - ($h * 3600);
+
+	$m = floor($seconds / 60);
+	if ($m == 1) $m_str = "minute";
+	$seconds = $seconds - ($m * 60);
+	
+	$s = $seconds;
+	if ($s == 1) $s_str = "second";
+	
+	if ($d > 0) return "$d $d_str $h $h_str";
+	if ($h > 0) return "$h $h_str $m $m_str";
+	if ($m > 0) return "$m $m_str $s $s_str";
+	return "$s $s_str";
+}
+
+function kjgrc_excerpt ($text,$chars_per_comment,$chars_per_word,$tag,$output)
+{
+	$length = strlen(str_replace($tag,"",strip_tags($output)));
+	$length = $chars_per_comment - $length;
+	$length = $length -2; // we will add three dots at the end
+	if ($length < 0) $length = 0;
+	if (strlen($text) > $length) {
+		$text = substr($text,0,$length);
+		$text = substr($text,0,strrpos($text,' '));
+		// last word exceeds max word length:
+		if ((strlen($text) - strrpos($text,' ')) > $chars_per_word) {
+			$text = substr($text,0,strlen($text)-3);
+		} 
+		$text = $text . "...";
+	}
+	#$text = "[EXCERPT]: '$text'";
+	return "$text";
+}
+
+function widget_kj_get_recent_comments_init() {
+	if (! function_exists("register_sidebar_widget")) {
+		return;
+	}
+	function widget_get_recent_comments($args) {
+		global $kjgrc_we_are_a_widget,$kjgrc_widget_args;
+		$kjgrc_we_are_a_widget = TRUE;
+		$kjgrc_widget_args = $args;
+		get_recent_comments();
+	}
+	function widget_get_recent_comments_control() {
+		global $kjgrc_we_are_a_widget;
+		$kjgrc_we_are_a_widget = TRUE;
+		if ( $_POST['get_recent_comments-submit'] ) {
+			kjgrc_set_option("grc_sidebar_title",stripslashes($_POST['get_recent_comments-title']));
+			kjgrc_invalidate_cache();
+		}
+		echo '<p style="text-align:right;"><label for="get_recent_comments-title">Title: <input style="width: 200px;" id="get_recent_comments-title" name="get_recent_comments-title" type="text" value="'.kjgrc_get_option("grc","sidebar_title").'" /></label></p>';
+		echo '<input type="hidden" id="get_recent_comments-submit" name="get_recent_comments-submit" value="1" />';
+		echo 'More options are on the <a href="options-general.php?page=get-recent-comments.php&amp;subpage=1">plugin page</a>.';
+	}
+	register_sidebar_widget('Get Recent Comments', 'widget_get_recent_comments');
+	register_widget_control('Get Recent Comments', 'widget_get_recent_comments_control', 300, 100);
+}
+
+function widget_kj_get_recent_trackbacks_init() {
+	if (! function_exists("register_sidebar_widget")) {
+		return;
+	}
+	function widget_get_recent_trackbacks($args) {
+		global $kjgrc_we_are_a_widget,$kjgrc_widget_args;;
+		$kjgrc_we_are_a_widget = TRUE;
+		$kjgrc_widget_args = $args;
+		get_recent_trackbacks();
+	}
+	function widget_get_recent_trackbacks_control() {
+		global $kjgrc_we_are_a_widget;
+		$kjgrc_we_are_a_widget = TRUE;
+		if ( $_POST['get_recent_trackbacks-submit'] ) {
+			kjgrc_set_option("grt_sidebar_title",stripslashes($_POST['get_recent_trackbacks-title']));
+			kjgrc_invalidate_cache();
+		}
+		echo '<p style="text-align:right;"><label for="get_recent_trackbacks-title">Title: <input style="width: 200px;" id="get_recent_trackbacks-title" name="get_recent_trackbacks-title" type="text" value="'.kjgrc_get_option("grt","sidebar_title").'" /></label></p>';
+		echo '<input type="hidden" id="get_recent_trackbacks-submit" name="get_recent_trackbacks-submit" value="1" />';
+		echo 'More options are on the <a href="options-general.php?page=get-recent-comments.php&amp;subpage=2">plugin page</a>.';
+	}
+	
+	register_sidebar_widget('Get Recent Trackbacks', 'widget_get_recent_trackbacks');
+	register_widget_control('Get Recent Trackbacks', 'widget_get_recent_trackbacks_control', 300, 100);
+}
+
+add_action('admin_menu', 'kjgrc_add_options_page');
+add_action('edit_comment','kjgrc_invalidate_cache');
+add_action('delete_comment','kjgrc_invalidate_cache');
+add_action('edit_post','kjgrc_invalidate_cache');
+add_action('delete_post','kjgrc_invalidate_cache');
+add_action('publish_post','kjgrc_invalidate_cache');
+add_action('switch_theme', 'kjgrc_invalidate_cache');
+add_action('wp_set_comment_status','kjgrc_invalidate_cache');
+add_action('comment_post','kjgrc_handle_new_comment');
+add_action('trackback_post','kjgrc_handle_new_comment');
+add_action('pingback_post','kjgrc_handle_new_comment');
+add_action('plugins_loaded', 'widget_kj_get_recent_comments_init');
+add_action('plugins_loaded', 'widget_kj_get_recent_trackbacks_init');
+
+?>
Index: /afridex/plugins/get-recent-comments 2/html2txt
===================================================================
--- /afridex/plugins/get-recent-comments 2/html2txt (revision 21)
+++ /afridex/plugins/get-recent-comments 2/html2txt (revision 21)
@@ -0,0 +1,1 @@
+w3m changelog.html -dump -cols 65 > changelog.txt
Index: /afridex/plugins/get-recent-comments 2/patch-2.0.1-2.0.2
===================================================================
--- /afridex/plugins/get-recent-comments 2/patch-2.0.1-2.0.2 (revision 21)
+++ /afridex/plugins/get-recent-comments 2/patch-2.0.1-2.0.2 (revision 21)
@@ -0,0 +1,56 @@
+--- ../tags/2.0.1/get-recent-comments.php	2007-09-25 21:57:50.000000000 +0200
++++ get-recent-comments.php	2007-09-25 22:18:38.000000000 +0200
+@@ -1,7 +1,7 @@
+ <?php
+ /*
+ Plugin Name: Get Recent Comments
+-Version: 2.0.1
++Version: 2.0.2
+ Plugin URI: http://blog.jodies.de/archiv/2004/11/13/recent-comments/
+ Author: Krischan Jodies
+ Author URI: http://blog.jodies.de
+@@ -127,8 +127,12 @@
+ function kjgrc_subpage_exclude_cat() 
+ {
+ 	global $wpdb;
+-	// $categories = $wpdb->get_results("SELECT * FROM $wpdb->categories ORDER BY cat_name");
+-	$categories = get_categories('&hide_empty=0');
++	if (function_exists("get_categories")) {
++		$categories = get_categories('&hide_empty=0');
++	} else {
++		// be still compatible to 2.0.11
++		$categories = $wpdb->get_results("SELECT * FROM $wpdb->categories ORDER BY cat_name");
++	}
+ 	$exclude_cat = kjgrc_get_exclude_cat();
+ ?>
+ <form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=get-recent-comments.php&amp;subpage=4&amp;updated=true">
+@@ -997,12 +1001,28 @@
+ 	return array($comment_author,$trackback_title);
+ }
+ 
++// original function from wordpress 2.3 for backwards compatibility to wordpress 2.0.11
++function kjgrc_get_users_of_blog( $id = '' ) {
++        global $wpdb, $blog_id;
++        if ( empty($id) )
++                $id = (int) $blog_id;
++        $users = $wpdb->get_results( "SELECT user_id, user_login, display_name, user_email, meta_value FROM $wpdb->users, $wpdb->usermeta WHERE " . $wpdb->users . 
++".ID = " . $wpdb->usermeta . ".user_id AND meta_key = '" . $wpdb->prefix . "capabilities' ORDER BY {$wpdb->usermeta}.user_id" );
++        return $users;
++}
++
++
+ /* This function is dedicated to Mike */
+ function kjgrc_is_wordpress_user($comment)
+ {
+ 	global $kjgrc_wordpress_users;
+ 	if ($kjgrc_wordpress_users == NULL) {
+-		$kjgrc_wordpress_users = get_users_of_blog();
++		// be still compatible to wordpress 2.0.11
++		if (function_exists("get_users_of_blog")) {
++			$kjgrc_wordpress_users = get_users_of_blog();
++		} else {
++			$kjgrc_wordpress_users = kjgrc_get_users_of_blog();
++		}
+ 	}
+ 	foreach ($kjgrc_wordpress_users as $user) {
+ 		if (strcasecmp($comment->comment_author_email,$user->user_email) == 0 ) {
Index: /afridex/plugins/get-recent-comments 2/changelog.html
===================================================================
--- /afridex/plugins/get-recent-comments 2/changelog.html (revision 21)
+++ /afridex/plugins/get-recent-comments 2/changelog.html (revision 21)
@@ -0,0 +1,188 @@
+  <p><strong>Changelog</strong></p>
+  <table>
+  <tbody><tr>
+  <td>Version</td>
+  <td>Date</td>
+
+  <td>Changes</td>
+  </tr>
+
+ <td valign="top">2.0.2</td>
+   <td valign="top">2007/09/25</td>
+   <td valign="top">Fix: Plugin was not compatible to WordPress 2.0.11 any more. Thank you to <a href="http://info.zumtee.de/">Stephan</a> for reporting the bug.
+   </td>
+  </tr>
+
+
+   <td valign="top">2.0.1</td>
+   <td valign="top">2007/09/24</td>
+   <td valign="top">Added switch on the categories page, which reverses the
+selection. It is now possible to include or exclude categories.
+   </td>
+  </tr>
+
+  <tr>
+   <td valign="top">2.0</td>
+   <td valign="top">2007/09/24</td>
+   <td valign="top">
+   New code for fetching the data:</br>
+   1. Instead of one expensive database query we now use two or more cheap queries. Thanks to mirra, who reported the problem. And again thank you to the people mentioned in changlog 1.4, where the cache was introduced for the same (performance-) problems on big blogs.</br>
+   2. This also fixed a bug, which lead to too less than requested comments in lists, ordered by post. Thanks to <a href="http://comicsworthreading.com/">Johanna</a> and <a href="http://comicsworthreading.com/">FrÃ©dÃ©ric</a> for reporting and documenting this.
+   </br>
+Changed the css in the admin gui, to work around a display issue with Tiger Admin. Thank you, <a href="http://gantenbein.ws/">Andi</a>, for finding this.
+   </br>
+   Added %time_since macro, which displays the time since the comment was posted. Thanks to <a href="http://www.iamalik.com/">Imran</a> and <a href="http://dcdl.org/">Keith</a> for sugesting (something like) this (very long ago).
+   </br>Admin interface: Added switch fpr turning on and off smileys. Thank you, panos, for requesting this feature.
+   </br>
+   Support for Custom Smileys Plugin. Thanks to Henry for suggesting this.
+   </br>
+   Fix: Username was not displayed as "Anonymous", if commentor left no name. Thanks to <a href="http://pixcapacitor.com/">Pixelation</a> for reporting this.   </br>
+   Added support for WordPress 2.3. It will drop the post2cat table. Changed plugin to new taxonomy scheme. A *great* thank you goes to <a href="http://djz.hu">Lakatos Zsolt</a>, who provided a complete patch for get-recent-comments-1.5.6, which made it very easy for me to understand how 2.0-beta10 had to be changed. Thank you also to xelios, Ville and <a href="http://diekretzschmars.de/">Kretzschmar</a> who warned me, that WordPress 2.3 will break the old plugin code.
+   </br>
+   </td>
+  </tr>
+<tr>
+  <td valign="top">1.5.5</td>
+  <td valign="top">2007/03/26</td>
+  <td valign="top">Added support for <a href="http://fredfred.net/skriker/index.php/polyglot">malyfred's</a> <a href="http://fredfred.net/skriker/index.php/polyglot">Polyglot</a> Plugin. Requested by <a href="http://blog.schreiter.info/">Torben</a>.
+</tr>
+<tr>
+  <td valign="top">1.5.4</td>
+  <td valign="top">2007/02/01</td>
+  <td valign="top">Use full pingback_author as %comment_author (instead of 'Unknown', if the pingback parser fails to recognize the pingback_author. Thanks again to <a href="http://www.garamiz.hu/blog">Gant</a> who found this in his blog.</br>
+ Added %author_url_href macros, which allows to generate inactive links, if the
+ commentator did not leave an url. This was wished (in part long ago) by <a href="http://somegirlwitha.com/">beej</a>, 
+ <a href="http://revivalblog.com/">carl</a>, <a href="http://www.schiesty.org/">FilSchiesty</a> and SwB.<br>
+ Added %profile_picture macro, which supports <a href="http://geekgrl.net">Hannah Gray's</a> <a href="http://geekgrl.net/2007/01/02/profile-pics-plugin-release/">Profile Pics Plugin</a>. Thank you for the idea and your help, <a href="http://mein.meerblickzimmer.de/">Markus</a>
+  </td>
+</tr>
+
+<tr>
+  <td valign="top">1.5.3</td>
+  <td valign="top">2007/01/15</td>
+  <td valign="top">Refresh cache, when a comment is approved by moderator. Problem found by <a href="http://www.garamiz.hu/blog">Gant</a>. Thank you!
+  </td>
+</tr>
+
+<tr>
+  <td valign="top">1.5.2</td>
+  <td valign="top">2007/01/05</td>
+  <td valign="top">Added option for excluding comments from blog authors. Suggested by <a href="http://www.sokwanele.com/thisiszimbabwe">This is Zimbabwe</a>, <a href="http://tricolore.ca/">Slim</a>, <a href="http://marilynshampoo.co.uk/">marilyn's shampoo</a> and <a href="http://www.bizmord.com/Blog">Igor M.</a><br>
+  </td>
+</tr>
+
+<tr>
+  <td valign="top">1.5.1</td>
+  <td valign="top">2006/12/29</td>
+  <td valign="top">Store the cache base64 encoded. There seems to be a problem with the unserialization of multibyte characters. Thanks to <a href="http://priv.tw/blog">priv</a>, who reported the problem and suggested the encoding.<br>
+  <strong>After upgrading to this version you should trigger a regeneration of the cache by adding a comment somewhere.</strong> 
+  </td>
+</tr>
+
+  <tr>
+  <td valign="top">1.5</td>
+  <td valign="top">2006/12/27</td>
+  <td valign="top">New pingback parser<br>
+  		   Stop losing html entities and tags in the post titles and comments by using wptexturize. Thanks to <a href="http://etherwork.net/blog/">ejm</a> (again!) and <a href="http://www.orthodoxanarchist.com/">mobius</a> for reporting the problem and making suggestions.<br>
+                   Bugfix in widget code: Error, when trackbacks came before comments
+  </td>
+  </tr>
+
+  <tr>
+  <td valign="top">1.4</td>
+  <td valign="top">2006/12/24</td>
+  <td valign="top">
+  The plugin is a widget now. Thanks to <a href="http://www.herrmueller.net/">herrmueller</a> and <a href="http://blog.deklein.de">Thomas de Klein</a> for suggesting this feature.<br />
+  Cache the output in order to reduce the database impact of the plugin. Thanks to the following people for reporting the poor performance and making suggestions to solve the problem: <a href="http://www.brandonstone.com/">Brandon Stone</a>, <a href="http://king-of-fools.com/">King of Fools</a>, <a href="http://www.basicthinking.de/blog/">Robert Basic</a> and especially <a href="http://www.4null4.de/">CountZero</a>.<br />
+  Option to combine comments and trackbacks in one list (requested by <a href="http://softwaremaniacs.org/blog/">Maniac</a> and <a href="http://www.dieproduzentin.de/">die produzentin</a>)<br />
+  Allow to Group comments by their posting (requested by <a href="http://www.dylanchords.com/blog">eyolf</a>)<br />
+  Allow limit of comments per post (suggested by <a href="http://blog.deklein.de">Thomas</a>)<br />
+  Use Wordpress 2.1 compatible database variables. Thanks to <a href="http://spencerp.net">spencerp</a>, for reporting and fixing.<br />
+  Bugfix: Wrong key used in gravatar hash (Thank you, <a href="http://scatterload.blogspot.com/">Hamzeh N.</a>, for finding and fixing this).<br />
+  Updated the stylesheets to the look of wordpress 2.x.<br />
+  Added two macros: %comment_type and %post_counter.<br />
+  Use less option variables in db.<br />
+  Updated instructions page.<br />
+  Dropped support for Wordpress 1.2
+  </td>
+  </tr>
+ 
+  <tr>
+  <td valign="top">1.3.1</td>
+  <td valign="top">2006/12/11</td>
+  <td valign="top">Fixes for problems with wordpress running under windows.</td>
+  </tr>
+
+  <tr>
+  <td valign="top">1.3</td>
+  <td valign="top">2006/11/26</td>
+  <td valign="top">Fixes for problems with php5.</td>
+  </tr>
+
+  <tr>
+  <td valign="top">1.2</td>
+  <td valign="top">2005/09/15</td>
+  <td valign="top">Prevent pingbacks from own blog.  Thanks to <a href="http://www.tatteredcoat.com/">Matt</a> for the idea and support!<br>To use the feature, go to the trackbacks configuration and enter the address of your webserver.</td>
+  </tr>
+  </tr>
+
+<tr>
+  <td valign="top">1.0</td>
+  <td valign="top" nowrap>2005/03/21</td>
+  <td valign="top">Also show comments to static pages. (They are new in WP 1.5). Thanks to <a href="http://www.mazalien.nl/weblog/">maza</a> for the hint.</td>
+  </tr>
+
+  <tr>
+  <td valign="top">0.9</td>
+  <td valign="top">2005/03/20</td>
+
+  <td valign="top">Introduced admin gui. Handle trackbacks different than comments. Replaced most regular expressions with basic string operations.
+  Dedicated macro for posting time. Requested by Zonekiller</td>
+  </tr>
+
+  <tr>
+  <td valign="top">0.8</td>
+  <td valign="top">2005/02/04</td>
+  <td valign="top">Readjusted sequence of arguments to the one described in the documentation. Thanks to <a href="http://blog.deklein.de">Thomas</a> </td>
+  </tr>
+
+<tr>
+  <td valign="top">0.7</td>
+  <td valign="top">2005/02/03</td>
+  <td valign="top">Renamed plugin to get-recent-comments, to make it possible to use the subversion system at www.wp-plugins.org<br>
+
+  Allow to specify your own formatting in the function call</td>
+  </tr>
+
+<tr>
+  <td valign="top">0.5</td>
+  <td valign="top">2005/01/02</td>
+  <td valign="top">Removed superfluous &lt;/p&gt;</td>
+</tr>
+
+<tr>
+  <td valign="top">0.4</td>
+  <td valign="top">2004/12/19</td>
+  <td valign="top">Use function arguments for displaying HTML before and after the comment<br>Make number of comments and number of characters also function arguments</td>
+</tr>
+
+<tr>
+  <td valign="top">0.3</td>
+  <td valign="top">2004/12/08</td>
+  <td valign="top">Link to permalink of comment<br>Wrap very long strings</td>
+</tr>
+
+<tr>
+  <td valign="top">0.2</td>
+  <td valign="top">&nbsp;</td>
+  <td valign="top">Donât show comments that are not approved</td>
+</tr>
+
+<tr>
+  <td valign="top">0.1</td>
+  <td valign="top">2004/11/03</td>
+  <td valign="top">Initial release</td>
+</tr>
+
+</tbody></table>
+  <p>Thanks to all who sent  bug reports and ideas for improvements. Please send me a mail if I forgot you to mention here.</p>
Index: /afridex/plugins/get-recent-comments 2/readme.txt
===================================================================
--- /afridex/plugins/get-recent-comments 2/readme.txt (revision 21)
+++ /afridex/plugins/get-recent-comments 2/readme.txt (revision 21)
@@ -0,0 +1,227 @@
+=== Get Recent Comments ===
+Tags: comments, widget
+Requires at least: 1.5
+Tested up to: 2.3
+Stable tag: trunk
+
+Display the most recent comments or trackbacks with your own formatting in the sidebar. 
+
+== Description ==
+
+This plugin shows excerpts of the latest comments and/or trackbacks in your
+sidebar. You have comprehensive control about their appearance. This ranges
+from the number of comments, the length of the excerpts up to the html layout.
+You can let the plugin order the comments by the corresponding post, or simply
+order them by date. The plugin can (optionally) separate the
+trackbacks/pingbacks from the comments. It can ignore comments to certain
+categories, and it offers support for gravatars. It only gives extra work to
+the database, when actually a new comment arrived. You can filter out
+unwanted pingbacks, which originate from your own blog. And it is a widget.
+
+You might want to have a look in the [changelog](http://blog.jodies.de/archiv/2004/11/13/recent-comments/2/#changelog).
+
+*Feature List*
+
+* Highly configurable via WordPress admin interface.
+* Support for WordPress 1.5, 2.0, 2.1, 2.2 and 2.3
+* Adjustable layout by macros.
+* Handles trackbacks and comments in separate lists, or in one combined list.
+* Widget support
+* Caches the output
+* Order comments by date, or by posting
+* Support for [gravatars](http://www.gravatar.com/).
+* Option to exclude comments to posts in certain categorys
+* Doesnât show pingbacks originating from own blog
+* There is a special version for lyceum multiblog installations: http://blog.jodies.de/blog/get-recent-comments/lyceum/
+* Supports [Hannah Grayâs](http://geekgrl.net/) [Profile Pics Plugin](http://geekgrl.net/2007/01/02/profile-pics-plugin-release/)
+
+== Installation ==
+1. Upload `get-recent-comments.php` to the `/wp-content/plugins/` directory.
+2. Activate `Get Recent Comments` through the 'Plugins' menu in WordPress.
+3. What to do now depends on how up to date your theme is:
+
+    **Modern theme with widget support**
+
+    The plugin is a [widget](http://automattic.com/code/widgets/). If your
+theme supports widgets, and you have installed the [widget
+plugin](http://wordpress.org/extend/plugins/widgets/), adding the plugin to the
+sidebar is easy: Go to the presentation menu and drag and drop the widget into
+the sidebar ([Screenshot](http://wordpress.org/extend/plugins/get-recent-comments/screenshots/)). Don't forget the Get Recent Trackbacks box. And you might want to
+change the title. All done.
+
+    **Old school theme without widget support**
+
+    You need to insert the following code snippet into the sidebar template.   
+*wp-content/themes/&lt;name of theme&gt;/sidebar.php*
+
+        <?php if (function_exists('get_recent_comments')) { ?>
+        <li><h2><?php _e('Recent Comments:'); ?></h2>
+              <ul>
+              <?php get_recent_comments(); ?>
+              </ul>
+        </li>
+        <?php } ?>   
+         
+        <?php if (function_exists('get_recent_trackbacks')) { ?>
+        <li><h2><?php _e('Recent Trackbacks:'); ?></h2>
+              <ul>
+              <?php get_recent_trackbacks(); ?>
+              </ul>
+        </li>
+        <?php } ?>
+
+== Contributors/Changelog ==
+
+Many users of the plugin gave feedback and contributed their ideas. They are
+referenced in the [changelog](http://blog.jodies.de/archiv/2004/11/13/recent-comments/2/#changelog):
+
+
+    Version Date       Changes
+    2.0.2   2007/09/25 Fix: Plugin was not compatible to WordPress
+                       2.0.11 any more. Thank you to Stephan for
+                       reporting the bug.
+
+    2.0.1   2007/09/24 Added switch on the categories page, which
+                       reverses the selection. It is now possible to
+                       include or exclude categories.
+
+    2.0     2007/09/24 New code for fetching the data: 1. Instead of
+                       one expensive database query we now use two
+                       or more cheap queries. Thanks to mirra, who
+                       reported the problem. And again thank you to
+                       the people mentioned in changlog 1.4, where
+                       the cache was introduced for the same
+                       (performance-) problems on big blogs. 2. This
+                       also fixed a bug, which lead to too less than
+                       requested comments in lists, ordered by post.
+                       Thanks to Johanna and FrÃ©dÃ©ric for reporting
+                       and documenting this. Changed the css in the
+                       admin gui, to work around a display issue
+                       with Tiger Admin. Thank you, Andi, for
+                       finding this. Added %time_since macro, which
+                       displays the time since the comment was
+                       posted. Thanks to Imran and Keith for
+                       sugesting (something like) this (very long
+                       ago). Admin interface: Added switch fpr
+                       turning on and off smileys. Thank you, panos,
+                       for requesting this feature. Support for
+                       Custom Smileys Plugin. Thanks to Henry for
+                       suggesting this. Fix: Username was not
+                       displayed as "Anonymous", if commentor left
+                       no name. Thanks to Pixelation for reporting
+                       this. Added support for WordPress 2.3. It
+                       will drop the post2cat table. Changed plugin
+                       to new taxonomy scheme. A *great* thank you
+                       goes to Lakatos Zsolt, who provided a
+                       complete patch for get-recent-comments-1.5.6,
+                       which made it very easy for me to understand
+                       how 2.0-beta10 had to be changed. Thank you
+                       also to xelios, Ville and Kretzschmar who
+                       warned me, that WordPress 2.3 will break the
+                       old plugin code.
+
+    1.5.5   2007/03/26 Added support for malyfred's Polyglot Plugin.
+                       Requested by Torben.
+    1.5.4   2007/02/01 Use full pingback_author as %comment_author
+                       (instead of 'Unknown', if the pingback parser
+                       fails to recognize the pingback_author.
+                       Thanks again to Gant who found this in his
+                       blog. Added %author_url_href macros, which
+                       allows to generate inactive links, if the
+                       commentator did not leave an url. This was
+                       wished (in part long ago) by beej, carl,
+                       FilSchiesty and SwB.
+                       Added %profile_picture macro, which supports
+                       Hannah Gray's Profile Pics Plugin. Thank you
+                       for the idea and your help, Markus
+    1.5.3   2007/01/15 Refresh cache, when a comment is approved by
+                       moderator. Problem found by Gant. Thank you!
+    1.5.2   2007/01/05 Added option for excluding comments from blog
+                       authors. Suggested by This is Zimbabwe, Slim,
+                       marilyn's shampoo and Igor M.
+    1.5.1   2006/12/29 Store the cache base64 encoded. There seems
+                       to be a problem with the unserialization of
+                       multibyte characters. Thanks to priv, who
+                       reported the problem and suggested the
+                       encoding.
+                       After upgrading to this version you should
+                       trigger a regeneration of the cache by adding
+                       a comment somewhere.
+    1.5     2006/12/27 New pingback parser
+                       Stop losing html entities and tags in the
+                       post titles and comments by using
+                       wptexturize. Thanks to ejm (again!) and
+                       mobius for reporting the problem and making
+                       suggestions.
+                       Bugfix in widget code: Error, when trackbacks
+                       came before comments
+    1.4     2006/12/24 The plugin is a widget now. Thanks to
+                       herrmueller and Thomas de Klein for
+                       suggesting this feature.
+                       Cache the output in order to reduce the
+                       database impact of the plugin. Thanks to the
+                       following people for reporting the poor
+                       performance and making suggestions to solve
+                       the problem: Brandon Stone, King of Fools,
+                       Robert Basic and especially CountZero.
+                       Option to combine comments and trackbacks in
+                       one list (requested by Maniac and die
+                       produzentin)
+                       Allow to Group comments by their posting
+                       (requested by eyolf)
+                       Allow limit of comments per post (suggested
+                       by Thomas)
+                       Use Wordpress 2.1 compatible database
+                       variables. Thanks to spencerp, for reporting
+                       and fixing.
+                       Bugfix: Wrong key used in gravatar hash
+                       (Thank you, Hamzeh N., for finding and fixing
+                       this).
+                       Updated the stylesheets to the look of
+                       wordpress 2.x.
+                       Added two macros: %comment_type and
+                       %post_counter.
+                       Use less option variables in db.
+                       Updated instructions page.
+                       Dropped support for Wordpress 1.2
+    1.3.1   2006/12/11 Fixes for problems with wordpress running
+                       under windows.
+    1.3     2006/11/26 Fixes for problems with php5.
+    1.2     2005/09/15 Prevent pingbacks from own blog. Thanks to
+                       Matt for the idea and support!
+                       To use the feature, go to the trackbacks
+                       configuration and enter the address of your
+                       webserver.
+    1.0     2005/03/21 Also show comments to static pages. (They are
+                       new in WP 1.5). Thanks to maza for the hint.
+    0.9     2005/03/20 Introduced admin gui. Handle trackbacks
+                       different than comments. Replaced most
+                       regular expressions with basic string
+                       operations. Dedicated macro for posting time.
+                       Requested by Zonekiller
+    0.8     2005/02/04 Readjusted sequence of arguments to the one
+                       described in the documentation. Thanks to
+                       Thomas
+    0.7     2005/02/03 Renamed plugin to get-recent-comments, to
+                       make it possible to use the subversion system
+                       at www.wp-plugins.org
+                       Allow to specify your own formatting in the
+                       function call
+    0.5     2005/01/02 Removed superfluous </p>
+    0.4     2004/12/19 Use function arguments for displaying HTML
+                       before and after the comment
+                       Make number of comments and number of
+                       characters also function arguments
+    0.3     2004/12/08 Link to permalink of comment
+                       Wrap very long strings
+    0.2                Donât show comments that are not approved
+    0.1     2004/11/03 Initial release
+
+Thanks to all who sent bug reports and ideas for improvements.
+Please send me a mail if I forgot you to mention here.
+
+
+== Screenshots ==
+1. Activation of widget
+2. Administration Interface
+
Index: /afridex/plugins/get-recent-comments 2/README
===================================================================
--- /afridex/plugins/get-recent-comments 2/README (revision 21)
+++ /afridex/plugins/get-recent-comments 2/README (revision 21)
@@ -0,0 +1,159 @@
+Installation
+~~~~~~~~~~~~
+
+1. Drop the file in wordpress/wp-content/plugins
+2. Activate the plugin the WordPress Plugin Management
+
+If you have WordPress 1.2.x go to section WordPress 1.2.x
+
+If you have WordPress 1.5 continue:
+
+WordPress 1.5
+~~~~~~~~~~~~~
+
+3. Insert one of the following code snippets into the sidebar
+template. Which one is the right depends on the theme you are using. There are
+two functions:
+
+get_recent_comments()   - Show comments
+get_recent_trackbacks() - Show trackbacks
+
+---------------------------------------------------------------
+Default Theme: wp-content/themes/default/sidebar.php 
+
+   <?php if (function_exists('get_recent_comments')) { ?>
+   <li><h2><?php _e('Recent Comments:'); ?></h2>
+        <ul>
+        <?php get_recent_comments(); ?>
+        </ul>
+   </li>
+   <?php } ?>   
+
+   <?php if (function_exists('get_recent_trackbacks')) { ?>
+   <li><h2><?php _e('Recent Trackbacks:'); ?></h2>
+        <ul>
+        <?php get_recent_trackbacks(); ?>
+        </ul>
+   </li>
+   <?php } ?>
+---------------------------------------------------------------
+Classic Theme: wp-content/themes/classic/sidebar.php 
+
+   <?php if (function_exists('get_recent_comments')) { ?>
+   <li><?php _e('Recent Comments:'); ?>
+        <ul>
+        <?php get_recent_comments(); ?>
+        </ul>
+   </li>
+   <?php } ?>
+
+   <?php if (function_exists('get_recent_trackbacks')) { ?>
+   <li><?php _e('Recent Trackbacks:'); ?>
+        <ul>
+        <?php get_recent_trackbacks(); ?>
+        </ul>
+   </li>
+   <?php } ?>
+---------------------------------------------------------------
+
+4. (Optional) Set options in the admin gui:
+Options/Recent Comments   
+
+You can configure the number of Comments and Trackbacks, that are shown.  And
+you can set the length of the cited comments and specify a maximum word length.
+Word longer than this value are wrapped, preventing them from damaging your
+layout.
+
+Using macros you can customize the format of the cited comments and trackbacks.
+%comment_excerpt The text of the comment. It might get shorted to the number of
+                 characters you entered in "Long comments are chopped off at..."
+
+%comment_link    The URL to the cited comment.
+
+%comment_author  The name, the commenter entered in the comment form. If she
+                 left the field empty, the name is "Anonymous".
+
+%comment_date    The date, when the comment was posted in the style you configured
+                 as default date format.
+
+%comment_time    The time, when the comment was posted
+
+%author_url      The URL, the comment author left in the comment form, or if the
+		 comment is a trackback, the URL of the site that issued the
+                 trackback.
+
+%post_title      The title of the posting that was commented.
+
+%post_link       The URL of the posting that was commented.
+
+%post_date       The date when the commented posting was published.
+
+%trackback_title Only applicable in trackbacks: The title of the trackback. It
+		 might get shorted to the number of characters you entered in
+                 "Long trackbacks are chopped off at..."
+
+WordPress 1.2.x
+~~~~~~~~~~~~~~~
+
+3. Write the following piece of code into your template (index.php)
+                  
+                  
+<li id="recent_comments"><?php _e('Recent Comments:'); ?>
+        <ul>
+        <? get_recent_comments(); ?>
+        </ul>
+</li>
+
+Done. If you want more, read on... 
+
+WordPress 1.2.x Advanced configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+4. (Optional) 
+   You can specify one to three arguments to the function. It is possible to
+   name only one argument or only two. Look at the following examples.
+   
+   The arguments have the following meaning: 
+   1. Number of comments to display.  
+   2. Maximum Number of characters in a comment excerpt
+   3. Formatting
+
+   If you don't specify arguments the plugin defaults to 5 comments, 120
+   characters per comment and a standard formatting.
+   
+   The format is a piece of html code where you use tags that will be replaced
+   by the actual data:
+
+   %comment_excerpt - Shortened comment. 
+   %comment_link    - Link to the comment. Something like:
+                      http://blog.example.com/wp/archives/2005/02/03/this-is-a-test/#comment-523
+   %comment_author  - Name left by the commenter
+   %comment_date    - Date of comment
+   %comment_time    - Time of comment
+   %post_title      - Title of the posting 
+   %post_link       - Link to the posting (and not the comment). Something like:
+                      http://blog.example.com/wp/archives/2005/02/03/this-is-a-test/
+   %post_date       - Date of the posting
+    
+
+WordPress 1.2.x Examples
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Show the 8 most recent comments
+<?php get_recent_comments(8); ?>
+
+Show the 8 most recent comments, with a maximum of 100 characters per comment:
+<?php get_recent_comments(8,100); ?>
+
+The same with your own formatting 
+<?php get_recent_comments(8,100,'<li><a href="%comment_link">%comment_author</a>: %comment_excerpt</li>');
+
+
+
+Feedback
+~~~~~~~~
+
+krischan@jodies.de
+http://blog.jodies.de
+
+
Index: /afridex/plugins/Flutter/RCCWP_Options.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_Options.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_Options.php (revision 21)
@@ -0,0 +1,37 @@
+<?php
+class RCCWP_Options
+{
+	function Delete()
+	{
+		delete_option(RC_CWP_OPTION_KEY);
+	}
+	
+	//function Update($hideWritePost, $hideWritePage, $promptEditingPost, $assignToRole, $useSnipshot, $enableEditnplace, $defaultCustomWritePanel, $enableHTMLPurifier,$tidyLevel)
+	function Update($options)
+	{
+		$options = serialize($options);
+		update_option(RC_CWP_OPTION_KEY, $options);
+	}
+	
+	function Get($key = null)
+	{
+		if (get_option(RC_CWP_OPTION_KEY) == "") return "";
+		if (is_array(get_option(RC_CWP_OPTION_KEY)))
+			$options = get_option(RC_CWP_OPTION_KEY);
+		else
+			$options = unserialize(get_option(RC_CWP_OPTION_KEY));
+
+		if (!empty($key))
+			return $options[$key];
+		else
+			return $options;
+	}
+
+	function Set($key, $val)
+	{
+		$options = RCCWP_Options::Get();
+		$options[$key] = $val;
+		RCCWP_Options::Update($options);
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_CustomWriteModule.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CustomWriteModule.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CustomWriteModule.php (revision 21)
@@ -0,0 +1,375 @@
+<?php
+
+/**
+ * @package FlutterDatabaseObjects
+ */
+
+/**
+ * Create/edit/delete modules 
+ * @package FlutterDatabaseObjects
+ */
+
+class RCCWP_CustomWriteModule
+{
+	
+	/**
+	 * Get all modules
+	 *
+	 * @return array of objects containing all modules.
+	 */
+	function GetCustomModules()
+	{
+		global $wpdb;
+	
+		$sql = "SELECT * FROM " . RC_CWP_TABLE_MODULES;
+		
+		$sql .= " ORDER BY name ASC";
+		$results = $wpdb->get_results($sql);
+		if (!isset($results)) 
+			$results = array();
+
+		return $results;
+	}
+	
+	
+	/**
+	 * Create a new module
+	 *
+	 * @param string $name module name, the name will be sanitized.
+	 * @param string $description module description
+	 * @param boolean $create_folders whether to create a folder for the module containing sample template
+	 * @return the id of the module or -1 if the module name already exist
+	 */
+
+	function Create($name, $description, $create_folders = true)
+	{
+		require_once('RC_Format.php');
+		global $wpdb;
+		
+		$special_chars = array (' ','`','"','\'','\\','/'," ","#","$","%","^","&","*","!","~","â","\"","â","'","=","?","/","[","]","(",")","|","<",">",";","\\",",");
+		$name = str_replace($special_chars,'',$name);
+
+		//Make sure the module doesn't already exist
+		$sql = "SELECT * FROM " . RC_CWP_TABLE_MODULES .
+			" WHERE name = '" . $name . "'";
+		if ($wpdb->get_row($sql))	return -1;
+
+		$sql = sprintf(
+			"INSERT INTO " . RC_CWP_TABLE_MODULES .
+			" (name, description)" .
+			" values" .
+			" (%s, %s)", 
+			RC_Format::TextToSql($name),
+			RC_Format::TextToSql($description)
+		);
+			
+		$wpdb->query($sql);
+		$customWriteModuleId = $wpdb->insert_id;
+
+		if ($create_folders == false)
+			return $customWriteModuleId;
+
+		// Create template folder
+		$moduleTemplateFolder = dirname(__FILE__)."/modules/".$name;
+		if (@mkdir($moduleTemplateFolder, 0777)){
+			@chmod($moduleTemplateFolder, 0777);
+			if (mkdir($moduleTemplateFolder.'/templates', 0777)){
+				@chmod($moduleTemplateFolder.'/templates', 0777);
+				if (mkdir($moduleTemplateFolder.'/templates/default', 0777)){	
+					@chmod($moduleTemplateFolder.'/templates/default', 0777);
+					RCCWP_CustomWriteModule::CreateTemplateFiles($moduleTemplateFolder.'/templates/default/small', $name);
+				}
+			}
+
+			//Create basic configuration file 
+			$configurationFileContent = 
+<<<EOF
+	<?xml version="1.0"?>
+	<canvas>
+		<function>
+			<author>
+			Flutter
+			</author>
+			<description>
+			This is module $name.
+			</description>
+			<variables>
+				<variable>
+					<name>
+					the_text
+					</name>
+					<description>
+					Text to display (HTML allowed)
+					</description>
+					<type>
+					Textarea
+					</type>
+					<default>
+					<![CDATA[<p>This is some text generated by $name module. </p>]]>
+					</default>
+				</variable>
+			</variables>
+		</function>
+	</canvas>
+EOF;
+
+	
+			file_put_contents ($moduleTemplateFolder."/configure.xml", $configurationFileContent);	
+			@chmod($moduleTemplateFolder."/configure.xml", 0666);
+		}
+		
+		return $customWriteModuleId;
+	}
+
+	/**
+	 * @access private 
+	 */
+	function CreateTemplateFiles($templatePath, $name){
+			$defaultFileContent = 
+<<<EOF
+	<?php
+		echo '<div class="block">';
+		echo mod_var('the_text');
+		echo '</div>';
+	?>
+EOF;
+
+		if (mkdir($templatePath, 0777)){
+			@chmod($templatePath, 0777);
+			file_put_contents ($templatePath."/default.php", $defaultFileContent);
+			@chmod($templatePath."/default.php", 0666);
+		}
+	}
+	
+	/**
+	 * Deletes a module and all its child fields as well as the module folder.
+	 *
+	 * @param integer $customWriteModuleId module id.
+	 */
+	
+	function Delete($customWriteModuleId = null)
+	{
+		include_once ('RCCWP_CustomGroup.php');
+		if (isset($customWriteModuleId))
+		{
+			global $wpdb;
+			
+			$customWriteModule = RCCWP_CustomWriteModule::Get($customWriteModuleId);
+			$customWriteModuleName = $customWriteModule->name;
+		  	
+  			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_MODULES .
+				" WHERE id = %d",
+				$customWriteModuleId
+				);
+			$wpdb->query($sql);
+
+			// Remove template folder
+			if (!empty($customWriteModuleName)){
+				$moduleTemplateFolder = dirname(__FILE__)."/modules/".$customWriteModuleName;
+				RCCWP_CustomWriteModule::remove_dir($moduleTemplateFolder);
+			}
+
+			// Remove canvas info
+			include_once "canvas-import_functions.php";
+			canvas_delete_block_by_mod_ID($customWriteModuleId);
+			
+		}
+		
+	}
+
+	/**
+	 * @access private 
+	 */
+	function remove_dir($dir)
+	{
+		if(is_dir($dir))
+		{
+		$dir = (substr($dir, -1) != "/")? $dir."/":$dir;
+		$openDir = opendir($dir);
+		while($file = readdir($openDir))
+		{
+			if(!in_array($file, array(".", "..")))
+			{
+			if(!is_dir($dir.$file))
+				@unlink($dir.$file);
+			else
+				RCCWP_CustomWriteModule::remove_dir($dir.$file);
+			}
+		}
+		closedir($openDir);
+		@rmdir($dir);
+		}
+	}
+	
+	/**
+	 * Retrieves the basic information of the module. 
+	 *
+	 * @param integer $customWriteModuleId module id
+	 * @return an object containing id, name and description
+	 */
+	function Get($customWriteModuleId)
+	{
+		global $wpdb;
+	
+		$sql = "SELECT * FROM " . RC_CWP_TABLE_MODULES .
+			" WHERE id = " . (int)$customWriteModuleId;
+		
+		$results = $wpdb->get_row($sql);
+		
+		return $results;
+	}
+
+	/**
+	 * Retrieves the basic information of the module given module name.
+	 *
+	 * @param string $customWriteModuleName module name
+	 * @return an object containing id, name and description
+	 */
+	function GetByName($customWriteModuleName)
+	{
+		global $wpdb;
+	
+		$sql = "SELECT * FROM " . RC_CWP_TABLE_MODULES .
+			" WHERE name = '$customWriteModuleName'";
+		
+		$results = $wpdb->get_row($sql);
+		
+		return $results;
+	}
+	
+	/**
+	 * Updates the basic information of a module
+	 *
+	 * @param integer $customWriteModuleId the id of the module that will be updated
+	 * @param string $name new name
+	 * @param string $description new description
+	 * @return the id of the module or -1 if the module name already exist
+	 */
+	
+	function Update($customWriteModuleId, $name, $description)
+	{
+		require_once('RC_Format.php');
+		global $wpdb;
+		
+		//$capabilityName = RCCWP_CustomWriteModule::GetCapabilityName($name);
+		$special_chars = array (' ','`','"','\'','\\','/'," ","#","$","%","^","&","*","!","~","â","\"","â","'","=","?","/","[","]","(",")","|","<",">",";","\\",",");
+		$name = str_replace($special_chars,'',$name);
+
+		//Make sure the module doesn't already exist
+		$sql = "SELECT * FROM " . RC_CWP_TABLE_MODULES .
+			" WHERE name = '" . $name . "' AND id <> $customWriteModuleId";
+		if ($wpdb->get_row($sql))	return -1;
+
+		//Get old name
+		$sql = "SELECT name FROM " . RC_CWP_TABLE_MODULES .
+			" WHERE id = $customWriteModuleId";
+		$originalModName = $wpdb->get_var($sql);
+		$oldModuleTemplateFolder = dirname(__FILE__)."/modules/".$originalModName;
+
+		// Update name
+		$sql = sprintf(
+			"UPDATE " . RC_CWP_TABLE_MODULES .
+			" SET name = %s,".
+			"     description = %s".
+			" where id = %d",
+			RC_Format::TextToSql($name),
+			RC_Format::TextToSql($description),
+			$customWriteModuleId );
+		
+		$wpdb->query($sql);
+
+		//Rename module folder
+		$newModuleTemplateFolder = dirname(__FILE__)."/modules/".$name;
+		rename($oldModuleTemplateFolder, $newModuleTemplateFolder);
+		
+		return $customWriteModuleId;
+		
+	}
+
+	
+	/**
+	 * Import a module given the zip file path. Importing a module means inserting it in
+	 * the database and copying its folder to modules folder. If a module with the same
+	 * name already exists, the function will append a number to the module name. You must 
+	 * have either php ZipArchive extension or unzip program installed.
+	 *
+	 * @param string $zipFilePath the full path of the zip file
+	 * @param string $moduleName the module name, if this value if false, the function will
+	 * 							use the zip filename as the module name. The default value is false
+	 * @return the module id, or false in case of error.
+	 */
+
+	function Import($zipFilePath, $moduleName = false)
+	{
+		include_once('RCCWP_CustomGroup.php');
+		include_once('RCCWP_CustomField.php');
+		include_once('RCCWP_CustomWritePanel.php');
+		include_once('RCCWP_Application.php');
+		include_once('RCCWP_CustomWritePanel.php');
+
+		if (!$moduleName)
+			//use zip filename
+			$moduleName = basename($zipFilePath, ".zip");
+
+		if ($moduleName == '') return false;
+
+		// Append a number if the module already exists,
+		$i = 1;
+		$newModuleName = $moduleName;
+		while (file_exists(FLUTTER_MODULES_DIR.$newModuleName)){
+			$newModuleName = $moduleName. "_" . $i++;
+		}
+		$moduleName = $newModuleName;
+
+
+		mkdir(FLUTTER_MODULES_DIR.$moduleName);
+		chmod(FLUTTER_MODULES_DIR.$moduleName, 0777);
+
+		$destPath = FLUTTER_MODULES_DIR.$moduleName.DIRECTORY_SEPARATOR;
+
+		if (class_exists('ZipArchive')){
+			$zip = new ZipArchive() ;
+
+			// open archive
+			if ($zip->open($zipFilePath) !== TRUE) {
+				die ("Could not open archive");
+			}
+			
+			// extract contents to destination directory
+			$zip->extractTo($destPath);
+			
+			// close archive
+			$zip->close();
+		}else{
+			if (RCCWP_Application::CheckDecompressionProgramUnzip()) 
+				$command = "unzip $zipFilePath -d ". $destPath;
+			else{
+				die("Cannot find unzip program!");
+			}
+			exec($command, $out, $err);
+		}
+		$moduleInfo_imported_data = unserialize(file_get_contents($destPath.'module_info.exp'));
+		
+		//Import module
+		$moduleID = RCCWP_CustomWriteModule::Create($moduleName, $moduleInfo_imported_data['moduleinfo']->description, false);
+		
+		//Import any panels
+		if ($handle = @opendir($destPath)) {
+			while (false !== ($file = readdir($handle))) { 
+				if (substr($file, 0, 1) == "_"){
+					$panelName = basename(substr($file, 1), ".pnl");
+					RCCWP_CustomWritePanel::Import($destPath.$file, $panelName);
+				}
+			}
+		}
+		
+		//Create Duplicates
+		foreach($moduleInfo_imported_data['duplicates'] as $moduleDuplicate){
+			RCCWP_ModuleDuplicate::Create($moduleID, $moduleDuplicate->duplicate_name);
+		}		
+		
+		return $moduleID;
+	}
+}
+?>
Index: /afridex/plugins/Flutter/canvas-plugin_form.php
===================================================================
--- /afridex/plugins/Flutter/canvas-plugin_form.php (revision 21)
+++ /afridex/plugins/Flutter/canvas-plugin_form.php (revision 21)
@@ -0,0 +1,368 @@
+<?php
+
+/*
+
+___Canvas Plugin Form___________________________________________
+
+Outputs the Lightbox form for plugins, building individual
+form elements based upon the plugin definitions.
+
+________________________________________________________________
+
+*/
+
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can('edit_posts')))
+	die("Athentication failed!");
+
+
+global $wpdb, $canvas;
+
+$zone = $_GET['zone'];
+$position = $_GET['position'];
+$block_id = $_GET['block_id'];
+$counter = 0;
+
+// Get module info from database
+$module_id= $wpdb->get_var("SELECT module_id FROM ".$canvas->main." WHERE block_id = $block_id");
+if ( $wpdb->get_var("SELECT zone FROM ".$canvas->main." WHERE block_id = $block_id") != "shelf"){
+	$template_size = $wpdb->get_var("SELECT template_size FROM ".$canvas->main." WHERE block_id = $block_id");
+	$template_name = $wpdb->get_var("SELECT template_name FROM ".$canvas->main." WHERE block_id = $block_id");
+}
+
+//if ('' == $template_name) $template_name = 'default';
+//if ('' == $template_size) $template_size = '-1';
+
+
+$module_template_size_div_id = 'canvas_'.$zone.'_'.$position.'_'.$block_id.'_module_size';
+$module_template_name_div_id = 'canvas_'.$zone.'_'.$position.'_'.$block_id.'_module_template_name';
+
+// Get module name
+include_once('RCCWP_CustomWriteModule.php');
+
+$customWriteModule = RCCWP_CustomWriteModule::Get($module_id);
+$module_name = $customWriteModule->name;
+
+// Get plugin sizes
+$moduleTemplatesFolder = dirname(__FILE__)."/modules/".$module_name."/templates";
+$moduleTemplateFolder = dirname(__FILE__)."/modules/".$module_name."/templates/".$template_name;
+
+// Load module template sizes
+$otherSizesStr = "";	
+if ($handle = @opendir($moduleTemplateFolder)) {
+	while (false !== ($file = readdir($handle))) { 
+		if (is_numeric($file)){
+			if ($template_size == $file)
+				$otherSizesStr = $otherSizesStr."<option selected='selected' value='".$file."'>".$file."</option>";	
+			else
+				$otherSizesStr = $otherSizesStr."<option value='".$file."'>".$file."</option>";	
+			
+		}
+		else{
+			$t_size_val = 0;
+			switch($file){
+				case "small":
+					$t_size_val = -1;
+					break;
+				case "medium":
+					$t_size_val = -2;
+					break;
+				case "large":
+					$t_size_val = -3;
+					break;
+				case "full":
+					$t_size_val = -4;
+					break;
+
+			}
+			if ($t_size_val<0){
+				if ($template_size == $t_size_val)
+					$otherSizesStr = $otherSizesStr."<option selected='selected' value='".$t_size_val."'>".$file."</option>";	
+				else
+					$otherSizesStr = $otherSizesStr."<option value='".$t_size_val."'>".$file."</option>";	
+			}
+		}
+			
+	}
+
+	closedir($handle);
+}
+
+// Load module template names
+$templatesNamesStr = "";	
+if ($handle = @opendir($moduleTemplatesFolder)) {
+	while (false !== ($file = readdir($handle))) { 
+		if ($file!= "." && $file!="..")
+			if ($template_name == $file)
+				$templatesNamesStr = $templatesNamesStr."<option selected='selected' value='".$file."'>".$file."</option>";	
+			else
+				$templatesNamesStr = $templatesNamesStr."<option value='".$file."'>".$file."</option>";		
+	}
+
+	closedir($handle);
+}
+
+
+?>
+<div class="lbContent">
+<?php
+	global $wpdb, $canvas;
+	$variables = $wpdb->get_results("SELECT * FROM ".$canvas->variables." WHERE parent = '$block_id' ORDER BY variable_id");
+	$title = $wpdb->get_results("SELECT * FROM ".$canvas->main." WHERE block_id = '$block_id'");
+
+	// Get module name
+	include_once('RCCWP_CustomWriteModule.php');
+	$customWriteModule = RCCWP_CustomWriteModule::Get($title[0]->module_id);
+	if ($title[0]->duplicate_id ==0 )
+		$title[0]->module_title = $customWriteModule->name;
+	else
+		$title[0]->module_title = $wpdb->get_var("SELECT duplicate_name FROM ".$canvas->duplicates." WHERE duplicate_id = ".$title[0]->duplicate_id);
+
+	$content = '<h3>'.$title[0]->module_title.'';
+	if($title[0]->author != '' && $title[0]->uri != '')
+		$content .= '<span>by <a target="new" href="'.$title[0]->uri.'">'.$title[0]->author.'</a></span>';
+	if($title[0]->description != '')
+		$content .= '<p>'.$title[0]->description.'</p>';
+
+
+	$F = "\$F";	
+	$module_template_name_id = 'canvas_'.$zone.'_'.$position.'_'.$block_id.'_module_template_name';
+
+	$ListboxAJAX = CANVASURI. "ajax/canvas-populate-listbox.php?mod_name=".$module_name."&template_name=";
+
+	$content .= 
+<<<EOF
+
+	<input type="hidden" id="updated_module_template_size_id" value="$module_template_size_div_id" />
+	<input type="hidden" id="updated_module_template_name_id" value="$module_template_name_div_id" />
+
+	<script language="JavaScript" type="text/javascript" >
+		curr_module_size = $F('$module_template_size_div_id');
+		curr_module_template_name = $F('$module_template_name_div_id');
+
+		function UpdateModuleBlock(){
+			$('$module_template_size_div_id').value = $('template_name').value;
+			$('$module_template_name_div_id').value = $('template_size').value;
+		}
+
+		function dynamicallyLoadSizeList(){ 
+			$('template_name').value = curr_module_template_name;
+			$("template_size").innerHTML = "<option>Loading ...</option>";
+			
+			var myAjax = new Ajax.Request('$ListboxAJAX' + curr_module_template_name + "&template_size=" + curr_module_size, { method:'post',
+				onSuccess: function(transport){
+					$("template_size").innerHTML = transport.responseText;
+					$("template_size").innerHTML = $("template_size").innerHTML + "<option>Hello</option>";
+				}
+			});
+		};
+
+EOF;
+
+	if ($template_name == ""){ //If the template name is not saved in the database, get the default template name stored in the module block
+		$content .= 
+<<<EOF
+		//Event.observe(window, 
+		//	'load', 
+		//	dynamicallyLoadSizeList ,
+		//	false);
+
+		dynamicallyLoadSizeList();
+
+EOF;
+	}
+	$content .= 
+<<<EOF
+
+
+		function changeTemplateSize(){
+			Event.observe($("template_name"), 'change',
+			function(){
+				var d = new Date();
+				var time = d.getTime();
+				$("template_size").innerHTML = "<option>Loading ...</option>";
+				var myAjax = new Ajax.Request('$ListboxAJAX' + $F("template_name") + '&time='+time, { method:'get',
+					onSuccess: function(transport){
+						var newContent = '<label for="template_size">Template Size:</label> <select  name="template_size" id="template_size" style="margin-left:11px;width:100px;font-size:11px;" onchange="Canvas.changePublishImg();">';
+						newContent += transport.responseText +" </select>";
+						$("wrap_template_size").innerHTML = newContent;
+					}
+				});
+	
+			}
+			);
+		}
+		
+		//Event.observe(window, 
+		//	'load', 
+		//	changeTemplateSize);
+		changeTemplateSize();
+
+		function saveAndReturn(){
+			lightbox.prototype.deactivate();
+			return false;
+		}
+		
+	
+	</script>
+
+	<a id="confirm" href="javascript:void(0)" class="lbAction" rel="deactivate" onclick="$('$module_size_div_id').value = $('template_name').value;$('$module_name_div_id').value = $('template_size').value;">Save and Return</a>
+	<a id="cancel" href="javascript:void(0)" class="lbAction" rel="cancel">Cancel</a></h3><div class="lbContent_inside">
+	
+	<form name="lightbox_form" action="" id="lightbox_form" onsubmit="lightbox.prototype.deactivate();return false;">
+		<label for="template_name">Template Name:</label> 
+		<select name="template_name" id="template_name" style="width:100px;font-size:11px;"  onchange="Canvas.changePublishImg();">
+			$templatesNamesStr
+		</select>
+		<br />
+		<div id="wrap_template_size">
+			<label for="template_size">Template Size:</label> 
+			<select  name="template_size" id="template_size" style="margin-left:11px;width:100px;font-size:11px;" onchange="Canvas.changePublishImg();">
+				$otherSizesStr
+			</select>
+		</div>
+		<br />
+		<hr />
+		<br />
+
+
+
+		
+EOF;
+	$content .= '<input type="hidden" name="parent" value="'.$block_id.'">'."\n";
+
+	foreach($variables as $variable) {
+		if(strtolower($variable->type) == 'text')
+			$content .= '<label class="textbox">'.$variable->description.': <input type="text" class="text" name="'.$variable->variable_name.'" value="'.$variable->value.'" /></label>'."\n";
+		elseif(strtolower($variable->type) == 'textarea')
+			$content .= '<label class="textbox">'.$variable->description.': <textarea rows="8" name="'.$variable->variable_name.'">'.htmlspecialchars($variable->value).'</textarea></label>'."\n";
+		elseif(strtolower($variable->type) == 'integer')
+			$content .= '<label class="textbox">'.$variable->description.': <input type="text" size="3" name="'.$variable->variable_name.'" value="'.$variable->value.'" /></label>'."\n";
+		elseif(strtolower($variable->type) == 'boolean') {
+			$content .= '<label class="boolean"><input type="checkbox" name="'.$variable->variable_name.'" value="1" ';
+			if($variable->value == 1) $content .= 'checked ';
+			$content .= '/>&nbsp;'.$variable->description.'</label>'."\n";
+		} elseif(strtolower($variable->type) == 'radio') {
+			$content .= '<span><p>'.$variable->description.':</p>';
+			
+			$options = $wpdb->get_results("SELECT * FROM ".$canvas->options." WHERE var_id = '$variable->variable_id'");
+			
+			foreach($options as $option) {
+				// If is a Wordpress database variable...
+				if(preg_match('/(.*)\-\>(.*)\:(.*)/', $option->option_value, $matches)) {
+					$content .= canvas_database_plugin($matches, 'radio', $variable->value, $option->option_params);
+				} else {
+					$content .= '<label><input type="radio" name="'.$variable->variable_name.'" value="'.$option->option_value.'" ';
+					if ($option->option_value == $variable->value) $content .= ' checked="checked" ';
+					$content .= '/>&nbsp;'.$option->option_text.'</label>';
+				}
+			}
+			$content .= '</span>'."\n";
+		} elseif(strtolower($variable->type) == 'select') {
+			$content .= '<span><p>'.$variable->description.':</p>';
+
+			$options = $wpdb->get_results("SELECT * FROM ".$canvas->options." WHERE var_id = '$variable->variable_id'");
+
+			$content .= '<select name="'.$variable->variable_name.'" id="'.$variable->variable_name.'">'."\n";
+			foreach($options as $option) {
+				// If is a Wordpress database variable...
+				if(preg_match('/(.*)\-\>(.*)\:(.*)/', $option->option_value, $matches)) {
+					$content .= canvas_database_plugin($matches, 'select', $variable->value, $option->option_params);
+				} else {
+					$content .= '<option value="'.$option->option_value.'" ';
+					if ($option->option_value == $variable->value) $content .= ' selected="selected" ';
+					$content .= '>&nbsp;'.$option->option_text."</option>\n";
+				}
+			}
+			$content .= '</select>'."\n";
+			$content .= '</span>'."\n";
+        } elseif(strtolower($variable->type) == 'image') {
+			$options = $wpdb->get_results("SELECT * FROM ".$canvas->options." WHERE var_id = '$variable->variable_id'");
+			$content .= '<p>'.$variable->description.':</p>';
+			$content .= '<input type="hidden" id="directory" value="'.$options[0]->option_value.'">';
+			$selected = '<input type="hidden" id="selected_image">';
+			$content .= '<input type="hidden" id="path" name="'.$variable->variable_name.'" value="'.$variable->value.'">';
+			$content .= '<div class="gallery">';
+			$files = scandirectory(ABSPATH.$options[0]->option_value);
+			foreach($files as $file) {
+				$class = '';
+				if(substr($file, -3) == 'jpg' || substr($file, -3) == 'gif' ||  substr($file, -3) == 'png') {
+					if($options[0]->option_value.$file == $variable->value) {
+						$class= 'class="selected_image"';
+						$selected = '<input type="hidden" id="selected_image" value="'.$file.'">';
+					}
+					list($width, $height) = @getimagesize(get_bloginfo('wpurl').'/'.$options[0]->option_value.$file);
+					if($width > 250) { $height = $height * 250 / $width; $width = 250; }
+					$content .= '<a href="javascript:void(0)" onclick="Canvas.gallerySwitch(\''.$file.'\')"><img '.$class.' id="'.$file.'" src="'.get_bloginfo('wpurl').'/'.$options[0]->option_value.$file.'" title="'.$file.'" alt="'.$file.'" style="height: '.$height.'px; width: '.$width.'px;" /></a>';
+				}
+			}
+			$content .= $selected;
+			$content .= '</div>';
+        } elseif(strtolower($variable->type) == 'list') {
+        	$listitems = explode('|', $variable->value);
+        	$content .= '<label class="textbox">'.$variable->description.' <small>(Note &#124; is an illegal character)</small></label>'."\n".'<span class="list" id="listlist">'."\n";
+        	foreach($listitems as $listitem) {
+        		$content .= '<span id="'.$variable->variable_name.$counter.'"><input type="text" class="text" name="'.$counter.'canvaslist_'.$variable->variable_name.'" value="'.htmlspecialchars($listitem).'" />'."\n";
+				$content .= '<a href="javascript:void(0)" onclick="Canvas.addListItem(this, \''.$variable->variable_name.'\')"><img src="'.CANVASURI.'images/list-duplicate.png" alt="Duplicate" title="Duplicate item"/></a>';
+				$content .= '<a href="javascript:void(0)" onclick="Canvas.removeListItem(this)"><img src="'.CANVASURI.'images/list-delete.png" alt="Delete" title="Delete item"/></a>';
+				$content .= '</span>';
+				$counter++;
+			}
+        	$content .= '</span>'."\n";
+	}
+	elseif(strtolower($variable->type) == 'fileupload'){
+		
+		$value = attribute_escape($variable->value);
+		$path = CANVASURI.'files_flutter/';
+		$valueRelative = $value;
+		$value = $path.$value;
+		
+		$varName = $variable->variable_name;
+		$iframePath = CANVASURI."RCCWP_upload.php?input_name=".$varName;
+		$content .= '<label class="textbox">'.$variable->description.': </label>'."\n";
+		if($valueRelative)
+			$content .= '(<a href="' . $value . '" target="_blank">View Current</a>)';
+
+		echo $content;
+		echo 
+<<<EOF
+		
+		
+		
+		<input tabindex="3" 
+			id="$varName" 
+			name="$varName" 
+			type="hidden" 
+			size="46"
+			onchange=""
+			value="$valueRelative"
+			/>
+		<p id="upload_progress_$varName" style="visibility:hidden;height:0px"></p>
+
+EOF;
+		?>
+
+		<?php
+		include_once( "RCCWP_SWFUpload.php" ) ;
+		RCCWP_SWFUpload::Body($varName, 0, 1, 10) ;
+ 		$content = "";
+	
+        } elseif(strtolower($variable->type) == 'gmodule') {
+        	//$variable->value WILL THIS BE THE XML FILE?
+        	$xmlfile = 'http://www.google.com/ig/modules/datetime.xml';
+        	$url = 'http://gmodules.com/ig/creator?synd=open&url='.$xmlfile;
+        	echo '<object id="gmodule_content" data="'.$url.'" type="text/html"></object>';
+        } elseif(strtolower($variable->type) == 'gmodule_script') {
+        	echo '<input type="text" id="gmodule_script" value="'.$variable->value.'">';
+        }
+	}
+	//if(count($variables) == 0) echo 'This plugin has no options (yes, it should be a block instead).';	
+
+	$content .= '<input type="hidden" id="listcount" name="listcount" value="'.$counter.'">'."\n";
+	$content .= '</form>'."\n";
+		
+	echo $content;
+
+?>
+</div>
+</div>
Index: /afridex/plugins/Flutter/phpthumb.gif.php
===================================================================
--- /afridex/plugins/Flutter/phpthumb.gif.php (revision 21)
+++ /afridex/plugins/Flutter/phpthumb.gif.php (revision 21)
@@ -0,0 +1,1168 @@
+<?php
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// GIF Util - (C) 2003 Yamasoft (S/C)
+// http://www.yamasoft.com
+// All Rights Reserved
+// This file can be freely copied, distributed, modified, updated by anyone under the only
+// condition to leave the original address (Yamasoft, http://www.yamasoft.com) and this header.
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// <gif>  = gif_loadFile(filename, [index])
+// <bool> = gif_getSize(<gif> or filename, &width, &height)
+// <bool> = gif_outputAsPng(<gif>, filename, [bgColor])
+// <bool> = gif_outputAsBmp(<gif>, filename, [bgcolor])
+// <bool> = gif_outputAsJpeg(<gif>, filename, [bgcolor]) - use cjpeg if available otherwise uses GD
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Original code by Fabien Ezber
+// Modified by James Heinrich <info@silisoftware.com> for use in phpThumb() - December 10, 2003
+// * Added function gif_loadFileToGDimageResource() - this returns a GD image resource
+// * Modified gif_outputAsJpeg() to check if it's running under Windows, or if cjpeg is not
+//   available, in which case it will attempt to output JPEG using GD functions
+// * added @ error-suppression to two lines where it checks: if ($this->m_img->m_bTrans)
+//   otherwise warnings are generated if error_reporting == E_ALL
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_loadFile($lpszFileName, $iIndex = 0)
+{
+	$gif = new CGIF();
+	if ($gif->loadFile($lpszFileName, $iIndex)) {
+		return $gif;
+	}
+	return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Added by James Heinrich <info@silisoftware.com> - December 10, 2003
+function gif_loadFileToGDimageResource($gifFilename, $bgColor = -1)
+{
+	if ($gif = gif_loadFile($gifFilename)) {
+
+		@set_time_limit(300);
+		// general strategy: convert raw data to PNG then convert PNG data to GD image resource
+		$PNGdata = $gif->getPng($bgColor);
+		if ($img = @ImageCreateFromString($PNGdata)) {
+
+			// excellent - PNG image data successfully converted to GD image
+			return $img;
+
+		} elseif ($img = $gif->getGD_PixelPlotterVersion()) {
+
+			// problem: ImageCreateFromString() didn't like the PNG image data.
+			//   This has been known to happen in PHP v4.0.6
+			// solution: take the raw image data and create a new GD image and plot
+			//   pixel-by-pixel on the GD image. This is extremely slow, but it does
+			//   work and a slow solution is better than no solution, right? :)
+			return $img;
+
+		}
+	}
+	return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_outputAsBmp($gif, $lpszFileName, $bgColor = -1)
+{
+	if (!isSet($gif) || (@get_class($gif) <> 'cgif') || !$gif->loaded() || ($lpszFileName == '')) {
+		return false;
+	}
+
+	$fd = $gif->getBmp($bgColor);
+	if (strlen($fd) <= 0) {
+		return false;
+	}
+
+	if (!($fh = @fopen($lpszFileName, 'wb'))) {
+		return false;
+	}
+	@fwrite($fh, $fd, strlen($fd));
+	@fflush($fh);
+	@fclose($fh);
+	return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_outputAsPng($gif, $lpszFileName, $bgColor = -1)
+{
+	if (!isSet($gif) || (@get_class($gif) <> 'cgif') || !$gif->loaded() || ($lpszFileName == '')) {
+		return false;
+	}
+
+	$fd = $gif->getPng($bgColor);
+	if (strlen($fd) <= 0) {
+		return false;
+	}
+
+	if (!($fh = @fopen($lpszFileName, 'wb'))) {
+		return false;
+	}
+	@fwrite($fh, $fd, strlen($fd));
+	@fflush($fh);
+	@fclose($fh);
+	return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_outputAsJpeg($gif, $lpszFileName, $bgColor = -1)
+{
+	// JPEG output that does not require cjpeg added by James Heinrich <info@silisoftware.com> - December 10, 2003
+	if ((strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') && (file_exists('/usr/local/bin/cjpeg') || `which cjpeg`)) {
+
+		if (gif_outputAsBmp($gif, $lpszFileName.'.bmp', $bgColor)) {
+			exec('cjpeg '.$lpszFileName.'.bmp >'.$lpszFileName.' 2>/dev/null');
+			@unLink($lpszFileName.'.bmp');
+
+			if (@file_exists($lpszFileName)) {
+				if (@fileSize($lpszFileName) > 0) {
+					return true;
+				}
+
+				@unLink($lpszFileName);
+			}
+		}
+
+	} else {
+
+		// either Windows, or cjpeg not found in path
+		if ($img = @ImageCreateFromString($gif->getPng($bgColor))) {
+			if (@ImageJPEG($img, $lpszFileName)) {
+				return true;
+			}
+		}
+
+	}
+
+	return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+function gif_getSize($gif, &$width, &$height)
+{
+	if (isSet($gif) && (@get_class($gif) == 'cgif') && $gif->loaded()) {
+		$width  = $gif->width();
+		$height = $gif->height();
+	} elseif (@file_exists($gif)) {
+		$myGIF = new CGIF();
+		if (!$myGIF->getSize($gif, $width, $height)) {
+			return false;
+		}
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFLZW
+{
+	var $MAX_LZW_BITS;
+	var $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode;
+	var $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIFLZW()
+	{
+		$this->MAX_LZW_BITS = 12;
+		unSet($this->Next);
+		unSet($this->Vals);
+		unSet($this->Stack);
+		unSet($this->Buf);
+
+		$this->Next  = range(0, (1 << $this->MAX_LZW_BITS)       - 1);
+		$this->Vals  = range(0, (1 << $this->MAX_LZW_BITS)       - 1);
+		$this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
+		$this->Buf   = range(0, 279);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function deCompress($data, &$datLen)
+	{
+		$stLen  = strlen($data);
+		$datLen = 0;
+		$ret    = '';
+
+		// INITIALIZATION
+		$this->LZWCommand($data, true);
+
+		while (($iIndex = $this->LZWCommand($data, false)) >= 0) {
+			$ret .= chr($iIndex);
+		}
+
+		$datLen = $stLen - strlen($data);
+
+		if ($iIndex != -2) {
+			return false;
+		}
+
+		return $ret;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function LZWCommand(&$data, $bInit)
+	{
+		if ($bInit) {
+			$this->SetCodeSize = ord($data{0});
+			$data = substr($data, 1);
+
+			$this->CodeSize    = $this->SetCodeSize + 1;
+			$this->ClearCode   = 1 << $this->SetCodeSize;
+			$this->EndCode     = $this->ClearCode + 1;
+			$this->MaxCode     = $this->ClearCode + 2;
+			$this->MaxCodeSize = $this->ClearCode << 1;
+
+			$this->GetCode($data, $bInit);
+
+			$this->Fresh = 1;
+			for ($i = 0; $i < $this->ClearCode; $i++) {
+				$this->Next[$i] = 0;
+				$this->Vals[$i] = $i;
+			}
+
+			for (; $i < (1 << $this->MAX_LZW_BITS); $i++) {
+				$this->Next[$i] = 0;
+				$this->Vals[$i] = 0;
+			}
+
+			$this->sp = 0;
+			return 1;
+		}
+
+		if ($this->Fresh) {
+			$this->Fresh = 0;
+			do {
+				$this->FirstCode = $this->GetCode($data, $bInit);
+				$this->OldCode   = $this->FirstCode;
+			}
+			while ($this->FirstCode == $this->ClearCode);
+
+			return $this->FirstCode;
+		}
+
+		if ($this->sp > 0) {
+			$this->sp--;
+			return $this->Stack[$this->sp];
+		}
+
+		while (($Code = $this->GetCode($data, $bInit)) >= 0) {
+			if ($Code == $this->ClearCode) {
+				for ($i = 0; $i < $this->ClearCode; $i++) {
+					$this->Next[$i] = 0;
+					$this->Vals[$i] = $i;
+				}
+
+				for (; $i < (1 << $this->MAX_LZW_BITS); $i++) {
+					$this->Next[$i] = 0;
+					$this->Vals[$i] = 0;
+				}
+
+				$this->CodeSize    = $this->SetCodeSize + 1;
+				$this->MaxCodeSize = $this->ClearCode << 1;
+				$this->MaxCode     = $this->ClearCode + 2;
+				$this->sp          = 0;
+				$this->FirstCode   = $this->GetCode($data, $bInit);
+				$this->OldCode     = $this->FirstCode;
+
+				return $this->FirstCode;
+			}
+
+			if ($Code == $this->EndCode) {
+				return -2;
+			}
+
+			$InCode = $Code;
+			if ($Code >= $this->MaxCode) {
+				$this->Stack[$this->sp] = $this->FirstCode;
+				$this->sp++;
+				$Code = $this->OldCode;
+			}
+
+			while ($Code >= $this->ClearCode) {
+				$this->Stack[$this->sp] = $this->Vals[$Code];
+				$this->sp++;
+
+				if ($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
+					return -1;
+
+				$Code = $this->Next[$Code];
+			}
+
+			$this->FirstCode = $this->Vals[$Code];
+			$this->Stack[$this->sp] = $this->FirstCode;
+			$this->sp++;
+
+			if (($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) {
+				$this->Next[$Code] = $this->OldCode;
+				$this->Vals[$Code] = $this->FirstCode;
+				$this->MaxCode++;
+
+				if (($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) {
+					$this->MaxCodeSize *= 2;
+					$this->CodeSize++;
+				}
+			}
+
+			$this->OldCode = $InCode;
+			if ($this->sp > 0) {
+				$this->sp--;
+				return $this->Stack[$this->sp];
+			}
+		}
+
+		return $Code;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function GetCode(&$data, $bInit)
+	{
+		if ($bInit) {
+			$this->CurBit   = 0;
+			$this->LastBit  = 0;
+			$this->Done     = 0;
+			$this->LastByte = 2;
+			return 1;
+		}
+
+		if (($this->CurBit + $this->CodeSize) >= $this->LastBit) {
+			if ($this->Done) {
+				if ($this->CurBit >= $this->LastBit) {
+					// Ran off the end of my bits
+					return 0;
+				}
+				return -1;
+			}
+
+			$this->Buf[0] = $this->Buf[$this->LastByte - 2];
+			$this->Buf[1] = $this->Buf[$this->LastByte - 1];
+
+			$Count = ord($data{0});
+			$data  = substr($data, 1);
+
+			if ($Count) {
+				for ($i = 0; $i < $Count; $i++) {
+					$this->Buf[2 + $i] = ord($data{$i});
+				}
+				$data = substr($data, $Count);
+			} else {
+				$this->Done = 1;
+			}
+
+			$this->LastByte = 2 + $Count;
+			$this->CurBit   = ($this->CurBit - $this->LastBit) + 16;
+			$this->LastBit  = (2 + $Count) << 3;
+		}
+
+		$iRet = 0;
+		for ($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) {
+			$iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j;
+		}
+
+		$this->CurBit += $this->CodeSize;
+		return $iRet;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFCOLORTABLE
+{
+	var $m_nColors;
+	var $m_arColors;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIFCOLORTABLE()
+	{
+		unSet($this->m_nColors);
+		unSet($this->m_arColors);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function load($lpData, $num)
+	{
+		$this->m_nColors  = 0;
+		$this->m_arColors = array();
+
+		for ($i = 0; $i < $num; $i++) {
+			$rgb = substr($lpData, $i * 3, 3);
+			if (strlen($rgb) < 3) {
+				return false;
+			}
+
+			$this->m_arColors[] = (ord($rgb{2}) << 16) + (ord($rgb{1}) << 8) + ord($rgb{0});
+			$this->m_nColors++;
+		}
+
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function toString()
+	{
+		$ret = '';
+
+		for ($i = 0; $i < $this->m_nColors; $i++) {
+			$ret .=
+				chr(($this->m_arColors[$i] & 0x000000FF))       . // R
+				chr(($this->m_arColors[$i] & 0x0000FF00) >>  8) . // G
+				chr(($this->m_arColors[$i] & 0x00FF0000) >> 16);  // B
+		}
+
+		return $ret;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function toRGBQuad()
+	{
+		$ret = '';
+
+		for ($i = 0; $i < $this->m_nColors; $i++) {
+			$ret .=
+				chr(($this->m_arColors[$i] & 0x00FF0000) >> 16) . // B
+				chr(($this->m_arColors[$i] & 0x0000FF00) >>  8) . // G
+				chr(($this->m_arColors[$i] & 0x000000FF))       . // R
+				"\x00";
+		}
+
+		return $ret;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function colorIndex($rgb)
+	{
+		$rgb  = intval($rgb) & 0xFFFFFF;
+		$r1   = ($rgb & 0x0000FF);
+		$g1   = ($rgb & 0x00FF00) >>  8;
+		$b1   = ($rgb & 0xFF0000) >> 16;
+		$idx  = -1;
+
+		for ($i = 0; $i < $this->m_nColors; $i++) {
+			$r2 = ($this->m_arColors[$i] & 0x000000FF);
+			$g2 = ($this->m_arColors[$i] & 0x0000FF00) >>  8;
+			$b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
+			$d  = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
+
+			if (($idx == -1) || ($d < $dif)) {
+				$idx = $i;
+				$dif = $d;
+			}
+		}
+
+		return $idx;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFFILEHEADER
+{
+	var $m_lpVer;
+	var $m_nWidth;
+	var $m_nHeight;
+	var $m_bGlobalClr;
+	var $m_nColorRes;
+	var $m_bSorted;
+	var $m_nTableSize;
+	var $m_nBgColor;
+	var $m_nPixelRatio;
+	var $m_colorTable;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIFFILEHEADER()
+	{
+		unSet($this->m_lpVer);
+		unSet($this->m_nWidth);
+		unSet($this->m_nHeight);
+		unSet($this->m_bGlobalClr);
+		unSet($this->m_nColorRes);
+		unSet($this->m_bSorted);
+		unSet($this->m_nTableSize);
+		unSet($this->m_nBgColor);
+		unSet($this->m_nPixelRatio);
+		unSet($this->m_colorTable);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function load($lpData, &$hdrLen)
+	{
+		$hdrLen = 0;
+
+		$this->m_lpVer = substr($lpData, 0, 6);
+		if (($this->m_lpVer <> 'GIF87a') && ($this->m_lpVer <> 'GIF89a')) {
+			return false;
+		}
+
+		$this->m_nWidth  = $this->w2i(substr($lpData, 6, 2));
+		$this->m_nHeight = $this->w2i(substr($lpData, 8, 2));
+		if (!$this->m_nWidth || !$this->m_nHeight) {
+			return false;
+		}
+
+		$b = ord(substr($lpData, 10, 1));
+		$this->m_bGlobalClr  = ($b & 0x80) ? true : false;
+		$this->m_nColorRes   = ($b & 0x70) >> 4;
+		$this->m_bSorted     = ($b & 0x08) ? true : false;
+		$this->m_nTableSize  = 2 << ($b & 0x07);
+		$this->m_nBgColor    = ord(substr($lpData, 11, 1));
+		$this->m_nPixelRatio = ord(substr($lpData, 12, 1));
+		$hdrLen = 13;
+
+		if ($this->m_bGlobalClr) {
+			$this->m_colorTable = new CGIFCOLORTABLE();
+			if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
+				return false;
+			}
+			$hdrLen += 3 * $this->m_nTableSize;
+		}
+
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function w2i($str)
+	{
+		return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFIMAGEHEADER
+{
+	var $m_nLeft;
+	var $m_nTop;
+	var $m_nWidth;
+	var $m_nHeight;
+	var $m_bLocalClr;
+	var $m_bInterlace;
+	var $m_bSorted;
+	var $m_nTableSize;
+	var $m_colorTable;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIFIMAGEHEADER()
+	{
+		unSet($this->m_nLeft);
+		unSet($this->m_nTop);
+		unSet($this->m_nWidth);
+		unSet($this->m_nHeight);
+		unSet($this->m_bLocalClr);
+		unSet($this->m_bInterlace);
+		unSet($this->m_bSorted);
+		unSet($this->m_nTableSize);
+		unSet($this->m_colorTable);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function load($lpData, &$hdrLen)
+	{
+		$hdrLen = 0;
+
+		$this->m_nLeft   = $this->w2i(substr($lpData, 0, 2));
+		$this->m_nTop    = $this->w2i(substr($lpData, 2, 2));
+		$this->m_nWidth  = $this->w2i(substr($lpData, 4, 2));
+		$this->m_nHeight = $this->w2i(substr($lpData, 6, 2));
+
+		if (!$this->m_nWidth || !$this->m_nHeight) {
+			return false;
+		}
+
+		$b = ord($lpData{8});
+		$this->m_bLocalClr  = ($b & 0x80) ? true : false;
+		$this->m_bInterlace = ($b & 0x40) ? true : false;
+		$this->m_bSorted    = ($b & 0x20) ? true : false;
+		$this->m_nTableSize = 2 << ($b & 0x07);
+		$hdrLen = 9;
+
+		if ($this->m_bLocalClr) {
+			$this->m_colorTable = new CGIFCOLORTABLE();
+			if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
+				return false;
+			}
+			$hdrLen += 3 * $this->m_nTableSize;
+		}
+
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function w2i($str)
+	{
+		return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIFIMAGE
+{
+	var $m_disp;
+	var $m_bUser;
+	var $m_bTrans;
+	var $m_nDelay;
+	var $m_nTrans;
+	var $m_lpComm;
+	var $m_gih;
+	var $m_data;
+	var $m_lzw;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function CGIFIMAGE()
+	{
+		unSet($this->m_disp);
+		unSet($this->m_bUser);
+		unSet($this->m_bTrans);
+		unSet($this->m_nDelay);
+		unSet($this->m_nTrans);
+		unSet($this->m_lpComm);
+		unSet($this->m_data);
+		$this->m_gih = new CGIFIMAGEHEADER();
+		$this->m_lzw = new CGIFLZW();
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function load($data, &$datLen)
+	{
+		$datLen = 0;
+
+		while (true) {
+			$b = ord($data{0});
+			$data = substr($data, 1);
+			$datLen++;
+
+			switch($b) {
+			case 0x21: // Extension
+				if (!$this->skipExt($data, $len = 0)) {
+					return false;
+				}
+				$datLen += $len;
+				break;
+
+			case 0x2C: // Image
+				// LOAD HEADER & COLOR TABLE
+				if (!$this->m_gih->load($data, $len = 0)) {
+					return false;
+				}
+				$data = substr($data, $len);
+				$datLen += $len;
+
+				// ALLOC BUFFER
+				if (!($this->m_data = $this->m_lzw->deCompress($data, $len = 0))) {
+					return false;
+				}
+				$data = substr($data, $len);
+				$datLen += $len;
+
+				if ($this->m_gih->m_bInterlace) {
+					$this->deInterlace();
+				}
+				return true;
+
+			case 0x3B: // EOF
+			default:
+				return false;
+			}
+		}
+		return false;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function skipExt(&$data, &$extLen)
+	{
+		$extLen = 0;
+
+		$b = ord($data{0});
+		$data = substr($data, 1);
+		$extLen++;
+
+		switch($b) {
+		case 0xF9: // Graphic Control
+			$b = ord($data{1});
+			$this->m_disp   = ($b & 0x1C) >> 2;
+			$this->m_bUser  = ($b & 0x02) ? true : false;
+			$this->m_bTrans = ($b & 0x01) ? true : false;
+			$this->m_nDelay = $this->w2i(substr($data, 2, 2));
+			$this->m_nTrans = ord($data{4});
+			break;
+
+		case 0xFE: // Comment
+			$this->m_lpComm = substr($data, 1, ord($data{0}));
+			break;
+
+		case 0x01: // Plain text
+			break;
+
+		case 0xFF: // Application
+			break;
+		}
+
+		// SKIP DEFAULT AS DEFS MAY CHANGE
+		$b = ord($data{0});
+		$data = substr($data, 1);
+		$extLen++;
+		while ($b > 0) {
+			$data = substr($data, $b);
+			$extLen += $b;
+			$b    = ord($data{0});
+			$data = substr($data, 1);
+			$extLen++;
+		}
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function w2i($str)
+	{
+		return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function deInterlace()
+	{
+		$data = $this->m_data;
+
+		for ($i = 0; $i < 4; $i++) {
+			switch($i) {
+			case 0:
+				$s = 8;
+				$y = 0;
+				break;
+
+			case 1:
+				$s = 8;
+				$y = 4;
+				break;
+
+			case 2:
+				$s = 4;
+				$y = 2;
+				break;
+
+			case 3:
+				$s = 2;
+				$y = 1;
+				break;
+			}
+
+			for (; $y < $this->m_gih->m_nHeight; $y += $s) {
+				$lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
+				$this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
+
+				$data =
+					substr($data, 0, $y * $this->m_gih->m_nWidth) .
+					$lne .
+					substr($data, ($y + 1) * $this->m_gih->m_nWidth);
+			}
+		}
+
+		$this->m_data = $data;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class CGIF
+{
+	var $m_gfh;
+	var $m_lpData;
+	var $m_img;
+	var $m_bLoaded;
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// CONSTRUCTOR
+	function CGIF()
+	{
+		$this->m_gfh     = new CGIFFILEHEADER();
+		$this->m_img     = new CGIFIMAGE();
+		$this->m_lpData  = '';
+		$this->m_bLoaded = false;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function loadFile($lpszFileName, $iIndex)
+	{
+		if ($iIndex < 0) {
+			return false;
+		}
+
+		// READ FILE
+		if (!($fh = @fopen($lpszFileName, 'rb'))) {
+			return false;
+		}
+		$this->m_lpData = @fRead($fh, @fileSize($lpszFileName));
+		fclose($fh);
+
+		// GET FILE HEADER
+		if (!$this->m_gfh->load($this->m_lpData, $len = 0)) {
+			return false;
+		}
+		$this->m_lpData = substr($this->m_lpData, $len);
+
+		do {
+			if (!$this->m_img->load($this->m_lpData, $imgLen = 0)) {
+				return false;
+			}
+			$this->m_lpData = substr($this->m_lpData, $imgLen);
+		}
+		while ($iIndex-- > 0);
+
+		$this->m_bLoaded = true;
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function getSize($lpszFileName, &$width, &$height)
+	{
+		if (!($fh = @fopen($lpszFileName, 'rb'))) {
+			return false;
+		}
+		$data = @fRead($fh, @fileSize($lpszFileName));
+		@fclose($fh);
+
+		$gfh = new CGIFFILEHEADER();
+		if (!$gfh->load($data, $len = 0)) {
+			return false;
+		}
+
+		$width  = $gfh->m_nWidth;
+		$height = $gfh->m_nHeight;
+		return true;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function getBmp($bgColor)
+	{
+		$out = '';
+
+		if (!$this->m_bLoaded) {
+			return false;
+		}
+
+		// PREPARE COLOR TABLE (RGBQUADs)
+		if ($this->m_img->m_gih->m_bLocalClr) {
+			$nColors = $this->m_img->m_gih->m_nTableSize;
+			$rgbq    = $this->m_img->m_gih->m_colorTable->toRGBQuad();
+			if ($bgColor != -1) {
+				$bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
+			}
+		} elseif ($this->m_gfh->m_bGlobalClr) {
+			$nColors = $this->m_gfh->m_nTableSize;
+			$rgbq    = $this->m_gfh->m_colorTable->toRGBQuad();
+			if ($bgColor != -1) {
+				$bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
+			}
+		} else {
+			$nColors =  0;
+			$bgColor = -1;
+		}
+
+		// PREPARE BITMAP BITS
+		$data = $this->m_img->m_data;
+		$nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth;
+		$bmp  = '';
+		$nPad = ($this->m_gfh->m_nWidth % 4) ? 4 - ($this->m_gfh->m_nWidth % 4) : 0;
+		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
+			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
+				if (
+					($x >= $this->m_img->m_gih->m_nLeft) &&
+					($y >= $this->m_img->m_gih->m_nTop) &&
+					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
+					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
+					// PART OF IMAGE
+					if (@$this->m_img->m_bTrans && (ord($data{$nPxl}) == $this->m_img->m_nTrans)) {
+						// TRANSPARENT -> BACKGROUND
+						if ($bgColor == -1) {
+							$bmp .= chr($this->m_gfh->m_nBgColor);
+						} else {
+							$bmp .= chr($bgColor);
+						}
+					} else {
+						$bmp .= $data{$nPxl};
+					}
+				} else {
+					// BACKGROUND
+					if ($bgColor == -1) {
+						$bmp .= chr($this->m_gfh->m_nBgColor);
+					} else {
+						$bmp .= chr($bgColor);
+					}
+				}
+			}
+			$nPxl -= $this->m_gfh->m_nWidth << 1;
+
+			// ADD PADDING
+			for ($x = 0; $x < $nPad; $x++) {
+				$bmp .= "\x00";
+			}
+		}
+
+		// BITMAPFILEHEADER
+		$out .= 'BM';
+		$out .= $this->dword(14 + 40 + ($nColors << 2) + strlen($bmp));
+		$out .= "\x00\x00";
+		$out .= "\x00\x00";
+		$out .= $this->dword(14 + 40 + ($nColors << 2));
+
+		// BITMAPINFOHEADER
+		$out .= $this->dword(40);
+		$out .= $this->dword($this->m_gfh->m_nWidth);
+		$out .= $this->dword($this->m_gfh->m_nHeight);
+		$out .= "\x01\x00";
+		$out .= "\x08\x00";
+		$out .= "\x00\x00\x00\x00";
+		$out .= "\x00\x00\x00\x00";
+		$out .= "\x12\x0B\x00\x00";
+		$out .= "\x12\x0B\x00\x00";
+		$out .= $this->dword($nColors % 256);
+		$out .= "\x00\x00\x00\x00";
+
+		// COLOR TABLE
+		if ($nColors > 0) {
+			$out .= $rgbq;
+		}
+
+		// DATA
+		$out .= $bmp;
+
+		return $out;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function getPng($bgColor)
+	{
+		$out = '';
+
+		if (!$this->m_bLoaded) {
+			return false;
+		}
+
+		// PREPARE COLOR TABLE (RGBQUADs)
+		if ($this->m_img->m_gih->m_bLocalClr) {
+			$nColors = $this->m_img->m_gih->m_nTableSize;
+			$pal     = $this->m_img->m_gih->m_colorTable->toString();
+			if ($bgColor != -1) {
+				$bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
+			}
+		} elseif ($this->m_gfh->m_bGlobalClr) {
+			$nColors = $this->m_gfh->m_nTableSize;
+			$pal     = $this->m_gfh->m_colorTable->toString();
+			if ($bgColor != -1) {
+				$bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
+			}
+		} else {
+			$nColors =  0;
+			$bgColor = -1;
+		}
+
+		// PREPARE BITMAP BITS
+		$data = $this->m_img->m_data;
+		$nPxl = 0;
+		$bmp  = '';
+		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
+			$bmp .= "\x00";
+			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
+				if (
+					($x >= $this->m_img->m_gih->m_nLeft) &&
+					($y >= $this->m_img->m_gih->m_nTop) &&
+					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
+					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
+					// PART OF IMAGE
+					$bmp .= $data{$nPxl};
+				} else {
+					// BACKGROUND
+					if ($bgColor == -1) {
+						$bmp .= chr($this->m_gfh->m_nBgColor);
+					} else {
+						$bmp .= chr($bgColor);
+					}
+				}
+			}
+		}
+		$bmp = gzcompress($bmp, 9);
+
+		///////////////////////////////////////////////////////////////////////
+		// SIGNATURE
+		$out .= "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
+		///////////////////////////////////////////////////////////////////////
+		// HEADER
+		$out .= "\x00\x00\x00\x0D";
+		$tmp  = 'IHDR';
+		$tmp .= $this->ndword($this->m_gfh->m_nWidth);
+		$tmp .= $this->ndword($this->m_gfh->m_nHeight);
+		$tmp .= "\x08\x03\x00\x00\x00";
+		$out .= $tmp;
+		$out .= $this->ndword(crc32($tmp));
+		///////////////////////////////////////////////////////////////////////
+		// PALETTE
+		if ($nColors > 0) {
+			$out .= $this->ndword($nColors * 3);
+			$tmp  = 'PLTE';
+			$tmp .= $pal;
+			$out .= $tmp;
+			$out .= $this->ndword(crc32($tmp));
+		}
+		///////////////////////////////////////////////////////////////////////
+		// TRANSPARENCY
+		if (@$this->m_img->m_bTrans && ($nColors > 0)) {
+			$out .= $this->ndword($nColors);
+			$tmp  = 'tRNS';
+			for ($i = 0; $i < $nColors; $i++) {
+				$tmp .= ($i == $this->m_img->m_nTrans) ? "\x00" : "\xFF";
+			}
+			$out .= $tmp;
+			$out .= $this->ndword(crc32($tmp));
+		}
+		///////////////////////////////////////////////////////////////////////
+		// DATA BITS
+		$out .= $this->ndword(strlen($bmp));
+		$tmp  = 'IDAT';
+		$tmp .= $bmp;
+		$out .= $tmp;
+		$out .= $this->ndword(crc32($tmp));
+		///////////////////////////////////////////////////////////////////////
+		// END OF FILE
+		$out .= "\x00\x00\x00\x00IEND\xAE\x42\x60\x82";
+
+		return $out;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	// Added by James Heinrich <info@silisoftware.com> - January 5, 2003
+
+	// Takes raw image data and plots it pixel-by-pixel on a new GD image and returns that
+	// It's extremely slow, but the only solution when ImageCreateFromString() fails
+	function getGD_PixelPlotterVersion()
+	{
+		if (!$this->m_bLoaded) {
+			return false;
+		}
+
+		// PREPARE COLOR TABLE (RGBQUADs)
+		if ($this->m_img->m_gih->m_bLocalClr) {
+			$pal = $this->m_img->m_gih->m_colorTable->toString();
+		} elseif ($this->m_gfh->m_bGlobalClr) {
+			$pal = $this->m_gfh->m_colorTable->toString();
+		} else {
+			die('No color table available in getGD_PixelPlotterVersion()');
+		}
+
+		$PlottingIMG = ImageCreate($this->m_gfh->m_nWidth, $this->m_gfh->m_nHeight);
+		$NumColorsInPal = floor(strlen($pal) / 3);
+		for ($i = 0; $i < $NumColorsInPal; $i++) {
+			$ThisImageColor[$i] = ImageColorAllocate(
+									$PlottingIMG,
+									ord($pal{(($i * 3) + 0)}),
+									ord($pal{(($i * 3) + 1)}),
+									ord($pal{(($i * 3) + 2)}));
+		}
+
+		// PREPARE BITMAP BITS
+		$data = $this->m_img->m_data;
+		$nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth;
+		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
+			set_time_limit(30);
+			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
+				if (
+					($x >= $this->m_img->m_gih->m_nLeft) &&
+					($y >= $this->m_img->m_gih->m_nTop) &&
+					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
+					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
+					// PART OF IMAGE
+					if (@$this->m_img->m_bTrans && (ord($data{$nPxl}) == $this->m_img->m_nTrans)) {
+						ImageSetPixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[$this->m_gfh->m_nBgColor]);
+					} else {
+						ImageSetPixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[ord($data{$nPxl})]);
+					}
+				} else {
+					// BACKGROUND
+					ImageSetPixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[$this->m_gfh->m_nBgColor]);
+				}
+			}
+			$nPxl -= $this->m_gfh->m_nWidth << 1;
+
+		}
+
+		return $PlottingIMG;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function dword($val)
+	{
+		$val = intval($val);
+		return chr($val & 0xFF).chr(($val & 0xFF00) >> 8).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF000000) >> 24);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function ndword($val)
+	{
+		$val = intval($val);
+		return chr(($val & 0xFF000000) >> 24).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF00) >> 8).chr($val & 0xFF);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function width()
+	{
+		return $this->m_gfh->m_nWidth;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function height()
+	{
+		return $this->m_gfh->m_nHeight;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function comment()
+	{
+		return $this->m_img->m_lpComm;
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	function loaded()
+	{
+		return $this->m_bLoaded;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+?>
Index: /afridex/plugins/Flutter/phpthumb.filters.php
===================================================================
--- /afridex/plugins/Flutter/phpthumb.filters.php (revision 21)
+++ /afridex/plugins/Flutter/phpthumb.filters.php (revision 21)
@@ -0,0 +1,1377 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// phpthumb.filters.php - image processing filter functions //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+class phpthumb_filters {
+
+	var $phpThumbObject = null;
+
+	function phpthumb_filters() {
+		return true;
+	}
+
+	function ApplyMask(&$gdimg_mask, &$gdimg_image) {
+		if (phpthumb_functions::gd_version() < 2) {
+			$this->DebugMessage('Skipping ApplyMask() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+			return false;
+		}
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.2', '>=')) {
+
+			$this->DebugMessage('Using alpha ApplyMask() technique', __FILE__, __LINE__);
+			if ($gdimg_mask_resized = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg_image), ImageSY($gdimg_image))) {
+
+				ImageCopyResampled($gdimg_mask_resized, $gdimg_mask, 0, 0, 0, 0, ImageSX($gdimg_image), ImageSY($gdimg_image), ImageSX($gdimg_mask), ImageSY($gdimg_mask));
+				if ($gdimg_mask_blendtemp = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg_image), ImageSY($gdimg_image))) {
+
+					$color_background = ImageColorAllocate($gdimg_mask_blendtemp, 0, 0, 0);
+					ImageFilledRectangle($gdimg_mask_blendtemp, 0, 0, ImageSX($gdimg_mask_blendtemp), ImageSY($gdimg_mask_blendtemp), $color_background);
+					ImageAlphaBlending($gdimg_mask_blendtemp, false);
+					ImageSaveAlpha($gdimg_mask_blendtemp, true);
+					for ($x = 0; $x < ImageSX($gdimg_image); $x++) {
+						for ($y = 0; $y < ImageSY($gdimg_image); $y++) {
+							//$RealPixel = phpthumb_functions::GetPixelColor($gdimg_mask_blendtemp, $x, $y);
+							$RealPixel = phpthumb_functions::GetPixelColor($gdimg_image, $x, $y);
+							$MaskPixel = phpthumb_functions::GrayscalePixel(phpthumb_functions::GetPixelColor($gdimg_mask_resized, $x, $y));
+							$MaskAlpha = 127 - (floor($MaskPixel['red'] / 2) * (1 - ($RealPixel['alpha'] / 127)));
+							$newcolor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_mask_blendtemp, $RealPixel['red'], $RealPixel['green'], $RealPixel['blue'], $MaskAlpha);
+							ImageSetPixel($gdimg_mask_blendtemp, $x, $y, $newcolor);
+						}
+					}
+					ImageAlphaBlending($gdimg_image, false);
+					ImageSaveAlpha($gdimg_image, true);
+					ImageCopy($gdimg_image, $gdimg_mask_blendtemp, 0, 0, 0, 0, ImageSX($gdimg_mask_blendtemp), ImageSY($gdimg_mask_blendtemp));
+					ImageDestroy($gdimg_mask_blendtemp);
+
+				} else {
+					$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
+				}
+				ImageDestroy($gdimg_mask_resized);
+
+			} else {
+				$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
+			}
+
+		} else {
+			// alpha merging requires PHP v4.3.2+
+			$this->DebugMessage('Skipping ApplyMask() technique because PHP is v"'.phpversion().'"', __FILE__, __LINE__);
+		}
+		return true;
+	}
+
+
+	function Bevel(&$gdimg, $width, $hexcolor1, $hexcolor2) {
+		$width     = ($width     ? $width     : 5);
+		$hexcolor1 = ($hexcolor1 ? $hexcolor1 : 'FFFFFF');
+		$hexcolor2 = ($hexcolor2 ? $hexcolor2 : '000000');
+
+		ImageAlphaBlending($gdimg, true);
+		for ($i = 0; $i < $width; $i++) {
+			$alpha = round(($i / $width) * 127);
+			$color1[$i] = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor1, false, $alpha);
+			$color2[$i] = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor2, false, $alpha);
+
+			ImageLine($gdimg,                   $i,                   $i,                   $i, ImageSY($gdimg) - $i, $color1[$i]); // left
+			ImageLine($gdimg,                   $i,                   $i, ImageSX($gdimg) - $i,                   $i, $color1[$i]); // top
+			ImageLine($gdimg, ImageSX($gdimg) - $i, ImageSY($gdimg) - $i, ImageSX($gdimg) - $i,                   $i, $color2[$i]); // right
+			ImageLine($gdimg, ImageSX($gdimg) - $i, ImageSY($gdimg) - $i,                   $i, ImageSY($gdimg) - $i, $color2[$i]); // bottom
+		}
+		return true;
+	}
+
+
+	function Blur(&$gdimg, $radius=0.5) {
+		// Taken from Torstein Hønsi's phpUnsharpMask (see phpthumb.unsharp.php)
+
+		$radius = round(max(0, min($radius, 50)) * 2);
+		if (!$radius) {
+			return false;
+		}
+
+		$w = ImageSX($gdimg);
+		$h = ImageSY($gdimg);
+		if ($imgBlur = ImageCreateTrueColor($w, $h)) {
+			// Gaussian blur matrix:
+			//	1	2	1
+			//	2	4	2
+			//	1	2	1
+
+			// Move copies of the image around one pixel at the time and merge them with weight
+			// according to the matrix. The same matrix is simply repeated for higher radii.
+			for ($i = 0; $i < $radius; $i++)	{
+				ImageCopy     ($imgBlur, $gdimg, 0, 0, 1, 1, $w - 1, $h - 1);            // up left
+				ImageCopyMerge($imgBlur, $gdimg, 1, 1, 0, 0, $w,     $h,     50.00000);  // down right
+				ImageCopyMerge($imgBlur, $gdimg, 0, 1, 1, 0, $w - 1, $h,     33.33333);  // down left
+				ImageCopyMerge($imgBlur, $gdimg, 1, 0, 0, 1, $w,     $h - 1, 25.00000);  // up right
+				ImageCopyMerge($imgBlur, $gdimg, 0, 0, 1, 0, $w - 1, $h,     33.33333);  // left
+				ImageCopyMerge($imgBlur, $gdimg, 1, 0, 0, 0, $w,     $h,     25.00000);  // right
+				ImageCopyMerge($imgBlur, $gdimg, 0, 0, 0, 1, $w,     $h - 1, 20.00000);  // up
+				ImageCopyMerge($imgBlur, $gdimg, 0, 1, 0, 0, $w,     $h,     16.666667); // down
+				ImageCopyMerge($imgBlur, $gdimg, 0, 0, 0, 0, $w,     $h,     50.000000); // center
+				ImageCopy     ($gdimg, $imgBlur, 0, 0, 0, 0, $w,     $h);
+			}
+			return true;
+		}
+		return false;
+	}
+
+
+	function BlurGaussian(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_GAUSSIAN_BLUR)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_GAUSSIAN_BLUR)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		$this->DebugMessage('FAILED: phpthumb_filters::BlurGaussian($gdimg) [using phpthumb_filters::Blur() instead]', __FILE__, __LINE__);
+		return phpthumb_filters::Blur($gdimg, 0.5);
+	}
+
+
+	function BlurSelective(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_SELECTIVE_BLUR)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_SELECTIVE_BLUR)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::BlurSelective($gdimg) [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Brightness(&$gdimg, $amount=0) {
+		if ($amount == 0) {
+			return true;
+		}
+		$amount = max(-255, min(255, $amount));
+
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_BRIGHTNESS, $amount)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_BRIGHTNESS, '.$amount.')', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+
+		$scaling = (255 - abs($amount)) / 255;
+		$baseamount = (($amount > 0) ? $amount : 0);
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				foreach ($OriginalPixel as $key => $value) {
+					$NewPixel[$key] = round($baseamount + ($OriginalPixel[$key] * $scaling));
+				}
+				$newColor = ImageColorAllocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function Contrast(&$gdimg, $amount=0) {
+		if ($amount == 0) {
+			return true;
+		}
+		$amount = max(-255, min(255, $amount));
+
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_CONTRAST, $amount)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_CONTRAST, '.$amount.')', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+
+		if ($amount > 0) {
+			$scaling = 1 + ($amount / 255);
+		} else {
+			$scaling = (255 - abs($amount)) / 255;
+		}
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				foreach ($OriginalPixel as $key => $value) {
+					$NewPixel[$key] = min(255, max(0, round($OriginalPixel[$key] * $scaling)));
+				}
+				$newColor = ImageColorAllocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+	}
+
+
+	function Colorize(&$gdimg, $amount, $targetColor) {
+		$amount      = (is_numeric($amount)                          ? $amount      : 25);
+		$targetColor = (phpthumb_functions::IsHexColor($targetColor) ? $targetColor : 'gray');
+
+		if ($amount == 0) {
+			return true;
+		}
+
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if ($targetColor == 'gray') {
+				$targetColor = '808080';
+			}
+			$r = substr($targetColor, 0, 2);
+			$g = substr($targetColor, 2, 2);
+			$b = substr($targetColor, 4, 2);
+			if (ImageFilter($gdimg, IMG_FILTER_COLORIZE, $r, $g, $b)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_COLORIZE)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+
+		// overridden below for grayscale
+		if ($targetColor != 'gray') {
+			$TargetPixel['red']   = hexdec(substr($targetColor, 0, 2));
+			$TargetPixel['green'] = hexdec(substr($targetColor, 2, 2));
+			$TargetPixel['blue']  = hexdec(substr($targetColor, 4, 2));
+		}
+
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				if ($targetColor == 'gray') {
+					$TargetPixel = phpthumb_functions::GrayscalePixel($OriginalPixel);
+				}
+				foreach ($TargetPixel as $key => $value) {
+					$NewPixel[$key] = round(max(0, min(255, ($OriginalPixel[$key] * ((100 - $amount) / 100)) + ($TargetPixel[$key] * ($amount / 100)))));
+				}
+				//$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue'], $OriginalPixel['alpha']);
+				$newColor = ImageColorAllocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function Crop(&$gdimg, $left=0, $right=0, $top=0, $bottom=0) {
+		if (!$left && !$right && !$top && !$bottom) {
+			return true;
+		}
+		$oldW = ImageSX($gdimg);
+		$oldH = ImageSY($gdimg);
+		if (($left   > 0) && ($left   < 1)) { $left   = round($left   * $oldW); }
+		if (($right  > 0) && ($right  < 1)) { $right  = round($right  * $oldW); }
+		if (($top    > 0) && ($top    < 1)) { $top    = round($top    * $oldH); }
+		if (($bottom > 0) && ($bottom < 1)) { $bottom = round($bottom * $oldH); }
+		$right  = min($oldW - $left - 1, $right);
+		$bottom = min($oldH - $top  - 1, $bottom);
+		$newW = $oldW - $left - $right;
+		$newH = $oldH - $top  - $bottom;
+
+		if ($imgCropped = ImageCreateTrueColor($newW, $newH)) {
+			ImageCopy($imgCropped, $gdimg, 0, 0, $left, $top, $newW, $newH);
+			if ($gdimg = ImageCreateTrueColor($newW, $newH)) {
+				ImageCopy($gdimg, $imgCropped, 0, 0, 0, 0, $newW, $newH);
+				ImageDestroy($imgCropped);
+				return true;
+			}
+			ImageDestroy($imgCropped);
+		}
+		return false;
+	}
+
+
+	function Desaturate(&$gdimg, $amount, $color='') {
+		if ($amount == 0) {
+			return true;
+		}
+		return phpthumb_filters::Colorize($gdimg, $amount, (phpthumb_functions::IsHexColor($color) ? $color : 'gray'));
+	}
+
+
+	function DropShadow(&$gdimg, $distance, $width, $hexcolor, $angle, $fade) {
+		if (phpthumb_functions::gd_version() < 2) {
+			return false;
+		}
+		$distance = ($distance ? $distance : 10);
+		$width    = ($width    ? $width    : 10);
+		$hexcolor = ($hexcolor ? $hexcolor : '000000');
+		$angle    = ($angle    ? $angle    : 225);
+		$fade     = ($fade     ? $fade     : 1);
+
+		$width_shadow  = cos(deg2rad($angle)) * ($distance + $width);
+		$height_shadow = sin(deg2rad($angle)) * ($distance + $width);
+
+		$scaling = min(ImageSX($gdimg) / (ImageSX($gdimg) + abs($width_shadow)), ImageSY($gdimg) / (ImageSY($gdimg) + abs($height_shadow)));
+
+		for ($i = 0; $i < $width; $i++) {
+			$WidthAlpha[$i] = (abs(($width / 2) - $i) / $width) * $fade;
+			$Offset['x'] = cos(deg2rad($angle)) * ($distance + $i);
+			$Offset['y'] = sin(deg2rad($angle)) * ($distance + $i);
+		}
+
+		$tempImageWidth  = ImageSX($gdimg)  + abs($Offset['x']);
+		$tempImageHeight = ImageSY($gdimg) + abs($Offset['y']);
+
+		if ($gdimg_dropshadow_temp = phpthumb_functions::ImageCreateFunction($tempImageWidth, $tempImageHeight)) {
+
+			ImageAlphaBlending($gdimg_dropshadow_temp, false);
+			ImageSaveAlpha($gdimg_dropshadow_temp, true);
+			$transparent1 = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_dropshadow_temp, 0, 0, 0, 127);
+			ImageFill($gdimg_dropshadow_temp, 0, 0, $transparent1);
+
+			for ($x = 0; $x < ImageSX($gdimg); $x++) {
+				for ($y = 0; $y < ImageSY($gdimg); $y++) {
+					$PixelMap[$x][$y] = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				}
+			}
+			for ($x = 0; $x < $tempImageWidth; $x++) {
+				for ($y = 0; $y < $tempImageHeight; $y++) {
+					//for ($i = 0; $i < $width; $i++) {
+					for ($i = 0; $i < 1; $i++) {
+						if (!isset($PixelMap[$x][$y]['alpha']) || ($PixelMap[$x][$y]['alpha'] > 0)) {
+							if (isset($PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha']) && ($PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha'] < 127)) {
+								$thisColor = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor, false, $PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha']);
+								ImageSetPixel($gdimg_dropshadow_temp, $x, $y, $thisColor);
+							}
+						}
+					}
+				}
+			}
+
+			ImageAlphaBlending($gdimg_dropshadow_temp, true);
+			for ($x = 0; $x < ImageSX($gdimg); $x++) {
+				for ($y = 0; $y < ImageSY($gdimg); $y++) {
+					if ($PixelMap[$x][$y]['alpha'] < 127) {
+						$thisColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_dropshadow_temp, $PixelMap[$x][$y]['red'], $PixelMap[$x][$y]['green'], $PixelMap[$x][$y]['blue'], $PixelMap[$x][$y]['alpha']);
+						ImageSetPixel($gdimg_dropshadow_temp, $x, $y, $thisColor);
+					}
+				}
+			}
+
+			ImageSaveAlpha($gdimg, true);
+			ImageAlphaBlending($gdimg, false);
+			//$this->is_alpha = true;
+			$transparent2 = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0, 0, 0, 127);
+			ImageFilledRectangle($gdimg, 0, 0, ImageSX($gdimg), ImageSY($gdimg), $transparent2);
+			ImageCopyResampled($gdimg, $gdimg_dropshadow_temp, 0, 0, 0, 0, ImageSX($gdimg), ImageSY($gdimg), ImageSX($gdimg_dropshadow_temp), ImageSY($gdimg_dropshadow_temp));
+
+			ImageDestroy($gdimg_dropshadow_temp);
+		}
+		return true;
+	}
+
+
+	function EdgeDetect(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_EDGEDETECT)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_EDGEDETECT)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::EdgeDetect($gdimg) [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Elipse($gdimg) {
+		if (phpthumb_functions::gd_version() < 2) {
+			return false;
+		}
+		// generate mask at twice desired resolution and downsample afterwards for easy antialiasing
+		if ($gdimg_elipsemask_double = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg) * 2, ImageSY($gdimg) * 2)) {
+			if ($gdimg_elipsemask = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg), ImageSY($gdimg))) {
+
+				$color_transparent = ImageColorAllocate($gdimg_elipsemask_double, 255, 255, 255);
+				ImageFilledEllipse($gdimg_elipsemask_double, ImageSX($gdimg), ImageSY($gdimg), (ImageSX($gdimg) - 1) * 2, (ImageSY($gdimg) - 1) * 2, $color_transparent);
+				ImageCopyResampled($gdimg_elipsemask, $gdimg_elipsemask_double, 0, 0, 0, 0, ImageSX($gdimg), ImageSY($gdimg), ImageSX($gdimg) * 2, ImageSY($gdimg) * 2);
+
+				phpthumb_filters::ApplyMask($gdimg_elipsemask, $gdimg);
+				ImageDestroy($gdimg_elipsemask);
+				return true;
+
+			} else {
+				$this->DebugMessage('$gdimg_elipsemask = phpthumb_functions::ImageCreateFunction() failed', __FILE__, __LINE__);
+			}
+			ImageDestroy($gdimg_elipsemask_double);
+		} else {
+			$this->DebugMessage('$gdimg_elipsemask_double = phpthumb_functions::ImageCreateFunction() failed', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	function Emboss(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_EMBOSS)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_EMBOSS)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::Emboss($gdimg) [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Flip(&$gdimg, $x=false, $y=false) {
+		if (!$x && !$y) {
+			return false;
+		}
+		if ($tempImage = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg), ImageSY($gdimg))) {
+			if ($x) {
+				ImageCopy($tempImage, $gdimg, 0, 0, 0, 0, ImageSX($gdimg), ImageSY($gdimg));
+				for ($x = 0; $x < ImageSX($gdimg); $x++) {
+					ImageCopy($gdimg, $tempImage, ImageSX($gdimg) - 1 - $x, 0, $x, 0, 1, ImageSY($gdimg));
+				}
+			}
+			if ($y) {
+				ImageCopy($tempImage, $gdimg, 0, 0, 0, 0, ImageSX($gdimg), ImageSY($gdimg));
+				for ($y = 0; $y < ImageSY($gdimg); $y++) {
+					ImageCopy($gdimg, $tempImage, 0, ImageSY($gdimg) - 1 - $y, 0, $y, ImageSX($gdimg), 1);
+				}
+			}
+			ImageDestroy($tempImage);
+		}
+		return true;
+	}
+
+
+	function Frame(&$gdimg, $frame_width, $edge_width, $hexcolor_frame, $hexcolor1, $hexcolor2) {
+		$frame_width    = ($frame_width    ? $frame_width    : 5);
+		$edge_width     = ($edge_width     ? $edge_width     : 1);
+		$hexcolor_frame = ($hexcolor_frame ? $hexcolor_frame : 'CCCCCC');
+		$hexcolor1      = ($hexcolor1      ? $hexcolor1      : 'FFFFFF');
+		$hexcolor2      = ($hexcolor2      ? $hexcolor2      : '000000');
+
+		$color_frame = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor_frame);
+		$color1      = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor1);
+		$color2      = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor2);
+		for ($i = 0; $i < $edge_width; $i++) {
+			// outer bevel
+			ImageLine($gdimg,                   $i,                   $i,                   $i, ImageSY($gdimg) - $i, $color1); // left
+			ImageLine($gdimg,                   $i,                   $i, ImageSX($gdimg) - $i,                   $i, $color1); // top
+			ImageLine($gdimg, ImageSX($gdimg) - $i, ImageSY($gdimg) - $i, ImageSX($gdimg) - $i,                   $i, $color2); // right
+			ImageLine($gdimg, ImageSX($gdimg) - $i, ImageSY($gdimg) - $i,                   $i, ImageSY($gdimg) - $i, $color2); // bottom
+		}
+		for ($i = 0; $i < $frame_width; $i++) {
+			// actual frame
+			ImageRectangle($gdimg, $edge_width + $i, $edge_width + $i, ImageSX($gdimg) - $edge_width - $i, ImageSY($gdimg) - $edge_width - $i, $color_frame);
+		}
+		for ($i = 0; $i < $edge_width; $i++) {
+			// inner bevel
+			ImageLine($gdimg,                   $frame_width + $edge_width + $i,                   $frame_width + $edge_width + $i,                   $frame_width + $edge_width + $i, ImageSY($gdimg) - $frame_width - $edge_width - $i, $color2); // left
+			ImageLine($gdimg,                   $frame_width + $edge_width + $i,                   $frame_width + $edge_width + $i, ImageSX($gdimg) - $frame_width - $edge_width - $i,                   $frame_width + $edge_width + $i, $color2); // top
+			ImageLine($gdimg, ImageSX($gdimg) - $frame_width - $edge_width - $i, ImageSY($gdimg) - $frame_width - $edge_width - $i, ImageSX($gdimg) - $frame_width - $edge_width - $i,                   $frame_width + $edge_width + $i, $color1); // right
+			ImageLine($gdimg, ImageSX($gdimg) - $frame_width - $edge_width - $i, ImageSY($gdimg) - $frame_width - $edge_width - $i,                   $frame_width + $edge_width + $i, ImageSY($gdimg) - $frame_width - $edge_width - $i, $color1); // bottom
+		}
+		return true;
+	}
+
+
+	function Gamma(&$gdimg, $amount) {
+		if (number_format($amount, 4) == '1.0000') {
+			return true;
+		}
+		return ImageGammaCorrect($gdimg, 1.0, $amount);
+	}
+
+
+	function Grayscale(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_GRAYSCALE)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_GRAYSCALE)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		return phpthumb_filters::Colorize($gdimg, 100, 'gray');
+	}
+
+
+	function HistogramAnalysis(&$gdimg, $calculateGray=false) {
+		$ImageSX = ImageSX($gdimg);
+		$ImageSY = ImageSY($gdimg);
+		for ($x = 0; $x < $ImageSX; $x++) {
+			for ($y = 0; $y < $ImageSY; $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				@$Analysis['red'][$OriginalPixel['red']]++;
+				@$Analysis['green'][$OriginalPixel['green']]++;
+				@$Analysis['blue'][$OriginalPixel['blue']]++;
+				@$Analysis['alpha'][$OriginalPixel['alpha']]++;
+				if ($calculateGray) {
+					$GrayPixel = phpthumb_functions::GrayscalePixel($OriginalPixel);
+					@$Analysis['gray'][$GrayPixel['red']]++;
+				}
+			}
+		}
+		$keys = array('red', 'green', 'blue', 'alpha');
+		if ($calculateGray) {
+			$keys[] = 'gray';
+		}
+		foreach ($keys as $dummy => $key) {
+			ksort($Analysis[$key]);
+		}
+		return $Analysis;
+	}
+
+
+	function HistogramStretch(&$gdimg, $band='*', $method=0, $threshold=0.1) {
+		// equivalent of "Auto Contrast" in Adobe Photoshop
+		// method 0 stretches according to RGB colors. Gives a more conservative stretch.
+		// method 1 band stretches according to grayscale which is color-biased (59% green, 30% red, 11% blue). May give a punchier / more aggressive stretch, possibly appearing over-saturated
+		$Analysis = phpthumb_filters::HistogramAnalysis($gdimg, true);
+		$keys = array('r'=>'red', 'g'=>'green', 'b'=>'blue', 'a'=>'alpha', '*'=>(($method == 0) ? 'all' : 'gray'));
+		$band = substr($band, 0, 1);
+		if (!isset($keys[$band])) {
+			return false;
+		}
+		$key = $keys[$band];
+
+		// If the absolute brightest and darkest pixels are used then one random
+		// pixel in the image could throw off the whole system. Instead, count up/down
+		// from the limit and allow <threshold> (default = 0.1%) of brightest/darkest
+		// pixels to be clipped to min/max
+		$threshold = floatval($threshold) / 100;
+		$clip_threshold = ImageSX($gdimg) * ImageSX($gdimg) * $threshold;
+		//if ($min >= 0) {
+		//	$range_min = min($min, 255);
+		//} else {
+			$countsum = 0;
+			for ($i = 0; $i <= 255; $i++) {
+				if ($method == 0) {
+					$countsum = max(@$Analysis['red'][$i], @$Analysis['green'][$i], @$Analysis['blue'][$i]);
+				} else {
+					$countsum += @$Analysis[$key][$i];
+				}
+				if ($countsum >= $clip_threshold) {
+					$range_min = $i - 1;
+					break;
+				}
+			}
+			$range_min = max($range_min, 0);
+		//}
+		//if ($max > 0) {
+		//	$range_max = max($max, 255);
+		//} else {
+			$countsum = 0;
+			for ($i = 255; $i >= 0; $i--) {
+				if ($method == 0) {
+					$countsum = max(@$Analysis['red'][$i], @$Analysis['green'][$i], @$Analysis['blue'][$i]);
+				} else {
+					$countsum += @$Analysis[$key][$i];
+				}
+				if ($countsum >= $clip_threshold) {
+					$range_max = $i + 1;
+					break;
+				}
+			}
+			$range_max = min($range_max, 255);
+		//}
+		$range_scale = (($range_max == $range_min) ? 1 : (255 / ($range_max - $range_min)));
+		if (($range_min == 0) && ($range_max == 255)) {
+			// no adjustment neccesary - don't waste CPU time!
+			return true;
+		}
+
+		$ImageSX = ImageSX($gdimg);
+		$ImageSY = ImageSY($gdimg);
+		for ($x = 0; $x < $ImageSX; $x++) {
+			for ($y = 0; $y < $ImageSY; $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				if ($band == '*') {
+					$new['red']   = min(255, max(0, ($OriginalPixel['red']   - $range_min) * $range_scale));
+					$new['green'] = min(255, max(0, ($OriginalPixel['green'] - $range_min) * $range_scale));
+					$new['blue']  = min(255, max(0, ($OriginalPixel['blue']  - $range_min) * $range_scale));
+					$new['alpha'] = min(255, max(0, ($OriginalPixel['alpha'] - $range_min) * $range_scale));
+				} else {
+					$new = $OriginalPixel;
+					$new[$key] = min(255, max(0, ($OriginalPixel[$key] - $range_min) * $range_scale));
+				}
+				$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $new['red'], $new['green'], $new['blue'], $new['alpha']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+
+		return true;
+	}
+
+
+	function HistogramOverlay(&$gdimg, $bands='*', $colors='', $width=0.25, $height=0.25, $alignment='BR', $opacity=50, $margin_x=5, $margin_y=null) {
+		$margin_y = (is_null($margin_y) ? $margin_x : $margin_y);
+
+		$Analysis = phpthumb_filters::HistogramAnalysis($gdimg, true);
+		$histW = round(($width > 1) ? min($width, ImageSX($gdimg)) : ImageSX($gdimg) * $width);
+		$histH = round(($width > 1) ? min($width, ImageSX($gdimg)) : ImageSX($gdimg) * $width);
+		if ($gdHist = ImageCreateTrueColor($histW, $histH)) {
+			$color_back = phpthumb_functions::ImageColorAllocateAlphaSafe($gdHist, 0, 0, 0, 127);
+			ImageFilledRectangle($gdHist, 0, 0, $histW, $histH, $color_back);
+			ImageAlphaBlending($gdHist, false);
+			ImageSaveAlpha($gdHist, true);
+
+			$HistogramTempWidth  = 256;
+			$HistogramTempHeight = 100;
+			if ($gdHistTemp = ImageCreateTrueColor($HistogramTempWidth, $HistogramTempHeight)) {
+				$color_back_temp = phpthumb_functions::ImageColorAllocateAlphaSafe($gdHistTemp, 255, 0, 255, 127);
+				ImageAlphaBlending($gdHistTemp, false);
+				ImageSaveAlpha($gdHistTemp, true);
+				ImageFilledRectangle($gdHistTemp, 0, 0, ImageSX($gdHistTemp), ImageSY($gdHistTemp), $color_back_temp);
+
+				$DefaultColors = array('r'=>'FF0000', 'g'=>'00FF00', 'b'=>'0000FF', 'a'=>'999999', '*'=>'FFFFFF');
+				$Colors = explode(';', $colors);
+				$BandsToGraph = array_unique(preg_split('//', $bands));
+				$keys = array('r'=>'red', 'g'=>'green', 'b'=>'blue', 'a'=>'alpha', '*'=>'gray');
+				foreach ($BandsToGraph as $key => $band) {
+					if (!isset($keys[$band])) {
+						continue;
+					}
+					$PeakValue = max($Analysis[$keys[$band]]);
+					$thisColor = phpthumb_functions::ImageHexColorAllocate($gdHistTemp, phpthumb_functions::IsHexColor(@$Colors[$key]) ? $Colors[$key] : $DefaultColors[$band]);
+					for ($x = 0; $x < $HistogramTempWidth; $x++) {
+						ImageLine($gdHistTemp, $x, $HistogramTempHeight - 1, $x, $HistogramTempHeight - 1 - round(@$Analysis[$keys[$band]][$x] / $PeakValue * $HistogramTempHeight), $thisColor);
+					}
+					ImageLine($gdHistTemp, 0, $HistogramTempHeight - 1, $HistogramTempWidth - 1, $HistogramTempHeight - 1, $thisColor);
+					ImageLine($gdHistTemp, 0, $HistogramTempHeight - 2, $HistogramTempWidth - 1, $HistogramTempHeight - 2, $thisColor);
+				}
+				ImageCopyResampled($gdHist, $gdHistTemp, 0, 0, 0, 0, ImageSX($gdHist), ImageSY($gdHist), ImageSX($gdHistTemp), ImageSY($gdHistTemp));
+				ImageDestroy($gdHistTemp);
+			} else {
+				return false;
+			}
+
+			phpthumb_filters::WatermarkOverlay($gdimg, $gdHist, $alignment, $opacity, $margin_x, $margin_y);
+			ImageDestroy($gdHist);
+			return true;
+		}
+		return false;
+	}
+
+
+	function ImageBorder(&$gdimg, $border_width, $radius_x, $radius_y, $hexcolor_border) {
+		$border_width = ($border_width ? $border_width : 1);
+		$radius_x     = ($radius_x     ? $radius_x     : 0);
+		$radius_y     = ($radius_y     ? $radius_y     : 0);
+
+		$output_width  = ImageSX($gdimg);
+		$output_height = ImageSY($gdimg);
+
+		list($new_width, $new_height) = phpthumb_functions::ProportionalResize($output_width, $output_height, $output_width - max($border_width * 2, $radius_x), $output_height - max($border_width * 2, $radius_y));
+		$offset_x = ($radius_x ? $output_width  - $new_width  - $radius_x : 0);
+		$offset_y = ($radius_y ? $output_height - $new_height - $radius_y : 0);
+
+//header('Content-Type: image/png');
+//ImagePNG($gdimg);
+//exit;
+		if ($gd_border_canvas = phpthumb_functions::ImageCreateFunction($output_width, $output_height)) {
+
+			ImageSaveAlpha($gd_border_canvas, true);
+			ImageAlphaBlending($gd_border_canvas, false);
+			$color_background = phpthumb_functions::ImageColorAllocateAlphaSafe($gd_border_canvas, 255, 255, 255, 127);
+			ImageFilledRectangle($gd_border_canvas, 0, 0, $output_width, $output_height, $color_background);
+
+			$color_border = phpthumb_functions::ImageHexColorAllocate($gd_border_canvas, (phpthumb_functions::IsHexColor($hexcolor_border) ? $hexcolor_border : '000000'));
+
+			for ($i = 0; $i < $border_width; $i++) {
+				ImageLine($gd_border_canvas,             floor($offset_x / 2) + $radius_x,                      $i, $output_width - $radius_x - ceil($offset_x / 2),                         $i, $color_border); // top
+				ImageLine($gd_border_canvas,             floor($offset_x / 2) + $radius_x, $output_height - 1 - $i, $output_width - $radius_x - ceil($offset_x / 2),    $output_height - 1 - $i, $color_border); // bottom
+				ImageLine($gd_border_canvas,                    floor($offset_x / 2) + $i,               $radius_y,                      floor($offset_x / 2) +  $i, $output_height - $radius_y, $color_border); // left
+				ImageLine($gd_border_canvas, $output_width - 1 - $i - ceil($offset_x / 2),               $radius_y,    $output_width - 1 - $i - ceil($offset_x / 2), $output_height - $radius_y, $color_border); // right
+			}
+
+			if ($radius_x && $radius_y) {
+
+				// PHP bug: ImageArc() with thicknesses > 1 give bad/undesirable/unpredicatable results
+				// Solution: Draw multiple 1px arcs side-by-side.
+
+				// Problem: parallel arcs give strange/ugly antialiasing problems
+				// Solution: draw non-parallel arcs, from one side of the line thickness at the start angle
+				//   to the opposite edge of the line thickness at the terminating angle
+				for ($thickness_offset = 0; $thickness_offset < $border_width; $thickness_offset++) {
+					ImageArc($gd_border_canvas, floor($offset_x / 2) + 1 +                 $radius_x,              $thickness_offset - 1 + $radius_y, $radius_x * 2, $radius_y * 2, 180, 270, $color_border); // top-left
+					ImageArc($gd_border_canvas,                     $output_width - $radius_x - 1 - ceil($offset_x / 2),              $thickness_offset - 1 + $radius_y, $radius_x * 2, $radius_y * 2, 270, 360, $color_border); // top-right
+					ImageArc($gd_border_canvas,                     $output_width - $radius_x - 1 - ceil($offset_x / 2), $output_height - $thickness_offset - $radius_y, $radius_x * 2, $radius_y * 2,   0,  90, $color_border); // bottom-right
+					ImageArc($gd_border_canvas, floor($offset_x / 2) + 1 +                 $radius_x, $output_height - $thickness_offset - $radius_y, $radius_x * 2, $radius_y * 2,  90, 180, $color_border); // bottom-left
+				}
+				if ($border_width > 1) {
+					for ($thickness_offset = 0; $thickness_offset < $border_width; $thickness_offset++) {
+						ImageArc($gd_border_canvas, floor($offset_x / 2) + $thickness_offset + $radius_x,                                      $radius_y, $radius_x * 2, $radius_y * 2, 180, 270, $color_border); // top-left
+						ImageArc($gd_border_canvas, $output_width - $thickness_offset - $radius_x - 1 - ceil($offset_x / 2),                                      $radius_y, $radius_x * 2, $radius_y * 2, 270, 360, $color_border); // top-right
+						ImageArc($gd_border_canvas, $output_width - $thickness_offset - $radius_x - 1 - ceil($offset_x / 2),                     $output_height - $radius_y, $radius_x * 2, $radius_y * 2,   0,  90, $color_border); // bottom-right
+						ImageArc($gd_border_canvas, floor($offset_x / 2) + $thickness_offset + $radius_x,                     $output_height - $radius_y, $radius_x * 2, $radius_y * 2,  90, 180, $color_border); // bottom-left
+					}
+				}
+
+			}
+			$this->phpThumbObject->ImageResizeFunction($gd_border_canvas, $gdimg, floor(($output_width - $new_width) / 2), round(($output_height - $new_height) / 2), 0, 0, $new_width, $new_height, $output_width, $output_height);
+
+			ImageDestroy($gdimg);
+			$gdimg = phpthumb_functions::ImageCreateFunction($output_width, $output_height);
+			ImageSaveAlpha($gdimg, true);
+			ImageAlphaBlending($gdimg, false);
+			$gdimg_color_background = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 255, 255, 255, 127);
+			ImageFilledRectangle($gdimg, 0, 0, $output_width, $output_height, $gdimg_color_background);
+
+			ImageCopy($gdimg, $gd_border_canvas, 0, 0, 0, 0, $output_width, $output_height);
+			//$gdimg = $gd_border_canvas;
+			ImageDestroy($gd_border_canvas);
+			return true;
+
+
+		} else {
+			$this->DebugMessage('FAILED: $gd_border_canvas = phpthumb_functions::ImageCreateFunction('.$output_width.', '.$output_height.')', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	function ImprovedImageRotate(&$gdimg_source, $rotate_angle=0, $config_background_hexcolor='FFFFFF', $bg=null) {
+		while ($rotate_angle < 0) {
+			$rotate_angle += 360;
+		}
+		$rotate_angle = $rotate_angle % 360;
+		if ($rotate_angle != 0) {
+
+			$background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_source, $config_background_hexcolor);
+
+			if ((phpthumb_functions::gd_version() >= 2) && !$bg && ($rotate_angle % 90)) {
+
+				//$this->DebugMessage('Using alpha rotate', __FILE__, __LINE__);
+				if ($gdimg_rotate_mask = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg_source), ImageSY($gdimg_source))) {
+
+					for ($i = 0; $i <= 255; $i++) {
+						$color_mask[$i] = ImageColorAllocate($gdimg_rotate_mask, $i, $i, $i);
+					}
+					ImageFilledRectangle($gdimg_rotate_mask, 0, 0, ImageSX($gdimg_rotate_mask), ImageSY($gdimg_rotate_mask), $color_mask[255]);
+					$imageX = ImageSX($gdimg_source);
+					$imageY = ImageSY($gdimg_source);
+					for ($x = 0; $x < $imageX; $x++) {
+						for ($y = 0; $y < $imageY; $y++) {
+							$pixelcolor = phpthumb_functions::GetPixelColor($gdimg_source, $x, $y);
+							ImageSetPixel($gdimg_rotate_mask, $x, $y, $color_mask[255 - round($pixelcolor['alpha'] * 255 / 127)]);
+						}
+					}
+					$gdimg_rotate_mask  = ImageRotate($gdimg_rotate_mask,  $rotate_angle, $color_mask[0]);
+					$gdimg_source = ImageRotate($gdimg_source, $rotate_angle, $background_color);
+
+					ImageAlphaBlending($gdimg_source, false);
+					ImageSaveAlpha($gdimg_source, true);
+					//$this->is_alpha = true;
+					$phpThumbFilters = new phpthumb_filters();
+					$phpThumbFilters->phpThumbObject = $this;
+					$phpThumbFilters->ApplyMask($gdimg_rotate_mask, $gdimg_source);
+
+					ImageDestroy($gdimg_rotate_mask);
+
+				} else {
+					//$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
+				}
+
+			} else {
+
+				if (phpthumb_functions::gd_version() < 2) {
+					//$this->DebugMessage('Using non-alpha rotate because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+				} elseif ($bg) {
+					//$this->DebugMessage('Using non-alpha rotate because $this->bg is "'.$bg.'"', __FILE__, __LINE__);
+				} elseif ($rotate_angle % 90) {
+					//$this->DebugMessage('Using non-alpha rotate because ($rotate_angle % 90) = "'.($rotate_angle % 90).'"', __FILE__, __LINE__);
+				} else {
+					//$this->DebugMessage('Using non-alpha rotate because $this->thumbnailFormat is "'.$this->thumbnailFormat.'"', __FILE__, __LINE__);
+				}
+
+				if (ImageColorTransparent($gdimg_source) >= 0) {
+					// ImageRotate() forgets all about an image's transparency and sets the transparent color to black
+					// To compensate, flood-fill the transparent color of the source image with the specified background color first
+					// then rotate and the colors should match
+
+					if (!function_exists('ImageIsTrueColor') || !ImageIsTrueColor($gdimg_source)) {
+						// convert paletted image to true-color before rotating to prevent nasty aliasing artifacts
+
+						//$this->source_width  = ImageSX($gdimg_source);
+						//$this->source_height = ImageSY($gdimg_source);
+						$gdimg_newsrc = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg_source), ImageSY($gdimg_source));
+						$background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_newsrc, $config_background_hexcolor);
+						ImageFilledRectangle($gdimg_newsrc, 0, 0, ImageSX($gdimg_source), ImageSY($gdimg_source), phpthumb_functions::ImageHexColorAllocate($gdimg_newsrc, $config_background_hexcolor));
+						ImageCopy($gdimg_newsrc, $gdimg_source, 0, 0, 0, 0, ImageSX($gdimg_source), ImageSY($gdimg_source));
+						ImageDestroy($gdimg_source);
+						unset($gdimg_source);
+						$gdimg_source = $gdimg_newsrc;
+						unset($gdimg_newsrc);
+
+					} else {
+
+						ImageColorSet(
+							$gdimg_source,
+							ImageColorTransparent($gdimg_source),
+							hexdec(substr($config_background_hexcolor, 0, 2)),
+							hexdec(substr($config_background_hexcolor, 2, 2)),
+							hexdec(substr($config_background_hexcolor, 4, 2)));
+
+						ImageColorTransparent($gdimg_source, -1);
+
+					}
+				}
+
+				$gdimg_source = ImageRotate($gdimg_source, $rotate_angle, $background_color);
+
+			}
+		}
+		return true;
+	}
+
+
+	function MeanRemoval(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_MEAN_REMOVAL)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_MEAN_REMOVAL)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::MeanRemoval($gdimg) [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Negative(&$gdimg) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_NEGATE)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_NEGATE)', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		$ImageSX = ImageSX($gdimg);
+		$ImageSY = ImageSY($gdimg);
+		for ($x = 0; $x < $ImageSX; $x++) {
+			for ($y = 0; $y < $ImageSY; $y++) {
+				$currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, (~$currentPixel['red'] & 0xFF), (~$currentPixel['green'] & 0xFF), (~$currentPixel['blue'] & 0xFF), $currentPixel['alpha']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function RoundedImageCorners(&$gdimg, $radius_x, $radius_y) {
+		// generate mask at twice desired resolution and downsample afterwards for easy antialiasing
+		// mask is generated as a white double-size elipse on a triple-size black background and copy-paste-resampled
+		// onto a correct-size mask image as 4 corners due to errors when the entire mask is resampled at once (gray edges)
+		if ($gdimg_cornermask_triple = phpthumb_functions::ImageCreateFunction($radius_x * 6, $radius_y * 6)) {
+			if ($gdimg_cornermask = phpthumb_functions::ImageCreateFunction(ImageSX($gdimg), ImageSY($gdimg))) {
+
+				$color_transparent = ImageColorAllocate($gdimg_cornermask_triple, 255, 255, 255);
+				ImageFilledEllipse($gdimg_cornermask_triple, $radius_x * 3, $radius_y * 3, $radius_x * 4, $radius_y * 4, $color_transparent);
+
+				ImageFilledRectangle($gdimg_cornermask, 0, 0, ImageSX($gdimg), ImageSY($gdimg), $color_transparent);
+
+				ImageCopyResampled($gdimg_cornermask, $gdimg_cornermask_triple,                           0,                           0,     $radius_x,     $radius_y, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2);
+				ImageCopyResampled($gdimg_cornermask, $gdimg_cornermask_triple,                           0, ImageSY($gdimg) - $radius_y,     $radius_x, $radius_y * 3, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2);
+				ImageCopyResampled($gdimg_cornermask, $gdimg_cornermask_triple, ImageSX($gdimg) - $radius_x, ImageSY($gdimg) - $radius_y, $radius_x * 3, $radius_y * 3, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2);
+				ImageCopyResampled($gdimg_cornermask, $gdimg_cornermask_triple, ImageSX($gdimg) - $radius_x,                           0, $radius_x * 3,     $radius_y, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2);
+
+				phpthumb_filters::ApplyMask($gdimg_cornermask, $gdimg);
+				ImageDestroy($gdimg_cornermask);
+				$this->DebugMessage('RoundedImageCorners('.$radius_x.', '.$radius_y.') succeeded', __FILE__, __LINE__);
+				return true;
+
+			} else {
+				$this->DebugMessage('FAILED: $gdimg_cornermask = phpthumb_functions::ImageCreateFunction('.ImageSX($gdimg).', '.ImageSY($gdimg).')', __FILE__, __LINE__);
+			}
+			ImageDestroy($gdimg_cornermask_triple);
+
+		} else {
+			$this->DebugMessage('FAILED: $gdimg_cornermask_triple = phpthumb_functions::ImageCreateFunction('.($radius_x * 6).', '.($radius_y * 6).')', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	function Saturation(&$gdimg, $amount, $color='') {
+		if ($amount == 0) {
+			return true;
+		} elseif ($amount > 0) {
+			$amount = 0 - $amount;
+		} else {
+			$amount = abs($amount);
+		}
+		return phpthumb_filters::Desaturate($gdimg, $amount, $color);
+	}
+
+
+	function Sepia(&$gdimg, $amount, $targetColor) {
+		$amount      = (is_numeric($amount) ? max(0, min(100, $amount)) : 50);
+		$targetColor = (phpthumb_functions::IsHexColor($targetColor) ? $targetColor : 'A28065');
+
+		if ($amount == 0) {
+			return true;
+		}
+
+		$TargetPixel['red']   = hexdec(substr($targetColor, 0, 2));
+		$TargetPixel['green'] = hexdec(substr($targetColor, 2, 2));
+		$TargetPixel['blue']  = hexdec(substr($targetColor, 4, 2));
+
+		$ImageSX = ImageSX($gdimg);
+		$ImageSY = ImageSY($gdimg);
+		for ($x = 0; $x < $ImageSX; $x++) {
+			for ($y = 0; $y < $ImageSY; $y++) {
+				$OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				$GrayPixel = phpthumb_functions::GrayscalePixel($OriginalPixel);
+
+				// http://www.gimpguru.org/Tutorials/SepiaToning/
+				// "In the traditional sepia toning process, the tinting occurs most in
+				// the mid-tones: the lighter and darker areas appear to be closer to B&W."
+				$SepiaAmount = ((128 - abs($GrayPixel['red'] - 128)) / 128) * ($amount / 100);
+
+				foreach ($TargetPixel as $key => $value) {
+					$NewPixel[$key] = round(max(0, min(255, $GrayPixel[$key] * (1 - $SepiaAmount) + ($TargetPixel[$key] * $SepiaAmount))));
+				}
+				$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue'], $OriginalPixel['alpha']);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function Smooth(&$gdimg, $amount=6) {
+		$amount = min(25, max(0, $amount));
+		if ($amount == 0) {
+			return true;
+		}
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+			if (ImageFilter($gdimg, IMG_FILTER_SMOOTH, $amount)) {
+				return true;
+			}
+			$this->DebugMessage('FAILED: ImageFilter($gdimg, IMG_FILTER_SMOOTH, '.$amount.')', __FILE__, __LINE__);
+			// fall through and try it the hard way
+		}
+		// currently not implemented "the hard way"
+		$this->DebugMessage('FAILED: phpthumb_filters::Smooth($gdimg, '.$amount.') [function not implemented]', __FILE__, __LINE__);
+		return false;
+	}
+
+
+	function Threshold(&$gdimg, $cutoff) {
+		$cutoff = min(255, max(0, ($cutoff ? $cutoff : 128)));
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				$grayPixel = phpthumb_functions::GrayscalePixel($currentPixel);
+				if ($grayPixel['red'] < $cutoff) {
+					$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0x00, 0x00, 0x00, $currentPixel['alpha']);
+				} else {
+					$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0xFF, 0xFF, 0xFF, $currentPixel['alpha']);
+				}
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function ImageTrueColorToPalette2(&$image, $dither, $ncolors) {
+		// http://www.php.net/manual/en/function.imagetruecolortopalette.php
+		// zmorris at zsculpt dot com (17-Aug-2004 06:58)
+		$width  = ImageSX($image);
+		$height = ImageSY($image);
+		$image_copy = ImageCreateTrueColor($width, $height);
+		//ImageCopyMerge($image_copy, $image, 0, 0, 0, 0, $width, $height, 100);
+		ImageCopy($image_copy, $image, 0, 0, 0, 0, $width, $height);
+		ImageTrueColorToPalette($image, $dither, $ncolors);
+		ImageColorMatch($image_copy, $image);
+		ImageDestroy($image_copy);
+		return true;
+	}
+
+	function ReduceColorDepth(&$gdimg, $colors=256, $dither=true) {
+		$colors = max(min($colors, 256), 2);
+		// ImageTrueColorToPalette usually makes ugly colors, the replacement is a bit better
+		//ImageTrueColorToPalette($gdimg, $dither, $colors);
+		phpthumb_filters::ImageTrueColorToPalette2($gdimg, $dither, $colors);
+		return true;
+	}
+
+
+	function WhiteBalance(&$gdimg, $targetColor='') {
+		if (phpthumb_functions::IsHexColor($targetColor)) {
+			$targetPixel = array(
+				'red'   => hexdec(substr($targetColor, 0, 2)),
+				'green' => hexdec(substr($targetColor, 2, 2)),
+				'blue'  => hexdec(substr($targetColor, 4, 2))
+			);
+		} else {
+			$Analysis = phpthumb_filters::HistogramAnalysis($gdimg, false);
+			$targetPixel = array(
+				'red'   => max(array_keys($Analysis['red'])),
+				'green' => max(array_keys($Analysis['green'])),
+				'blue'  => max(array_keys($Analysis['blue']))
+			);
+		}
+		$grayValue = phpthumb_functions::GrayscaleValue($targetPixel['red'], $targetPixel['green'], $targetPixel['blue']);
+		$scaleR = $grayValue / $targetPixel['red'];
+		$scaleG = $grayValue / $targetPixel['green'];
+		$scaleB = $grayValue / $targetPixel['blue'];
+
+		for ($x = 0; $x < ImageSX($gdimg); $x++) {
+			for ($y = 0; $y < ImageSY($gdimg); $y++) {
+				$currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y);
+				$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe(
+					$gdimg,
+					max(0, min(255, round($currentPixel['red']   * $scaleR))),
+					max(0, min(255, round($currentPixel['green'] * $scaleG))),
+					max(0, min(255, round($currentPixel['blue']  * $scaleB))),
+					$currentPixel['alpha']
+				);
+				ImageSetPixel($gdimg, $x, $y, $newColor);
+			}
+		}
+		return true;
+	}
+
+
+	function WatermarkText(&$gdimg, $text, $size, $alignment, $hex_color='000000', $ttffont='', $opacity=100, $margin=5, $angle=0, $bg_color=false, $bg_opacity=0, $fillextend='') {
+		// text watermark requested
+		if (!$text) {
+			return false;
+		}
+		ImageAlphaBlending($gdimg, true);
+
+		$metaTextArray = array(
+			'^Fb' =>       $this->phpThumbObject->getimagesizeinfo['filesize'],
+			'^Fk' => round($this->phpThumbObject->getimagesizeinfo['filesize'] / 1024),
+			'^Fm' => round($this->phpThumbObject->getimagesizeinfo['filesize'] / 1048576),
+			'^X'  => $this->phpThumbObject->getimagesizeinfo[0],
+			'^Y'  => $this->phpThumbObject->getimagesizeinfo[1],
+			'^x'  => ImageSX($gdimg),
+			'^y'  => ImageSY($gdimg),
+			'^^'  => '^',
+		);
+		$text = strtr($text, $metaTextArray);
+
+		$text = str_replace("\r\n", "\n", $text);
+		$text = str_replace("\r",   "\n", $text);
+		$textlines = explode("\n", $text);
+
+		if (@is_readable($ttffont) && is_file($ttffont)) {
+
+			$opacity = 100 - intval(max(min($opacity, 100), 0));
+
+			$this->DebugMessage('Using TTF font "'.$ttffont.'"', __FILE__, __LINE__);
+
+			$TTFbox = ImageTTFbBox($size, $angle, $ttffont, $text);
+
+			$min_x = min($TTFbox[0], $TTFbox[2], $TTFbox[4], $TTFbox[6]);
+			$max_x = max($TTFbox[0], $TTFbox[2], $TTFbox[4], $TTFbox[6]);
+			//$text_width = round($max_x - $min_x + ($size * 0.5));
+			$text_width = round($max_x - $min_x);
+
+			$min_y = min($TTFbox[1], $TTFbox[3], $TTFbox[5], $TTFbox[7]);
+			$max_y = max($TTFbox[1], $TTFbox[3], $TTFbox[5], $TTFbox[7]);
+			//$text_height = round($max_y - $min_y + ($size * 0.5));
+			$text_height = round($max_y - $min_y);
+
+			$TTFboxChar = ImageTTFbBox($size, $angle, $ttffont, 'jH');
+			$char_min_y = min($TTFboxChar[1], $TTFboxChar[3], $TTFboxChar[5], $TTFboxChar[7]);
+			$char_max_y = max($TTFboxChar[1], $TTFboxChar[3], $TTFboxChar[5], $TTFboxChar[7]);
+			$char_height = round($char_max_y - $char_min_y);
+
+			switch ($alignment) {
+				case 'T':
+					$text_origin_x = round((ImageSX($gdimg) - $text_width) / 2);
+					$text_origin_y = $char_height + $margin;
+					break;
+
+				case 'B':
+					$text_origin_x = round((ImageSX($gdimg) - $text_width) / 2);
+					$text_origin_y = ImageSY($gdimg) + $TTFbox[1] - $margin;
+					break;
+
+				case 'L':
+					$text_origin_x = $margin;
+					$text_origin_y = round((ImageSY($gdimg) - $text_height) / 2) + $char_height;
+					break;
+
+				case 'R':
+					$text_origin_x = ImageSX($gdimg) - $text_width  + $TTFbox[0] - $min_x + round($size * 0.25) - $margin;
+					$text_origin_y = round((ImageSY($gdimg) - $text_height) / 2) + $char_height;
+					break;
+
+				case 'C':
+					$text_origin_x = round((ImageSX($gdimg) - $text_width) / 2);
+					$text_origin_y = round((ImageSY($gdimg) - $text_height) / 2) + $char_height;
+					break;
+
+				case 'TL':
+					$text_origin_x = $margin;
+					$text_origin_y = $char_height + $margin;
+					break;
+
+				case 'TR':
+					$text_origin_x = ImageSX($gdimg) - $text_width  + $TTFbox[0] - $min_x + round($size * 0.25) - $margin;
+					$text_origin_y = $char_height + $margin;
+					break;
+
+				case 'BL':
+					$text_origin_x = $margin;
+					$text_origin_y = ImageSY($gdimg) + $TTFbox[1] - $margin;
+					break;
+
+				case 'BR':
+				default:
+					$text_origin_x = ImageSX($gdimg) - $text_width  + $TTFbox[0] - $min_x + round($size * 0.25) - $margin;
+					$text_origin_y = ImageSY($gdimg) + $TTFbox[1] - $margin;
+					break;
+			}
+			$letter_color_text = phpthumb_functions::ImageHexColorAllocate($gdimg, $hex_color, false, $opacity * 1.27);
+
+			if ($alignment == '*') {
+
+				$text_origin_y = $char_height + $margin;
+				while (($text_origin_y - $text_height) < ImageSY($gdimg)) {
+					$text_origin_x = $margin;
+					while ($text_origin_x < ImageSX($gdimg)) {
+						ImageTTFtext($gdimg, $size, $angle, $text_origin_x, $text_origin_y, $letter_color_text, $ttffont, $text);
+						$text_origin_x += ($text_width + $margin);
+					}
+					$text_origin_y += ($text_height + $margin);
+				}
+
+			} else {
+
+				//ImageRectangle($gdimg, $text_origin_x + $min_x, $text_origin_y + $TTFbox[1], $text_origin_x + $min_x + $text_width, $text_origin_y + $TTFbox[1] - $text_height, $letter_color_text);
+				if (phpthumb_functions::IsHexColor($bg_color)) {
+					$text_background_alpha = round(127 * ((100 - min(max(0, $bg_opacity), 100)) / 100));
+					$text_color_background = phpthumb_functions::ImageHexColorAllocate($gdimg, $bg_color, false, $text_background_alpha);
+				} else {
+					$text_color_background = phpthumb_functions::ImageHexColorAllocate($gdimg, 'FFFFFF', false, 127);
+				}
+				$x1 = $text_origin_x + $min_x;
+				$y1 = $text_origin_y + $TTFbox[1];
+				$x2 = $text_origin_x + $min_x + $text_width;
+				$y2 = $text_origin_y + $TTFbox[1] - $text_height;
+				$x_TL = eregi('x', $fillextend) ?               0 : min($x1, $x2);
+				$y_TL = eregi('y', $fillextend) ?               0 : min($y1, $y2);
+				$x_BR = eregi('x', $fillextend) ? ImageSX($gdimg) : max($x1, $x2);
+				$y_BR = eregi('y', $fillextend) ? ImageSY($gdimg) : max($y1, $y2);
+				//while ($y_BR > ImageSY($gdimg)) {
+				//	$y_TL--;
+				//	$y_BR--;
+				//	$text_origin_y--;
+				//}
+				ImageFilledRectangle($gdimg, $x_TL, $y_TL, $x_BR, $y_BR, $text_color_background);
+				ImageTTFtext($gdimg, $size, $angle, $text_origin_x, $text_origin_y, $letter_color_text, $ttffont, $text);
+
+			}
+			return true;
+
+		} else {
+
+			$size = min(5, max(1, $size));
+			$this->DebugMessage('Using built-in font (size='.$size.') for text watermark'.($ttffont ? ' because $ttffont !is_readable('.$ttffont.')' : ''), __FILE__, __LINE__);
+
+			$text_width  = 0;
+			$text_height = 0;
+			foreach ($textlines as $dummy => $line) {
+				$text_width   = max($text_width, ImageFontWidth($size) * strlen($line));
+				$text_height += ImageFontHeight($size);
+			}
+			if ($img_watermark = phpthumb_functions::ImageCreateFunction($text_width, $text_height)) {
+				ImageAlphaBlending($img_watermark, false);
+				if (phpthumb_functions::IsHexColor($bg_color)) {
+					$text_background_alpha = round(127 * ((100 - min(max(0, $bg_opacity), 100)) / 100));
+					$text_color_background = phpthumb_functions::ImageHexColorAllocate($img_watermark, $bg_color, false, $text_background_alpha);
+				} else {
+					$text_color_background = phpthumb_functions::ImageHexColorAllocate($img_watermark, 'FFFFFF', false, 127);
+				}
+				ImageFilledRectangle($img_watermark, 0, 0, ImageSX($img_watermark), ImageSY($img_watermark), $text_color_background);
+
+				if ($angle && function_exists('ImageRotate')) {
+					// using $img_watermark_mask is pointless if ImageRotate function isn't available
+					if ($img_watermark_mask = phpthumb_functions::ImageCreateFunction($text_width, $text_height)) {
+						$mask_color_background = ImageColorAllocate($img_watermark_mask, 0, 0, 0);
+						ImageAlphaBlending($img_watermark_mask, false);
+						ImageFilledRectangle($img_watermark_mask, 0, 0, ImageSX($img_watermark_mask), ImageSY($img_watermark_mask), $mask_color_background);
+						$mask_color_watermark = ImageColorAllocate($img_watermark_mask, 255, 255, 255);
+					}
+				}
+
+				$text_color_watermark = phpthumb_functions::ImageHexColorAllocate($img_watermark, $hex_color);
+				foreach ($textlines as $key => $line) {
+					switch ($alignment) {
+						case 'C':
+						case 'T':
+						case 'B':
+							$x_offset = round(($text_width - (ImageFontWidth($size) * strlen($line))) / 2);
+							break;
+
+						case 'L':
+						case 'TL':
+						case 'BL':
+							$x_offset = 0;
+							break;
+
+						case 'R':
+						case 'TR':
+						case 'BR':
+						default:
+							$x_offset = $text_width - (ImageFontWidth($size) * strlen($line));
+							break;
+					}
+					ImageString($img_watermark, $size, $x_offset, $key * ImageFontHeight($size), $line, $text_color_watermark);
+					if ($angle && $img_watermark_mask) {
+						ImageString($img_watermark_mask, $size, $x_offset, $key * ImageFontHeight($size), $text, $mask_color_watermark);
+					}
+				}
+				if ($angle && $img_watermark_mask) {
+					$img_watermark      = ImageRotate($img_watermark,      $angle, $text_color_background);
+					$img_watermark_mask = ImageRotate($img_watermark_mask, $angle, $mask_color_background);
+					phpthumb_filters::ApplyMask($img_watermark_mask, $img_watermark);
+				}
+				phpthumb_filters::WatermarkOverlay($gdimg, $img_watermark, $alignment, $opacity, $margin);
+				ImageDestroy($img_watermark);
+				return true;
+			}
+
+		}
+		return false;
+	}
+
+
+	function WatermarkOverlay(&$gdimg_dest, &$img_watermark, $alignment='*', $opacity=50, $margin_x=5, $margin_y=null) {
+		if (is_resource($gdimg_dest) && is_resource($img_watermark)) {
+			$watermark_source_x        = 0;
+			$watermark_source_y        = 0;
+			$img_source_width          = ImageSX($gdimg_dest);
+			$img_source_height         = ImageSY($gdimg_dest);
+			$watermark_source_width    = ImageSX($img_watermark);
+			$watermark_source_height   = ImageSY($img_watermark);
+			$watermark_opacity_percent = max(0, min(100, $opacity));
+			$margin_y = (is_null($margin_y) ? $margin_x : $margin_y);
+			$watermark_margin_x = ((($margin_x > 0) && ($margin_x < 1)) ? round((1 - $margin_x) * $img_source_width)  : $margin_x);
+			$watermark_margin_y = ((($margin_y > 0) && ($margin_y < 1)) ? round((1 - $margin_y) * $img_source_height) : $margin_y);
+			switch ($alignment) {
+				case '*':
+					if ($gdimg_tiledwatermark = phpthumb_functions::ImageCreateFunction($img_source_width, $img_source_height)) {
+
+						ImageAlphaBlending($gdimg_tiledwatermark, false);
+						ImageSaveAlpha($gdimg_tiledwatermark, true);
+						$text_color_transparent = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_tiledwatermark, 255, 0, 255, 127);
+						ImageFill($gdimg_tiledwatermark, 0, 0, $text_color_transparent);
+
+						// set the tiled image transparent color to whatever the untiled image transparency index is
+//						ImageColorTransparent($gdimg_tiledwatermark, ImageColorTransparent($img_watermark));
+
+						// a "cleaner" way of doing it, but can't handle the margin feature :(
+//						ImageSetTile($gdimg_tiledwatermark, $img_watermark);
+//						ImageFill($gdimg_tiledwatermark, 0, 0, IMG_COLOR_TILED);
+//						break;
+
+//						ImageFill($gdimg_tiledwatermark, 0, 0, ImageColorTransparent($gdimg_tiledwatermark));
+						// tile the image as many times as can fit
+						for ($x = $watermark_margin_x; $x < ($img_source_width + $watermark_source_width); $x += ($watermark_source_width + $watermark_margin_x)) {
+							for ($y = $watermark_margin_y; $y < ($img_source_height + $watermark_source_height); $y += ($watermark_source_height + $watermark_margin_y)) {
+								ImageCopy(
+									$gdimg_tiledwatermark,
+									$img_watermark,
+									$x,
+									$y,
+									0,
+									0,
+									min($watermark_source_width,  $img_source_width  - $x - $watermark_margin_x),
+									min($watermark_source_height, $img_source_height - $y - $watermark_margin_y)
+								);
+							}
+						}
+
+						$watermark_source_width  = ImageSX($gdimg_tiledwatermark);
+						$watermark_source_height = ImageSY($gdimg_tiledwatermark);
+						$watermark_destination_x = 0;
+						$watermark_destination_y = 0;
+
+						ImageDestroy($img_watermark);
+						$img_watermark = $gdimg_tiledwatermark;
+					}
+					break;
+
+				case 'T':
+					$watermark_destination_x = round((($img_source_width  / 2) - ($watermark_source_width / 2)) + $watermark_margin_x);
+					$watermark_destination_y = $watermark_margin_y;
+					break;
+
+				case 'B':
+					$watermark_destination_x = round((($img_source_width  / 2) - ($watermark_source_width / 2)) + $watermark_margin_x);
+					$watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y;
+					break;
+
+				case 'L':
+					$watermark_destination_x = $watermark_margin_x;
+					$watermark_destination_y = round((($img_source_height / 2) - ($watermark_source_height / 2)) + $watermark_margin_y);
+					break;
+
+				case 'R':
+					$watermark_destination_x = $img_source_width - $watermark_source_width - $watermark_margin_x;
+					$watermark_destination_y = round((($img_source_height / 2) - ($watermark_source_height / 2)) + $watermark_margin_y);
+					break;
+
+				case 'C':
+					$watermark_destination_x = round(($img_source_width  / 2) - ($watermark_source_width  / 2));
+					$watermark_destination_y = round(($img_source_height / 2) - ($watermark_source_height / 2));
+					break;
+
+				case 'TL':
+					$watermark_destination_x = $watermark_margin_x;
+					$watermark_destination_y = $watermark_margin_y;
+					break;
+
+				case 'TR':
+					$watermark_destination_x = $img_source_width - $watermark_source_width - $watermark_margin_x;
+					$watermark_destination_y = $watermark_margin_y;
+					break;
+
+				case 'BL':
+//echo '<pre>';
+////var_dump($watermark_destination_x);
+////var_dump($watermark_destination_y);
+//var_dump($watermark_margin_x);
+//var_dump($img_source_height);
+//var_dump($watermark_source_height);
+//var_dump($watermark_margin_y);
+					$watermark_destination_x = $watermark_margin_x;
+					$watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y;
+					break;
+
+				case 'BR':
+				default:
+					$watermark_destination_x = $img_source_width  - $watermark_source_width  - $watermark_margin_x;
+					$watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y;
+					break;
+			}
+			ImageAlphaBlending($gdimg_dest, false);
+			ImageSaveAlpha($gdimg_dest, true);
+			ImageSaveAlpha($img_watermark, true);
+			phpthumb_functions::ImageCopyRespectAlpha($gdimg_dest, $img_watermark, $watermark_destination_x, $watermark_destination_y, 0, 0, $watermark_source_width, $watermark_source_height, $watermark_opacity_percent);
+
+			return true;
+		}
+		return false;
+	}
+
+
+	function DebugMessage($message, $file='', $line='') {
+		if (is_object($this->phpThumbObject)) {
+			return $this->phpThumbObject->DebugMessage($message, $file, $line);
+		}
+		return false;
+	}
+}
+
+?>
Index: /afridex/plugins/Flutter/wpmu/Flutter.php
===================================================================
--- /afridex/plugins/Flutter/wpmu/Flutter.php (revision 21)
+++ /afridex/plugins/Flutter/wpmu/Flutter.php (revision 21)
@@ -0,0 +1,3 @@
+<?php
+include_once "Flutter/Main.php";
+?>
Index: /afridex/plugins/Flutter/RCCWP_GetDuplicate.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_GetDuplicate.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_GetDuplicate.php (revision 21)
@@ -0,0 +1,24 @@
+<?php
+	require( dirname(__FILE__) . '/../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+		
+	require_once("RCCWP_WritePostPage.php");
+	require_once("RCCWP_CustomGroup.php");
+
+	if( isset($_POST['flag']) && $_POST['flag'] == "group" )
+	{
+		$customGroup = RCCWP_CustomGroup::Get( $_POST['groupId'] ) ;
+		RCCWP_WritePostPage::GroupDuplicate($customGroup,$_POST['groupCounter']) ;
+	}
+
+	else
+	{
+		$customFieldId = $_POST['customFieldId'];
+		$groupCounter = $_POST['groupCounter'];
+		$fieldCounter = $_POST['fieldCounter'];
+		RCCWP_WritePostPage::CustomFieldInterface($customFieldId, $groupCounter, $fieldCounter);
+		?>
+		<?php
+	}
+?>
Index: /afridex/plugins/Flutter/cache/index.php
===================================================================
--- /afridex/plugins/Flutter/cache/index.php (revision 21)
+++ /afridex/plugins/Flutter/cache/index.php (revision 21)
@@ -0,0 +1,4 @@
+<?php
+header('Location: /');
+exit;
+?>
Index: /afridex/plugins/Flutter/RCCWP_SnipshotCallback.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_SnipshotCallback.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_SnipshotCallback.php (revision 21)
@@ -0,0 +1,111 @@
+<?php
+
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can('edit_posts')))
+	die("Athentication failed!");
+
+
+	//print_r("Req\n");
+	//print_r($_REQUEST);
+	//print_r("Files\n");
+	//print_r($_FILES);
+	//print_r("Get\n");
+	//print_r($_GET);
+	//print_r("Post\n");
+	//print_r($_POST);
+//$filepath = dirname(__FILE__) . '/files_flutter/log.txt';
+//file_put_contents($filepath, , FILE_APPEND);
+
+//
+    //Snipshot callback test
+    //
+    //Install this script on your server to test and debug callbacks from 
+    //www.snipshot.com. Note: this script will need read and write access 
+    //to your image directory.
+
+    //CHANGE THIS TO THE DIRECTORY WHERE YOUR IMAGES WILL BE SAVED
+    $IMG_DIR = './files_flutter';
+    chdir($IMG_DIR);
+    
+    //CHANGE THIS TO THE NAME OF THE FIELD THAT CARRIES THE IMAGE DATA OR URL
+    $OUTPUT = 'file';
+
+    function print_pre($r, $desc=''){
+        echo '<pre>' . $desc . ' ' . print_r($r) . '</pre>';
+    }
+    function glob_rsort_modtime( $patt ) {
+        if ( ( $files = @glob($patt, GLOB_BRACE) ) === false )
+            return array(false, 'Glob error.');
+        if ( !count($files) )
+            return array(false, 'No files found.');
+        $rtn = array();
+        foreach ( $files as $filename )
+            $rtn[$filename] = filemtime($filename);
+        arsort($rtn);
+        reset($rtn);
+        foreach ( $rtn as $filename => $t )
+            $rtn[$filename] = date('r', $t);
+        return $rtn;
+    }
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>Snipshot callback</title>
+    
+    <?	
+	$operationSuccess = "false";
+        if (!empty($_REQUEST)){
+            //print_pre($_REQUEST, 'REQUEST');
+            if (!empty($_FILES)){ //snipshot_callback_agent=snipshot
+                //print_pre($_FILES[$OUTPUT], 'FILES');    
+                move_uploaded_file(
+                    $_FILES[$OUTPUT]['tmp_name'], 
+                    $_FILES[$OUTPUT]['name']
+                );//file_put_contents($filepath, time()."trying ".$_FILES[$OUTPUT]['name']."\n", FILE_APPEND);
+		//file_put_contents("log.txt", time()."trying ".basename($_GET[$OUTPUT]));
+            } 
+            else if (!empty($_GET[$OUTPUT])){ //snipshot_callback_agent=user
+                $data = file_get_contents($_GET[$OUTPUT]);
+		//echo "\nfrom ----".$_GET[$OUTPUT]."\n";
+                $fp = fopen(basename($_GET[$OUTPUT]), 'wb');
+		//echo "\nfrom ----".basename($_GET[$OUTPUT])."\n";
+                fwrite($fp, $data);
+                fclose($fp);
+		chmod(basename($_GET[$OUTPUT]), 0644);
+		$filename = basename($_GET[$OUTPUT]);
+
+		//$filename = 'hello_'.time() . '.jpg';
+ 		//rename(basename($_GET[$OUTPUT]), $filename);
+
+		$operationSuccess = "true";
+		//file_put_contents("log.txt", var_export($_REQUEST, true)."\n new name = $filename\n",FILE_APPEND);
+            }
+        }
+        
+        //$file_list = glob_rsort_modtime('{*.jpg,*.png,*.gif,*.tif,*.psd,*.pdf}');
+        //$file_list = array_slice($file_list, 0, 10);
+        //print_pre($file_list, 'LAST 10 IMAGES');
+        //foreach($file_list as $fn => $mt){
+         //   echo '<p><img src="'.$fn.'"/>';
+        //}
+    ?>
+</head>
+<body>
+
+	<script language="javascript">
+
+    		var par = window.parent.document;
+		
+		if (<?php echo $operationSuccess?>){
+			
+
+
+		}
+		
+	</script>
+	<h3> The file was updated successfuly</h3>
+	<p> Please save the page in order to see the changes. <a href="" onclick="self.parent.tb_remove();">Close this window</a> </p>
+</body>
+</html>
Index: /afridex/plugins/Flutter/RCCWP_CreateCustomWritePanelPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CreateCustomWritePanelPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CreateCustomWritePanelPage.php (revision 21)
@@ -0,0 +1,29 @@
+<?php
+include_once('RCCWP_CustomWritePanelPage.php');
+class RCCWP_CreateCustomWritePanelPage
+{
+	function Main()
+	{
+		?>
+
+		<div class="wrap">
+
+		<h2><?php _e('Create Custom Write Panel'); ?></h2>
+		
+		<form action="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('finish-create-custom-write-panel')?>" method="post" id="create-new-write-panel-form">
+		
+		<?php RCCWP_CustomWritePanelPage::Content(); ?>
+		
+		<p class="submit" >
+			<a style="color:black" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('cancel-create-custom-write-panel')?>" class="button"><?php _e('Cancel'); ?></a>
+			<input type="submit" id="finish-create-custom-write-panel" value="<?php _e('Finish'); ?>" />
+		</p>
+		
+		</form>
+
+		</div>
+
+		<?php
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_CreatePanelModulePage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CreatePanelModulePage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CreatePanelModulePage.php (revision 21)
@@ -0,0 +1,60 @@
+<?php
+include_once('RCCWP_CustomWriteModulePage.php');
+
+
+class RCCWP_CreatePanelModulePage
+{
+	function Main()
+	{
+
+		$modules = RCCWP_CustomWriteModule::GetCustomModules();
+
+		if(isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id']) )
+			$customWritePanelId = (int)$_GET['custom-write-panel-id'];
+		if(isset($_POST['custom-write-panel-id']) && !empty($_POST['custom-write-panel-id']) )
+			$customWritePanelId = (int)$_POST['custom-write-panel-id'];
+		?>
+
+		<div class="wrap">
+
+		<h2><?php _e('Add Module'); ?></h2>
+		
+		<form action="" method="post" id="add-new-module-form">
+		
+		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+		<tbody>
+
+		<tr valign="top">
+			<th scope="row"  align="right">Name:</th>
+			<td>
+		
+				<input type="hidden" name="custom-write-panel-id" value="<?php echo $customWritePanelId?>" />
+				<select tabindex="3" name="custom-write-module-id"  id="custom-write-module-id">
+					<?php
+					foreach ($modules as $module) :
+					?>
+					
+						<option value="<?php echo $module->id?>"><?php echo $module->name?></option>
+					
+					<?php
+					endforeach;
+					?>
+				</select>
+			</td>
+		</tr>
+		</tbody>
+		</table>
+		
+		<p class="submit" >
+			<input name="cancel-add-module" type="submit" id="cancel-add-module" value="<?php _e('Cancel'); ?>" /> 
+			<input name="finish-add-module" type="submit" id="finish-add-module" value="<?php _e('Finish'); ?>" />
+		</p>
+		
+		</form>
+
+		</div>
+
+		<?php
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_ExportModule.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_ExportModule.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_ExportModule.php (revision 21)
@@ -0,0 +1,142 @@
+<?php
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can(FLUTTER_CAPABILITY_MODULES)))
+	die("Athentication failed!");
+	
+require_once('RCCWP_CustomWriteModule.php');
+require_once('RCCWP_CustomWritePanel.php');
+require_once('RCCWP_Application.php');
+require_once('RCCWP_CustomWritePanel.php');
+
+$moduleID = (int)$_REQUEST['custom-write-module-id'];
+$module = RCCWP_CustomWriteModule::Get($moduleID);
+
+if (isset($_POST["write_panels"])){
+	
+	$write_panels = json_decode(stripslashes($_POST["write_panels"]));
+	
+	$modulePath = FLUTTER_MODULES_DIR.$module->name.DIRECTORY_SEPARATOR;
+	$tmpPath = sys_get_temp_dir().DIRECTORY_SEPARATOR;
+	
+	
+	// Copy dir to tmp folder
+	dircopy($modulePath, $tmpPath. $module->name);
+	$moduleTmpPath = "$tmpPath{$module->name}";
+	chmod_R($moduleTmpPath, 0777);
+	
+	// Export write panels
+	foreach($write_panels as $panelID){
+		$writePanel = RCCWP_CustomWritePanel::Get($panelID);
+		$exportedFilename = $moduleTmpPath.DIRECTORY_SEPARATOR. '_'.$writePanel->name . '.pnl';
+		RCCWP_CustomWritePanel::Export($panelID, $exportedFilename);
+	}
+	
+	// Export duplicates and description
+	$moduleInfoFilename = $moduleTmpPath.DIRECTORY_SEPARATOR.'module_info.exp';
+	$moduleInfo_exported_data['duplicates'] = RCCWP_ModuleDuplicate::GetCustomModulesDuplicates($moduleID);
+	$moduleInfo_exported_data['moduleinfo'] = RCCWP_CustomWriteModule::Get($moduleID); 
+	$handle = fopen($moduleInfoFilename, "w");
+	$result = @fwrite($handle, serialize($moduleInfo_exported_data));
+	@fclose($handle);
+	
+	// -- Create zip file
+	$zipFile = "$tmpPath{$module->name}.zip";
+	chdir($moduleTmpPath.DIRECTORY_SEPARATOR);
+	if (RCCWP_Application::CheckCompressionProgramZip()) 
+		$command = "zip -r $zipFile  ./*"; 
+	else{
+		echo "Cannot find zip program";
+		return;
+	}
+	exec($command, $out, $err);
+	
+	
+	// send file in header
+	header('Content-type: binary');
+	header('Content-Disposition: attachment; filename="'.$module->name.'.zip"');
+	readfile($zipFile);
+	
+	// Remove file and directory
+	unlink($zipFile);
+	advancedRmdir($moduleTmpPath);
+	exit();
+}
+
+$customWritePanels = RCCWP_CustomWritePanel::GetCustomWritePanels();
+		
+?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-basic11.dtd" >
+<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>
+<head>
+	<title>Export Module</title>
+	<link rel='stylesheet' href='<?php echo get_bloginfo('wpurl');?>/wp-admin/css/global.css' type='text/css' />
+	<link rel='stylesheet' href='<?php echo get_bloginfo('wpurl');?>/wp-admin/wp-admin.css' type='text/css' />
+	<link rel='stylesheet' href='<?php echo get_bloginfo('wpurl');?>/wp-admin/css/colors-fresh.css' type='text/css' />
+	
+	<script type="text/javascript">
+		function startExport(){
+		
+			// Collect write panels
+			var write_panels = []; 
+			var write_panels_elements = document.export_module_form.elements["write_panels[]"];
+			if (write_panels_elements != undefined){
+				if (write_panels_elements.length != undefined){
+					for(i=0;i<write_panels_elements.length;i++)
+					{
+						if (write_panels_elements[i].checked)
+							write_panels.push(write_panels_elements[i].value);
+					}
+				}
+				else
+				{
+					if (write_panels_elements.checked)
+						write_panels.push(write_panels_elements.value);
+				}
+			}
+			
+			// Submit data through a hidden form in the parent window
+			var par = window.parent.document;
+			par.forms['do_export'].elements["custom-write-module-id"].value= "<?php echo $moduleID ?>";
+			par.forms['do_export'].elements["write_panels"].value= self.parent.Object.toJSON(write_panels);
+			par.forms['do_export'].submit();
+			
+			self.parent.tb_remove();
+			return true;
+			
+		}
+		
+	</script>
+	
+</head>
+<body>
+
+<div class="wrap">
+
+<h2>Export <?php echo $module->name ?></h2>
+		
+<form action="" method="post" name="export_module_form" id="export-module-form" >
+	<strong> <?php _e('If you want to export Write Panels along with the module, check them below:')?> </strong>
+	<ul>
+	<?php
+	foreach ($customWritePanels as $panel) :
+	?>
+		<li>
+		<input type="checkbox" name="write_panels[]" value="<?php echo $panel->id?>">&nbsp;<?php echo $panel->name;?></input>
+		</li>
+	<?php
+	endforeach;
+	?>
+	</ul>
+	<p class="submit" > 
+		<input type="button" name="submit-export-module" id="submit-export-module" value="<?php _e('Export'); ?>" onclick="startExport()" />
+	</p>
+	
+</form>
+
+
+</div>
+
+</body>
+</html>
+	
Index: /afridex/plugins/Flutter/RCCWP_EditnPlaceResponse.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_EditnPlaceResponse.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_EditnPlaceResponse.php (revision 21)
@@ -0,0 +1,39 @@
+<?php
+	if (!isset($_POST['post_id'])) die('Invalid post id');
+	$postID = $_POST['post_id'];
+	require( dirname(__FILE__) . '/../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_post', $postID)))
+		die("Athentication failed!");
+
+	// Start saving data
+	global $wpdb;
+	
+	$postID = $wpdb->escape($_POST['post_id']);
+	$field_value = urldecode($_POST['field_value']);
+	$field_type = $wpdb->escape($_POST['field_type']);
+	$meta_id = $wpdb->escape($_POST['meta_id']);
+	
+	$post = & get_post( $postID, ARRAY_A );
+	
+	switch ($field_type){
+		
+		case 'EIP_title':
+			$post['post_title'] = $field_value ; //apply_filters( 'title_save_pre', $field_value );
+			wp_update_post( $post);
+			break;
+			
+		case 'EIP_content':
+			$post['post_content'] = $field_value ; //apply_filters( 'content_save_pre', $field_value );
+			wp_update_post( $post);
+			break;
+			
+		case 'EIP_textbox':
+		case 'EIP_mulittextbox':
+			$wpdb->query( "UPDATE $wpdb->postmeta SET meta_value = '".$field_value."' WHERE meta_id = '$meta_id'" );
+			break;
+	}
+	
+	//$post = add_magic_quotes( $post);
+	
+
+?>
Index: /afridex/plugins/Flutter/RCCWP_CustomWriteModulePage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CustomWriteModulePage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CustomWriteModulePage.php (revision 21)
@@ -0,0 +1,185 @@
+<?php
+include_once('RCCWP_CustomWriteModule.php');
+
+class RCCWP_CustomWriteModulePage
+{
+	function Content($customWriteModule = null)
+	{
+		$customWriteModuleName = "";
+		
+		if ($customWriteModule != null)
+		{
+			$customWriteModuleName = $customWriteModule->name;
+		?>
+		<input type="hidden" name="custom-write-module-id" value="<?php echo $_REQUEST['custom-write-module-id']?>" />
+		<?php
+		}
+		
+  		?>
+
+		<?php
+		if (isset($_GET['err_msg'])) :
+			switch ($_GET['err_msg']){
+				case -1:
+		?>
+			<div class="updated fade"><p> A module with the same name already exists. Please choose a different name.</p></div>
+
+		<?php
+			}
+		endif;
+		?>
+
+		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+		<tbody>
+
+		<tr valign="top">
+			<th scope="row"  align="right">Name:</th>
+			<td><input name="custom-write-module-name" id="custom-write-module-name" size="40" type="text" value="<?php echo $customWriteModuleName?>" /></td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row"  align="right">Description:</th>
+			<td><textarea name="custom-write-module-description" id="custom-write-module-description" rows="3" cols="40"><?php echo $customWriteModule->description?></textarea></td>
+		</tr>
+
+		</tbody>
+		</table>
+		
+		<?php
+	}
+	
+	function Edit()
+	{
+		$customWriteModule = RCCWP_CustomWriteModule::Get((int)$_REQUEST['custom-write-module-id']);
+		?>
+
+
+
+		<div class="wrap">
+		
+		<h2>Edit Custom Write Module</h2>
+		
+		<form action="<?php echo RCCWP_ManagementPage::GetCustomWriteModuleEditUrl($_REQUEST['custom-write-module-id']) ?>" method="post" id="edit-custom-write-module-form">
+		
+		<?php
+		RCCWP_CustomWriteModulePage::Content($customWriteModule);
+		?>
+		
+		<p class="submit" >
+			<input name="cancel-edit-custom-write-module" type="submit" id="cancel-edit-custom-write-module" value="<?php _e('Cancel'); ?>" /> 
+			<input name="submit-edit-custom-write-module" type="submit" id="submit-edit-custom-write-module" value="<?php _e('Update'); ?>" />
+		</p>
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+	
+	
+	function View($param = 23)
+	{
+		if(isset($_GET['custom-write-module-id']) && !empty($_GET['custom-write-module-id']) )
+			$customWriteModuleId = (int)$_GET['custom-write-module-id'];
+		if(isset($_POST['custom-write-module-id']) && !empty($_POST['custom-write-module-id']) )
+			$customWriteModuleId = (int)$_POST['custom-write-module-id'];
+
+		$customWriteModule = RCCWP_CustomWriteModule::Get($customWriteModuleId);
+		?>
+
+		<div class="wrap">
+
+		<h2>Custom Write Module Info</h2>
+		<h4><a href="?page=FlutterManageModules&view-modules=1"> Â« Back to Custom Modules List</a></h4>
+		<form action="" method="post" id="view-write-module-form">
+		
+		<input type="hidden" name="custom-write-module-id" value="<?php echo $customWriteModuleId?>" />
+			
+
+  		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+  		<tbody>
+  		<tr>
+			<th scope="row" align="right">Name:</th>
+			<td><?php echo $customWriteModule->name?></td>
+		</tr>
+		<tr>
+			<th scope="row" align="right">Description:</th>
+			<td><?php echo $customWriteModule->description?></td>
+		</tr>
+  		</tbody>
+  		</table>
+		  
+		<script type="text/javascript" language="javascript">
+			function confirmBeforeDelete()
+			{
+				return confirm("Are you sure you want to delete this module? Please notice that all the template files of this module will be deleted too.");							
+			}
+		</script>
+		<p class="submit" >
+			<input name="edit-custom-write-module" type="submit" id="edit-custom-write-module" value="Edit Module" />
+			<input onclick="return confirmBeforeDelete();" name="delete-custom-write-module" type="submit" id="delete-custom-write-module" value="Delete Module" />
+		</p>
+		
+		</form>
+		
+		<form action="" method="post" id="view-module-duplicates">
+			<br /><br />
+			<h3>Module Duplicates for Layout</h3>
+			<div class="tablenav"><div class="alignright">
+				<input name="create-module-duplicate" type="submit" id="create-module-duplicate" value="Create Duplicate" class="button-secondary"  />
+			</div></div>
+			<br class="clear"/>
+
+			<table cellpadding="3" cellspacing="3" width="100%" class="widefat">
+			<thead>
+			<tr>
+				<th scope="col">Name</th>
+				<th scope="col" colspan="2">Action</th>
+			</tr>
+			</thead>
+			<tbody>
+			<?php
+			$customWriteModuleDuplicates = RCCWP_ModuleDuplicate::GetCustomModulesDuplicates($customWriteModuleId);
+			foreach ($customWriteModuleDuplicates as $customWriteModuleDuplicate) :
+				$class = $class == '' ? 'alternate' : '';
+			?>
+				<tr class="<?php echo $class?>">
+					<td><?php echo $customWriteModuleDuplicate->duplicate_name?></td>
+					<td><a href="<?php echo RCCWP_ManagementPage::GetModuleDuplicateEditUrl($customWriteModuleId, $customWriteModuleDuplicate->duplicate_id)?>" class="edit">Rename</a></td>
+					<td><a href="<?php echo RCCWP_ManagementPage::GetModuleDuplicateDeleteUrl($customWriteModuleId, $customWriteModuleDuplicate->duplicate_id)?>" class="delete">Delete</a></td>
+				</tr>
+			<?php
+			endforeach;
+			?>
+			</tbody>
+			</table>
+		  
+		</form>
+
+		</div>
+		
+		<?php
+	}
+
+	function Import()
+	{
+		include_once('RCCWP_CustomWriteModule.php');
+		
+		if(isset($_FILES['import-module-file']) && !empty($_FILES['import-module-file']['tmp_name']) ) {
+			$zipFilePath = $_FILES['import-module-file']['tmp_name'];
+		}
+		else {
+			die("Error uploading file!");
+		}
+
+		$moduleName = basename($_FILES['import-module-file']['name'], ".zip");
+		$moduleID = RCCWP_CustomWriteModule::Import($zipFilePath, $moduleName);
+		unlink($zipFilePath);
+		
+		echo "<h3>The module was imported successfuly.</h3>";
+		echo '<p><a href="' . RCCWP_ManagementPage::GetCustomWriteModuleEditUrl($moduleID).'"> Click here </a> to edit the module. </p>';
+		
+	}
+	
+}
+?>
Index: /afridex/plugins/Flutter/js/swfupload.js
===================================================================
--- /afridex/plugins/Flutter/js/swfupload.js (revision 21)
+++ /afridex/plugins/Flutter/js/swfupload.js (revision 21)
@@ -0,0 +1,1049 @@
+/**
+ * SWFUpload v2.0 by Jacob Roberts, Nov 2007, http://www.swfupload.org, http://linebyline.blogspot.com
+ * -------- -------- -------- -------- -------- -------- -------- --------
+ * SWFUpload is (c) 2006 Lars Huring and Mammon Media and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * See Changelog.txt for version history
+ *
+ * Development Notes:
+ *  * This version of SWFUpload requires Flash Player 9.0.28 and should autodetect the correct flash version.
+ *  * In Linux Flash Player 9 setting the post file variable name does not work. It is always set to "Filedata".
+ *  * There is a lot of repeated code that could be refactored to single functions.  Feel free.
+ *  * It's dangerous to do "circular calls" between Flash and JavaScript. I've taken steps to try to work around issues
+ *     by having the event calls pipe through setTimeout.  However you should still avoid calling in to Flash from
+ *     within the event handler methods.  Especially the "startUpload" event since it cannot use the setTimeout hack.
+ */
+
+
+/* *********** */
+/* Constructor */
+/* *********** */
+
+var SWFUpload = function (init_settings) {
+	this.initSWFUpload(init_settings);
+};
+
+SWFUpload.prototype.initSWFUpload = function (init_settings) {
+	// Remove background flicker in IE (read this: http://misterpixel.blogspot.com/2006/09/forensic-analysis-of-ie6.html)
+	// This doesn't have anything to do with SWFUpload but can help your UI behave better in IE.
+	try {
+		document.execCommand('BackgroundImageCache', false, true);
+	} catch (ex1) {
+	}
+
+
+	try {
+		this.customSettings = {};	// A container where developers can place their own settings associated with this instance.
+		this.settings = {};
+		this.eventQueue = [];
+		this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
+		this.movieElement = null;
+
+		// Setup global control tracking
+		SWFUpload.instances[this.movieName] = this;
+
+		// Load the settings.  Load the Flash movie.
+		this.initSettings(init_settings);
+		this.loadFlash();
+
+		this.displayDebugInfo();
+
+	} catch (ex2) {
+		this.debug(ex2);
+	}
+}
+
+/* *************** */
+/* Static thingies */
+/* *************** */
+SWFUpload.instances = {};
+SWFUpload.movieCount = 0;
+SWFUpload.QUEUE_ERROR = {
+	QUEUE_LIMIT_EXCEEDED	  		: -100,
+	FILE_EXCEEDS_SIZE_LIMIT  		: -110,
+	ZERO_BYTE_FILE			  		: -120,
+	INVALID_FILETYPE		  		: -130
+};
+SWFUpload.UPLOAD_ERROR = {
+	HTTP_ERROR				  		: -200,
+	MISSING_UPLOAD_URL	      		: -210,
+	IO_ERROR				  		: -220,
+	SECURITY_ERROR			  		: -230,
+	UPLOAD_LIMIT_EXCEEDED	  		: -240,
+	UPLOAD_FAILED			  		: -250,
+	SPECIFIED_FILE_ID_NOT_FOUND		: -260,
+	FILE_VALIDATION_FAILED	  		: -270,
+	FILE_CANCELLED			  		: -280,
+	UPLOAD_STOPPED					: -290
+};
+SWFUpload.FILE_STATUS = {
+	QUEUED		 : -1,
+	IN_PROGRESS	 : -2,
+	ERROR		 : -3,
+	COMPLETE	 : -4,
+	CANCELLED	 : -5
+};
+
+
+/* ***************** */
+/* Instance Thingies */
+/* ***************** */
+// init is a private method that ensures that all the object settings are set, getting a default value if one was not assigned.
+
+SWFUpload.prototype.initSettings = function (init_settings) {
+	// Upload backend settings
+	this.addSetting("upload_url",		 		init_settings.upload_url,		  		"");
+	this.addSetting("file_post_name",	 		init_settings.file_post_name,	  		"Filedata");
+	this.addSetting("post_params",		 		init_settings.post_params,		  		{});
+
+	// File Settings
+	this.addSetting("file_types",			  	init_settings.file_types,				"*.*");
+	this.addSetting("file_types_description", 	init_settings.file_types_description, 	"All Files");
+	this.addSetting("file_size_limit",		  	init_settings.file_size_limit,			"1024");
+	this.addSetting("file_upload_limit",	  	init_settings.file_upload_limit,		"0");
+	this.addSetting("file_queue_limit",		  	init_settings.file_queue_limit,			"0");
+
+	// Flash Settings
+	this.addSetting("flash_url",		  		init_settings.flash_url,				"swfupload.swf");
+	this.addSetting("flash_width",		  		init_settings.flash_width,				"1px");
+	this.addSetting("flash_height",		  		init_settings.flash_height,				"1px");
+	this.addSetting("flash_color",		  		init_settings.flash_color,				"#FFFFFF");
+
+	// Debug Settings
+	this.addSetting("debug_enabled", init_settings.debug,  false);
+
+	// Event Handlers
+	this.flashReady_handler         = SWFUpload.flashReady;	// This is a non-overrideable event handler
+	this.swfUploadLoaded_handler    = this.retrieveSetting(init_settings.swfupload_loaded_handler,	    SWFUpload.swfUploadLoaded);
+	
+	this.fileDialogStart_handler	= this.retrieveSetting(init_settings.file_dialog_start_handler,		SWFUpload.fileDialogStart);
+	this.fileQueued_handler			= this.retrieveSetting(init_settings.file_queued_handler,			SWFUpload.fileQueued);
+	this.fileQueueError_handler		= this.retrieveSetting(init_settings.file_queue_error_handler,		SWFUpload.fileQueueError);
+	this.fileDialogComplete_handler	= this.retrieveSetting(init_settings.file_dialog_complete_handler,	SWFUpload.fileDialogComplete);
+	
+	this.uploadStart_handler		= this.retrieveSetting(init_settings.upload_start_handler,			SWFUpload.uploadStart);
+	this.uploadProgress_handler		= this.retrieveSetting(init_settings.upload_progress_handler,		SWFUpload.uploadProgress);
+	this.uploadError_handler		= this.retrieveSetting(init_settings.upload_error_handler,			SWFUpload.uploadError);
+	this.uploadSuccess_handler		= this.retrieveSetting(init_settings.upload_success_handler,		SWFUpload.uploadSuccess);
+	this.uploadComplete_handler		= this.retrieveSetting(init_settings.upload_complete_handler,		SWFUpload.uploadComplete);
+
+	this.debug_handler				= this.retrieveSetting(init_settings.debug_handler,			   		SWFUpload.debug);
+
+	// Other settings
+	this.customSettings = this.retrieveSetting(init_settings.custom_settings, {});
+};
+
+// loadFlash is a private method that generates the HTML tag for the Flash
+// It then adds the flash to the "target" or to the body and stores a
+// reference to the flash element in "movieElement".
+SWFUpload.prototype.loadFlash = function () {
+	var html, target_element, container;
+
+	// Make sure an element with the ID we are going to use doesn't already exist
+	if (document.getElementById(this.movieName) !== null) {
+		return false;
+	}
+
+	// Get the body tag where we will be adding the flash movie
+	try {
+		target_element = document.getElementsByTagName("body")[0];
+		if (typeof(target_element) === "undefined" || target_element === null) {
+			this.debug('Could not find the BODY element. SWFUpload failed to load.');
+			return false;
+		}
+	} catch (ex) {
+		return false;
+	}
+
+	// Append the container and load the flash
+	container = document.createElement("div");
+	container.style.width = this.getSetting("flash_width");
+	container.style.height = this.getSetting("flash_height");
+
+	target_element.appendChild(container);
+	container.innerHTML = this.getFlashHTML();	// Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
+};
+
+// Generates the embed/object tags needed to embed the flash in to the document
+SWFUpload.prototype.getFlashHTML = function () {
+	var html = "";
+
+	// Create Mozilla Embed HTML
+	if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) {
+		// Build the basic embed html
+		html = '<embed type="application/x-shockwave-flash" src="' + this.getSetting("flash_url") + '" width="' + this.getSetting("flash_width") + '" height="' + this.getSetting("flash_height") + '"';
+		html += ' id="' + this.movieName + '" name="' + this.movieName + '" ';
+		html += 'bgcolor="' + this.getSetting("flash_color") + '" quality="high" menu="false" flashvars="';
+
+		html += this.getFlashVars();
+
+		html += '" />';
+
+		// Create IE Object HTML
+	} else {
+
+		// Build the basic Object tag
+		html = '<object id="' + this.movieName + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="' + this.getSetting("flash_width") + '" height="' + this.getSetting("flash_height") + '">';
+		html += '<param name="movie" value="' + this.getSetting("flash_url") + '">';
+
+		html += '<param name="bgcolor" value="' + this.getSetting("flash_color") + '" />';
+		html += '<param name="quality" value="high" />';
+		html += '<param name="menu" value="false" />';
+
+		html += '<param name="flashvars" value="' + this.getFlashVars() + '" />';
+		html += '</object>';
+	}
+
+	return html;
+};
+
+// This private method builds the parameter string that will be passed
+// to flash.
+SWFUpload.prototype.getFlashVars = function () {
+	// Build a string from the post param object
+	var param_string = this.buildParamString();
+
+	// Build the parameter string
+	var html = "";
+	html += "movieName=" + encodeURIComponent(this.movieName);
+	html += "&uploadURL=" + encodeURIComponent(this.getSetting("upload_url"));
+	html += "&params=" + encodeURIComponent(param_string);
+	html += "&filePostName=" + encodeURIComponent(this.getSetting("file_post_name"));
+	html += "&fileTypes=" + encodeURIComponent(this.getSetting("file_types"));
+	html += "&fileTypesDescription=" + encodeURIComponent(this.getSetting("file_types_description"));
+	html += "&fileSizeLimit=" + encodeURIComponent(this.getSetting("file_size_limit"));
+	html += "&fileUploadLimit=" + encodeURIComponent(this.getSetting("file_upload_limit"));
+	html += "&fileQueueLimit=" + encodeURIComponent(this.getSetting("file_queue_limit"));
+	html += "&debugEnabled=" + encodeURIComponent(this.getSetting("debug_enabled"));
+
+	return html;
+};
+
+SWFUpload.prototype.getMovieElement = function () {
+	if (typeof(this.movieElement) === "undefined" || this.movieElement === null) {
+		this.movieElement = document.getElementById(this.movieName);
+
+		// Fix IEs "Flash can't callback when in a form" issue (http://www.extremefx.com.ar/blog/fixing-flash-external-interface-inside-form-on-internet-explorer)
+		// Removed because Revision 6 always adds the flash to the body (inside a containing div)
+		// If you insist on adding the Flash file inside a Form then in IE you have to make you wait until the DOM is ready
+		// and run this code to make the form's ID available from the window object so Flash and JavaScript can communicate.
+		//if (typeof(window[this.movieName]) === "undefined" || window[this.moveName] !== this.movieElement) {
+		//	window[this.movieName] = this.movieElement;
+		//}
+	}
+
+	return this.movieElement;
+};
+
+SWFUpload.prototype.buildParamString = function () {
+	var post_params = this.getSetting("post_params");
+	var param_string_pairs = [];
+	var i, value, name;
+
+	// Retrieve the user defined parameters
+	if (typeof(post_params) === "object") {
+		for (name in post_params) {
+			if (post_params.hasOwnProperty(name)) {
+				if (typeof(post_params[name]) === "string") {
+					param_string_pairs.push(encodeURIComponent(name) + "=" + encodeURIComponent(post_params[name]));
+				}
+			}
+		}
+	}
+
+	return param_string_pairs.join("&");
+};
+
+// Saves a setting.	 If the value given is undefined or null then the default_value is used.
+SWFUpload.prototype.addSetting = function (name, value, default_value) {
+	if (typeof(value) === "undefined" || value === null) {
+		this.settings[name] = default_value;
+	} else {
+		this.settings[name] = value;
+	}
+
+	return this.settings[name];
+};
+
+// Gets a setting.	Returns empty string if not found.
+SWFUpload.prototype.getSetting = function (name) {
+	if (typeof(this.settings[name]) === "undefined") {
+		return "";
+	} else {
+		return this.settings[name];
+	}
+};
+
+// Gets a setting, if the setting is undefined then return the default value
+// This does not affect or use the interal setting object.
+SWFUpload.prototype.retrieveSetting = function (value, default_value) {
+	if (typeof(value) === "undefined" || value === null) {
+		return default_value;
+	} else {
+		return value;
+	}
+};
+
+
+// It loops through all the settings and displays
+// them in the debug Console.
+SWFUpload.prototype.displayDebugInfo = function () {
+	var key, debug_message = "";
+
+	debug_message += "----- SWFUPLOAD SETTINGS     ----\nID: " + this.moveName + "\n";
+
+	debug_message += this.outputObject(this.settings);
+
+	debug_message += "----- SWFUPLOAD SETTINGS END ----\n";
+	debug_message += "\n";
+
+	this.debug(debug_message);
+};
+SWFUpload.prototype.outputObject = function (object, prefix) {
+	var output = "", key;
+
+	if (typeof(prefix) !== "string") {
+		prefix = "";
+	}
+	if (typeof(object) !== "object") {
+		return "";
+	}
+
+	for (key in object) {
+		if (object.hasOwnProperty(key)) {
+			if (typeof(object[key]) === "object") {
+				output += (prefix + key + ": { \n" + this.outputObject(object[key], "\t" + prefix) + prefix + "}" + "\n");
+			} else {
+				output += (prefix + key + ": " + object[key] + "\n");
+			}
+		}
+	}
+
+	return output;
+};
+
+/* *****************************
+	-- Flash control methods --
+	Your UI should use these
+	to operate SWFUpload
+   ***************************** */
+
+SWFUpload.prototype.selectFile = function () {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SelectFile) === "function") {
+		try {
+			movie_element.SelectFile();
+		}
+		catch (ex) {
+			this.debug("Could not call SelectFile: " + ex);
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+
+};
+
+SWFUpload.prototype.selectFiles = function () {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SelectFiles) === "function") {
+		try {
+			movie_element.SelectFiles();
+		}
+		catch (ex) {
+			this.debug("Could not call SelectFiles: " + ex);
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+
+};
+
+
+/* Start the upload.  If a file_id is specified that file is uploaded. Otherwise the first
+ * file in the queue is uploaded.  If no files are in the queue then nothing happens.
+ * This call uses setTimeout since Flash will be calling back in to JavaScript
+ */
+SWFUpload.prototype.startUpload = function (file_id) {
+	var self = this;
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.StartUpload) === "function") {
+		setTimeout(
+			function () {
+				try {
+					movie_element.StartUpload(file_id);
+				}
+				catch (ex) {
+					self.debug("Could not call StartUpload: " + ex);
+				}
+			}, 0
+		);
+	} else {
+		this.debug("Could not find Flash element");
+	}
+
+};
+
+/* Cancels a the file upload.  You must specify a file_id */
+SWFUpload.prototype.cancelUpload = function (file_id) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.CancelUpload) === "function") {
+		try {
+			movie_element.CancelUpload(file_id);
+		}
+		catch (ex) {
+			this.debug("Could not call CancelUpload: " + ex);
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+
+};
+
+// Stops the current upload.  The file is re-queued.  If nothing is currently uploading then nothing happens.
+SWFUpload.prototype.stopUpload = function () {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.StopUpload) === "function") {
+		try {
+			movie_element.StopUpload();
+		}
+		catch (ex) {
+			this.debug("Could not call StopUpload: " + ex);
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+
+};
+
+/* ************************
+ * Settings methods
+ *   These methods change the settings inside SWFUpload
+ *   They shouldn't need to be called in a setTimeout since they
+ *   should not call back from Flash to JavaScript (except perhaps in a Debug call)
+ *   and some need to return data so setTimeout won't work.
+ */
+
+/* Gets the file statistics object.	 It looks like this (where n = number):
+	{
+		files_queued: n,
+		complete_uploads: n,
+		upload_errors: n,
+		uploads_cancelled: n,
+		queue_errors: n
+	}
+*/
+SWFUpload.prototype.getStats = function () {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.GetStats) === "function") {
+		try {
+			return movie_element.GetStats();
+		}
+		catch (ex) {
+			this.debug("Could not call GetStats");
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+};
+SWFUpload.prototype.setStats = function (stats_object) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetStats) === "function") {
+		try {
+			movie_element.SetStats(stats_object);
+		}
+		catch (ex) {
+			this.debug("Could not call SetStats");
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+};
+
+SWFUpload.prototype.setCredentials = function(name, password) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetCredentials) === "function") {
+		try {
+			return movie_element.SetCredentials(name, password);
+		}
+		catch (ex) {
+			this.debug("Could not call SetCredentials");
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+};
+
+SWFUpload.prototype.getFile = function (file_id) {
+	var movie_element = this.getMovieElement();
+			if (typeof(file_id) === "number") {
+				if (movie_element !== null && typeof(movie_element.GetFileByIndex) === "function") {
+					try {
+						return movie_element.GetFileByIndex(file_id);
+					}
+					catch (ex) {
+						this.debug("Could not call GetFileByIndex");
+					}
+				} else {
+					this.debug("Could not find Flash element");
+				}
+			} else {
+				if (movie_element !== null && typeof(movie_element.GetFile) === "function") {
+					try {
+						return movie_element.GetFile(file_id);
+					}
+					catch (ex) {
+						this.debug("Could not call GetFile");
+					}
+				} else {
+					this.debug("Could not find Flash element");
+				}
+			}
+};
+
+SWFUpload.prototype.addFileParam = function (file_id, name, value) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.AddFileParam) === "function") {
+		try {
+			return movie_element.AddFileParam(file_id, name, value);
+		}
+		catch (ex) {
+			this.debug("Could not call AddFileParam");
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+};
+
+SWFUpload.prototype.removeFileParam = function (file_id, name) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.RemoveFileParam) === "function") {
+		try {
+			return movie_element.RemoveFileParam(file_id, name);
+		}
+		catch (ex) {
+			this.debug("Could not call AddFileParam");
+		}
+	} else {
+		this.debug("Could not find Flash element");
+	}
+
+};
+
+SWFUpload.prototype.setUploadURL = function (url) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetUploadURL) === "function") {
+		try {
+			this.addSetting("upload_url", url);
+			movie_element.SetUploadURL(this.getSetting("upload_url"));
+		}
+		catch (ex) {
+			this.debug("Could not call SetUploadURL");
+		}
+	} else {
+		this.debug("Could not find Flash element in setUploadURL");
+	}
+};
+
+SWFUpload.prototype.setPostParams = function (param_object) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetPostParams) === "function") {
+		try {
+			this.addSetting("post_params", param_object);
+			movie_element.SetPostParams(this.getSetting("post_params"));
+		}
+		catch (ex) {
+			this.debug("Could not call SetPostParams");
+		}
+	} else {
+		this.debug("Could not find Flash element in SetPostParams");
+	}
+};
+
+SWFUpload.prototype.setFileTypes = function (types, description) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetFileTypes) === "function") {
+		try {
+			this.addSetting("file_types", types);
+			this.addSetting("file_types_description", description);
+			movie_element.SetFileTypes(this.getSetting("file_types"), this.getSetting("file_types_description"));
+		}
+		catch (ex) {
+			this.debug("Could not call SetFileTypes");
+		}
+	} else {
+		this.debug("Could not find Flash element in SetFileTypes");
+	}
+};
+
+SWFUpload.prototype.setFileSizeLimit = function (file_size_limit) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetFileSizeLimit) === "function") {
+		try {
+			this.addSetting("file_size_limit", file_size_limit);
+			movie_element.SetFileSizeLimit(this.getSetting("file_size_limit"));
+		}
+		catch (ex) {
+			this.debug("Could not call SetFileSizeLimit");
+		}
+	} else {
+		this.debug("Could not find Flash element in SetFileSizeLimit");
+	}
+};
+
+SWFUpload.prototype.setFileUploadLimit = function (file_upload_limit) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetFileUploadLimit) === "function") {
+		try {
+			this.addSetting("file_upload_limit", file_upload_limit);
+			movie_element.SetFileUploadLimit(this.getSetting("file_upload_limit"));
+		}
+		catch (ex) {
+			this.debug("Could not call SetFileUploadLimit");
+		}
+	} else {
+		this.debug("Could not find Flash element in SetFileUploadLimit");
+	}
+};
+
+SWFUpload.prototype.setFileQueueLimit = function (file_queue_limit) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetFileQueueLimit) === "function") {
+		try {
+			this.addSetting("file_queue_limit", file_queue_limit);
+			movie_element.SetFileQueueLimit(this.getSetting("file_queue_limit"));
+		}
+		catch (ex) {
+			this.debug("Could not call SetFileQueueLimit");
+		}
+	} else {
+		this.debug("Could not find Flash element in SetFileQueueLimit");
+	}
+};
+
+SWFUpload.prototype.setFilePostName = function (file_post_name) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetFilePostName) === "function") {
+		try {
+			this.addSetting("file_post_name", file_post_name);
+			movie_element.SetFilePostName(this.getSetting("file_post_name"));
+		}
+		catch (ex) {
+			this.debug("Could not call SetFilePostName");
+		}
+	} else {
+		this.debug("Could not find Flash element in SetFilePostName");
+	}
+};
+
+SWFUpload.prototype.setDebugEnabled = function (debug_enabled) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.SetDebugEnabled) === "function") {
+		try {
+			this.addSetting("debug_enabled", debug_enabled);
+			movie_element.SetDebugEnabled(this.getSetting("debug_enabled"));
+		}
+		catch (ex) {
+			this.debug("Could not call SetDebugEnabled");
+		}
+	} else {
+		this.debug("Could not find Flash element in SetDebugEnabled");
+	}
+};
+
+/* *******************************
+	Internal Event Callers
+	Don't override these! These event callers ensure that your custom event handlers
+	are called safely and in order.
+******************************* */
+
+/* This is the callback method that the Flash movie will call when it has been loaded and is ready to go.
+   Calling this or showUI() "manually" will bypass the Flash Detection built in to SWFUpload.
+   Use a ui_function setting if you want to control the UI loading after the flash has loaded.
+*/
+SWFUpload.prototype.flashReady = function () {
+	// Check that the movie element is loaded correctly with its ExternalInterface methods defined
+	var movie_element = this.getMovieElement();
+	if (movie_element === null || typeof(movie_element.StartUpload) !== "function") {
+		this.debug("ExternalInterface methods failed to initialize.");
+		return;
+	}
+	
+	var self = this;
+	if (typeof(self.flashReady_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.flashReady_handler(); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("flashReady_handler event not defined");
+	}
+};
+
+/*
+	Event Queue.  Rather can call events directly from Flash they events are
+	are placed in a queue and then executed.  This ensures that each event is
+	executed in the order it was called which is not guarenteed when calling
+	setTimeout.  Out of order events was especially problematic in Safari.
+*/
+SWFUpload.prototype.executeNextEvent = function () {
+	var  f = this.eventQueue.shift();
+	if (typeof(f) === "function") {
+		f();
+	}
+}
+
+/* This is a chance to do something before the browse window opens */
+SWFUpload.prototype.fileDialogStart = function () {
+	var self = this;
+	if (typeof(self.fileDialogStart_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.fileDialogStart_handler(); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("fileDialogStart event not defined");
+	}
+};
+
+
+/* Called when a file is successfully added to the queue. */
+SWFUpload.prototype.fileQueued = function (file) {
+	var self = this;
+	if (typeof(self.fileQueued_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.fileQueued_handler(file); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("fileQueued event not defined");
+	}
+};
+
+
+/* Handle errors that occur when an attempt to queue a file fails. */
+SWFUpload.prototype.fileQueueError = function (file, error_code, message) {
+	var self = this;
+	if (typeof(self.fileQueueError_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() {  self.fileQueueError_handler(file, error_code, message); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("fileQueueError event not defined");
+	}
+};
+
+/* Called after the file dialog has closed and the selected files have been queued.
+	You could call startUpload here if you want the queued files to begin uploading immediately. */
+SWFUpload.prototype.fileDialogComplete = function (num_files_selected) {
+	var self = this;
+	if (typeof(self.fileDialogComplete_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.fileDialogComplete_handler(num_files_selected); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("fileDialogComplete event not defined");
+	}
+};
+
+/* Gets called when a file upload is about to be started.  Return true to continue the upload. Return false to stop the upload.
+	If you return false then uploadError and uploadComplete are called (like normal).
+	
+	This is a good place to do any file validation you need.
+	*/
+SWFUpload.prototype.uploadStart = function (file) {
+	var self = this;
+	if (typeof(self.fileDialogComplete_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.returnUploadStart(self.uploadStart_handler(file)); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("uploadStart event not defined");
+	}
+};
+
+/* Note: Internal use only.  This function returns the result of uploadStart to
+	flash.  Since returning values in the normal way can result in Flash/JS circular
+	call issues we split up the call in a Timeout.  This is transparent from the API
+	point of view.
+*/
+SWFUpload.prototype.returnUploadStart = function (return_value) {
+	var movie_element = this.getMovieElement();
+	if (movie_element !== null && typeof(movie_element.ReturnUploadStart) === "function") {
+		try {
+			movie_element.ReturnUploadStart(return_value);
+		}
+		catch (ex) {
+			this.debug("Could not call ReturnUploadStart");
+		}
+	} else {
+		this.debug("Could not find Flash element in returnUploadStart");
+	}
+};
+
+
+
+/* Called during upload as the file progresses. Use this event to update your UI. */
+SWFUpload.prototype.uploadProgress = function (file, bytes_complete, bytes_total) {
+	var self = this;
+	if (typeof(self.uploadProgress_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.uploadProgress_handler(file, bytes_complete, bytes_total); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("uploadProgress event not defined");
+	}
+};
+
+/* Called when an error occurs during an upload. Use error_code and the SWFUpload.UPLOAD_ERROR constants to determine
+   which error occurred. The uploadComplete event is called after an error code indicating that the next file is
+   ready for upload.  For files cancelled out of order the uploadComplete event will not be called. */
+SWFUpload.prototype.uploadError = function (file, error_code, message) {
+	var self = this;
+	if (typeof(this.uploadError_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.uploadError_handler(file, error_code, message); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("uploadError event not defined");
+	}
+};
+
+/* This gets called when a file finishes uploading and the server-side upload script has completed and returned a 200
+status code. Any text returned by the server is available in server_data.
+**NOTE: The upload script MUST return some text or the uploadSuccess and uploadComplete events will not fire and the
+upload will become 'stuck'. */
+SWFUpload.prototype.uploadSuccess = function (file, server_data) {
+	var self = this;
+	if (typeof(self.uploadSuccess_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.uploadSuccess_handler(file, server_data); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("uploadSuccess event not defined");
+	}
+};
+
+/* uploadComplete is called when the file is uploaded or an error occurred and SWFUpload is ready to make the next upload.
+   If you want the next upload to start to automatically you can call startUpload() from this event. */
+SWFUpload.prototype.uploadComplete = function (file) {
+	var self = this;
+	if (typeof(self.uploadComplete_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.uploadComplete_handler(file); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.debug("uploadComplete event not defined");
+	}
+};
+
+/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
+   internal debug console.  You can override this event and have messages written where you want. */
+SWFUpload.prototype.debug = function (message) {
+	var self = this;
+	if (typeof(self.debug_handler) === "function") {
+		this.eventQueue[this.eventQueue.length] = function() { self.debug_handler(message); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	} else {
+		this.eventQueue[this.eventQueue.length] = function() { self.debugMessage(message); };
+		setTimeout(function () { self.executeNextEvent();}, 0);
+	}
+};
+
+
+/* **********************************
+	Default Event Handlers.
+	These event handlers are used by default if an overriding handler is
+	not defined in the SWFUpload settings object.
+	
+	JS Note: even though these are defined on the SWFUpload object (rather than the prototype) they
+	are attached (read: copied) to a SWFUpload instance and 'this' is given the proper context.
+   ********************************** */
+
+/* This is a special event handler that has no override in the settings.  Flash calls this when it has
+   been loaded by the browser and is ready for interaction.  You should not override it.  If you need
+   to do something with SWFUpload has loaded then use the swfupload_loaded_handler setting.
+*/
+SWFUpload.flashReady = function () {
+	try {
+		this.debug("Flash called back and is ready.");
+
+		if (typeof(this.swfUploadLoaded_handler) === "function") {
+			this.swfUploadLoaded_handler();
+		}
+	} catch (ex) {
+		this.debug(ex);
+	}
+};
+
+/* This is a chance to something immediately after SWFUpload has loaded.
+   Like, hide the default/degraded upload form and display the SWFUpload form. */
+SWFUpload.swfUploadLoaded = function () {
+};
+
+/* This is a chance to do something before the browse window opens */
+SWFUpload.fileDialogStart = function () {
+};
+
+
+/* Called when a file is successfully added to the queue. */
+SWFUpload.fileQueued = function (file) {
+};
+
+
+/* Handle errors that occur when an attempt to queue a file fails. */
+SWFUpload.fileQueueError = function (file, error_code, message) {
+	try {
+		switch (error_code) {
+		case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:
+			this.debug("Error Code: File too big, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
+			break;
+		case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:
+			this.debug("Error Code: Zero Byte File, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
+			break;
+		case SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED:
+			this.debug("Error Code: Upload limit reached, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
+			break;
+		case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE:
+			this.debug("Error Code: File extension is not allowed, Message: " + message);
+			break;
+		default:
+			this.debug("Error Code: Unhandled error occured. Errorcode: " + error_code);
+		}
+	} catch (ex) {
+		this.debug(ex);
+	}
+};
+
+/* Called after the file dialog has closed and the selected files have been queued.
+	You could call startUpload here if you want the queued files to begin uploading immediately. */
+SWFUpload.fileDialogComplete = function (num_files_selected) {
+};
+
+/* Gets called when a file upload is about to be started.  Return true to continue the upload. Return false to stop the upload.
+	If you return false then the uploadError callback is called and then uploadComplete (like normal).
+	
+	This is a good place to do any file validation you need.
+	
+	This is the only function that cannot be called on a setTimeout because it must return a value to Flash.
+	You SHOULD NOT make any calls in to Flash (e.i, changing settings, getting stats, etc).  Flash Player bugs prevent
+	calls in to Flash from working reliably.
+*/
+SWFUpload.uploadStart = function (file) {
+	return true;
+};
+
+// Called during upload as the file progresses
+SWFUpload.uploadProgress = function (file, bytes_complete, bytes_total) {
+	this.debug("File Progress: " + file.id + ", Bytes: " + bytes_complete + ". Total: " + bytes_total);
+};
+
+/* This gets called when a file finishes uploading and the upload script has completed and returned a 200 status code.	Any text returned by the
+server is available in server_data.	 The upload script must return some text or uploadSuccess will not fire (neither will uploadComplete). */
+SWFUpload.uploadSuccess = function (file, server_data) {
+};
+
+/* This is called last.	 The file is uploaded or an error occurred and SWFUpload is ready to make the next upload.
+	If you want to automatically start the next file just call startUpload from here.
+*/
+SWFUpload.uploadComplete = function (file) {
+};
+
+// Called by SWFUpload JavaScript and Flash functions when debug is enabled.
+// Override this method in your settings to call your own debug message handler
+SWFUpload.debug = function (message) {
+	if (this.getSetting("debug_enabled")) {
+		this.debugMessage(message);
+	}
+};
+
+/* Called when an upload occurs during upload.  For HTTP errors 'message' will contain the HTTP STATUS CODE */
+SWFUpload.uploadError = function (file, error_code, message) {
+	try {
+		switch (errcode) {
+		case SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND:
+			this.debug("Error Code: File ID specified for upload was not found, Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
+			this.debug("Error Code: HTTP Error, File name: " + file.name + ", Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL:
+			this.debug("Error Code: No backend file, File name: " + file.name + ", Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.IO_ERROR:
+			this.debug("Error Code: IO Error, File name: " + file.name + ", Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
+			this.debug("Error Code: Security Error, File name: " + file.name + ", Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:
+			this.debug("Error Code: Upload limit reached, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:
+			this.debug("Error Code: Upload Initialization exception, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:
+			this.debug("Error Code: uploadStart callback returned false, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
+			this.debug("Error Code: The file upload was cancelled, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
+			break;
+		case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
+			this.debug("Error Code: The file upload was stopped, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
+			break;
+		default:
+			this.debug("Error Code: Unhandled error occured. Errorcode: " + errcode);
+		}
+	} catch (ex) {
+		this.debug(ex);
+	}
+};
+
+
+
+/* **********************************
+	Debug Console
+	The debug console is a self contained, in page location
+	for debug message to be sent.  The Debug Console adds
+	itself to the body if necessary.
+
+	The console is automatically scrolled as messages appear.
+	
+	You can override this console (to use FireBug's console for instance) by setting the debug event method to your own function
+	that handles the debug message
+   ********************************** */
+SWFUpload.prototype.debugMessage = function (message) {
+	var exception_message, exception_values;
+
+	if (typeof(message) === "object" && typeof(message.name) === "string" && typeof(message.message) === "string") {
+		exception_message = "";
+		exception_values = [];
+		for (var key in message) {
+			exception_values.push(key + ": " + message[key]);
+		}
+		exception_message = exception_values.join("\n");
+		exception_values = exception_message.split("\n");
+		exception_message = "EXCEPTION: " + exception_values.join("\nEXCEPTION: ");
+		SWFUpload.Console.writeLine(exception_message);
+	} else {
+		SWFUpload.Console.writeLine(message);
+	}
+};
+
+SWFUpload.Console = {};
+SWFUpload.Console.writeLine = function (message) {
+	var console, documentForm;
+
+	try {
+		console = document.getElementById("SWFUpload_Console");
+
+		if (!console) {
+			documentForm = document.createElement("form");
+			document.getElementsByTagName("body")[0].appendChild(documentForm);
+
+			console = document.createElement("textarea");
+			console.id = "SWFUpload_Console";
+			console.style.fontFamily = "monospace";
+			console.setAttribute("wrap", "off");
+			console.wrap = "off";
+			console.style.overflow = "auto";
+			console.style.width = "700px";
+			console.style.height = "350px";
+			console.style.margin = "5px";
+			documentForm.appendChild(console);
+		}
+
+		console.value += message + "\n";
+
+		console.scrollTop = console.scrollHeight - console.clientHeight;
+	} catch (ex) {
+		alert("Exception: " + ex.name + " Message: " + ex.message);
+	}
+};
Index: /afridex/plugins/Flutter/js/canvas-options.js
===================================================================
--- /afridex/plugins/Flutter/js/canvas-options.js (revision 21)
+++ /afridex/plugins/Flutter/js/canvas-options.js (revision 21)
@@ -0,0 +1,21 @@
+Event.observe(window, 'load', canvasOptionForm, false);
+Event.observe(window, 'unload', Event.unloadCache, false);
+
+function canvasOptionForm() {
+	Event.observe('save_options', 'click', optionsUpdate, false);
+}
+
+function optionsUpdate() {
+	$('save_button').style.backgroundImage = "url('" + JS_CANVASURI + "images/spinner.gif')";
+	var pars = Form.serialize('options');
+	$$('form#options label input').each(function(item){
+		if($(item).checked == false) pars = pars+item.id+'=0&';
+	});
+	pars = pars.substr(0,pars.length-1);
+    var url = JS_CANVASURI + 'ajax/canvas-save-option.php';
+    var myAjax = new Ajax.Request(url, 
+        {method: 'post',
+        parameters: pars,
+        onComplete: function(){ $('save_button').style.backgroundImage = "url('')"; }
+        });
+}
Index: /afridex/plugins/Flutter/js/greybox/gb_styles.css
===================================================================
--- /afridex/plugins/Flutter/js/greybox/gb_styles.css (revision 21)
+++ /afridex/plugins/Flutter/js/greybox/gb_styles.css (revision 21)
@@ -0,0 +1,148 @@
+/* Last-Modified: 28/06/06 00:08:22 */
+#GB_overlay {
+    background-color: #000;
+    position: absolute;
+    margin: auto;
+    top: 0;
+    left: 0;
+    z-index: 100;
+}
+
+#GB_window {
+    left: 0;
+    top: 0;
+    font-size: 1px;
+    position: absolute;
+    overflow: visible;
+    z-index: 150;
+}
+
+#GB_window .content {
+    width: auto;
+    margin: 0;
+    padding: 0;
+}
+
+#GB_frame {
+    border: 0;
+    margin: 0;
+    padding: 0;
+    overflow: auto;
+    white-space: nowrap;
+}
+
+
+.GB_Gallery {
+    margin: 0 22px 0 22px;
+}
+
+.GB_Gallery .content {
+    background-color: #fff;
+    border: 3px solid #ddd;
+}
+
+.GB_header {
+    top: 10px;
+    left: 0;
+    margin: 0;
+    z-index: 500;
+    position: absolute;
+    border-bottom: 2px solid #555;
+    border-top: 2px solid #555;
+}
+
+.GB_header .inner {
+    background-color: #333;
+    font-family: Arial, Verdana, sans-serif;
+    padding: 2px 20px 2px 20px;
+}
+
+.GB_header table {
+    margin: 0;
+    width: 100%;
+    border-collapse: collapse;
+}
+
+.GB_header .caption {
+    text-align: left;
+    color: #eee;
+    white-space: nowrap;
+    font-size: 20px;
+}
+
+.GB_header .close {
+    text-align: right;
+}
+
+.GB_header .close img {
+    z-index: 500;
+    cursor: pointer;
+}
+
+.GB_header .middle {
+    white-space: nowrap;
+    text-align: center;
+}
+
+
+#GB_middle {
+    color: #eee;
+}
+
+#GB_middle img {
+    cursor: pointer;
+    vertical-align: middle;
+}
+
+#GB_middle .disabled {
+    cursor: default;
+}
+
+#GB_middle .left {
+    padding-right: 10px;
+}
+
+#GB_middle .right {
+    padding-left: 10px;
+}
+
+
+.GB_Window .content {
+    background-color: #fff;
+    border: 3px solid #ccc;
+    border-top: none;
+}
+
+.GB_Window .header {
+    border-bottom: 1px solid #aaa;
+    border-top: 1px solid #999;
+    border-left: 3px solid #ccc;
+    border-right: 3px solid #ccc;
+    margin: 0;
+
+    height: 22px;
+    font-size: 12px;
+    padding: 3px 0;
+    color: #333;
+}
+
+.GB_Window .caption {
+    font-size: 12px;
+    text-align: left;
+    font-weight: bold;
+    white-space: nowrap;
+    padding-right: 20px;
+}
+
+.GB_Window .close { text-align: right; }
+.GB_Window .close span { 
+    font-size: 12px;
+    cursor: pointer; 
+}
+.GB_Window .close img {
+    cursor: pointer;
+    padding: 0 3px 0 0;
+}
+
+.GB_Window .on { border-bottom: 1px solid #333; }
+.GB_Window .click { border-bottom: 1px solid red; }
Index: /afridex/plugins/Flutter/js/greybox/loader_frame.html
===================================================================
--- /afridex/plugins/Flutter/js/greybox/loader_frame.html (revision 21)
+++ /afridex/plugins/Flutter/js/greybox/loader_frame.html (revision 21)
@@ -0,0 +1,104 @@
+<!--
+Notice: I feel so dirty doing this, but its the only way to make it cross browser.
+-->
+<html>
+<head>
+  <script>
+    var GB = parent.GB_CURRENT;
+    document.write('<script type="text/javascript" src="AJS.js"><\/script>');
+    if(GB.use_fx) {
+        document.write('<script type="text/javascript" src="AJS_fx.js"><\/script>');
+    }
+  </script>
+  <style>
+    body {
+      padding: 0;
+      margin: 0;
+      overflow: hidden;
+    }
+
+    #GB_frame {
+      visibility: hidden;
+      width: 100%;
+      height: 100%;
+    }
+
+    #loading {
+      padding-top: 50px;
+      position: absolute;
+      width: 100%;
+      top: 0;
+      text-align: center;
+      vertical-align: middle;
+    }
+  </style>
+</head>
+<body>
+
+<div id="loading">
+  <img src="indicator.gif">
+</div>
+
+<script>
+var loading = AJS.$('loading');
+var gb_type = GB.type;
+var gb_url = GB.url;
+
+//Start loading in the iframe
+if(gb_type == "page") {
+  document.write('<iframe id="GB_frame" src="' + gb_url + '" frameborder="0"></iframe>');
+}
+else {
+  var img_holder = new Image();
+  img_holder.src = gb_url;
+  document.write('<img id="GB_frame" src="' + gb_url + '">');
+}
+var frame = AJS.$('GB_frame');
+</script>
+
+</body>
+<script>
+function setupOuterGB() {
+    frame.style.visibility = 'visible';
+    GB.setFrameSize();
+    GB.setWindowPosition();
+}
+
+function loaded() {
+    AJS.removeElement(loading);
+
+    GB.overlay.innerHTML += "&nbsp;"; //Safari bugfix
+    
+    if(gb_type == "image") {
+        if(img_holder.width != 0 && img_holder.height != 0) {
+            var width = img_holder.width;
+            var height = img_holder.height;
+
+            GB.width = width;
+            GB.height = height;
+
+            setupOuterGB();
+
+            if(GB.use_fx) {
+                AJS.setOpacity(frame, 0);
+                AJS.fx.fadeIn(frame);
+            }
+        }
+    }
+    else {
+        GB.width = frame.offsetWidth;
+        GB.height = frame.offsetHeight;
+        setupOuterGB();
+    }
+}
+
+if(GB.show_loading) {
+    AJS.AEV(window, 'load', function(e) {
+        loaded();
+    });
+}
+else {
+    loaded();
+}
+</script>
+</html>
Index: /afridex/plugins/Flutter/js/greybox/AJS_fx.js
===================================================================
--- /afridex/plugins/Flutter/js/greybox/AJS_fx.js (revision 21)
+++ /afridex/plugins/Flutter/js/greybox/AJS_fx.js (revision 21)
@@ -0,0 +1,138 @@
+AJS.fx={_shades:{0:"ffffff",1:"ffffee",2:"ffffdd",3:"ffffcc",4:"ffffbb",5:"ffffaa",6:"ffff99"},highlight:function(_1,_2){
+var _3=new AJS.fx.Base();
+_3.elm=AJS.$(_1);
+_3.options.duration=600;
+_3.setOptions(_2);
+AJS.update(_3,{increase:function(){
+if(this.now==7){
+_1.style.backgroundColor="#fff";
+}else{
+_1.style.backgroundColor="#"+AJS.fx._shades[Math.floor(this.now)];
+}
+}});
+return _3.custom(6,0);
+},fadeIn:function(_4,_5){
+_5=_5||{};
+if(!_5.from){
+_5.from=0;
+AJS.setOpacity(_4,0);
+}
+if(!_5.to){
+_5.to=1;
+}
+var s=new AJS.fx.Style(_4,"opacity",_5);
+return s.custom(_5.from,_5.to);
+},fadeOut:function(_7,_8){
+_8=_8||{};
+if(!_8.from){
+_8.from=1;
+}
+if(!_8.to){
+_8.to=0;
+}
+_8.duration=300;
+var s=new AJS.fx.Style(_7,"opacity",_8);
+return s.custom(_8.from,_8.to);
+},setWidth:function(_a,_b){
+var s=new AJS.fx.Style(_a,"width",_b);
+return s.custom(_b.from,_b.to);
+},setHeight:function(_d,_e){
+var s=new AJS.fx.Style(_d,"height",_e);
+return s.custom(_e.from,_e.to);
+}};
+AJS.fx.Base=new AJS.Class({init:function(_10){
+this.options={onStart:function(){
+},onComplete:function(){
+},transition:AJS.fx.Transitions.sineInOut,duration:500,wait:true,fps:50};
+AJS.update(this.options,_10);
+AJS.bindMethods(this);
+},setOptions:function(_11){
+AJS.update(this.options,_11);
+},step:function(){
+var _12=new Date().getTime();
+if(_12<this.time+this.options.duration){
+this.cTime=_12-this.time;
+this.setNow();
+}else{
+setTimeout(AJS.$b(this.options.onComplete,this,[this.elm]),10);
+this.clearTimer();
+this.now=this.to;
+}
+this.increase();
+},setNow:function(){
+this.now=this.compute(this.from,this.to);
+},compute:function(_13,to){
+var _15=to-_13;
+return this.options.transition(this.cTime,_13,_15,this.options.duration);
+},clearTimer:function(){
+clearInterval(this.timer);
+this.timer=null;
+return this;
+},_start:function(_16,to){
+if(!this.options.wait){
+this.clearTimer();
+}
+if(this.timer){
+return;
+}
+setTimeout(AJS.$p(this.options.onStart,this.elm),10);
+this.from=_16;
+this.to=to;
+this.time=new Date().getTime();
+this.timer=setInterval(this.step,Math.round(1000/this.options.fps));
+return this;
+},custom:function(_18,to){
+return this._start(_18,to);
+},set:function(to){
+this.now=to;
+this.increase();
+return this;
+},setStyle:function(elm,_1c,val){
+if(this.property=="opacity"){
+AJS.setOpacity(elm,val);
+}else{
+AJS.setStyle(elm,_1c,val);
+}
+}});
+AJS.fx.Style=AJS.fx.Base.extend({init:function(elm,_1f,_20){
+this.parent();
+this.elm=elm;
+this.setOptions(_20);
+this.property=_1f;
+},increase:function(){
+this.setStyle(this.elm,this.property,this.now);
+}});
+AJS.fx.Styles=AJS.fx.Base.extend({init:function(elm,_22){
+this.parent();
+this.elm=AJS.$(elm);
+this.setOptions(_22);
+this.now={};
+},setNow:function(){
+for(p in this.from){
+this.now[p]=this.compute(this.from[p],this.to[p]);
+}
+},custom:function(obj){
+if(this.timer&&this.options.wait){
+return;
+}
+var _24={};
+var to={};
+for(p in obj){
+_24[p]=obj[p][0];
+to[p]=obj[p][1];
+}
+return this._start(_24,to);
+},increase:function(){
+for(var p in this.now){
+this.setStyle(this.elm,p,this.now[p]);
+}
+}});
+AJS.fx.Transitions={linear:function(t,b,c,d){
+return c*t/d+b;
+},sineInOut:function(t,b,c,d){
+return -c/2*(Math.cos(Math.PI*t/d)-1)+b;
+}};
+script_loaded=true;
+
+
+script_loaded=true;
Index: /afridex/plugins/Flutter/js/greybox/AJS.js
===================================================================
--- /afridex/plugins/Flutter/js/greybox/AJS.js (revision 21)
+++ /afridex/plugins/Flutter/js/greybox/AJS.js (revision 21)
@@ -0,0 +1,514 @@
+AJS={BASE_URL:"",drag_obj:null,drag_elm:null,_drop_zones:[],_cur_pos:null,getScrollTop:function(){
+var t;
+if(document.documentElement&&document.documentElement.scrollTop){
+t=document.documentElement.scrollTop;
+}else{
+if(document.body){
+t=document.body.scrollTop;
+}
+}
+return t;
+},addClass:function(){
+var _2=AJS.forceArray(arguments);
+var _3=_2.pop();
+var _4=function(o){
+if(!new RegExp("(^|\\s)"+_3+"(\\s|$)").test(o.className)){
+o.className+=(o.className?" ":"")+_3;
+}
+};
+AJS.map(_2,function(_6){
+_4(_6);
+});
+},setStyle:function(){
+var _7=AJS.forceArray(arguments);
+var _8=_7.pop();
+var _9=_7.pop();
+AJS.map(_7,function(_a){
+_a.style[_9]=AJS.getCssDim(_8);
+});
+},extend:function(_b){
+var _c=new this("no_init");
+for(k in _b){
+var _d=_c[k];
+var _e=_b[k];
+if(_d&&_d!=_e&&typeof _e=="function"){
+_e=this._parentize(_e,_d);
+}
+_c[k]=_e;
+}
+return new AJS.Class(_c);
+},log:function(o){
+if(window.console){
+console.log(o);
+}else{
+var div=AJS.$("ajs_logger");
+if(!div){
+div=AJS.DIV({id:"ajs_logger","style":"color: green; position: absolute; left: 0"});
+div.style.top=AJS.getScrollTop()+"px";
+AJS.ACN(AJS.getBody(),div);
+}
+AJS.setHTML(div,""+o);
+}
+},setHeight:function(){
+var _11=AJS.forceArray(arguments);
+_11.splice(_11.length-1,0,"height");
+AJS.setStyle.apply(null,_11);
+},_getRealScope:function(fn,_13){
+_13=AJS.$A(_13);
+var _14=fn._cscope||window;
+return function(){
+var _15=AJS.$FA(arguments).concat(_13);
+return fn.apply(_14,_15);
+};
+},documentInsert:function(elm){
+if(typeof (elm)=="string"){
+elm=AJS.HTML2DOM(elm);
+}
+document.write("<span id=\"dummy_holder\"></span>");
+AJS.swapDOM(AJS.$("dummy_holder"),elm);
+},getWindowSize:function(doc){
+doc=doc||document;
+var _18,_19;
+if(self.innerHeight){
+_18=self.innerWidth;
+_19=self.innerHeight;
+}else{
+if(doc.documentElement&&doc.documentElement.clientHeight){
+_18=doc.documentElement.clientWidth;
+_19=doc.documentElement.clientHeight;
+}else{
+if(doc.body){
+_18=doc.body.clientWidth;
+_19=doc.body.clientHeight;
+}
+}
+}
+return {"w":_18,"h":_19};
+},flattenList:function(_1a){
+var r=[];
+var _1c=function(r,l){
+AJS.map(l,function(o){
+if(o==null){
+}else{
+if(AJS.isArray(o)){
+_1c(r,o);
+}else{
+r.push(o);
+}
+}
+});
+};
+_1c(r,_1a);
+return r;
+},isFunction:function(obj){
+return (typeof obj=="function");
+},setEventKey:function(e){
+e.key=e.keyCode?e.keyCode:e.charCode;
+if(window.event){
+e.ctrl=window.event.ctrlKey;
+e.shift=window.event.shiftKey;
+}else{
+e.ctrl=e.ctrlKey;
+e.shift=e.shiftKey;
+}
+switch(e.key){
+case 63232:
+e.key=38;
+break;
+case 63233:
+e.key=40;
+break;
+case 63235:
+e.key=39;
+break;
+case 63234:
+e.key=37;
+break;
+}
+},removeElement:function(){
+var _22=AJS.forceArray(arguments);
+AJS.map(_22,function(elm){
+AJS.swapDOM(elm,null);
+});
+},_unloadListeners:function(){
+if(AJS.listeners){
+AJS.map(AJS.listeners,function(elm,_25,fn){
+AJS.REV(elm,_25,fn);
+});
+}
+AJS.listeners=[];
+},join:function(_27,_28){
+try{
+return _28.join(_27);
+}
+catch(e){
+var r=_28[0]||"";
+AJS.map(_28,function(elm){
+r+=_27+elm;
+},1);
+return r+"";
+}
+},getIndex:function(elm,_2c,_2d){
+for(var i=0;i<_2c.length;i++){
+if(_2d&&_2d(_2c[i])||elm==_2c[i]){
+return i;
+}
+}
+return -1;
+},isIn:function(elm,_30){
+var i=AJS.getIndex(elm,_30);
+if(i!=-1){
+return true;
+}else{
+return false;
+}
+},isArray:function(obj){
+return obj instanceof Array;
+},setLeft:function(){
+var _33=AJS.forceArray(arguments);
+_33.splice(_33.length-1,0,"left");
+AJS.setStyle.apply(null,_33);
+},appendChildNodes:function(elm){
+if(arguments.length>=2){
+AJS.map(arguments,function(n){
+if(AJS.isString(n)){
+n=AJS.TN(n);
+}
+if(AJS.isDefined(n)){
+elm.appendChild(n);
+}
+},1);
+}
+return elm;
+},getElementsByTagAndClassName:function(_36,_37,_38,_39){
+var _3a=[];
+if(!AJS.isDefined(_38)){
+_38=document;
+}
+if(!AJS.isDefined(_36)){
+_36="*";
+}
+var els=_38.getElementsByTagName(_36);
+var _3c=els.length;
+var _3d=new RegExp("(^|\\s)"+_37+"(\\s|$)");
+for(i=0,j=0;i<_3c;i++){
+if(_3d.test(els[i].className)||_37==null){
+_3a[j]=els[i];
+j++;
+}
+}
+if(_39){
+return _3a[0];
+}else{
+return _3a;
+}
+},isOpera:function(){
+return (navigator.userAgent.toLowerCase().indexOf("opera")!=-1);
+},isString:function(obj){
+return (typeof obj=="string");
+},hideElement:function(elm){
+var _40=AJS.forceArray(arguments);
+AJS.map(_40,function(elm){
+elm.style.display="none";
+});
+},setOpacity:function(elm,p){
+elm.style.opacity=p;
+elm.style.filter="alpha(opacity="+p*100+")";
+},insertBefore:function(elm,_45){
+_45.parentNode.insertBefore(elm,_45);
+return elm;
+},setWidth:function(){
+var _46=AJS.forceArray(arguments);
+_46.splice(_46.length-1,0,"width");
+AJS.setStyle.apply(null,_46);
+},createArray:function(v){
+if(AJS.isArray(v)&&!AJS.isString(v)){
+return v;
+}else{
+if(!v){
+return [];
+}else{
+return [v];
+}
+}
+},isDict:function(o){
+var _49=String(o);
+return _49.indexOf(" Object")!=-1;
+},isMozilla:function(){
+return (navigator.userAgent.toLowerCase().indexOf("gecko")!=-1&&navigator.productSub>=20030210);
+},removeEventListener:function(elm,_4b,fn,_4d){
+var _4e="ajsl_"+_4b+fn;
+if(!_4d){
+_4d=false;
+}
+fn=elm[_4e]||fn;
+if(elm["on"+_4b]==fn){
+elm["on"+_4b]=elm[_4e+"old"];
+}
+if(elm.removeEventListener){
+elm.removeEventListener(_4b,fn,_4d);
+if(AJS.isOpera()){
+elm.removeEventListener(_4b,fn,!_4d);
+}
+}else{
+if(elm.detachEvent){
+elm.detachEvent("on"+_4b,fn);
+}
+}
+},callLater:function(fn,_50){
+var _51=function(){
+fn();
+};
+window.setTimeout(_51,_50);
+},setTop:function(){
+var _52=AJS.forceArray(arguments);
+_52.splice(_52.length-1,0,"top");
+AJS.setStyle.apply(null,_52);
+},_createDomShortcuts:function(){
+var _53=["ul","li","td","tr","th","tbody","table","input","span","b","a","div","img","button","h1","h2","h3","h4","h5","h6","br","textarea","form","p","select","option","optgroup","iframe","script","center","dl","dt","dd","small","pre","i"];
+var _54=function(elm){
+AJS[elm.toUpperCase()]=function(){
+return AJS.createDOM.apply(null,[elm,arguments]);
+};
+};
+AJS.map(_53,_54);
+AJS.TN=function(_56){
+return document.createTextNode(_56);
+};
+},addCallback:function(fn){
+this.callbacks.unshift(fn);
+},bindMethods:function(_58){
+for(var k in _58){
+var _5a=_58[k];
+if(typeof (_5a)=="function"){
+_58[k]=AJS.$b(_5a,_58);
+}
+}
+},partial:function(fn){
+var _5c=AJS.$FA(arguments);
+_5c.shift();
+return function(){
+_5c=_5c.concat(AJS.$FA(arguments));
+return fn.apply(window,_5c);
+};
+},isNumber:function(obj){
+return (typeof obj=="number");
+},getCssDim:function(dim){
+if(AJS.isString(dim)){
+return dim;
+}else{
+return dim+"px";
+}
+},isIe:function(){
+return (navigator.userAgent.toLowerCase().indexOf("msie")!=-1&&navigator.userAgent.toLowerCase().indexOf("opera")==-1);
+},removeClass:function(){
+var _5f=AJS.forceArray(arguments);
+var cls=_5f.pop();
+var _61=function(o){
+o.className=o.className.replace(new RegExp("\\s?"+cls,"g"),"");
+};
+AJS.map(_5f,function(elm){
+_61(elm);
+});
+},setHTML:function(elm,_65){
+elm.innerHTML=_65;
+return elm;
+},map:function(_66,fn,_68,_69){
+var i=0,l=_66.length;
+if(_68){
+i=_68;
+}
+if(_69){
+l=_69;
+}
+for(i;i<l;i++){
+var val=fn(_66[i],i);
+if(val!=undefined){
+return val;
+}
+}
+},addEventListener:function(elm,_6e,fn,_70,_71){
+var _72="ajsl_"+_6e+fn;
+if(!_71){
+_71=false;
+}
+AJS.listeners=AJS.$A(AJS.listeners);
+if(AJS.isIn(_6e,["keypress","keydown","keyup","click"])){
+var _73=fn;
+fn=function(e){
+AJS.setEventKey(e);
+return _73.apply(window,arguments);
+};
+}
+var _75=AJS.isIn(_6e,["submit","load","scroll","resize"]);
+var _76=AJS.$A(elm);
+AJS.map(_76,function(_77){
+if(_70){
+var _78=fn;
+fn=function(e){
+AJS.REV(_77,_6e,fn);
+return _78.apply(window,arguments);
+};
+}
+if(_75){
+var _7a=_77["on"+_6e];
+var _7b=function(){
+if(_7a){
+fn(arguments);
+return _7a(arguments);
+}else{
+return fn(arguments);
+}
+};
+_77[_72]=_7b;
+_77[_72+"old"]=_7a;
+elm["on"+_6e]=_7b;
+}else{
+_77[_72]=fn;
+if(_77.attachEvent){
+_77.attachEvent("on"+_6e,fn);
+}else{
+if(_77.addEventListener){
+_77.addEventListener(_6e,fn,_71);
+}
+}
+AJS.listeners.push([_77,_6e,fn]);
+}
+});
+},preloadImages:function(){
+AJS.AEV(window,"load",AJS.$p(function(_7c){
+AJS.map(_7c,function(src){
+var pic=new Image();
+pic.src=src;
+});
+},arguments));
+},forceArray:function(_7f){
+var r=[];
+AJS.map(_7f,function(elm){
+r.push(elm);
+});
+return r;
+},update:function(l1,l2){
+for(var i in l2){
+l1[i]=l2[i];
+}
+return l1;
+},getBody:function(){
+return AJS.$bytc("body")[0];
+},HTML2DOM:function(_85,_86){
+var d=AJS.DIV();
+d.innerHTML=_85;
+if(_86){
+return d.childNodes[0];
+}else{
+return d;
+}
+},getElement:function(id){
+if(AJS.isString(id)||AJS.isNumber(id)){
+return document.getElementById(id);
+}else{
+return id;
+}
+},showElement:function(){
+var _89=AJS.forceArray(arguments);
+AJS.map(_89,function(elm){
+elm.style.display="";
+});
+},bind:function(fn,_8c,_8d){
+fn._cscope=_8c;
+return AJS._getRealScope(fn,_8d);
+},createDOM:function(_8e,_8f){
+var i=0,_91;
+var elm=document.createElement(_8e);
+var _93=_8f[0];
+if(AJS.isDict(_8f[i])){
+for(k in _93){
+_91=_93[k];
+if(k=="style"||k=="s"){
+elm.style.cssText=_91;
+}else{
+if(k=="c"||k=="class"||k=="className"){
+elm.className=_91;
+}else{
+elm.setAttribute(k,_91);
+}
+}
+}
+i++;
+}
+if(_93==null){
+i=1;
+}
+for(var j=i;j<_8f.length;j++){
+var _91=_8f[j];
+if(_91){
+var _95=typeof (_91);
+if(_95=="string"||_95=="number"){
+_91=AJS.TN(_91);
+}
+elm.appendChild(_91);
+}
+}
+return elm;
+},swapDOM:function(_96,src){
+_96=AJS.getElement(_96);
+var _98=_96.parentNode;
+if(src){
+src=AJS.getElement(src);
+_98.replaceChild(src,_96);
+}else{
+_98.removeChild(_96);
+}
+return src;
+},isDefined:function(o){
+return (o!="undefined"&&o!=null);
+}};
+AJS.$=AJS.getElement;
+AJS.$$=AJS.getElements;
+AJS.$f=AJS.getFormElement;
+AJS.$p=AJS.partial;
+AJS.$b=AJS.bind;
+AJS.$A=AJS.createArray;
+AJS.DI=AJS.documentInsert;
+AJS.ACN=AJS.appendChildNodes;
+AJS.RCN=AJS.replaceChildNodes;
+AJS.AEV=AJS.addEventListener;
+AJS.REV=AJS.removeEventListener;
+AJS.$bytc=AJS.getElementsByTagAndClassName;
+AJS.$AP=AJS.absolutePosition;
+AJS.$FA=AJS.forceArray;
+AJS.addEventListener(window,"unload",AJS._unloadListeners);
+AJS._createDomShortcuts();
+AJS.Class=function(_9a){
+var fn=function(){
+if(arguments[0]!="no_init"){
+return this.init.apply(this,arguments);
+}
+};
+fn.prototype=_9a;
+AJS.update(fn,AJS.Class.prototype);
+return fn;
+};
+AJS.Class.prototype={extend:function(_9c){
+var _9d=new this("no_init");
+for(k in _9c){
+var _9e=_9d[k];
+var cur=_9c[k];
+if(_9e&&_9e!=cur&&typeof cur=="function"){
+cur=this._parentize(cur,_9e);
+}
+_9d[k]=cur;
+}
+return new AJS.Class(_9d);
+},implement:function(_a0){
+AJS.update(this.prototype,_a0);
+},_parentize:function(cur,_a2){
+return function(){
+this.parent=_a2;
+return cur.apply(this,arguments);
+};
+}};
+script_loaded=true;
+
+
+script_loaded=true;
Index: /afridex/plugins/Flutter/js/greybox/gb_scripts.js
===================================================================
--- /afridex/plugins/Flutter/js/greybox/gb_scripts.js (revision 21)
+++ /afridex/plugins/Flutter/js/greybox/gb_scripts.js (revision 21)
@@ -0,0 +1,445 @@
+var GB_CURRENT=null;
+GB_hide=function(cb){
+GB_CURRENT.hide(cb);
+};
+GreyBox=new AJS.Class({init:function(_2){
+this.use_fx=AJS.fx;
+this.type="page";
+this.overlay_click_close=false;
+this.salt=0;
+this.root_dir=GB_ROOT_DIR;
+this.callback_fns=[];
+this.reload_on_close=false;
+this.src_loader=this.root_dir+"loader_frame.html";
+var _3=window.location.hostname.indexOf("www");
+var _4=this.src_loader.indexOf("www");
+if(_3!=-1&&_4==-1){
+this.src_loader=this.src_loader.replace("://","://www.");
+}
+if(_3==-1&&_4!=-1){
+this.src_loader=this.src_loader.replace("://www.","://");
+}
+this.show_loading=true;
+AJS.update(this,_2);
+},addCallback:function(fn){
+if(fn){
+this.callback_fns.push(fn);
+}
+},show:function(_6){
+GB_CURRENT=this;
+this.url=_6;
+var _7=[AJS.$bytc("object"),AJS.$bytc("select")];
+AJS.map(AJS.flattenList(_7),function(_8){
+_8.style.visibility="hidden";
+});
+this.createElements();
+return false;
+},hide:function(cb){
+var me=this;
+AJS.callLater(function(){
+var _b=me.callback_fns;
+if(_b!=[]){
+AJS.map(_b,function(fn){
+fn();
+});
+}
+me.onHide();
+if(me.use_fx){
+var _d=me.overlay;
+AJS.fx.fadeOut(me.overlay,{onComplete:function(){
+AJS.removeElement(_d);
+_d=null;
+},duration:300});
+AJS.removeElement(me.g_window);
+}else{
+AJS.removeElement(me.g_window,me.overlay);
+}
+me.removeFrame();
+AJS.REV(window,"scroll",_GB_setOverlayDimension);
+AJS.REV(window,"resize",_GB_update);
+var _e=[AJS.$bytc("object"),AJS.$bytc("select")];
+AJS.map(AJS.flattenList(_e),function(_f){
+_f.style.visibility="visible";
+});
+GB_CURRENT=null;
+if(me.reload_on_close){
+window.location.reload();
+}
+if(AJS.isFunction(cb)){
+cb();
+}
+},10);
+},update:function(){
+this.setOverlayDimension();
+this.setFrameSize();
+this.setWindowPosition();
+},createElements:function(){
+this.initOverlay();
+this.g_window=AJS.DIV({"id":"GB_window"});
+AJS.hideElement(this.g_window);
+AJS.getBody().insertBefore(this.g_window,this.overlay.nextSibling);
+this.initFrame();
+this.initHook();
+this.update();
+var me=this;
+if(this.use_fx){
+AJS.fx.fadeIn(this.overlay,{duration:300,to:0.7,onComplete:function(){
+me.onShow();
+AJS.showElement(me.g_window);
+me.startLoading();
+}});
+}else{
+AJS.setOpacity(this.overlay,0.7);
+AJS.showElement(this.g_window);
+this.onShow();
+this.startLoading();
+}
+AJS.AEV(window,"scroll",_GB_setOverlayDimension);
+AJS.AEV(window,"resize",_GB_update);
+},removeFrame:function(){
+try{
+AJS.removeElement(this.iframe);
+}
+catch(e){
+}
+this.iframe=null;
+},startLoading:function(){
+this.iframe.src=this.src_loader+"?s="+this.salt++;
+AJS.showElement(this.iframe);
+},setOverlayDimension:function(){
+var _11=AJS.getWindowSize();
+if(AJS.isMozilla()||AJS.isOpera()){
+AJS.setWidth(this.overlay,"100%");
+}else{
+AJS.setWidth(this.overlay,_11.w);
+}
+var _12=Math.max(AJS.getScrollTop()+_11.h,AJS.getScrollTop()+this.height);
+if(_12<AJS.getScrollTop()){
+AJS.setHeight(this.overlay,_12);
+}else{
+AJS.setHeight(this.overlay,AJS.getScrollTop()+_11.h);
+}
+},initOverlay:function(){
+this.overlay=AJS.DIV({"id":"GB_overlay"});
+if(this.overlay_click_close){
+AJS.AEV(this.overlay,"click",GB_hide);
+}
+AJS.setOpacity(this.overlay,0);
+AJS.getBody().insertBefore(this.overlay,AJS.getBody().firstChild);
+},initFrame:function(){
+if(!this.iframe){
+var d={"name":"GB_frame","class":"GB_frame","frameBorder":0};
+if(AJS.isIe()){
+d.src="javascript:false;document.write(\"\");";
+}
+this.iframe=AJS.IFRAME(d);
+this.middle_cnt=AJS.DIV({"class":"content"},this.iframe);
+this.top_cnt=AJS.DIV();
+this.bottom_cnt=AJS.DIV();
+AJS.ACN(this.g_window,this.top_cnt,this.middle_cnt,this.bottom_cnt);
+}
+},onHide:function(){
+},onShow:function(){
+},setFrameSize:function(){
+},setWindowPosition:function(){
+},initHook:function(){
+}});
+_GB_update=function(){
+if(GB_CURRENT){
+GB_CURRENT.update();
+}
+};
+_GB_setOverlayDimension=function(){
+if(GB_CURRENT){
+GB_CURRENT.setOverlayDimension();
+}
+};
+AJS.preloadImages(GB_ROOT_DIR+"indicator.gif");
+script_loaded=true;
+var GB_SETS={};
+function decoGreyboxLinks(){
+var as=AJS.$bytc("a");
+AJS.map(as,function(a){
+if(a.getAttribute("href")&&a.getAttribute("rel")){
+var rel=a.getAttribute("rel");
+if(rel.indexOf("gb_")==0){
+var _17=rel.match(/\w+/)[0];
+var _18=rel.match(/\[(.*)\]/)[1];
+var _19=0;
+var _1a={"caption":a.title||"","url":a.href};
+if(_17=="gb_pageset"||_17=="gb_imageset"){
+if(!GB_SETS[_18]){
+GB_SETS[_18]=[];
+}
+GB_SETS[_18].push(_1a);
+_19=GB_SETS[_18].length;
+}
+if(_17=="gb_pageset"){
+a.onclick=function(){
+GB_showFullScreenSet(GB_SETS[_18],_19);
+return false;
+};
+}
+if(_17=="gb_imageset"){
+a.onclick=function(){
+GB_showImageSet(GB_SETS[_18],_19);
+return false;
+};
+}
+if(_17=="gb_image"){
+a.onclick=function(){
+GB_showImage(_1a.caption,_1a.url);
+return false;
+};
+}
+if(_17=="gb_page"){
+a.onclick=function(){
+var sp=_18.split(/, ?/);
+GB_show(_1a.caption,_1a.url,parseInt(sp[1]),parseInt(sp[0]));
+return false;
+};
+}
+if(_17=="gb_page_fs"){
+a.onclick=function(){
+GB_showFullScreen(_1a.caption,_1a.url);
+return false;
+};
+}
+if(_17=="gb_page_center"){
+a.onclick=function(){
+var sp=_18.split(/, ?/);
+GB_showCenter(_1a.caption,_1a.url,parseInt(sp[1]),parseInt(sp[0]));
+return false;
+};
+}
+}
+}
+});
+}
+AJS.AEV(window,"load",decoGreyboxLinks);
+GB_showImage=function(_1d,url,_1f){
+var _20={width:300,height:300,type:"image",fullscreen:false,center_win:true,caption:_1d,callback_fn:_1f};
+var win=new GB_Gallery(_20);
+return win.show(url);
+};
+GB_showPage=function(_22,url,_24){
+var _25={type:"page",caption:_22,callback_fn:_24,fullscreen:true,center_win:false};
+var win=new GB_Gallery(_25);
+return win.show(url);
+};
+GB_Gallery=GreyBox.extend({init:function(_27){
+this.parent({});
+this.img_close=this.root_dir+"g_close.gif";
+AJS.update(this,_27);
+this.addCallback(this.callback_fn);
+},initHook:function(){
+AJS.addClass(this.g_window,"GB_Gallery");
+var _28=AJS.DIV({"class":"inner"});
+this.header=AJS.DIV({"class":"GB_header"},_28);
+AJS.setOpacity(this.header,0);
+AJS.getBody().insertBefore(this.header,this.overlay.nextSibling);
+var _29=AJS.TD({"id":"GB_caption","class":"caption","width":"40%"},this.caption);
+var _2a=AJS.TD({"id":"GB_middle","class":"middle","width":"20%"});
+var _2b=AJS.IMG({"src":this.img_close});
+AJS.AEV(_2b,"click",GB_hide);
+var _2c=AJS.TD({"class":"close","width":"40%"},_2b);
+var _2d=AJS.TBODY(AJS.TR(_29,_2a,_2c));
+var _2e=AJS.TABLE({"cellspacing":"0","cellpadding":0,"border":0},_2d);
+AJS.ACN(_28,_2e);
+if(this.fullscreen){
+AJS.AEV(window,"scroll",AJS.$b(this.setWindowPosition,this));
+}else{
+AJS.AEV(window,"scroll",AJS.$b(this._setHeaderPos,this));
+}
+},setFrameSize:function(){
+var _2f=this.overlay.offsetWidth;
+var _30=AJS.getWindowSize();
+if(this.fullscreen){
+this.width=_2f-40;
+this.height=_30.h-80;
+}
+AJS.setWidth(this.iframe,this.width);
+AJS.setHeight(this.iframe,this.height);
+AJS.setWidth(this.header,_2f);
+},_setHeaderPos:function(){
+AJS.setTop(this.header,AJS.getScrollTop()+10);
+},setWindowPosition:function(){
+var _31=this.overlay.offsetWidth;
+var _32=AJS.getWindowSize();
+AJS.setLeft(this.g_window,((_31-50-this.width)/2));
+var _33=AJS.getScrollTop()+55;
+if(!this.center_win){
+AJS.setTop(this.g_window,_33);
+}else{
+var fl=((_32.h-this.height)/2)+20+AJS.getScrollTop();
+if(fl<0){
+fl=0;
+}
+if(_33>fl){
+fl=_33;
+}
+AJS.setTop(this.g_window,fl);
+}
+this._setHeaderPos();
+},onHide:function(){
+AJS.removeElement(this.header);
+AJS.removeClass(this.g_window,"GB_Gallery");
+},onShow:function(){
+if(this.use_fx){
+AJS.fx.fadeIn(this.header,{to:1});
+}else{
+AJS.setOpacity(this.header,1);
+}
+}});
+AJS.preloadImages(GB_ROOT_DIR+"g_close.gif");
+GB_showFullScreenSet=function(set,_36,_37){
+var _38={type:"page",fullscreen:true,center_win:false};
+var _39=new GB_Sets(_38,set);
+_39.addCallback(_37);
+_39.showSet(_36-1);
+return false;
+};
+GB_showImageSet=function(set,_3b,_3c){
+var _3d={type:"image",fullscreen:false,center_win:true,width:300,height:300};
+var _3e=new GB_Sets(_3d,set);
+_3e.addCallback(_3c);
+_3e.showSet(_3b-1);
+return false;
+};
+GB_Sets=GB_Gallery.extend({init:function(_3f,set){
+this.parent(_3f);
+if(!this.img_next){
+this.img_next=this.root_dir+"next.gif";
+}
+if(!this.img_prev){
+this.img_prev=this.root_dir+"prev.gif";
+}
+this.current_set=set;
+},showSet:function(_41){
+this.current_index=_41;
+var _42=this.current_set[this.current_index];
+this.show(_42.url);
+this._setCaption(_42.caption);
+this.btn_prev=AJS.IMG({"class":"left",src:this.img_prev});
+this.btn_next=AJS.IMG({"class":"right",src:this.img_next});
+AJS.AEV(this.btn_prev,"click",AJS.$b(this.switchPrev,this));
+AJS.AEV(this.btn_next,"click",AJS.$b(this.switchNext,this));
+GB_STATUS=AJS.SPAN({"class":"GB_navStatus"});
+AJS.ACN(AJS.$("GB_middle"),this.btn_prev,GB_STATUS,this.btn_next);
+this.updateStatus();
+},updateStatus:function(){
+AJS.setHTML(GB_STATUS,(this.current_index+1)+" / "+this.current_set.length);
+if(this.current_index==0){
+AJS.addClass(this.btn_prev,"disabled");
+}else{
+AJS.removeClass(this.btn_prev,"disabled");
+}
+if(this.current_index==this.current_set.length-1){
+AJS.addClass(this.btn_next,"disabled");
+}else{
+AJS.removeClass(this.btn_next,"disabled");
+}
+},_setCaption:function(_43){
+AJS.setHTML(AJS.$("GB_caption"),_43);
+},updateFrame:function(){
+var _44=this.current_set[this.current_index];
+this._setCaption(_44.caption);
+this.url=_44.url;
+this.startLoading();
+},switchPrev:function(){
+if(this.current_index!=0){
+this.current_index--;
+this.updateFrame();
+this.updateStatus();
+}
+},switchNext:function(){
+if(this.current_index!=this.current_set.length-1){
+this.current_index++;
+this.updateFrame();
+this.updateStatus();
+}
+}});
+AJS.AEV(window,"load",function(){
+AJS.preloadImages(GB_ROOT_DIR+"next.gif",GB_ROOT_DIR+"prev.gif");
+});
+GB_show=function(_45,url,_47,_48,_49){
+var _4a={caption:_45,height:_47||500,width:_48||500,fullscreen:false,callback_fn:_49};
+var win=new GB_Window(_4a);
+return win.show(url);
+};
+GB_showCenter=function(_4c,url,_4e,_4f,_50){
+var _51={caption:_4c,center_win:true,height:_4e||500,width:_4f||500,fullscreen:false,callback_fn:_50};
+var win=new GB_Window(_51);
+return win.show(url);
+};
+GB_showFullScreen=function(_53,url,_55){
+var _56={caption:_53,fullscreen:true,callback_fn:_55};
+var win=new GB_Window(_56);
+return win.show(url);
+};
+GB_Window=GreyBox.extend({init:function(_58){
+this.parent({});
+this.img_header=this.root_dir+"header_bg.gif";
+this.img_close=this.root_dir+"w_close.gif";
+this.show_close_img=true;
+AJS.update(this,_58);
+this.addCallback(this.callback_fn);
+},initHook:function(){
+AJS.addClass(this.g_window,"GB_Window");
+this.header=AJS.TABLE({"class":"header"});
+this.header.style.backgroundImage="url("+this.img_header+")";
+var _59=AJS.TD({"class":"caption"},this.caption);
+var _5a=AJS.TD({"class":"close"});
+if(this.show_close_img){
+var _5b=AJS.IMG({"src":this.img_close});
+var _5c=AJS.SPAN("Close");
+var btn=AJS.DIV(_5b,_5c);
+AJS.AEV([_5b,_5c],"mouseover",function(){
+AJS.addClass(_5c,"on");
+});
+AJS.AEV([_5b,_5c],"mouseout",function(){
+AJS.removeClass(_5c,"on");
+});
+AJS.AEV([_5b,_5c],"mousedown",function(){
+AJS.addClass(_5c,"click");
+});
+AJS.AEV([_5b,_5c],"mouseup",function(){
+AJS.removeClass(_5c,"click");
+});
+AJS.AEV([_5b,_5c],"click",GB_hide);
+AJS.ACN(_5a,btn);
+}
+tbody_header=AJS.TBODY();
+AJS.ACN(tbody_header,AJS.TR(_59,_5a));
+AJS.ACN(this.header,tbody_header);
+AJS.ACN(this.top_cnt,this.header);
+if(this.fullscreen){
+AJS.AEV(window,"scroll",AJS.$b(this.setWindowPosition,this));
+}
+},setFrameSize:function(){
+if(this.fullscreen){
+var _5e=AJS.getWindowSize();
+overlay_h=_5e.h;
+this.width=Math.round(this.overlay.offsetWidth-(this.overlay.offsetWidth/100)*10);
+this.height=Math.round(overlay_h-(overlay_h/100)*10);
+}
+AJS.setWidth(this.header,this.width+6);
+AJS.setWidth(this.iframe,this.width);
+AJS.setHeight(this.iframe,this.height);
+},setWindowPosition:function(){
+var _5f=AJS.getWindowSize();
+AJS.setLeft(this.g_window,((_5f.w-this.width)/2)-13);
+if(!this.center_win){
+AJS.setTop(this.g_window,AJS.getScrollTop());
+}else{
+var fl=((_5f.h-this.height)/2)-20+AJS.getScrollTop();
+if(fl<0){
+fl=0;
+}
+AJS.setTop(this.g_window,fl);
+}
+}});
+AJS.preloadImages(GB_ROOT_DIR+"w_close.gif",GB_ROOT_DIR+"header_bg.gif");
+
+
+script_loaded=true;
Index: /afridex/plugins/Flutter/js/prototype.js
===================================================================
--- /afridex/plugins/Flutter/js/prototype.js (revision 21)
+++ /afridex/plugins/Flutter/js/prototype.js (revision 21)
@@ -0,0 +1,4221 @@
+/*  Prototype JavaScript framework, version 1.6.0.2
+ *  (c) 2005-2008 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.6.0.2',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      document.createElement('div').__proto__ &&
+      document.createElement('div').__proto__ !==
+        document.createElement('form').__proto__
+  },
+
+  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+  Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+  create: function() {
+    var parent = null, properties = $A(arguments);
+    if (Object.isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
+      this.initialize.apply(this, arguments);
+    }
+
+    Object.extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      var subclass = function() { };
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      parent.subclasses.push(klass);
+    }
+
+    for (var i = 0; i < properties.length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = Prototype.emptyFunction;
+
+    klass.prototype.constructor = klass;
+
+    return klass;
+  }
+};
+
+Class.Methods = {
+  addMethods: function(source) {
+    var ancestor   = this.superclass && this.superclass.prototype;
+    var properties = Object.keys(source);
+
+    if (!Object.keys({ toString: true }).length)
+      properties.push("toString", "valueOf");
+
+    for (var i = 0, length = properties.length; i < length; i++) {
+      var property = properties[i], value = source[property];
+      if (ancestor && Object.isFunction(value) &&
+          value.argumentNames().first() == "$super") {
+        var method = value, value = Object.extend((function(m) {
+          return function() { return ancestor[m].apply(this, arguments) };
+        })(property).wrap(method), {
+          valueOf:  function() { return method },
+          toString: function() { return method.toString() }
+        });
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+  for (var property in source)
+    destination[property] = source[property];
+  return destination;
+};
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (Object.isUndefined(object)) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : String(object);
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch (type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (Object.isElement(object)) return;
+
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (!Object.isUndefined(value))
+        results.push(property.toJSON() + ': ' + value);
+    }
+
+    return '{' + results.join(', ') + '}';
+  },
+
+  toQueryString: function(object) {
+    return $H(object).toQueryString();
+  },
+
+  toHTML: function(object) {
+    return object && object.toHTML ? object.toHTML() : String.interpret(object);
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({ }, object);
+  },
+
+  isElement: function(object) {
+    return object && object.nodeType == 1;
+  },
+
+  isArray: function(object) {
+    return object != null && typeof object == "object" &&
+      'splice' in object && 'join' in object;
+  },
+
+  isHash: function(object) {
+    return object instanceof Hash;
+  },
+
+  isFunction: function(object) {
+    return typeof object == "function";
+  },
+
+  isString: function(object) {
+    return typeof object == "string";
+  },
+
+  isNumber: function(object) {
+    return typeof object == "number";
+  },
+
+  isUndefined: function(object) {
+    return typeof object == "undefined";
+  }
+});
+
+Object.extend(Function.prototype, {
+  argumentNames: function() {
+    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+    return names.length == 1 && !names[0] ? [] : names;
+  },
+
+  bind: function() {
+    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function() {
+      return __method.apply(object, args.concat($A(arguments)));
+    }
+  },
+
+  bindAsEventListener: function() {
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function(event) {
+      return __method.apply(object, [event || window.event].concat(args));
+    }
+  },
+
+  curry: function() {
+    if (!arguments.length) return this;
+    var __method = this, args = $A(arguments);
+    return function() {
+      return __method.apply(this, args.concat($A(arguments)));
+    }
+  },
+
+  delay: function() {
+    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+    return window.setTimeout(function() {
+      return __method.apply(__method, args);
+    }, timeout);
+  },
+
+  wrap: function(wrapper) {
+    var __method = this;
+    return function() {
+      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+    }
+  },
+
+  methodize: function() {
+    if (this._methodized) return this._methodized;
+    var __method = this;
+    return this._methodized = function() {
+      return __method.apply(null, [this].concat($A(arguments)));
+    };
+  }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getUTCFullYear() + '-' +
+    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+    this.getUTCDate().toPaddedString(2) + 'T' +
+    this.getUTCHours().toPaddedString(2) + ':' +
+    this.getUTCMinutes().toPaddedString(2) + ':' +
+    this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) { }
+    }
+
+    return returnValue;
+  }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  execute: function() {
+    this.callback(this);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.execute();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+});
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = Object.isUndefined(count) ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return String(this);
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = Object.isUndefined(truncation) ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : String(this);
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = new Element('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return { };
+
+    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    return count < 1 ? '' : new Array(count + 1).join(this);
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  isJSON: function() {
+    var str = this;
+    if (str.blank()) return false;
+    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  },
+
+  interpolate: function(object, pattern) {
+    return new Template(this, pattern).evaluate(object);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (Object.isFunction(replacement)) return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    if (Object.isFunction(object.toTemplateReplacements))
+      object = object.toTemplateReplacements();
+
+    return this.template.gsub(this.pattern, function(match) {
+      if (object == null) return '';
+
+      var before = match[1] || '';
+      if (before == '\\') return match[2];
+
+      var ctx = object, expr = match[3];
+      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+      match = pattern.exec(expr);
+      if (match == null) return before;
+
+      while (match != null) {
+        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+        ctx = ctx[comp];
+        if (null == ctx || '' == match[3]) break;
+        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+        match = pattern.exec(expr);
+      }
+
+      return before + String.interpret(ctx);
+    });
+  }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+  each: function(iterator, context) {
+    var index = 0;
+    iterator = iterator.bind(context);
+    try {
+      this._each(function(value) {
+        iterator(value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.collect(iterator, context);
+  },
+
+  all: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!iterator(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!iterator(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(filter, iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var results = [];
+
+    if (Object.isString(filter))
+      filter = new RegExp(filter);
+
+    this.each(function(value, index) {
+      if (filter.match(value))
+        results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  include: function(object) {
+    if (Object.isFunction(this.indexOf))
+      if (this.indexOf(object) != -1) return true;
+
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator, context) {
+    iterator = iterator.bind(context);
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator(value, index);
+      if (result == null || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator(value, index);
+      if (result == null || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      (iterator(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator, context) {
+    iterator = iterator.bind(context);
+    return this.map(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (Object.isFunction(args.last()))
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+};
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  filter:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray,
+  every:   Enumerable.all,
+  some:    Enumerable.any
+});
+function $A(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) return iterable.toArray();
+  var length = iterable.length || 0, results = new Array(length);
+  while (length--) results[length] = iterable[length];
+  return results;
+}
+
+if (Prototype.Browser.WebKit) {
+  $A = function(iterable) {
+    if (!iterable) return [];
+    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+        iterable.toArray) return iterable.toArray();
+    var length = iterable.length || 0, results = new Array(length);
+    while (length--) results[length] = iterable[length];
+    return results;
+  };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(Object.isArray(value) ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  intersect: function(array) {
+    return this.uniq().findAll(function(item) {
+      return array.detect(function(value) { return item === value });
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (!Object.isUndefined(value)) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+  Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+  i || (i = 0);
+  var length = this.length;
+  if (i < 0) i = length + i;
+  for (; i < length; i++)
+    if (this[i] === item) return i;
+  return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+  var n = this.slice(0, i).reverse().indexOf(item);
+  return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  if (!Object.isString(string)) return [];
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (Object.isArray(arguments[i])) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  };
+}
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+$w('abs round ceil floor').each(function(method){
+  Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+  return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+  function toQueryPair(key, value) {
+    if (Object.isUndefined(value)) return key;
+    return key + '=' + encodeURIComponent(String.interpret(value));
+  }
+
+  return {
+    initialize: function(object) {
+      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+    },
+
+    _each: function(iterator) {
+      for (var key in this._object) {
+        var value = this._object[key], pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    },
+
+    set: function(key, value) {
+      return this._object[key] = value;
+    },
+
+    get: function(key) {
+      return this._object[key];
+    },
+
+    unset: function(key) {
+      var value = this._object[key];
+      delete this._object[key];
+      return value;
+    },
+
+    toObject: function() {
+      return Object.clone(this._object);
+    },
+
+    keys: function() {
+      return this.pluck('key');
+    },
+
+    values: function() {
+      return this.pluck('value');
+    },
+
+    index: function(value) {
+      var match = this.detect(function(pair) {
+        return pair.value === value;
+      });
+      return match && match.key;
+    },
+
+    merge: function(object) {
+      return this.clone().update(object);
+    },
+
+    update: function(object) {
+      return new Hash(object).inject(this, function(result, pair) {
+        result.set(pair.key, pair.value);
+        return result;
+      });
+    },
+
+    toQueryString: function() {
+      return this.map(function(pair) {
+        var key = encodeURIComponent(pair.key), values = pair.value;
+
+        if (values && typeof values == 'object') {
+          if (Object.isArray(values))
+            return values.map(toQueryPair.curry(key)).join('&');
+        }
+        return toQueryPair(key, values);
+      }).join('&');
+    },
+
+    inspect: function() {
+      return '#<Hash:{' + this.map(function(pair) {
+        return pair.map(Object.inspect).join(': ');
+      }).join(', ') + '}>';
+    },
+
+    toJSON: function() {
+      return Object.toJSON(this.toObject());
+    },
+
+    clone: function() {
+      return new Hash(this);
+    }
+  }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+};
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (Object.isFunction(responder[callback])) {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) { }
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate:   function() { Ajax.activeRequestCount++ },
+  onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+  initialize: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   '',
+      evalJSON:     true,
+      evalJS:       true
+    };
+    Object.extend(this.options, options || { });
+
+    this.options.method = this.options.method.toLowerCase();
+
+    if (Object.isString(this.options.parameters))
+      this.options.parameters = this.options.parameters.toQueryParams();
+    else if (Object.isHash(this.options.parameters))
+      this.options.parameters = this.options.parameters.toObject();
+  }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+  _complete: false,
+
+  initialize: function($super, url, options) {
+    $super(options);
+    this.transport = Ajax.getTransport();
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Object.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      var response = new Ajax.Response(this);
+      if (this.options.onCreate) this.options.onCreate(response);
+      Ajax.Responders.dispatch('onCreate', this, response);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (Object.isFunction(extras.push))
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    var status = this.getStatus();
+    return !status || (status >= 200 && status < 300);
+  },
+
+  getStatus: function() {
+    try {
+      return this.transport.status || 0;
+    } catch (e) { return 0 }
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + response.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(response, response.headerJSON);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = response.getHeader('Content-type');
+      if (this.options.evalJS == 'force'
+          || (this.options.evalJS && this.isSameOrigin() && contentType
+          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  isSameOrigin: function() {
+    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+      protocol: location.protocol,
+      domain: document.domain,
+      port: location.port ? ':' + location.port : ''
+    }));
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name) || null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+  initialize: function(request){
+    this.request = request;
+    var transport  = this.transport  = request.transport,
+        readyState = this.readyState = transport.readyState;
+
+    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+      this.status       = this.getStatus();
+      this.statusText   = this.getStatusText();
+      this.responseText = String.interpret(transport.responseText);
+      this.headerJSON   = this._getHeaderJSON();
+    }
+
+    if(readyState == 4) {
+      var xml = transport.responseXML;
+      this.responseXML  = Object.isUndefined(xml) ? null : xml;
+      this.responseJSON = this._getResponseJSON();
+    }
+  },
+
+  status:      0,
+  statusText: '',
+
+  getStatus: Ajax.Request.prototype.getStatus,
+
+  getStatusText: function() {
+    try {
+      return this.transport.statusText || '';
+    } catch (e) { return '' }
+  },
+
+  getHeader: Ajax.Request.prototype.getHeader,
+
+  getAllHeaders: function() {
+    try {
+      return this.getAllResponseHeaders();
+    } catch (e) { return null }
+  },
+
+  getResponseHeader: function(name) {
+    return this.transport.getResponseHeader(name);
+  },
+
+  getAllResponseHeaders: function() {
+    return this.transport.getAllResponseHeaders();
+  },
+
+  _getHeaderJSON: function() {
+    var json = this.getHeader('X-JSON');
+    if (!json) return null;
+    json = decodeURIComponent(escape(json));
+    try {
+      return json.evalJSON(this.request.options.sanitizeJSON ||
+        !this.request.isSameOrigin());
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  },
+
+  _getResponseJSON: function() {
+    var options = this.request.options;
+    if (!options.evalJSON || (options.evalJSON != 'force' &&
+      !(this.getHeader('Content-type') || '').include('application/json')) ||
+        this.responseText.blank())
+          return null;
+    try {
+      return this.responseText.evalJSON(options.sanitizeJSON ||
+        !this.request.isSameOrigin());
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+  initialize: function($super, container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    };
+
+    options = Object.clone(options);
+    var onComplete = options.onComplete;
+    options.onComplete = (function(response, json) {
+      this.updateContent(response.responseText);
+      if (Object.isFunction(onComplete)) onComplete(response, json);
+    }).bind(this);
+
+    $super(url, options);
+  },
+
+  updateContent: function(responseText) {
+    var receiver = this.container[this.success() ? 'success' : 'failure'],
+        options = this.options;
+
+    if (!options.evalScripts) responseText = responseText.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (options.insertion) {
+        if (Object.isString(options.insertion)) {
+          var insertion = { }; insertion[options.insertion] = responseText;
+          receiver.insert(insertion);
+        }
+        else options.insertion(receiver, responseText);
+      }
+      else receiver.update(responseText);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+  initialize: function($super, container, url, options) {
+    $super(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = { };
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(response) {
+    if (this.options.decay) {
+      this.decay = (response.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = response.responseText;
+    }
+    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (Object.isString(element))
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(Element.extend(query.snapshotItem(i)));
+    return results;
+  };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+  // DOM level 2 ECMAScript Language Binding
+  Object.extend(Node, {
+    ELEMENT_NODE: 1,
+    ATTRIBUTE_NODE: 2,
+    TEXT_NODE: 3,
+    CDATA_SECTION_NODE: 4,
+    ENTITY_REFERENCE_NODE: 5,
+    ENTITY_NODE: 6,
+    PROCESSING_INSTRUCTION_NODE: 7,
+    COMMENT_NODE: 8,
+    DOCUMENT_NODE: 9,
+    DOCUMENT_TYPE_NODE: 10,
+    DOCUMENT_FRAGMENT_NODE: 11,
+    NOTATION_NODE: 12
+  });
+}
+
+(function() {
+  var element = this.Element;
+  this.Element = function(tagName, attributes) {
+    attributes = attributes || { };
+    tagName = tagName.toLowerCase();
+    var cache = Element.cache;
+    if (Prototype.Browser.IE && attributes.name) {
+      tagName = '<' + tagName + ' name="' + attributes.name + '">';
+      delete attributes.name;
+      return Element.writeAttribute(document.createElement(tagName), attributes);
+    }
+    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+  };
+  Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+    content = Object.toHTML(content);
+    element.innerHTML = content.stripScripts();
+    content.evalScripts.bind(content).defer();
+    return element;
+  },
+
+  replace: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    else if (!Object.isElement(content)) {
+      content = Object.toHTML(content);
+      var range = element.ownerDocument.createRange();
+      range.selectNode(element);
+      content.evalScripts.bind(content).defer();
+      content = range.createContextualFragment(content.stripScripts());
+    }
+    element.parentNode.replaceChild(content, element);
+    return element;
+  },
+
+  insert: function(element, insertions) {
+    element = $(element);
+
+    if (Object.isString(insertions) || Object.isNumber(insertions) ||
+        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+          insertions = {bottom:insertions};
+
+    var content, insert, tagName, childNodes;
+
+    for (var position in insertions) {
+      content  = insertions[position];
+      position = position.toLowerCase();
+      insert = Element._insertionTranslations[position];
+
+      if (content && content.toElement) content = content.toElement();
+      if (Object.isElement(content)) {
+        insert(element, content);
+        continue;
+      }
+
+      content = Object.toHTML(content);
+
+      tagName = ((position == 'before' || position == 'after')
+        ? element.parentNode : element).tagName.toUpperCase();
+
+      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+      if (position == 'top' || position == 'after') childNodes.reverse();
+      childNodes.each(insert.curry(element));
+
+      content.evalScripts.bind(content).defer();
+    }
+
+    return element;
+  },
+
+  wrap: function(element, wrapper, attributes) {
+    element = $(element);
+    if (Object.isElement(wrapper))
+      $(wrapper).writeAttribute(attributes || { });
+    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+    else wrapper = new Element('div', wrapper);
+    if (element.parentNode)
+      element.parentNode.replaceChild(wrapper, element);
+    wrapper.appendChild(element);
+    return wrapper;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $(element).select("*");
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (Object.isString(selector))
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return Object.isNumber(expression) ? ancestors[expression] :
+      Selector.findElement(ancestors, expression, index);
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    return Object.isNumber(expression) ? element.descendants()[expression] :
+      element.select(expression)[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return Object.isNumber(expression) ? previousSiblings[expression] :
+      Selector.findElement(previousSiblings, expression, index);
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return Object.isNumber(expression) ? nextSiblings[expression] :
+      Selector.findElement(nextSiblings, expression, index);
+  },
+
+  select: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  adjacent: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element.parentNode, args).without(element);
+  },
+
+  identify: function(element) {
+    element = $(element);
+    var id = element.readAttribute('id'), self = arguments.callee;
+    if (id) return id;
+    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+    element.writeAttribute('id', id);
+    return id;
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      var t = Element._attributeTranslations.read;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name]) name = t.names[name];
+      if (name.include(':')) {
+        return (!element.attributes || !element.attributes[name]) ? null :
+         element.attributes[name].value;
+      }
+    }
+    return element.getAttribute(name);
+  },
+
+  writeAttribute: function(element, name, value) {
+    element = $(element);
+    var attributes = { }, t = Element._attributeTranslations.write;
+
+    if (typeof name == 'object') attributes = name;
+    else attributes[name] = Object.isUndefined(value) ? true : value;
+
+    for (var attr in attributes) {
+      name = t.names[attr] || attr;
+      value = attributes[attr];
+      if (t.values[attr]) name = t.values[attr](element, value);
+      if (value === false || value === null)
+        element.removeAttribute(name);
+      else if (value === true)
+        element.setAttribute(name, name);
+      else element.setAttribute(name, value);
+    }
+    return element;
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    return (elementClassName.length > 0 && (elementClassName == className ||
+      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    if (!element.hasClassName(className))
+      element.className += (element.className ? ' ' : '') + className;
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    element.className = element.className.replace(
+      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return element[element.hasClassName(className) ?
+      'removeClassName' : 'addClassName'](className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    var originalAncestor = ancestor;
+
+    if (element.compareDocumentPosition)
+      return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+    if (element.sourceIndex && !Prototype.Browser.Opera) {
+      var e = element.sourceIndex, a = ancestor.sourceIndex,
+       nextAncestor = ancestor.nextSibling;
+      if (!nextAncestor) {
+        do { ancestor = ancestor.parentNode; }
+        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+      }
+      if (nextAncestor && nextAncestor.sourceIndex)
+       return (e > a && e < nextAncestor.sourceIndex);
+    }
+
+    while (element = element.parentNode)
+      if (element == originalAncestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = element.cumulativeOffset();
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles) {
+    element = $(element);
+    var elementStyle = element.style, match;
+    if (Object.isString(styles)) {
+      element.style.cssText += ';' + styles;
+      return styles.include('opacity') ?
+        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+    }
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property]);
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+            property] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+    if (element._overflow !== 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if (element.tagName == 'BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p !== 'static') break;
+      }
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'absolute') return;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    var offsets = element.positionedOffset();
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+    return element;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'relative') return;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+    return element;
+  },
+
+  cumulativeScrollOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  getOffsetParent: function(element) {
+    if (element.offsetParent) return $(element.offsetParent);
+    if (element == document.body) return $(element);
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return $(element);
+
+    return $(document.body);
+  },
+
+  viewportOffset: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body &&
+        Element.getStyle(element, 'position') == 'absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  clonePosition: function(element, source) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || { });
+
+    // find page position of source
+    source = $(source);
+    var p = source.viewportOffset();
+
+    // find coordinate system to use
+    element = $(element);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(element, 'position') == 'absolute') {
+      parent = element.getOffsetParent();
+      delta = parent.viewportOffset();
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
+    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+    return element;
+  }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+  getElementsBySelector: Element.Methods.select,
+  childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+  write: {
+    names: {
+      className: 'class',
+      htmlFor:   'for'
+    },
+    values: { }
+  }
+};
+
+if (Prototype.Browser.Opera) {
+  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+    function(proceed, element, style) {
+      switch (style) {
+        case 'left': case 'top': case 'right': case 'bottom':
+          if (proceed(element, 'position') === 'static') return null;
+        case 'height': case 'width':
+          // returns '0px' for hidden elements; we want it to return null
+          if (!Element.visible(element)) return null;
+
+          // returns the border-box dimensions rather than the content-box
+          // dimensions, so we subtract padding and borders from the value
+          var dim = parseInt(proceed(element, style), 10);
+
+          if (dim !== element['offset' + style.capitalize()])
+            return dim + 'px';
+
+          var properties;
+          if (style === 'height') {
+            properties = ['border-top-width', 'padding-top',
+             'padding-bottom', 'border-bottom-width'];
+          }
+          else {
+            properties = ['border-left-width', 'padding-left',
+             'padding-right', 'border-right-width'];
+          }
+          return properties.inject(dim, function(memo, property) {
+            var val = proceed(element, property);
+            return val === null ? memo : memo - parseInt(val, 10);
+          }) + 'px';
+        default: return proceed(element, style);
+      }
+    }
+  );
+
+  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+    function(proceed, element, attribute) {
+      if (attribute === 'title') return element.title;
+      return proceed(element, attribute);
+    }
+  );
+}
+
+else if (Prototype.Browser.IE) {
+  // IE doesn't report offsets correctly for static elements, so we change them
+  // to "relative" to get the values, then change them back.
+  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+    function(proceed, element) {
+      element = $(element);
+      var position = element.getStyle('position');
+      if (position !== 'static') return proceed(element);
+      element.setStyle({ position: 'relative' });
+      var value = proceed(element);
+      element.setStyle({ position: position });
+      return value;
+    }
+  );
+
+  $w('positionedOffset viewportOffset').each(function(method) {
+    Element.Methods[method] = Element.Methods[method].wrap(
+      function(proceed, element) {
+        element = $(element);
+        var position = element.getStyle('position');
+        if (position !== 'static') return proceed(element);
+        // Trigger hasLayout on the offset parent so that IE6 reports
+        // accurate offsetTop and offsetLeft values for position: fixed.
+        var offsetParent = element.getOffsetParent();
+        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+          offsetParent.setStyle({ zoom: 1 });
+        element.setStyle({ position: 'relative' });
+        var value = proceed(element);
+        element.setStyle({ position: position });
+        return value;
+      }
+    );
+  });
+
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset' + style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    function stripAlpha(filter){
+      return filter.replace(/alpha\([^\)]*\)/gi,'');
+    }
+    element = $(element);
+    var currentStyle = element.currentStyle;
+    if ((currentStyle && !currentStyle.hasLayout) ||
+      (!currentStyle && element.style.zoom == 'normal'))
+        element.style.zoom = 1;
+
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      (filter = stripAlpha(filter)) ?
+        style.filter = filter : style.removeAttribute('filter');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = stripAlpha(filter) +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  Element._attributeTranslations = {
+    read: {
+      names: {
+        'class': 'className',
+        'for':   'htmlFor'
+      },
+      values: {
+        _getAttr: function(element, attribute) {
+          return element.getAttribute(attribute, 2);
+        },
+        _getAttrNode: function(element, attribute) {
+          var node = element.getAttributeNode(attribute);
+          return node ? node.value : "";
+        },
+        _getEv: function(element, attribute) {
+          attribute = element.getAttribute(attribute);
+          return attribute ? attribute.toString().slice(23, -2) : null;
+        },
+        _flag: function(element, attribute) {
+          return $(element).hasAttribute(attribute) ? attribute : null;
+        },
+        style: function(element) {
+          return element.style.cssText.toLowerCase();
+        },
+        title: function(element) {
+          return element.title;
+        }
+      }
+    }
+  };
+
+  Element._attributeTranslations.write = {
+    names: Object.extend({
+      cellpadding: 'cellPadding',
+      cellspacing: 'cellSpacing'
+    }, Element._attributeTranslations.read.names),
+    values: {
+      checked: function(element, value) {
+        element.checked = !!value;
+      },
+
+      style: function(element, value) {
+        element.style.cssText = value ? value : '';
+      }
+    }
+  };
+
+  Element._attributeTranslations.has = {};
+
+  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+      'encType maxLength readOnly longDesc').each(function(attr) {
+    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+  });
+
+  (function(v) {
+    Object.extend(v, {
+      href:        v._getAttr,
+      src:         v._getAttr,
+      type:        v._getAttr,
+      action:      v._getAttrNode,
+      disabled:    v._flag,
+      checked:     v._flag,
+      readonly:    v._flag,
+      multiple:    v._flag,
+      onload:      v._getEv,
+      onunload:    v._getEv,
+      onclick:     v._getEv,
+      ondblclick:  v._getEv,
+      onmousedown: v._getEv,
+      onmouseup:   v._getEv,
+      onmouseover: v._getEv,
+      onmousemove: v._getEv,
+      onmouseout:  v._getEv,
+      onfocus:     v._getEv,
+      onblur:      v._getEv,
+      onkeypress:  v._getEv,
+      onkeydown:   v._getEv,
+      onkeyup:     v._getEv,
+      onsubmit:    v._getEv,
+      onreset:     v._getEv,
+      onselect:    v._getEv,
+      onchange:    v._getEv
+    });
+  })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+else if (Prototype.Browser.WebKit) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+
+    if (value == 1)
+      if(element.tagName == 'IMG' && element.width) {
+        element.width++; element.width--;
+      } else try {
+        var n = document.createTextNode(' ');
+        element.appendChild(n);
+        element.removeChild(n);
+      } catch (e) { }
+
+    return element;
+  };
+
+  // Safari returns margins on body which is incorrect if the child is absolutely
+  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
+  // KHTML/WebKit only.
+  Element.Methods.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return Element._returnOffset(valueL, valueT);
+  };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+  Element.Methods.update = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+
+    content = Object.toHTML(content);
+    var tagName = element.tagName.toUpperCase();
+
+    if (tagName in Element._insertionTranslations.tags) {
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+        .each(function(node) { element.appendChild(node) });
+    }
+    else element.innerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+  Element.Methods.replace = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) {
+      element.parentNode.replaceChild(content, element);
+      return element;
+    }
+
+    content = Object.toHTML(content);
+    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+    if (Element._insertionTranslations.tags[tagName]) {
+      var nextSibling = element.next();
+      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+      parent.removeChild(element);
+      if (nextSibling)
+        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+      else
+        fragments.each(function(node) { parent.appendChild(node) });
+    }
+    else element.outerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+Element._returnOffset = function(l, t) {
+  var result = [l, t];
+  result.left = l;
+  result.top = t;
+  return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+  if (t) {
+    div.innerHTML = t[0] + html + t[1];
+    t[2].times(function() { div = div.firstChild });
+  } else div.innerHTML = html;
+  return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+  before: function(element, node) {
+    element.parentNode.insertBefore(node, element);
+  },
+  top: function(element, node) {
+    element.insertBefore(node, element.firstChild);
+  },
+  bottom: function(element, node) {
+    element.appendChild(node);
+  },
+  after: function(element, node) {
+    element.parentNode.insertBefore(node, element.nextSibling);
+  },
+  tags: {
+    TABLE:  ['<table>',                '</table>',                   1],
+    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
+    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
+    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+    SELECT: ['<select>',               '</select>',                  1]
+  }
+};
+
+(function() {
+  Object.extend(this.tags, {
+    THEAD: this.tags.TBODY,
+    TFOOT: this.tags.TBODY,
+    TH:    this.tags.TD
+  });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    attribute = Element._attributeTranslations.has[attribute] || attribute;
+    var node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+    document.createElement('div').__proto__) {
+  window.HTMLElement = { };
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+  if (Prototype.BrowserFeatures.SpecificElementExtensions)
+    return Prototype.K;
+
+  var Methods = { }, ByTag = Element.Methods.ByTag;
+
+  var extend = Object.extend(function(element) {
+    if (!element || element._extendedByPrototype ||
+        element.nodeType != 1 || element == window) return element;
+
+    var methods = Object.clone(Methods),
+      tagName = element.tagName, property, value;
+
+    // extend methods for specific tags
+    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+    for (property in methods) {
+      value = methods[property];
+      if (Object.isFunction(value) && !(property in element))
+        element[property] = value.methodize();
+    }
+
+    element._extendedByPrototype = Prototype.emptyFunction;
+    return element;
+
+  }, {
+    refresh: function() {
+      // extend methods for all tags (Safari doesn't need this)
+      if (!Prototype.BrowserFeatures.ElementExtensions) {
+        Object.extend(Methods, Element.Methods);
+        Object.extend(Methods, Element.Methods.Simulated);
+      }
+    }
+  });
+
+  extend.refresh();
+  return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || { });
+  else {
+    if (Object.isArray(tagName)) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = { };
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!Object.isFunction(value)) continue;
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = value.methodize();
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = { };
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (Object.isUndefined(klass)) continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+
+  if (Element.extend.refresh) Element.extend.refresh();
+  Element.cache = { };
+};
+
+document.viewport = {
+  getDimensions: function() {
+    var dimensions = { };
+    var B = Prototype.Browser;
+    $w('width height').each(function(d) {
+      var D = d.capitalize();
+      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+    });
+    return dimensions;
+  },
+
+  getWidth: function() {
+    return this.getDimensions().width;
+  },
+
+  getHeight: function() {
+    return this.getDimensions().height;
+  },
+
+  getScrollOffsets: function() {
+    return Element._returnOffset(
+      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+  }
+};
+/* Portions of the Selector class are derived from Jack Slocumâs DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
+  },
+
+  shouldUseXPath: function() {
+    if (!Prototype.BrowserFeatures.XPath) return false;
+
+    var e = this.expression;
+
+    // Safari 3 chokes on :*-of-type and :empty
+    if (Prototype.Browser.WebKit &&
+     (e.include("-of-type") || e.include(":empty")))
+      return false;
+
+    // XPath can't do namespaced attributes, nor can it read
+    // the "checked" property from DOM nodes
+    if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+      return false;
+
+    return true;
+  },
+
+  compileMatcher: function() {
+    if (this.shouldUseXPath())
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e];
+      return;
+    }
+
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+    	      new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le, m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
+  },
+
+  match: function(element) {
+    this.tokens = [];
+
+    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+    var le, p, m;
+
+    while (e && le !== e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          // use the Selector.assertions methods unless the selector
+          // is too complex.
+          if (as[i]) {
+            this.tokens.push([i, Object.clone(m)]);
+            e = e.replace(m[0], '');
+          } else {
+            // reluctantly do a document-wide search
+            // and look for a match in the array
+            return this.findElements(document).include(element);
+          }
+        }
+      }
+    }
+
+    var match = true, name, matches;
+    for (var i = 0, token; token = this.tokens[i]; i++) {
+      name = token[0], matches = token[1];
+      if (!Selector.assertions[name](element, matches)) {
+        match = false; break;
+      }
+    }
+
+    return match;
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+});
+
+Object.extend(Selector, {
+  _cache: { },
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: function(m) {
+      m[1] = m[1].toLowerCase();
+      return new Template("[@#{1}]").evaluate(m);
+    },
+    attr: function(m) {
+      m[1] = m[1].toLowerCase();
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (Object.isFunction(h)) return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
+    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+    },
+    pseudo: function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+    attrPresence: /^\[([\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  // for Selector.match and Element#match
+  assertions: {
+    tagName: function(element, matches) {
+      return matches[1].toUpperCase() == element.tagName.toUpperCase();
+    },
+
+    className: function(element, matches) {
+      return Element.hasClassName(element, matches[1]);
+    },
+
+    id: function(element, matches) {
+      return element.id === matches[1];
+    },
+
+    attrPresence: function(element, matches) {
+      return Element.hasAttribute(element, matches[1]);
+    },
+
+    attr: function(element, matches) {
+      var nodeValue = Element.readAttribute(element, matches[1]);
+      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+    }
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      var _true = Prototype.emptyFunction;
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._countedByPrototype = _true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._countedByPrototype = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._countedByPrototype = Prototype.emptyFunction;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          var node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._countedByPrototype) {
+          n._countedByPrototype = Prototype.emptyFunction;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+	      if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      var uTagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() === uTagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!targetNode) return [];
+      if (!nodes && root == document) return [targetNode];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr, combinator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator, combinator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._countedByPrototype) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._countedByPrototype) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled) results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv.startsWith(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+  },
+
+  split: function(expression) {
+    var expressions = [];
+    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    return expressions;
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = $$(expression), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._countedByPrototype) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (Object.isNumber(expression)) {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    expressions = Selector.split(expressions.join(','));
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+if (Prototype.Browser.IE) {
+  Object.extend(Selector.handlers, {
+    // IE returns comment nodes on getElementsByTagName("*").
+    // Filter them out.
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        if (node.tagName !== "!") a.push(node);
+      return a;
+    },
+
+    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node.removeAttribute('_countedByPrototype');
+      return nodes;
+    }
+  });
+}
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, options) {
+    if (typeof options != 'object') options = { hash: !!options };
+    else if (Object.isUndefined(options.hash)) options.hash = true;
+    var key, value, submitted = false, submit = options.submit;
+
+    var data = elements.inject({ }, function(result, element) {
+      if (!element.disabled && element.name) {
+        key = element.name; value = $(element).getValue();
+        if (value != null && (element.type != 'submit' || (!submitted &&
+            submit !== false && (!submit || key == submit) && (submitted = true)))) {
+          if (key in result) {
+            // a key is already present; construct an array of values
+            if (!Object.isArray(result[key])) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return options.hash ? data : Object.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, options) {
+    return Form.serializeElements(Form.getElements(form), options);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    var elements = $(form).getElements().findAll(function(element) {
+      return 'hidden' != element.type && !element.disabled;
+    });
+    var firstByIndex = elements.findAll(function(element) {
+      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+    }).sortBy(function(element) { return element.tabIndex }).first();
+
+    return firstByIndex ? firstByIndex : elements.find(function(element) {
+      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || { });
+
+    var params = options.parameters, action = form.readAttribute('action') || '';
+    if (action.blank()) action = window.location.href;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (Object.isString(params)) params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(action, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+};
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = { };
+        pair[element.name] = value;
+        return Object.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  setValue: function(element, value) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    Form.Element.Serializers[method](element, value);
+    return element;
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+          !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) { }
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element, value) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element, value);
+      default:
+        return Form.Element.Serializers.textarea(element, value);
+    }
+  },
+
+  inputSelector: function(element, value) {
+    if (Object.isUndefined(value)) return element.checked ? element.value : null;
+    else element.checked = !!value;
+  },
+
+  textarea: function(element, value) {
+    if (Object.isUndefined(value)) return element.value;
+    else element.value = value;
+  },
+
+  select: function(element, index) {
+    if (Object.isUndefined(index))
+      return this[element.type == 'select-one' ?
+        'selectOne' : 'selectMany'](element);
+    else {
+      var opt, value, single = !Object.isArray(index);
+      for (var i = 0, length = element.length; i < length; i++) {
+        opt = element.options[i];
+        value = this.optionValue(opt);
+        if (single) {
+          if (value == index) {
+            opt.selected = true;
+            return;
+          }
+        }
+        else opt.selected = index.include(value);
+      }
+    }
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+  initialize: function($super, element, frequency, callback) {
+    $super(callback, frequency);
+    this.element   = $(element);
+    this.lastValue = this.getValue();
+  },
+
+  execute: function() {
+    var value = this.getValue();
+    if (Object.isString(this.lastValue) && Object.isString(value) ?
+        this.lastValue != value : String(this.lastValue) != String(value)) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback, this);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+  KEY_INSERT:   45,
+
+  cache: { },
+
+  relatedTarget: function(event) {
+    var element;
+    switch(event.type) {
+      case 'mouseover': element = event.fromElement; break;
+      case 'mouseout':  element = event.toElement;   break;
+      default: return null;
+    }
+    return Element.extend(element);
+  }
+});
+
+Event.Methods = (function() {
+  var isButton;
+
+  if (Prototype.Browser.IE) {
+    var buttonMap = { 0: 1, 1: 4, 2: 2 };
+    isButton = function(event, code) {
+      return event.button == buttonMap[code];
+    };
+
+  } else if (Prototype.Browser.WebKit) {
+    isButton = function(event, code) {
+      switch (code) {
+        case 0: return event.which == 1 && !event.metaKey;
+        case 1: return event.which == 1 && event.metaKey;
+        default: return false;
+      }
+    };
+
+  } else {
+    isButton = function(event, code) {
+      return event.which ? (event.which === code + 1) : (event.button === code);
+    };
+  }
+
+  return {
+    isLeftClick:   function(event) { return isButton(event, 0) },
+    isMiddleClick: function(event) { return isButton(event, 1) },
+    isRightClick:  function(event) { return isButton(event, 2) },
+
+    element: function(event) {
+      var node = Event.extend(event).target;
+      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+    },
+
+    findElement: function(event, expression) {
+      var element = Event.element(event);
+      if (!expression) return element;
+      var elements = [element].concat(element.ancestors());
+      return Selector.findElement(elements, expression, 0);
+    },
+
+    pointer: function(event) {
+      return {
+        x: event.pageX || (event.clientX +
+          (document.documentElement.scrollLeft || document.body.scrollLeft)),
+        y: event.pageY || (event.clientY +
+          (document.documentElement.scrollTop || document.body.scrollTop))
+      };
+    },
+
+    pointerX: function(event) { return Event.pointer(event).x },
+    pointerY: function(event) { return Event.pointer(event).y },
+
+    stop: function(event) {
+      Event.extend(event);
+      event.preventDefault();
+      event.stopPropagation();
+      event.stopped = true;
+    }
+  };
+})();
+
+Event.extend = (function() {
+  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+    m[name] = Event.Methods[name].methodize();
+    return m;
+  });
+
+  if (Prototype.Browser.IE) {
+    Object.extend(methods, {
+      stopPropagation: function() { this.cancelBubble = true },
+      preventDefault:  function() { this.returnValue = false },
+      inspect: function() { return "[object Event]" }
+    });
+
+    return function(event) {
+      if (!event) return false;
+      if (event._extendedByPrototype) return event;
+
+      event._extendedByPrototype = Prototype.emptyFunction;
+      var pointer = Event.pointer(event);
+      Object.extend(event, {
+        target: event.srcElement,
+        relatedTarget: Event.relatedTarget(event),
+        pageX:  pointer.x,
+        pageY:  pointer.y
+      });
+      return Object.extend(event, methods);
+    };
+
+  } else {
+    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+    Object.extend(Event.prototype, methods);
+    return Prototype.K;
+  }
+})();
+
+Object.extend(Event, (function() {
+  var cache = Event.cache;
+
+  function getEventID(element) {
+    if (element._prototypeEventID) return element._prototypeEventID[0];
+    arguments.callee.id = arguments.callee.id || 1;
+    return element._prototypeEventID = [++arguments.callee.id];
+  }
+
+  function getDOMEventName(eventName) {
+    if (eventName && eventName.include(':')) return "dataavailable";
+    return eventName;
+  }
+
+  function getCacheForID(id) {
+    return cache[id] = cache[id] || { };
+  }
+
+  function getWrappersForEventName(id, eventName) {
+    var c = getCacheForID(id);
+    return c[eventName] = c[eventName] || [];
+  }
+
+  function createWrapper(element, eventName, handler) {
+    var id = getEventID(element);
+    var c = getWrappersForEventName(id, eventName);
+    if (c.pluck("handler").include(handler)) return false;
+
+    var wrapper = function(event) {
+      if (!Event || !Event.extend ||
+        (event.eventName && event.eventName != eventName))
+          return false;
+
+      Event.extend(event);
+      handler.call(element, event);
+    };
+
+    wrapper.handler = handler;
+    c.push(wrapper);
+    return wrapper;
+  }
+
+  function findWrapper(id, eventName, handler) {
+    var c = getWrappersForEventName(id, eventName);
+    return c.find(function(wrapper) { return wrapper.handler == handler });
+  }
+
+  function destroyWrapper(id, eventName, handler) {
+    var c = getCacheForID(id);
+    if (!c[eventName]) return false;
+    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+  }
+
+  function destroyCache() {
+    for (var id in cache)
+      for (var eventName in cache[id])
+        cache[id][eventName] = null;
+  }
+
+  if (window.attachEvent) {
+    window.attachEvent("onunload", destroyCache);
+  }
+
+  return {
+    observe: function(element, eventName, handler) {
+      element = $(element);
+      var name = getDOMEventName(eventName);
+
+      var wrapper = createWrapper(element, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.addEventListener) {
+        element.addEventListener(name, wrapper, false);
+      } else {
+        element.attachEvent("on" + name, wrapper);
+      }
+
+      return element;
+    },
+
+    stopObserving: function(element, eventName, handler) {
+      element = $(element);
+      var id = getEventID(element), name = getDOMEventName(eventName);
+
+      if (!handler && eventName) {
+        getWrappersForEventName(id, eventName).each(function(wrapper) {
+          element.stopObserving(eventName, wrapper.handler);
+        });
+        return element;
+
+      } else if (!eventName) {
+        Object.keys(getCacheForID(id)).each(function(eventName) {
+          element.stopObserving(eventName);
+        });
+        return element;
+      }
+
+      var wrapper = findWrapper(id, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.removeEventListener) {
+        element.removeEventListener(name, wrapper, false);
+      } else {
+        element.detachEvent("on" + name, wrapper);
+      }
+
+      destroyWrapper(id, eventName, handler);
+
+      return element;
+    },
+
+    fire: function(element, eventName, memo) {
+      element = $(element);
+      if (element == document && document.createEvent && !element.dispatchEvent)
+        element = document.documentElement;
+
+      var event;
+      if (document.createEvent) {
+        event = document.createEvent("HTMLEvents");
+        event.initEvent("dataavailable", true, true);
+      } else {
+        event = document.createEventObject();
+        event.eventType = "ondataavailable";
+      }
+
+      event.eventName = eventName;
+      event.memo = memo || { };
+
+      if (document.createEvent) {
+        element.dispatchEvent(event);
+      } else {
+        element.fireEvent(event.eventType, event);
+      }
+
+      return Event.extend(event);
+    }
+  };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+  fire:          Event.fire,
+  observe:       Event.observe,
+  stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+  fire:          Element.Methods.fire.methodize(),
+  observe:       Element.Methods.observe.methodize(),
+  stopObserving: Element.Methods.stopObserving.methodize(),
+  loaded:        false
+});
+
+(function() {
+  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+     Matthias Miller, Dean Edwards and John Resig. */
+
+  var timer;
+
+  function fireContentLoadedEvent() {
+    if (document.loaded) return;
+    if (timer) window.clearInterval(timer);
+    document.fire("dom:loaded");
+    document.loaded = true;
+  }
+
+  if (document.addEventListener) {
+    if (Prototype.Browser.WebKit) {
+      timer = window.setInterval(function() {
+        if (/loaded|complete/.test(document.readyState))
+          fireContentLoadedEvent();
+      }, 0);
+
+      Event.observe(window, "load", fireContentLoadedEvent);
+
+    } else {
+      document.addEventListener("DOMContentLoaded",
+        fireContentLoadedEvent, false);
+    }
+
+  } else {
+    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+    $("__onDOMContentLoaded").onreadystatechange = function() {
+      if (this.readyState == "complete") {
+        this.onreadystatechange = null;
+        fireContentLoadedEvent();
+      }
+    };
+  }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+  Before: function(element, content) {
+    return Element.insert(element, {before:content});
+  },
+
+  Top: function(element, content) {
+    return Element.insert(element, {top:content});
+  },
+
+  Bottom: function(element, content) {
+    return Element.insert(element, {bottom:content});
+  },
+
+  After: function(element, content) {
+    return Element.insert(element, {after:content});
+  }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = Element.cumulativeScrollOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  // Deprecation layer -- use newer Element methods now (1.5.2).
+
+  cumulativeOffset: Element.Methods.cumulativeOffset,
+
+  positionedOffset: Element.Methods.positionedOffset,
+
+  absolutize: function(element) {
+    Position.prepare();
+    return Element.absolutize(element);
+  },
+
+  relativize: function(element) {
+    Position.prepare();
+    return Element.relativize(element);
+  },
+
+  realOffset: Element.Methods.cumulativeScrollOffset,
+
+  offsetParent: Element.Methods.getOffsetParent,
+
+  page: Element.Methods.viewportOffset,
+
+  clone: function(source, target, options) {
+    options = options || { };
+    return Element.clonePosition(target, source, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+  function iter(name) {
+    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+  }
+
+  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+  function(element, className) {
+    className = className.toString().strip();
+    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+  } : function(element, className) {
+    className = className.toString().strip();
+    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+    if (!classNames && !className) return elements;
+
+    var nodes = $(element).getElementsByTagName('*');
+    className = ' ' + className + ' ';
+
+    for (var i = 0, child, cn; child = nodes[i]; i++) {
+      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+          (classNames && classNames.all(function(name) {
+            return !name.toString().blank() && cn.include(' ' + name + ' ');
+          }))))
+        elements.push(Element.extend(child));
+    }
+    return elements;
+  };
+
+  return function(className, parentElement) {
+    return $(parentElement || document.body).getElementsByClassName(className);
+  };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
Index: /afridex/plugins/Flutter/js/editnplace.js
===================================================================
--- /afridex/plugins/Flutter/js/editnplace.js (revision 21)
+++ /afridex/plugins/Flutter/js/editnplace.js (revision 21)
@@ -0,0 +1,654 @@
+var currentItemInEdit;
+
+
+Event.observe(window, 'load', CreateEditnPlaceObjects);
+
+
+var EIPObject = Class.create({
+
+	 // -- Constructor
+	 initialize: function(element, valueType, showPanel) {
+	 
+	 	var originalContent = "";
+	 
+	 	// Initialize object variables
+		this.postID  = '';
+		this.metaID = '';
+		this.elementID = '';
+		this.elementInnerID = '';
+		this.myNicEditor1 = null;
+		this.editActive = false;
+		this.oldVal = '';
+		this.valueType = valueType;
+		this.showPanel = showPanel;
+		this.panelID = '';
+		this.highlightColor = '#FFFFCC';
+		
+		
+		// Get post ID
+		var tmpPostID = '';
+		var tmpMetaID = '';
+		$w(element.className).each(function(currClassName){
+			if (currClassName.substr(0, 10) == 'EIP_postid')
+				tmpPostID = currClassName.substr(10);
+				
+			if (currClassName.substr(0, 8) == 'EIP_mid_')
+				tmpMetaID = currClassName.substr(8);
+		});
+		this.postID = tmpPostID;
+		this.metaID = tmpMetaID;
+		
+		// Get/create element ID
+		if (element.id == ''){
+			// Create random ID
+			var d = new Date();
+			randomnumber= Math.floor(Math.random()*1000);
+			element.id = "id" + d.getMilliseconds() + "_" + randomnumber;
+		}
+		this.elementID = element.id;
+		this.elementInnerID = element.id + '_inner';
+		originalContent = $(this.elementID).innerHTML;
+		elementBG = GetFontColor(this.elementID); 
+		
+		// Create nicEditor object
+		if (showPanel){
+			this.myNicEditor1 = new nicEditor({iconsPath : JS_FLUTTER_URI + 'js/nicEditorIcons.gif',buttonList : ['bold','italic','underline','ol','ul','link','unlink']});
+		}
+		else{
+			this.myNicEditor1 = new nicEditor({buttonList : []});
+		}
+		
+		// Creat nicEditor panel
+		this.panelID = "panel_" + this.elementID; 
+		myNicPanel = new Element('div', {'id': this.panelID, 'class': 'EIPnicPanelDiv','style': "display:none"});
+		$(document.body).insert({top: myNicPanel});				
+		this.myNicEditor1.setPanel(this.panelID);
+		
+			
+		// Wrap the field in a div		
+		element_inner = new Element('div', {'id': this.elementInnerID, 'style':'overflow:hidden'}).update(element.innerHTML);
+		element.innerHTML = "";
+		element.insert({top: element_inner});
+		
+		// Inherit styles
+		stylesList = ["fontSize", "fontFamily", "fontWeight", "letterSpacing", "color", "textTransform", "lineHeight"];
+		applyStyles(this.elementID, this.elementInnerID, stylesList);
+		//applyStyles(this.elementID, this.elementID, ['height']);
+		
+		if (showPanel){
+			recursiveApplyStyles($(this.elementInnerID));			
+		}
+		
+				
+		// Attach niceditor		
+		this.myNicEditor1.addInstance(this.elementID);
+		if (isset(element.firstChild.contentDocument)){
+			this.elementDivDocument =  element.firstChild.contentDocument;
+			Event.observe(this.elementDivDocument.firstChild, "mousedown", this.startEdit.bindAsEventListener(this));
+		}
+		else{
+			this.elementInnerID = this.elementID;
+			$(this.elementID).innerHTML = originalContent;
+			this.elementDivDocument = document; // $(this.elementInnerID)
+			Event.observe($(this.elementInnerID), "mousedown", this.startEdit.bindAsEventListener(this));
+		}
+		
+		// Adjust highlight color to be suitable for the theme
+		
+		if (elementBG != ''){
+			newcolor = new Color(elementBG);
+	 		this.highlightColor = newcolor.invert().getHex();  
+		}
+		
+		$$('EIP_title:hover, .EIP_content:hover').each(function(element) {
+				element.setStyle({backgroundColor: this.highlightColor});
+				});
+					
+	 },
+	 
+	 startEdit: function (){
+		if (!this.editActive){
+			
+			// set current field
+			cancelSaveField(); 
+			currentItemInEdit = this; 
+			
+			// Adjust pane/save positions
+			objOffset = $(this.elementID).cumulativeOffset();
+			elementTop = objOffset['top'] ;
+			if(Prototype.Browser.IE) elementTop = elementTop-20; 
+			elementLeft = objOffset['left'];
+			if (this.showPanel){
+				panel_top = elementTop - $(this.panelID).getHeight();
+				$(this.panelID).setStyle({display: "", top:panel_top+"px",left:elementLeft+"px"});
+			}
+			
+			currWidth = $(this.elementID).getWidth(); 
+			if ( currWidth < $('save_cancel_field').getWidth()) 
+				currWidth = $('save_cancel_field').getWidth();
+					
+			save_cancel_field_left = elementLeft + currWidth - $('save_cancel_field').getWidth();
+			save_cancel_field_top = elementTop - $('save_cancel_field').getHeight();
+			$('save_cancel_field').setStyle({display: "", top:save_cancel_field_top+"px",left:save_cancel_field_left+"px"});
+
+			// Save old value to restore it on cancel						
+			this.oldVal = this.elementDivDocument.getElementById(this.elementInnerID).innerHTML;
+			this.elementDivDocument.getElementById(this.elementInnerID).style.backgroundColor = this.highlightColor;
+			
+			this.editActive = true;
+		}
+	}
+	
+	
+	 
+});
+
+
+//------------------------------------------------------------
+// Create common objects (save button and status messages)
+// and initialize editnplace objects
+//------------------------------------------------------------
+
+function CreateEditnPlaceObjects() {
+	
+	// Create save/cancel buttons
+	saveCancel = new Element('div', {'id': 'save_cancel_field', 'class':'EIPSaveCancel', 'style': "display:none;"});
+	saveCancel.innerHTML = "<div id='savingDiv' style='display:none'>saving ...</div><div id='saveButton'><input type='button' value='Save' onclick='saveField()' /> Or <input type='button' value='Cancel' onclick=' cancelSaveField()' /></div>";
+	$(document.body).insert({top: saveCancel});
+	
+	/*// Create status message div
+	savingDiv = new Element('div', {'id': 'savingDiv', 'class':'EIPSaveStatus',  'style': "display:none;"});
+	savingDiv.innerHTML = "Saving ...";
+	$(document.body).insert({top: savingDiv});*/	
+		
+	// Loop through all post titles
+	$$('.EIP_title').each(function(element){
+		new EIPObject(element, 'EIP_title', false);
+	});
+	
+	// Loop through all post contents 
+	$$('.EIP_content').each(function(element){
+		new EIPObject(element, 'EIP_content', true);
+	});
+	
+	// Loop through text fields
+	$$('.EIP_textbox').each(function(element){
+		new EIPObject(element, 'EIP_textbox', false);
+	});
+	
+	// Loop through all post contents 
+	$$('.EIP_mulittextbox').each(function(element){
+		new EIPObject(element, 'EIP_mulittextbox', true);
+	});
+	
+	
+						
+}
+
+//------------------------------------------------------------
+// Saving a field
+//------------------------------------------------------------
+
+function saveField(){
+	var postParameters;	
+	
+	$('savingDiv').style.display = "";
+	$('saveButton').style.display = "none";
+	//$('save_cancel_field').style.display = "";
+	//$(currentItemInEdit.elementID).style.display = "none";
+	if (currentItemInEdit.showPanel) $(currentItemInEdit.panelID).style.display = "none";
+	
+	fieldVal = currentItemInEdit.elementDivDocument.getElementById(currentItemInEdit.elementInnerID).innerHTML;
+	
+	postParameters = 
+		"post_id=" + escape(encodeURI(currentItemInEdit.postID)) +
+		"&meta_id=" + escape(encodeURI(currentItemInEdit.metaID)) +
+		"&field_value=" + escape(encodeURI(fieldVal )) + 
+		"&field_type=" + escape(encodeURI(currentItemInEdit.valueType));
+	
+	new Ajax.Request(JS_FLUTTER_URI + 'RCCWP_EditnPlaceResponse.php',
+		{
+			method:'post',
+			onSuccess: function(transport){
+				currentItemInEdit.oldVal = fieldVal;
+				cancelSaveField();	
+			},
+			parameters: postParameters
+		});
+}
+
+//------------------------------------------------------------
+// Cancel Saving
+//------------------------------------------------------------
+
+function cancelSaveField(){
+	if (!isset(currentItemInEdit)) return;
+	currentItemInEdit.elementDivDocument.getElementById(currentItemInEdit.elementInnerID).innerHTML = currentItemInEdit.oldVal;
+
+	$('savingDiv').style.display = "none";
+	$('saveButton').style.display = "";
+	$('save_cancel_field').style.display = "none";
+	if (currentItemInEdit.showPanel) $(currentItemInEdit.panelID).style.display = "none";
+	//$(currentItemInEdit.elementID).style.display = "";
+	
+	currentItemInEdit.elementDivDocument.getElementById(currentItemInEdit.elementInnerID).style.backgroundColor = "";
+	currentItemInEdit.editActive = false;
+}
+
+//------------------------------------------------------------
+// Copy styles from an object to another
+//------------------------------------------------------------
+
+function applyStyles(from, to, stylesList){
+	to_styles = {};
+	stylesList.each( function (elmnt){
+		to_styles[elmnt] = $(from).getStyle(elmnt);
+	});
+	$(to).setStyle(to_styles);
+}
+
+//------------------------------------------------------------
+// Recursivly apply all styles of an object as an inline
+// styles to that object.
+//------------------------------------------------------------
+
+function recursiveApplyStyles(elmnt){
+	stylesList = [	"backgroundAttachment",	"backgroundColor","backgroundImage", "backgroundPosition","backgroundRepeat",
+					"fontSize", "fontFamily", "fontWeight", "fontStyle", 
+					"color", "direction", "lineHeight", "letterSpacing", "textAlign", "textDecoration", "textIndent", "textTransform", "whiteSpace", "wordSpacing", 
+					"borderBottomColor", "borderBottomStyle", "borderBottomWidth",
+					"borderLeftColor", "borderLeftStyle", "borderLeftWidth",
+					"borderTopColor", "borderTopStyle", "borderTopWidth",
+					"borderRightColor", "borderRightStyle", "borderRightWidth",
+					"listStyleImage", "listStylePosition", "listStyleType",
+					"paddingLeft", "paddingRight", "paddingTop", "paddingBottom", 
+					"marginLeft", "marginRight", "marginTop", "marginBottom",
+					"left", "right", "top", "bottom", "width",
+					"clear", "cursor", "display", "float", "position", "visibility"
+					
+					];
+					
+	applyStyles(elmnt, elmnt, stylesList);
+
+	childElements = elmnt.childElements();
+	if (childElements)
+		childElements.each(recursiveApplyStyles);
+}
+
+
+function isset(  ) {
+	// http://kevin.vanzonneveld.net
+	// +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+	// +   improved by: FremyCompany
+	// *     example 1: isset( undefined, true);
+	// *     returns 1: false
+	// *     example 2: isset( 'Kevin van Zonneveld' );
+	// *     returns 2: true
+	
+	var a=arguments; var l=a.length; var i=0;
+	
+	while ( i!=l ) {
+		if (typeof(a[i])=='undefined') { 
+		return false; 
+		} else { 
+		i++; 
+		}
+	}
+	
+	return true;
+}
+
+
+
+//------------------------------------------------------------------------------------
+// Colors library
+// Based on code from : http://www.ozzu.com/programming-forum/javascript-color-object-t66915.html
+//------------------------------------------------------------------------------------
+
+// Try to get the hex of the color 
+function GetColorHex(colorName){
+
+	if (!colorName || colorName == "" || colorName == "transparent") return "";
+	if (colorName.charAt(0) == "#") return colorName;
+	
+	tmpColor = Color.getFilteredObject(colorName);
+	if (tmpColor == false){
+		for (i=0;i<147;i=i+2){
+			if (colorName.toLowerCase == COLOR_NAMES[i].toLowerCase)
+				return COLOR_NAMES[i+1];
+		}
+	}
+	return colorName;
+}
+
+// Recursive function to get the font color
+function GetFontColor(element){
+	var elementBGColor = GetColorHex($(element).getStyle('color'));
+		
+	tmpColor = Color.getFilteredObject(elementBGColor);
+	if (tmpColor == false){
+		if (isset($(element).parentNode))
+			return GetFontColor($(element).parentNode);
+		else
+			return '';
+	}
+	else
+		return elementBGColor;
+}  
+
+
+/*
+   Converts INT to HEX
+   If Prototype library is loaded, use theirs, else use ours.
+*/
+if(!Number.toColorPart){Number.prototype.toColorPart = function(){return ((this < 16 ? '0' : '') + this.toString(16));}}
+
+/*
+   Constructor
+   @String c : hexadecimal, shorthand hex, or rgb()
+   #returns : Object reference to instance or false
+*/
+Color = function(c){
+   if(!c || !(c = Color.getFilteredObject(c))){return false;}
+   this.original = c;
+   this.r=c.r;this.g=c.g;this.b=c.b;
+   this.check();
+   this.gray = Math.round(.3*this.r + .59*this.g + .11*this.b);
+   this.hex = this.getHex();
+   this.rgb = this.getRGB();
+   return this;
+}
+
+/*
+   Screens color strings.
+   @String str : hexadecimal, shorthand hex, or rgb()
+   #returns : Object {r: XXX, g: XXX, b: XXX} or false
+*/
+Color.getFilteredObject = function(str){
+   if(/^#?([\da-f]{3}|[\da-f]{6})$/i.test(str)){
+      function _(s,i){return parseInt(s.substr(i,2), 16);}
+      str = str.replace(/^#/, '').replace(/^([\da-f])([\da-f])([\da-f])$/i, "$1$1$2$2$3$3");
+      return {r:_(str,0), g:_(str,2), b:_(str,4)}
+   }else if(/^rgb *\( *\d{0,3} *, *\d{0,3} *, *\d{0,3} *\)$/i.test(str)){
+      str = str.match(/^rgb *\( *(\d{0,3}) *, *(\d{0,3}) *, *(\d{0,3}) *\)$/i);
+      return {r:parseInt(str[1]), g:parseInt(str[2]), b:parseInt(str[3])};
+   }
+   return false;
+}
+
+/*
+   Checks the internal RGB registers for out of range values.
+   Resets out of range values.
+   #returns : Object reference to instance
+*/
+Color.prototype.check = function(){
+   if(this.r>255){this.r=255;}else if(this.r<0){this.r=0;}
+   if(this.g>255){this.g=255;}else if(this.g<0){this.g=0;}
+   if(this.b>255){this.b=255;}else if(this.b<0){this.b=0;}
+   return this;
+}
+
+/*
+   Resets color to the original color passed to the constructor.
+   #returns : Object reference to instance
+*/
+Color.prototype.revert = function(){
+   this.r=this.original.r;this.g=this.original.g;this.b=this.original.b;
+   return this;
+}
+
+/*
+   Inverts the color.
+   Black to White, vice versa
+   #returns : Object reference to instance
+*/
+Color.prototype.invert = function(){
+   this.check();
+   this.r = 255-this.r;
+   this.g = 255-this.g;
+   this.b = 255-this.b;
+   return this;
+}
+
+/*
+   Lightens the color.
+   @Int amount : 1-254 -- RGB amount to lighten the color
+   #returns : Object reference to instance
+*/
+Color.prototype.lighten = function(amount){
+   this.r += parseInt(amount);
+   this.g += parseInt(amount);
+   this.b += parseInt(amount);
+   return this;
+}
+
+/*
+   Darkens the color.
+   @Int amount : 1-254 -- RGB amount to darken the color
+   #returns : Object reference to instance
+*/
+Color.prototype.darken = function(amount){
+   return this.lighten(parseInt('-'+amount));
+}
+
+/*
+   Converts the color to Grayscale
+   #returns : Object reference to instance
+*/
+Color.prototype.grayscale = function(){
+   this.check();
+   this.gray = Math.round(.3*this.r + .59*this.g + .11*this.b);
+   this.r=this.gray;this.g=this.gray;this.b=this.gray;
+   return this;
+}
+
+/*
+   Convenience function for lightening color.
+   @Int amount : amount to lighten color
+   @Bool returnRGB : true uses RGB return string, false uses HEX return string.
+   #returns : String color
+*/
+Color.prototype.getLighter = function(amount, returnRGB){
+   return this.lighten(amount).check()[returnRGB ? 'getRGB' : 'getHex']();
+}
+
+/*
+   Convenience function for darkening color.
+   @Int amount : amount to darken color
+   @Bool returnRGB : true uses RGB return string, false uses HEX return string.
+   #returns : String color
+*/
+Color.prototype.getDarker = function(amount, returnRGB){
+   return this.darken(amount).check()[returnRGB ? 'getRGB' : 'getHex']();
+}
+
+/*
+   Convenience function for grayscaling color.
+   @Bool returnRGB : true uses RGB return string, false uses HEX return string.
+   #returns : String color
+*/
+Color.prototype.getGrayscale = function(returnRGB){
+   this.grayscale();
+   return (returnRGB ? ('rgb('+this.gray+','+this.gray+','+this.gray+')') : this.gray.toColorPart().replace(/^([\da-f]{2})$/i, "#$1$1$1"));
+}
+
+/*
+   Convenience function for inverting color.
+   @Bool returnRGB : true uses RGB return string, false uses HEX return string.
+   #returns : String color
+*/
+Color.prototype.getInverted = function(returnRGB){
+   return this.invert()[returnRGB ? 'getRGB' : 'getHex']();
+}
+
+/*
+   Gets the rgb(x,x,x) value of the color
+   #returns : String rgb color
+*/
+Color.prototype.getRGB = function(){
+   this.check();
+   this.rgb = 'rgb('+this.r+','+this.g+','+this.b+')';
+   return this.rgb;
+}
+
+/*
+   Gets the hex value of the color
+   @Bool shorthandReturnAcceptable : true will return #333 instead of #333333
+   #returns : String hex color
+*/
+Color.prototype.getHex = function(shorthandReturnAcceptable){
+   this.check();
+   this.hex = '#' + this.r.toColorPart() + this.g.toColorPart() + this.b.toColorPart();
+   if(shorthandReturnAcceptable){return this.hex.replace(/^#([\da-f])\1([\da-f])\2([\da-f])\3$/i, "#$1$2$3");}
+   return this.hex;
+}
+
+
+var COLOR_NAMES = new Array(
+	"AliceBlue" , "#F0F8FF",
+	"AntiqueWhite" , "#FAEBD7",
+	"Aqua" , "#00FFFF",
+	"Aquamarine" , "#7FFFD4",
+	"Azure" , "#F0FFFF",
+	"Beige" , "#F5F5DC",
+	"Bisque" , "#FFE4C4",
+	"Black" , "#000000",
+	"BlanchedAlmond" , "#FFEBCD",
+	"Blue" , "#0000FF",
+	"BlueViolet" , "#8A2BE2",
+	"Brown" , "#A52A2A",
+	"BurlyWood" , "#DEB887",
+	"CadetBlue" , "#5F9EA0",
+	"Chartreuse" , "#7FFF00",
+	"Chocolate" , "#D2691E",
+	"Coral" , "#FF7F50",
+	"CornflowerBlue" , "#6495ED",
+	"Cornsilk" , "#FFF8DC",
+	"Crimson" , "#DC143C",
+	"Cyan" , "#00FFFF",
+	"DarkBlue" , "#00008B",
+	"DarkCyan" , "#008B8B",
+	"DarkGoldenRod" , "#B8860B",
+	"DarkGray" , "#A9A9A9",
+	"DarkGrey" , "#A9A9A9",
+	"DarkGreen" , "#006400",
+	"DarkKhaki" , "#BDB76B",
+	"DarkMagenta" , "#8B008B",
+	"DarkOliveGreen" , "#556B2F",
+	"Darkorange" , "#FF8C00",
+	"DarkOrchid" , "#9932CC",
+	"DarkRed" , "#8B0000",
+	"DarkSalmon" , "#E9967A",
+	"DarkSeaGreen" , "#8FBC8F",
+	"DarkSlateBlue" , "#483D8B",
+	"DarkSlateGray" , "#2F4F4F",
+	"DarkSlateGrey" , "#2F4F4F",
+	"DarkTurquoise" , "#00CED1",
+	"DarkViolet" , "#9400D3",
+	"DeepPink" , "#FF1493",
+	"DeepSkyBlue" , "#00BFFF",
+	"DimGray" , "#696969",
+	"DimGrey" , "#696969",
+	"DodgerBlue" , "#1E90FF",
+	"FireBrick" , "#B22222",
+	"FloralWhite" , "#FFFAF0",
+	"ForestGreen" , "#228B22",
+	"Fuchsia" , "#FF00FF",
+	"Gainsboro" , "#DCDCDC",
+	"GhostWhite" , "#F8F8FF",
+	"Gold" , "#FFD700",
+	"GoldenRod" , "#DAA520",
+	"Gray" , "#808080",
+	"Grey" , "#808080",
+	"Green" , "#008000",
+	"GreenYellow" , "#ADFF2F",
+	"HoneyDew" , "#F0FFF0",
+	"HotPink" , "#FF69B4",
+	"IndianRed " , "#CD5C5C",
+	"Indigo " , "#4B0082",
+	"Ivory" , "#FFFFF0",
+	"Khaki" , "#F0E68C",
+	"Lavender" , "#E6E6FA",
+	"LavenderBlush" , "#FFF0F5",
+	"LawnGreen" , "#7CFC00",
+	"LemonChiffon" , "#FFFACD",
+	"LightBlue" , "#ADD8E6",
+	"LightCoral" , "#F08080",
+	"LightCyan" , "#E0FFFF",
+	"LightGoldenRodYellow" , "#FAFAD2",
+	"LightGray" , "#D3D3D3",
+	"LightGrey" , "#D3D3D3",
+	"LightGreen" , "#90EE90",
+	"LightPink" , "#FFB6C1",
+	"LightSalmon" , "#FFA07A",
+	"LightSeaGreen" , "#20B2AA",
+	"LightSkyBlue" , "#87CEFA",
+	"LightSlateGray" , "#778899",
+	"LightSlateGrey" , "#778899",
+	"LightSteelBlue" , "#B0C4DE",
+	"LightYellow" , "#FFFFE0",
+	"Lime" , "#00FF00",
+	"LimeGreen" , "#32CD32",
+	"Linen" , "#FAF0E6",
+	"Magenta" , "#FF00FF",
+	"Maroon" , "#800000",
+	"MediumAquaMarine" , "#66CDAA",
+	"MediumBlue" , "#0000CD",
+	"MediumOrchid" , "#BA55D3",
+	"MediumPurple" , "#9370D8",
+	"MediumSeaGreen" , "#3CB371",
+	"MediumSlateBlue" , "#7B68EE",
+	"MediumSpringGreen" , "#00FA9A",
+	"MediumTurquoise" , "#48D1CC",
+	"MediumVioletRed" , "#C71585",
+	"MidnightBlue" , "#191970",
+	"MintCream" , "#F5FFFA",
+	"MistyRose" , "#FFE4E1",
+	"Moccasin" , "#FFE4B5",
+	"NavajoWhite" , "#FFDEAD",
+	"Navy" , "#000080",
+	"OldLace" , "#FDF5E6",
+	"Olive" , "#808000",
+	"OliveDrab" , "#6B8E23",
+	"Orange" , "#FFA500",
+	"OrangeRed" , "#FF4500",
+	"Orchid" , "#DA70D6",
+	"PaleGoldenRod" , "#EEE8AA",
+	"PaleGreen" , "#98FB98",
+	"PaleTurquoise" , "#AFEEEE",
+	"PaleVioletRed" , "#D87093",
+	"PapayaWhip" , "#FFEFD5",
+	"PeachPuff" , "#FFDAB9",
+	"Peru" , "#CD853F",
+	"Pink" , "#FFC0CB",
+	"Plum" , "#DDA0DD",
+	"PowderBlue" , "#B0E0E6",
+	"Purple" , "#800080",
+	"Red" , "#FF0000",
+	"RosyBrown" , "#BC8F8F",
+	"RoyalBlue" , "#4169E1",
+	"SaddleBrown" , "#8B4513",
+	"Salmon" , "#FA8072",
+	"SandyBrown" , "#F4A460",
+	"SeaGreen" , "#2E8B57",
+	"SeaShell" , "#FFF5EE",
+	"Sienna" , "#A0522D",
+	"Silver" , "#C0C0C0",
+	"SkyBlue" , "#87CEEB",
+	"SlateBlue" , "#6A5ACD",
+	"SlateGray" , "#708090",
+	"SlateGrey" , "#708090",
+	"Snow" , "#FFFAFA",
+	"SpringGreen" , "#00FF7F",
+	"SteelBlue" , "#4682B4",
+	"Tan" , "#D2B48C",
+	"Teal" , "#008080",
+	"Thistle" , "#D8BFD8",
+	"Tomato" , "#FF6347",
+	"Turquoise" , "#40E0D0",
+	"Violet" , "#EE82EE",
+	"Wheat" , "#F5DEB3",
+	"White" , "#FFFFFF",
+	"WhiteSmoke" , "#F5F5F5",
+	"Yellow" , "#FFFF00",
+	"YellowGreen" , "#9ACD32");
Index: /afridex/plugins/Flutter/js/cropper.js
===================================================================
--- /afridex/plugins/Flutter/js/cropper.js (revision 21)
+++ /afridex/plugins/Flutter/js/cropper.js (revision 21)
@@ -0,0 +1,567 @@
+/** 
+ * Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * http://www.opensource.org/licenses/bsd-license.php
+ * 
+ * See scriptaculous.js for full scriptaculous licence
+ */
+
+
+var CropDraggable=Class.create();
+Object.extend(Object.extend(CropDraggable.prototype,Draggable.prototype),{initialize:function(_1){
+this.options=Object.extend({drawMethod:function(){
+}},arguments[1]||{});
+this.element=$(_1);
+this.handle=this.element;
+this.delta=this.currentDelta();
+this.dragging=false;
+this.eventMouseDown=this.initDrag.bindAsEventListener(this);
+Event.observe(this.handle,"mousedown",this.eventMouseDown);
+Draggables.register(this);
+},draw:function(_2){
+var _3=Position.cumulativeOffset(this.element);
+var d=this.currentDelta();
+_3[0]-=d[0];
+_3[1]-=d[1];
+var p=[0,1].map(function(i){
+return (_2[i]-_3[i]-this.offset[i]);
+}.bind(this));
+this.options.drawMethod(p);
+}});
+var Cropper={};
+Cropper.Img=Class.create();
+Cropper.Img.prototype={initialize:function(_7,_8){
+this.options=Object.extend({ratioDim:{x:0,y:0},minWidth:0,minHeight:0,displayOnInit:false,onEndCrop:Prototype.emptyFunction,captureKeys:true,onloadCoords:null,maxWidth:0,maxHeight:0},_8||{});
+this.img=$(_7);
+this.clickCoords={x:0,y:0};
+this.dragging=false;
+this.resizing=false;
+this.isWebKit=/Konqueror|Safari|KHTML/.test(navigator.userAgent);
+this.isIE=/MSIE/.test(navigator.userAgent);
+this.isOpera8=/Opera\s[1-8]/.test(navigator.userAgent);
+this.ratioX=0;
+this.ratioY=0;
+this.attached=false;
+this.fixedWidth=(this.options.maxWidth>0&&(this.options.minWidth>=this.options.maxWidth));
+this.fixedHeight=(this.options.maxHeight>0&&(this.options.minHeight>=this.options.maxHeight));
+if(typeof this.img=="undefined"){
+return;
+}
+$A(document.getElementsByTagName("script")).each(function(s){
+if(s.src.match(/cropper\.js/)){
+var _a=s.src.replace(/cropper\.js(.*)?/,"");
+var _b=document.createElement("link");
+_b.rel="stylesheet";
+_b.type="text/css";
+_b.href=_a+"../css/cropper.css";
+_b.media="screen";
+document.getElementsByTagName("head")[0].appendChild(_b);
+}
+});
+if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){
+var _c=this.getGCD(this.options.ratioDim.x,this.options.ratioDim.y);
+this.ratioX=this.options.ratioDim.x/_c;
+this.ratioY=this.options.ratioDim.y/_c;
+}
+this.subInitialize();
+if(this.img.complete||this.isWebKit){
+this.onLoad();
+}else{
+Event.observe(this.img,"load",this.onLoad.bindAsEventListener(this));
+}
+},getGCD:function(a,b){
+if(b==0){
+return a;
+}
+return this.getGCD(b,a%b);
+},onLoad:function(){
+var _f="imgCrop_";
+var _10=this.img.parentNode;
+var _11="";
+if(this.isOpera8){
+_11=" opera8";
+}
+this.imgWrap=Builder.node("div",{"class":_f+"wrap"+_11});
+this.north=Builder.node("div",{"class":_f+"overlay "+_f+"north"},[Builder.node("span")]);
+this.east=Builder.node("div",{"class":_f+"overlay "+_f+"east"},[Builder.node("span")]);
+this.south=Builder.node("div",{"class":_f+"overlay "+_f+"south"},[Builder.node("span")]);
+this.west=Builder.node("div",{"class":_f+"overlay "+_f+"west"},[Builder.node("span")]);
+var _12=[this.north,this.east,this.south,this.west];
+this.dragArea=Builder.node("div",{"class":_f+"dragArea"},_12);
+this.handleN=Builder.node("div",{"class":_f+"handle "+_f+"handleN"});
+this.handleNE=Builder.node("div",{"class":_f+"handle "+_f+"handleNE"});
+this.handleE=Builder.node("div",{"class":_f+"handle "+_f+"handleE"});
+this.handleSE=Builder.node("div",{"class":_f+"handle "+_f+"handleSE"});
+this.handleS=Builder.node("div",{"class":_f+"handle "+_f+"handleS"});
+this.handleSW=Builder.node("div",{"class":_f+"handle "+_f+"handleSW"});
+this.handleW=Builder.node("div",{"class":_f+"handle "+_f+"handleW"});
+this.handleNW=Builder.node("div",{"class":_f+"handle "+_f+"handleNW"});
+this.selArea=Builder.node("div",{"class":_f+"selArea"},[Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeNorth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeEast"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeSouth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeWest"},[Builder.node("span")]),this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW,Builder.node("div",{"class":_f+"clickArea"})]);
+this.imgWrap.appendChild(this.img);
+this.imgWrap.appendChild(this.dragArea);
+this.dragArea.appendChild(this.selArea);
+this.dragArea.appendChild(Builder.node("div",{"class":_f+"clickArea"}));
+_10.appendChild(this.imgWrap);
+this.startDragBind=this.startDrag.bindAsEventListener(this);
+Event.observe(this.dragArea,"mousedown",this.startDragBind);
+this.onDragBind=this.onDrag.bindAsEventListener(this);
+Event.observe(document,"mousemove",this.onDragBind);
+this.endCropBind=this.endCrop.bindAsEventListener(this);
+Event.observe(document,"mouseup",this.endCropBind);
+this.resizeBind=this.startResize.bindAsEventListener(this);
+this.handles=[this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW];
+this.registerHandles(true);
+if(this.options.captureKeys){
+this.keysBind=this.handleKeys.bindAsEventListener(this);
+Event.observe(document,"keypress",this.keysBind);
+}
+new CropDraggable(this.selArea,{drawMethod:this.moveArea.bindAsEventListener(this)});
+this.setParams();
+},registerHandles:function(_13){
+for(var i=0;i<this.handles.length;i++){
+var _15=$(this.handles[i]);
+if(_13){
+var _16=false;
+if(this.fixedWidth&&this.fixedHeight){
+_16=true;
+}else{
+if(this.fixedWidth||this.fixedHeight){
+var _17=_15.className.match(/([S|N][E|W])$/);
+var _18=_15.className.match(/(E|W)$/);
+var _19=_15.className.match(/(N|S)$/);
+if(_17){
+_16=true;
+}else{
+if(this.fixedWidth&&_18){
+_16=true;
+}else{
+if(this.fixedHeight&&_19){
+_16=true;
+}
+}
+}
+}
+}
+if(_16){
+_15.hide();
+}else{
+Event.observe(_15,"mousedown",this.resizeBind);
+}
+}else{
+_15.show();
+Event.stopObserving(_15,"mousedown",this.resizeBind);
+}
+}
+},setParams:function(){
+this.imgW=this.img.width;
+this.imgH=this.img.height;
+$(this.north).setStyle({height:0});
+$(this.east).setStyle({width:0,height:0});
+$(this.south).setStyle({height:0});
+$(this.west).setStyle({width:0,height:0});
+$(this.imgWrap).setStyle({"width":this.imgW+"px","height":this.imgH+"px"});
+$(this.selArea).hide();
+var _1a={x1:0,y1:0,x2:0,y2:0};
+var _1b=false;
+if(this.options.onloadCoords!=null){
+_1a=this.cloneCoords(this.options.onloadCoords);
+_1b=true;
+}else{
+if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){
+_1a.x1=Math.ceil((this.imgW-this.options.ratioDim.x)/2);
+_1a.y1=Math.ceil((this.imgH-this.options.ratioDim.y)/2);
+_1a.x2=_1a.x1+this.options.ratioDim.x;
+_1a.y2=_1a.y1+this.options.ratioDim.y;
+_1b=true;
+}
+}
+this.setAreaCoords(_1a,false,false,1);
+if(this.options.displayOnInit&&_1b){
+this.selArea.show();
+this.drawArea();
+this.endCrop();
+}
+this.attached=true;
+},remove:function(){
+if(this.attached){
+this.attached=false;
+this.imgWrap.parentNode.insertBefore(this.img,this.imgWrap);
+this.imgWrap.parentNode.removeChild(this.imgWrap);
+Event.stopObserving(this.dragArea,"mousedown",this.startDragBind);
+Event.stopObserving(document,"mousemove",this.onDragBind);
+Event.stopObserving(document,"mouseup",this.endCropBind);
+this.registerHandles(false);
+if(this.options.captureKeys){
+Event.stopObserving(document,"keypress",this.keysBind);
+}
+}
+},reset:function(){
+if(!this.attached){
+this.onLoad();
+}else{
+this.setParams();
+}
+this.endCrop();
+},handleKeys:function(e){
+var dir={x:0,y:0};
+if(!this.dragging){
+switch(e.keyCode){
+case (37):
+dir.x=-1;
+break;
+case (38):
+dir.y=-1;
+break;
+case (39):
+dir.x=1;
+break;
+case (40):
+dir.y=1;
+break;
+}
+if(dir.x!=0||dir.y!=0){
+if(e.shiftKey){
+dir.x*=10;
+dir.y*=10;
+}
+this.moveArea([this.areaCoords.x1+dir.x,this.areaCoords.y1+dir.y]);
+Event.stop(e);
+}
+}
+},calcW:function(){
+return (this.areaCoords.x2-this.areaCoords.x1);
+},calcH:function(){
+return (this.areaCoords.y2-this.areaCoords.y1);
+},moveArea:function(_1e){
+this.setAreaCoords({x1:_1e[0],y1:_1e[1],x2:_1e[0]+this.calcW(),y2:_1e[1]+this.calcH()},true,false);
+this.drawArea();
+},cloneCoords:function(_1f){
+return {x1:_1f.x1,y1:_1f.y1,x2:_1f.x2,y2:_1f.y2};
+},setAreaCoords:function(_20,_21,_22,_23,_24){
+if(_21){
+var _25=_20.x2-_20.x1;
+var _26=_20.y2-_20.y1;
+if(_20.x1<0){
+_20.x1=0;
+_20.x2=_25;
+}
+if(_20.y1<0){
+_20.y1=0;
+_20.y2=_26;
+}
+if(_20.x2>this.imgW){
+_20.x2=this.imgW;
+_20.x1=this.imgW-_25;
+}
+if(_20.y2>this.imgH){
+_20.y2=this.imgH;
+_20.y1=this.imgH-_26;
+}
+}else{
+if(_20.x1<0){
+_20.x1=0;
+}
+if(_20.y1<0){
+_20.y1=0;
+}
+if(_20.x2>this.imgW){
+_20.x2=this.imgW;
+}
+if(_20.y2>this.imgH){
+_20.y2=this.imgH;
+}
+if(_23!=null){
+if(this.ratioX>0){
+this.applyRatio(_20,{x:this.ratioX,y:this.ratioY},_23,_24);
+}else{
+if(_22){
+this.applyRatio(_20,{x:1,y:1},_23,_24);
+}
+}
+var _27=[this.options.minWidth,this.options.minHeight];
+var _28=[this.options.maxWidth,this.options.maxHeight];
+if(_27[0]>0||_27[1]>0||_28[0]>0||_28[1]>0){
+var _29={a1:_20.x1,a2:_20.x2};
+var _2a={a1:_20.y1,a2:_20.y2};
+var _2b={min:0,max:this.imgW};
+var _2c={min:0,max:this.imgH};
+if((_27[0]!=0||_27[1]!=0)&&_22){
+if(_27[0]>0){
+_27[1]=_27[0];
+}else{
+if(_27[1]>0){
+_27[0]=_27[1];
+}
+}
+}
+if((_28[0]!=0||_28[0]!=0)&&_22){
+if(_28[0]>0&&_28[0]<=_28[1]){
+_28[1]=_28[0];
+}else{
+if(_28[1]>0&&_28[1]<=_28[0]){
+_28[0]=_28[1];
+}
+}
+}
+if(_27[0]>0){
+this.applyDimRestriction(_29,_27[0],_23.x,_2b,"min");
+}
+if(_27[1]>1){
+this.applyDimRestriction(_2a,_27[1],_23.y,_2c,"min");
+}
+if(_28[0]>0){
+this.applyDimRestriction(_29,_28[0],_23.x,_2b,"max");
+}
+if(_28[1]>1){
+this.applyDimRestriction(_2a,_28[1],_23.y,_2c,"max");
+}
+_20={x1:_29.a1,y1:_2a.a1,x2:_29.a2,y2:_2a.a2};
+}
+}
+}
+this.areaCoords=_20;
+},applyDimRestriction:function(_2d,val,_2f,_30,_31){
+var _32;
+if(_31=="min"){
+_32=((_2d.a2-_2d.a1)<val);
+}else{
+_32=((_2d.a2-_2d.a1)>val);
+}
+if(_32){
+if(_2f==1){
+_2d.a2=_2d.a1+val;
+}else{
+_2d.a1=_2d.a2-val;
+}
+if(_2d.a1<_30.min){
+_2d.a1=_30.min;
+_2d.a2=val;
+}else{
+if(_2d.a2>_30.max){
+_2d.a1=_30.max-val;
+_2d.a2=_30.max;
+}
+}
+}
+},applyRatio:function(_33,_34,_35,_36){
+var _37;
+if(_36=="N"||_36=="S"){
+_37=this.applyRatioToAxis({a1:_33.y1,b1:_33.x1,a2:_33.y2,b2:_33.x2},{a:_34.y,b:_34.x},{a:_35.y,b:_35.x},{min:0,max:this.imgW});
+_33.x1=_37.b1;
+_33.y1=_37.a1;
+_33.x2=_37.b2;
+_33.y2=_37.a2;
+}else{
+_37=this.applyRatioToAxis({a1:_33.x1,b1:_33.y1,a2:_33.x2,b2:_33.y2},{a:_34.x,b:_34.y},{a:_35.x,b:_35.y},{min:0,max:this.imgH});
+_33.x1=_37.a1;
+_33.y1=_37.b1;
+_33.x2=_37.a2;
+_33.y2=_37.b2;
+}
+},applyRatioToAxis:function(_38,_39,_3a,_3b){
+var _3c=Object.extend(_38,{});
+var _3d=_3c.a2-_3c.a1;
+var _3e=Math.floor(_3d*_39.b/_39.a);
+var _3f;
+var _40;
+var _41=null;
+if(_3a.b==1){
+_3f=_3c.b1+_3e;
+if(_3f>_3b.max){
+_3f=_3b.max;
+_41=_3f-_3c.b1;
+}
+_3c.b2=_3f;
+}else{
+_3f=_3c.b2-_3e;
+if(_3f<_3b.min){
+_3f=_3b.min;
+_41=_3f+_3c.b2;
+}
+_3c.b1=_3f;
+}
+if(_41!=null){
+_40=Math.floor(_41*_39.a/_39.b);
+if(_3a.a==1){
+_3c.a2=_3c.a1+_40;
+}else{
+_3c.a1=_3c.a1=_3c.a2-_40;
+}
+}
+return _3c;
+},drawArea:function(){
+var _42=this.calcW();
+var _43=this.calcH();
+var px="px";
+var _45=[this.areaCoords.x1+px,this.areaCoords.y1+px,_42+px,_43+px,this.areaCoords.x2+px,this.areaCoords.y2+px,(this.img.width-this.areaCoords.x2)+px,(this.img.height-this.areaCoords.y2)+px];
+var _46=this.selArea.style;
+_46.left=_45[0];
+_46.top=_45[1];
+_46.width=_45[2];
+_46.height=_45[3];
+var _47=Math.ceil((_42-6)/2)+px;
+var _48=Math.ceil((_43-6)/2)+px;
+this.handleN.style.left=_47;
+this.handleE.style.top=_48;
+this.handleS.style.left=_47;
+this.handleW.style.top=_48;
+this.north.style.height=_45[1];
+var _49=this.east.style;
+_49.top=_45[1];
+_49.height=_45[3];
+_49.left=_45[4];
+_49.width=_45[6];
+var _4a=this.south.style;
+_4a.top=_45[5];
+_4a.height=_45[7];
+var _4b=this.west.style;
+_4b.top=_45[1];
+_4b.height=_45[3];
+_4b.width=_45[0];
+this.subDrawArea();
+this.forceReRender();
+},forceReRender:function(){
+if(this.isIE||this.isWebKit){
+var n=document.createTextNode(" ");
+var d,el,fixEL,i;
+if(this.isIE){
+fixEl=this.selArea;
+}else{
+if(this.isWebKit){
+fixEl=document.getElementsByClassName("imgCrop_marqueeSouth",this.imgWrap)[0];
+d=Builder.node("div","");
+d.style.visibility="hidden";
+var _4e=["SE","S","SW"];
+for(i=0;i<_4e.length;i++){
+el=document.getElementsByClassName("imgCrop_handle"+_4e[i],this.selArea)[0];
+if(el.childNodes.length){
+el.removeChild(el.childNodes[0]);
+}
+el.appendChild(d);
+}
+}
+}
+fixEl.appendChild(n);
+fixEl.removeChild(n);
+}
+},startResize:function(e){
+this.startCoords=this.cloneCoords(this.areaCoords);
+this.resizing=true;
+this.resizeHandle=Event.element(e).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/,"");
+Event.stop(e);
+},startDrag:function(e){
+this.selArea.show();
+this.clickCoords=this.getCurPos(e);
+this.setAreaCoords({x1:this.clickCoords.x,y1:this.clickCoords.y,x2:this.clickCoords.x,y2:this.clickCoords.y},false,false,null);
+this.dragging=true;
+this.onDrag(e);
+Event.stop(e);
+},getCurPos:function(e){
+var el=this.imgWrap,wrapOffsets=Position.cumulativeOffset(el);
+while(el.nodeName!="BODY"){
+wrapOffsets[1]-=el.scrollTop||0;
+wrapOffsets[0]-=el.scrollLeft||0;
+el=el.parentNode;
+}
+return curPos={x:Event.pointerX(e)-wrapOffsets[0],y:Event.pointerY(e)-wrapOffsets[1]};
+},onDrag:function(e){
+if(this.dragging||this.resizing){
+var _54=null;
+var _55=this.getCurPos(e);
+var _56=this.cloneCoords(this.areaCoords);
+var _57={x:1,y:1};
+if(this.dragging){
+if(_55.x<this.clickCoords.x){
+_57.x=-1;
+}
+if(_55.y<this.clickCoords.y){
+_57.y=-1;
+}
+this.transformCoords(_55.x,this.clickCoords.x,_56,"x");
+this.transformCoords(_55.y,this.clickCoords.y,_56,"y");
+}else{
+if(this.resizing){
+_54=this.resizeHandle;
+if(_54.match(/E/)){
+this.transformCoords(_55.x,this.startCoords.x1,_56,"x");
+if(_55.x<this.startCoords.x1){
+_57.x=-1;
+}
+}else{
+if(_54.match(/W/)){
+this.transformCoords(_55.x,this.startCoords.x2,_56,"x");
+if(_55.x<this.startCoords.x2){
+_57.x=-1;
+}
+}
+}
+if(_54.match(/N/)){
+this.transformCoords(_55.y,this.startCoords.y2,_56,"y");
+if(_55.y<this.startCoords.y2){
+_57.y=-1;
+}
+}else{
+if(_54.match(/S/)){
+this.transformCoords(_55.y,this.startCoords.y1,_56,"y");
+if(_55.y<this.startCoords.y1){
+_57.y=-1;
+}
+}
+}
+}
+}
+this.setAreaCoords(_56,false,e.shiftKey,_57,_54);
+this.drawArea();
+Event.stop(e);
+}
+},transformCoords:function(_58,_59,_5a,_5b){
+var _5c=[_58,_59];
+if(_58>_59){
+_5c.reverse();
+}
+_5a[_5b+"1"]=_5c[0];
+_5a[_5b+"2"]=_5c[1];
+},endCrop:function(){
+this.dragging=false;
+this.resizing=false;
+this.options.onEndCrop(this.areaCoords,{width:this.calcW(),height:this.calcH()});
+},subInitialize:function(){
+},subDrawArea:function(){
+}};
+Cropper.ImgWithPreview=Class.create();
+Object.extend(Object.extend(Cropper.ImgWithPreview.prototype,Cropper.Img.prototype),{subInitialize:function(){
+this.hasPreviewImg=false;
+if(typeof (this.options.previewWrap)!="undefined"&&this.options.minWidth>0&&this.options.minHeight>0){
+this.previewWrap=$(this.options.previewWrap);
+this.previewImg=this.img.cloneNode(false);
+this.previewImg.id="imgCrop_"+this.previewImg.id;
+this.options.displayOnInit=true;
+this.hasPreviewImg=true;
+this.previewWrap.addClassName("imgCrop_previewWrap");
+this.previewWrap.setStyle({width:this.options.minWidth+"px",height:this.options.minHeight+"px"});
+this.previewWrap.appendChild(this.previewImg);
+}
+},subDrawArea:function(){
+if(this.hasPreviewImg){
+var _5d=this.calcW();
+var _5e=this.calcH();
+var _5f={x:this.imgW/_5d,y:this.imgH/_5e};
+var _60={x:_5d/this.options.minWidth,y:_5e/this.options.minHeight};
+var _61={w:Math.ceil(this.options.minWidth*_5f.x)+"px",h:Math.ceil(this.options.minHeight*_5f.y)+"px",x:"-"+Math.ceil(this.areaCoords.x1/_60.x)+"px",y:"-"+Math.ceil(this.areaCoords.y1/_60.y)+"px"};
+var _62=this.previewImg.style;
+_62.width=_61.w;
+_62.height=_61.h;
+_62.left=_61.x;
+_62.top=_61.y;
+}
+}});
+
Index: /afridex/plugins/Flutter/js/epoch_classes.js
===================================================================
--- /afridex/plugins/Flutter/js/epoch_classes.js (revision 21)
+++ /afridex/plugins/Flutter/js/epoch_classes.js (revision 21)
@@ -0,0 +1,1095 @@
+/*!!
+Epoch DHTML JavaScript Calendar - Version 2.0.2
+English Edition
+Primary JavaScript File
+(c) 2006-2007 MeanFreePath
+Free for NON-COMMERCIAL use - see website for details and updates
+http://www.meanfreepath.com/javascript_calendar/index.html
+!!*/
+
+/**
+* The main Epoch class.  All publicly-accessible methods and properties are called from this class
+*/
+function Epoch(name,mode,targetelement,multiselect, m_afterHide, m_afterHideParam, m_dateformat) {
+	var self = this; //workaround due to varying definitions of "this" in variable scopes. see http://www.meanfreepath.com/support/epoch/epoch.html#self for details
+	//DEFINE PRIVATE METHODS
+	//-----------------------------------------------------------------------------
+	/**
+	* Declares and initializes the calendar variables.  All the variables here can be safely changed
+	* (within reason ;) by the developer
+	*/
+	function calConfig() {
+		self.versionNumber = '2.0.2';
+		self.displayYearInitial = self.curDate.getFullYear(); //the initial year to display on load
+		self.displayMonthInitial = self.curDate.getMonth(); //the initial month to display on load (0-11)
+		self.displayYear = self.displayYearInitial;
+		self.displayMonth = self.displayMonthInitial;
+
+		var d = new Date();
+		var curr_year = d.getFullYear();
+		self.minDate = new Date(curr_year-5,0,1);
+//		self.maxDate = new Date(2012,11,31);
+		self.maxDate = new Date(parseInt(curr_year)+parseInt(10),11,31);
+
+		self.startDay = 0; // the day the week will 'start' on: 0(Sun) to 6(Sat)
+		self.showWeeks = true; //whether the week numbers will be shown
+		self.selCurMonthOnly = true; //allow user to only select dates in the currently displayed month
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* All language settings for Epoch are made here.
+	* Check Date.dateFormat() for the Date object's language settings
+	*/
+	function setLang() {
+		self.daylist = new Array('S','M','T','W','T','F','S','S','M','T','W','T','F','S');
+		self.months_sh = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+		self.monthup_title = 'Go to the next month';
+		self.monthdn_title = 'Go to the previous month';
+		self.clearbtn_caption = 'Clear';
+		self.clearbtn_title = 'Clears any dates selected on the calendar';
+		self.maxrange_caption = 'This is the maximum range';
+		self.closebtn_caption = 'Close';
+		self.closebtn_title = 'Close the calendar';
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Initializes the standard Gregorian Calendar parameters
+	*/
+	function setDays() {
+		self.daynames = new Array();
+		var j=0;
+		for(var i=self.startDay;i<self.startDay + 7;i++) {
+			self.daynames[j++] = self.daylist[i];
+		}
+		self.monthDayCount = new Array(31,((self.curDate.getFullYear() - 2000) % 4 ? 28 : 29),31,30,31,30,31,31,30,31,30,31);
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the full DOM implementation of the calendar
+	*/
+	function createCalendar() {
+		var tbody, tr, td;
+		self.calendar = document.createElement('table');
+		self.calendar.setAttribute('id',self.name+'_calendar');
+		setClass(self.calendar,'calendar');
+		self.calendar.style.display = 'none'; //default to invisible
+		//to prevent IE from selecting text when clicking on the calendar
+		addEventHandler(self.calendar,'selectstart', function() {return false;});
+		addEventHandler(self.calendar,'drag', function() {return false;});
+		tbody = document.createElement('tbody');
+
+		//create the Main Calendar Heading
+		tr = document.createElement('tr');
+		td = document.createElement('td');
+		td.appendChild(createMainHeading());
+		tr.appendChild(td);
+		tbody.appendChild(tr);
+
+		//create the calendar Day Heading & the calendar Day Cells
+		tr = document.createElement('tr');
+		td = document.createElement('td');
+		self.calendar.celltable = document.createElement('table');
+		setClass(self.calendar.celltable,'cells');
+		self.calendar.celltable.appendChild(createDayHeading());
+		self.calendar.celltable.appendChild(createCalCells());
+		td.appendChild(self.calendar.celltable);
+		tr.appendChild(td);
+		tbody.appendChild(tr);
+
+		//create the calendar footer
+		tr = document.createElement('tr');
+		td = document.createElement('td');
+		td.appendChild(createFooter());
+		tr.appendChild(td);
+		tbody.appendChild(tr);
+
+		//add the tbody element to the main calendar table
+		self.calendar.appendChild(tbody);
+
+		//and add the onmouseover events to the calendar table
+		addEventHandler(self.calendar,'mouseover',cal_onmouseover);
+		addEventHandler(self.calendar,'mouseout',cal_onmouseout);
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the primary calendar heading, with months & years
+	*/
+	function createMainHeading() {
+		//create the containing <div> element
+		var container = document.createElement('div');
+		setClass(container,'mainheading');
+		//create the child elements and other variables
+		self.monthSelect = document.createElement('select');
+		self.yearSelect = document.createElement('select');
+		var monthDn = document.createElement('input'), monthUp = document.createElement('input');
+		var opt, i;
+		//fill the month select box
+		for(i=0;i<12;i++) {
+			opt = document.createElement('option');
+			opt.setAttribute('value',i);
+			if(self.displayMonth == i) {
+				opt.setAttribute('selected','selected');
+			}
+			opt.appendChild(document.createTextNode(self.months_sh[i]));
+			self.monthSelect.appendChild(opt);
+		}
+		//and fill the year select box
+		var yrMax = self.maxDate.getFullYear(), yrMin = self.minDate.getFullYear();
+		for(i=yrMin;i<=yrMax;i++) {
+			opt = document.createElement('option');
+			opt.setAttribute('value',i);
+			if(self.displayYear == i) {
+				opt.setAttribute('selected','selected');
+			}
+			opt.appendChild(document.createTextNode(i));
+			self.yearSelect.appendChild(opt);
+		}
+		//add the appropriate children for the month buttons
+		monthUp.setAttribute('type','button');
+		monthUp.setAttribute('value','>');
+		monthUp.setAttribute('title',self.monthup_title);
+		monthDn.setAttribute('type','button');
+		monthDn.setAttribute('value','<');
+		monthDn.setAttribute('title',self.monthdn_title);
+		self.monthSelect.owner = self.yearSelect.owner = monthUp.owner = monthDn.owner = self;  //hack to allow us to access self calendar in the events (<fix>??)
+
+		//assign the event handlers for the controls
+		function selectonchange()	{
+			if(self.goToMonth(self.yearSelect.value,self.monthSelect.value)) {
+				self.displayMonth = self.monthSelect.value;
+				self.displayYear = self.yearSelect.value;
+			}
+			else {
+				self.monthSelect.value = self.displayMonth;
+				self.yearSelect.value = self.displayYear;
+			}
+		}
+		addEventHandler(monthUp,'click',function(){self.nextMonth();});
+		addEventHandler(monthDn,'click',function(){self.prevMonth();});
+		addEventHandler(self.monthSelect,'change',selectonchange);
+		addEventHandler(self.yearSelect,'change',selectonchange);
+
+		//and finally add the elements to the containing div
+		container.appendChild(monthDn);
+		container.appendChild(self.monthSelect);
+		container.appendChild(self.yearSelect);
+		container.appendChild(monthUp);
+		return container;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the footer of the calendar - goes under the calendar cells
+	*/
+	function createFooter() {
+		var container = document.createElement('div');
+		var clearSelected = document.createElement('input');
+		clearSelected.setAttribute('type','button');
+		clearSelected.setAttribute('value',self.clearbtn_caption);
+		clearSelected.setAttribute('title',self.clearbtn_title);
+		clearSelected.owner = self;
+		addEventHandler(clearSelected,'click',function() {self.resetSelections(false);});
+		container.appendChild(clearSelected);
+		if(self.mode == 'popup') {
+			var closeBtn = document.createElement('input');
+			closeBtn.setAttribute('type','button');
+			closeBtn.setAttribute('value',self.closebtn_caption);
+			closeBtn.setAttribute('title',self.closebtn_title);
+			addEventHandler(closeBtn,'click',function(){self.hide();});
+			setClass(closeBtn,'closeBtn');
+			container.appendChild(closeBtn);
+		}
+		return container;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the heading containing the day names
+	*/
+	function createDayHeading() {
+		//create the table element
+		self.calHeading = document.createElement('thead');
+		setClass(self.calHeading,'caldayheading');
+		var tr = document.createElement('tr'), th;
+		self.cols = new Array(false,false,false,false,false,false,false);
+
+		//if we're showing the week headings, create an empty <td> for filler
+		if(self.showWeeks) {
+			th = document.createElement('th');
+			setClass(th,'wkhead');
+			tr.appendChild(th);
+		}
+		//populate the day titles
+		for(var dow=0;dow<7;dow++) {
+			th = document.createElement('th');
+			th.appendChild(document.createTextNode(self.daynames[dow]));
+			if(self.selectMultiple) { //if selectMultiple is true, assign the cell a CalHeading Object to handle all events
+				th.headObj = new CalHeading(self,th,(dow + self.startDay < 7 ? dow + self.startDay : dow + self.startDay - 7));
+			}
+			tr.appendChild(th);
+		}
+		self.calHeading.appendChild(tr);
+		return self.calHeading;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Creates the table containing the calendar day cells
+	*/
+	function createCalCells() {
+		self.rows = new Array(false,false,false,false,false,false);
+		self.cells = new Array();
+		var row = -1, totalCells = (self.showWeeks ? 48 : 42);
+		var beginDate = new Date(self.displayYear,self.displayMonth,1);
+		var endDate = new Date(self.displayYear,self.displayMonth,self.monthDayCount[self.displayMonth]);
+		var sdt = new Date(beginDate);
+		sdt.setDate(sdt.getDate() + (self.startDay - beginDate.getDay()) - (self.startDay - beginDate.getDay() > 0 ? 7 : 0) );
+		//create the table element to hold the cells
+		self.calCells = document.createElement('tbody');
+		var tr,td;
+		var cellIdx = 0, cell, week, dayval;
+
+		for(var i=0;i<totalCells;i++) {
+			if(self.showWeeks) { //if we are showing the week headings
+				if(i % 8 == 0) {
+					row++;
+					week = sdt.getWeek(self.startDay);
+					tr = document.createElement('tr');
+					td = document.createElement('td');
+					if(self.selectMultiple) { //if selectMultiple is enabled, create the associated weekObj objects
+						td.weekObj = new WeekHeading(self,td,week,row)
+					}
+					else {//otherwise just set the class of the td for consistent look
+						setClass(td,'wkhead');
+					}
+					td.appendChild(document.createTextNode(week));
+					tr.appendChild(td);
+					i++;
+				}
+			}
+			else if(i % 7 == 0) { //otherwise, new row every 7 cells
+				row++;
+				week = sdt.getWeek(self.startDay);
+				tr = document.createElement('tr');
+			}
+			//create the day cells
+			dayval = sdt.getDate();
+			td = document.createElement('td');
+			td.appendChild(document.createTextNode(dayval));
+			cell = new CalCell(self,td,sdt,row,week);//,'normal',sdt.getTime() >= self.minDate.getTime() && sdt.getTime() <= self.maxDate.getTime());
+			self.cells[cellIdx] = cell;
+			td.cellObj = cell;
+			tr.appendChild(td);
+			self.calCells.appendChild(tr);
+			self.reDraw(cellIdx++); //and paint the cell according to its properties
+			sdt.setDate(dayval + 1); //increment the date
+		}
+		return self.calCells;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Runs all the operations necessary to change the mode of the calendar
+	* @param HTMLInputElement targetelement
+	*/
+	function setMode(targetelement)	{
+		if(self.mode == 'popup') { //set positioning to absolute for popup
+			self.calendar.style.position = 'absolute';
+		}
+		//if a target element has been set, append the calendar to it
+		if(targetelement) {
+			switch(self.mode) {
+				case 'flat':
+					self.tgt = targetelement;
+					self.tgt.appendChild(self.calendar);
+					self.visible = true;
+					break;
+				case 'popup':
+					self.calendar.style.position = 'absolute';
+					document.body.appendChild(self.calendar);
+					self.setTarget(targetelement,false);
+					break;
+			}
+		}
+		else { //otherwise, add the calendar to the document.body (useful if targetelement will not be defined until after the calendar is initialized)
+			document.body.appendChild(self.calendar);
+			self.visible = false;
+		}
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Removes the calendar table cells from the DOM (does not delete the cell objects associated with them)
+	*/
+	function deleteCells() {
+		self.calendar.celltable.removeChild(self.calendar.celltable.childNodes[1]); //remove the tbody element from the cell table
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the CSS class of the element, W3C & IE
+	* @param HTMLElement element
+	* @param string className
+	*/
+	function setClass(element,className) {
+		element.setAttribute('class',className);
+		element.setAttribute('className',className); //<iehack>
+	}
+	/**
+	* Updates a cell's data, including css class and selection properties
+	* @param int cellindex
+	*/
+	function setCellProperties(cellindex) {
+		var cell = self.cells[cellindex];
+		var date;
+		idx = self.dateInArray(self.dates,cell.date);
+		if(idx > -1) {
+			date = self.dates[idx]; //reduce indirection
+			cell.date.selected = date.selected || false;
+			cell.date.type = date.type;
+			cell.date.canSelect = date.canSelect;
+			cell.setTitle(date.title);
+			cell.setURL(date.href);
+			cell.setHTML(date.cellHTML);
+		}
+		else {
+			cell.date.selected = false; //if the cell's date isn't in the dates array, set it's selected value to false
+		}
+		//make all cells lying outside the min and max dates un-selectable
+		if(cell.date.getTime() < self.minDate.getTime() || cell.date.getTime() > self.maxDate.getTime()) {
+			cell.date.canSelect = false;
+		}
+		cell.setClass();
+	}
+	//-----------------------------------------------------------------------------
+	function cal_onmouseover() {
+		self.mousein = true;
+	}
+	//-----------------------------------------------------------------------------
+	function cal_onmouseout()	{
+		self.mousein = false;
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	 * Updates the calendar's selectedDates pointer array
+	 */
+	function updateSelectedDates() {
+		var idx = 0;
+		self.selectedDates = new Array();
+		for(i=0;i<self.dates.length;i++) {
+			if(self.dates[i].selected) {
+				self.selectedDates[idx++] = self.dates[i];
+			}
+		}
+	}
+	//PUBLIC METHODS
+	//-----------------------------------------------------------------------------
+	/**
+	* Find a date in the given array, returning its index if found, -1 if not
+	* @param array arr
+	* @param Date searchVal
+	* @param int startIndex
+	* @return int
+	*/
+	self.dateInArray = function(arr,searchVal,startIndex) {
+		startIndex = (startIndex != null ? startIndex : 0); //default startIndex to 0, if not set
+		for(var i=startIndex;i<arr.length;i++) {
+			if(searchVal.getUeDay() == arr[i].getUeDay()) {
+				return i;
+			}
+		}
+		return -1;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Changes the target element of this calendar to another input.
+	* Many thanks to Jake Olefsky - jake@olefsky.com
+	* @param HTMLInputElement targetelement
+	* @param bool focus
+	*/
+	self.setTarget = function (targetelement, focus)
+	{
+		//if this is a popup calendar
+		if(self.mode == 'popup') {
+			//declare the event handlers for the target element
+			function popupFocus() {
+				self.show();
+			}
+			function popupBlur() {
+				if(!self.mousein){
+					self.hide();
+				}
+			}
+			function popupKeyDown() {
+				self.hide();
+			}
+			//unset old target element event handlers (if there is one yet)
+			if(self.tgt) {
+				removeEventHandler(self.tgt,'focus',popupFocus);
+				removeEventHandler(self.tgt,'blur',popupBlur);
+				removeEventHandler(self.tgt,'keydown',popupKeyDown);
+			}
+			//and set the new target element
+			self.tgt = targetelement;
+			//create a pointer to the INPUT's date object and init the new data array
+			var dto = self.tgt.dateObj,pdateArr = new Array;
+			//if a date is set for the target element
+			if(dto) {
+				if(self.tgt.value.length) { //load it into the calendar...
+					pdateArr[0] = dto;
+				}
+				self.goToMonth(dto.getFullYear(),dto.getMonth()); //...and go to the target's month/year
+			}
+			self.selectDates(pdateArr,true,true,true);
+
+			self.topOffset = self.tgt.offsetHeight; // the vertical distance (in pixels) to display the calendar from the Top of its input element
+			self.leftOffset = 0; 					// the horizontal distance (in pixels) to display the calendar from the Left of its input element
+			self.updatePos(self.tgt);
+			//and add the event handlers to the new element
+			addEventHandler(self.tgt,'focus',popupFocus);
+			addEventHandler(self.tgt,'blur',popupBlur);
+			addEventHandler(self.tgt,'keydown',popupKeyDown);
+			if(focus !== false) { //focus the target element immediately, unless otherwise specified
+				popupFocus();
+			}
+		}
+		else { //if this is a flat or inline calendar
+			//if the target is already set, remove the calendar's DOM representation from it
+			if(self.tgt) {
+				self.tgt.removeChild(self.calendar);
+			}
+			//now, set the calendar's target to the new target element, and show the calendar
+			self.tgt = targetelement;
+			self.tgt.appendChild(self.calendar);
+			self.show();
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Go to the next month.  if the month is December, go to January of the next year
+	* Returns true if the month will be incremented
+	* @return bool
+	*/
+	self.nextMonth = function () {
+		var month = self.displayMonth;
+		var year = self.displayYear;
+		//increment the month/year values, provided they're within the min/max ranges
+		if(self.displayMonth < 11) { //i.e. if currently in the year
+			month++;
+		}
+		else if(self.yearSelect.value < self.maxDate.getFullYear()) { //if not, increment the year as well
+			month = 0;
+			year++;
+		}
+		return self.goToMonth(year,month);
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Go to the previous month - if the month is January, go to December of the previous year.
+	* Returns true if the month will be decremented
+	* @return bool
+	*/
+	self.prevMonth = function () {
+		var month = self.displayMonth;
+		var year = self.displayYear;
+		//increment the month/year values, provided they're within the min/max ranges
+		if(self.displayMonth > 0) { //i.e. if currently in the year
+			month--;
+		}
+		else { //if not, decrement the year as well
+			month = 11;
+			year--;
+		}
+		return self.goToMonth(year,month);
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the calendar to display the requested month/year, returning true if the
+	* date is within the minimum and maximum allowed dates
+	* @param int year
+	* @param int month
+	* @return bool
+	*/
+	self.goToMonth = function (year,month) {
+		var testdatemin = new Date(year, month, 31);
+		var testdatemax = new Date(year, month, 1);
+		if(testdatemin >= self.minDate && testdatemax <= self.maxDate) {
+			self.monthSelect.value = self.displayMonth = month;
+			self.yearSelect.value = self.displayYear = year;
+			//recreate the calendar for the new month
+			createCalCells();
+			deleteCells();
+			self.calendar.celltable.appendChild(self.calCells);
+			return true;
+		}
+		else {
+			alert(self.maxrange_caption);
+			return false;
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Moves the calendar's position to the target element's location (popup mode only)
+	*/
+	self.updatePos = function (target) {
+		if(self.mode == 'popup') {
+			self.calendar.style.top = getTop(target) + self.topOffset + 'px';
+			self.calendar.style.left = getLeft(target) + self.leftOffset + 'px';
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Displays the calendar
+	*/
+	self.show = function ()	{
+		self.updatePos(self.tgt); //update the calendar position, in case the page layout has changed since loading
+		self.calendar.style.display = 'block'; //'table'; //<iehack> 'table' is the W3C-recommended spec, but IE isn't a fan of those
+		self.visible = true;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Hides the calendar
+	*/
+	self.hide = function () {
+		self.calendar.style.display = 'none';
+		self.visible = false;
+		eval(m_afterHide +"('" + m_afterHideParam + "');");
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Toggles (shows/hides) the calendar depending on its current state
+	*/
+	self.toggle = function () {
+		self.visible ? self.hide() : self.show();
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Adds the array "dates" to the calendar's dates array, removing duplicate dates,
+	* and redraws the calendar if redraw is true
+	* @param array dates
+	* @param bool redraw
+	*/
+	self.addDates = function (dates,redraw) {
+		var i;
+		for(i=0;i<dates.length;i++) {
+			if(self.dateInArray(self.dates,dates[i]) == -1) { //if the date isn't already in the array, add it!
+				self.dates[self.dates.length] = dates[i];
+			}
+		}
+		//now rebuild the selectedDates pointer array
+		updateSelectedDates();
+		if(redraw != false) { //redraw  the calendar if "redraw" is false or undefined
+			self.reDraw();
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Removes the dates from the calendar's dates array and redraws the calendar
+	* if redraw is true
+	* @param array dates
+	* @param bool redraw
+	*/
+	self.removeDates = function (dates,redraw) {
+		var idx;
+		for(var i=0;i<dates.length;i++) {
+			idx = self.dateInArray(self.dates,dates[i]);
+			if(idx != -1) { //search for the dates in the dates array, removing them if the dates match
+				self.dates.splice(idx,1);
+			}
+		}
+		updateSelectedDates();
+		if(redraw != false) { //redraw  the calendar if "redraw" is true or undefined
+			self.reDraw();
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Selects or Deselects an array of dates
+	* @param Array inpdates
+	* @param bool selectVal
+	* @param bool redraw
+	* @param bool removeothers
+	*/
+	self.selectDates = function (inpdates,selectVal,redraw,removeothers) {
+		var i, idx;
+		if(removeothers == true) {
+			for(i=0;i<self.dates.length;i++) {
+				self.dates[i].selected = false;
+			}
+		}
+		for(i=0;i<inpdates.length;i++) {
+			idx = self.dateInArray(self.dates,inpdates[i]);
+			if(selectVal == true) {
+				inpdates[i].selected = true;
+				if(idx == -1) { //if the date does not exist in the calendar's dates array, add it
+					self.dates[self.dates.length] = inpdates[i];
+				}
+				else { //if not, just select it
+					self.dates[idx].selected = true;
+				}
+			}
+			else { //if deselecting...
+				if(idx > -1) { //if the date is found, deselect and/or remove it from the calendar's dates array
+					self.dates[idx].selected = inpdates[i].selected = false;
+					if(self.dates[idx].type == 'normal') { //remove 'normal' dates from the dates array, since they're useless unless selected
+						self.dates.splice(idx,1);
+					}
+				}
+			}
+		}
+		//now rebuild the selectedDates pointer array
+		updateSelectedDates();
+		if(redraw != false) { //redraw the calendar if "redraw" is false or undefined
+			self.reDraw();
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Adds the dates in dates as hidden inputs to the form "form".  inputname
+	* is the name of each hidden element. "form" can either be a pointer to the form's
+	* DOM element or its id string.
+	* @param mixed form
+	* @param string inputname
+	*/
+	self.sendForm = function(form,inputname) {
+		var inpname = inputname || 'epochdates', f, inp;
+		f = (typeof(form) == 'string' ? document.getElementById(form) : form);
+		if(!f) {
+			alert('ERROR: Invalid form input');
+			return false;
+		}
+		for(var i=0;i<self.dates.length;i++) {
+			inp = document.createElement('input');
+			inp.setAttribute('type','hidden');
+			inp.setAttribute('name',inpname + '['+i+']');
+			inp.setAttribute('value',encodeURIComponent(self.dates[i].dateFormat('Y-m-d')));  //default to the ISO date format
+			f.appendChild(inp);
+		}
+		return true;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Erases the dates array and resets the calendar's selection variables to defaults.
+	* If retMonth is true, the calendar will return to the initial default month/year
+	* @param bool retMonth
+	*/
+	self.resetSelections = function (retMonth) {
+		var dateArray = new Array();
+		var dt = self.dates;
+		for(var i=0;i<dt.length;i++) {
+			if(dt[i].selected) {
+				dateArray[dateArray.length] = dt[i];
+			}
+		}
+		self.selectDates(dateArray,false,false);
+		self.rows = new Array(false,false,false,false,false,false,false);
+		self.cols = new Array(false,false,false,false,false,false,false);
+		if(self.mode == 'popup') { //hide the calendar and clear the input element if in popup mode
+			self.tgt.value = '';
+			self.hide();
+		}
+		retMonth == true ? self.goToMonth(self.displayYearInitial,self.displayMonthInitial) : self.reDraw();
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Reapplies all the CSS classes for the calendar cells - usually called after changing their state
+	* If index is specified, it will redraw that cell only.
+	* @param int index
+	*/
+	self.reDraw = function (index) {
+		self.state = 1;
+		var len = index ? index + 1 : self.cells.length;
+		for(var i = index || 0;i<len;i++) {
+			setCellProperties(i);
+		}
+		self.state = 2;
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Returns the index of the cell whose date value matches "date", or -1 if not found
+	* @param Date date
+	* @return int
+	*/
+	self.getCellIndex = function(date) {
+		for(var i=0;i<self.cells.length;i++) {
+			if(self.cells[i].date.getUeDay() == date.getUeDay()) {
+				return i;
+			}
+		}
+		return -1;
+	};
+	//-----------------------------------------------------------------------------
+	//begin constructor code:
+
+	//PUBLIC VARIABLES
+	self.state = 0;
+	self.name = name;
+	self.curDate = new Date();
+	self.mode = mode;
+	self.selectMultiple = (multiselect == true); //'false' if not true or not set at all
+	//the various calendar variables
+	self.dates = new Array();
+	self.selectedDates = new Array();
+
+	self.calendar;
+	self.calHeading;
+	self.calCells;
+	self.rows;
+	self.cols;
+	self.cells = new Array();
+	//The controls
+	self.monthSelect;
+	self.yearSelect;
+	self.mousein = false;
+
+	//Initialize the calendar and its variables{
+	calConfig();
+	setLang();
+	setDays();
+	createCalendar(); //create the calendar DOM element and its children, and their related objects
+	targetelement = typeof(targetelement) == 'string' ? document.getElementById(targetelement) : targetelement;
+	setMode(targetelement);
+	self.state = 2; //0: initializing, 1: redrawing, 2: finished!
+	self.visible ? self.show() : self.hide();
+
+	self.afterHide = m_afterHide;
+	self.afterHideParam = m_afterHideParam;
+	self.dateformat = m_dateformat;
+}
+//-----------------------------------------------------------------------------
+/*****************************************************************************/
+/**
+* Object that contains the methods and properties for the calendar day headings
+*/
+function CalHeading(owner,tableCell,dayOfWeek) {
+	//-----------------------------------------------------------------------------
+	function DayHeadingonclick() {//selects/deselects the days for this object's day of week
+		//reduce indirection:
+		var sdates = owner.dates;
+		var cells = owner.cells;
+		var dateArray = new Array();
+		owner.cols[dayOfWeek] = !owner.cols[dayOfWeek];
+		for(var i=0;i<cells.length;i++) { //cycle through all the cells in the calendar, selecting all cells with the same dayOfWeek as this heading
+			if(cells[i].dayOfWeek == dayOfWeek && cells[i].date.canSelect && (!owner.selCurMonthOnly || cells[i].date.getMonth() == owner.displayMonth && cells[i].date.getFullYear() == owner.displayYear)) { //if the cell's DoW matches, with other conditions
+				dateArray[dateArray.length] = cells[i].date;
+			}
+		}
+		owner.selectDates(dateArray,owner.cols[dayOfWeek],true);
+	}
+	//-----------------------------------------------------------------------------
+	var self = this;
+	self.dayOfWeek = dayOfWeek;
+	addEventHandler(tableCell,'mouseup',DayHeadingonclick);
+}
+/*****************************************************************************/
+/**
+* Object that contains the methods and properties for the calendar week headings
+*/
+function WeekHeading(owner,tableCell,week,tableRow) {
+	//-----------------------------------------------------------------------------
+	function weekHeadingonclick() {
+		//reduce indirection:
+		var cells = owner.cells;
+		var sdates = owner.dates;
+		var dateArray = new Array();
+		owner.rows[tableRow] = !owner.rows[tableRow];
+		for(var i=0;i<cells.length;i++) {
+			if(cells[i].tableRow == tableRow && cells[i].date.canSelect && (!owner.selCurMonthOnly || cells[i].date.getMonth() == owner.displayMonth && cells[i].date.getFullYear() == owner.displayYear)) { //if the cell's DoW matches, with other conditions)
+				dateArray[dateArray.length] = cells[i].date;
+			}
+		}
+		owner.selectDates(dateArray,owner.rows[tableRow],true);
+	}
+	//-----------------------------------------------------------------------------
+	var self = this;
+	self.week = week;
+	tableCell.setAttribute('class','wkhead');
+	tableCell.setAttribute('className','wkhead'); //<iehack>
+	addEventHandler(tableCell,'mouseup',weekHeadingonclick);
+}
+/*****************************************************************************/
+/**
+* Object that holds all data & code related to a calendar cell
+*/
+/**
+ * The CalCell constructor function
+ * @param Epoch owner
+ * @param HTMLTableCellElement tableCell
+ * @param Date dateObj
+ * @param int row
+ * @param int week
+ */
+function CalCell(owner,tableCell,dateObj,row,week) {
+	var self = this;
+	//-----------------------------------------------------------------------------
+	function calCellonclick() {
+		if(self.date.canSelect) {
+			if(owner.selectMultiple == true) { //if we can select multiple cells simultaneously, add the currently selected self's date to the dates array
+				owner.selectDates(new Array(self.date),!self.date.selected,false);
+				self.setClass(); //update the current cell's style to reflect the changes - a full redraw isn't necessary
+			}
+			else { //if we can only select one date at a time
+				owner.selectDates(new Array(self.date),true,false,true);
+				if(owner.mode == 'popup') { //update the target element's value and hide the calendar if in popup mode
+					//owner.tgt.value = self.date.dateFormat(); //use the default date format defined in dateFormat
+					owner.tgt.value = self.date.dateFormat(owner.dateformat);
+					owner.tgt.dateObj = new Date(self.date); //add a Date object to the target element for later reference
+					owner.hide();
+				}
+				owner.reDraw(); //redraw all the calendar cells
+			}
+		}
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Replicate the CSS :hover effect for non-supporting browsers <iehack>
+	*/
+	function calCellonmouseover() {
+		if(self.date.canSelect) {
+			tableCell.setAttribute('class',self.cellClass + ' hover');
+			tableCell.setAttribute('className',self.cellClass + ' hover');
+		}
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Replicate the CSS :hover effect for non-supporting browsers <iehack>
+	*/
+	function calCellonmouseout() {
+		self.setClass();
+	}
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the CSS class of the cell based on the specified criteria
+	*/
+	self.setClass = function ()
+	{
+		if(self.date.canSelect !== false) {
+			if(self.date.selected) {
+				self.cellClass = 'cell_selected';
+			}
+			else if(owner.displayMonth != self.date.getMonth() ) {
+				self.cellClass = 'notmnth';
+			}
+			else if(self.date.type == 'holiday') {
+				self.cellClass = 'hlday';
+			}
+			else if(self.dayOfWeek > 0 && self.dayOfWeek < 6) {
+				self.cellClass = 'wkday';
+			}
+			else {
+				self.cellClass = 'wkend';
+			}
+		}
+		else {
+			self.cellClass = 'noselect';
+		}
+		//highlight the current date
+		if(self.date.getUeDay() == owner.curDate.getUeDay()) {
+			self.cellClass = self.cellClass + ' curdate';
+		}
+		tableCell.setAttribute('class',self.cellClass);
+		tableCell.setAttribute('className',self.cellClass); //<iehack>
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the cell's hyperlink, if declared
+	* @param string href
+	* @param string type ('anchor' or 'js' - default 'anchor')
+	*/
+	self.setURL = function(href,type) {
+		if(href) {
+			if(type == 'js') { //Make the WHOLE cell be a clickable link
+				addEventHandler(self.tableCell,'mousedown',function(){window.location.href = href;});
+			}
+			else { //make only the date number of the cell a clickable link:
+				var url = document.createElement('a');
+				url.setAttribute('href',href);
+				url.appendChild(document.createTextNode(self.date.getDate()));
+				self.tableCell.replaceChild(url,self.tableCell.firstChild); //assumes the first child of the cell DOM node is the date text
+			}
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the title (i.e. tooltip) that appears when a user holds their mouse cursor over a cell
+	* @param string titleStr
+	*/
+	self.setTitle = function(titleStr) {
+		if(titleStr && titleStr.length > 0) {
+			self.title = titleStr;
+			self.tableCell.setAttribute('title',titleStr);
+		}
+	};
+	//-----------------------------------------------------------------------------
+	/**
+	* Sets the internal html of the cell, using a string containing html markup
+	* @param string html
+	*/
+	self.setHTML = function(html) {
+		if(html && html.length > 0) {
+			if(self.tableCell.childNodes[1]) {
+				self.tableCell.childNodes[1].innerHTML = html;
+			}
+			else {
+				var htmlCont = document.createElement('div');
+				htmlCont.innerHTML = html;
+				self.tableCell.appendChild(htmlCont);
+			}
+		}
+	};
+	//-----------------------------------------------------------------------------
+	self.cellClass;			//the CSS class of the cell
+	self.tableRow = row;
+	self.tableCell = tableCell;
+	self.date = new Date(dateObj);
+	self.date.canSelect = true; //whether this cell can be selected or not - always true unless set otherwise externally
+	self.date.type = 'normal';  //i.e. normal date, holiday, etc - always true unless set otherwise externally
+	self.date.selected = false;	//whether the cell is selected (and is therefore stored in the owner's dates array)
+	self.date.cellHTML = '';
+	self.dayOfWeek = self.date.getDay();
+	self.week = week;
+	//assign the event handlers for the table cell element
+	addEventHandler(tableCell,'click', calCellonclick);
+	addEventHandler(tableCell,'mouseover', calCellonmouseover);
+	addEventHandler(tableCell,'mouseout', calCellonmouseout);
+	self.setClass();
+}
+/*****************************************************************************/
+Date.prototype.getDayOfYear = function () //returns the day of the year for this date
+{
+	return parseInt((this.getTime() - new Date(this.getFullYear(),0,1).getTime())/86400000 + 1);
+};
+//-----------------------------------------------------------------------------
+/**
+ * Returns the week number for this date.  dowOffset is the day of week the week
+ * "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday),
+ * the week returned is the ISO 8601 week number.
+ * @param int dowOffset
+ * @return int
+ */
+Date.prototype.getWeek = function (dowOffset) {
+	dowOffset = typeof(dowOffset) == 'int' ? dowOffset : 0; //default dowOffset to zero
+	var newYear = new Date(this.getFullYear(),0,1);
+	var day = newYear.getDay() - dowOffset; //the day of week the year begins on
+	day = (day >= 0 ? day : day + 7);
+	var weeknum, daynum = Math.floor((this.getTime() - newYear.getTime() - (this.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1;
+	//if the year starts before the middle of a week
+	if(day < 4) {
+		weeknum = Math.floor((daynum+day-1)/7) + 1;
+		if(weeknum > 52) {
+			nYear = new Date(this.getFullYear() + 1,0,1);
+			nday = nYear.getDay() - dowOffset;
+			nday = nday >= 0 ? nday : nday + 7;
+			weeknum = nday < 4 ? 1 : 53; //if the next year starts before the middle of the week, it is week #1 of that year
+		}
+	}
+	else {
+		weeknum = Math.floor((daynum+day-1)/7);
+	}
+	return weeknum;
+};
+//-----------------------------------------------------------------------------
+Date.prototype.getUeDay = function () //returns the number of DAYS since the UNIX Epoch - good for comparing the date portion
+{
+	return parseInt(Math.floor((this.getTime() - this.getTimezoneOffset() * 60000)/86400000)); //must take into account the local timezone
+};
+//-----------------------------------------------------------------------------
+var __DATE_FORMAT = 'm/d/Y';
+Date.prototype.dateFormat = function(format)
+{
+	if(!format) { // the default date format to use - can be customized to the current locale
+		format = __DATE_FORMAT;
+	}
+	LZ = function(x) {return(x < 0 || x > 9 ? '' : '0') + x};
+	var MONTH_NAMES = new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+	var DAY_NAMES = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+	var result="";
+	var i_format=0;
+	var c="";
+	var token="";
+	var y=this.getFullYear().toString();
+	var M=this.getMonth()+1;
+	var d=this.getDate();
+	var E=this.getDay();
+	var H=this.getHours();
+	var m=this.getMinutes();
+	var s=this.getSeconds();
+	value = {
+		Y: y.toString(),
+		y: y.substring(2),
+		n: M,
+		m: LZ(M),
+		F: MONTH_NAMES[M-1],
+		M: MONTH_NAMES[M+11],
+		j: d,
+		d: LZ(d),
+		D: DAY_NAMES[E+7],
+		l: DAY_NAMES[E],
+		G: H,
+		H: LZ(H)
+	};
+	if (H==0) {value['g']=12;}
+	else if (H>12){value['g']=H-12;}
+	else {value['g']=H;}
+	value['h']=LZ(value['g']);
+	if (H > 11) {value['a']='pm'; value['A'] = 'PM';}
+	else { value['a']='am'; value['A'] = 'AM';}
+	value['i']=LZ(m);
+	value['s']=LZ(s);
+	//construct the result string
+	while (i_format < format.length) {
+		c=format.charAt(i_format);
+		token="";
+		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
+			token += format.charAt(i_format++);
+		}
+		if (value[token] != null) { result=result + value[token]; }
+		else { result=result + token; }
+	}
+	return result;
+};
+/*****************************************************************************/
+//-----------------------------------------------------------------------------
+function addEventHandler(element, type, func) { //unfortunate hack to deal with Internet Explorer's horrible DOM event model <iehack>
+	if(element.addEventListener) {
+		element.addEventListener(type,func,false);
+	}
+	else if (element.attachEvent) {
+		element.attachEvent('on'+type,func);
+	}
+}
+//-----------------------------------------------------------------------------
+function removeEventHandler(element, type, func) { //unfortunate hack to deal with Internet Explorer's horrible DOM event model <iehack>
+	if(element.removeEventListener) {
+		element.removeEventListener(type,func,false);
+	}
+	else if (element.attachEvent) {
+		element.detachEvent('on'+type,func);
+	}
+}
+//-----------------------------------------------------------------------------
+function getTop(element) {//returns the absolute Top value of element, in pixels
+	var oNode = element;
+	var iTop = 0;
+
+	while(oNode.tagName != 'HTML') {
+		iTop += oNode.offsetTop || 0;
+		if(oNode.offsetParent) { //i.e. the parent element is not hidden
+			oNode = oNode.offsetParent;
+		}
+		else {
+			break;
+		}
+	}
+	return iTop;
+}
+//-----------------------------------------------------------------------------
+function getLeft(element) { //returns the absolute Left value of element, in pixels
+	var oNode = element;
+	var iLeft = 0;
+	while(oNode.tagName != 'HTML') {
+		iLeft += oNode.offsetLeft || 0;
+		if(oNode.offsetParent) { //i.e. the parent element is not hidden
+			oNode = oNode.offsetParent;
+		}
+		else {
+			break;
+		}
+	}
+	return iLeft;
+}
+//-----------------------------------------------------------------------------
Index: /afridex/plugins/Flutter/js/util.js
===================================================================
--- /afridex/plugins/Flutter/js/util.js (revision 21)
+++ /afridex/plugins/Flutter/js/util.js (revision 21)
@@ -0,0 +1,273 @@
+// small but works-for-me stuff for testing javascripts
+// not ready for "production" use
+
+Object.inspect = function(obj) {
+  var info = [];
+  
+  if(typeof obj=="string" || 
+     typeof obj=="number") {
+    return obj;
+  } else {
+    for(property in obj)
+      if(typeof obj[property]!="function")
+        info.push(property + ' => "' + obj[property] + '"');
+  }
+  
+  return ("'" + obj + "' #" + typeof obj + 
+    ": {" + info.join(", ") + "}");
+}
+
+// borrowed from http://www.schuerig.de/michael/javascript/stdext.js
+// Copyright (c) 2005, Michael Schuerig, michael@schuerig.de
+
+Array.flatten = function(array, excludeUndefined) {
+  if (excludeUndefined === undefined) {
+    excludeUndefined = false;
+  }
+  var result = [];
+  var len = array.length;
+  for (var i = 0; i < len; i++) {
+    var el = array[i];
+    if (el instanceof Array) {
+      var flat = el.flatten(excludeUndefined);
+      result = result.concat(flat);
+    } else if (!excludeUndefined || el != undefined) {
+      result.push(el);
+    }
+  }
+  return result;
+};
+
+if (!Array.prototype.flatten) {
+  Array.prototype.flatten = function(excludeUndefined) {
+    return Array.flatten(this, excludeUndefined);
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Builder = {
+  node: function(elementName) {
+    var element = document.createElement(elementName);
+
+    // attributes (or text)
+
+    if(arguments[1]) {
+      if(this._isStringOrNumber(arguments[1]) ||
+         (arguments[1] instanceof Array))
+        this._children(element, arguments[1]);
+      else
+        this._attributes(element, arguments[1]);
+    }
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+  _attributes: function(element, attributes) {
+    for(attribute in attributes)
+      if(this._isStringOrNumber(attributes[attribute]))
+        element.setAttribute(
+          attribute=='className' ? 'class' : attribute,
+          attributes[attribute]);
+  },
+  _children: function(element, children) {
+    if(typeof children=='object') { // array can hold nodes and text
+      children = children.flatten();
+      for(var i = 0; i<children.length; i++)
+        if(typeof children[i]=='object')
+          element.appendChild(children[i]);
+        else
+          if(this._isStringOrNumber(children[i]))
+            element.appendChild(this._text(children[i]));
+    } else
+      if(this._isStringOrNumber(children)) 
+         element.appendChild(this._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  }
+}
+
+/* ------------- element ext -------------- */
+
+// adapted from http://dhtmlkitchen.com/learn/js/setstyle/index4.jsp
+// note: Safari return null on elements with display:none; see http://bugzilla.opendarwin.org/show_bug.cgi?id=4125
+// instead of "auto" values returns null so it's easier to use with || constructs
+
+String.prototype.camelize = function() {
+  var oStringList = this.split('-');
+  if(oStringList.length == 1)    
+    return oStringList[0];
+  var ret = this.indexOf("-") == 0 ? 
+    oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0];
+  for(var i = 1, len = oStringList.length; i < len; i++){
+    var s = oStringList[i];
+    ret += s.charAt(0).toUpperCase() + s.substring(1)
+  }
+  return ret;
+}
+
+Element.getStyle = function(element, style) {
+  element = $(element);
+  var value = element.style[style.camelize()];
+  if(!value)
+    if(document.defaultView && document.defaultView.getComputedStyle) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = (css!=null) ? css.getPropertyValue(style) : null;
+    } else if(element.currentStyle) {
+      value = element.currentStyle[style.camelize()];  
+    }
+  if(value=='auto') value = null;
+  return value;
+}
+
+Element.makePositioned = function(element) {
+  element = $(element);
+  if(Element.getStyle(element, 'position')=='static')
+    element.style.position = "relative";
+}
+
+Element.makeClipping = function(element) {
+  element = $(element);
+  element._overflow = Element.getStyle(element, 'overflow') || 'visible';
+  if(element._overflow!='hidden') element.style.overflow = 'hidden';
+}
+
+Element.undoClipping = function(element) {
+  element = $(element);
+  if(element._overflow!='hidden') element.style.overflow = element._overflow;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Position.absolutize = function(element) {
+  element = $(element);
+  if(element.style.position=='absolute') return;
+  Position.prepare();
+
+  var offsets = Position.cumulativeOffset(element);
+  var top     = offsets[1];
+  var left    = offsets[0];
+  var width   = element.clientWidth;
+  var height  = element.clientHeight;
+
+  element._originalLeft   = left - parseFloat(element.style.left  || 0);
+  element._originalTop    = top  - parseFloat(element.style.top || 0);
+  element._originalWidth  = element.style.width;
+  element._originalHeight = element.style.height;
+
+  element.style.position = 'absolute';
+  element.style.top    = top + 'px';;
+  element.style.left   = left + 'px';;
+  element.style.width  = width + 'px';;
+  element.style.height = height + 'px';;
+}
+
+Position.relativize = function(element) {
+  element = $(element);
+  if(element.style.position=='relative') return;
+  Position.prepare();
+
+  element.style.position = 'relative';
+  var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+  var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+  element.style.top    = top + 'px';
+  element.style.left   = left + 'px';
+  element.style.height = element._originalHeight;
+  element.style.width  = element._originalWidth;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Element.Class = {
+    // Element.toggleClass(element, className) toggles the class being on/off
+    // Element.toggleClass(element, className1, className2) toggles between both classes,
+    //   defaulting to className1 if neither exist
+    toggle: function(element, className) {
+      if(Element.Class.has(element, className)) {
+        Element.Class.remove(element, className);
+        if(arguments.length == 3) Element.Class.add(element, arguments[2]);
+      } else {
+        Element.Class.add(element, className);
+        if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
+      }
+    },
+
+    // gets space-delimited classnames of an element as an array
+    get: function(element) {
+      element = $(element);
+      return element.className.split(' ');
+    },
+
+    // functions adapted from original functions by Gavin Kistner
+    remove: function(element) {
+      element = $(element);
+      var regEx;
+      for(var i = 1; i < arguments.length; i++) {
+        regEx = new RegExp("^" + arguments[i] + "\\b\\s*|\\s*\\b" + arguments[i] + "\\b", 'g');
+        element.className = element.className.replace(regEx, '')
+      }
+    },
+
+    add: function(element) {
+      element = $(element);
+      for(var i = 1; i < arguments.length; i++) {
+        Element.Class.remove(element, arguments[i]);
+        element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
+      }
+    },
+
+    // returns true if all given classes exist in said element
+    has: function(element) {
+      element = $(element);
+      if(!element || !element.className) return false;
+      var regEx;
+      for(var i = 1; i < arguments.length; i++) {
+        regEx = new RegExp("\\b" + arguments[i] + "\\b");
+        if(!regEx.test(element.className)) return false;
+      }
+      return true;
+    },
+
+    // expects arrays of strings and/or strings as optional paramters
+    // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
+    has_any: function(element) {
+      element = $(element);
+      if(!element || !element.className) return false;
+      var regEx;
+      for(var i = 1; i < arguments.length; i++) {
+        if((typeof arguments[i] == 'object') && 
+          (arguments[i].constructor == Array)) {
+          for(var j = 0; j < arguments[i].length; j++) {
+            regEx = new RegExp("\\b" + arguments[i][j] + "\\b");
+            if(regEx.test(element.className)) return true;
+          }
+        } else {
+          regEx = new RegExp("\\b" + arguments[i] + "\\b");
+          if(regEx.test(element.className)) return true;
+        }
+      }
+      return false;
+    },
+
+    childrenWith: function(element, className) {
+      var children = $(element).getElementsByTagName('*');
+      var elements = new Array();
+
+      for (var i = 0; i < children.length; i++) {
+        if (Element.Class.has(children[i], className)) {
+          elements.push(children[i]);
+          break;
+        }
+      }
+
+      return elements;
+    }
+}
Index: /afridex/plugins/Flutter/js/nicEdit.js
===================================================================
--- /afridex/plugins/Flutter/js/nicEdit.js (revision 21)
+++ /afridex/plugins/Flutter/js/nicEdit.js (revision 21)
@@ -0,0 +1,93 @@
+/* NicEdit - Micro Inline WYSIWYG
+ * Copyright 2007-2008 Brian Kirchoff
+ *
+ * NicEdit is distributed under the terms of the MIT license
+ * For more information visit http://nicedit.com/
+ * Do not remove this copyright message
+ */
+var bkExtend=function(){var A=arguments;if(A.length==1){A=[this,A[0]]}for(var B in A[1]){A[0][B]=A[1][B]}return A[0]};function bkClass(){}bkClass.prototype.construct=function(){};bkClass.extend=function(C){var A=function(){if(arguments[0]!==bkClass){return this.construct.apply(this,arguments)}};var B=new this(bkClass);bkExtend(B,C);A.prototype=B;A.extend=this.extend;return A};var bkElement=bkClass.extend({construct:function(A){if(typeof (A)=="string"){A=document.createElement(A)}A=$BK(A);return A},appendTo:function(A){A.appendChild(this);return this},appendBefore:function(A){A.parentNode.insertBefore(this,A);return this},addEvent:function(B,A){bkLib.addEvent(this,B,A);return this},setContent:function(A){this.innerHTML=A;return this},pos:function(){var C=curtop=0;var B=obj=this;if(obj.offsetParent){do{C+=obj.offsetLeft;curtop+=obj.offsetTop}while(obj=obj.offsetParent)}var A=(!window.opera)?parseInt(this.getStyle("border-width")||this.style.border)||0:0;return[C+A,curtop+A+this.offsetHeight]},noSelect:function(){bkLib.noSelect(this);return this},parentTag:function(A){var B=this;do{if(B&&B.nodeName&&B.nodeName.toUpperCase()==A){return B}B=B.parentNode}while(B);return false},hasClass:function(A){return this.className.match(new RegExp("(\\s|^)nicEdit-"+A+"(\\s|$)"))},addClass:function(A){if(!this.hasClass(A)){this.className+=" nicEdit-"+A}return this},removeClass:function(A){if(this.hasClass(A)){this.className=this.className.replace(new RegExp("(\\s|^)nicEdit-"+A+"(\\s|$)")," ")}return this},setStyle:function(A){var B=this.style;for(var C in A){switch(C){case"float":B.cssFloat=B.styleFloat=A[C];break;case"opacity":B.opacity=A[C];B.filter="alpha(opacity="+Math.round(A[C]*100)+")";break;case"className":this.className=A[C];break;default:B[C]=A[C]}}return this},getStyle:function(A,C){var B=(!C)?document.defaultView:C;if(this.nodeType==1){return(B&&B.getComputedStyle)?B.getComputedStyle(this,null).getPropertyValue(A):this.currentStyle[bkLib.camelize(A)]}},remove:function(){this.parentNode.removeChild(this);return this},setAttributes:function(A){for(var B in A){this[B]=A[B]}return this}});var bkLib={isMSIE:(navigator.appVersion.indexOf("MSIE")!=-1),addEvent:function(C,B,A){(C.addEventListener)?C.addEventListener(B,A,false):C.attachEvent("on"+B,A)},toArray:function(C){var B=C.length,A=new Array(B);while(B--){A[B]=C[B]}return A},noSelect:function(B){if(B.setAttribute&&B.nodeName.toLowerCase()!="input"&&B.nodeName.toLowerCase()!="textarea"){B.setAttribute("unselectable","on")}for(var A=0;A<B.childNodes.length;A++){bkLib.noSelect(B.childNodes[A])}},camelize:function(A){return A.replace(/\-(.)/g,function(B,C){return C.toUpperCase()})},inArray:function(A,B){return(bkLib.search(A,B)!=null)},search:function(A,C){for(var B=0;B<A.length;B++){if(A[B]==C){return B}}return null},cancelEvent:function(A){A=A||window.event;if(A.preventDefault&&A.stopPropagation){A.preventDefault();A.stopPropagation()}return false},domLoad:[],domLoaded:function(){if(arguments.callee.done){return }arguments.callee.done=true;for(i=0;i<bkLib.domLoad.length;i++){bkLib.domLoad[i]()}},onDomLoaded:function(A){this.domLoad.push(A);if(document.addEventListener){document.addEventListener("DOMContentLoaded",bkLib.domLoaded,null)}else{if(bkLib.isMSIE){document.write("<script id=__ie_onload defer "+((location.protocol=="https:")?"src='javascript:void(0)'":"src=//0")+"><\/script>");$BK("__ie_onload").onreadystatechange=function(){if(this.readyState=="complete"){bkLib.domLoaded()}}}}window.onload=bkLib.domLoaded}};function $BK(A){if(typeof (A)=="string"){A=document.getElementById(A)}return(A&&!A.appendTo)?bkExtend(A,bkElement.prototype):A}var bkEvent={addEvent:function(A,B){if(B){this.eventList=this.eventList||{};this.eventList[A]=this.eventList[A]||[];this.eventList[A].push(B)}return this},fireEvent:function(){var A=bkLib.toArray(arguments),C=A.shift();if(this.eventList&&this.eventList[C]){for(var B=0;B<this.eventList[C].length;B++){this.eventList[C][B].apply(this,A)}}}};function __(A){return A}Function.prototype.closure=function(){var A=this,B=bkLib.toArray(arguments),C=B.shift();return function(){if(typeof (bkLib)!="undefined"){return A.apply(C,B.concat(bkLib.toArray(arguments)))}}};Function.prototype.closureListener=function(){var A=this,C=bkLib.toArray(arguments),B=C.shift();return function(E){E=E||window.event;if(E.target){var D=E.target}else{var D=E.srcElement}return A.apply(B,[E,D].concat(C))}};
+
+
+
+var nicEditorConfig = bkClass.extend({
+	buttons : {
+		'bold' : {name : __('Click to Bold'), command : 'Bold', tags : ['B','STRONG'], css : {'font-weight' : 'bold'}, key : 'b'},
+		'italic' : {name : __('Click to Italic'), command : 'Italic', tags : ['EM','I'], css : {'font-style' : 'italic'}, key : 'i'},
+		'underline' : {name : __('Click to Underline'), command : 'Underline', tags : ['U'], css : {'text-decoration' : 'underline'}, key : 'u'},
+		'left' : {name : __('Left Align'), command : 'justifyleft', noActive : true},
+		'center' : {name : __('Center Align'), command : 'justifycenter', noActive : true},
+		'right' : {name : __('Right Align'), command : 'justifyright', noActive : true},
+		'ol' : {name : __('Insert Ordered List'), command : 'insertorderedlist', tags : ['OL']},
+		'ul' : 	{name : __('Insert Unordered List'), command : 'insertunorderedlist', tags : ['UL']},
+		'subscript' : {name : __('Click to Subscript'), command : 'subscript', tags : ['SUB']},
+		'superscript' : {name : __('Click to Superscript'), command : 'superscript', tags : ['SUP']},
+		'strikethrough' : {name : __('Click to Strike Through'), command : 'strikeThrough', css : {'text-decoration' : 'line-through'}},
+		'indent' : {name : __('Indent Text'), command : 'indent', noActive : true},
+		'outdent' : {name : __('Remove Indent'), command : 'outdent', noActive : true},
+		'hr' : {name : __('Horizontal Rule'), command : 'insertHorizontalRule', noActive : true}
+	},
+	iconsPath : '../nicEditorIcons.gif',
+	buttonList : ['bold','italic','underline','left','center','right','ol','ul','fontSize','fontFamily','fontFormat','indent','outdent','link','image'],
+	iconList : {"bgcolor":1,"forecolor":2,"bold":3,"center":4,"hr":5,"indent":6,"italic":7,"justify":8,"left":9,"ol":10,"outdent":11,"right":12,"save":13,"strikethrough":14,"subscript":15,"superscript":16,"ul":17,"underline":18,"image":19,"link":20,"unlink":21,"close":22,"arrow":23}
+	
+});
+;
+var nicEditors={nicPlugins:[],editors:[],registerPlugin:function(B,A){this.nicPlugins.push({p:B,o:A})},allTextAreas:function(C){var A=document.getElementsByTagName("textarea");for(var B=0;B<A.length;B++){nicEditors.editors.push(new nicEditor(C).panelInstance(A[B]))}return nicEditors.editors},findEditor:function(C){var B=nicEditors.editors;for(var A=0;A<B.length;A++){if(B[A].instanceById(C)){return B[A]}}}};var nicEditor=bkClass.extend({construct:function(C){this.options=new nicEditorConfig();bkExtend(this.options,C);this.nicInstances=new Array();this.loadedPlugins=new Array();var A=nicEditors.nicPlugins;for(var B=0;B<A.length;B++){this.loadedPlugins.push(new A[B].p(this,A[B].o))}nicEditors.editors.push(this);bkLib.addEvent(document.body,"mousedown",this.selectCheck.closureListener(this))},panelInstance:function(B,C){B=this.checkReplace($BK(B));var A=new bkElement("DIV").setStyle({width:(parseInt(B.getStyle("width"))||B.clientWidth)+"px"}).appendBefore(B);this.setPanel(A);return this.addInstance(B,C)},checkReplace:function(B){var A=nicEditors.findEditor(B);if(A){A.removeInstance(B);A.removePanel()}return B},addInstance:function(B,C){B=this.checkReplace($BK(B));if(B.contentEditable||!!window.opera){var A=new nicEditorInstance(B,C,this)}else{var A=new nicEditorIFrameInstance(B,C,this)}this.nicInstances.push(A);this.fireEvent("add",A);return this},removeInstance:function(C){C=$BK(C);var B=this.nicInstances;for(var A=0;A<B.length;A++){if(B[A].e==C){B[A].remove();this.nicInstances.splice(A,1)}}},removePanel:function(A){if(this.nicPanel){this.nicPanel.remove();this.nicPanel=null}},instanceById:function(C){C=$BK(C);var B=this.nicInstances;for(var A=0;A<B.length;A++){if(B[A].e==C){return B[A]}}},setPanel:function(A){this.nicPanel=new nicEditorPanel($BK(A),this.options,this);this.fireEvent("panel",this.nicPanel);return this},nicCommand:function(B,A){if(this.selectedInstance){this.selectedInstance.nicCommand(B,A)}},getIcon:function(D,A){var C=this.options.iconList[D];var B=(A.iconFiles)?A.iconFiles[D]:"";return{backgroundImage:"url('"+((C)?this.options.iconsPath:B)+"')",backgroundPosition:((C)?((C-1)*-18):0)+"px 0px"}},selectCheck:function(C,A){var B=false;do{if(A.className&&A.className.indexOf("nicEdit")!=-1){return false}}while(A=A.parentNode);this.fireEvent("blur",this.selectedInstance,A);this.lastSelectedInstance=this.selectedInstance;this.selectedInstance=null;return false}});nicEditor=nicEditor.extend(bkEvent);
+var nicEditorInstance=bkClass.extend({isSelected:false,construct:function(F,D,C){this.ne=C;this.elm=this.e=F;this.options=D||{};newX=parseInt(F.getStyle("width"))||F.clientWidth;newY=parseInt(F.getStyle("height"))||F.clientHeight;this.initialHeight=newY-8;var G=(F.nodeName.toLowerCase()=="textarea");if(G||this.options.hasPanel){this.editorContain=new bkElement("DIV").setStyle({width:newX+"px",border:"1px solid #ccc",borderTop:0,overflowY:"auto",overflowX:"hidden",maxHeight:(this.ne.options.maxHeight)?this.ne.options.maxHeight+"px":null}).appendBefore(F);var A=new bkElement("DIV").setStyle({width:(newX-8)+"px",margin:"4px",minHeight:newY+"px"}).addClass("main").appendTo(this.editorContain);F.setStyle({display:"none"});if(G){A.setContent(F.value);this.copyElm=F;while(F=F.parentNode){if(F.nodeName=="FORM"){bkLib.addEvent(F,"submit",this.saveContent.closure(this))}}}else{A.innerHTML=F.innerHTML}var B=(bkLib.isMSIE&&!((typeof document.body.style.maxHeight!="undefined")&&document.compatMode=="CSS1Compat"));A.setStyle((B)?{height:newY+"px"}:{overflow:"hidden"});this.elm=A}this.ne.addEvent("blur",this.blur.closure(this));this.init();this.blur();try{this.nicCommand("styleWithCSS",true)}catch(E){}},init:function(){this.elm.setAttribute("contentEditable","true");if(this.getContent()==""){this.setContent("<br />")}this.instanceDoc=document.defaultView;this.elm.addEvent("mousedown",this.selected.closureListener(this)).addEvent("keypress",this.keyDown.closureListener(this)).addEvent("focus",this.selected.closure(this)).addEvent("blur",this.blur.closure(this)).addEvent("keyup",this.selected.closure(this))},remove:function(){this.saveContent();if(this.copyElm||this.options.hasPanel){this.editorContain.remove();this.e.setStyle({display:"block"});this.ne.removePanel()}this.disable();this.ne.fireEvent("removeInstance",this)},disable:function(){this.elm.setAttribute("contentEditable","false")},getSel:function(){return(window.getSelection)?window.getSelection():document.selection},getRng:function(){var A=this.getSel();if(!A){return null}return(A.rangeCount>0)?A.getRangeAt(0):A.createRange()},selRng:function(A,B){if(window.getSelection){B.removeAllRanges();B.addRange(A)}else{A.select()}},selElm:function(){var C=this.getRng();if(C.startContainer){var D=C.startContainer;if(C.cloneContents().childNodes.length==1){for(var B=0;B<D.childNodes.length;B++){var A=D.childNodes[B].ownerDocument.createRange();A.selectNode(D.childNodes[B]);if(C.compareBoundaryPoints(Range.START_TO_START,A)!=1&&C.compareBoundaryPoints(Range.END_TO_END,A)!=-1){return $BK(D.childNodes[B])}}}return $BK(D)}else{return $BK((this.getSel().type=="Control")?C.item(0):C.parentElement())}},saveRng:function(){this.savedRange=this.getRng();this.savedSel=this.getSel()},restoreRng:function(){if(this.savedRange){this.selRng(this.savedRange,this.savedSel)}},keyDown:function(C,B){if(C.keyCode==13){if(bkLib.isMSIE){var A=this.getRng();A.pasteHTML("<br />");A.collapse(false);A.select();return false}}if(C.ctrlKey){this.ne.fireEvent("key",this,C);if(C.preventDefault){C.preventDefault()}return false}},selected:function(C,A){if(!A){A=this.selElm()}if(!C.ctrlKey){var B=this.ne.selectedInstance;if(B!=this){if(B){this.ne.fireEvent("blur",B,A)}this.ne.selectedInstance=this;this.ne.fireEvent("focus",B,A)}this.ne.fireEvent("selected",B,A);this.isFocused=true;this.elm.addClass("selected")}return false},blur:function(){this.isFocused=false;this.elm.removeClass("selected")},saveContent:function(){if(this.copyElm||this.options.hasPanel){this.ne.fireEvent("save",this);(this.copyElm)?this.copyElm.value=this.getContent():this.e.innerHTML=this.getContent()}},getElm:function(){return this.elm},getContent:function(){this.content=this.getElm().innerHTML;this.ne.fireEvent("get",this);return this.content},setContent:function(A){this.content=A;this.ne.fireEvent("set",this);this.elm.innerHTML=this.content},nicCommand:function(B,A){document.execCommand(B,false,A)}});
+var nicEditorIFrameInstance=nicEditorInstance.extend({savedStyles:[],init:function(){var B=this.elm.innerHTML.replace(/^\s+|\s+$/g,"");this.elm.innerHTML="";(!B)?B="<br />":B;this.initialContent=B;this.elmFrame=new bkElement("iframe").setAttributes({src:"javascript:;",frameBorder:0,allowTransparency:"true",scrolling:"no"}).setStyle({height:"100px",width:"100%"}).addClass("frame").appendTo(this.elm);if(this.copyElm){this.elmFrame.setStyle({width:(this.elm.offsetWidth-4)+"px"})}var A={fontSize:"font-size",fontFamily:"font-family",fontWeight:"font-weight",color:"color"};for(itm in A){this.savedStyles[itm]=this.elm.getStyle(A[itm])}setTimeout(this.initFrame.closure(this),50)},disable:function(){this.elm.innerHTML=this.getContent()},initFrame:function(){this.frameDoc=$BK(this.elmFrame.contentWindow.document);this.frameDoc.designMode="on";this.frameDoc.open();this.frameDoc.write('<html><head></head><body id="nicEditContent" style="margin: 0 !important; background-color: transparent !important;">'+this.initialContent+"</body></html>");this.frameDoc.close();this.frameWin=$BK(this.elmFrame.contentWindow);this.frameContent=$BK(this.frameWin.document.body).setStyle(this.savedStyles);this.instanceDoc=this.frameWin.document.defaultView;this.heightUpdate();this.frameDoc.addEvent("mousedown",this.selected.closureListener(this)).addEvent("keyup",this.heightUpdate.closureListener(this)).addEvent("keydown",this.keyDown.closureListener(this)).addEvent("keyup",this.selected.closure(this))},getElm:function(){return this.frameContent},setContent:function(A){this.content=A;this.ne.fireEvent("set",this);this.frameContent.innerHTML=this.content;this.heightUpdate()},getSel:function(){return(this.frameWin)?this.frameWin.getSelection():this.frameDoc.selection},heightUpdate:function(){var A=this.frameContent.offsetHeight;this.elmFrame.style.height=Math.max(A,this.initialHeight)+"px"},nicCommand:function(B,A){this.frameDoc.execCommand(B,false,A);setTimeout(this.heightUpdate.closure(this),100)}});
+var nicEditorPanel=bkClass.extend({construct:function(E,B,A){this.elm=E;this.options=B;this.ne=A;this.panelButtons=new Array();this.buttonList=bkExtend([],this.ne.options.buttonList);this.panelContain=new bkElement("DIV").setStyle({overflow:"hidden",width:"100%",border:"1px solid #cccccc",backgroundColor:"#efefef"}).addClass("panelContain");this.panelElm=new bkElement("DIV").setStyle({margin:"2px",marginTop:"0px",zoom:1,overflow:"hidden"}).addClass("panel").appendTo(this.panelContain);this.panelContain.appendTo(E);var C=this.ne.options;var D=C.buttons;for(button in D){this.addButton(button,C,true)}this.reorder();E.noSelect()},addButton:function(buttonName,options,noOrder){var button=options.buttons[buttonName];var type=(button.type)?eval("(typeof("+button.type+') == "undefined") ? null : '+button.type+";"):nicEditorButton;var hasButton=bkLib.inArray(this.buttonList,buttonName);if(type&&(hasButton||this.ne.options.fullPanel)){this.panelButtons.push(new type(this.panelElm,buttonName,options,this.ne));if(!hasButton){this.buttonList.push(buttonName)}}},findButton:function(B){for(var A=0;A<this.panelButtons.length;A++){if(this.panelButtons[A].name==B){return this.panelButtons[A]}}},reorder:function(){var C=this.buttonList;for(var B=0;B<C.length;B++){var A=this.findButton(C[B]);if(A){this.panelElm.appendChild(A.margin)}}},remove:function(){this.elm.remove()}});
+var nicEditorButton=bkClass.extend({isDisabled:false,isHover:false,isActive:false,construct:function(D,A,C,B){this.options=C.buttons[A];this.name=A;this.ne=B;this.elm=D;this.margin=new bkElement("DIV").setStyle({"float":"left",marginTop:"2px"}).appendTo(D);this.contain=new bkElement("DIV").setStyle({width:"20px",height:"20px"}).addClass("buttonContain").appendTo(this.margin);this.border=new bkElement("DIV").setStyle({backgroundColor:"#efefef",border:"1px solid #efefef"}).appendTo(this.contain);this.button=new bkElement("DIV").setStyle({width:"18px",height:"18px",overflow:"hidden",zoom:1,cursor:"pointer"}).addClass("button").setStyle(this.ne.getIcon(A,C)).appendTo(this.border);this.button.addEvent("mouseover",this.hoverOn.closure(this)).addEvent("mouseout",this.hoverOff.closure(this)).addEvent("mousedown",this.mouseClick.closure(this)).noSelect();if(!window.opera){this.button.onmousedown=bkLib.cancelEvent;this.button.onclick=bkLib.cancelEvent}B.addEvent("selected",this.enable.closure(this)).addEvent("blur",this.disable.closure(this)).addEvent("key",this.key.closure(this));this.disable();this.init()},init:function(){},hide:function(){this.contain.setStyle({display:"none"})},updateState:function(){if(this.isDisabled){this.setBg()}else{if(this.isHover){this.setBg("hover")}else{if(this.isActive){this.setBg("active")}else{this.setBg()}}}},setBg:function(A){switch(A){case"hover":var B={border:"1px solid #666",backgroundColor:"#ddd"};break;case"active":var B={border:"1px solid #666",backgroundColor:"#ccc"};break;default:var B={border:"1px solid #efefef",backgroundColor:"#efefef"}}this.border.setStyle(B).addClass("button-"+A)},checkNodes:function(A){var B=A;do{if(this.options.tags&&bkLib.inArray(this.options.tags,B.nodeName)){this.activate();return true}}while(B=B.parentNode&&B.className!="nicEdit");B=$BK(A);while(B.nodeType==3){B=$BK(B.parentNode)}if(this.options.css){for(itm in this.options.css){if(B.getStyle(itm,this.ne.selectedInstance.instanceDoc)==this.options.css[itm]){this.activate();return true}}}this.deactivate();return false},activate:function(){if(!this.isDisabled){this.isActive=true;this.updateState();this.ne.fireEvent("buttonActivate",this)}},deactivate:function(){this.isActive=false;this.updateState();if(!this.isDisabled){this.ne.fireEvent("buttonDeactivate",this)}},enable:function(A,B){this.isDisabled=false;this.contain.setStyle({opacity:1}).addClass("buttonEnabled");this.updateState();this.checkNodes(B)},disable:function(A,B){this.isDisabled=true;this.contain.setStyle({opacity:0.6}).removeClass("buttonEnabled");this.updateState()},toggleActive:function(){(this.isActive)?this.deactivate():this.activate()},hoverOn:function(){if(!this.isDisabled){this.isHover=true;this.updateState();this.ne.fireEvent("buttonOver",this)}},hoverOff:function(){this.isHover=false;this.updateState();this.ne.fireEvent("buttonOut",this)},mouseClick:function(){if(this.options.command){this.ne.nicCommand(this.options.command,this.options.commandArgs);if(!this.options.noActive){this.toggleActive()}}this.ne.fireEvent("buttonClick",this)},key:function(A,B){if(this.options.key&&B.ctrlKey&&String.fromCharCode(B.keyCode).toLowerCase()==this.options.key){this.mouseClick()}}});
+var nicPlugin=bkClass.extend({construct:function(B,A){this.options=A;this.ne=B;this.ne.addEvent("panel",this.loadPanel.closure(this));this.init()},loadPanel:function(C){var B=this.options.buttons;for(var A in B){C.addButton(A,this.options)}C.reorder()},init:function(){}});
+
+
+var nicPaneOptions = { };
+
+var nicEditorPane=bkClass.extend({construct:function(D,C,B,A){this.ne=C;this.elm=D;this.pos=D.pos();this.contain=new bkElement("div").setStyle({zIndex:"99999",overflow:"hidden",position:"absolute",left:this.pos[0]+"px",top:this.pos[1]+"px"});this.pane=new bkElement("div").setStyle({fontSize:"12px",border:"1px solid #ccc",overflow:"hidden",padding:"4px",textAlign:"left",backgroundColor:"#ffffc9"}).addClass("pane").setStyle(B).appendTo(this.contain);if(A&&!A.options.noClose){this.close=new bkElement("div").setStyle({"float":"right",height:"16px",width:"16px",cursor:"pointer"}).setStyle(this.ne.getIcon("close",nicPaneOptions)).addEvent("mousedown",A.removePane.closure(this)).appendTo(this.pane)}this.contain.noSelect().appendTo(document.body);this.position();this.init()},init:function(){},position:function(){if(this.ne.nicPanel){var B=this.ne.nicPanel.elm;var A=B.pos();var C=A[0]+parseInt(B.getStyle("width"))-(parseInt(this.pane.getStyle("width"))+8);if(C<this.pos[0]){this.contain.setStyle({left:C+"px"})}}},toggle:function(){this.isVisible=!this.isVisible;this.contain.setStyle({display:((this.isVisible)?"block":"none")})},remove:function(){if(this.contain){this.contain.remove();this.contain=null}},append:function(A){A.appendTo(this.pane)},setContent:function(A){this.pane.setContent(A)}});
+
+var nicEditorAdvancedButton=nicEditorButton.extend({init:function(){this.ne.addEvent("selected",this.removePane.closure(this)).addEvent("blur",this.removePane.closure(this))},mouseClick:function(){if(!this.isDisabled){if(this.pane&&this.pane.pane){this.removePane()}else{this.pane=new nicEditorPane(this.contain,this.ne,{width:(this.width||"270px"),backgroundColor:"#fff"},this);this.addPane();this.ne.selectedInstance.saveRng()}}},addForm:function(C,G){this.form=new bkElement("form").addEvent("submit",this.submit.closureListener(this));this.pane.append(this.form);this.inputs={};for(itm in C){var D=C[itm];var F="";if(G){F=G.getAttribute(itm)}if(!F){F=D.value||""}var A=C[itm].type;if(A=="title"){new bkElement("div").setContent(D.txt).setStyle({fontSize:"14px",fontWeight:"bold",padding:"0px",margin:"2px 0"}).appendTo(this.form)}else{var B=new bkElement("div").setStyle({overflow:"hidden",clear:"both"}).appendTo(this.form);if(D.txt){new bkElement("label").setAttributes({"for":itm}).setContent(D.txt).setStyle({margin:"2px 4px",fontSize:"13px",width:"50px",lineHeight:"20px",textAlign:"right","float":"left"}).appendTo(B)}switch(A){case"text":this.inputs[itm]=new bkElement("input").setAttributes({id:itm,value:F,type:"text"}).setStyle({margin:"2px 0",fontSize:"13px","float":"left",height:"20px",border:"1px solid #ccc",overflow:"hidden"}).setStyle(D.style).appendTo(B);break;case"select":this.inputs[itm]=new bkElement("select").setAttributes({id:itm}).setStyle({border:"1px solid #ccc","float":"left",margin:"2px 0"}).appendTo(B);for(opt in D.options){var E=new bkElement("option").setAttributes({value:opt,selected:(opt==F)?"selected":""}).setContent(D.options[opt]).appendTo(this.inputs[itm])}break;case"content":this.inputs[itm]=new bkElement("textarea").setAttributes({id:itm}).setStyle({border:"1px solid #ccc","float":"left"}).setStyle(D.style).appendTo(B);this.inputs[itm].value=F}}}new bkElement("input").setAttributes({type:"submit"}).setStyle({backgroundColor:"#efefef",border:"1px solid #ccc",margin:"3px 0","float":"left",clear:"both"}).appendTo(this.form);this.form.onsubmit=bkLib.cancelEvent},submit:function(){},findElm:function(B,A,E){var D=this.ne.selectedInstance.getElm().getElementsByTagName(B);for(var C=0;C<D.length;C++){if(D[C].getAttribute(A)==E){return $BK(D[C])}}},removePane:function(){if(this.pane){this.pane.remove();this.pane=null;this.ne.selectedInstance.restoreRng()}}});
+
+var nicButtonTips=bkClass.extend({construct:function(A){this.ne=A;A.addEvent("buttonOver",this.show.closure(this)).addEvent("buttonOut",this.hide.closure(this))},show:function(A){this.timer=setTimeout(this.create.closure(this,A),400)},create:function(A){this.timer=null;if(!this.pane){this.pane=new nicEditorPane(A.button,this.ne,{fontSize:"12px",marginTop:"5px"});this.pane.setContent(A.options.name)}},hide:function(A){if(this.timer){clearTimeout(this.timer)}if(this.pane){this.pane=this.pane.remove()}}});nicEditors.registerPlugin(nicButtonTips);
+
+
+var nicSelectOptions = {
+	buttons : {
+		'fontSize' : {name : __('Select Font Size'), type : 'nicEditorFontSizeSelect', command : 'fontsize'},
+		'fontFamily' : {name : __('Select Font Family'), type : 'nicEditorFontFamilySelect', command : 'fontname'},
+		'fontFormat' : {name : __('Select Font Format'), type : 'nicEditorFontFormatSelect', command : 'formatBlock'}
+	}
+};
+
+var nicEditorSelect=bkClass.extend({construct:function(D,A,C,B){this.options=C.buttons[A];this.elm=D;this.ne=B;this.name=A;this.selOptions=new Array();this.margin=new bkElement("div").setStyle({"float":"left",margin:"2px 1px 0 1px"}).appendTo(this.elm);this.contain=new bkElement("div").setStyle({width:"90px",height:"20px",cursor:"pointer",overflow:"hidden"}).addClass("selectContain").addEvent("click",this.toggle.closure(this)).appendTo(this.margin);this.items=new bkElement("div").setStyle({overflow:"hidden",zoom:1,border:"1px solid #ccc",paddingLeft:"3px",backgroundColor:"#fff"}).appendTo(this.contain);this.control=new bkElement("div").setStyle({overflow:"hidden","float":"right",height:"18px",width:"16px"}).addClass("selectControl").setStyle(this.ne.getIcon("arrow",C)).appendTo(this.items);this.txt=new bkElement("div").setStyle({overflow:"hidden","float":"left",width:"66px",height:"14px",marginTop:"1px",fontFamily:"sans-serif",textAlign:"center",fontSize:"12px"}).addClass("selectTxt").appendTo(this.items);if(!window.opera){this.contain.onmousedown=this.control.onmousedown=this.txt.onmousedown=bkLib.cancelEvent}this.margin.noSelect();this.ne.addEvent("selected",this.enable.closure(this)).addEvent("blur",this.disable.closure(this));this.disable();this.init()},disable:function(){this.isDisabled=true;this.close();this.contain.setStyle({opacity:0.6})},enable:function(A){this.isDisabled=false;this.close();this.contain.setStyle({opacity:1})},setDisplay:function(A){this.txt.setContent(A)},toggle:function(){if(!this.isDisabled){(this.pane)?this.close():this.open()}},open:function(){this.pane=new nicEditorPane(this.items,this.ne,{width:"88px",padding:"0px",borderTop:0,borderLeft:"1px solid #ccc",borderRight:"1px solid #ccc",borderBottom:"0px",backgroundColor:"#fff"});for(var C=0;C<this.selOptions.length;C++){var B=this.selOptions[C];var A=new bkElement("div").setStyle({overflow:"hidden",borderBottom:"1px solid #ccc",width:"88px",textAlign:"left",overflow:"hidden",cursor:"pointer"});var D=new bkElement("div").setStyle({padding:"0px 4px"}).setContent(B[1]).appendTo(A).noSelect();D.addEvent("click",this.update.closure(this,B[0])).addEvent("mouseover",this.over.closure(this,D)).addEvent("mouseout",this.out.closure(this,D)).setAttributes("id",B[0]);this.pane.append(A);if(!window.opera){D.onmousedown=bkLib.cancelEvent}}},close:function(){if(this.pane){this.pane=this.pane.remove()}},over:function(A){A.setStyle({backgroundColor:"#ccc"})},out:function(A){A.setStyle({backgroundColor:"#fff"})},add:function(B,A){this.selOptions.push(new Array(B,A))},update:function(A){this.ne.nicCommand(this.options.command,A);this.close()}});var nicEditorFontSizeSelect=nicEditorSelect.extend({sel:{1:"1&nbsp;(8pt)",2:"2&nbsp;(10pt)",3:"3&nbsp;(12pt)",4:"4&nbsp;(14pt)",5:"5&nbsp;(18pt)",6:"6&nbsp;(24pt)"},init:function(){this.setDisplay("Font&nbsp;Size...");for(itm in this.sel){this.add(itm,'<font size="'+itm+'">'+this.sel[itm]+"</font>")}}});var nicEditorFontFamilySelect=nicEditorSelect.extend({sel:{arial:"Arial","comic sans ms":"Comic Sans","courier new":"Courier New",georgia:"Georgia",helvetica:"Helvetica",impact:"Impact","times new roman":"Times","trebuchet ms":"Trebuchet",verdana:"Verdana"},init:function(){this.setDisplay("Font&nbsp;Family...");for(itm in this.sel){this.add(itm,'<font face="'+itm+'">'+this.sel[itm]+"</font>")}}});var nicEditorFontFormatSelect=nicEditorSelect.extend({sel:{p:"Paragraph",pre:"Pre",h6:"Heading&nbsp;6",h5:"Heading&nbsp;5",h4:"Heading&nbsp;4",h3:"Heading&nbsp;3",h2:"Heading&nbsp;2",h1:"Heading&nbsp;1"},init:function(){this.setDisplay("Font&nbsp;Format...");for(itm in this.sel){var A=itm.toUpperCase();this.add("<"+A+">","<"+itm+' style="padding: 0px; margin: 0px;">'+this.sel[itm]+"</"+A+">")}}});nicEditors.registerPlugin(nicPlugin,nicSelectOptions);
+
+
+var nicLinkOptions = {
+	buttons : {
+		'link' : {name : 'Add Link', type : 'nicLinkButton', tags : ['A']},
+		'unlink' : {name : 'Remove Link',  command : 'unlink', noActive : true}
+	}
+};
+
+var nicLinkButton=nicEditorAdvancedButton.extend({addPane:function(){this.ln=this.ne.selectedInstance.selElm().parentTag("A");this.addForm({"":{type:"title",txt:"Add/Edit Link"},href:{type:"text",txt:"URL",value:"http://",style:{width:"150px"}},title:{type:"text",txt:"Title"},target:{type:"select",txt:"Open In",options:{"":"Current Window",_blank:"New Window"},style:{width:"100px"}}},this.ln)},submit:function(C){var A=this.inputs.href.value;if(A=="http://"||A==""){alert("You must enter a URL to Create a Link");return false}this.removePane();if(!this.ln){var B="javascript:nicTemp();";this.ne.nicCommand("createlink",B);this.ln=this.findElm("A","href",B)}if(this.ln){this.ln.setAttributes({href:this.inputs.href.value,title:this.inputs.title.value,target:this.inputs.target.options[this.inputs.target.selectedIndex].value})}}});nicEditors.registerPlugin(nicPlugin,nicLinkOptions);
+
+
+var nicColorOptions = {
+	buttons : {
+		'forecolor' : {name : __('Change Text Color'), type : 'nicEditorColorButton', noClose : true},
+		'bgcolor' : {name : __('Change Background Color'), type : 'nicEditorBgColorButton', noClose : true}
+	}
+};
+
+var nicEditorColorButton=nicEditorAdvancedButton.extend({addPane:function(){var D={0:"00",1:"33",2:"66",3:"99",4:"CC",5:"FF"};var H=new bkElement("DIV").setStyle({width:"270px"});for(var A in D){for(var F in D){for(var E in D){var I="#"+D[A]+D[E]+D[F];var C=new bkElement("DIV").setStyle({cursor:"pointer",height:"15px","float":"left"}).appendTo(H);var G=new bkElement("DIV").setStyle({border:"2px solid "+I}).appendTo(C);var B=new bkElement("DIV").setStyle({backgroundColor:I,overflow:"hidden",width:"11px",height:"11px"}).addEvent("click",this.colorSelect.closure(this,I)).addEvent("mouseover",this.on.closure(this,G)).addEvent("mouseout",this.off.closure(this,G,I)).appendTo(G);if(!window.opera){C.onmousedown=B.onmousedown=bkLib.cancelEvent}}}}this.pane.append(H.noSelect())},colorSelect:function(A){this.ne.nicCommand("foreColor",A);this.removePane()},on:function(A){A.setStyle({border:"2px solid #000"})},off:function(A,B){A.setStyle({border:"2px solid "+B})}});var nicEditorBgColorButton=nicEditorColorButton.extend({colorSelect:function(A){this.ne.nicCommand("hiliteColor",A);this.removePane()}});nicEditors.registerPlugin(nicPlugin,nicColorOptions);
+
+
+var nicImageOptions = {
+	buttons : {
+		'image' : {name : 'Add Image', type : 'nicImageButton', tags : ['IMG']}
+	}
+	
+};
+
+var nicImageButton=nicEditorAdvancedButton.extend({addPane:function(){this.im=this.ne.selectedInstance.selElm().parentTag("IMG");this.addForm({"":{type:"title",txt:"Add/Edit Image"},src:{type:"text",txt:"URL",value:"http://",style:{width:"150px"}},alt:{type:"text",txt:"Alt Text",style:{width:"100px"}},align:{type:"select",txt:"Align",options:{left:"Left",right:"Right"}}},this.im)},submit:function(B){var C=this.inputs.src.value;if(C==""||C=="http://"){alert("You must enter a Image URL to insert");return false}this.removePane();if(!this.im){var A="javascript:nicImTemp();";this.ne.nicCommand("insertImage",A);this.im=this.findElm("IMG","src",A)}if(this.im){this.im.setAttributes({src:this.inputs.src.value,alt:this.inputs.alt.value,align:this.inputs.align.value})}}});nicEditors.registerPlugin(nicPlugin,nicImageOptions);
+
+var nicXHTML=bkClass.extend({stripAttributes:["_moz_dirty","_moz_resizing","_extended"],noShort:["style","title","script","textarea","a"],cssReplace:{"font-weight:bold;":"strong","font-style:italic;":"em"},sizes:{1:"xx-small",2:"x-small",3:"small",4:"medium",5:"large",6:"x-large"},construct:function(A){this.ne=A;if(this.ne.options.xhtml){A.addEvent("get",this.cleanup.closure(this))}},cleanup:function(A){var B=A.getElm();var C=this.toXHTML(B);A.content=C},toXHTML:function(C,A,L){var G="";var O="";var P="";var I=C.nodeType;var Q=C.nodeName.toLowerCase();var N=C.hasChildNodes&&C.hasChildNodes();var B=new Array();switch(I){case 1:var H=C.attributes;switch(Q){case"b":Q="strong";break;case"i":Q="em";break;case"font":Q="span";break}if(A){for(var F=0;F<H.length;F++){var K=H[F];var M=K.nodeName.toLowerCase();var D=K.nodeValue;if(!K.specified||!D||bkLib.inArray(this.stripAttributes,M)||typeof (D)=="function"){continue}switch(M){case"style":var J=D.replace(/ /g,"");for(itm in this.cssReplace){if(J.indexOf(itm)!=-1){B.push(this.cssReplace[itm]);J=J.replace(itm,"")}}P+=J;D="";break;case"class":D=D.replace("Apple-style-span","");break;case"size":P+="font-size:"+this.sizes[D]+";";D="";break}if(D){O+=" "+M+'="'+D+'"'}}if(P){O+=' style="'+P+'"'}for(var F=0;F<B.length;F++){G+="<"+B[F]+">"}if(O==""&&Q=="span"){A=false}if(A){G+="<"+Q;if(Q!="br"){G+=O}}}if(!N&&!bkLib.inArray(this.noShort,M)){if(A){G+=" />"}}else{if(A){G+=">"}for(var F=0;F<C.childNodes.length;F++){var E=this.toXHTML(C.childNodes[F],true,true);if(E){G+=E}}}if(A&&N){G+="</"+Q+">"}for(var F=0;F<B.length;F++){G+="</"+B[F]+">"}break;case 3:G+=C.nodeValue;break}return G}});nicEditors.registerPlugin(nicXHTML);
+
Index: /afridex/plugins/Flutter/js/cropper.uncompressed.js
===================================================================
--- /afridex/plugins/Flutter/js/cropper.uncompressed.js (revision 21)
+++ /afridex/plugins/Flutter/js/cropper.uncompressed.js (revision 21)
@@ -0,0 +1,1331 @@
+/**
+ * Image Cropper (v. 1.2.0 - 2006-10-30 )
+ * Copyright (c) 2006 David Spurr (http://www.defusion.org.uk/)
+ * 
+ * The image cropper provides a way to draw a crop area on an image and capture
+ * the coordinates of the drawn crop area.
+ * 
+ * Features include:
+ * 		- Based on Prototype and Scriptaculous
+ * 		- Image editing package styling, the crop area functions and looks 
+ * 		  like those found in popular image editing software
+ * 		- Dynamic inclusion of required styles
+ * 		- Drag to draw areas
+ * 		- Shift drag to draw/resize areas as squares
+ * 		- Selection area can be moved 
+ * 		- Seleciton area can be resized using resize handles
+ * 		- Allows dimension ratio limited crop areas
+ * 		- Allows minimum dimension crop areas
+ * 		- Allows maximum dimesion crop areas
+ * 		- If both min & max dimension options set to the same value for a single axis,then the cropper will not 
+ * 		  display the resize handles as appropriate (when min & max dimensions are passed for both axes this
+ * 		  results in a 'fixed size' crop area)
+ * 		- Allows dynamic preview of resultant crop ( if minimum width & height are provided ), this is
+ * 		  implemented as a subclass so can be excluded when not required
+ * 		- Movement of selection area by arrow keys ( shift + arrow key will move selection area by
+ * 		  10 pixels )
+ *		- All operations stay within bounds of image
+ * 		- All functionality & display compatible with most popular browsers supported by Prototype:
+ * 			PC:	IE 7, 6 & 5.5, Firefox 1.5, Opera 8.5 (see known issues) & 9.0b
+ * 			MAC: Camino 1.0, Firefox 1.5, Safari 2.0
+ * 
+ * Requires:
+ * 		- Prototype v. 1.5.0_rc0 > (as packaged with Scriptaculous 1.6.1)
+ * 		- Scriptaculous v. 1.6.1 > modules: builder, dragdrop 
+ * 		
+ * Known issues:
+ * 		- Safari animated gifs, only one of each will animate, this seems to be a known Safari issue
+ * 
+ * 		- After drawing an area and then clicking to start a new drag in IE 5.5 the rendered height 
+ *        appears as the last height until the user drags, this appears to be the related to the error 
+ *        that the forceReRender() method fixes for IE 6, i.e. IE 5.5 is not redrawing the box properly.
+ * 
+ * 		- Lack of CSS opacity support in Opera before version 9 mean we disable those style rules, these 
+ * 		  could be fixed by using PNGs with transparency if Opera 8.5 support is high priority for you
+ * 
+ * 		- Marching ants keep reloading in IE <6 (not tested in IE7), it is a known issue in IE and I have 
+ *        found no viable workarounds that can be included in the release. If this really is an issue for you
+ *        either try this post: http://mir.aculo.us/articles/2005/08/28/internet-explorer-and-ajax-image-caching-woes
+ *        or uncomment the 'FIX MARCHING ANTS IN IE' rules in the CSS file
+ *		
+ *		- Styling & borders on image, any CSS styling applied directly to the image itself (floats, borders, padding, margin, etc.) will 
+ *		  cause problems with the cropper. The use of a wrapper element to apply these styles to is recommended.
+ * 
+ * 		- overflow: auto or overflow: scroll on parent will cause cropper to burst out of parent in IE and Opera (maybe Mac browsers too)
+ *		  I'm not sure why yet.
+ * 
+ * Usage:
+ * 		See Cropper.Img & Cropper.ImgWithPreview for usage details
+ * 
+ * Changelog:
+ * v1.2.0 - 2006-10-30
+ * 		+ Added id to the preview image element using 'imgCrop_[originalImageID]'
+ *      * #00001 - Fixed bug: Doesn't account for scroll offsets
+ *      * #00009 - Fixed bug: Placing the cropper inside differently positioned elements causes incorrect co-ordinates and display
+ *      * #00013 - Fixed bug: I-bar cursor appears on drag plane
+ *      * #00014 - Fixed bug: If ID for image tag is not found in document script throws error
+ *      * Fixed bug with drag start co-ordinates if wrapper element has moved in browser (e.g. dragged to a new position)
+ *      * Fixed bug with drag start co-ordinates if image contained in a wrapper with scrolling - this may be buggy if image 
+ * 		  has other ancestors with scrolling applied (except the body)
+ *      * #00015 - Fixed bug: When cropper removed and then reapplied onEndCrop callback gets called multiple times, solution suggestion from Bill Smith
+ *      * Various speed increases & code cleanup which meant improved performance in Mac - which allowed removal of different overlay methods for
+ *        IE and all other browsers, which led to a fix for:
+ * 		* #00010 - Fixed bug: Select area doesn't adhere to image size when image resized using img attributes
+ *      - #00006 - Removed default behaviour of automatically setting a ratio when both min width & height passed, the ratioDimensions must be passed in
+ * 		+ #00005 - Added ability to set maximum crop dimensions, if both min & max set as the same value then we'll get a fixed cropper size on the axes as appropriate
+ *        and the resize handles will not be displayed as appropriate
+ * 		* Switched keydown for keypress for moving select area with cursor keys (makes for nicer action) - doesn't appear to work in Safari
+ * 
+ * v1.1.3 - 2006-08-21
+ * 		* Fixed wrong cursor on western handle in CSS
+ * 		+ #00008 & #00003 - Added feature: Allow to set dimensions & position for cropper on load
+ *      * #00002 - Fixed bug: Pressing 'remove cropper' twice removes image in IE
+ * 
+ * v1.1.2 - 2006-06-09
+ * 		* Fixed bugs with ratios when GCD is low (patch submitted by Andy Skelton)
+ * 
+ * v1.1.1 - 2006-06-03
+ * 		* Fixed bug with rendering issues fix in IE 5.5
+ * 		* Fixed bug with endCrop callback issues once cropper had been removed & reset in IE
+ * 
+ * v1.1.0 - 2006-06-02
+ * 		* Fixed bug with IE constantly trying to reload select area background image
+ * 		* Applied more robust fix to Safari & IE rendering issues
+ * 		+ Added method to reset parameters - useful for when dynamically changing img cropper attached to
+ * 		+ Added method to remove cropper from image
+ * 
+ * v1.0.0 - 2006-05-18 
+ * 		+ Initial verison
+ * 
+ * 
+ * Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)
+ * All rights reserved.
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * http://www.opensource.org/licenses/bsd-license.php
+ * 
+ * See scriptaculous.js for full scriptaculous licence
+ */
+ 
+/**
+ * Extend the Draggable class to allow us to pass the rendering
+ * down to the Cropper object.
+ */
+var CropDraggable = Class.create();
+
+Object.extend( Object.extend( CropDraggable.prototype, Draggable.prototype), {
+	
+	initialize: function(element) {
+		this.options = Object.extend(
+			{
+				/**
+				 * The draw method to defer drawing to
+				 */
+				drawMethod: function() {}
+			}, 
+			arguments[1] || {}
+		);
+
+		this.element = $(element);
+
+		this.handle = this.element;
+
+		this.delta    = this.currentDelta();
+		this.dragging = false;   
+
+		this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+		Event.observe(this.handle, "mousedown", this.eventMouseDown);
+
+		Draggables.register(this);
+	},
+	
+	/**
+	 * Defers the drawing of the draggable to the supplied method
+	 */
+	draw: function(point) {
+		var pos = Position.cumulativeOffset(this.element);
+		var d = this.currentDelta();
+		pos[0] -= d[0]; 
+		pos[1] -= d[1];
+				
+		var p = [0,1].map(function(i) { 
+			return (point[i]-pos[i]-this.offset[i]) 
+		}.bind(this));
+				
+		this.options.drawMethod( p );
+	}
+	
+});
+
+
+/**
+ * The Cropper object, this will attach itself to the provided image by wrapping it with 
+ * the generated xHTML structure required by the cropper.
+ * 
+ * Usage:
+ * 	@param obj Image element to attach to
+ * 	@param obj Optional options:
+ * 		- ratioDim obj 
+ * 			The pixel dimensions to apply as a restrictive ratio, with properties x & y
+ * 
+ * 		- minWidth int 
+ * 			The minimum width for the select area in pixels
+ * 
+ * 		- minHeight	int 
+ * 			The mimimum height for the select area in pixels
+ * 
+ * 		- maxWidth int
+ * 			The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)
+ * 
+ * 		- maxHeight int
+ *			The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)
+ * 
+ * 		- displayOnInit int 
+ * 			Whether to display the select area on initialisation, only used when providing minimum width & height or ratio
+ * 
+ * 		- onEndCrop func
+ * 			The callback function to provide the crop details to on end of a crop (see below)
+ * 
+ * 		- captureKeys boolean
+ * 			Whether to capture the keys for moving the select area, as these can cause some problems at the moment
+ * 
+ * 		- onloadCoords obj
+ * 			A coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area to display onload
+ * 	
+ *----------------------------------------------
+ * 
+ * The callback function provided via the onEndCrop option should accept the following parameters:
+ * 		- coords obj
+ * 			The coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area
+ * 
+ * 		- dimensions obj
+ * 			The dimensions object with properites width & height; for the dimensions of the select area
+ * 		
+ *
+ * 		Example:
+ * 			function onEndCrop( coords, dimensions ) {
+ *				$( 'x1' ).value 	= coords.x1;
+ *				$( 'y1' ).value 	= coords.y1;
+ *				$( 'x2' ).value 	= coords.x2;
+ *				$( 'y2' ).value 	= coords.y2;
+ *				$( 'width' ).value 	= dimensions.width;
+ *				$( 'height' ).value	= dimensions.height;
+ *			}
+ * 
+ */
+var Cropper = {};
+Cropper.Img = Class.create();
+Cropper.Img.prototype = {
+	
+	/**
+	 * Initialises the class
+	 * 
+	 * @access public
+	 * @param obj Image element to attach to
+	 * @param obj Options
+	 * @return void
+	 */
+	initialize: function(element, options) {
+		this.options = Object.extend(
+			{
+				/**
+				 * @var obj
+				 * The pixel dimensions to apply as a restrictive ratio
+				 */
+				ratioDim: { x: 0, y: 0 },
+				/**
+				 * @var int
+				 * The minimum pixel width, also used as restrictive ratio if min height passed too
+				 */
+				minWidth:		0,
+				/**
+				 * @var int
+				 * The minimum pixel height, also used as restrictive ratio if min width passed too
+				 */
+				minHeight:		0,
+				/**
+				 * @var boolean
+				 * Whether to display the select area on initialisation, only used when providing minimum width & height or ratio
+				 */
+				displayOnInit:	false,
+				/**
+				 * @var function
+				 * The call back function to pass the final values to
+				 */
+				onEndCrop: Prototype.emptyFunction,
+				/**
+				 * @var boolean
+				 * Whether to capture key presses or not
+				 */
+				captureKeys: true,
+				/**
+				 * @var obj Coordinate object x1, y1, x2, y2
+				 * The coordinates to optionally display the select area at onload
+				 */
+				onloadCoords: null,
+				/**
+				 * @var int
+				 * The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)
+				 */
+				maxWidth: 0,
+				/**
+				 * @var int
+				 * The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)
+				 */
+				maxHeight: 0
+			}, 
+			options || {}
+		);				
+		/**
+		 * @var obj
+		 * The img node to attach to
+		 */
+		this.img			= $( element );
+		/**
+		 * @var obj
+		 * The x & y coordinates of the click point
+		 */
+		this.clickCoords	= { x: 0, y: 0 };
+		/**
+		 * @var boolean
+		 * Whether the user is dragging
+		 */
+		this.dragging		= false;
+		/**
+		 * @var boolean
+		 * Whether the user is resizing
+		 */
+		this.resizing		= false;
+		/**
+		 * @var boolean
+		 * Whether the user is on a webKit browser
+		 */
+		this.isWebKit 		= /Konqueror|Safari|KHTML/.test( navigator.userAgent );
+		/**
+		 * @var boolean
+		 * Whether the user is on IE
+		 */
+		this.isIE 			= /MSIE/.test( navigator.userAgent );
+		/**
+		 * @var boolean
+		 * Whether the user is on Opera below version 9
+		 */
+		this.isOpera8		= /Opera\s[1-8]/.test( navigator.userAgent );
+		/**
+		 * @var int
+		 * The x ratio 
+		 */
+		this.ratioX			= 0;
+		/**
+		 * @var int
+		 * The y ratio
+		 */
+		this.ratioY			= 0;
+		/**
+		 * @var boolean
+		 * Whether we've attached sucessfully
+		 */
+		this.attached		= false;
+		/**
+		 * @var boolean
+		 * Whether we've got a fixed width (if minWidth EQ or GT maxWidth then we have a fixed width
+		 * in the case of minWidth > maxWidth maxWidth wins as the fixed width)
+		 */
+		this.fixedWidth		= ( this.options.maxWidth > 0 && ( this.options.minWidth >= this.options.maxWidth ) );
+		/**
+		 * @var boolean
+		 * Whether we've got a fixed height (if minHeight EQ or GT maxHeight then we have a fixed height
+		 * in the case of minHeight > maxHeight maxHeight wins as the fixed height)
+		 */
+		this.fixedHeight	= ( this.options.maxHeight > 0 && ( this.options.minHeight >= this.options.maxHeight ) );
+		
+		// quit if the image element doesn't exist
+		if( typeof this.img == 'undefined' ) return;
+				
+		// include the stylesheet		
+		$A( document.getElementsByTagName( 'script' ) ).each( 
+			function(s) {
+				if( s.src.match( /cropper\.js/ ) ) {
+					var path 	= s.src.replace( /cropper\.js(.*)?/, '' );
+					// '<link rel="stylesheet" type="text/css" href="' + path + 'cropper.css" media="screen" />';
+					var style 		= document.createElement( 'link' );
+					style.rel 		= 'stylesheet';
+					style.type 		= 'text/css';
+					style.href 		= path + 'cropper.css';
+					style.media 	= 'screen';
+					document.getElementsByTagName( 'head' )[0].appendChild( style );
+				}
+	    	}
+	    );   
+	
+		// calculate the ratio when neccessary
+		if( this.options.ratioDim.x > 0 && this.options.ratioDim.y > 0 ) {
+			var gcd = this.getGCD( this.options.ratioDim.x, this.options.ratioDim.y );
+			this.ratioX = this.options.ratioDim.x / gcd;
+			this.ratioY = this.options.ratioDim.y / gcd;
+			// dump( 'RATIO : ' + this.ratioX + ':' + this.ratioY + '\n' );
+		}
+							
+		// initialise sub classes
+		this.subInitialize();
+
+		// only load the event observers etc. once the image is loaded
+		// this is done after the subInitialize() call just in case the sub class does anything
+		// that will affect the result of the call to onLoad()
+		if( this.img.complete || this.isWebKit ) this.onLoad(); // for some reason Safari seems to support img.complete but returns 'undefined' on the this.img object
+		else Event.observe( this.img, 'load', this.onLoad.bindAsEventListener( this) );		
+	},
+	
+	/**
+	 * The Euclidean algorithm used to find the greatest common divisor
+	 * 
+	 * @acces private
+	 * @param int Value 1
+	 * @param int Value 2
+	 * @return int
+	 */
+	getGCD : function( a , b ) {
+		if( b == 0 ) return a;
+		return this.getGCD(b, a % b );
+	},
+	
+	/**
+	 * Attaches the cropper to the image once it has loaded
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	onLoad: function( ) {
+		/*
+		 * Build the container and all related elements, will result in the following
+		 *
+		 * <div class="imgCrop_wrap">
+		 * 		<img ... this.img ... />
+		 * 		<div class="imgCrop_dragArea">
+		 * 			<!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->
+		 * 			<div class="imgCrop_overlay imageCrop_north"><span></span></div>
+		 * 			<div class="imgCrop_overlay imageCrop_east"><span></span></div>
+		 * 			<div class="imgCrop_overlay imageCrop_south"><span></span></div>
+		 * 			<div class="imgCrop_overlay imageCrop_west"><span></span></div>
+		 * 			<div class="imgCrop_selArea">
+		 * 				<!-- marquees -->
+		 * 				<!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->
+		 * 				<div class="imgCrop_marqueeHoriz imgCrop_marqueeNorth"><span></span></div>
+		 * 				<div class="imgCrop_marqueeVert imgCrop_marqueeEast"><span></span></div>
+		 * 				<div class="imgCrop_marqueeHoriz imgCrop_marqueeSouth"><span></span></div>
+		 * 				<div class="imgCrop_marqueeVert imgCrop_marqueeWest"><span></span></div>			
+		 * 				<!-- handles -->
+		 * 				<div class="imgCrop_handle imgCrop_handleN"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleNE"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleE"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleSE"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleS"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleSW"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleW"></div>
+		 * 				<div class="imgCrop_handle imgCrop_handleNW"></div>
+		 * 				<div class="imgCrop_clickArea"></div>
+		 * 			</div>	
+		 * 			<div class="imgCrop_clickArea"></div>
+		 * 		</div>	
+		 * </div>
+		 */
+		var cNamePrefix = 'imgCrop_';
+		
+		// get the point to insert the container
+		var insertPoint = this.img.parentNode;
+		
+		// apply an extra class to the wrapper to fix Opera below version 9
+		var fixOperaClass = '';
+		if( this.isOpera8 ) fixOperaClass = ' opera8';
+		this.imgWrap = Builder.node( 'div', { 'class': cNamePrefix + 'wrap' + fixOperaClass } );
+		
+		this.north		= Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'north' }, [Builder.node( 'span' )] );
+		this.east		= Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'east' } , [Builder.node( 'span' )] );
+		this.south		= Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'south' }, [Builder.node( 'span' )] );
+		this.west		= Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'west' } , [Builder.node( 'span' )] );
+		
+		var overlays	= [ this.north, this.east, this.south, this.west ];
+
+		this.dragArea	= Builder.node( 'div', { 'class': cNamePrefix + 'dragArea' }, overlays );
+						
+		this.handleN	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleN' } );
+		this.handleNE	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleNE' } );
+		this.handleE	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleE' } );
+		this.handleSE	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleSE' } );
+		this.handleS	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleS' } );
+		this.handleSW	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleSW' } );
+		this.handleW	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleW' } );
+		this.handleNW	= Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleNW' } );
+				
+		this.selArea	= Builder.node( 'div', { 'class': cNamePrefix + 'selArea' },
+			[
+				Builder.node( 'div', { 'class': cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeNorth' }, [Builder.node( 'span' )] ),
+				Builder.node( 'div', { 'class': cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeEast' }  , [Builder.node( 'span' )] ),
+				Builder.node( 'div', { 'class': cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeSouth' }, [Builder.node( 'span' )] ),
+				Builder.node( 'div', { 'class': cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeWest' }  , [Builder.node( 'span' )] ),
+				this.handleN,
+				this.handleNE,
+				this.handleE,
+				this.handleSE,
+				this.handleS,
+				this.handleSW,
+				this.handleW,
+				this.handleNW,
+				Builder.node( 'div', { 'class': cNamePrefix + 'clickArea' } )
+			]
+		);
+				
+		this.imgWrap.appendChild( this.img );
+		this.imgWrap.appendChild( this.dragArea );
+		this.dragArea.appendChild( this.selArea );
+		this.dragArea.appendChild( Builder.node( 'div', { 'class': cNamePrefix + 'clickArea' } ) );
+
+		insertPoint.appendChild( this.imgWrap );
+
+		// add event observers
+		this.startDragBind 	= this.startDrag.bindAsEventListener( this );
+		Event.observe( this.dragArea, 'mousedown', this.startDragBind );
+		
+		this.onDragBind 	= this.onDrag.bindAsEventListener( this );
+		Event.observe( document, 'mousemove', this.onDragBind );
+		
+		this.endCropBind 	= this.endCrop.bindAsEventListener( this );
+		Event.observe( document, 'mouseup', this.endCropBind );
+		
+		this.resizeBind		= this.startResize.bindAsEventListener( this );
+		this.handles = [ this.handleN, this.handleNE, this.handleE, this.handleSE, this.handleS, this.handleSW, this.handleW, this.handleNW ];
+		this.registerHandles( true );
+		
+		if( this.options.captureKeys ) {
+			this.keysBind = this.handleKeys.bindAsEventListener( this );
+			Event.observe( document, 'keypress', this.keysBind );
+		}
+
+		// attach the dragable to the select area
+		new CropDraggable( this.selArea, { drawMethod: this.moveArea.bindAsEventListener( this ) } );
+		
+		this.setParams();
+	},
+	
+	/**
+	 * Manages adding or removing the handle event handler and hiding or displaying them as appropriate
+	 * 
+	 * @access private
+	 * @param boolean registration true = add, false = remove
+	 * @return void
+	 */
+	registerHandles: function( registration ) {	
+		for( var i = 0; i < this.handles.length; i++ ) {
+			var handle = $( this.handles[i] );
+			
+			if( registration ) {
+				var hideHandle	= false;	// whether to hide the handle
+				
+				// disable handles asappropriate if we've got fixed dimensions
+				// if both dimensions are fixed we don't need to do much
+				if( this.fixedWidth && this.fixedHeight ) hideHandle = true;
+				else if( this.fixedWidth || this.fixedHeight ) {
+					// if one of the dimensions is fixed then just hide those handles
+					var isCornerHandle	= handle.className.match( /([S|N][E|W])$/ )
+					var isWidthHandle 	= handle.className.match( /(E|W)$/ );
+					var isHeightHandle 	= handle.className.match( /(N|S)$/ );
+					if( isCornerHandle ) hideHandle = true;
+					else if( this.fixedWidth && isWidthHandle ) hideHandle = true;
+					else if( this.fixedHeight && isHeightHandle ) hideHandle = true;
+				}
+				if( hideHandle ) handle.hide();
+				else Event.observe( handle, 'mousedown', this.resizeBind );
+			} else {
+				handle.show();
+				Event.stopObserving( handle, 'mousedown', this.resizeBind );
+			}
+		}
+	},
+		
+	/**
+	 * Sets up all the cropper parameters, this can be used to reset the cropper when dynamically
+	 * changing the images
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	setParams: function() {
+		/**
+		 * @var int
+		 * The image width
+		 */
+		this.imgW = this.img.width;
+		/**
+		 * @var int
+		 * The image height
+		 */
+		this.imgH = this.img.height;			
+
+		$( this.north ).setStyle( { height: 0 } );
+		$( this.east ).setStyle( { width: 0, height: 0 } );
+		$( this.south ).setStyle( { height: 0 } );
+		$( this.west ).setStyle( { width: 0, height: 0 } );
+		
+		// resize the container to fit the image
+		$( this.imgWrap ).setStyle( { 'width': this.imgW + 'px', 'height': this.imgH + 'px' } );
+		
+		// hide the select area
+		$( this.selArea ).hide();
+						
+		// setup the starting position of the select area
+		var startCoords = { x1: 0, y1: 0, x2: 0, y2: 0 };
+		var validCoordsSet = false;
+		
+		// display the select area 
+		if( this.options.onloadCoords != null ) {
+			// if we've being given some coordinates to 
+			startCoords = this.cloneCoords( this.options.onloadCoords );
+			validCoordsSet = true;
+		} else if( this.options.ratioDim.x > 0 && this.options.ratioDim.y > 0 ) {
+			// if there is a ratio limit applied and the then set it to initial ratio
+			startCoords.x1 = Math.ceil( ( this.imgW - this.options.ratioDim.x ) / 2 );
+			startCoords.y1 = Math.ceil( ( this.imgH - this.options.ratioDim.y ) / 2 );
+			startCoords.x2 = startCoords.x1 + this.options.ratioDim.x;
+			startCoords.y2 = startCoords.y1 + this.options.ratioDim.y;
+			validCoordsSet = true;
+		}
+		
+		this.setAreaCoords( startCoords, false, false, 1 );
+		
+		if( this.options.displayOnInit && validCoordsSet ) {
+			this.selArea.show();
+			this.drawArea();
+			this.endCrop();
+		}
+		
+		this.attached = true;
+	},
+	
+	/**
+	 * Removes the cropper
+	 * 
+	 * @access public
+	 * @return void
+	 */
+	remove: function() {
+		if( this.attached ) {
+			this.attached = false;
+			
+			// remove the elements we inserted
+			this.imgWrap.parentNode.insertBefore( this.img, this.imgWrap );
+			this.imgWrap.parentNode.removeChild( this.imgWrap );
+			
+			// remove the event observers
+			Event.stopObserving( this.dragArea, 'mousedown', this.startDragBind );
+			Event.stopObserving( document, 'mousemove', this.onDragBind );		
+			Event.stopObserving( document, 'mouseup', this.endCropBind );
+			this.registerHandles( false );
+			if( this.options.captureKeys ) Event.stopObserving( document, 'keypress', this.keysBind );
+		}
+	},
+	
+	/**
+	 * Resets the cropper, can be used either after being removed or any time you wish
+	 * 
+	 * @access public
+	 * @return void
+	 */
+	reset: function() {
+		if( !this.attached ) this.onLoad();
+		else this.setParams();
+		this.endCrop();
+	},
+	
+	/**
+	 * Handles the key functionality, currently just using arrow keys to move, if the user
+	 * presses shift then the area will move by 10 pixels
+	 */
+	handleKeys: function( e ) {
+		var dir = { x: 0, y: 0 }; // direction to move it in & the amount in pixels
+		if( !this.dragging ) {
+			
+			// catch the arrow keys
+			switch( e.keyCode ) {
+				case( 37 ) : // left
+					dir.x = -1;
+					break;
+				case( 38 ) : // up
+					dir.y = -1;
+					break;
+				case( 39 ) : // right
+					dir.x = 1;
+					break
+				case( 40 ) : // down
+					dir.y = 1;
+					break;
+			}
+			
+			if( dir.x != 0 || dir.y != 0 ) {
+				// if shift is pressed then move by 10 pixels
+				if( e.shiftKey ) {
+					dir.x *= 10;
+					dir.y *= 10;
+				}
+				
+				this.moveArea( [ this.areaCoords.x1 + dir.x, this.areaCoords.y1 + dir.y ] );
+				Event.stop( e ); 
+			}
+		}
+	},
+	
+	/**
+	 * Calculates the width from the areaCoords
+	 * 
+	 * @access private
+	 * @return int
+	 */
+	calcW: function() {
+		return (this.areaCoords.x2 - this.areaCoords.x1)
+	},
+	
+	/**
+	 * Calculates the height from the areaCoords
+	 * 
+	 * @access private
+	 * @return int
+	 */
+	calcH: function() {
+		return (this.areaCoords.y2 - this.areaCoords.y1)
+	},
+	
+	/**
+	 * Moves the select area to the supplied point (assumes the point is x1 & y1 of the select area)
+	 * 
+	 * @access public
+	 * @param array Point for x1 & y1 to move select area to
+	 * @return void
+	 */
+	moveArea: function( point ) {
+		// dump( 'moveArea        : ' + point[0] + ',' + point[1] + ',' + ( point[0] + ( this.areaCoords.x2 - this.areaCoords.x1 ) ) + ',' + ( point[1] + ( this.areaCoords.y2 - this.areaCoords.y1 ) ) + '\n' );
+		this.setAreaCoords( 
+			{
+				x1: point[0], 
+				y1: point[1],
+				x2: point[0] + this.calcW(),
+				y2: point[1] + this.calcH()
+			},
+			true,
+			false
+		);
+		this.drawArea();
+	},
+
+	/**
+	 * Clones a co-ordinates object, stops problems with handling them by reference
+	 * 
+	 * @access private
+	 * @param obj Coordinate object x1, y1, x2, y2
+	 * @return obj Coordinate object x1, y1, x2, y2
+	 */
+	cloneCoords: function( coords ) {
+		return { x1: coords.x1, y1: coords.y1, x2: coords.x2, y2: coords.y2 };
+	},
+
+	/**
+	 * Sets the select coords to those provided but ensures they don't go
+	 * outside the bounding box
+	 * 
+	 * @access private
+	 * @param obj Coordinates x1, y1, x2, y2
+	 * @param boolean Whether this is a move
+	 * @param boolean Whether to apply squaring
+	 * @param obj Direction of mouse along both axis x, y ( -1 = negative, 1 = positive ) only required when moving etc.
+	 * @param string The current resize handle || null
+	 * @return void
+	 */
+	setAreaCoords: function( coords, moving, square, direction, resizeHandle ) {
+		// dump( 'setAreaCoords (in) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 );
+		if( moving ) {
+			// if moving
+			var targW = coords.x2 - coords.x1;
+			var targH = coords.y2 - coords.y1;
+			
+			// ensure we're within the bounds
+			if( coords.x1 < 0 ) {
+				coords.x1 = 0;
+				coords.x2 = targW;
+			}
+			if( coords.y1 < 0 ) {
+				coords.y1 = 0;
+				coords.y2 = targH;
+			}
+			if( coords.x2 > this.imgW ) {
+				coords.x2 = this.imgW;
+				coords.x1 = this.imgW - targW;
+			}
+			if( coords.y2 > this.imgH ) {
+				coords.y2 = this.imgH;
+				coords.y1 = this.imgH - targH;
+			}			
+		} else {
+			// ensure we're within the bounds
+			if( coords.x1 < 0 ) coords.x1 = 0;
+			if( coords.y1 < 0 ) coords.y1 = 0;
+			if( coords.x2 > this.imgW ) coords.x2 = this.imgW;
+			if( coords.y2 > this.imgH ) coords.y2 = this.imgH;
+			
+			// This is passed as null in onload
+			if( direction != null ) {
+								
+				// apply the ratio or squaring where appropriate
+				if( this.ratioX > 0 ) this.applyRatio( coords, { x: this.ratioX, y: this.ratioY }, direction, resizeHandle );
+				else if( square ) this.applyRatio( coords, { x: 1, y: 1 }, direction, resizeHandle );
+										
+				var mins = [ this.options.minWidth, this.options.minHeight ]; // minimum dimensions [x,y]			
+				var maxs = [ this.options.maxWidth, this.options.maxHeight ]; // maximum dimensions [x,y]
+		
+				// apply dimensions where appropriate
+				if( mins[0] > 0 || mins[1] > 0 || maxs[0] > 0 || maxs[1] > 0) {
+				
+					var coordsTransX 	= { a1: coords.x1, a2: coords.x2 };
+					var coordsTransY 	= { a1: coords.y1, a2: coords.y2 };
+					var boundsX			= { min: 0, max: this.imgW };
+					var boundsY			= { min: 0, max: this.imgH };
+					
+					// handle squaring properly on single axis minimum dimensions
+					if( (mins[0] != 0 || mins[1] != 0) && square ) {
+						if( mins[0] > 0 ) mins[1] = mins[0];
+						else if( mins[1] > 0 ) mins[0] = mins[1];
+					}
+					
+					if( (maxs[0] != 0 || maxs[0] != 0) && square ) {
+						// if we have a max x value & it is less than the max y value then we set the y max to the max x (so we don't go over the minimum maximum of one of the axes - if that makes sense)
+						if( maxs[0] > 0 && maxs[0] <= maxs[1] ) maxs[1] = maxs[0];
+						else if( maxs[1] > 0 && maxs[1] <= maxs[0] ) maxs[0] = maxs[1];
+					}
+					
+					if( mins[0] > 0 ) this.applyDimRestriction( coordsTransX, mins[0], direction.x, boundsX, 'min' );
+					if( mins[1] > 1 ) this.applyDimRestriction( coordsTransY, mins[1], direction.y, boundsY, 'min' );
+					
+					if( maxs[0] > 0 ) this.applyDimRestriction( coordsTransX, maxs[0], direction.x, boundsX, 'max' );
+					if( maxs[1] > 1 ) this.applyDimRestriction( coordsTransY, maxs[1], direction.y, boundsY, 'max' );
+					
+					coords = { x1: coordsTransX.a1, y1: coordsTransY.a1, x2: coordsTransX.a2, y2: coordsTransY.a2 };
+				}
+				
+			}
+		}
+		
+		// dump( 'setAreaCoords (out) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 + '\n' );
+		this.areaCoords = coords;
+	},
+	
+	/**
+	 * Applies the supplied dimension restriction to the supplied coordinates along a single axis
+	 * 
+	 * @access private
+	 * @param obj Single axis coordinates, a1, a2 (e.g. for the x axis a1 = x1 & a2 = x2)
+	 * @param int The restriction value
+	 * @param int The direction ( -1 = negative, 1 = positive )
+	 * @param obj The bounds of the image ( for this axis )
+	 * @param string The dimension restriction type ( 'min' | 'max' )
+	 * @return void
+	 */
+	applyDimRestriction: function( coords, val, direction, bounds, type ) {
+		var check;
+		if( type == 'min' ) check = ( ( coords.a2 - coords.a1 ) < val );
+		else check = ( ( coords.a2 - coords.a1 ) > val );
+		if( check ) {
+			if( direction == 1 ) coords.a2 = coords.a1 + val;
+			else coords.a1 = coords.a2 - val;
+			
+			// make sure we're still in the bounds (not too pretty for the user, but needed)
+			if( coords.a1 < bounds.min ) {
+				coords.a1 = bounds.min;
+				coords.a2 = val;
+			} else if( coords.a2 > bounds.max ) {
+				coords.a1 = bounds.max - val;
+				coords.a2 = bounds.max;
+			}
+		}
+	},
+		
+	/**
+	 * Applies the supplied ratio to the supplied coordinates
+	 * 
+	 * @access private
+	 * @param obj Coordinates, x1, y1, x2, y2
+	 * @param obj Ratio, x, y
+	 * @param obj Direction of mouse, x & y : -1 == negative 1 == positive
+	 * @param string The current resize handle || null
+	 * @return void
+	 */
+	applyRatio : function( coords, ratio, direction, resizeHandle ) {
+		// dump( 'direction.y : ' + direction.y + '\n');
+		var newCoords;
+		if( resizeHandle == 'N' || resizeHandle == 'S' ) {
+			// dump( 'north south \n');
+			// if moving on either the lone north & south handles apply the ratio on the y axis
+			newCoords = this.applyRatioToAxis( 
+				{ a1: coords.y1, b1: coords.x1, a2: coords.y2, b2: coords.x2 },
+				{ a: ratio.y, b: ratio.x },
+				{ a: direction.y, b: direction.x },
+				{ min: 0, max: this.imgW }
+			);
+			coords.x1 = newCoords.b1;
+			coords.y1 = newCoords.a1;
+			coords.x2 = newCoords.b2;
+			coords.y2 = newCoords.a2;
+		} else {
+			// otherwise deal with it as if we're applying the ratio on the x axis
+			newCoords = this.applyRatioToAxis( 
+				{ a1: coords.x1, b1: coords.y1, a2: coords.x2, b2: coords.y2 },
+				{ a: ratio.x, b: ratio.y },
+				{ a: direction.x, b: direction.y },
+				{ min: 0, max: this.imgH }
+			);
+			coords.x1 = newCoords.a1;
+			coords.y1 = newCoords.b1;
+			coords.x2 = newCoords.a2;
+			coords.y2 = newCoords.b2;
+		}
+		
+	},
+	
+	/**
+	 * Applies the provided ratio to the provided coordinates based on provided direction & bounds,
+	 * use to encapsulate functionality to make it easy to apply to either axis. This is probably
+	 * quite hard to visualise so see the x axis example within applyRatio()
+	 * 
+	 * Example in parameter details & comments is for requesting applying ratio to x axis.
+	 * 
+	 * @access private
+	 * @param obj Coords object (a1, b1, a2, b2) where a = x & b = y in example
+	 * @param obj Ratio object (a, b) where a = x & b = y in example
+	 * @param obj Direction object (a, b) where a = x & b = y in example
+	 * @param obj Bounds (min, max)
+	 * @return obj Coords object (a1, b1, a2, b2) where a = x & b = y in example
+	 */
+	applyRatioToAxis: function( coords, ratio, direction, bounds ) {
+		var newCoords = Object.extend( coords, {} );
+		var calcDimA = newCoords.a2 - newCoords.a1;			// calculate dimension a (e.g. width)
+		var targDimB = Math.floor( calcDimA * ratio.b / ratio.a );	// the target dimension b (e.g. height)
+		var targB;											// to hold target b (e.g. y value)
+		var targDimA;                                		// to hold target dimension a (e.g. width)
+		var calcDimB = null;								// to hold calculated dimension b (e.g. height)
+		
+		// dump( 'newCoords[0]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
+				
+		if( direction.b == 1 ) {							// if travelling in a positive direction
+			// make sure we're not going out of bounds
+			targB = newCoords.b1 + targDimB;
+			if( targB > bounds.max ) {
+				targB = bounds.max;
+				calcDimB = targB - newCoords.b1;			// calcuate dimension b (e.g. height)
+			}
+			
+			newCoords.b2 = targB;
+		} else {											// if travelling in a negative direction
+			// make sure we're not going out of bounds
+			targB = newCoords.b2 - targDimB;
+			if( targB < bounds.min ) {
+				targB = bounds.min;
+				calcDimB = targB + newCoords.b2;			// calcuate dimension b (e.g. height)
+			}
+			newCoords.b1 = targB;
+		}
+		
+		// dump( 'newCoords[1]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
+			
+		// apply the calculated dimensions
+		if( calcDimB != null ) {
+			targDimA = Math.floor( calcDimB * ratio.a / ratio.b );
+			
+			if( direction.a == 1 ) newCoords.a2 = newCoords.a1 + targDimA;
+			else newCoords.a1 = newCoords.a1 = newCoords.a2 - targDimA;
+		}
+		
+		// dump( 'newCoords[2]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
+			
+		return newCoords;
+	},
+	
+	/**
+	 * Draws the select area
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	drawArea: function( ) {	
+		/*
+		 * NOTE: I'm not using the Element.setStyle() shortcut as they make it 
+		 * quite sluggish on Mac based browsers
+		 */
+		// dump( 'drawArea        : ' + this.areaCoords.x1 + ',' + this.areaCoords.y1 + ',' + this.areaCoords.x2 + ',' + this.areaCoords.y2 + '\n' );
+		var areaWidth     = this.calcW();
+		var areaHeight    = this.calcH();
+		
+		/*
+		 * Calculate all the style strings before we use them, allows reuse & produces quicker
+		 * rendering (especially noticable in Mac based browsers)
+		 */
+		var px = 'px';
+		var params = [
+			this.areaCoords.x1 + px, 	// the left of the selArea
+			this.areaCoords.y1 + px,		// the top of the selArea
+			areaWidth + px,					// width of the selArea
+			areaHeight + px,					// height of the selArea
+			this.areaCoords.x2 + px,		// bottom of the selArea
+			this.areaCoords.y2 + px,		// right of the selArea
+			(this.img.width - this.areaCoords.x2) + px,	// right edge of selArea
+			(this.img.height - this.areaCoords.y2) + px	// bottom edge of selArea
+		];
+				
+		// do the select area
+		var areaStyle				= this.selArea.style;
+		areaStyle.left				= params[0];
+		areaStyle.top				= params[1];
+		areaStyle.width				= params[2];
+		areaStyle.height			= params[3];
+			  	
+		// position the north, east, south & west handles
+		var horizHandlePos = Math.ceil( (areaWidth - 6) / 2 ) + px;
+		var vertHandlePos = Math.ceil( (areaHeight - 6) / 2 ) + px;
+		
+		this.handleN.style.left 	= horizHandlePos;
+		this.handleE.style.top 		= vertHandlePos;
+		this.handleS.style.left 	= horizHandlePos;
+		this.handleW.style.top		= vertHandlePos;
+		
+		// draw the four overlays
+		this.north.style.height 	= params[1];
+		
+		var eastStyle 				= this.east.style;
+		eastStyle.top				= params[1];
+		eastStyle.height			= params[3];
+		eastStyle.left				= params[4];
+	    eastStyle.width				= params[6];
+	   
+	   	var southStyle 				= this.south.style;
+	   	southStyle.top				= params[5];
+	   	southStyle.height			= params[7];
+	   
+	    var westStyle       		= this.west.style;
+	    westStyle.top				= params[1];
+	    westStyle.height			= params[3];
+	   	westStyle.width				= params[0];
+	   	
+		// call the draw method on sub classes
+		this.subDrawArea();
+		
+		this.forceReRender();
+	},
+	
+	/**
+	 * Force the re-rendering of the selArea element which fixes rendering issues in Safari 
+	 * & IE PC, especially evident when re-sizing perfectly vertical using any of the south handles
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	forceReRender: function() {
+		if( this.isIE || this.isWebKit) {
+			var n = document.createTextNode(' ');
+			var d,el,fixEL,i;
+		
+			if( this.isIE ) fixEl = this.selArea;
+			else if( this.isWebKit ) {
+				fixEl = document.getElementsByClassName( 'imgCrop_marqueeSouth', this.imgWrap )[0];
+				/* we have to be a bit more forceful for Safari, otherwise the the marquee &
+				 * the south handles still don't move
+				 */ 
+				d = Builder.node( 'div', '' );
+				d.style.visibility = 'hidden';
+				
+				var classList = ['SE','S','SW'];
+				for( i = 0; i < classList.length; i++ ) {
+					el = document.getElementsByClassName( 'imgCrop_handle' + classList[i], this.selArea )[0];
+					if( el.childNodes.length ) el.removeChild( el.childNodes[0] );
+					el.appendChild(d);
+				}
+			}
+			fixEl.appendChild(n);
+			fixEl.removeChild(n);
+		}
+	},
+	
+	/**
+	 * Starts the resize
+	 * 
+	 * @access private
+	 * @param obj Event
+	 * @return void
+	 */
+	startResize: function( e ) {
+		this.startCoords = this.cloneCoords( this.areaCoords );
+		
+		this.resizing = true;
+		this.resizeHandle = Event.element( e ).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/, '');
+		// dump( 'this.resizeHandle : ' + this.resizeHandle + '\n' );
+		Event.stop( e );
+	},
+	
+	/**
+	 * Starts the drag
+	 * 
+	 * @access private
+	 * @param obj Event
+	 * @return void
+	 */
+	startDrag: function( e ) {	
+		this.selArea.show();
+		this.clickCoords = this.getCurPos( e );
+     	
+    	this.setAreaCoords( { x1: this.clickCoords.x, y1: this.clickCoords.y, x2: this.clickCoords.x, y2: this.clickCoords.y }, false, false, null );
+    	
+    	this.dragging = true;
+    	this.onDrag( e ); // incase the user just clicks once after already making a selection
+    	Event.stop( e );
+	},
+	
+	/**
+	 * Gets the current cursor position relative to the image
+	 * 
+	 * @access private
+	 * @param obj Event
+	 * @return obj x,y pixels of the cursor
+	 */
+	getCurPos: function( e ) {
+		// get the offsets for the wrapper within the document
+		var el = this.imgWrap, wrapOffsets = Position.cumulativeOffset( el );
+		// remove any scrolling that is applied to the wrapper (this may be buggy) - don't count the scroll on the body as that won't affect us
+		while( el.nodeName != 'BODY' ) {
+			wrapOffsets[1] -= el.scrollTop  || 0;
+			wrapOffsets[0] -= el.scrollLeft || 0;
+			el = el.parentNode;
+	    }		
+		return curPos = { 
+			x: Event.pointerX(e) - wrapOffsets[0],
+			y: Event.pointerY(e) - wrapOffsets[1]
+		}
+	},
+  	
+  	/**
+  	 * Performs the drag for both resize & inital draw dragging
+  	 * 
+  	 * @access private
+	 * @param obj Event
+	 * @return void
+	 */
+  	onDrag: function( e ) {
+  		if( this.dragging || this.resizing ) {	
+  		
+  			var resizeHandle = null;
+  			var curPos = this.getCurPos( e );			
+			var newCoords = this.cloneCoords( this.areaCoords );
+  			var direction = { x: 1, y: 1 };
+  	  					
+		    if( this.dragging ) {
+		    	if( curPos.x < this.clickCoords.x ) direction.x = -1;
+		    	if( curPos.y < this.clickCoords.y ) direction.y = -1;
+		    	
+				this.transformCoords( curPos.x, this.clickCoords.x, newCoords, 'x' );
+				this.transformCoords( curPos.y, this.clickCoords.y, newCoords, 'y' );
+			} else if( this.resizing ) {
+				resizeHandle = this.resizeHandle;			
+				// do x movements first
+				if( resizeHandle.match(/E/) ) {
+					// if we're moving an east handle
+					this.transformCoords( curPos.x, this.startCoords.x1, newCoords, 'x' );	
+					if( curPos.x < this.startCoords.x1 ) direction.x = -1;
+				} else if( resizeHandle.match(/W/) ) {
+					// if we're moving an west handle
+					this.transformCoords( curPos.x, this.startCoords.x2, newCoords, 'x' );
+					if( curPos.x < this.startCoords.x2 ) direction.x = -1;
+				}
+									
+				// do y movements second
+				if( resizeHandle.match(/N/) ) {
+					// if we're moving an north handle	
+					this.transformCoords( curPos.y, this.startCoords.y2, newCoords, 'y' );
+					if( curPos.y < this.startCoords.y2 ) direction.y = -1;
+				} else if( resizeHandle.match(/S/) ) {
+					// if we're moving an south handle
+					this.transformCoords( curPos.y, this.startCoords.y1, newCoords, 'y' );	
+					if( curPos.y < this.startCoords.y1 ) direction.y = -1;
+				}	
+							
+			}
+		
+			this.setAreaCoords( newCoords, false, e.shiftKey, direction, resizeHandle );
+			this.drawArea();
+			Event.stop( e ); // stop the default event (selecting images & text) in Safari & IE PC
+		}
+	},
+	
+	/**
+	 * Applies the appropriate transform to supplied co-ordinates, on the
+	 * defined axis, depending on the relationship of the supplied values
+	 * 
+	 * @access private
+	 * @param int Current value of pointer
+	 * @param int Base value to compare current pointer val to
+	 * @param obj Coordinates to apply transformation on x1, x2, y1, y2
+	 * @param string Axis to apply transformation on 'x' || 'y'
+	 * @return void
+	 */
+	transformCoords : function( curVal, baseVal, coords, axis ) {
+		var newVals = [ curVal, baseVal ];
+		if( curVal > baseVal ) newVals.reverse();
+		coords[ axis + '1' ] = newVals[0];
+		coords[ axis + '2' ] = newVals[1];		
+	},
+	
+	/**
+	 * Ends the crop & passes the values of the select area on to the appropriate 
+	 * callback function on completion of a crop
+	 * 
+	 * @access private
+	 * @return void
+	 */
+	endCrop : function() {
+		this.dragging = false;
+		this.resizing = false;
+		
+		this.options.onEndCrop(
+			this.areaCoords,
+			{
+				width: this.calcW(), 
+				height: this.calcH() 
+			}
+		);
+	},
+	
+	/**
+	 * Abstract method called on the end of initialization
+	 * 
+	 * @access private
+	 * @abstract
+	 * @return void
+	 */
+	subInitialize: function() {},
+	
+	/**
+	 * Abstract method called on the end of drawArea()
+	 * 
+	 * @access private
+	 * @abstract
+	 * @return void
+	 */
+	subDrawArea: function() {}
+};
+
+
+
+
+/**
+ * Extend the Cropper.Img class to allow for presentation of a preview image of the resulting crop,
+ * the option for displayOnInit is always overridden to true when displaying a preview image
+ * 
+ * Usage:
+ * 	@param obj Image element to attach to
+ * 	@param obj Optional options:
+ * 		- see Cropper.Img for base options
+ * 		
+ * 		- previewWrap obj
+ * 			HTML element that will be used as a container for the preview image		
+ */
+Cropper.ImgWithPreview = Class.create();
+
+Object.extend( Object.extend( Cropper.ImgWithPreview.prototype, Cropper.Img.prototype ), {
+	
+	/**
+	 * Implements the abstract method from Cropper.Img to initialize preview image settings.
+	 * Will only attach a preview image is the previewWrap element is defined and the minWidth
+	 * & minHeight options are set.
+	 * 
+	 * @see Croper.Img.subInitialize
+	 */
+	subInitialize: function() {
+		/**
+		 * Whether or not we've attached a preview image
+		 * @var boolean
+		 */
+		this.hasPreviewImg = false;
+		if( typeof(this.options.previewWrap) != 'undefined' 
+			&& this.options.minWidth > 0 
+			&& this.options.minHeight > 0
+		) {
+			/**
+			 * The preview image wrapper element
+			 * @var obj HTML element
+			 */
+			this.previewWrap 	= $( this.options.previewWrap );
+			/**
+			 * The preview image element
+			 * @var obj HTML IMG element
+			 */
+			this.previewImg 	= this.img.cloneNode( false );
+			// set the ID of the preview image to be unique
+			this.previewImg.id	= 'imgCrop_' + this.previewImg.id;
+			
+						
+			// set the displayOnInit option to true so we display the select area at the same time as the thumbnail
+			this.options.displayOnInit = true;
+
+			this.hasPreviewImg 	= true;
+			
+			this.previewWrap.addClassName( 'imgCrop_previewWrap' );
+			
+			this.previewWrap.setStyle(
+			 { 
+			 	width: this.options.minWidth + 'px',
+			 	height: this.options.minHeight + 'px'
+			 }
+			);
+			
+			this.previewWrap.appendChild( this.previewImg );
+		}
+	},
+	
+	/**
+	 * Implements the abstract method from Cropper.Img to draw the preview image
+	 * 
+	 * @see Croper.Img.subDrawArea
+	 */
+	subDrawArea: function() {
+		if( this.hasPreviewImg ) {
+			// get the ratio of the select area to the src image
+			var calcWidth = this.calcW();
+			var calcHeight = this.calcH();
+			// ratios for the dimensions of the preview image
+			var dimRatio = { 
+				x: this.imgW / calcWidth, 
+				y: this.imgH / calcHeight 
+			}; 
+			//ratios for the positions within the preview
+			var posRatio = { 
+				x: calcWidth / this.options.minWidth, 
+				y: calcHeight / this.options.minHeight 
+			};
+			
+			// setting the positions in an obj before apply styles for rendering speed increase
+			var calcPos	= {
+				w: Math.ceil( this.options.minWidth * dimRatio.x ) + 'px',
+				h: Math.ceil( this.options.minHeight * dimRatio.y ) + 'px',
+				x: '-' + Math.ceil( this.areaCoords.x1 / posRatio.x )  + 'px',
+				y: '-' + Math.ceil( this.areaCoords.y1 / posRatio.y ) + 'px'
+			}
+			
+			var previewStyle 	= this.previewImg.style;
+			previewStyle.width 	= calcPos.w;
+			previewStyle.height	= calcPos.h;
+			previewStyle.left	= calcPos.x;
+			previewStyle.top	= calcPos.y;
+		}
+	}
+	
+});
Index: /afridex/plugins/Flutter/js/lightbox.js
===================================================================
--- /afridex/plugins/Flutter/js/lightbox.js (revision 21)
+++ /afridex/plugins/Flutter/js/lightbox.js (revision 21)
@@ -0,0 +1,246 @@
+/*
+Created By: Chris Campbell
+Website: http://particletree.com
+Date: 2/1/2006
+
+Inspired by the lightbox implementation found at http://www.huddletogether.com/projects/lightbox/
+*/
+
+/*-------------------------------GLOBAL VARIABLES------------------------------------*/
+
+var detect = navigator.userAgent.toLowerCase();
+var OS,browser,version,total,thestring;
+
+/*-----------------------------------------------------------------------------------------------*/
+
+//Browser detect script origionally created by Peter Paul Koch at http://www.quirksmode.org/
+
+function getBrowserInfo() {
+	if (checkIt('konqueror')) {
+		browser = "Konqueror";
+		OS = "Linux";
+	}
+	else if (checkIt('safari')) browser 	= "Safari"
+	else if (checkIt('omniweb')) browser 	= "OmniWeb"
+	else if (checkIt('opera')) browser 		= "Opera"
+	else if (checkIt('webtv')) browser 		= "WebTV";
+	else if (checkIt('icab')) browser 		= "iCab"
+	else if (checkIt('msie')) browser 		= "Internet Explorer"
+	else if (!checkIt('compatible')) {
+		browser = "Netscape Navigator"
+		version = detect.charAt(8);
+	}
+	else browser = "An unknown browser";
+
+	if (!version) version = detect.charAt(place + thestring.length);
+
+	if (!OS) {
+		if (checkIt('linux')) OS 		= "Linux";
+		else if (checkIt('x11')) OS 	= "Unix";
+		else if (checkIt('mac')) OS 	= "Mac"
+		else if (checkIt('win')) OS 	= "Windows"
+		else OS 								= "an unknown operating system";
+	}
+}
+
+function checkIt(string) {
+	place = detect.indexOf(string) + 1;
+	thestring = string;
+	return place;
+}
+
+/*-----------------------------------------------------------------------------------------------*/
+
+Event.observe(window, 'load', initialize, false);
+Event.observe(window, 'load', getBrowserInfo, false);
+Event.observe(window, 'unload', Event.unloadCache, false);
+
+var lightbox = Class.create();
+
+lightbox.prototype = {
+
+	yPos : 0,
+	xPos : 0,
+
+	initialize: function(ctrl) {
+		this.hrefarray = ctrl.href.split("?");
+		this.content = this.hrefarray[0];
+		this.pars = this.hrefarray[1];
+		Event.observe(ctrl, 'click', this.activate.bindAsEventListener(this), false);
+		ctrl.onclick = function(){return false;};
+	},
+	
+	// Turn everything on - mainly the IE fixes
+	activate: function(){
+		if (browser == 'Internet Explorer'){
+			this.getScroll();
+			this.prepareIE('100%', 'hidden');
+			this.setScroll(0,0);
+			this.hideSelects('hidden');
+		}
+		this.displayLightbox("block");
+	},
+	
+	// Ie requires height to 100% and overflow hidden or else you can scroll down past the lightbox
+	prepareIE: function(height, overflow){
+		bod = document.getElementsByTagName('body')[0];
+		bod.style.height = height;
+		bod.style.overflow = overflow;
+  
+		htm = document.getElementsByTagName('html')[0];
+		htm.style.height = height;
+		htm.style.overflow = overflow; 
+	},
+	
+	// In IE, select elements hover on top of the lightbox
+	hideSelects: function(visibility){
+		selects = document.getElementsByTagName('select');
+		for(i = 0; i < selects.length; i++) {
+			selects[i].style.visibility = visibility;
+		}
+	},
+	
+	// Taken from lightbox implementation found at http://www.huddletogether.com/projects/lightbox/
+	getScroll: function(){
+		if (self.pageYOffset) {
+			this.yPos = self.pageYOffset;
+		} else if (document.documentElement && document.documentElement.scrollTop){
+			this.yPos = document.documentElement.scrollTop; 
+		} else if (document.body) {
+			this.yPos = document.body.scrollTop;
+		}
+	},
+	
+	setScroll: function(x, y){
+		window.scrollTo(x, y); 
+	},
+	
+	displayLightbox: function(display){
+		$('overlay').style.display = display;
+		$('lightbox').style.display = display;
+		if(display != 'none') {
+			this.loadInfo();
+			Event.observe('overlay', 'click', this.cancel.bindAsEventListener(this), false);
+		}
+	},
+	
+	// Begin Ajax request based off of the href of the clicked linked
+	loadInfo: function() {
+		var myAjax = new Ajax.Request(
+        uncache(this.content),
+        {method: 'get', parameters: this.pars, onComplete: this.processInfo.bindAsEventListener(this)}
+		);
+		
+	},
+	
+	// Display Ajax response
+	processInfo: function(response){
+		info = "<div id='lbContent'>" + response.responseText + "</div>";
+		new Insertion.Before($('lbLoadMessage'), info)
+		$('lightbox').className = "done";	
+		this.actions();
+		Event.observe($('gmodule_content'), 'load', this.gmoduleListener, false);
+	},
+	
+	// Search through new links within the lightbox, and attach click event
+	actions: function(){
+		lbActions = document.getElementsByClassName('lbAction');
+
+		for(i = 0; i < lbActions.length; i++) {
+			Event.observe(lbActions[i], 'click', this[lbActions[i].rel].bindAsEventListener(this), false);
+			lbActions[i].onclick = function(){return false;};
+		}
+		
+		// Added by Karsten for Canvas
+		// new Form.EventObserver('lightbox_form',canvas_form_save);
+
+	},
+	
+	gmoduleListener: function() {
+		var gmodule = $('gmodule_content').contentDocument.$('thecode');
+		alert(gmodule);
+	},
+	
+	// Example of creating your own functionality once lightbox is initiated
+	insert: function(e){
+	   link = Event.element(e).parentNode;
+	   Element.remove($('lbContent'));
+	   var myAjax = new Ajax.Request(
+			  link.href,
+			  {method: 'post', parameters: "", onComplete: this.processInfo.bindAsEventListener(this)}
+	   );
+	},
+	
+	// Example of creating your own functionality once lightbox is initiated
+	deactivate: function(){
+		
+		// Added by Karsten for Canvas
+		canvas_form_save();
+	
+		Element.remove($('lbContent'));
+		
+		if (browser == "Internet Explorer"){
+			this.setScroll(0,this.yPos);
+			this.prepareIE("auto", "auto");
+			this.hideSelects("visible");
+		}
+		this.displayLightbox("none");
+	},
+
+	cancel: function(){
+	
+		Element.remove($('lbContent'));
+		
+		if (browser == "Internet Explorer"){
+			this.setScroll(0,this.yPos);
+			this.prepareIE("auto", "auto");
+			this.hideSelects("visible");
+		}
+		this.displayLightbox("none");
+	}
+	
+}
+
+/*-----------------------------------------------------------------------------------------------*/
+
+// Added by Karsten, Canvas function: Allows save of lightbox form data
+function canvas_form_save() {
+	if($('lightbox_form')) Canvas.saveBlock('lightbox_form', Form.serialize('lightbox_form'));
+}
+
+
+
+// Onload, make all links that need to trigger a lightbox active
+function initialize(){
+	addLightboxMarkup();
+	lbox = document.getElementsByClassName('lbOn');
+	for(i = 0; i < lbox.length; i++) {
+		valid = new lightbox(lbox[i]);
+	}
+}
+
+// Add in markup necessary to make this work. Basically two divs:
+// Overlay holds the shadow
+// Lightbox is the centered square that the content is put into.
+function addLightboxMarkup() {
+	bod 				= document.getElementsByTagName('body')[0];
+	overlay 			= document.createElement('div');
+	overlay.id		= 'overlay';
+	lb					= document.createElement('div');
+	lb.id				= 'lightbox';
+	lb.className 	= 'loading';
+	lb.innerHTML	= '<div id="lbLoadMessage">' +
+						  '<p><img src=JS_CANVASURI + "images/spinner.gif" alt="spinner"/></p>' +
+						  '</div>';
+	bod.appendChild(overlay);
+	bod.appendChild(lb);
+}
+
+function uncache(url){
+	var d = new Date();
+	var time = d.getTime();
+	if (url.indexOf('?') > 0 )
+		return url + '&time='+time;
+	else
+		return url + '?time='+time;
+}
Index: /afridex/plugins/Flutter/js/canvas-ajax.js.php
===================================================================
--- /afridex/plugins/Flutter/js/canvas-ajax.js.php (revision 21)
+++ /afridex/plugins/Flutter/js/canvas-ajax.js.php (revision 21)
@@ -0,0 +1,559 @@
+<?php require('../../../../wp-config.php'); ?>
+<?php if(get_option('canvas_auto_publish') == 'true') $on_update = ', onUpdate:function(){ Canvas.saveAll;}';
+		else $on_update = ', onUpdate: Canvas.changePublish'; ?>
+Event.observe(window, 'load', function() { Canvas.initialize() }, false);
+Event.observe(window, 'unload', Event.unloadCache, false);
+
+var CanvasAjax = Ajax;
+
+
+var Canvas = {
+
+	addListItem: function(id, name) {
+		var count = $F('listcount');
+		var elementId = id.parentNode;
+		var copiedElement = elementId.cloneNode(true);
+		copiedElement.id = name + count;
+		copiedElement.firstChild.name = count + 'canvaslist_' + name;
+		var parent = elementId.parentNode;
+		parent.insertBefore(copiedElement, elementId.nextSibling);
+		$('listcount').value++;
+	},
+
+	canvasOption: function(name, value) {
+		var url = JS_CANVASURI + 'ajax/canvas-save-option.php';
+		var pars = name+'='+value;
+		var myAjax = new Ajax.Request(url, 
+			{method: 'post', 
+			parameters: pars }
+			);
+	},
+
+
+
+	changePublishImg: function() {
+		image = $('publish_image');
+		if(image) image.src = JS_CANVASURI + 'images/publish-changed.gif';
+	},
+	
+	changePublish: function(myelement) {
+		
+		Canvas.changePublishImg();
+		Canvas.canvasHideMsg();
+		
+		if (myelement.id != "shelf"){
+			default_template_name = $(myelement.id + '_' + 'template_name').value;
+			template_size = Canvas.canvasMapTemplateSize($(myelement.id + '_' + 'template_size').value);
+			allow_other_sizes = $(myelement.id + '_' + 'allow_other_sizes').value;
+			allow_other_modules = $(myelement.id + '_' + 'allow_other_modules').value;
+			default_module = $(myelement.id + '_' + 'default_module').value;
+			
+			var children = myelement.childElements();
+			children.each( 
+				function(myelement){
+					if (myelement.hasClassName('container-plugin')){
+						if (!(allow_other_modules) && default_module != $F(myelement.id + '_module_name')){
+							myelement.remove();
+							$('shelf').insert(myelement);
+							Canvas.canvasDisplayMsg("Failed! Default module (" + default_module + ") only can be placed in this canvas.")
+						}
+						else if (Canvas.canvasMapTemplateSize($F(myelement.id + '_module_size')) > template_size){
+							if (allow_other_sizes){
+								Canvas.canvasDisplayMsg("Warning! You have placed a module with size greater than the canvas size.")
+							}
+							else{	
+								myelement.remove();
+								$('shelf').insert(myelement);
+								Canvas.canvasDisplayMsg("Failed! The selected module size is greater than the canvas size.")
+							}
+						}
+						if ($F(myelement.id + '_module_template_name') == "") $(myelement.id + '_module_template_name').value = default_template_name;
+						if ($F(myelement.id + '_module_size') == "") $(myelement.id + '_module_size').value = template_size;
+
+					}
+			
+			});		
+		}
+		else{
+			// Make sure all template name and sizes for modules on the shelf are reset
+			myelement.childElements().each( 
+				function(childElement){
+					if (childElement.hasClassName('container-plugin')){
+						$(childElement.id + '_module_template_name').value = "";
+						$(childElement.id + '_module_size').value = "";
+						
+					}
+				}
+			);
+		}	
+	},
+
+	canvasDisplayMsg: function(msg) {
+		
+		$('canvas_status').style.display = "";
+		$('canvas_status').innerHTML = "<p>" + msg +  "</p>";
+	},
+
+	canvasHideMsg: function(msg) {
+		$('canvas_status').style.display = "none";
+	},
+
+
+	canvasMapTemplateSize: function(templateValue) {
+		return templateValue;
+		/*if (templateValue > 0) return templateValue;
+		switch (templateValue){
+
+			case "-1": // MODULE_TEMPLATE_SIZE_SMALL
+				return 240;
+				break;
+
+			case "-2": // MODULE_TEMPLATE_SIZE_MEDIUM
+				return 480;
+				break;
+
+			case "-3": // MODULE_TEMPLATE_SIZE_LARGE
+				return 720;
+				break;
+
+			case "-4": // MODULE_TEMPLATE_SIZE_FULL
+				return 960;
+				break;
+		}*/
+		
+	},
+
+	endDrag: function(myelement) {
+		//myelement.revert = true;
+		//myelement.options.revert = true;
+		
+		
+	},
+
+	
+
+	createDroppables: function(id) {
+		var elements = $$('#'+id+' div.canvas_droppable_zone');
+		var shelf = $('shelf');
+		Sortable.create('shelf',{tag:'div', only: ['container','container-plugin'], overlap:'horizontal', handle:'handle', constraint:false, containment:elements.concat(shelf), dropOnEmpty:true<?php echo $on_update; ?>});
+
+		elements.each(function(item){
+			Sortable.create(item.id,{tag:'div', only: ['container','container-plugin'], overlap:'horizontal', handle:'handle', constraint:false, containment:elements.concat(shelf), dropOnEmpty:true<?php echo $on_update; ?>});
+		
+			//Grab default module
+			default_module = $(item.id + '_' + 'default_module').value;
+			allow_other_modules = $(item.id + '_' + 'allow_other_modules').value;
+
+			default_template_name = $(item.id + '_' + 'template_name').value;
+			template_size = Canvas.canvasMapTemplateSize($(item.id + '_' + 'template_size').value);
+			
+			var children = $('shelf').childElements();
+			children.each( 
+				function(myelement){
+					if (default_module == $F(myelement.id + '_module_name')){
+						// Check whether there is any modules in this canvas
+						var children = $(item.id).childElements();
+						modulesExist = false;
+						children.each( function(item){
+							if (item.hasClassName('container-plugin')){
+								modulesExist = true;
+							}
+						});
+
+						if (!modulesExist && LOAD_DEFAULT_MODULES){
+							myelement.remove();
+							$(item.id).insert(myelement);
+
+							if ($F(myelement.id + '_module_template_name') == "") $(myelement.id + '_module_template_name').value = default_template_name;
+							if ($F(myelement.id + '_module_size') == "") $(myelement.id + '_module_size').value = template_size;
+
+							Canvas.changePublish($('shelf'));
+							Canvas.canvasDisplayMsg("Default modules are added automatically to the canvas. Click Publish to save changes.");
+							if (!(allow_other_modules)){
+								myelement.select( 'h6.handle').find(function(s) {s.remove();});
+							}
+						}
+					}
+					
+				}
+			);
+			
+			var children1 = $(item.id).childElements();
+			children1.each( 
+				function(myelement){
+					//console.log(item.id +" - " + myelement.id + '_module_name' +" - " +  Object.isElement(myelement.id + '_module_name') +" - " +  default_module+" - " ) ;
+					if ($(myelement.id).hasClassName('container-plugin') && default_module == $F(myelement.id + '_module_name')){
+						if (!(allow_other_modules)){
+							myelement.select( 'h6.handle').find(function(s) {s.remove();});
+						}
+					}
+					
+				}
+			);
+		});
+			
+
+				
+		/*Droppables.add('shelf', {
+			accept: ['container','container-plugin']
+		});
+
+		var elements = $$('#'+id+' div.canvas_droppable_zone');
+		elements.each(function(item){
+			Droppables.add(item.id, {
+					accept: ['container','container-plugin']
+					});	
+			
+		});
+
+
+		$$('#'+$F('canvas_page')+' div.canvas_droppable_zone').concat($('shelf')).each(function(item){
+			var children = item.childElements();
+			children.each( function(myelement){
+						if (myelement.hasClassName('container-plugin')){
+							new Draggable(myelement.id, {});
+						}
+			});	
+		});*/
+	},
+	
+	destroyDroppables: function(id) {
+		Sortable.destroy('shelf');
+		$$('#'+id+' div.canvas_droppable_zone').each(function(item){
+			Sortable.destroy(item.id);
+		});
+	},
+	
+
+	gallerySwitch: function(id) {
+		if($F('selected_image') != '') $($F('selected_image')).className = '';
+		$(id).className = 'selected_image';
+		$('selected_image').value = id;
+		$('path').value = $F('directory')+id;
+	},
+	
+	importXml: function() {
+		var pars = 'path_to_xml='+$F('path_to_xml');
+		$('export_message').className = "importing";
+		var target = 'export_message';
+		var url = JS_CANVASURI + 'ajax/canvas-import.php';
+		var myAjax = new Ajax.Updater({success: target}, url, 
+			{method: 'post', 
+			parameters: pars,
+			onComplete: function() { $('export_message').className = ''; $('export_message').style.paddingLeft = '0px'; }
+			});
+	},
+	
+	initialize: function() {
+		//var bigdiv = document.createElement('div');
+		//document.body.insertBefore(bigdiv, $('wphead'));
+		//bigdiv.appendChild($('wphead'));
+		//bigdiv.appendChild($('adminmenu'));
+		//bigdiv.appendChild($('submenu'));
+	
+		var hide_instructions = $('hide_instructions');
+		if(hide_instructions) {
+			hide_instructions.onclick = function(){ 
+				Canvas.canvasOption('canvas_show_instructions','0');
+				new Effect.Fade('instructions', { duration: 0.4 });
+				};
+		}
+		$$('ul.page_dropdown ul li a').each(function(item){
+			//$(item).onclick = function(){ Canvas.switchCanvas(item.id) };
+		});
+		//$('show_content').onclick = function(){ Canvas.toggleMenu('show_content', 'hide_content', bigdiv); };
+		//$('hide_content').onclick = function(){ Canvas.toggleMenu('show_content', 'hide_content', bigdiv); };
+		$$('div.titlebar a img').each(function(item){
+			$(item).onclick = function(){ Canvas.sortGroups(item.id) };
+		});
+		$$('.canvas_droppable_zone h8').each(function(item){
+			$(item).onclick = function(){ Canvas.toggleZoneOptions(item.id) };
+		});
+		if($('canvas_page')) {
+			mainpage = $F('canvas_page');
+			curr_page = gup('t_page');
+			if (curr_page == '')
+				curr_page = mainpage;
+			$(curr_page).style.display = "block";
+			var mainmenu = $(curr_page+'__menu');
+			if(mainmenu) { mainmenu.parentNode.className = 'selected'; }
+			var publish = $('publish');
+			if(publish) { publish.onclick = Canvas.saveAll; }
+			Canvas.createDroppables(mainpage);
+		}
+	},
+	
+	removeListItem: function(id) {
+		if(window.confirm('Are you sure you want to remove this item?')) {
+			new Effect.Fade(id.parentNode,{ duration: 0.6, afterFinish: function(){ Element.remove(id.parentNode); } });
+		}
+	},
+
+	restorePublish: function() {
+		image = $('publish_image');
+		if(image) image.src = JS_CANVASURI + 'images/publish.gif';
+	},
+	
+	saveAll: function() {
+		Canvas.canvasHideMsg();
+
+		if($('publish')) { 
+			$('publish').setStyle({backgroundImage: "url(" + JS_CANVASURI + "images/spinner.gif)"}); 
+		}
+		var layout_params;
+		var saveCheckError = false;
+		$$('#'+$F('canvas_page')+' div.canvas_droppable_zone').concat($('shelf')).each(function(item){
+
+			if (item.id == 'shelf') {
+				layout_params = Canvas.serializeHelper(layout_params, item.id);
+				return; 
+			}
+
+			template_size = Canvas.canvasMapTemplateSize($(item.id + '_' + 'template_size').value);
+			allow_other_sizes = $(item.id + '_' + 'allow_other_sizes').value;
+
+			// Verify there are no invalid template size
+			item.childElements().each(
+				function(myelement){
+					if (myelement.hasClassName('container-plugin')){
+						if (Canvas.canvasMapTemplateSize($F(myelement.id + '_module_size')) > template_size){
+							if (!allow_other_sizes){
+								Canvas.canvasDisplayMsg("Error! You have placed module (" + $F(myelement.id + '_module_name') + ") with size greater than the canvas size.")
+								saveCheckError = true;
+							}
+						}
+							
+					}
+				});
+
+			layout_params = Canvas.serializeHelper(layout_params, item.id);
+		});
+
+		if (!saveCheckError) {
+			Canvas.saveLayout(layout_params);
+			Canvas.zoneOptions();
+		}
+		else{
+			if($('publish')){ $('publish').style.backgroundImage = "url('')"; Canvas.restorePublish(); }
+		}
+	},
+	
+	saveBlock: function(element, pars) {
+
+		// Update the module template name and size
+		$($F('updated_module_template_size_id')).value = $F('template_size'); //.getValue();
+		$($F('updated_module_template_name_id')).value = $F('template_name');
+
+		var url = JS_CANVASURI + 'ajax/canvas-save-plugin.php';
+		var myAjax = new Ajax.Request(url, 
+			{method: 'get', 
+			parameters: pars }
+			);
+	},
+	
+	saveLayout: function(pars) {
+		var url = JS_CANVASURI + 'ajax/canvas-save-layout.php';
+		var myAjax = new Ajax.Request(url, 
+			{method: 'get', 
+			parameters: pars
+			});
+	},
+		
+	saveZone: function(pars) {
+		var url = JS_CANVASURI + 'ajax/canvas-save-zone-options.php';
+		var myAjax = new Ajax.Request(url, 
+			{method: 'get', 
+			parameters: pars,
+			onComplete: function(){ if($('publish')){ $('publish').style.backgroundImage = "url('')"; Canvas.restorePublish(); } }
+			});
+	},
+
+	serializeHelper: function(layout_params, block) {
+		layout_params = Try.these (
+			function() {
+				  //template_name = Canvas.getBlockAttribute(block, 'template_name');
+				  //template_size = Canvas.getBlockAttribute(block, 'template_size');	
+	
+
+
+				  content_exists = Canvas.customSerialize(block);//Sortable.serialize(block);
+
+				  if (content_exists == '') {
+					  return layout_params;
+				  } else if (typeof(layout_params) == 'undefined')  {
+					  return content_exists;
+				  } else {
+					  return layout_params + content_exists;
+				  }
+			}
+		);
+		return layout_params;
+	},
+
+	getBlockAttribute: function(block, attribute) {
+		if (block != "shelf") { 
+			attribute_val = $(block + '_' + attribute).value;
+			return '_' + block + '_' + attribute + '=' + attribute_val + "&";
+		}
+		return "";
+	},
+
+	customSerialize: function(block) {
+		newContent = Sortable.serialize(block) + "&";
+	
+		if (block != "shelf") { 
+			name = encodeURIComponent(block);
+
+			var children = $(block).childElements();
+			children.each( 
+				function(item){
+					if (item.hasClassName('container-plugin')){
+						newContent = newContent + "_" + name + "_template_size[]=" + encodeURIComponent($F(item.id + '_module_size')) + "&";
+						newContent = newContent + "_" + name + "_template_name[]=" + encodeURIComponent($F(item.id + '_module_template_name')) + "&";
+				}
+			});
+		}
+
+		return newContent;
+	},
+
+
+
+
+
+	
+	sortGroups: function(group) {
+		if(group == 'all') {
+			$$('div.shelf_column div').each(function(item){
+				$(item).style.display = 'block';
+			});
+		} else {
+			$$('div.shelf_column div.container').each(function(item){
+				$(item).style.display = 'none';
+			});
+			$$('div.shelf_column div.container-plugin').each(function(item){
+				$(item).style.display = 'none';
+			});
+			$$('div.shelf_column div.'+group).each(function(item){
+				$(item).style.display = 'block';
+			});
+		}
+	},
+	
+	switchCanvas: function(element) {
+		var currentPage = $F('canvas_page');
+		$$('ul.page_dropdown ul li').each(function(item){ if($(item).className == 'selected') $(item).className = ''; });
+		$(element).parentNode.className = 'selected';
+		var string = element.split('__');
+		$(currentPage).style.display = 'none';
+		Canvas.destroyDroppables(currentPage);
+		$(string[0]).style.display = 'block';
+		$('canvas_page').value = string[0];
+		Canvas.createDroppables(string[0]);
+	},
+	
+	toggleMenu: function(show,hide,block) {
+		if($(block).style.display == "none") {
+			$(show).style.display  = "none";
+			$(hide).style.display = "inline";
+			new Effect.BlindDown(block, {duration: 0.3 })
+		} else {
+			$(show).style.display  = "inline";
+			$(hide).style.display = "none";
+			new Effect.BlindUp(block, {duration: 0.3 })
+		}
+	},
+	
+	toggleZoneOptions: function(id) {
+		if($(id.replace('togglezoneoption_','options_')).style.display == 'block') {
+			$(id.replace('togglezoneoption_','options_')).style.display = 'none';
+			$(id).style.backgroundColor = '#CFCFCF';
+		} else {
+			$(id.replace('togglezoneoption_','options_')).style.display = 'block';
+			$(id).style.backgroundColor = 'transparent';
+		}
+	},
+	
+	zoneHandler: function(zone, position) {
+		$$('span#options_' + zone + ' img').each(function(item){
+			item.src = item.src.replace('_down', '_up');
+			item.src = item.src.replace(position+'_up', position+'_down');
+		});
+		$('zoneoption_'+zone).value = position;
+		Canvas.changePublish();
+	},
+
+	zoneOptions: function() {
+		string = '';
+		$$('#'+$F('canvas_page')+' select.zone_content_options').each(function(item){
+			string = string + '&' + item.id + '=' + $F(item.id);
+		});
+		Canvas.saveZone(string);
+	}
+};
+
+
+// This code was found here: http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html
+
+function f_clientWidth() {
+	return f_filterResults (
+		window.innerWidth ? window.innerWidth : 0,
+		document.documentElement ? document.documentElement.clientWidth : 0,
+		document.body ? document.body.clientWidth : 0
+	);
+}
+function f_clientHeight() {
+	return f_filterResults (
+		window.innerHeight ? window.innerHeight : 0,
+		document.documentElement ? document.documentElement.clientHeight : 0,
+		document.body ? document.body.clientHeight : 0
+	);
+}
+function f_scrollLeft() {
+	return f_filterResults (
+		window.pageXOffset ? window.pageXOffset : 0,
+		document.documentElement ? document.documentElement.scrollLeft : 0,
+		document.body ? document.body.scrollLeft : 0
+	);
+}
+function f_scrollTop() {
+	return f_filterResults (
+		window.pageYOffset ? window.pageYOffset : 0,
+		document.documentElement ? document.documentElement.scrollTop : 0,
+		document.body ? document.body.scrollTop : 0
+	);
+}
+function f_filterResults(n_win, n_docel, n_body) {
+	var n_result = n_win ? n_win : 0;
+	if (n_docel && (!n_result || (n_result > n_docel)))
+		n_result = n_docel;
+	return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
+}
+
+/* This script and many more are available free online at
+The JavaScript Source :: http://javascript.internet.com
+Created by: Francis Cocharrua :: http://scripts.franciscocharrua.com/ */
+
+function Select_Value_Set(SelectName, Value) {
+	eval('SelectObject = document.' + SelectName + ';');
+
+	for(index = 0; index < SelectObject.length; index++) {
+		if(SelectObject[index].value == Value)
+			SelectObject.selectedIndex = index;
+	}
+}
+
+/*
+	Get the value of an HTML GET parameter
+*/
+function gup( name )
+{
+	name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+	var regexS = "[\\?&]"+name+"=([^&#]*)";
+	var regex = new RegExp( regexS );
+	var results = regex.exec( window.location.href );
+	if( results == null )
+		return "";
+	else
+		return results[1];
+}
Index: /afridex/plugins/Flutter/js/canvas-management.js
===================================================================
--- /afridex/plugins/Flutter/js/canvas-management.js (revision 21)
+++ /afridex/plugins/Flutter/js/canvas-management.js (revision 21)
@@ -0,0 +1,440 @@
+Event.observe(window, 'load', function() { CanvasManage.watchManagementFields() }, false);
+Event.observe(window, 'load', function() { CanvasManage.watchTitles() }, false);
+Event.observe(window, 'load', function() { CanvasManage.initializeButtons() }, false);
+Event.observe(window, 'unload', Event.unloadCache, false);
+
+var CanvasManage = {
+	
+	dbmanage: function(element) {
+		if(window.confirm('Are you sure you want to '+element+' Canvas?')) {
+		var pars = 'action='+element;
+		var target = 'message'
+		var url = JS_CANVASURI + 'ajax/canvas-rescan.php';
+		var myAjax = new Ajax.Updater({success: target}, url,
+			{method: 'get',
+			parameters: pars,
+			onComplete: function() { CanvasManage.updateAfterDBChange() }
+			});
+		}
+	},
+	
+	deleteBlock: function(element) {
+		if(window.confirm('Are you sure you want to delete this block?')) {
+		var item = 'block_'+element;
+		var pars = 'delete='+element;
+		var url = JS_CANVASURI + 'ajax/canvas-management_ajax.php';
+		var myAjax = new Ajax.Request(url,
+			{method: 'get',
+			parameters: pars,
+			onComplete: function() { CanvasManage.removeBlock(item) }
+			});
+		}
+	},
+	
+	duplicateBlock: function(element) {
+		var target = 'block_'+element;
+		var pars = 'duplicate='+element;
+		var url = JS_CANVASURI + 'ajax/canvas-management_ajax.php';
+		var myAjax = new Ajax.Updater({success: target}, url,
+			{method: 'get',
+			insertion: Insertion.After,
+			parameters: pars,
+			onComplete: function() { CanvasManage.updateButtons() }
+			});
+	},
+	
+	editTitle: function(item) {
+		new CanvasAjax.CanvasInPlaceEditor(item, JS_CANVASURI + 'ajax/canvas-management_ajax.php', {
+					callback: function(form, value){ return 'rename=true&id='+item.id+'&value='+escape(value); },
+					savingClassName: 'manageSaving'
+					});
+	},
+	
+	initializeButtons: function() {
+		document.getElementsByClassName('block_info').each(function(item){
+			Event.observe(item, 'click', function(){ CanvasManage.toggleBlockInfo(item.id); }, false);
+			if($('block_'+item.id).style.display == 'none') new Effect.Appear('block_'+item.id);
+		});
+		document.getElementsByClassName('delete_block').each(function(item){
+			Event.observe(item, 'click', function(){ CanvasManage.deleteBlock(item.id); }, false);
+		});
+		document.getElementsByClassName('duplicate_block').each(function(item){
+			Event.observe(item, 'click', function(){ CanvasManage.duplicateBlock(item.id); }, false);
+		});
+	},
+	
+	removeBlock: function(element) {
+		new Effect.Fade(element,{ duration: 0.6, afterFinish: function(){ Element.remove(element); CanvasManage.updateCount('-1') } });
+	},
+	
+	resetCount: function() {
+		var count = $$('ul.manage li').length;
+		count = parseInt(count);
+		$('count').firstChild.nodeValue = count;
+	},
+
+	sortGroups: function(group) {
+		if(group == 'all') {
+			$$('ul.manage li').each(function(item){
+				$(item).style.display = 'block';
+			});
+		} else {
+			$$('ul.manage li').each(function(item){
+				$(item).style.display = 'none';
+			});
+			$$('ul.manage li.'+group).each(function(item){
+				$(item).style.display = 'block';
+			});
+		}
+	},
+
+	toggleBlockInfo: function(element) {
+		item = 'info_'+element;
+		if($(item).style.display == 'none') {
+			new Effect.BlindDown(item,{ duration: 0.4 });
+		} else {
+			new Effect.BlindUp(item,{ duration: 0.4 });
+		}
+	},
+	
+	updateAfterDBChange: function() {
+		var target = 'manage';
+		var pars = 'update=all';
+		var url = JS_CANVASURI + 'ajax/canvas-management_ajax.php';
+		var myAjax = new Ajax.Updater({success: target}, url,
+			{method: 'get',
+			parameters: pars,
+			onComplete: function() { CanvasManage.initializeButtons(); CanvasManage.resetCount(); }
+			});
+	},
+
+	updateButtons: function() {
+		document.getElementsByClassName('delete_block').each(function(item){
+			if($('block_'+item.id).style.display == 'none') {
+				Event.observe(item, 'click', function(){ CanvasManage.deleteBlock(item.id); }, false);
+			}
+		});
+		document.getElementsByClassName('duplicate_block').each(function(item){
+			if($('block_'+item.id).style.display == 'none') {
+				Event.observe(item, 'click', function(){ CanvasManage.duplicateBlock(item.id); }, false);
+			}
+		});
+		document.getElementsByClassName('block_info').each(function(item){
+			if($('block_'+item.id).style.display == 'none') {
+				Event.observe(item, 'click', function(){ CanvasManage.toggleBlockInfo(item.id); }, false);
+				new Effect.Appear('block_'+item.id);
+			}
+		});
+		document.getElementsByClassName('title').each(function(item){
+			if(item.parentNode.parentNode.style.display == 'none') {
+				CanvasManage.editTitle(item);
+			}
+		});
+		updateCount('+1');
+	},
+	
+	updateCount: function(operation) {
+		var count = parseInt($('count').firstChild.nodeValue);
+		count = eval(count+operation);
+		$('count').firstChild.nodeValue = count;
+	},
+	
+	watchManagementFields: function() {
+		document.getElementsByClassName('dbmanage').each(function(item){
+			Event.observe(item, 'click', function(){ CanvasManage.dbmanage(item.id); }, false);
+		});
+	},
+	
+	watchTitles: function() {
+		document.getElementsByClassName('title').each(function(item){
+			CanvasManage.editTitle(item);
+		});
+	}
+};
+
+// Because Script.aculo.us does some dumb things with backgroundColor
+// in the Ajax.InPlaceEditor, we've hacked our own...
+
+CanvasAjax.CanvasInPlaceEditor = Class.create();
+CanvasAjax.CanvasInPlaceEditor.prototype = {
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = $(element);
+
+    this.options = Object.extend({
+      okButton: true,
+      okText: "ok",
+      cancelLink: true,
+      cancelText: "cancel",
+      savingText: "Saving...",
+      clickToEditText: "Click to edit",
+      okText: "ok",
+      rows: 1,
+      onFailure: function(transport) {
+        alert("Error communicating with the server: " + transport.responseText.stripTags());
+      },
+      callback: function(form) {
+        return Form.serialize(form);
+      },
+      handleLineBreaks: true,
+      loadingText: 'Loading...',
+      savingClassName: 'inplaceeditor-saving',
+      loadingClassName: 'inplaceeditor-loading',
+      formClassName: 'inplaceeditor-form',
+      externalControl: null,
+      submitOnBlur: false,
+      ajaxOptions: {},
+      evalScripts: false
+    }, options || {});
+
+    if(!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + "-inplaceeditor";
+      if ($(this.options.formId)) {
+        // there's already a form with that name, don't specify an id
+        this.options.formId = null;
+      }
+    }
+    
+    if (this.options.externalControl) {
+      this.options.externalControl = $(this.options.externalControl);
+    }
+    
+    this.element.title = this.options.clickToEditText;
+    
+    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+    Event.observe(this.element, 'click', this.onclickListener);
+    Event.observe(this.element, 'mouseover', this.mouseoverListener);
+    Event.observe(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.observe(this.options.externalControl, 'click', this.onclickListener);
+      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  },
+  enterEditMode: function(evt) {
+    if (this.saving) return;
+    if (this.editing) return;
+    this.editing = true;
+    this.onEnterEditMode();
+    if (this.options.externalControl) {
+      Element.hide(this.options.externalControl);
+    }
+    Element.hide(this.element);
+    this.createForm();
+    this.element.parentNode.insertBefore(this.form, this.element);
+    Field.scrollFreeActivate(this.editField);
+    // stop the event to avoid a page refresh in Safari
+    if (evt) {
+      Event.stop(evt);
+    }
+    return false;
+  },
+  createForm: function() {
+    this.form = document.createElement("form");
+    this.form.id = this.options.formId;
+    Element.addClassName(this.form, this.options.formClassName)
+    this.form.onsubmit = this.onSubmit.bind(this);
+
+    this.createEditField();
+
+    if (this.options.textarea) {
+      var br = document.createElement("br");
+      this.form.appendChild(br);
+    }
+
+    if (this.options.okButton) {
+      okButton = document.createElement("input");
+      okButton.type = "submit";
+      okButton.value = this.options.okText;
+      okButton.className = 'editor_ok_button';
+      this.form.appendChild(okButton);
+    }
+
+    if (this.options.cancelLink) {
+      cancelLink = document.createElement("a");
+      cancelLink.href = "#";
+      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+      cancelLink.onclick = this.onclickCancel.bind(this);
+      cancelLink.className = 'editor_cancel';      
+      this.form.appendChild(cancelLink);
+    }
+  },
+  hasHTMLLineBreaks: function(string) {
+    if (!this.options.handleLineBreaks) return false;
+    return string.match(/<br/i) || string.match(/<p>/i);
+  },
+  convertHTMLLineBreaks: function(string) {
+    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+  },
+  createEditField: function() {
+    var text;
+    if(this.options.loadTextURL) {
+      text = this.options.loadingText;
+    } else {
+      text = this.getText();
+    }
+
+    var obj = this;
+    
+    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+      this.options.textarea = false;
+      var textField = document.createElement("input");
+      textField.obj = this;
+      textField.type = "text";
+      textField.name = "value";
+      textField.value = text;
+      textField.className = 'editor_field';
+      var size = this.options.size || this.options.cols || 0;
+      if (size != 0) textField.size = size;
+      if (this.options.submitOnBlur)
+        textField.onblur = this.onSubmit.bind(this);
+      this.editField = textField;
+    } else {
+      this.options.textarea = true;
+      var textArea = document.createElement("textarea");
+      textArea.obj = this;
+      textArea.name = "value";
+      textArea.value = this.convertHTMLLineBreaks(text);
+      textArea.rows = this.options.rows;
+      textArea.cols = this.options.cols || 40;
+      textArea.className = 'editor_field';      
+      if (this.options.submitOnBlur)
+        textArea.onblur = this.onSubmit.bind(this);
+      this.editField = textArea;
+    }
+    
+    if(this.options.loadTextURL) {
+      this.loadExternalText();
+    }
+    this.form.appendChild(this.editField);
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  loadExternalText: function() {
+    Element.addClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = true;
+    new Ajax.Request(
+      this.options.loadTextURL,
+      Object.extend({
+        asynchronous: true,
+        onComplete: this.onLoadedExternalText.bind(this)
+      }, this.options.ajaxOptions)
+    );
+  },
+  onLoadedExternalText: function(transport) {
+    Element.removeClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = false;
+    this.editField.value = transport.responseText.stripTags();
+  },
+  onclickCancel: function() {
+    this.onComplete();
+    this.leaveEditMode();
+    return false;
+  },
+  onFailure: function(transport) {
+    this.options.onFailure(transport);
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+      this.oldInnerHTML = null;
+    }
+    return false;
+  },
+  onSubmit: function() {
+
+    var form = this.form;
+    var value = this.editField.value;
+    
+    this.onLoading();
+    
+    if (this.options.evalScripts) {
+      new Ajax.Request(
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this),
+          asynchronous:true, 
+          evalScripts:true
+        }, this.options.ajaxOptions));
+    } else  {
+      new Ajax.Updater(
+        { success: this.element,
+          failure: null }, 
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this)
+        }, this.options.ajaxOptions));
+    }
+
+    if (arguments.length > 1) {
+      Event.stop(arguments[0]);
+    }
+    return false;
+  },
+  onLoading: function() {
+    this.saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  showSaving: function() {
+    this.oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    Element.addClassName(this.element, this.options.savingClassName);
+    Element.show(this.element);
+  },
+  removeForm: function() {
+    if(this.form) {
+      if (this.form.parentNode) Element.remove(this.form);
+      this.form = null;
+    }
+  },
+  enterHover: function() {
+    if (this.saving) return;
+    if (this.effect) {
+      this.effect.cancel();
+    }
+    Element.addClassName(this.element, this.options.hoverClassName)
+  },
+  leaveHover: function() {
+    Element.removeClassName(this.element, this.options.hoverClassName)
+    if (this.saving) return;
+  },
+  leaveEditMode: function() {
+    Element.removeClassName(this.element, this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    Element.show(this.element);
+    if (this.options.externalControl) {
+      Element.show(this.options.externalControl);
+    }
+    this.editing = false;
+    this.saving = false;
+    this.oldInnerHTML = null;
+    this.onLeaveEditMode();
+  },
+  onComplete: function(transport) {
+    this.leaveEditMode();
+    if(this.options.onComplete)
+	    this.options.onComplete.bind(this)(transport, this.element);
+  },
+  onEnterEditMode: function() {},
+  onLeaveEditMode: function() {},
+  dispose: function() {
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+    }
+    this.leaveEditMode();
+    Event.stopObserving(this.element, 'click', this.onclickListener);
+    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  }
+};
Index: /afridex/plugins/Flutter/js/scriptaculous/builder.js
===================================================================
--- /afridex/plugins/Flutter/js/scriptaculous/builder.js (revision 21)
+++ /afridex/plugins/Flutter/js/scriptaculous/builder.js (revision 21)
@@ -0,0 +1,136 @@
+// script.aculo.us builder.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Builder = {
+  NODEMAP: {
+    AREA: 'map',
+    CAPTION: 'table',
+    COL: 'table',
+    COLGROUP: 'table',
+    LEGEND: 'fieldset',
+    OPTGROUP: 'select',
+    OPTION: 'select',
+    PARAM: 'object',
+    TBODY: 'table',
+    TD: 'table',
+    TFOOT: 'table',
+    TH: 'table',
+    THEAD: 'table',
+    TR: 'table'
+  },
+  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+  //       due to a Firefox bug
+  node: function(elementName) {
+    elementName = elementName.toUpperCase();
+    
+    // try innerHTML approach
+    var parentTag = this.NODEMAP[elementName] || 'div';
+    var parentElement = document.createElement(parentTag);
+    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+    } catch(e) {}
+    var element = parentElement.firstChild || null;
+      
+    // see if browser added wrapping tags
+    if(element && (element.tagName.toUpperCase() != elementName))
+      element = element.getElementsByTagName(elementName)[0];
+    
+    // fallback to createElement approach
+    if(!element) element = document.createElement(elementName);
+    
+    // abort if nothing could be created
+    if(!element) return;
+
+    // attributes (or text)
+    if(arguments[1])
+      if(this._isStringOrNumber(arguments[1]) ||
+        (arguments[1] instanceof Array) ||
+        arguments[1].tagName) {
+          this._children(element, arguments[1]);
+        } else {
+          var attrs = this._attributes(arguments[1]);
+          if(attrs.length) {
+            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+              parentElement.innerHTML = "<" +elementName + " " +
+                attrs + "></" + elementName + ">";
+            } catch(e) {}
+            element = parentElement.firstChild || null;
+            // workaround firefox 1.0.X bug
+            if(!element) {
+              element = document.createElement(elementName);
+              for(attr in arguments[1]) 
+                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+            }
+            if(element.tagName.toUpperCase() != elementName)
+              element = parentElement.getElementsByTagName(elementName)[0];
+          }
+        } 
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+
+  ATTR_MAP: {
+    'className': 'class',
+    'htmlFor': 'for'
+  },
+
+  _attributes: function(attributes) {
+    var attrs = [];
+    for(attribute in attributes)
+      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
+          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
+    return attrs.join(" ");
+  },
+  _children: function(element, children) {
+    if(children.tagName) {
+      element.appendChild(children);
+      return;
+    }
+    if(typeof children=='object') { // array can hold nodes and text
+      children.flatten().each( function(e) {
+        if(typeof e=='object')
+          element.appendChild(e)
+        else
+          if(Builder._isStringOrNumber(e))
+            element.appendChild(Builder._text(e));
+      });
+    } else
+      if(Builder._isStringOrNumber(children))
+        element.appendChild(Builder._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  },
+  build: function(html) {
+    var element = this.node('div');
+    $(element).update(html.strip());
+    return element.down();
+  },
+  dump: function(scope) { 
+    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
+  
+    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+  
+    tags.each( function(tag){ 
+      scope[tag] = function() { 
+        return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
+      } 
+    });
+  }
+}
Index: /afridex/plugins/Flutter/js/scriptaculous/sound.js
===================================================================
--- /afridex/plugins/Flutter/js/scriptaculous/sound.js (revision 21)
+++ /afridex/plugins/Flutter/js/scriptaculous/sound.js (revision 21)
@@ -0,0 +1,55 @@
+// script.aculo.us sound.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Based on code created by Jules Gravinese (http://www.webveteran.com/)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+Sound = {
+  tracks: {},
+  _enabled: true,
+  template:
+    new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
+  enable: function(){
+    Sound._enabled = true;
+  },
+  disable: function(){
+    Sound._enabled = false;
+  },
+  play: function(url){
+    if(!Sound._enabled) return;
+    var options = Object.extend({
+      track: 'global', url: url, replace: false
+    }, arguments[1] || {});
+    
+    if(options.replace && this.tracks[options.track]) {
+      $R(0, this.tracks[options.track].id).each(function(id){
+        var sound = $('sound_'+options.track+'_'+id);
+        sound.Stop && sound.Stop();
+        sound.remove();
+      })
+      this.tracks[options.track] = null;
+    }
+      
+    if(!this.tracks[options.track])
+      this.tracks[options.track] = { id: 0 }
+    else
+      this.tracks[options.track].id++;
+      
+    options.id = this.tracks[options.track].id;
+    $$('body')[0].insert( 
+      Prototype.Browser.IE ? new Element('bgsound',{
+        id: 'sound_'+options.track+'_'+options.id,
+        src: options.url, loop: 1, autostart: true
+      }) : Sound.template.evaluate(options));
+  }
+};
+
+if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
+  if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
+    Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>')
+  else
+    Sound.play = function(){}
+}
Index: /afridex/plugins/Flutter/js/scriptaculous/effects.js
===================================================================
--- /afridex/plugins/Flutter/js/scriptaculous/effects.js (revision 21)
+++ /afridex/plugins/Flutter/js/scriptaculous/effects.js (revision 21)
@@ -0,0 +1,1122 @@
+// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/ 
+
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';
+  if (this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if (this.slice(0,1) == '#') {  
+      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if (this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return (color.length==7 ? color : (arguments[0] || this));  
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);  
+  element.setStyle({fontSize: (percent/100) + 'em'});   
+  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+  return element;
+};
+
+Element.getInlineOpacity = function(element){
+  return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
+  Transitions: {
+    linear: Prototype.K,
+    sinoidal: function(pos) {
+      return (-Math.cos(pos*Math.PI)/2) + 0.5;
+    },
+    reverse: function(pos) {
+      return 1-pos;
+    },
+    flicker: function(pos) {
+      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+      return pos > 1 ? 1 : pos;
+    },
+    wobble: function(pos) {
+      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+    },
+    pulse: function(pos, pulses) { 
+      pulses = pulses || 5; 
+      return (
+        ((pos % (1/pulses)) * pulses).round() == 0 ? 
+              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
+          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
+        );
+    },
+    spring: function(pos) { 
+      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
+    },
+    none: function(pos) {
+      return 0;
+    },
+    full: function(pos) {
+      return 1;
+    }
+  },
+  DefaultOptions: {
+    duration:   1.0,   // seconds
+    fps:        100,   // 100= assume 66fps max.
+    sync:       false, // true for combining
+    from:       0.0,
+    to:         1.0,
+    delay:      0.0,
+    queue:      'parallel'
+  },
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+    
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if (child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            new Element('span', {style: tagifyStyle}).update(
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if (((typeof element == 'object') || 
+        Object.isFunction(element)) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || { });
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || { });
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;    
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = Object.isString(effect.options.queue) ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'with-last':
+        timestamp = this.effects.pluck('startOn').max() || timestamp;
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if (!this.interval)
+      this.interval = setInterval(this.loop.bind(this), 15);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if (this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    for(var i=0, len=this.effects.length;i<len;i++) 
+      this.effects[i] && this.effects[i].loop(timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if (!Object.isString(queueName)) return queueName;
+    
+    return this.instances.get(queueName) ||
+      this.instances.set(queueName, new Effect.ScopedQueue());
+  }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+  position: null,
+  start: function(options) {
+    function codeForEvent(options,eventName){
+      return (
+        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+      );
+    }
+    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn+(this.options.duration*1000);
+    this.fromToDelta  = this.options.to-this.options.from;
+    this.totalTime    = this.finishOn-this.startOn;
+    this.totalFrames  = this.options.fps*this.options.duration;
+    
+    eval('this.render = function(pos){ '+
+      'if (this.state=="idle"){this.state="running";'+
+      codeForEvent(this.options,'beforeSetup')+
+      (this.setup ? 'this.setup();':'')+ 
+      codeForEvent(this.options,'afterSetup')+
+      '};if (this.state=="running"){'+
+      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
+      'this.position=pos;'+
+      codeForEvent(this.options,'beforeUpdate')+
+      (this.update ? 'this.update(pos);':'')+
+      codeForEvent(this.options,'afterUpdate')+
+      '}}');
+    
+    this.event('beforeStart');
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if (timePos >= this.startOn) {
+      if (timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if (this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / this.totalTime,
+          frame = (pos * this.totalFrames).round();
+      if (frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  cancel: function() {
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if (this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    var data = $H();
+    for(property in this)
+      if (!Object.isFunction(this[property])) data.set(property, this[property]);
+    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if (effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+  initialize: function(object, from, to) {
+    object = Object.isString(object) ? $(object) : object;
+    var args = $A(arguments), method = args.last(), 
+      options = args.length == 5 ? args[3] : null;
+    this.method = Object.isFunction(method) ? method.bind(object) :
+      Object.isFunction(object[method]) ? object[method].bind(object) : 
+      function(value) { object[method] = value };
+    this.start(Object.extend({ from: from, to: to }, options || { }));
+  },
+  update: function(position) {
+    this.method(position);
+  }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+  initialize: function() {
+    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+  },
+  update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    // make this work on IE on elements without 'layout'
+    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if (this.options.mode == 'absolute') {
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: (this.options.x  * position + this.originalLeft).round() + 'px',
+      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+  initialize: function(element, percent) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = { };
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%','pt'].each( function(fontSizeType) {
+      if (fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if (this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if (/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if (!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if (this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = { };
+    if (this.options.scaleX) d.width = width.round() + 'px';
+    if (this.options.scaleY) d.height = height.round() + 'px';
+    if (this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if (this.elementPositioning == 'absolute') {
+        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if (this.options.scaleY) d.top = -topd + 'px';
+        if (this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = { };
+    if (!this.options.keepBackgroundImage) {
+      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+      this.element.setStyle({backgroundImage: 'none'});
+    }
+    if (!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if (!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = function(element) {
+  var options = arguments[1] || { },
+    scrollOffsets = document.viewport.getScrollOffsets(),
+    elementOffsets = $(element).cumulativeOffset(),
+    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  
+
+  if (options.offset) elementOffsets[1] += options.offset;
+
+  return new Effect.Tween(null,
+    scrollOffsets.top,
+    elementOffsets[1] > max ? max : elementOffsets[1],
+    options,
+    function(p){ scrollTo(scrollOffsets.left, p.round()) }
+  );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+    from: element.getOpacity() || 1.0,
+    to:   0.0,
+    afterFinishInternal: function(effect) { 
+      if (effect.options.to!=0) return;
+      effect.element.hide().setStyle({opacity: oldOpacity}); 
+    }
+  }, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from).show(); 
+  }}, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = { 
+    opacity: element.getInlineOpacity(), 
+    position: element.getStyle('position'),
+    top:  element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height
+  };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) {
+        Position.absolutize(effect.effects[0].element)
+      },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide().setStyle(oldStyle); }
+     }, arguments[1] || { })
+   );
+};
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0,
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping();
+      } 
+    }, arguments[1] || { })
+  );
+};
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false,
+    scaleFrom: 0,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping();
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, Object.extend({
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned().makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+        } 
+      }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    distance: 20,
+    duration: 0.5
+  }, arguments[1] || {});
+  var distance = parseFloat(options.distance);
+  var split = parseFloat(options.duration) / 10.0;
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element,
+      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned().setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+};
+
+Effect.SlideDown = function(element) {
+  element = $(element).cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || { })
+  );
+};
+
+Effect.SlideUp = function(element) {
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().show();
+    },  
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+    }
+   }, arguments[1] || { })
+  );
+};
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, { 
+    restoreAfterFinish: true,
+    beforeSetup: function(effect) {
+      effect.element.makeClipping(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping(); 
+    }
+  });
+};
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide().makeClipping().makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'}).show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+};
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned().makeClipping(); 
+         },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+       }, options)
+  );
+};
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || { };
+  var oldOpacity = element.getInlineOpacity();
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  element.makeClipping();
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping().setStyle(oldStyle);
+      } });
+  }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: { }
+    }, arguments[1] || { });
+    
+    if (!Object.isString(options.style)) this.style = $H(options.style);
+    else {
+      if (options.style.include(':'))
+        this.style = options.style.parseStyle();
+      else {
+        this.element.addClassName(options.style);
+        this.style = $H(this.element.getStyles());
+        this.element.removeClassName(options.style);
+        var css = this.element.getStyles();
+        this.style = this.style.reject(function(style) {
+          return style.value == css[style.key];
+        });
+        options.afterFinishInternal = function(effect) {
+          effect.element.addClassName(effect.options.style);
+          effect.transforms.each(function(transform) {
+            effect.element.style[transform.style] = '';
+          });
+        }
+      }
+    }
+    this.start(options);
+  },
+  
+  setup: function(){
+    function parseColor(color){
+      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+      });
+    }
+    this.transforms = this.style.map(function(pair){
+      var property = pair[0], value = pair[1], unit = null;
+
+      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+        value = value.parseColor();
+        unit  = 'color';
+      } else if (property == 'opacity') {
+        value = parseFloat(value);
+        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+          this.element.setStyle({zoom: 1});
+      } else if (Element.CSS_LENGTH.test(value)) {
+          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+          value = parseFloat(components[1]);
+          unit = (components.length == 3) ? components[2] : null;
+      }
+
+      var originalValue = this.element.getStyle(property);
+      return { 
+        style: property.camelize(), 
+        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
+        targetValue: unit=='color' ? parseColor(value) : value,
+        unit: unit
+      };
+    }.bind(this)).reject(function(transform){
+      return (
+        (transform.originalValue == transform.targetValue) ||
+        (
+          transform.unit != 'color' &&
+          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+        )
+      )
+    });
+  },
+  update: function(position) {
+    var style = { }, transform, i = this.transforms.length;
+    while(i--)
+      style[(transform = this.transforms[i]).style] = 
+        transform.unit=='color' ? '#'+
+          (Math.round(transform.originalValue[0]+
+            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+          (Math.round(transform.originalValue[1]+
+            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+          (Math.round(transform.originalValue[2]+
+            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+        (transform.originalValue +
+          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
+            (transform.unit === null ? '' : transform.unit);
+    this.element.setStyle(style, true);
+  }
+});
+
+Effect.Transform = Class.create({
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || { };
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      track = $H(track);
+      var data = track.values().first();
+      this.tracks.push($H({
+        ids:     track.keys().first(),
+        effect:  Effect.Morph,
+        options: { style: data }
+      }));
+    }.bind(this));
+    return this;
+  },
+  play: function(){
+    return new Effect.Parallel(
+      this.tracks.map(function(track){
+        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+        var elements = [$(ids) || $$(ids)].flatten();
+        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = $w(
+  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
+  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+  'fontSize fontWeight height left letterSpacing lineHeight ' +
+  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+  'right textIndent top width wordSpacing zIndex');
+  
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+  var style, styleRules = $H();
+  if (Prototype.Browser.WebKit)
+    style = new Element('div',{style:this}).style;
+  else {
+    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+    style = String.__parseStyleElement.childNodes[0].style;
+  }
+  
+  Element.CSS_PROPERTIES.each(function(property){
+    if (style[property]) styleRules.set(property, style[property]); 
+  });
+  
+  if (Prototype.Browser.IE && this.include('opacity'))
+    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+  return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+  Element.getStyles = function(element) {
+    var css = document.defaultView.getComputedStyle($(element), null);
+    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+      styles[property] = css[property];
+      return styles;
+    });
+  };
+} else {
+  Element.getStyles = function(element) {
+    element = $(element);
+    var css = element.currentStyle, styles;
+    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+      results[property] = css[property];
+      return results;
+    });
+    if (!styles.opacity) styles.opacity = element.getOpacity();
+    return styles;
+  };
+};
+
+Effect.Methods = {
+  morph: function(element, style) {
+    element = $(element);
+    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+    return element;
+  },
+  visualEffect: function(element, effect, options) {
+    element = $(element)
+    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+    new Effect[klass](element, options);
+    return element;
+  },
+  highlight: function(element, options) {
+    element = $(element);
+    new Effect.Highlight(element, options);
+    return element;
+  }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+  'pulsate shake puff squish switchOff dropOut').each(
+  function(effect) { 
+    Effect.Methods[effect] = function(element, options){
+      element = $(element);
+      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+      return element;
+    }
+  }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
+  function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods);
Index: /afridex/plugins/Flutter/js/scriptaculous/unittest.js
===================================================================
--- /afridex/plugins/Flutter/js/scriptaculous/unittest.js (revision 21)
+++ /afridex/plugins/Flutter/js/scriptaculous/unittest.js (revision 21)
@@ -0,0 +1,568 @@
+// script.aculo.us unittest.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+//           (c) 2005-2007 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+  var options = Object.extend({
+    pointerX: 0,
+    pointerY: 0,
+    buttons:  0,
+    ctrlKey:  false,
+    altKey:   false,
+    shiftKey: false,
+    metaKey:  false
+  }, arguments[2] || {});
+  var oEvent = document.createEvent("MouseEvents");
+  oEvent.initMouseEvent(eventName, true, true, document.defaultView, 
+    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element));
+  
+  if(this.mark) Element.remove(this.mark);
+  this.mark = document.createElement('div');
+  this.mark.appendChild(document.createTextNode(" "));
+  document.body.appendChild(this.mark);
+  this.mark.style.position = 'absolute';
+  this.mark.style.top = options.pointerY + "px";
+  this.mark.style.left = options.pointerX + "px";
+  this.mark.style.width = "5px";
+  this.mark.style.height = "5px;";
+  this.mark.style.borderTop = "1px solid red;"
+  this.mark.style.borderLeft = "1px solid red;"
+  
+  if(this.step)
+    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+  
+  $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+  var options = Object.extend({
+    ctrlKey: false,
+    altKey: false,
+    shiftKey: false,
+    metaKey: false,
+    keyCode: 0,
+    charCode: 0
+  }, arguments[2] || {});
+
+  var oEvent = document.createEvent("KeyEvents");
+  oEvent.initKeyEvent(eventName, true, true, window, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+    options.keyCode, options.charCode );
+  $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+  for(var i=0; i<command.length; i++) {
+    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+  }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = Object.inspect;
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+  initialize: function(log) {
+    this.log = $(log);
+    if (this.log) {
+      this._createLogTable();
+    }
+  },
+  start: function(testName) {
+    if (!this.log) return;
+    this.testName = testName;
+    this.lastLogLine = document.createElement('tr');
+    this.statusCell = document.createElement('td');
+    this.nameCell = document.createElement('td');
+    this.nameCell.className = "nameCell";
+    this.nameCell.appendChild(document.createTextNode(testName));
+    this.messageCell = document.createElement('td');
+    this.lastLogLine.appendChild(this.statusCell);
+    this.lastLogLine.appendChild(this.nameCell);
+    this.lastLogLine.appendChild(this.messageCell);
+    this.loglines.appendChild(this.lastLogLine);
+  },
+  finish: function(status, summary) {
+    if (!this.log) return;
+    this.lastLogLine.className = status;
+    this.statusCell.innerHTML = status;
+    this.messageCell.innerHTML = this._toHTML(summary);
+    this.addLinksToResults();
+  },
+  message: function(message) {
+    if (!this.log) return;
+    this.messageCell.innerHTML = this._toHTML(message);
+  },
+  summary: function(summary) {
+    if (!this.log) return;
+    this.logsummary.innerHTML = this._toHTML(summary);
+  },
+  _createLogTable: function() {
+    this.log.innerHTML =
+    '<div id="logsummary"></div>' +
+    '<table id="logtable">' +
+    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+    '<tbody id="loglines"></tbody>' +
+    '</table>';
+    this.logsummary = $('logsummary')
+    this.loglines = $('loglines');
+  },
+  _toHTML: function(txt) {
+    return txt.escapeHTML().replace(/\n/g,"<br/>");
+  },
+  addLinksToResults: function(){ 
+    $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log
+      td.title = "Run only this test"
+      Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;});
+    });
+    $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log
+      td.title = "Run all tests"
+      Event.observe(td, 'click', function(){ window.location.search = "";});
+    });
+  }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+  initialize: function(testcases) {
+    this.options = Object.extend({
+      testLog: 'testlog'
+    }, arguments[1] || {});
+    this.options.resultsURL = this.parseResultsURLQueryParameter();
+    this.options.tests      = this.parseTestsQueryParameter();
+    if (this.options.testLog) {
+      this.options.testLog = $(this.options.testLog) || null;
+    }
+    if(this.options.tests) {
+      this.tests = [];
+      for(var i = 0; i < this.options.tests.length; i++) {
+        if(/^test/.test(this.options.tests[i])) {
+          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+        }
+      }
+    } else {
+      if (this.options.test) {
+        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+      } else {
+        this.tests = [];
+        for(var testcase in testcases) {
+          if(/^test/.test(testcase)) {
+            this.tests.push(
+               new Test.Unit.Testcase(
+                 this.options.context ? ' -> ' + this.options.titles[testcase] : testcase, 
+                 testcases[testcase], testcases["setup"], testcases["teardown"]
+               ));
+          }
+        }
+      }
+    }
+    this.currentTest = 0;
+    this.logger = new Test.Unit.Logger(this.options.testLog);
+    setTimeout(this.runTests.bind(this), 1000);
+  },
+  parseResultsURLQueryParameter: function() {
+    return window.location.search.parseQuery()["resultsURL"];
+  },
+  parseTestsQueryParameter: function(){
+    if (window.location.search.parseQuery()["tests"]){
+        return window.location.search.parseQuery()["tests"].split(',');
+    };
+  },
+  // Returns:
+  //  "ERROR" if there was an error,
+  //  "FAILURE" if there was a failure, or
+  //  "SUCCESS" if there was neither
+  getResult: function() {
+    var hasFailure = false;
+    for(var i=0;i<this.tests.length;i++) {
+      if (this.tests[i].errors > 0) {
+        return "ERROR";
+      }
+      if (this.tests[i].failures > 0) {
+        hasFailure = true;
+      }
+    }
+    if (hasFailure) {
+      return "FAILURE";
+    } else {
+      return "SUCCESS";
+    }
+  },
+  postResults: function() {
+    if (this.options.resultsURL) {
+      new Ajax.Request(this.options.resultsURL, 
+        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+    }
+  },
+  runTests: function() {
+    var test = this.tests[this.currentTest];
+    if (!test) {
+      // finished!
+      this.postResults();
+      this.logger.summary(this.summary());
+      return;
+    }
+    if(!test.isWaiting) {
+      this.logger.start(test.name);
+    }
+    test.run();
+    if(test.isWaiting) {
+      this.logger.message("Waiting for " + test.timeToWait + "ms");
+      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+    } else {
+      this.logger.finish(test.status(), test.summary());
+      this.currentTest++;
+      // tail recursive, hopefully the browser will skip the stackframe
+      this.runTests();
+    }
+  },
+  summary: function() {
+    var assertions = 0;
+    var failures = 0;
+    var errors = 0;
+    var messages = [];
+    for(var i=0;i<this.tests.length;i++) {
+      assertions +=   this.tests[i].assertions;
+      failures   +=   this.tests[i].failures;
+      errors     +=   this.tests[i].errors;
+    }
+    return (
+      (this.options.context ? this.options.context + ': ': '') + 
+      this.tests.length + " tests, " + 
+      assertions + " assertions, " + 
+      failures   + " failures, " +
+      errors     + " errors");
+  }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+  initialize: function() {
+    this.assertions = 0;
+    this.failures   = 0;
+    this.errors     = 0;
+    this.messages   = [];
+  },
+  summary: function() {
+    return (
+      this.assertions + " assertions, " + 
+      this.failures   + " failures, " +
+      this.errors     + " errors" + "\n" +
+      this.messages.join("\n"));
+  },
+  pass: function() {
+    this.assertions++;
+  },
+  fail: function(message) {
+    this.failures++;
+    this.messages.push("Failure: " + message);
+  },
+  info: function(message) {
+    this.messages.push("Info: " + message);
+  },
+  error: function(error) {
+    this.errors++;
+    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+  },
+  status: function() {
+    if (this.failures > 0) return 'failed';
+    if (this.errors > 0) return 'error';
+    return 'passed';
+  },
+  assert: function(expression) {
+    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+    try { expression ? this.pass() : 
+      this.fail(message); }
+    catch(e) { this.error(e); }
+  },
+  assertEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEqual";
+    try { (expected == actual) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertInspect: function(expected, actual) {
+    var message = arguments[2] || "assertInspect";
+    try { (expected == actual.inspect()) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertEnumEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEnumEqual";
+    try { $A(expected).length == $A(actual).length && 
+      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
+          ', actual ' + Test.Unit.inspect(actual)); }
+    catch(e) { this.error(e); }
+  },
+  assertNotEqual: function(expected, actual) {
+    var message = arguments[2] || "assertNotEqual";
+    try { (expected != actual) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertIdentical: function(expected, actual) { 
+    var message = arguments[2] || "assertIdentical"; 
+    try { (expected === actual) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + Test.Unit.inspect(actual) + '"'); } 
+    catch(e) { this.error(e); } 
+  },
+  assertNotIdentical: function(expected, actual) { 
+    var message = arguments[2] || "assertNotIdentical"; 
+    try { !(expected === actual) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + Test.Unit.inspect(actual) + '"'); } 
+    catch(e) { this.error(e); } 
+  },
+  assertNull: function(obj) {
+    var message = arguments[1] || 'assertNull'
+    try { (obj==null) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertMatch: function(expected, actual) {
+    var message = arguments[2] || 'assertMatch';
+    var regex = new RegExp(expected);
+    try { (regex.exec(actual)) ? this.pass() :
+      this.fail(message + ' : regex: "' +  Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertHidden: function(element) {
+    var message = arguments[1] || 'assertHidden';
+    this.assertEqual("none", element.style.display, message);
+  },
+  assertNotNull: function(object) {
+    var message = arguments[1] || 'assertNotNull';
+    this.assert(object != null, message);
+  },
+  assertType: function(expected, actual) {
+    var message = arguments[2] || 'assertType';
+    try { 
+      (actual.constructor == expected) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + (actual.constructor) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertNotOfType: function(expected, actual) {
+    var message = arguments[2] || 'assertNotOfType';
+    try { 
+      (actual.constructor != expected) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + (actual.constructor) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertInstanceOf';
+    try { 
+      (actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was not an instance of the expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertNotInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertNotInstanceOf';
+    try { 
+      !(actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was an instance of the not expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertRespondsTo: function(method, obj) {
+    var message = arguments[2] || 'assertRespondsTo';
+    try {
+      (obj[method] && typeof obj[method] == 'function') ? this.pass() : 
+      this.fail(message + ": object doesn't respond to [" + method + "]"); }
+    catch(e) { this.error(e); }
+  },
+  assertReturnsTrue: function(method, obj) {
+    var message = arguments[2] || 'assertReturnsTrue';
+    try {
+      var m = obj[method];
+      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
+      m() ? this.pass() : 
+      this.fail(message + ": method returned false"); }
+    catch(e) { this.error(e); }
+  },
+  assertReturnsFalse: function(method, obj) {
+    var message = arguments[2] || 'assertReturnsFalse';
+    try {
+      var m = obj[method];
+      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
+      !m() ? this.pass() : 
+      this.fail(message + ": method returned true"); }
+    catch(e) { this.error(e); }
+  },
+  assertRaise: function(exceptionName, method) {
+    var message = arguments[2] || 'assertRaise';
+    try { 
+      method();
+      this.fail(message + ": exception expected but none was raised"); }
+    catch(e) {
+      ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e); 
+    }
+  },
+  assertElementsMatch: function() {
+    var expressions = $A(arguments), elements = $A(expressions.shift());
+    if (elements.length != expressions.length) {
+      this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
+      return false;
+    }
+    elements.zip(expressions).all(function(pair, index) {
+      var element = $(pair.first()), expression = pair.last();
+      if (element.match(expression)) return true;
+      this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
+    }.bind(this)) && this.pass();
+  },
+  assertElementMatches: function(element, expression) {
+    this.assertElementsMatch([element], expression);
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  },
+  _isVisible: function(element) {
+    element = $(element);
+    if(!element.parentNode) return true;
+    this.assertNotNull(element);
+    if(element.style && Element.getStyle(element, 'display') == 'none')
+      return false;
+    
+    return this._isVisible(element.parentNode);
+  },
+  assertNotVisible: function(element) {
+    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+  },
+  assertVisible: function(element) {
+    this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+  initialize: function(name, test, setup, teardown) {
+    Test.Unit.Assertions.prototype.initialize.bind(this)();
+    this.name           = name;
+    
+    if(typeof test == 'string') {
+      test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
+      test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
+      this.test = function() {
+        eval('with(this){'+test+'}');
+      }
+    } else {
+      this.test = test || function() {};
+    }
+    
+    this.setup          = setup || function() {};
+    this.teardown       = teardown || function() {};
+    this.isWaiting      = false;
+    this.timeToWait     = 1000;
+  },
+  wait: function(time, nextPart) {
+    this.isWaiting = true;
+    this.test = nextPart;
+    this.timeToWait = time;
+  },
+  run: function() {
+    try {
+      try {
+        if (!this.isWaiting) this.setup.bind(this)();
+        this.isWaiting = false;
+        this.test.bind(this)();
+      } finally {
+        if(!this.isWaiting) {
+          this.teardown.bind(this)();
+        }
+      }
+    }
+    catch(e) { this.error(e); }
+  }
+});
+
+// *EXPERIMENTAL* BDD-style testing to please non-technical folk
+// This draws many ideas from RSpec http://rspec.rubyforge.org/
+
+Test.setupBDDExtensionMethods = function(){
+  var METHODMAP = {
+    shouldEqual:     'assertEqual',
+    shouldNotEqual:  'assertNotEqual',
+    shouldEqualEnum: 'assertEnumEqual',
+    shouldBeA:       'assertType',
+    shouldNotBeA:    'assertNotOfType',
+    shouldBeAn:      'assertType',
+    shouldNotBeAn:   'assertNotOfType',
+    shouldBeNull:    'assertNull',
+    shouldNotBeNull: 'assertNotNull',
+    
+    shouldBe:        'assertReturnsTrue',
+    shouldNotBe:     'assertReturnsFalse',
+    shouldRespondTo: 'assertRespondsTo'
+  };
+  var makeAssertion = function(assertion, args, object) { 
+   	this[assertion].apply(this,(args || []).concat([object]));
+  }
+  
+  Test.BDDMethods = {};   
+  $H(METHODMAP).each(function(pair) { 
+    Test.BDDMethods[pair.key] = function() { 
+       var args = $A(arguments); 
+       var scope = args.shift(); 
+       makeAssertion.apply(scope, [pair.value, args, this]); }; 
+  });
+  
+  [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(
+    function(p){ Object.extend(p, Test.BDDMethods) }
+  );
+}
+
+Test.context = function(name, spec, log){
+  Test.setupBDDExtensionMethods();
+  
+  var compiledSpec = {};
+  var titles = {};
+  for(specName in spec) {
+    switch(specName){
+      case "setup":
+      case "teardown":
+        compiledSpec[specName] = spec[specName];
+        break;
+      default:
+        var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
+        var body = spec[specName].toString().split('\n').slice(1);
+        if(/^\{/.test(body[0])) body = body.slice(1);
+        body.pop();
+        body = body.map(function(statement){ 
+          return statement.strip()
+        });
+        compiledSpec[testName] = body.join('\n');
+        titles[testName] = specName;
+    }
+  }
+  new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
+};
Index: /afridex/plugins/Flutter/js/scriptaculous/scriptaculous.js
===================================================================
--- /afridex/plugins/Flutter/js/scriptaculous/scriptaculous.js (revision 21)
+++ /afridex/plugins/Flutter/js/scriptaculous/scriptaculous.js (revision 21)
@@ -0,0 +1,58 @@
+// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Scriptaculous = {
+  Version: '1.8.1',
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
+  },
+  REQUIRED_PROTOTYPE: '1.6.0',
+  load: function() {
+    function convertVersionString(versionString){
+      var r = versionString.split('.');
+      return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
+    }
+ 
+    if((typeof Prototype=='undefined') || 
+       (typeof Element == 'undefined') || 
+       (typeof Element.Methods=='undefined') ||
+       (convertVersionString(Prototype.Version) < 
+        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
+       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
+        Scriptaculous.REQUIRED_PROTOTYPE);
+    
+    $A(document.getElementsByTagName("script")).findAll( function(s) {
+      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+    }).each( function(s) {
+      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+      var includes = s.src.match(/\?.*load=([a-z,]*)/);
+      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
+       function(include) { Scriptaculous.require(path+include+'.js') });
+    });
+  }
+}
+
+Scriptaculous.load();
Index: /afridex/plugins/Flutter/js/scriptaculous/dragdrop.js
===================================================================
--- /afridex/plugins/Flutter/js/scriptaculous/dragdrop.js (revision 21)
+++ /afridex/plugins/Flutter/js/scriptaculous/dragdrop.js (revision 21)
@@ -0,0 +1,974 @@
+// script.aculo.us dragdrop.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if(Object.isUndefined(Effect))
+  throw("dragdrop.js requires including script.aculo.us' effects.js library");
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || { });
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if(Object.isArray(containment)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var drop, affected = [];
+    
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0)
+      drop = Droppables.findDeepestChild(affected);
+
+    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
+    if (drop) {
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      if (drop != this.last_active) Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) {
+        this.last_active.onDrop(element, this.last_active.element, event); 
+        return true; 
+      }
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    if(draggable.options.delay) { 
+      this._timeout = setTimeout(function() { 
+        Draggables._timeout = null; 
+        window.focus(); 
+        Draggables.activeDraggable = draggable; 
+      }.bind(this), draggable.options.delay); 
+    } else {
+      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+      this.activeDraggable = draggable;
+    }
+  },
+  
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(this._timeout) { 
+      clearTimeout(this._timeout); 
+      this._timeout = null; 
+    }
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create({
+  initialize: function(element) {
+    var defaults = {
+      handle: false,
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+          queue: {scope:'_draggable', position:'end'}
+        });
+      },
+      endeffect: function(element) {
+        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
+          queue: {scope:'_draggable', position:'end'},
+          afterFinish: function(){ 
+            Draggable._dragging[element] = false 
+          }
+        }); 
+      },
+      zindex: 1000,
+      revert: false,
+      quiet: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
+      delay: 0
+    };
+    
+    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
+      Object.extend(defaults, {
+        starteffect: function(element) {
+          element._opacity = Element.getOpacity(element);
+          Draggable._dragging[element] = true;
+          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+        }
+      });
+    
+    var options = Object.extend(defaults, arguments[1] || { });
+
+    this.element = $(element);
+    
+    if(options.handle && Object.isString(options.handle))
+      this.handle = this.element.down('.'+options.handle, 0);
+    
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+    
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
+      options.scroll = $(options.scroll);
+      this._isScrollChild = Element.childOf(this.element, options.scroll);
+    }
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
+      Draggable._dragging[this.element]) return;
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if((tag_name = src.tagName.toUpperCase()) && (
+        tag_name=='INPUT' ||
+        tag_name=='SELECT' ||
+        tag_name=='OPTION' ||
+        tag_name=='BUTTON' ||
+        tag_name=='TEXTAREA')) return;
+        
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    if(!this.delta)
+      this.delta = this.currentDelta();
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+      if (!this.element._originallyAbsolute)
+        Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+        
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    
+    if(!this.options.quiet){
+      Position.prepare();
+      Droppables.show(pointer, this.element);
+    }
+    
+    Draggables.notify('onDrag', this, event);
+    
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+        p[1] += this.options.scroll.scrollTop + Position.deltaY;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+    
+    if(this.options.quiet){
+      Position.prepare();
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      Droppables.show(pointer, this.element);
+    }
+
+    if(this.options.ghosting) {
+      if (!this.element._originallyAbsolute)
+        Position.relativize(this.element);
+      delete this.element._originallyAbsolute;
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    var dropped = false; 
+    if(success) { 
+      dropped = Droppables.fire(event, this.element); 
+      if (!dropped) dropped = false; 
+    }
+    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && Object.isFunction(revert)) revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      if (dropped == 0 || revert != 'failure')
+        this.options.reverteffect(this.element,
+          d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+      
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    if(this.options.ghosting) {
+      var r   = Position.realOffset(this.element);
+      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+    }
+    
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(Object.isFunction(this.options.snap)) {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(Object.isArray(this.options.snap)) {
+        p = p.map( function(v, i) {
+          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return (v/this.options.snap).round()*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  startScrolling: function(speed) {
+    if(!(speed[0] || speed[1])) return;
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    if (this._isScrollChild) {
+      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+      if (Draggables._lastScrollPointer[0] < 0)
+        Draggables._lastScrollPointer[0] = 0;
+      if (Draggables._lastScrollPointer[1] < 0)
+        Draggables._lastScrollPointer[1] = 0;
+      this.draw(Draggables._lastScrollPointer);
+    }
+    
+    if(this.options.change) this.options.change(this);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+});
+
+Draggable._dragging = { };
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create({
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+});
+
+var Sortable = {
+  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+  
+  sortables: { },
+  
+  _findRootElement: function(element) {
+    while (element.tagName.toUpperCase() != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    var s = Sortable.options(element);
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      delay:       0,
+      hoverclass:  null,
+      ghosting:    false,
+      quiet:       false, 
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      this.SERIALIZE_RULE,
+      
+      // these take arrays of elements or ids and can be 
+      // used for better initialization performance
+      elements:    false,
+      handles:     false,
+      
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || { });
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      quiet:       options.quiet,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      delay:       options.delay,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+    }
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
+      var handle = options.handles ? $(options.handles[i]) :
+        (options.handle ? $(e).select('.' + options.handle)[0] : e); 
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Sortable._marker.hide();
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = 
+        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+          hide().addClassName('dropmarker').setStyle({position:'absolute'});
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
+      else
+        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
+    
+    Sortable._marker.show();
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: [],
+        position: parent.children.length,
+        container: $(children[i]).down(options.treeTag)
+      }
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child)
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || { });
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: [],
+      container: element,
+      position: 0
+    }
+    
+    return Sortable._tree(element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || { });
+    
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || { });
+    
+    var nodeMap = { };
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || { });
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "[id]=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+}
+
+// Returns true if child is contained within element
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+  if (child.parentNode == element) return true;
+  return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {   
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}
Index: /afridex/plugins/Flutter/js/scriptaculous/slider.js
===================================================================
--- /afridex/plugins/Flutter/js/scriptaculous/slider.js (revision 21)
+++ /afridex/plugins/Flutter/js/scriptaculous/slider.js (revision 21)
@@ -0,0 +1,275 @@
+// script.aculo.us slider.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs 
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if (!Control) var Control = { };
+
+// options:
+//  axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+//  onChange(value)
+//  onSlide(value)
+Control.Slider = Class.create({
+  initialize: function(handle, track, options) {
+    var slider = this;
+    
+    if (Object.isArray(handle)) {
+      this.handles = handle.collect( function(e) { return $(e) });
+    } else {
+      this.handles = [$(handle)];
+    }
+    
+    this.track   = $(track);
+    this.options = options || { };
+
+    this.axis      = this.options.axis || 'horizontal';
+    this.increment = this.options.increment || 1;
+    this.step      = parseInt(this.options.step || '1');
+    this.range     = this.options.range || $R(0,1);
+    
+    this.value     = 0; // assure backwards compat
+    this.values    = this.handles.map( function() { return 0 });
+    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+    this.options.startSpan = $(this.options.startSpan || null);
+    this.options.endSpan   = $(this.options.endSpan || null);
+
+    this.restricted = this.options.restricted || false;
+
+    this.maximum   = this.options.maximum || this.range.end;
+    this.minimum   = this.options.minimum || this.range.start;
+
+    // Will be used to align the handle onto the track, if necessary
+    this.alignX = parseInt(this.options.alignX || '0');
+    this.alignY = parseInt(this.options.alignY || '0');
+    
+    this.trackLength = this.maximumOffset() - this.minimumOffset();
+
+    this.handleLength = this.isVertical() ? 
+      (this.handles[0].offsetHeight != 0 ? 
+        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : 
+      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : 
+        this.handles[0].style.width.replace(/px$/,""));
+
+    this.active   = false;
+    this.dragging = false;
+    this.disabled = false;
+
+    if (this.options.disabled) this.setDisabled();
+
+    // Allowed values array
+    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+    if (this.allowedValues) {
+      this.minimum = this.allowedValues.min();
+      this.maximum = this.allowedValues.max();
+    }
+
+    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+    this.eventMouseMove = this.update.bindAsEventListener(this);
+
+    // Initialize handles in reverse (make sure first handle is active)
+    this.handles.each( function(h,i) {
+      i = slider.handles.length-1-i;
+      slider.setValue(parseFloat(
+        (Object.isArray(slider.options.sliderValue) ? 
+          slider.options.sliderValue[i] : slider.options.sliderValue) || 
+         slider.range.start), i);
+      h.makePositioned().observe("mousedown", slider.eventMouseDown);
+    });
+    
+    this.track.observe("mousedown", this.eventMouseDown);
+    document.observe("mouseup", this.eventMouseUp);
+    document.observe("mousemove", this.eventMouseMove);
+    
+    this.initialized = true;
+  },
+  dispose: function() {
+    var slider = this;    
+    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+    Event.stopObserving(document, "mouseup", this.eventMouseUp);
+    Event.stopObserving(document, "mousemove", this.eventMouseMove);
+    this.handles.each( function(h) {
+      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+    });
+  },
+  setDisabled: function(){
+    this.disabled = true;
+  },
+  setEnabled: function(){
+    this.disabled = false;
+  },  
+  getNearestValue: function(value){
+    if (this.allowedValues){
+      if (value >= this.allowedValues.max()) return(this.allowedValues.max());
+      if (value <= this.allowedValues.min()) return(this.allowedValues.min());
+      
+      var offset = Math.abs(this.allowedValues[0] - value);
+      var newValue = this.allowedValues[0];
+      this.allowedValues.each( function(v) {
+        var currentOffset = Math.abs(v - value);
+        if (currentOffset <= offset){
+          newValue = v;
+          offset = currentOffset;
+        } 
+      });
+      return newValue;
+    }
+    if (value > this.range.end) return this.range.end;
+    if (value < this.range.start) return this.range.start;
+    return value;
+  },
+  setValue: function(sliderValue, handleIdx){
+    if (!this.active) {
+      this.activeHandleIdx = handleIdx || 0;
+      this.activeHandle    = this.handles[this.activeHandleIdx];
+      this.updateStyles();
+    }
+    handleIdx = handleIdx || this.activeHandleIdx || 0;
+    if (this.initialized && this.restricted) {
+      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+        sliderValue = this.values[handleIdx-1];
+      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+        sliderValue = this.values[handleIdx+1];
+    }
+    sliderValue = this.getNearestValue(sliderValue);
+    this.values[handleIdx] = sliderValue;
+    this.value = this.values[0]; // assure backwards compat
+    
+    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
+      this.translateToPx(sliderValue);
+    
+    this.drawSpans();
+    if (!this.dragging || !this.event) this.updateFinished();
+  },
+  setValueBy: function(delta, handleIdx) {
+    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
+      handleIdx || this.activeHandleIdx || 0);
+  },
+  translateToPx: function(value) {
+    return Math.round(
+      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
+      (value - this.range.start)) + "px";
+  },
+  translateToValue: function(offset) {
+    return ((offset/(this.trackLength-this.handleLength) * 
+      (this.range.end-this.range.start)) + this.range.start);
+  },
+  getRange: function(range) {
+    var v = this.values.sortBy(Prototype.K); 
+    range = range || 0;
+    return $R(v[range],v[range+1]);
+  },
+  minimumOffset: function(){
+    return(this.isVertical() ? this.alignY : this.alignX);
+  },
+  maximumOffset: function(){
+    return(this.isVertical() ? 
+      (this.track.offsetHeight != 0 ? this.track.offsetHeight :
+        this.track.style.height.replace(/px$/,"")) - this.alignY : 
+      (this.track.offsetWidth != 0 ? this.track.offsetWidth : 
+        this.track.style.width.replace(/px$/,"")) - this.alignX);
+  },  
+  isVertical:  function(){
+    return (this.axis == 'vertical');
+  },
+  drawSpans: function() {
+    var slider = this;
+    if (this.spans)
+      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+    if (this.options.startSpan)
+      this.setSpan(this.options.startSpan,
+        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+    if (this.options.endSpan)
+      this.setSpan(this.options.endSpan, 
+        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+  },
+  setSpan: function(span, range) {
+    if (this.isVertical()) {
+      span.style.top = this.translateToPx(range.start);
+      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+    } else {
+      span.style.left = this.translateToPx(range.start);
+      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+    }
+  },
+  updateStyles: function() {
+    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+    Element.addClassName(this.activeHandle, 'selected');
+  },
+  startDrag: function(event) {
+    if (Event.isLeftClick(event)) {
+      if (!this.disabled){
+        this.active = true;
+        
+        var handle = Event.element(event);
+        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
+        var track = handle;
+        if (track==this.track) {
+          var offsets  = Position.cumulativeOffset(this.track); 
+          this.event = event;
+          this.setValue(this.translateToValue( 
+           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+          ));
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        } else {
+          // find the handle (prevents issues with Safari)
+          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
+            handle = handle.parentNode;
+            
+          if (this.handles.indexOf(handle)!=-1) {
+            this.activeHandle    = handle;
+            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+            this.updateStyles();
+            
+            var offsets  = Position.cumulativeOffset(this.activeHandle);
+            this.offsetX = (pointer[0] - offsets[0]);
+            this.offsetY = (pointer[1] - offsets[1]);
+          }
+        }
+      }
+      Event.stop(event);
+    }
+  },
+  update: function(event) {
+   if (this.active) {
+      if (!this.dragging) this.dragging = true;
+      this.draw(event);
+      if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+      Event.stop(event);
+   }
+  },
+  draw: function(event) {
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    var offsets = Position.cumulativeOffset(this.track);
+    pointer[0] -= this.offsetX + offsets[0];
+    pointer[1] -= this.offsetY + offsets[1];
+    this.event = event;
+    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+    if (this.initialized && this.options.onSlide)
+      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+  },
+  endDrag: function(event) {
+    if (this.active && this.dragging) {
+      this.finishDrag(event, true);
+      Event.stop(event);
+    }
+    this.active = false;
+    this.dragging = false;
+  },  
+  finishDrag: function(event, success) {
+    this.active = false;
+    this.dragging = false;
+    this.updateFinished();
+  },
+  updateFinished: function() {
+    if (this.initialized && this.options.onChange) 
+      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+    this.event = null;
+  }
+});
Index: /afridex/plugins/Flutter/js/scriptaculous/controls.js
===================================================================
--- /afridex/plugins/Flutter/js/scriptaculous/controls.js (revision 21)
+++ /afridex/plugins/Flutter/js/scriptaculous/controls.js (revision 21)
@@ -0,0 +1,965 @@
+// script.aculo.us controls.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+  throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { }
+Autocompleter.Base = Class.create({
+  baseInitialize: function(element, update, options) {
+    element          = $(element)
+    this.element     = element; 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+    this.oldElementValue = this.element.value;
+
+    if(this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || { };
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+      function(element, update){ 
+        if(!update.style.position || update.style.position=='absolute') {
+          update.style.position = 'absolute';
+          Position.clone(element, update, {
+            setHeight: false, 
+            offsetTop: element.offsetHeight
+          });
+        }
+        Effect.Appear(update,{duration:0.15});
+      };
+    this.options.onHide = this.options.onHide || 
+      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if(typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+    // Force carriage returns as token delimiters anyway
+    if (!this.options.tokens.include('\n'))
+      this.options.tokens.push('\n');
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (Prototype.Browser.IE) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCount-1;
+    this.getEntry(this.index).scrollIntoView(true);
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+    this.getEntry(this.index).scrollIntoView(false);
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = $(selectedElement).select('.' + this.options.select) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var bounds = this.getTokenBounds();
+    if (bounds[0] != -1) {
+      var newValue = this.element.value.substr(0, bounds[0]);
+      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+    } else {
+      this.element.value = value;
+    }
+    this.oldElementValue = this.element.value;
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.down());
+
+      if(this.update.firstChild && this.update.down().childNodes) {
+        this.entryCount = 
+          this.update.down().childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+      this.index = 0;
+      
+      if(this.entryCount==1 && this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    this.tokenBounds = null;
+    if(this.getToken().length>=this.options.minChars) {
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+    this.oldElementValue = this.element.value;
+  },
+
+  getToken: function() {
+    var bounds = this.getTokenBounds();
+    return this.element.value.substring(bounds[0], bounds[1]).strip();
+  },
+
+  getTokenBounds: function() {
+    if (null != this.tokenBounds) return this.tokenBounds;
+    var value = this.element.value;
+    if (value.strip().empty()) return [-1, 0];
+    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+    var offset = (diff == this.oldElementValue.length ? 1 : 0);
+    var prevTokenPos = -1, nextTokenPos = value.length;
+    var tp;
+    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+      if (tp > prevTokenPos) prevTokenPos = tp;
+      tp = value.indexOf(this.options.tokens[index], diff + offset);
+      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+    }
+    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+  }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+  var boundary = Math.min(newS.length, oldS.length);
+  for (var index = 0; index < boundary; ++index)
+    if (newS[index] != oldS[index])
+      return index;
+  return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    this.startIndicator();
+    
+    var entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+    
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || { });
+  }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create({
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = element = $(element);
+    this.prepareOptions();
+    this._controls = { };
+    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+    Object.extend(this.options, options || { });
+    if (!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + '-inplaceeditor';
+      if ($(this.options.formId))
+        this.options.formId = '';
+    }
+    if (this.options.externalControl)
+      this.options.externalControl = $(this.options.externalControl);
+    if (!this.options.externalControl)
+      this.options.externalControlOnly = false;
+    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+    this.element.title = this.options.clickToEditText;
+    this._boundCancelHandler = this.handleFormCancellation.bind(this);
+    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+    this._boundWrapperHandler = this.wrapUp.bind(this);
+    this.registerListeners();
+  },
+  checkForEscapeOrReturn: function(e) {
+    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+    if (Event.KEY_ESC == e.keyCode)
+      this.handleFormCancellation(e);
+    else if (Event.KEY_RETURN == e.keyCode)
+      this.handleFormSubmission(e);
+  },
+  createControl: function(mode, handler, extraClasses) {
+    var control = this.options[mode + 'Control'];
+    var text = this.options[mode + 'Text'];
+    if ('button' == control) {
+      var btn = document.createElement('input');
+      btn.type = 'submit';
+      btn.value = text;
+      btn.className = 'editor_' + mode + '_button';
+      if ('cancel' == mode)
+        btn.onclick = this._boundCancelHandler;
+      this._form.appendChild(btn);
+      this._controls[mode] = btn;
+    } else if ('link' == control) {
+      var link = document.createElement('a');
+      link.href = '#';
+      link.appendChild(document.createTextNode(text));
+      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+      link.className = 'editor_' + mode + '_link';
+      if (extraClasses)
+        link.className += ' ' + extraClasses;
+      this._form.appendChild(link);
+      this._controls[mode] = link;
+    }
+  },
+  createEditField: function() {
+    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+    var fld;
+    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+      fld = document.createElement('input');
+      fld.type = 'text';
+      var size = this.options.size || this.options.cols || 0;
+      if (0 < size) fld.size = size;
+    } else {
+      fld = document.createElement('textarea');
+      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+      fld.cols = this.options.cols || 40;
+    }
+    fld.name = this.options.paramName;
+    fld.value = text; // No HTML breaks conversion anymore
+    fld.className = 'editor_field';
+    if (this.options.submitOnBlur)
+      fld.onblur = this._boundSubmitHandler;
+    this._controls.editor = fld;
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+  createForm: function() {
+    var ipe = this;
+    function addText(mode, condition) {
+      var text = ipe.options['text' + mode + 'Controls'];
+      if (!text || condition === false) return;
+      ipe._form.appendChild(document.createTextNode(text));
+    };
+    this._form = $(document.createElement('form'));
+    this._form.id = this.options.formId;
+    this._form.addClassName(this.options.formClassName);
+    this._form.onsubmit = this._boundSubmitHandler;
+    this.createEditField();
+    if ('textarea' == this._controls.editor.tagName.toLowerCase())
+      this._form.appendChild(document.createElement('br'));
+    if (this.options.onFormCustomization)
+      this.options.onFormCustomization(this, this._form);
+    addText('Before', this.options.okControl || this.options.cancelControl);
+    this.createControl('ok', this._boundSubmitHandler);
+    addText('Between', this.options.okControl && this.options.cancelControl);
+    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+    addText('After', this.options.okControl || this.options.cancelControl);
+  },
+  destroy: function() {
+    if (this._oldInnerHTML)
+      this.element.innerHTML = this._oldInnerHTML;
+    this.leaveEditMode();
+    this.unregisterListeners();
+  },
+  enterEditMode: function(e) {
+    if (this._saving || this._editing) return;
+    this._editing = true;
+    this.triggerCallback('onEnterEditMode');
+    if (this.options.externalControl)
+      this.options.externalControl.hide();
+    this.element.hide();
+    this.createForm();
+    this.element.parentNode.insertBefore(this._form, this.element);
+    if (!this.options.loadTextURL)
+      this.postProcessEditField();
+    if (e) Event.stop(e);
+  },
+  enterHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.addClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onEnterHover');
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  handleAJAXFailure: function(transport) {
+    this.triggerCallback('onFailure', transport);
+    if (this._oldInnerHTML) {
+      this.element.innerHTML = this._oldInnerHTML;
+      this._oldInnerHTML = null;
+    }
+  },
+  handleFormCancellation: function(e) {
+    this.wrapUp();
+    if (e) Event.stop(e);
+  },
+  handleFormSubmission: function(e) {
+    var form = this._form;
+    var value = $F(this._controls.editor);
+    this.prepareSubmission();
+    var params = this.options.callback(form, value) || '';
+    if (Object.isString(params))
+      params = params.toQueryParams();
+    params.editorId = this.element.id;
+    if (this.options.htmlResponse) {
+      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Updater({ success: this.element }, this.url, options);
+    } else {
+      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Request(this.url, options);
+    }
+    if (e) Event.stop(e);
+  },
+  leaveEditMode: function() {
+    this.element.removeClassName(this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+    if (this.options.externalControl)
+      this.options.externalControl.show();
+    this._saving = false;
+    this._editing = false;
+    this._oldInnerHTML = null;
+    this.triggerCallback('onLeaveEditMode');
+  },
+  leaveHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.removeClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onLeaveHover');
+  },
+  loadExternalText: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this._controls.editor.disabled = true;
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._form.removeClassName(this.options.loadingClassName);
+        var text = transport.responseText;
+        if (this.options.stripLoadedTextTags)
+          text = text.stripTags();
+        this._controls.editor.value = text;
+        this._controls.editor.disabled = false;
+        this.postProcessEditField();
+      }.bind(this),
+      onFailure: this._boundFailureHandler
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+  postProcessEditField: function() {
+    var fpc = this.options.fieldPostCreation;
+    if (fpc)
+      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+  },
+  prepareOptions: function() {
+    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+      Object.extend(this.options, defs);
+    }.bind(this));
+  },
+  prepareSubmission: function() {
+    this._saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  registerListeners: function() {
+    this._listeners = { };
+    var listener;
+    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+      listener = this[pair.value].bind(this);
+      this._listeners[pair.key] = listener;
+      if (!this.options.externalControlOnly)
+        this.element.observe(pair.key, listener);
+      if (this.options.externalControl)
+        this.options.externalControl.observe(pair.key, listener);
+    }.bind(this));
+  },
+  removeForm: function() {
+    if (!this._form) return;
+    this._form.remove();
+    this._form = null;
+    this._controls = { };
+  },
+  showSaving: function() {
+    this._oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    this.element.addClassName(this.options.savingClassName);
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+  },
+  triggerCallback: function(cbName, arg) {
+    if ('function' == typeof this.options[cbName]) {
+      this.options[cbName](this, arg);
+    }
+  },
+  unregisterListeners: function() {
+    $H(this._listeners).each(function(pair) {
+      if (!this.options.externalControlOnly)
+        this.element.stopObserving(pair.key, pair.value);
+      if (this.options.externalControl)
+        this.options.externalControl.stopObserving(pair.key, pair.value);
+    }.bind(this));
+  },
+  wrapUp: function(transport) {
+    this.leaveEditMode();
+    // Can't use triggerCallback due to backward compatibility: requires
+    // binding + direct element
+    this._boundComplete(transport, this.element);
+  }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+  dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+  initialize: function($super, element, url, options) {
+    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+    $super(element, url, options);
+  },
+
+  createEditField: function() {
+    var list = document.createElement('select');
+    list.name = this.options.paramName;
+    list.size = 1;
+    this._controls.editor = list;
+    this._collection = this.options.collection || [];
+    if (this.options.loadCollectionURL)
+      this.loadCollection();
+    else
+      this.checkForExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+
+  loadCollection: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this.showLoadingText(this.options.loadingCollectionText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        var js = transport.responseText.strip();
+        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+          throw 'Server returned an invalid collection representation.';
+        this._collection = eval(js);
+        this.checkForExternalText();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadCollectionURL, options);
+  },
+
+  showLoadingText: function(text) {
+    this._controls.editor.disabled = true;
+    var tempOption = this._controls.editor.firstChild;
+    if (!tempOption) {
+      tempOption = document.createElement('option');
+      tempOption.value = '';
+      this._controls.editor.appendChild(tempOption);
+      tempOption.selected = true;
+    }
+    tempOption.update((text || '').stripScripts().stripTags());
+  },
+
+  checkForExternalText: function() {
+    this._text = this.getText();
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    else
+      this.buildOptionList();
+  },
+
+  loadExternalText: function() {
+    this.showLoadingText(this.options.loadingText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._text = transport.responseText.strip();
+        this.buildOptionList();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+
+  buildOptionList: function() {
+    this._form.removeClassName(this.options.loadingClassName);
+    this._collection = this._collection.map(function(entry) {
+      return 2 === entry.length ? entry : [entry, entry].flatten();
+    });
+    var marker = ('value' in this.options) ? this.options.value : this._text;
+    var textFound = this._collection.any(function(entry) {
+      return entry[0] == marker;
+    }.bind(this));
+    this._controls.editor.update('');
+    var option;
+    this._collection.each(function(entry, index) {
+      option = document.createElement('option');
+      option.value = entry[0];
+      option.selected = textFound ? entry[0] == marker : 0 == index;
+      option.appendChild(document.createTextNode(entry[1]));
+      this._controls.editor.appendChild(option);
+    }.bind(this));
+    this._controls.editor.disabled = false;
+    Field.scrollFreeActivate(this._controls.editor);
+  }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only  exists for a while,  in order to  let ****
+//**** users adapt to  the new API.  Read up on the new ****
+//**** API and convert your code to it ASAP!            ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+  if (!options) return;
+  function fallback(name, expr) {
+    if (name in options || expr === undefined) return;
+    options[name] = expr;
+  };
+  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+    options.cancelLink == options.cancelButton == false ? false : undefined)));
+  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+    options.okLink == options.okButton == false ? false : undefined)));
+  fallback('highlightColor', options.highlightcolor);
+  fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+  DefaultOptions: {
+    ajaxOptions: { },
+    autoRows: 3,                                // Use when multi-line w/ rows == 1
+    cancelControl: 'link',                      // 'link'|'button'|false
+    cancelText: 'cancel',
+    clickToEditText: 'Click to edit',
+    externalControl: null,                      // id|elt
+    externalControlOnly: false,
+    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
+    formClassName: 'inplaceeditor-form',
+    formId: null,                               // id|elt
+    highlightColor: '#ffff99',
+    highlightEndColor: '#ffffff',
+    hoverClassName: '',
+    htmlResponse: true,
+    loadingClassName: 'inplaceeditor-loading',
+    loadingText: 'Loading...',
+    okControl: 'button',                        // 'link'|'button'|false
+    okText: 'ok',
+    paramName: 'value',
+    rows: 1,                                    // If 1 and multi-line, uses autoRows
+    savingClassName: 'inplaceeditor-saving',
+    savingText: 'Saving...',
+    size: 0,
+    stripLoadedTextTags: false,
+    submitOnBlur: false,
+    textAfterControls: '',
+    textBeforeControls: '',
+    textBetweenControls: ''
+  },
+  DefaultCallbacks: {
+    callback: function(form) {
+      return Form.serialize(form);
+    },
+    onComplete: function(transport, element) {
+      // For backward compatibility, this one is bound to the IPE, and passes
+      // the element directly.  It was too often customized, so we don't break it.
+      new Effect.Highlight(element, {
+        startcolor: this.options.highlightColor, keepBackgroundImage: true });
+    },
+    onEnterEditMode: null,
+    onEnterHover: function(ipe) {
+      ipe.element.style.backgroundColor = ipe.options.highlightColor;
+      if (ipe._effect)
+        ipe._effect.cancel();
+    },
+    onFailure: function(transport, ipe) {
+      alert('Error communication with the server: ' + transport.responseText.stripTags());
+    },
+    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+    onLeaveEditMode: null,
+    onLeaveHover: function(ipe) {
+      ipe._effect = new Effect.Highlight(ipe.element, {
+        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+        restorecolor: ipe._originalBackground, keepBackgroundImage: true
+      });
+    }
+  },
+  Listeners: {
+    click: 'enterEditMode',
+    keydown: 'checkForEscapeOrReturn',
+    mouseover: 'enterHover',
+    mouseout: 'leaveHover'
+  }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+  loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+});
Index: /afridex/plugins/Flutter/js/swfupload/swfupload.js
===================================================================
--- /afridex/plugins/Flutter/js/swfupload/swfupload.js (revision 21)
+++ /afridex/plugins/Flutter/js/swfupload/swfupload.js (revision 21)
@@ -0,0 +1,755 @@
+/**
+ * SWFUpload v2.1.0 by Jacob Roberts, Feb 2008, http://www.swfupload.org, http://swfupload.googlecode.com, http://www.swfupload.org
+ * -------- -------- -------- -------- -------- -------- -------- --------
+ * SWFUpload is (c) 2006 Lars Huring, Olov Nilzï¿œn and Mammon Media and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * See Changelog.txt for version history
+ *
+ */
+
+
+/* *********** */
+/* Constructor */
+/* *********** */
+
+var SWFUpload = function (settings) {
+	this.initSWFUpload(settings);
+};
+
+SWFUpload.prototype.initSWFUpload = function (settings) {
+	try {
+		this.customSettings = {};	// A container where developers can place their own settings associated with this instance.
+		this.settings = settings;
+		this.eventQueue = [];
+		this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
+		this.movieElement = null;
+
+		// Setup global control tracking
+		SWFUpload.instances[this.movieName] = this;
+
+		// Load the settings.  Load the Flash movie.
+		this.initSettings();
+		this.loadFlash();
+		this.displayDebugInfo();
+	} catch (ex) {
+		delete SWFUpload.instances[this.movieName];
+		throw ex;
+	}
+};
+
+/* *************** */
+/* Static Members  */
+/* *************** */
+SWFUpload.instances = {};
+SWFUpload.movieCount = 0;
+SWFUpload.version = "2.1.0";
+SWFUpload.QUEUE_ERROR = {
+	QUEUE_LIMIT_EXCEEDED	  		: -100,
+	FILE_EXCEEDS_SIZE_LIMIT  		: -110,
+	ZERO_BYTE_FILE			  		: -120,
+	INVALID_FILETYPE		  		: -130
+};
+SWFUpload.UPLOAD_ERROR = {
+	HTTP_ERROR				  		: -200,
+	MISSING_UPLOAD_URL	      		: -210,
+	IO_ERROR				  		: -220,
+	SECURITY_ERROR			  		: -230,
+	UPLOAD_LIMIT_EXCEEDED	  		: -240,
+	UPLOAD_FAILED			  		: -250,
+	SPECIFIED_FILE_ID_NOT_FOUND		: -260,
+	FILE_VALIDATION_FAILED	  		: -270,
+	FILE_CANCELLED			  		: -280,
+	UPLOAD_STOPPED					: -290
+};
+SWFUpload.FILE_STATUS = {
+	QUEUED		 : -1,
+	IN_PROGRESS	 : -2,
+	ERROR		 : -3,
+	COMPLETE	 : -4,
+	CANCELLED	 : -5
+};
+
+
+/* ******************** */
+/* Instance Members  */
+/* ******************** */
+
+// Private: initSettings ensures that all the
+// settings are set, getting a default value if one was not assigned.
+SWFUpload.prototype.initSettings = function () {
+	this.ensureDefault = function (settingName, defaultValue) {
+		this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
+	};
+	
+	// Upload backend settings
+	this.ensureDefault("upload_url", "");
+	this.ensureDefault("file_post_name", "Filedata");
+	this.ensureDefault("post_params", {});
+	this.ensureDefault("use_query_string", false);
+	this.ensureDefault("requeue_on_error", false);
+	
+	// File Settings
+	this.ensureDefault("file_types", "*.*");
+	this.ensureDefault("file_types_description", "All Files");
+	this.ensureDefault("file_size_limit", 0);	// Default zero means "unlimited"
+	this.ensureDefault("file_upload_limit", 0);
+	this.ensureDefault("file_queue_limit", 0);
+
+	// Flash Settings
+	this.ensureDefault("flash_url", "swfupload_f9.swf");
+	this.ensureDefault("flash_color", "#FFFFFF");
+
+	// Debug Settings
+	this.ensureDefault("debug", false);
+	this.settings.debug_enabled = this.settings.debug;	// Here to maintain v2 API
+	
+	// Event Handlers
+	this.settings.return_upload_start_handler = this.returnUploadStart;
+	this.ensureDefault("swfupload_loaded_handler", null);
+	this.ensureDefault("file_dialog_start_handler", null);
+	this.ensureDefault("file_queued_handler", null);
+	this.ensureDefault("file_queue_error_handler", null);
+	this.ensureDefault("file_dialog_complete_handler", null);
+	
+	this.ensureDefault("upload_start_handler", null);
+	this.ensureDefault("upload_progress_handler", null);
+	this.ensureDefault("upload_error_handler", null);
+	this.ensureDefault("upload_success_handler", null);
+	this.ensureDefault("upload_complete_handler", null);
+	
+	this.ensureDefault("debug_handler", this.debugMessage);
+
+	this.ensureDefault("custom_settings", {});
+
+	// Other settings
+	this.customSettings = this.settings.custom_settings;
+	
+	delete this.ensureDefault;
+};
+
+// Private: loadFlash generates the HTML tag for the Flash
+// It then adds the flash to the body
+SWFUpload.prototype.loadFlash = function () {
+	var targetElement, container;
+
+	// Make sure an element with the ID we are going to use doesn't already exist
+	if (document.getElementById(this.movieName) !== null) {
+		throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
+	}
+
+	// Get the body tag where we will be adding the flash movie
+	targetElement = document.getElementsByTagName("body")[0];
+
+	if (targetElement == undefined) {
+		throw "Could not find the 'body' element.";
+	}
+
+	// Append the container and load the flash
+	container = document.createElement("div");
+	container.style.width = "1px";
+	container.style.height = "1px";
+
+	targetElement.appendChild(container);
+	container.innerHTML = this.getFlashHTML();	// Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
+};
+
+// Private: getFlashHTML generates the object tag needed to embed the flash in to the document
+SWFUpload.prototype.getFlashHTML = function () {
+	// Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
+	return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.settings.flash_url, '" width="1" height="1" style="-moz-user-focus: ignore;">',
+				'<param name="movie" value="', this.settings.flash_url, '" />',
+				'<param name="bgcolor" value="', this.settings.flash_color, '" />',
+				'<param name="quality" value="high" />',
+				'<param name="menu" value="false" />',
+				'<param name="allowScriptAccess" value="always" />',
+				'<param name="flashvars" value="' + this.getFlashVars() + '" />',
+				'</object>'].join("");
+};
+
+// Private: getFlashVars builds the parameter string that will be passed
+// to flash in the flashvars param.
+SWFUpload.prototype.getFlashVars = function () {
+	// Build a string from the post param object
+	var paramString = this.buildParamString();
+
+	// Build the parameter string
+	return ["movieName=", encodeURIComponent(this.movieName),
+			"&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
+			"&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
+			"&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
+			"&amp;params=", encodeURIComponent(paramString),
+			"&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
+			"&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
+			"&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
+			"&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
+			"&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
+			"&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
+			"&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled)].join("");
+};
+
+// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
+// The element is cached after the first lookup
+SWFUpload.prototype.getMovieElement = function () {
+	if (this.movieElement == undefined) {
+		this.movieElement = document.getElementById(this.movieName);
+	}
+
+	if (this.movieElement === null) {
+		throw "Could not find Flash element";
+	}
+	
+	return this.movieElement;
+};
+
+// Private: buildParamString takes the name/value pairs in the post_params setting object
+// and joins them up in to a string formatted "name=value&amp;name=value"
+SWFUpload.prototype.buildParamString = function () {
+	var postParams = this.settings.post_params;
+	var paramStringPairs = [];
+
+	if (typeof(postParams) === "object") {
+		for (var name in postParams) {
+			if (postParams.hasOwnProperty(name)) {
+				paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
+			}
+		}
+	}
+
+	return paramStringPairs.join("&amp;");
+};
+
+// Public: Used to remove a SWFUpload instance from the page. This method strives to remove
+// all references to the SWF, and other objects so memory is properly freed.
+// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
+SWFUpload.prototype.destroy = function () {
+	try {
+		// Make sure Flash is done before we try to remove it
+		this.stopUpload();
+		
+		// Remove the SWFUpload DOM nodes
+		var movieElement = null;
+		try {
+			movieElement = this.getMovieElement();
+		} catch (ex) {
+		}
+		
+		if (movieElement != undefined && movieElement.parentNode != undefined && typeof(movieElement.parentNode.removeChild) === "function") {
+			var container = movieElement.parentNode;
+			if (container != undefined) {
+				container.removeChild(movieElement);
+				if (container.parentNode != undefined && typeof(container.parentNode.removeChild) === "function") {
+					container.parentNode.removeChild(container);
+				}
+			}
+		}
+		
+		// Destroy references
+		SWFUpload.instances[this.movieName] = null;
+		delete SWFUpload.instances[this.movieName];
+
+		delete this.movieElement;
+		delete this.settings;
+		delete this.customSettings;
+		delete this.eventQueue;
+		delete this.movieName;
+		
+		return true;
+	} catch (ex1) {
+		return false;
+	}
+};
+
+// Public: displayDebugInfo prints out settings and configuration
+// information about this SWFUpload instance.
+// This function (and any references to it) can be deleted when placing
+// SWFUpload in production.
+SWFUpload.prototype.displayDebugInfo = function () {
+	this.debug(
+		[
+			"---SWFUpload Instance Info---\n",
+			"Version: ", SWFUpload.version, "\n",
+			"Movie Name: ", this.movieName, "\n",
+			"Settings:\n",
+			"\t", "upload_url:             ", this.settings.upload_url, "\n",
+			"\t", "use_query_string:       ", this.settings.use_query_string.toString(), "\n",
+			"\t", "file_post_name:         ", this.settings.file_post_name, "\n",
+			"\t", "post_params:            ", this.settings.post_params.toString(), "\n",
+			"\t", "file_types:             ", this.settings.file_types, "\n",
+			"\t", "file_types_description: ", this.settings.file_types_description, "\n",
+			"\t", "file_size_limit:        ", this.settings.file_size_limit, "\n",
+			"\t", "file_upload_limit:      ", this.settings.file_upload_limit, "\n",
+			"\t", "file_queue_limit:       ", this.settings.file_queue_limit, "\n",
+			"\t", "flash_url:              ", this.settings.flash_url, "\n",
+			"\t", "flash_color:            ", this.settings.flash_color, "\n",
+			"\t", "debug:                  ", this.settings.debug.toString(), "\n",
+			"\t", "custom_settings:        ", this.settings.custom_settings.toString(), "\n",
+			"Event Handlers:\n",
+			"\t", "swfupload_loaded_handler assigned:  ", (typeof(this.settings.swfupload_loaded_handler) === "function").toString(), "\n",
+			"\t", "file_dialog_start_handler assigned: ", (typeof(this.settings.file_dialog_start_handler) === "function").toString(), "\n",
+			"\t", "file_queued_handler assigned:       ", (typeof(this.settings.file_queued_handler) === "function").toString(), "\n",
+			"\t", "file_queue_error_handler assigned:  ", (typeof(this.settings.file_queue_error_handler) === "function").toString(), "\n",
+			"\t", "upload_start_handler assigned:      ", (typeof(this.settings.upload_start_handler) === "function").toString(), "\n",
+			"\t", "upload_progress_handler assigned:   ", (typeof(this.settings.upload_progress_handler) === "function").toString(), "\n",
+			"\t", "upload_error_handler assigned:      ", (typeof(this.settings.upload_error_handler) === "function").toString(), "\n",
+			"\t", "upload_success_handler assigned:    ", (typeof(this.settings.upload_success_handler) === "function").toString(), "\n",
+			"\t", "upload_complete_handler assigned:   ", (typeof(this.settings.upload_complete_handler) === "function").toString(), "\n",
+			"\t", "debug_handler assigned:             ", (typeof(this.settings.debug_handler) === "function").toString(), "\n"
+		].join("")
+	);
+};
+
+/* Note: addSetting and getSetting are no longer used by SWFUpload but are included
+	the maintain v2 API compatibility
+*/
+// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
+SWFUpload.prototype.addSetting = function (name, value, default_value) {
+    if (value == undefined) {
+        return (this.settings[name] = default_value);
+    } else {
+        return (this.settings[name] = value);
+	}
+};
+
+// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
+SWFUpload.prototype.getSetting = function (name) {
+    if (this.settings[name] != undefined) {
+        return this.settings[name];
+	}
+
+    return "";
+};
+
+
+
+// Private: callFlash handles function calls made to the Flash element.
+// Calls are made with a setTimeout for some functions to work around
+// bugs in the ExternalInterface library.
+SWFUpload.prototype.callFlash = function (functionName, argumentArray) {
+	argumentArray = argumentArray || [];
+	
+	var self = this;
+	var callFunction = function () {
+		var movieElement = self.getMovieElement();
+		var returnValue;
+		if (typeof(movieElement[functionName]) === "function") {
+			// We have to go through all this if/else stuff because the Flash functions don't have apply() and only accept the exact number of arguments.
+			if (argumentArray.length === 0) {
+				returnValue = movieElement[functionName]();
+			} else if (argumentArray.length === 1) {
+				returnValue = movieElement[functionName](argumentArray[0]);
+			} else if (argumentArray.length === 2) {
+				returnValue = movieElement[functionName](argumentArray[0], argumentArray[1]);
+			} else if (argumentArray.length === 3) {
+				returnValue = movieElement[functionName](argumentArray[0], argumentArray[1], argumentArray[2]);
+			} else {
+				throw "Too many arguments";
+			}
+			
+			// Unescape file post param values
+			if (returnValue != undefined && typeof(returnValue.post) === "object") {
+				returnValue = self.unescapeFilePostParams(returnValue);
+			}
+			
+			return returnValue;
+		} else {
+			throw "Invalid function name";
+		}
+	};
+	
+	return callFunction();
+};
+
+
+/* *****************************
+	-- Flash control methods --
+	Your UI should use these
+	to operate SWFUpload
+   ***************************** */
+
+// Public: selectFile causes a File Selection Dialog window to appear.  This
+// dialog only allows 1 file to be selected.
+SWFUpload.prototype.selectFile = function () {
+	this.callFlash("SelectFile");
+};
+
+// Public: selectFiles causes a File Selection Dialog window to appear/ This
+// dialog allows the user to select any number of files
+// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
+// If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
+// for this bug.
+SWFUpload.prototype.selectFiles = function () {
+	this.callFlash("SelectFiles");
+};
+
+
+// Public: startUpload starts uploading the first file in the queue unless
+// the optional parameter 'fileID' specifies the ID 
+SWFUpload.prototype.startUpload = function (fileID) {
+	this.callFlash("StartUpload", [fileID]);
+};
+
+/* Cancels a the file upload.  You must specify a file_id */
+// Public: cancelUpload cancels any queued file.  The fileID parameter
+// must be specified.
+SWFUpload.prototype.cancelUpload = function (fileID) {
+	this.callFlash("CancelUpload", [fileID]);
+};
+
+// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
+// If nothing is currently uploading then nothing happens.
+SWFUpload.prototype.stopUpload = function () {
+	this.callFlash("StopUpload");
+};
+
+/* ************************
+ * Settings methods
+ *   These methods change the SWFUpload settings.
+ *   SWFUpload settings should not be changed directly on the settings object
+ *   since many of the settings need to be passed to Flash in order to take
+ *   effect.
+ * *********************** */
+
+// Public: getStats gets the file statistics object.
+SWFUpload.prototype.getStats = function () {
+	return this.callFlash("GetStats");
+};
+
+// Public: setStats changes the SWFUpload statistics.  You shouldn't need to 
+// change the statistics but you can.  Changing the statistics does not
+// affect SWFUpload accept for the successful_uploads count which is used
+// by the upload_limit setting to determine how many files the user may upload.
+SWFUpload.prototype.setStats = function (statsObject) {
+	this.callFlash("SetStats", [statsObject]);
+};
+
+// Public: getFile retrieves a File object by ID or Index.  If the file is
+// not found then 'null' is returned.
+SWFUpload.prototype.getFile = function (fileID) {
+	if (typeof(fileID) === "number") {
+		return this.callFlash("GetFileByIndex", [fileID]);
+	} else {
+		return this.callFlash("GetFile", [fileID]);
+	}
+};
+
+// Public: addFileParam sets a name/value pair that will be posted with the
+// file specified by the Files ID.  If the name already exists then the
+// exiting value will be overwritten.
+SWFUpload.prototype.addFileParam = function (fileID, name, value) {
+	return this.callFlash("AddFileParam", [fileID, name, value]);
+};
+
+// Public: removeFileParam removes a previously set (by addFileParam) name/value
+// pair from the specified file.
+SWFUpload.prototype.removeFileParam = function (fileID, name) {
+	this.callFlash("RemoveFileParam", [fileID, name]);
+};
+
+// Public: setUploadUrl changes the upload_url setting.
+SWFUpload.prototype.setUploadURL = function (url) {
+	this.settings.upload_url = url.toString();
+	this.callFlash("SetUploadURL", [url]);
+};
+
+// Public: setPostParams changes the post_params setting
+SWFUpload.prototype.setPostParams = function (paramsObject) {
+	this.settings.post_params = paramsObject;
+	this.callFlash("SetPostParams", [paramsObject]);
+};
+
+// Public: addPostParam adds post name/value pair.  Each name can have only one value.
+SWFUpload.prototype.addPostParam = function (name, value) {
+	this.settings.post_params[name] = value;
+	this.callFlash("SetPostParams", [this.settings.post_params]);
+};
+
+// Public: removePostParam deletes post name/value pair.
+SWFUpload.prototype.removePostParam = function (name) {
+	delete this.settings.post_params[name];
+	this.callFlash("SetPostParams", [this.settings.post_params]);
+};
+
+// Public: setFileTypes changes the file_types setting and the file_types_description setting
+SWFUpload.prototype.setFileTypes = function (types, description) {
+	this.settings.file_types = types;
+	this.settings.file_types_description = description;
+	this.callFlash("SetFileTypes", [types, description]);
+};
+
+// Public: setFileSizeLimit changes the file_size_limit setting
+SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) {
+	this.settings.file_size_limit = fileSizeLimit;
+	this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
+};
+
+// Public: setFileUploadLimit changes the file_upload_limit setting
+SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) {
+	this.settings.file_upload_limit = fileUploadLimit;
+	this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
+};
+
+// Public: setFileQueueLimit changes the file_queue_limit setting
+SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) {
+	this.settings.file_queue_limit = fileQueueLimit;
+	this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
+};
+
+// Public: setFilePostName changes the file_post_name setting
+SWFUpload.prototype.setFilePostName = function (filePostName) {
+	this.settings.file_post_name = filePostName;
+	this.callFlash("SetFilePostName", [filePostName]);
+};
+
+// Public: setUseQueryString changes the use_query_string setting
+SWFUpload.prototype.setUseQueryString = function (useQueryString) {
+	this.settings.use_query_string = useQueryString;
+	this.callFlash("SetUseQueryString", [useQueryString]);
+};
+
+// Public: setRequeueOnError changes the requeue_on_error setting
+SWFUpload.prototype.setRequeueOnError = function (requeueOnError) {
+	this.settings.requeue_on_error = requeueOnError;
+	this.callFlash("SetRequeueOnError", [requeueOnError]);
+};
+
+// Public: setDebugEnabled changes the debug_enabled setting
+SWFUpload.prototype.setDebugEnabled = function (debugEnabled) {
+	this.settings.debug_enabled = debugEnabled;
+	this.callFlash("SetDebugEnabled", [debugEnabled]);
+};
+
+
+/* *******************************
+	Flash Event Interfaces
+	These functions are used by Flash to trigger the various
+	events.
+	
+	All these functions a Private.
+	
+	Because the ExternalInterface library is buggy the event calls
+	are added to a queue and the queue then executed by a setTimeout.
+	This ensures that events are executed in a determinate order and that
+	the ExternalInterface bugs are avoided.
+******************************* */
+
+SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) {
+	// Warning: Don't call this.debug inside here or you'll create an infinite loop
+	
+	if (argumentArray == undefined) {
+		argumentArray = [];
+	} else if (!(argumentArray instanceof Array)) {
+		argumentArray = [argumentArray];
+	}
+	
+	var self = this;
+	if (typeof(this.settings[handlerName]) === "function") {
+		// Queue the event
+		this.eventQueue.push(function () {
+			this.settings[handlerName].apply(this, argumentArray);
+		});
+		
+		// Execute the next queued event
+		setTimeout(function () {
+			self.executeNextEvent();
+		}, 0);
+		
+	} else if (this.settings[handlerName] !== null) {
+		throw "Event handler " + handlerName + " is unknown or is not a function";
+	}
+};
+
+// Private: Causes the next event in the queue to be executed.  Since events are queued using a setTimeout
+// we must queue them in order to garentee that they are executed in order.
+SWFUpload.prototype.executeNextEvent = function () {
+	// Warning: Don't call this.debug inside here or you'll create an infinite loop
+
+	var  f = this.eventQueue ? this.eventQueue.shift() : null;
+	if (typeof(f) === "function") {
+		f.apply(this);
+	}
+};
+
+// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterfance cannot have
+// properties that contain characters that are not valid for JavaScript identifiers. To work around this
+// the Flash Component escapes the parameter names and we must unescape again before passing them along.
+SWFUpload.prototype.unescapeFilePostParams = function (file) {
+	var reg = /[$]([0-9a-f]{4})/i;
+	var unescapedPost = {};
+	var uk;
+
+	if (file != undefined) {
+		for (var k in file.post) {
+			if (file.post.hasOwnProperty(k)) {
+				uk = k;
+				var match;
+				while ((match = reg.exec(uk)) !== null) {
+					uk = uk.replace(match[0], String.fromCharCode(parseInt("0x"+match[1], 16)));
+				}
+				unescapedPost[uk] = file.post[k];
+			}
+		}
+
+		file.post = unescapedPost;
+	}
+
+	return file;
+};
+
+SWFUpload.prototype.flashReady = function () {
+	// Check that the movie element is loaded correctly with its ExternalInterface methods defined
+	var movieElement = this.getMovieElement();
+	if (typeof(movieElement.StartUpload) !== "function") {
+		throw "ExternalInterface methods failed to initialize.";
+	}
+	
+	this.queueEvent("swfupload_loaded_handler");
+};
+
+
+/* This is a chance to do something before the browse window opens */
+SWFUpload.prototype.fileDialogStart = function () {
+	this.queueEvent("file_dialog_start_handler");
+};
+
+
+/* Called when a file is successfully added to the queue. */
+SWFUpload.prototype.fileQueued = function (file) {
+	file = this.unescapeFilePostParams(file);
+	this.queueEvent("file_queued_handler", file);
+};
+
+
+/* Handle errors that occur when an attempt to queue a file fails. */
+SWFUpload.prototype.fileQueueError = function (file, errorCode, message) {
+	file = this.unescapeFilePostParams(file);
+	this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
+};
+
+/* Called after the file dialog has closed and the selected files have been queued.
+	You could call startUpload here if you want the queued files to begin uploading immediately. */
+SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued) {
+	this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued]);
+};
+
+SWFUpload.prototype.uploadStart = function (file) {
+	file = this.unescapeFilePostParams(file);
+	this.queueEvent("return_upload_start_handler", file);
+};
+
+SWFUpload.prototype.returnUploadStart = function (file) {
+	var returnValue;
+	if (typeof(this.settings.upload_start_handler) === "function") {
+		file = this.unescapeFilePostParams(file);
+		returnValue = this.settings.upload_start_handler.call(this, file);
+	} else if (this.settings.upload_start_handler != undefined) {
+		throw "upload_start_handler must be a function";
+	}
+
+	// Convert undefined to true so if nothing is returned from the upload_start_handler it is
+	// interpretted as 'true'.
+	if (returnValue === undefined) {
+		returnValue = true;
+	}
+	
+	returnValue = !!returnValue;
+	
+	this.callFlash("ReturnUploadStart", [returnValue]);
+};
+
+
+
+SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) {
+	file = this.unescapeFilePostParams(file);
+	this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
+};
+
+SWFUpload.prototype.uploadError = function (file, errorCode, message) {
+	file = this.unescapeFilePostParams(file);
+	this.queueEvent("upload_error_handler", [file, errorCode, message]);
+};
+
+SWFUpload.prototype.uploadSuccess = function (file, serverData) {
+	file = this.unescapeFilePostParams(file);
+	this.queueEvent("upload_success_handler", [file, serverData]);
+};
+
+SWFUpload.prototype.uploadComplete = function (file) {
+	file = this.unescapeFilePostParams(file);
+	this.queueEvent("upload_complete_handler", file);
+};
+
+/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
+   internal debug console.  You can override this event and have messages written where you want. */
+SWFUpload.prototype.debug = function (message) {
+	this.queueEvent("debug_handler", message);
+};
+
+
+/* **********************************
+	Debug Console
+	The debug console is a self contained, in page location
+	for debug message to be sent.  The Debug Console adds
+	itself to the body if necessary.
+
+	The console is automatically scrolled as messages appear.
+	
+	If you are using your own debug handler or when you deploy to production and
+	have debug disabled you can remove these functions to reduce the file size
+	and complexity.
+********************************** */
+   
+// Private: debugMessage is the default debug_handler.  If you want to print debug messages
+// call the debug() function.  When overriding the function your own function should
+// check to see if the debug setting is true before outputting debug information.
+SWFUpload.prototype.debugMessage = function (message) {
+	if (this.settings.debug) {
+		var exceptionMessage, exceptionValues = [];
+
+		// Check for an exception object and print it nicely
+		if (typeof(message) === "object" && typeof(message.name) === "string" && typeof(message.message) === "string") {
+			for (var key in message) {
+				if (message.hasOwnProperty(key)) {
+					exceptionValues.push(key + ": " + message[key]);
+				}
+			}
+			exceptionMessage = exceptionValues.join("\n") || "";
+			exceptionValues = exceptionMessage.split("\n");
+			exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
+			SWFUpload.Console.writeLine(exceptionMessage);
+		} else {
+			SWFUpload.Console.writeLine(message);
+		}
+	}
+};
+
+SWFUpload.Console = {};
+SWFUpload.Console.writeLine = function (message) {
+	var console, documentForm;
+
+	try {
+		console = document.getElementById("SWFUpload_Console");
+
+		if (!console) {
+			documentForm = document.createElement("form");
+			document.getElementsByTagName("body")[0].appendChild(documentForm);
+
+			console = document.createElement("textarea");
+			console.id = "SWFUpload_Console";
+			console.style.fontFamily = "monospace";
+			console.setAttribute("wrap", "off");
+			console.wrap = "off";
+			console.style.overflow = "auto";
+			console.style.width = "700px";
+			console.style.height = "350px";
+			console.style.margin = "5px";
+			documentForm.appendChild(console);
+		}
+
+		console.value += message + "\n";
+
+		console.scrollTop = console.scrollHeight - console.clientHeight;
+	} catch (ex) {
+		alert("Exception: " + ex.name + " Message: " + ex.message);
+	}
+};
Index: /afridex/plugins/Flutter/js/swfupload/swfcallbacks.js
===================================================================
--- /afridex/plugins/Flutter/js/swfupload/swfcallbacks.js (revision 21)
+++ /afridex/plugins/Flutter/js/swfupload/swfcallbacks.js (revision 21)
@@ -0,0 +1,102 @@
+var swfu;
+var holder = new Array() ;
+var current_name ;
+Event.observe(window, 'load', function ()
+{
+	swfu = new SWFUpload({ 
+		upload_url : flutter_path+"RCCWP_GetFile.php",
+		flash_url : flutter_path+"js/swfupload/swfupload_f9.swf",
+		file_size_limit : "20480",
+		debug : false,
+		file_queued_handler : startUploading,
+		upload_start_handler : adjust,
+		upload_progress_handler : progress,
+		upload_success_handler : completed,
+		post_params : {
+			"auth_cookie" : swf_authentication,
+			"_wpnonce" : swf_nonce
+			}
+		});
+});
+
+
+function openFile(input_name)
+{
+	swfu.selectFile() ;
+	current_name = input_name ;
+}
+
+function startUploading(file)
+{
+	holder[file.id] = current_name ;
+	var progr = document.getElementById("upload_progress_"+current_name) ;
+	progr.style.visibility = "visible" ;
+	progr.style.height = "auto" ;
+	progr.innerHTML = "Waiting ... " ;
+	var h = swfu.getStats() ;
+	swfu.startUpload() ;
+}
+
+function adjust(file)
+{
+	var progr = document.getElementById("upload_progress_"+holder[file.id]) ;
+	progr.style.visibility = "visible" ;
+	progr.style.height = "auto" ;
+	progr.innerHTML = "<img src="+flutter_path+"images/spinner.gif /> uploading ... <img src='"+flutter_path+"images/bar.jpg' height=10 width=0 />" ;
+}
+
+function progress(file,compl,total)
+{
+	var progr = document.getElementById("upload_progress_"+holder[file.id]) ;
+	progr.innerHTML = "<img src="+flutter_path+"images/spinner.gif />uploading ... " + parseInt(100*compl/total) + "% <img src='"+flutter_path+"images/bar.jpg' height=10 width="+(100*compl/total)+" />" ;
+	
+}
+
+function completed(file,server_data)
+{
+	var progr = document.getElementById("upload_progress_"+holder[file.id]) ;
+	progr.style.visibility = "visible" ;
+	progr.style.height = "auto" ;
+	var hold = new Array() ;
+	hold = server_data.split("*") ;
+	progr.innerHTML = hold[0] ;
+	document.getElementById(holder[file.id]).value = hold[1] ;
+	if (document.getElementById( "img_thumb_"+holder[file.id] ))
+	{
+		document.getElementById( "img_thumb_"+holder[file.id] ).src = flutter_path+"phpThumb.php?&w=150&h=120&src="+"files_flutter/"+hold[1] ;
+		var s = "<a href='#impossible_location' onclick=call_thickbox('"+hold[2]+"')>" ;
+		var e = "<strong onclick=prepareUpdatePhoto('"+holder[file.id]+"')>Edit</strong> </a>" ;
+ 		document.getElementById( "photo_edit_link_"+holder[file.id] ).innerHTML = s + e ;
+	}
+}
+
+function call_thickbox(url)
+{
+	tb_show("Flutter",url,false) ;
+}
+function uploadurl(input_name,file_type)
+{
+	var url = document.getElementById( "upload_url_"+input_name).value ;
+	var progr = document.getElementById("upload_progress_"+input_name) ;
+	var h ;
+	progr.style.visibility = "visible" ;
+	progr.style.height = "auto" ;
+	progr.innerHTML = "<img src="+flutter_path+"images/spinner.gif /> Downlaoding File ..." ;
+	new Ajax.Request(flutter_path+'RCCWP_GetFile.php',
+		{
+			method:'post',
+			onSuccess: function(transport){
+			h = transport.responseText.split("*") ;
+			document.getElementById(input_name).value = h[1] ;
+			progr.innerHTML = h[0] ;
+			if( document.getElementById( "img_thumb_"+input_name ) )
+			{
+				document.getElementById( "img_thumb_"+input_name ).src = flutter_path+"phpThumb.php?&w=150&h=120&src="+"files_flutter/"+h[1] ;
+				var s = "<a href='#impossible_location' onclick=call_thickbox('"+h[2]+"')>" ;
+				var e = "<strong onclick=prepareUpdatePhoto('"+input_name+"')>Edit</strong> </a>" ;
+ 				document.getElementById( "photo_edit_link_"+input_name ).innerHTML = s + e ;
+			}
+			},
+			parameters: "upload_url="+url+"&input_name="+input_name+"&type="+file_type
+			});
+}
Index: /afridex/plugins/Flutter/js/lib/builder.js
===================================================================
--- /afridex/plugins/Flutter/js/lib/builder.js (revision 21)
+++ /afridex/plugins/Flutter/js/lib/builder.js (revision 21)
@@ -0,0 +1,136 @@
+// script.aculo.us builder.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Builder = {
+  NODEMAP: {
+    AREA: 'map',
+    CAPTION: 'table',
+    COL: 'table',
+    COLGROUP: 'table',
+    LEGEND: 'fieldset',
+    OPTGROUP: 'select',
+    OPTION: 'select',
+    PARAM: 'object',
+    TBODY: 'table',
+    TD: 'table',
+    TFOOT: 'table',
+    TH: 'table',
+    THEAD: 'table',
+    TR: 'table'
+  },
+  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+  //       due to a Firefox bug
+  node: function(elementName) {
+    elementName = elementName.toUpperCase();
+    
+    // try innerHTML approach
+    var parentTag = this.NODEMAP[elementName] || 'div';
+    var parentElement = document.createElement(parentTag);
+    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+    } catch(e) {}
+    var element = parentElement.firstChild || null;
+      
+    // see if browser added wrapping tags
+    if(element && (element.tagName.toUpperCase() != elementName))
+      element = element.getElementsByTagName(elementName)[0];
+    
+    // fallback to createElement approach
+    if(!element) element = document.createElement(elementName);
+    
+    // abort if nothing could be created
+    if(!element) return;
+
+    // attributes (or text)
+    if(arguments[1])
+      if(this._isStringOrNumber(arguments[1]) ||
+        (arguments[1] instanceof Array) ||
+        arguments[1].tagName) {
+          this._children(element, arguments[1]);
+        } else {
+          var attrs = this._attributes(arguments[1]);
+          if(attrs.length) {
+            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+              parentElement.innerHTML = "<" +elementName + " " +
+                attrs + "></" + elementName + ">";
+            } catch(e) {}
+            element = parentElement.firstChild || null;
+            // workaround firefox 1.0.X bug
+            if(!element) {
+              element = document.createElement(elementName);
+              for(attr in arguments[1]) 
+                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+            }
+            if(element.tagName.toUpperCase() != elementName)
+              element = parentElement.getElementsByTagName(elementName)[0];
+          }
+        } 
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+
+  ATTR_MAP: {
+    'className': 'class',
+    'htmlFor': 'for'
+  },
+
+  _attributes: function(attributes) {
+    var attrs = [];
+    for(attribute in attributes)
+      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
+          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
+    return attrs.join(" ");
+  },
+  _children: function(element, children) {
+    if(children.tagName) {
+      element.appendChild(children);
+      return;
+    }
+    if(typeof children=='object') { // array can hold nodes and text
+      children.flatten().each( function(e) {
+        if(typeof e=='object')
+          element.appendChild(e)
+        else
+          if(Builder._isStringOrNumber(e))
+            element.appendChild(Builder._text(e));
+      });
+    } else
+      if(Builder._isStringOrNumber(children))
+        element.appendChild(Builder._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  },
+  build: function(html) {
+    var element = this.node('div');
+    $(element).update(html.strip());
+    return element.down();
+  },
+  dump: function(scope) { 
+    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
+  
+    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+  
+    tags.each( function(tag){ 
+      scope[tag] = function() { 
+        return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
+      } 
+    });
+  }
+}
Index: /afridex/plugins/Flutter/js/lib/sound.js
===================================================================
--- /afridex/plugins/Flutter/js/lib/sound.js (revision 21)
+++ /afridex/plugins/Flutter/js/lib/sound.js (revision 21)
@@ -0,0 +1,55 @@
+// script.aculo.us sound.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Based on code created by Jules Gravinese (http://www.webveteran.com/)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+Sound = {
+  tracks: {},
+  _enabled: true,
+  template:
+    new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
+  enable: function(){
+    Sound._enabled = true;
+  },
+  disable: function(){
+    Sound._enabled = false;
+  },
+  play: function(url){
+    if(!Sound._enabled) return;
+    var options = Object.extend({
+      track: 'global', url: url, replace: false
+    }, arguments[1] || {});
+    
+    if(options.replace && this.tracks[options.track]) {
+      $R(0, this.tracks[options.track].id).each(function(id){
+        var sound = $('sound_'+options.track+'_'+id);
+        sound.Stop && sound.Stop();
+        sound.remove();
+      })
+      this.tracks[options.track] = null;
+    }
+      
+    if(!this.tracks[options.track])
+      this.tracks[options.track] = { id: 0 }
+    else
+      this.tracks[options.track].id++;
+      
+    options.id = this.tracks[options.track].id;
+    $$('body')[0].insert( 
+      Prototype.Browser.IE ? new Element('bgsound',{
+        id: 'sound_'+options.track+'_'+options.id,
+        src: options.url, loop: 1, autostart: true
+      }) : Sound.template.evaluate(options));
+  }
+};
+
+if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
+  if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
+    Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>')
+  else
+    Sound.play = function(){}
+}
Index: /afridex/plugins/Flutter/js/lib/effects.js
===================================================================
--- /afridex/plugins/Flutter/js/lib/effects.js (revision 21)
+++ /afridex/plugins/Flutter/js/lib/effects.js (revision 21)
@@ -0,0 +1,1122 @@
+// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/ 
+
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';
+  if (this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if (this.slice(0,1) == '#') {  
+      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if (this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return (color.length==7 ? color : (arguments[0] || this));  
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);  
+  element.setStyle({fontSize: (percent/100) + 'em'});   
+  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+  return element;
+};
+
+Element.getInlineOpacity = function(element){
+  return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
+  Transitions: {
+    linear: Prototype.K,
+    sinoidal: function(pos) {
+      return (-Math.cos(pos*Math.PI)/2) + 0.5;
+    },
+    reverse: function(pos) {
+      return 1-pos;
+    },
+    flicker: function(pos) {
+      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+      return pos > 1 ? 1 : pos;
+    },
+    wobble: function(pos) {
+      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+    },
+    pulse: function(pos, pulses) { 
+      pulses = pulses || 5; 
+      return (
+        ((pos % (1/pulses)) * pulses).round() == 0 ? 
+              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
+          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
+        );
+    },
+    spring: function(pos) { 
+      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
+    },
+    none: function(pos) {
+      return 0;
+    },
+    full: function(pos) {
+      return 1;
+    }
+  },
+  DefaultOptions: {
+    duration:   1.0,   // seconds
+    fps:        100,   // 100= assume 66fps max.
+    sync:       false, // true for combining
+    from:       0.0,
+    to:         1.0,
+    delay:      0.0,
+    queue:      'parallel'
+  },
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+    
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if (child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            new Element('span', {style: tagifyStyle}).update(
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if (((typeof element == 'object') || 
+        Object.isFunction(element)) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || { });
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || { });
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;    
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = Object.isString(effect.options.queue) ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'with-last':
+        timestamp = this.effects.pluck('startOn').max() || timestamp;
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if (!this.interval)
+      this.interval = setInterval(this.loop.bind(this), 15);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if (this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    for(var i=0, len=this.effects.length;i<len;i++) 
+      this.effects[i] && this.effects[i].loop(timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if (!Object.isString(queueName)) return queueName;
+    
+    return this.instances.get(queueName) ||
+      this.instances.set(queueName, new Effect.ScopedQueue());
+  }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+  position: null,
+  start: function(options) {
+    function codeForEvent(options,eventName){
+      return (
+        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+      );
+    }
+    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn+(this.options.duration*1000);
+    this.fromToDelta  = this.options.to-this.options.from;
+    this.totalTime    = this.finishOn-this.startOn;
+    this.totalFrames  = this.options.fps*this.options.duration;
+    
+    eval('this.render = function(pos){ '+
+      'if (this.state=="idle"){this.state="running";'+
+      codeForEvent(this.options,'beforeSetup')+
+      (this.setup ? 'this.setup();':'')+ 
+      codeForEvent(this.options,'afterSetup')+
+      '};if (this.state=="running"){'+
+      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
+      'this.position=pos;'+
+      codeForEvent(this.options,'beforeUpdate')+
+      (this.update ? 'this.update(pos);':'')+
+      codeForEvent(this.options,'afterUpdate')+
+      '}}');
+    
+    this.event('beforeStart');
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if (timePos >= this.startOn) {
+      if (timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if (this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / this.totalTime,
+          frame = (pos * this.totalFrames).round();
+      if (frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  cancel: function() {
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if (this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    var data = $H();
+    for(property in this)
+      if (!Object.isFunction(this[property])) data.set(property, this[property]);
+    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if (effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+  initialize: function(object, from, to) {
+    object = Object.isString(object) ? $(object) : object;
+    var args = $A(arguments), method = args.last(), 
+      options = args.length == 5 ? args[3] : null;
+    this.method = Object.isFunction(method) ? method.bind(object) :
+      Object.isFunction(object[method]) ? object[method].bind(object) : 
+      function(value) { object[method] = value };
+    this.start(Object.extend({ from: from, to: to }, options || { }));
+  },
+  update: function(position) {
+    this.method(position);
+  }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+  initialize: function() {
+    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+  },
+  update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    // make this work on IE on elements without 'layout'
+    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if (this.options.mode == 'absolute') {
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: (this.options.x  * position + this.originalLeft).round() + 'px',
+      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+  initialize: function(element, percent) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = { };
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%','pt'].each( function(fontSizeType) {
+      if (fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if (this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if (/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if (!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if (this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = { };
+    if (this.options.scaleX) d.width = width.round() + 'px';
+    if (this.options.scaleY) d.height = height.round() + 'px';
+    if (this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if (this.elementPositioning == 'absolute') {
+        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if (this.options.scaleY) d.top = -topd + 'px';
+        if (this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = { };
+    if (!this.options.keepBackgroundImage) {
+      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+      this.element.setStyle({backgroundImage: 'none'});
+    }
+    if (!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if (!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = function(element) {
+  var options = arguments[1] || { },
+    scrollOffsets = document.viewport.getScrollOffsets(),
+    elementOffsets = $(element).cumulativeOffset(),
+    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  
+
+  if (options.offset) elementOffsets[1] += options.offset;
+
+  return new Effect.Tween(null,
+    scrollOffsets.top,
+    elementOffsets[1] > max ? max : elementOffsets[1],
+    options,
+    function(p){ scrollTo(scrollOffsets.left, p.round()) }
+  );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+    from: element.getOpacity() || 1.0,
+    to:   0.0,
+    afterFinishInternal: function(effect) { 
+      if (effect.options.to!=0) return;
+      effect.element.hide().setStyle({opacity: oldOpacity}); 
+    }
+  }, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from).show(); 
+  }}, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = { 
+    opacity: element.getInlineOpacity(), 
+    position: element.getStyle('position'),
+    top:  element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height
+  };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) {
+        Position.absolutize(effect.effects[0].element)
+      },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide().setStyle(oldStyle); }
+     }, arguments[1] || { })
+   );
+};
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0,
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping();
+      } 
+    }, arguments[1] || { })
+  );
+};
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false,
+    scaleFrom: 0,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping();
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, Object.extend({
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned().makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+        } 
+      }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    distance: 20,
+    duration: 0.5
+  }, arguments[1] || {});
+  var distance = parseFloat(options.distance);
+  var split = parseFloat(options.duration) / 10.0;
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element,
+      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned().setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+};
+
+Effect.SlideDown = function(element) {
+  element = $(element).cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || { })
+  );
+};
+
+Effect.SlideUp = function(element) {
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().show();
+    },  
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+    }
+   }, arguments[1] || { })
+  );
+};
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, { 
+    restoreAfterFinish: true,
+    beforeSetup: function(effect) {
+      effect.element.makeClipping(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping(); 
+    }
+  });
+};
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide().makeClipping().makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'}).show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+};
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned().makeClipping(); 
+         },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+       }, options)
+  );
+};
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || { };
+  var oldOpacity = element.getInlineOpacity();
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  element.makeClipping();
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping().setStyle(oldStyle);
+      } });
+  }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: { }
+    }, arguments[1] || { });
+    
+    if (!Object.isString(options.style)) this.style = $H(options.style);
+    else {
+      if (options.style.include(':'))
+        this.style = options.style.parseStyle();
+      else {
+        this.element.addClassName(options.style);
+        this.style = $H(this.element.getStyles());
+        this.element.removeClassName(options.style);
+        var css = this.element.getStyles();
+        this.style = this.style.reject(function(style) {
+          return style.value == css[style.key];
+        });
+        options.afterFinishInternal = function(effect) {
+          effect.element.addClassName(effect.options.style);
+          effect.transforms.each(function(transform) {
+            effect.element.style[transform.style] = '';
+          });
+        }
+      }
+    }
+    this.start(options);
+  },
+  
+  setup: function(){
+    function parseColor(color){
+      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+      });
+    }
+    this.transforms = this.style.map(function(pair){
+      var property = pair[0], value = pair[1], unit = null;
+
+      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+        value = value.parseColor();
+        unit  = 'color';
+      } else if (property == 'opacity') {
+        value = parseFloat(value);
+        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+          this.element.setStyle({zoom: 1});
+      } else if (Element.CSS_LENGTH.test(value)) {
+          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+          value = parseFloat(components[1]);
+          unit = (components.length == 3) ? components[2] : null;
+      }
+
+      var originalValue = this.element.getStyle(property);
+      return { 
+        style: property.camelize(), 
+        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
+        targetValue: unit=='color' ? parseColor(value) : value,
+        unit: unit
+      };
+    }.bind(this)).reject(function(transform){
+      return (
+        (transform.originalValue == transform.targetValue) ||
+        (
+          transform.unit != 'color' &&
+          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+        )
+      )
+    });
+  },
+  update: function(position) {
+    var style = { }, transform, i = this.transforms.length;
+    while(i--)
+      style[(transform = this.transforms[i]).style] = 
+        transform.unit=='color' ? '#'+
+          (Math.round(transform.originalValue[0]+
+            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+          (Math.round(transform.originalValue[1]+
+            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+          (Math.round(transform.originalValue[2]+
+            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+        (transform.originalValue +
+          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
+            (transform.unit === null ? '' : transform.unit);
+    this.element.setStyle(style, true);
+  }
+});
+
+Effect.Transform = Class.create({
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || { };
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      track = $H(track);
+      var data = track.values().first();
+      this.tracks.push($H({
+        ids:     track.keys().first(),
+        effect:  Effect.Morph,
+        options: { style: data }
+      }));
+    }.bind(this));
+    return this;
+  },
+  play: function(){
+    return new Effect.Parallel(
+      this.tracks.map(function(track){
+        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+        var elements = [$(ids) || $$(ids)].flatten();
+        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = $w(
+  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
+  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+  'fontSize fontWeight height left letterSpacing lineHeight ' +
+  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+  'right textIndent top width wordSpacing zIndex');
+  
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+  var style, styleRules = $H();
+  if (Prototype.Browser.WebKit)
+    style = new Element('div',{style:this}).style;
+  else {
+    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+    style = String.__parseStyleElement.childNodes[0].style;
+  }
+  
+  Element.CSS_PROPERTIES.each(function(property){
+    if (style[property]) styleRules.set(property, style[property]); 
+  });
+  
+  if (Prototype.Browser.IE && this.include('opacity'))
+    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+  return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+  Element.getStyles = function(element) {
+    var css = document.defaultView.getComputedStyle($(element), null);
+    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+      styles[property] = css[property];
+      return styles;
+    });
+  };
+} else {
+  Element.getStyles = function(element) {
+    element = $(element);
+    var css = element.currentStyle, styles;
+    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+      results[property] = css[property];
+      return results;
+    });
+    if (!styles.opacity) styles.opacity = element.getOpacity();
+    return styles;
+  };
+};
+
+Effect.Methods = {
+  morph: function(element, style) {
+    element = $(element);
+    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+    return element;
+  },
+  visualEffect: function(element, effect, options) {
+    element = $(element)
+    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+    new Effect[klass](element, options);
+    return element;
+  },
+  highlight: function(element, options) {
+    element = $(element);
+    new Effect.Highlight(element, options);
+    return element;
+  }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+  'pulsate shake puff squish switchOff dropOut').each(
+  function(effect) { 
+    Effect.Methods[effect] = function(element, options){
+      element = $(element);
+      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+      return element;
+    }
+  }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
+  function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods);
Index: /afridex/plugins/Flutter/js/lib/unittest.js
===================================================================
--- /afridex/plugins/Flutter/js/lib/unittest.js (revision 21)
+++ /afridex/plugins/Flutter/js/lib/unittest.js (revision 21)
@@ -0,0 +1,568 @@
+// script.aculo.us unittest.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+//           (c) 2005-2007 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+  var options = Object.extend({
+    pointerX: 0,
+    pointerY: 0,
+    buttons:  0,
+    ctrlKey:  false,
+    altKey:   false,
+    shiftKey: false,
+    metaKey:  false
+  }, arguments[2] || {});
+  var oEvent = document.createEvent("MouseEvents");
+  oEvent.initMouseEvent(eventName, true, true, document.defaultView, 
+    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element));
+  
+  if(this.mark) Element.remove(this.mark);
+  this.mark = document.createElement('div');
+  this.mark.appendChild(document.createTextNode(" "));
+  document.body.appendChild(this.mark);
+  this.mark.style.position = 'absolute';
+  this.mark.style.top = options.pointerY + "px";
+  this.mark.style.left = options.pointerX + "px";
+  this.mark.style.width = "5px";
+  this.mark.style.height = "5px;";
+  this.mark.style.borderTop = "1px solid red;"
+  this.mark.style.borderLeft = "1px solid red;"
+  
+  if(this.step)
+    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+  
+  $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+  var options = Object.extend({
+    ctrlKey: false,
+    altKey: false,
+    shiftKey: false,
+    metaKey: false,
+    keyCode: 0,
+    charCode: 0
+  }, arguments[2] || {});
+
+  var oEvent = document.createEvent("KeyEvents");
+  oEvent.initKeyEvent(eventName, true, true, window, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+    options.keyCode, options.charCode );
+  $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+  for(var i=0; i<command.length; i++) {
+    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+  }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = Object.inspect;
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+  initialize: function(log) {
+    this.log = $(log);
+    if (this.log) {
+      this._createLogTable();
+    }
+  },
+  start: function(testName) {
+    if (!this.log) return;
+    this.testName = testName;
+    this.lastLogLine = document.createElement('tr');
+    this.statusCell = document.createElement('td');
+    this.nameCell = document.createElement('td');
+    this.nameCell.className = "nameCell";
+    this.nameCell.appendChild(document.createTextNode(testName));
+    this.messageCell = document.createElement('td');
+    this.lastLogLine.appendChild(this.statusCell);
+    this.lastLogLine.appendChild(this.nameCell);
+    this.lastLogLine.appendChild(this.messageCell);
+    this.loglines.appendChild(this.lastLogLine);
+  },
+  finish: function(status, summary) {
+    if (!this.log) return;
+    this.lastLogLine.className = status;
+    this.statusCell.innerHTML = status;
+    this.messageCell.innerHTML = this._toHTML(summary);
+    this.addLinksToResults();
+  },
+  message: function(message) {
+    if (!this.log) return;
+    this.messageCell.innerHTML = this._toHTML(message);
+  },
+  summary: function(summary) {
+    if (!this.log) return;
+    this.logsummary.innerHTML = this._toHTML(summary);
+  },
+  _createLogTable: function() {
+    this.log.innerHTML =
+    '<div id="logsummary"></div>' +
+    '<table id="logtable">' +
+    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+    '<tbody id="loglines"></tbody>' +
+    '</table>';
+    this.logsummary = $('logsummary')
+    this.loglines = $('loglines');
+  },
+  _toHTML: function(txt) {
+    return txt.escapeHTML().replace(/\n/g,"<br/>");
+  },
+  addLinksToResults: function(){ 
+    $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log
+      td.title = "Run only this test"
+      Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;});
+    });
+    $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log
+      td.title = "Run all tests"
+      Event.observe(td, 'click', function(){ window.location.search = "";});
+    });
+  }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+  initialize: function(testcases) {
+    this.options = Object.extend({
+      testLog: 'testlog'
+    }, arguments[1] || {});
+    this.options.resultsURL = this.parseResultsURLQueryParameter();
+    this.options.tests      = this.parseTestsQueryParameter();
+    if (this.options.testLog) {
+      this.options.testLog = $(this.options.testLog) || null;
+    }
+    if(this.options.tests) {
+      this.tests = [];
+      for(var i = 0; i < this.options.tests.length; i++) {
+        if(/^test/.test(this.options.tests[i])) {
+          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+        }
+      }
+    } else {
+      if (this.options.test) {
+        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+      } else {
+        this.tests = [];
+        for(var testcase in testcases) {
+          if(/^test/.test(testcase)) {
+            this.tests.push(
+               new Test.Unit.Testcase(
+                 this.options.context ? ' -> ' + this.options.titles[testcase] : testcase, 
+                 testcases[testcase], testcases["setup"], testcases["teardown"]
+               ));
+          }
+        }
+      }
+    }
+    this.currentTest = 0;
+    this.logger = new Test.Unit.Logger(this.options.testLog);
+    setTimeout(this.runTests.bind(this), 1000);
+  },
+  parseResultsURLQueryParameter: function() {
+    return window.location.search.parseQuery()["resultsURL"];
+  },
+  parseTestsQueryParameter: function(){
+    if (window.location.search.parseQuery()["tests"]){
+        return window.location.search.parseQuery()["tests"].split(',');
+    };
+  },
+  // Returns:
+  //  "ERROR" if there was an error,
+  //  "FAILURE" if there was a failure, or
+  //  "SUCCESS" if there was neither
+  getResult: function() {
+    var hasFailure = false;
+    for(var i=0;i<this.tests.length;i++) {
+      if (this.tests[i].errors > 0) {
+        return "ERROR";
+      }
+      if (this.tests[i].failures > 0) {
+        hasFailure = true;
+      }
+    }
+    if (hasFailure) {
+      return "FAILURE";
+    } else {
+      return "SUCCESS";
+    }
+  },
+  postResults: function() {
+    if (this.options.resultsURL) {
+      new Ajax.Request(this.options.resultsURL, 
+        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+    }
+  },
+  runTests: function() {
+    var test = this.tests[this.currentTest];
+    if (!test) {
+      // finished!
+      this.postResults();
+      this.logger.summary(this.summary());
+      return;
+    }
+    if(!test.isWaiting) {
+      this.logger.start(test.name);
+    }
+    test.run();
+    if(test.isWaiting) {
+      this.logger.message("Waiting for " + test.timeToWait + "ms");
+      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+    } else {
+      this.logger.finish(test.status(), test.summary());
+      this.currentTest++;
+      // tail recursive, hopefully the browser will skip the stackframe
+      this.runTests();
+    }
+  },
+  summary: function() {
+    var assertions = 0;
+    var failures = 0;
+    var errors = 0;
+    var messages = [];
+    for(var i=0;i<this.tests.length;i++) {
+      assertions +=   this.tests[i].assertions;
+      failures   +=   this.tests[i].failures;
+      errors     +=   this.tests[i].errors;
+    }
+    return (
+      (this.options.context ? this.options.context + ': ': '') + 
+      this.tests.length + " tests, " + 
+      assertions + " assertions, " + 
+      failures   + " failures, " +
+      errors     + " errors");
+  }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+  initialize: function() {
+    this.assertions = 0;
+    this.failures   = 0;
+    this.errors     = 0;
+    this.messages   = [];
+  },
+  summary: function() {
+    return (
+      this.assertions + " assertions, " + 
+      this.failures   + " failures, " +
+      this.errors     + " errors" + "\n" +
+      this.messages.join("\n"));
+  },
+  pass: function() {
+    this.assertions++;
+  },
+  fail: function(message) {
+    this.failures++;
+    this.messages.push("Failure: " + message);
+  },
+  info: function(message) {
+    this.messages.push("Info: " + message);
+  },
+  error: function(error) {
+    this.errors++;
+    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+  },
+  status: function() {
+    if (this.failures > 0) return 'failed';
+    if (this.errors > 0) return 'error';
+    return 'passed';
+  },
+  assert: function(expression) {
+    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+    try { expression ? this.pass() : 
+      this.fail(message); }
+    catch(e) { this.error(e); }
+  },
+  assertEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEqual";
+    try { (expected == actual) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertInspect: function(expected, actual) {
+    var message = arguments[2] || "assertInspect";
+    try { (expected == actual.inspect()) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertEnumEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEnumEqual";
+    try { $A(expected).length == $A(actual).length && 
+      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
+          ', actual ' + Test.Unit.inspect(actual)); }
+    catch(e) { this.error(e); }
+  },
+  assertNotEqual: function(expected, actual) {
+    var message = arguments[2] || "assertNotEqual";
+    try { (expected != actual) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertIdentical: function(expected, actual) { 
+    var message = arguments[2] || "assertIdentical"; 
+    try { (expected === actual) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + Test.Unit.inspect(actual) + '"'); } 
+    catch(e) { this.error(e); } 
+  },
+  assertNotIdentical: function(expected, actual) { 
+    var message = arguments[2] || "assertNotIdentical"; 
+    try { !(expected === actual) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + Test.Unit.inspect(actual) + '"'); } 
+    catch(e) { this.error(e); } 
+  },
+  assertNull: function(obj) {
+    var message = arguments[1] || 'assertNull'
+    try { (obj==null) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertMatch: function(expected, actual) {
+    var message = arguments[2] || 'assertMatch';
+    var regex = new RegExp(expected);
+    try { (regex.exec(actual)) ? this.pass() :
+      this.fail(message + ' : regex: "' +  Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertHidden: function(element) {
+    var message = arguments[1] || 'assertHidden';
+    this.assertEqual("none", element.style.display, message);
+  },
+  assertNotNull: function(object) {
+    var message = arguments[1] || 'assertNotNull';
+    this.assert(object != null, message);
+  },
+  assertType: function(expected, actual) {
+    var message = arguments[2] || 'assertType';
+    try { 
+      (actual.constructor == expected) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + (actual.constructor) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertNotOfType: function(expected, actual) {
+    var message = arguments[2] || 'assertNotOfType';
+    try { 
+      (actual.constructor != expected) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + (actual.constructor) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertInstanceOf';
+    try { 
+      (actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was not an instance of the expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertNotInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertNotInstanceOf';
+    try { 
+      !(actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was an instance of the not expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertRespondsTo: function(method, obj) {
+    var message = arguments[2] || 'assertRespondsTo';
+    try {
+      (obj[method] && typeof obj[method] == 'function') ? this.pass() : 
+      this.fail(message + ": object doesn't respond to [" + method + "]"); }
+    catch(e) { this.error(e); }
+  },
+  assertReturnsTrue: function(method, obj) {
+    var message = arguments[2] || 'assertReturnsTrue';
+    try {
+      var m = obj[method];
+      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
+      m() ? this.pass() : 
+      this.fail(message + ": method returned false"); }
+    catch(e) { this.error(e); }
+  },
+  assertReturnsFalse: function(method, obj) {
+    var message = arguments[2] || 'assertReturnsFalse';
+    try {
+      var m = obj[method];
+      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
+      !m() ? this.pass() : 
+      this.fail(message + ": method returned true"); }
+    catch(e) { this.error(e); }
+  },
+  assertRaise: function(exceptionName, method) {
+    var message = arguments[2] || 'assertRaise';
+    try { 
+      method();
+      this.fail(message + ": exception expected but none was raised"); }
+    catch(e) {
+      ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e); 
+    }
+  },
+  assertElementsMatch: function() {
+    var expressions = $A(arguments), elements = $A(expressions.shift());
+    if (elements.length != expressions.length) {
+      this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
+      return false;
+    }
+    elements.zip(expressions).all(function(pair, index) {
+      var element = $(pair.first()), expression = pair.last();
+      if (element.match(expression)) return true;
+      this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
+    }.bind(this)) && this.pass();
+  },
+  assertElementMatches: function(element, expression) {
+    this.assertElementsMatch([element], expression);
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  },
+  _isVisible: function(element) {
+    element = $(element);
+    if(!element.parentNode) return true;
+    this.assertNotNull(element);
+    if(element.style && Element.getStyle(element, 'display') == 'none')
+      return false;
+    
+    return this._isVisible(element.parentNode);
+  },
+  assertNotVisible: function(element) {
+    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+  },
+  assertVisible: function(element) {
+    this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+  initialize: function(name, test, setup, teardown) {
+    Test.Unit.Assertions.prototype.initialize.bind(this)();
+    this.name           = name;
+    
+    if(typeof test == 'string') {
+      test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
+      test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
+      this.test = function() {
+        eval('with(this){'+test+'}');
+      }
+    } else {
+      this.test = test || function() {};
+    }
+    
+    this.setup          = setup || function() {};
+    this.teardown       = teardown || function() {};
+    this.isWaiting      = false;
+    this.timeToWait     = 1000;
+  },
+  wait: function(time, nextPart) {
+    this.isWaiting = true;
+    this.test = nextPart;
+    this.timeToWait = time;
+  },
+  run: function() {
+    try {
+      try {
+        if (!this.isWaiting) this.setup.bind(this)();
+        this.isWaiting = false;
+        this.test.bind(this)();
+      } finally {
+        if(!this.isWaiting) {
+          this.teardown.bind(this)();
+        }
+      }
+    }
+    catch(e) { this.error(e); }
+  }
+});
+
+// *EXPERIMENTAL* BDD-style testing to please non-technical folk
+// This draws many ideas from RSpec http://rspec.rubyforge.org/
+
+Test.setupBDDExtensionMethods = function(){
+  var METHODMAP = {
+    shouldEqual:     'assertEqual',
+    shouldNotEqual:  'assertNotEqual',
+    shouldEqualEnum: 'assertEnumEqual',
+    shouldBeA:       'assertType',
+    shouldNotBeA:    'assertNotOfType',
+    shouldBeAn:      'assertType',
+    shouldNotBeAn:   'assertNotOfType',
+    shouldBeNull:    'assertNull',
+    shouldNotBeNull: 'assertNotNull',
+    
+    shouldBe:        'assertReturnsTrue',
+    shouldNotBe:     'assertReturnsFalse',
+    shouldRespondTo: 'assertRespondsTo'
+  };
+  var makeAssertion = function(assertion, args, object) { 
+   	this[assertion].apply(this,(args || []).concat([object]));
+  }
+  
+  Test.BDDMethods = {};   
+  $H(METHODMAP).each(function(pair) { 
+    Test.BDDMethods[pair.key] = function() { 
+       var args = $A(arguments); 
+       var scope = args.shift(); 
+       makeAssertion.apply(scope, [pair.value, args, this]); }; 
+  });
+  
+  [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(
+    function(p){ Object.extend(p, Test.BDDMethods) }
+  );
+}
+
+Test.context = function(name, spec, log){
+  Test.setupBDDExtensionMethods();
+  
+  var compiledSpec = {};
+  var titles = {};
+  for(specName in spec) {
+    switch(specName){
+      case "setup":
+      case "teardown":
+        compiledSpec[specName] = spec[specName];
+        break;
+      default:
+        var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
+        var body = spec[specName].toString().split('\n').slice(1);
+        if(/^\{/.test(body[0])) body = body.slice(1);
+        body.pop();
+        body = body.map(function(statement){ 
+          return statement.strip()
+        });
+        compiledSpec[testName] = body.join('\n');
+        titles[testName] = specName;
+    }
+  }
+  new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
+};
Index: /afridex/plugins/Flutter/js/lib/scriptaculous.js
===================================================================
--- /afridex/plugins/Flutter/js/lib/scriptaculous.js (revision 21)
+++ /afridex/plugins/Flutter/js/lib/scriptaculous.js (revision 21)
@@ -0,0 +1,58 @@
+// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Scriptaculous = {
+  Version: '1.8.1',
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
+  },
+  REQUIRED_PROTOTYPE: '1.6.0',
+  load: function() {
+    function convertVersionString(versionString){
+      var r = versionString.split('.');
+      return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
+    }
+ 
+    if((typeof Prototype=='undefined') || 
+       (typeof Element == 'undefined') || 
+       (typeof Element.Methods=='undefined') ||
+       (convertVersionString(Prototype.Version) < 
+        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
+       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
+        Scriptaculous.REQUIRED_PROTOTYPE);
+    
+    $A(document.getElementsByTagName("script")).findAll( function(s) {
+      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+    }).each( function(s) {
+      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+      var includes = s.src.match(/\?.*load=([a-z,]*)/);
+      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
+       function(include) { Scriptaculous.require(path+include+'.js') });
+    });
+  }
+}
+
+Scriptaculous.load();
Index: /afridex/plugins/Flutter/js/lib/dragdrop.js
===================================================================
--- /afridex/plugins/Flutter/js/lib/dragdrop.js (revision 21)
+++ /afridex/plugins/Flutter/js/lib/dragdrop.js (revision 21)
@@ -0,0 +1,974 @@
+// script.aculo.us dragdrop.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if(Object.isUndefined(Effect))
+  throw("dragdrop.js requires including script.aculo.us' effects.js library");
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || { });
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if(Object.isArray(containment)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var drop, affected = [];
+    
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0)
+      drop = Droppables.findDeepestChild(affected);
+
+    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
+    if (drop) {
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      if (drop != this.last_active) Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) {
+        this.last_active.onDrop(element, this.last_active.element, event); 
+        return true; 
+      }
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    if(draggable.options.delay) { 
+      this._timeout = setTimeout(function() { 
+        Draggables._timeout = null; 
+        window.focus(); 
+        Draggables.activeDraggable = draggable; 
+      }.bind(this), draggable.options.delay); 
+    } else {
+      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+      this.activeDraggable = draggable;
+    }
+  },
+  
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(this._timeout) { 
+      clearTimeout(this._timeout); 
+      this._timeout = null; 
+    }
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create({
+  initialize: function(element) {
+    var defaults = {
+      handle: false,
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+          queue: {scope:'_draggable', position:'end'}
+        });
+      },
+      endeffect: function(element) {
+        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
+          queue: {scope:'_draggable', position:'end'},
+          afterFinish: function(){ 
+            Draggable._dragging[element] = false 
+          }
+        }); 
+      },
+      zindex: 1000,
+      revert: false,
+      quiet: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
+      delay: 0
+    };
+    
+    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
+      Object.extend(defaults, {
+        starteffect: function(element) {
+          element._opacity = Element.getOpacity(element);
+          Draggable._dragging[element] = true;
+          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+        }
+      });
+    
+    var options = Object.extend(defaults, arguments[1] || { });
+
+    this.element = $(element);
+    
+    if(options.handle && Object.isString(options.handle))
+      this.handle = this.element.down('.'+options.handle, 0);
+    
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+    
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
+      options.scroll = $(options.scroll);
+      this._isScrollChild = Element.childOf(this.element, options.scroll);
+    }
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
+      Draggable._dragging[this.element]) return;
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if((tag_name = src.tagName.toUpperCase()) && (
+        tag_name=='INPUT' ||
+        tag_name=='SELECT' ||
+        tag_name=='OPTION' ||
+        tag_name=='BUTTON' ||
+        tag_name=='TEXTAREA')) return;
+        
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    if(!this.delta)
+      this.delta = this.currentDelta();
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+      if (!this.element._originallyAbsolute)
+        Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+        
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    
+    if(!this.options.quiet){
+      Position.prepare();
+      Droppables.show(pointer, this.element);
+    }
+    
+    Draggables.notify('onDrag', this, event);
+    
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+        p[1] += this.options.scroll.scrollTop + Position.deltaY;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+    
+    if(this.options.quiet){
+      Position.prepare();
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      Droppables.show(pointer, this.element);
+    }
+
+    if(this.options.ghosting) {
+      if (!this.element._originallyAbsolute)
+        Position.relativize(this.element);
+      delete this.element._originallyAbsolute;
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    var dropped = false; 
+    if(success) { 
+      dropped = Droppables.fire(event, this.element); 
+      if (!dropped) dropped = false; 
+    }
+    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && Object.isFunction(revert)) revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      if (dropped == 0 || revert != 'failure')
+        this.options.reverteffect(this.element,
+          d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+      
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    if(this.options.ghosting) {
+      var r   = Position.realOffset(this.element);
+      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+    }
+    
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(Object.isFunction(this.options.snap)) {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(Object.isArray(this.options.snap)) {
+        p = p.map( function(v, i) {
+          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return (v/this.options.snap).round()*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  startScrolling: function(speed) {
+    if(!(speed[0] || speed[1])) return;
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    if (this._isScrollChild) {
+      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+      if (Draggables._lastScrollPointer[0] < 0)
+        Draggables._lastScrollPointer[0] = 0;
+      if (Draggables._lastScrollPointer[1] < 0)
+        Draggables._lastScrollPointer[1] = 0;
+      this.draw(Draggables._lastScrollPointer);
+    }
+    
+    if(this.options.change) this.options.change(this);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+});
+
+Draggable._dragging = { };
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create({
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+});
+
+var Sortable = {
+  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+  
+  sortables: { },
+  
+  _findRootElement: function(element) {
+    while (element.tagName.toUpperCase() != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    var s = Sortable.options(element);
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      delay:       0,
+      hoverclass:  null,
+      ghosting:    false,
+      quiet:       false, 
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      this.SERIALIZE_RULE,
+      
+      // these take arrays of elements or ids and can be 
+      // used for better initialization performance
+      elements:    false,
+      handles:     false,
+      
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || { });
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      quiet:       options.quiet,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      delay:       options.delay,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+    }
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
+      var handle = options.handles ? $(options.handles[i]) :
+        (options.handle ? $(e).select('.' + options.handle)[0] : e); 
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Sortable._marker.hide();
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = 
+        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+          hide().addClassName('dropmarker').setStyle({position:'absolute'});
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
+      else
+        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
+    
+    Sortable._marker.show();
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: [],
+        position: parent.children.length,
+        container: $(children[i]).down(options.treeTag)
+      }
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child)
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || { });
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: [],
+      container: element,
+      position: 0
+    }
+    
+    return Sortable._tree(element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || { });
+    
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || { });
+    
+    var nodeMap = { };
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || { });
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "[id]=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+}
+
+// Returns true if child is contained within element
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+  if (child.parentNode == element) return true;
+  return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {   
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}
Index: /afridex/plugins/Flutter/js/lib/slider.js
===================================================================
--- /afridex/plugins/Flutter/js/lib/slider.js (revision 21)
+++ /afridex/plugins/Flutter/js/lib/slider.js (revision 21)
@@ -0,0 +1,275 @@
+// script.aculo.us slider.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs 
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if (!Control) var Control = { };
+
+// options:
+//  axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+//  onChange(value)
+//  onSlide(value)
+Control.Slider = Class.create({
+  initialize: function(handle, track, options) {
+    var slider = this;
+    
+    if (Object.isArray(handle)) {
+      this.handles = handle.collect( function(e) { return $(e) });
+    } else {
+      this.handles = [$(handle)];
+    }
+    
+    this.track   = $(track);
+    this.options = options || { };
+
+    this.axis      = this.options.axis || 'horizontal';
+    this.increment = this.options.increment || 1;
+    this.step      = parseInt(this.options.step || '1');
+    this.range     = this.options.range || $R(0,1);
+    
+    this.value     = 0; // assure backwards compat
+    this.values    = this.handles.map( function() { return 0 });
+    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+    this.options.startSpan = $(this.options.startSpan || null);
+    this.options.endSpan   = $(this.options.endSpan || null);
+
+    this.restricted = this.options.restricted || false;
+
+    this.maximum   = this.options.maximum || this.range.end;
+    this.minimum   = this.options.minimum || this.range.start;
+
+    // Will be used to align the handle onto the track, if necessary
+    this.alignX = parseInt(this.options.alignX || '0');
+    this.alignY = parseInt(this.options.alignY || '0');
+    
+    this.trackLength = this.maximumOffset() - this.minimumOffset();
+
+    this.handleLength = this.isVertical() ? 
+      (this.handles[0].offsetHeight != 0 ? 
+        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : 
+      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : 
+        this.handles[0].style.width.replace(/px$/,""));
+
+    this.active   = false;
+    this.dragging = false;
+    this.disabled = false;
+
+    if (this.options.disabled) this.setDisabled();
+
+    // Allowed values array
+    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+    if (this.allowedValues) {
+      this.minimum = this.allowedValues.min();
+      this.maximum = this.allowedValues.max();
+    }
+
+    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+    this.eventMouseMove = this.update.bindAsEventListener(this);
+
+    // Initialize handles in reverse (make sure first handle is active)
+    this.handles.each( function(h,i) {
+      i = slider.handles.length-1-i;
+      slider.setValue(parseFloat(
+        (Object.isArray(slider.options.sliderValue) ? 
+          slider.options.sliderValue[i] : slider.options.sliderValue) || 
+         slider.range.start), i);
+      h.makePositioned().observe("mousedown", slider.eventMouseDown);
+    });
+    
+    this.track.observe("mousedown", this.eventMouseDown);
+    document.observe("mouseup", this.eventMouseUp);
+    document.observe("mousemove", this.eventMouseMove);
+    
+    this.initialized = true;
+  },
+  dispose: function() {
+    var slider = this;    
+    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+    Event.stopObserving(document, "mouseup", this.eventMouseUp);
+    Event.stopObserving(document, "mousemove", this.eventMouseMove);
+    this.handles.each( function(h) {
+      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+    });
+  },
+  setDisabled: function(){
+    this.disabled = true;
+  },
+  setEnabled: function(){
+    this.disabled = false;
+  },  
+  getNearestValue: function(value){
+    if (this.allowedValues){
+      if (value >= this.allowedValues.max()) return(this.allowedValues.max());
+      if (value <= this.allowedValues.min()) return(this.allowedValues.min());
+      
+      var offset = Math.abs(this.allowedValues[0] - value);
+      var newValue = this.allowedValues[0];
+      this.allowedValues.each( function(v) {
+        var currentOffset = Math.abs(v - value);
+        if (currentOffset <= offset){
+          newValue = v;
+          offset = currentOffset;
+        } 
+      });
+      return newValue;
+    }
+    if (value > this.range.end) return this.range.end;
+    if (value < this.range.start) return this.range.start;
+    return value;
+  },
+  setValue: function(sliderValue, handleIdx){
+    if (!this.active) {
+      this.activeHandleIdx = handleIdx || 0;
+      this.activeHandle    = this.handles[this.activeHandleIdx];
+      this.updateStyles();
+    }
+    handleIdx = handleIdx || this.activeHandleIdx || 0;
+    if (this.initialized && this.restricted) {
+      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+        sliderValue = this.values[handleIdx-1];
+      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+        sliderValue = this.values[handleIdx+1];
+    }
+    sliderValue = this.getNearestValue(sliderValue);
+    this.values[handleIdx] = sliderValue;
+    this.value = this.values[0]; // assure backwards compat
+    
+    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
+      this.translateToPx(sliderValue);
+    
+    this.drawSpans();
+    if (!this.dragging || !this.event) this.updateFinished();
+  },
+  setValueBy: function(delta, handleIdx) {
+    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
+      handleIdx || this.activeHandleIdx || 0);
+  },
+  translateToPx: function(value) {
+    return Math.round(
+      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
+      (value - this.range.start)) + "px";
+  },
+  translateToValue: function(offset) {
+    return ((offset/(this.trackLength-this.handleLength) * 
+      (this.range.end-this.range.start)) + this.range.start);
+  },
+  getRange: function(range) {
+    var v = this.values.sortBy(Prototype.K); 
+    range = range || 0;
+    return $R(v[range],v[range+1]);
+  },
+  minimumOffset: function(){
+    return(this.isVertical() ? this.alignY : this.alignX);
+  },
+  maximumOffset: function(){
+    return(this.isVertical() ? 
+      (this.track.offsetHeight != 0 ? this.track.offsetHeight :
+        this.track.style.height.replace(/px$/,"")) - this.alignY : 
+      (this.track.offsetWidth != 0 ? this.track.offsetWidth : 
+        this.track.style.width.replace(/px$/,"")) - this.alignX);
+  },  
+  isVertical:  function(){
+    return (this.axis == 'vertical');
+  },
+  drawSpans: function() {
+    var slider = this;
+    if (this.spans)
+      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+    if (this.options.startSpan)
+      this.setSpan(this.options.startSpan,
+        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+    if (this.options.endSpan)
+      this.setSpan(this.options.endSpan, 
+        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+  },
+  setSpan: function(span, range) {
+    if (this.isVertical()) {
+      span.style.top = this.translateToPx(range.start);
+      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+    } else {
+      span.style.left = this.translateToPx(range.start);
+      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+    }
+  },
+  updateStyles: function() {
+    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+    Element.addClassName(this.activeHandle, 'selected');
+  },
+  startDrag: function(event) {
+    if (Event.isLeftClick(event)) {
+      if (!this.disabled){
+        this.active = true;
+        
+        var handle = Event.element(event);
+        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
+        var track = handle;
+        if (track==this.track) {
+          var offsets  = Position.cumulativeOffset(this.track); 
+          this.event = event;
+          this.setValue(this.translateToValue( 
+           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+          ));
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        } else {
+          // find the handle (prevents issues with Safari)
+          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
+            handle = handle.parentNode;
+            
+          if (this.handles.indexOf(handle)!=-1) {
+            this.activeHandle    = handle;
+            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+            this.updateStyles();
+            
+            var offsets  = Position.cumulativeOffset(this.activeHandle);
+            this.offsetX = (pointer[0] - offsets[0]);
+            this.offsetY = (pointer[1] - offsets[1]);
+          }
+        }
+      }
+      Event.stop(event);
+    }
+  },
+  update: function(event) {
+   if (this.active) {
+      if (!this.dragging) this.dragging = true;
+      this.draw(event);
+      if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+      Event.stop(event);
+   }
+  },
+  draw: function(event) {
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    var offsets = Position.cumulativeOffset(this.track);
+    pointer[0] -= this.offsetX + offsets[0];
+    pointer[1] -= this.offsetY + offsets[1];
+    this.event = event;
+    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+    if (this.initialized && this.options.onSlide)
+      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+  },
+  endDrag: function(event) {
+    if (this.active && this.dragging) {
+      this.finishDrag(event, true);
+      Event.stop(event);
+    }
+    this.active = false;
+    this.dragging = false;
+  },  
+  finishDrag: function(event, success) {
+    this.active = false;
+    this.dragging = false;
+    this.updateFinished();
+  },
+  updateFinished: function() {
+    if (this.initialized && this.options.onChange) 
+      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+    this.event = null;
+  }
+});
Index: /afridex/plugins/Flutter/js/lib/controls.js
===================================================================
--- /afridex/plugins/Flutter/js/lib/controls.js (revision 21)
+++ /afridex/plugins/Flutter/js/lib/controls.js (revision 21)
@@ -0,0 +1,965 @@
+// script.aculo.us controls.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+  throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { }
+Autocompleter.Base = Class.create({
+  baseInitialize: function(element, update, options) {
+    element          = $(element)
+    this.element     = element; 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+    this.oldElementValue = this.element.value;
+
+    if(this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || { };
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+      function(element, update){ 
+        if(!update.style.position || update.style.position=='absolute') {
+          update.style.position = 'absolute';
+          Position.clone(element, update, {
+            setHeight: false, 
+            offsetTop: element.offsetHeight
+          });
+        }
+        Effect.Appear(update,{duration:0.15});
+      };
+    this.options.onHide = this.options.onHide || 
+      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if(typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+    // Force carriage returns as token delimiters anyway
+    if (!this.options.tokens.include('\n'))
+      this.options.tokens.push('\n');
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (Prototype.Browser.IE) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCount-1;
+    this.getEntry(this.index).scrollIntoView(true);
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+    this.getEntry(this.index).scrollIntoView(false);
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = $(selectedElement).select('.' + this.options.select) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var bounds = this.getTokenBounds();
+    if (bounds[0] != -1) {
+      var newValue = this.element.value.substr(0, bounds[0]);
+      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+    } else {
+      this.element.value = value;
+    }
+    this.oldElementValue = this.element.value;
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.down());
+
+      if(this.update.firstChild && this.update.down().childNodes) {
+        this.entryCount = 
+          this.update.down().childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+      this.index = 0;
+      
+      if(this.entryCount==1 && this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    this.tokenBounds = null;
+    if(this.getToken().length>=this.options.minChars) {
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+    this.oldElementValue = this.element.value;
+  },
+
+  getToken: function() {
+    var bounds = this.getTokenBounds();
+    return this.element.value.substring(bounds[0], bounds[1]).strip();
+  },
+
+  getTokenBounds: function() {
+    if (null != this.tokenBounds) return this.tokenBounds;
+    var value = this.element.value;
+    if (value.strip().empty()) return [-1, 0];
+    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+    var offset = (diff == this.oldElementValue.length ? 1 : 0);
+    var prevTokenPos = -1, nextTokenPos = value.length;
+    var tp;
+    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+      if (tp > prevTokenPos) prevTokenPos = tp;
+      tp = value.indexOf(this.options.tokens[index], diff + offset);
+      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+    }
+    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+  }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+  var boundary = Math.min(newS.length, oldS.length);
+  for (var index = 0; index < boundary; ++index)
+    if (newS[index] != oldS[index])
+      return index;
+  return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    this.startIndicator();
+    
+    var entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+    
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || { });
+  }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create({
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = element = $(element);
+    this.prepareOptions();
+    this._controls = { };
+    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+    Object.extend(this.options, options || { });
+    if (!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + '-inplaceeditor';
+      if ($(this.options.formId))
+        this.options.formId = '';
+    }
+    if (this.options.externalControl)
+      this.options.externalControl = $(this.options.externalControl);
+    if (!this.options.externalControl)
+      this.options.externalControlOnly = false;
+    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+    this.element.title = this.options.clickToEditText;
+    this._boundCancelHandler = this.handleFormCancellation.bind(this);
+    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+    this._boundWrapperHandler = this.wrapUp.bind(this);
+    this.registerListeners();
+  },
+  checkForEscapeOrReturn: function(e) {
+    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+    if (Event.KEY_ESC == e.keyCode)
+      this.handleFormCancellation(e);
+    else if (Event.KEY_RETURN == e.keyCode)
+      this.handleFormSubmission(e);
+  },
+  createControl: function(mode, handler, extraClasses) {
+    var control = this.options[mode + 'Control'];
+    var text = this.options[mode + 'Text'];
+    if ('button' == control) {
+      var btn = document.createElement('input');
+      btn.type = 'submit';
+      btn.value = text;
+      btn.className = 'editor_' + mode + '_button';
+      if ('cancel' == mode)
+        btn.onclick = this._boundCancelHandler;
+      this._form.appendChild(btn);
+      this._controls[mode] = btn;
+    } else if ('link' == control) {
+      var link = document.createElement('a');
+      link.href = '#';
+      link.appendChild(document.createTextNode(text));
+      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+      link.className = 'editor_' + mode + '_link';
+      if (extraClasses)
+        link.className += ' ' + extraClasses;
+      this._form.appendChild(link);
+      this._controls[mode] = link;
+    }
+  },
+  createEditField: function() {
+    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+    var fld;
+    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+      fld = document.createElement('input');
+      fld.type = 'text';
+      var size = this.options.size || this.options.cols || 0;
+      if (0 < size) fld.size = size;
+    } else {
+      fld = document.createElement('textarea');
+      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+      fld.cols = this.options.cols || 40;
+    }
+    fld.name = this.options.paramName;
+    fld.value = text; // No HTML breaks conversion anymore
+    fld.className = 'editor_field';
+    if (this.options.submitOnBlur)
+      fld.onblur = this._boundSubmitHandler;
+    this._controls.editor = fld;
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+  createForm: function() {
+    var ipe = this;
+    function addText(mode, condition) {
+      var text = ipe.options['text' + mode + 'Controls'];
+      if (!text || condition === false) return;
+      ipe._form.appendChild(document.createTextNode(text));
+    };
+    this._form = $(document.createElement('form'));
+    this._form.id = this.options.formId;
+    this._form.addClassName(this.options.formClassName);
+    this._form.onsubmit = this._boundSubmitHandler;
+    this.createEditField();
+    if ('textarea' == this._controls.editor.tagName.toLowerCase())
+      this._form.appendChild(document.createElement('br'));
+    if (this.options.onFormCustomization)
+      this.options.onFormCustomization(this, this._form);
+    addText('Before', this.options.okControl || this.options.cancelControl);
+    this.createControl('ok', this._boundSubmitHandler);
+    addText('Between', this.options.okControl && this.options.cancelControl);
+    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+    addText('After', this.options.okControl || this.options.cancelControl);
+  },
+  destroy: function() {
+    if (this._oldInnerHTML)
+      this.element.innerHTML = this._oldInnerHTML;
+    this.leaveEditMode();
+    this.unregisterListeners();
+  },
+  enterEditMode: function(e) {
+    if (this._saving || this._editing) return;
+    this._editing = true;
+    this.triggerCallback('onEnterEditMode');
+    if (this.options.externalControl)
+      this.options.externalControl.hide();
+    this.element.hide();
+    this.createForm();
+    this.element.parentNode.insertBefore(this._form, this.element);
+    if (!this.options.loadTextURL)
+      this.postProcessEditField();
+    if (e) Event.stop(e);
+  },
+  enterHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.addClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onEnterHover');
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  handleAJAXFailure: function(transport) {
+    this.triggerCallback('onFailure', transport);
+    if (this._oldInnerHTML) {
+      this.element.innerHTML = this._oldInnerHTML;
+      this._oldInnerHTML = null;
+    }
+  },
+  handleFormCancellation: function(e) {
+    this.wrapUp();
+    if (e) Event.stop(e);
+  },
+  handleFormSubmission: function(e) {
+    var form = this._form;
+    var value = $F(this._controls.editor);
+    this.prepareSubmission();
+    var params = this.options.callback(form, value) || '';
+    if (Object.isString(params))
+      params = params.toQueryParams();
+    params.editorId = this.element.id;
+    if (this.options.htmlResponse) {
+      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Updater({ success: this.element }, this.url, options);
+    } else {
+      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Request(this.url, options);
+    }
+    if (e) Event.stop(e);
+  },
+  leaveEditMode: function() {
+    this.element.removeClassName(this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+    if (this.options.externalControl)
+      this.options.externalControl.show();
+    this._saving = false;
+    this._editing = false;
+    this._oldInnerHTML = null;
+    this.triggerCallback('onLeaveEditMode');
+  },
+  leaveHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.removeClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onLeaveHover');
+  },
+  loadExternalText: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this._controls.editor.disabled = true;
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._form.removeClassName(this.options.loadingClassName);
+        var text = transport.responseText;
+        if (this.options.stripLoadedTextTags)
+          text = text.stripTags();
+        this._controls.editor.value = text;
+        this._controls.editor.disabled = false;
+        this.postProcessEditField();
+      }.bind(this),
+      onFailure: this._boundFailureHandler
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+  postProcessEditField: function() {
+    var fpc = this.options.fieldPostCreation;
+    if (fpc)
+      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+  },
+  prepareOptions: function() {
+    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+      Object.extend(this.options, defs);
+    }.bind(this));
+  },
+  prepareSubmission: function() {
+    this._saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  registerListeners: function() {
+    this._listeners = { };
+    var listener;
+    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+      listener = this[pair.value].bind(this);
+      this._listeners[pair.key] = listener;
+      if (!this.options.externalControlOnly)
+        this.element.observe(pair.key, listener);
+      if (this.options.externalControl)
+        this.options.externalControl.observe(pair.key, listener);
+    }.bind(this));
+  },
+  removeForm: function() {
+    if (!this._form) return;
+    this._form.remove();
+    this._form = null;
+    this._controls = { };
+  },
+  showSaving: function() {
+    this._oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    this.element.addClassName(this.options.savingClassName);
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+  },
+  triggerCallback: function(cbName, arg) {
+    if ('function' == typeof this.options[cbName]) {
+      this.options[cbName](this, arg);
+    }
+  },
+  unregisterListeners: function() {
+    $H(this._listeners).each(function(pair) {
+      if (!this.options.externalControlOnly)
+        this.element.stopObserving(pair.key, pair.value);
+      if (this.options.externalControl)
+        this.options.externalControl.stopObserving(pair.key, pair.value);
+    }.bind(this));
+  },
+  wrapUp: function(transport) {
+    this.leaveEditMode();
+    // Can't use triggerCallback due to backward compatibility: requires
+    // binding + direct element
+    this._boundComplete(transport, this.element);
+  }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+  dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+  initialize: function($super, element, url, options) {
+    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+    $super(element, url, options);
+  },
+
+  createEditField: function() {
+    var list = document.createElement('select');
+    list.name = this.options.paramName;
+    list.size = 1;
+    this._controls.editor = list;
+    this._collection = this.options.collection || [];
+    if (this.options.loadCollectionURL)
+      this.loadCollection();
+    else
+      this.checkForExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+
+  loadCollection: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this.showLoadingText(this.options.loadingCollectionText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        var js = transport.responseText.strip();
+        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+          throw 'Server returned an invalid collection representation.';
+        this._collection = eval(js);
+        this.checkForExternalText();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadCollectionURL, options);
+  },
+
+  showLoadingText: function(text) {
+    this._controls.editor.disabled = true;
+    var tempOption = this._controls.editor.firstChild;
+    if (!tempOption) {
+      tempOption = document.createElement('option');
+      tempOption.value = '';
+      this._controls.editor.appendChild(tempOption);
+      tempOption.selected = true;
+    }
+    tempOption.update((text || '').stripScripts().stripTags());
+  },
+
+  checkForExternalText: function() {
+    this._text = this.getText();
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    else
+      this.buildOptionList();
+  },
+
+  loadExternalText: function() {
+    this.showLoadingText(this.options.loadingText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._text = transport.responseText.strip();
+        this.buildOptionList();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+
+  buildOptionList: function() {
+    this._form.removeClassName(this.options.loadingClassName);
+    this._collection = this._collection.map(function(entry) {
+      return 2 === entry.length ? entry : [entry, entry].flatten();
+    });
+    var marker = ('value' in this.options) ? this.options.value : this._text;
+    var textFound = this._collection.any(function(entry) {
+      return entry[0] == marker;
+    }.bind(this));
+    this._controls.editor.update('');
+    var option;
+    this._collection.each(function(entry, index) {
+      option = document.createElement('option');
+      option.value = entry[0];
+      option.selected = textFound ? entry[0] == marker : 0 == index;
+      option.appendChild(document.createTextNode(entry[1]));
+      this._controls.editor.appendChild(option);
+    }.bind(this));
+    this._controls.editor.disabled = false;
+    Field.scrollFreeActivate(this._controls.editor);
+  }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only  exists for a while,  in order to  let ****
+//**** users adapt to  the new API.  Read up on the new ****
+//**** API and convert your code to it ASAP!            ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+  if (!options) return;
+  function fallback(name, expr) {
+    if (name in options || expr === undefined) return;
+    options[name] = expr;
+  };
+  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+    options.cancelLink == options.cancelButton == false ? false : undefined)));
+  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+    options.okLink == options.okButton == false ? false : undefined)));
+  fallback('highlightColor', options.highlightcolor);
+  fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+  DefaultOptions: {
+    ajaxOptions: { },
+    autoRows: 3,                                // Use when multi-line w/ rows == 1
+    cancelControl: 'link',                      // 'link'|'button'|false
+    cancelText: 'cancel',
+    clickToEditText: 'Click to edit',
+    externalControl: null,                      // id|elt
+    externalControlOnly: false,
+    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
+    formClassName: 'inplaceeditor-form',
+    formId: null,                               // id|elt
+    highlightColor: '#ffff99',
+    highlightEndColor: '#ffffff',
+    hoverClassName: '',
+    htmlResponse: true,
+    loadingClassName: 'inplaceeditor-loading',
+    loadingText: 'Loading...',
+    okControl: 'button',                        // 'link'|'button'|false
+    okText: 'ok',
+    paramName: 'value',
+    rows: 1,                                    // If 1 and multi-line, uses autoRows
+    savingClassName: 'inplaceeditor-saving',
+    savingText: 'Saving...',
+    size: 0,
+    stripLoadedTextTags: false,
+    submitOnBlur: false,
+    textAfterControls: '',
+    textBeforeControls: '',
+    textBetweenControls: ''
+  },
+  DefaultCallbacks: {
+    callback: function(form) {
+      return Form.serialize(form);
+    },
+    onComplete: function(transport, element) {
+      // For backward compatibility, this one is bound to the IPE, and passes
+      // the element directly.  It was too often customized, so we don't break it.
+      new Effect.Highlight(element, {
+        startcolor: this.options.highlightColor, keepBackgroundImage: true });
+    },
+    onEnterEditMode: null,
+    onEnterHover: function(ipe) {
+      ipe.element.style.backgroundColor = ipe.options.highlightColor;
+      if (ipe._effect)
+        ipe._effect.cancel();
+    },
+    onFailure: function(transport, ipe) {
+      alert('Error communication with the server: ' + transport.responseText.stripTags());
+    },
+    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+    onLeaveEditMode: null,
+    onLeaveHover: function(ipe) {
+      ipe._effect = new Effect.Highlight(ipe.element, {
+        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+        restorecolor: ipe._originalBackground, keepBackgroundImage: true
+      });
+    }
+  },
+  Listeners: {
+    click: 'enterEditMode',
+    keydown: 'checkForEscapeOrReturn',
+    mouseover: 'enterHover',
+    mouseout: 'leaveHover'
+  }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+  loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+});
Index: /afridex/plugins/Flutter/js/ajax-dynamic-list/ajax-dynamic-list.js
===================================================================
--- /afridex/plugins/Flutter/js/ajax-dynamic-list/ajax-dynamic-list.js (revision 21)
+++ /afridex/plugins/Flutter/js/ajax-dynamic-list/ajax-dynamic-list.js (revision 21)
@@ -0,0 +1,285 @@
+	/************************************************************************************************************
+	(C) www.dhtmlgoodies.com, April 2006
+	
+	This is a script from www.dhtmlgoodies.com. You will find this and a lot of other scripts at our website.	
+	
+	Terms of use:
+	You are free to use this script as long as the copyright message is kept intact. However, you may not
+	redistribute, sell or repost it without our permission.
+	
+	Thank you!
+	
+	www.dhtmlgoodies.com
+	Alf Magne Kalleland
+	
+	************************************************************************************************************/	
+
+	var ajaxBox_offsetX = 0;
+	var ajaxBox_offsetY = 0;
+	var ajax_list_externalFile = flutter_path+'ajax-list-field-names.php?panel_id='+panel_id+'&';	// Path to external file
+	var minimumLettersBeforeLookup = 1;	// Number of letters entered before a lookup is performed.
+	var valueSelectedFunction = "copyField()";
+	
+	var ajax_list_objects = new Array();
+	var ajax_list_cachedLists = new Array();
+	var ajax_list_activeInput = false;
+	var ajax_list_activeItem;
+	var ajax_list_optionDivFirstItem = false;
+	var ajax_list_currentLetters = new Array();
+	var ajax_optionDiv = false;
+	var ajax_optionDiv_iframe = false;
+
+	var ajax_list_MSIE = false;
+	if(navigator.userAgent.indexOf('MSIE')>=0 && navigator.userAgent.indexOf('Opera')<0)ajax_list_MSIE=true;
+	
+	var currentListIndex = 0;
+	
+	function ajax_getTopPos(inputObj)
+	{
+		
+	  var returnValue = inputObj.offsetTop;
+	  while((inputObj = inputObj.offsetParent) != null){
+	  	returnValue += inputObj.offsetTop;
+	  }
+	  return returnValue;
+	}
+	function ajax_list_cancelEvent()
+	{
+		return false;
+	}
+	
+	function ajax_getLeftPos(inputObj)
+	{
+	  var returnValue = inputObj.offsetLeft;
+	  while((inputObj = inputObj.offsetParent) != null)returnValue += inputObj.offsetLeft;
+	  
+	  return returnValue;
+	}
+	
+	function ajax_option_setValue(e,inputObj)
+	{
+		if(!inputObj)inputObj=this;
+		var tmpValue = inputObj.innerHTML;
+		if(ajax_list_MSIE)tmpValue = inputObj.innerText;else tmpValue = inputObj.textContent;
+		if(!tmpValue)tmpValue = inputObj.innerHTML;
+		ajax_list_activeInput.value = tmpValue;
+		if(document.getElementById(ajax_list_activeInput.name + '_hidden')){
+			document.getElementById(ajax_list_activeInput.name + '_hidden').value = inputObj.id;
+			// CHANGES specific for Flutter
+			eval(valueSelectedFunction);
+		} 
+		ajax_options_hide();
+	}
+	
+	function ajax_options_hide()
+	{
+		if(ajax_optionDiv)ajax_optionDiv.style.display='none';	
+		if(ajax_optionDiv_iframe)ajax_optionDiv_iframe.style.display='none';
+	}
+
+	function ajax_options_rollOverActiveItem(item,fromKeyBoard)
+	{
+		if(ajax_list_activeItem)ajax_list_activeItem.className='optionDiv';
+		item.className='optionDivSelected';
+		ajax_list_activeItem = item;
+		
+		if(fromKeyBoard){
+			if(ajax_list_activeItem.offsetTop>ajax_optionDiv.offsetHeight){
+				ajax_optionDiv.scrollTop = ajax_list_activeItem.offsetTop - ajax_optionDiv.offsetHeight + ajax_list_activeItem.offsetHeight + 2 ;
+			}
+			if(ajax_list_activeItem.offsetTop<ajax_optionDiv.scrollTop)
+			{
+				ajax_optionDiv.scrollTop = 0;	
+			}
+		}
+	}
+	
+	function ajax_option_list_buildList(letters,paramToExternalFile)
+	{
+		
+		ajax_optionDiv.innerHTML = '';
+		ajax_list_activeItem = false;
+		if(ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()].length<=1){
+			ajax_options_hide();
+			return;			
+		}
+		
+		
+		
+		ajax_list_optionDivFirstItem = false;
+		var optionsAdded = false;
+		for(var no=0;no<ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()].length;no++){
+			if(ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()][no].length==0)continue;
+			optionsAdded = true;
+			var div = document.createElement('DIV');
+			var items = ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()][no].split(/###/gi);
+			
+			if(ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()].length==1 && ajax_list_activeInput.value == items[0]){
+				ajax_options_hide();
+				return;						
+			}
+			
+			
+			div.innerHTML = items[items.length-1];
+			div.id = items[0];
+			div.className='optionDiv';
+			div.onmouseover = function(){ ajax_options_rollOverActiveItem(this,false) }
+			div.onclick = ajax_option_setValue;
+			if(!ajax_list_optionDivFirstItem)ajax_list_optionDivFirstItem = div;
+			ajax_optionDiv.appendChild(div);
+		}	
+		if(optionsAdded){
+			ajax_optionDiv.style.display='block';
+			if(ajax_optionDiv_iframe)ajax_optionDiv_iframe.style.display='';
+			ajax_options_rollOverActiveItem(ajax_list_optionDivFirstItem,true);
+		}
+					
+	}
+	
+	function ajax_option_list_showContent(ajaxIndex,inputObj,paramToExternalFile,whichIndex)
+	{
+		if(whichIndex!=currentListIndex)return;
+		var letters = inputObj.value;
+		var content = ajax_list_objects[ajaxIndex].response;
+		var elements = content.split('|');
+		ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()] = elements;
+		ajax_option_list_buildList(letters,paramToExternalFile);
+		
+	}
+	
+	function ajax_option_resize(inputObj)
+	{
+		ajax_optionDiv.style.top = (ajax_getTopPos(inputObj) + inputObj.offsetHeight + ajaxBox_offsetY) + 'px';
+		ajax_optionDiv.style.left = (ajax_getLeftPos(inputObj) + ajaxBox_offsetX) + 'px';
+		if(ajax_optionDiv_iframe){
+			ajax_optionDiv_iframe.style.left = ajax_optionDiv.style.left;
+			ajax_optionDiv_iframe.style.top = ajax_optionDiv.style.top;			
+		}		
+		
+	}
+	
+	function ajax_showOptions(inputObj,paramToExternalFile,e)
+	{
+		if(e.keyCode==13 || e.keyCode==9)return;
+		if(ajax_list_currentLetters[inputObj.name]==inputObj.value)return;
+		if(!ajax_list_cachedLists[paramToExternalFile])ajax_list_cachedLists[paramToExternalFile] = new Array();
+		ajax_list_currentLetters[inputObj.name] = inputObj.value;
+		if(!ajax_optionDiv){
+			ajax_optionDiv = document.createElement('DIV');
+			ajax_optionDiv.id = 'ajax_listOfOptions';	
+			document.body.appendChild(ajax_optionDiv);
+			
+			if(ajax_list_MSIE){
+				ajax_optionDiv_iframe = document.createElement('IFRAME');
+				ajax_optionDiv_iframe.border='0';
+				ajax_optionDiv_iframe.style.width = ajax_optionDiv.clientWidth + 'px';
+				ajax_optionDiv_iframe.style.height = ajax_optionDiv.clientHeight + 'px';
+				ajax_optionDiv_iframe.id = 'ajax_listOfOptions_iframe';
+				
+				document.body.appendChild(ajax_optionDiv_iframe);
+			}
+			
+			var allInputs = document.getElementsByTagName('INPUT');
+			for(var no=0;no<allInputs.length;no++){
+				if(!allInputs[no].onkeyup)allInputs[no].onfocus = ajax_options_hide;
+			}			
+			var allSelects = document.getElementsByTagName('SELECT');
+			for(var no=0;no<allSelects.length;no++){
+				allSelects[no].onfocus = ajax_options_hide;
+			}
+
+			var oldonkeydown=document.body.onkeydown;
+			if(typeof oldonkeydown!='function'){
+				document.body.onkeydown=ajax_option_keyNavigation;
+			}else{
+				document.body.onkeydown=function(){
+					oldonkeydown();
+				ajax_option_keyNavigation() ;}
+			}
+			var oldonresize=document.body.onresize;
+			if(typeof oldonresize!='function'){
+				document.body.onresize=function() {ajax_option_resize(inputObj); };
+			}else{
+				document.body.onresize=function(){oldonresize();
+				ajax_option_resize(inputObj) ;}
+			}
+				
+		}
+		
+		if(inputObj.value.length<minimumLettersBeforeLookup){
+			ajax_options_hide();
+			return;
+		}
+				
+
+		ajax_optionDiv.style.top = (ajax_getTopPos(inputObj) + inputObj.offsetHeight + ajaxBox_offsetY) + 'px';
+		ajax_optionDiv.style.left = (ajax_getLeftPos(inputObj) + ajaxBox_offsetX) + 'px';
+		if(ajax_optionDiv_iframe){
+			ajax_optionDiv_iframe.style.left = ajax_optionDiv.style.left;
+			ajax_optionDiv_iframe.style.top = ajax_optionDiv.style.top;			
+		}
+		
+		ajax_list_activeInput = inputObj;
+		ajax_optionDiv.onselectstart =  ajax_list_cancelEvent;
+		currentListIndex++;
+		if(ajax_list_cachedLists[paramToExternalFile][inputObj.value.toLowerCase()]){
+			ajax_option_list_buildList(inputObj.value,paramToExternalFile,currentListIndex);			
+		}else{
+			var tmpIndex=currentListIndex/1;
+			ajax_optionDiv.innerHTML = '';
+			var ajaxIndex = ajax_list_objects.length;
+			ajax_list_objects[ajaxIndex] = new sack();
+			var url = ajax_list_externalFile + paramToExternalFile + '=1&letters=' + inputObj.value.replace(" ","+");
+			ajax_list_objects[ajaxIndex].requestFile = url;	// Specifying which file to get
+			ajax_list_objects[ajaxIndex].onCompletion = function(){ ajax_option_list_showContent(ajaxIndex,inputObj,paramToExternalFile,tmpIndex); };	// Specify function that will be executed after file has been found
+			ajax_list_objects[ajaxIndex].runAJAX();		// Execute AJAX function		
+		}
+		
+			
+	}
+	
+	function ajax_option_keyNavigation(e)
+	{
+		if(document.all)e = event;
+		
+		if(!ajax_optionDiv)return;
+		if(ajax_optionDiv.style.display=='none')return;
+		
+		if(e.keyCode==38){	// Up arrow
+			if(!ajax_list_activeItem)return;
+			if(ajax_list_activeItem && !ajax_list_activeItem.previousSibling)return;
+			ajax_options_rollOverActiveItem(ajax_list_activeItem.previousSibling,true);
+		}
+		
+		if(e.keyCode==40){	// Down arrow
+			if(!ajax_list_activeItem){
+				ajax_options_rollOverActiveItem(ajax_list_optionDivFirstItem,true);
+			}else{
+				if(!ajax_list_activeItem.nextSibling)return;
+				ajax_options_rollOverActiveItem(ajax_list_activeItem.nextSibling,true);
+			}
+		}
+		
+		if(e.keyCode==13 || e.keyCode==9){	// Enter key or tab key
+			if(ajax_list_activeItem && ajax_list_activeItem.className=='optionDivSelected')ajax_option_setValue(false,ajax_list_activeItem);
+			if(e.keyCode==13)return false; else return true;
+		}
+		if(e.keyCode==27){	// Escape key
+			ajax_options_hide();			
+		}
+	}
+	
+	
+	document.documentElement.onclick = autoHideList;
+	
+	function autoHideList(e)
+	{
+		if(document.all)e = event;
+		
+		if (e.target) source = e.target;
+			else if (e.srcElement) source = e.srcElement;
+			if (source.nodeType == 3) // defeat Safari bug
+				source = source.parentNode;		
+		if(source.tagName.toLowerCase()!='input' && source.tagName.toLowerCase()!='textarea')ajax_options_hide();
+		
+	}
Index: /afridex/plugins/Flutter/js/ajax-dynamic-list/lgpl.txt
===================================================================
--- /afridex/plugins/Flutter/js/ajax-dynamic-list/lgpl.txt (revision 21)
+++ /afridex/plugins/Flutter/js/ajax-dynamic-list/lgpl.txt (revision 21)
@@ -0,0 +1,505 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+
Index: /afridex/plugins/Flutter/js/ajax-dynamic-list/ajax.js
===================================================================
--- /afridex/plugins/Flutter/js/ajax-dynamic-list/ajax.js (revision 21)
+++ /afridex/plugins/Flutter/js/ajax-dynamic-list/ajax.js (revision 21)
@@ -0,0 +1,193 @@
+/* Simple AJAX Code-Kit (SACK) v1.6.1 */
+/* ©2005 Gregory Wild-Smith */
+/* www.twilightuniverse.com */
+/* Software licenced under a modified X11 licence,
+   see documentation or authors website for more details */
+
+function sack(file) {
+	this.xmlhttp = null;
+
+	this.resetData = function() {
+		this.method = "POST";
+  		this.queryStringSeparator = "?";
+		this.argumentSeparator = "&";
+		this.URLString = "";
+		this.encodeURIString = true;
+  		this.execute = false;
+  		this.element = null;
+		this.elementObj = null;
+		this.requestFile = file;
+		this.vars = new Object();
+		this.responseStatus = new Array(2);
+  	};
+
+	this.resetFunctions = function() {
+  		this.onLoading = function() { };
+  		this.onLoaded = function() { };
+  		this.onInteractive = function() { };
+  		this.onCompletion = function() { };
+  		this.onError = function() { };
+		this.onFail = function() { };
+	};
+
+	this.reset = function() {
+		this.resetFunctions();
+		this.resetData();
+	};
+
+	this.createAJAX = function() {
+		try {
+			this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
+		} catch (e1) {
+			try {
+				this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+			} catch (e2) {
+				this.xmlhttp = null;
+			}
+		}
+
+		if (! this.xmlhttp) {
+			if (typeof XMLHttpRequest != "undefined") {
+				this.xmlhttp = new XMLHttpRequest();
+			} else {
+				this.failed = true;
+			}
+		}
+	};
+
+	this.setVar = function(name, value){
+		this.vars[name] = Array(value, false);
+	};
+
+	this.encVar = function(name, value, returnvars) {
+		if (true == returnvars) {
+			return Array(encodeURIComponent(name), encodeURIComponent(value));
+		} else {
+			this.vars[encodeURIComponent(name)] = Array(encodeURIComponent(value), true);
+		}
+	}
+
+	this.processURLString = function(string, encode) {
+		encoded = encodeURIComponent(this.argumentSeparator);
+		regexp = new RegExp(this.argumentSeparator + "|" + encoded);
+		varArray = string.split(regexp);
+		for (i = 0; i < varArray.length; i++){
+			urlVars = varArray[i].split("=");
+			if (true == encode){
+				this.encVar(urlVars[0], urlVars[1]);
+			} else {
+				this.setVar(urlVars[0], urlVars[1]);
+			}
+		}
+	}
+
+	this.createURLString = function(urlstring) {
+		if (this.encodeURIString && this.URLString.length) {
+			this.processURLString(this.URLString, true);
+		}
+
+		if (urlstring) {
+			if (this.URLString.length) {
+				this.URLString += this.argumentSeparator + urlstring;
+			} else {
+				this.URLString = urlstring;
+			}
+		}
+
+		// prevents caching of URLString
+		this.setVar("rndval", new Date().getTime());
+
+		urlstringtemp = new Array();
+		for (key in this.vars) {
+			if (false == this.vars[key][1] && true == this.encodeURIString) {
+				encoded = this.encVar(key, this.vars[key][0], true);
+				delete this.vars[key];
+				this.vars[encoded[0]] = Array(encoded[1], true);
+				key = encoded[0];
+			}
+
+			urlstringtemp[urlstringtemp.length] = key + "=" + this.vars[key][0];
+		}
+		if (urlstring){
+			this.URLString += this.argumentSeparator + urlstringtemp.join(this.argumentSeparator);
+		} else {
+			this.URLString += urlstringtemp.join(this.argumentSeparator);
+		}
+	}
+
+	this.runResponse = function() {
+		eval(this.response);
+	}
+
+	this.runAJAX = function(urlstring) {
+		if (this.failed) {
+			this.onFail();
+		} else {
+			this.createURLString(urlstring);
+			if (this.element) {
+				this.elementObj = document.getElementById(this.element);
+			}
+			if (this.xmlhttp) {
+				var self = this;
+				if (this.method == "GET") {
+					totalurlstring = this.requestFile + this.queryStringSeparator + this.URLString;
+					this.xmlhttp.open(this.method, totalurlstring, true);
+				} else {
+					this.xmlhttp.open(this.method, this.requestFile, true);
+					try {
+						this.xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
+					} catch (e) { }
+				}
+
+				this.xmlhttp.onreadystatechange = function() {
+					switch (self.xmlhttp.readyState) {
+						case 1:
+							self.onLoading();
+							break;
+						case 2:
+							self.onLoaded();
+							break;
+						case 3:
+							self.onInteractive();
+							break;
+						case 4:
+							self.response = self.xmlhttp.responseText;
+							self.responseXML = self.xmlhttp.responseXML;
+							self.responseStatus[0] = self.xmlhttp.status;
+							self.responseStatus[1] = self.xmlhttp.statusText;
+
+							if (self.execute) {
+								self.runResponse();
+							}
+
+							if (self.elementObj) {
+								elemNodeName = self.elementObj.nodeName;
+								elemNodeName.toLowerCase();
+								if (elemNodeName == "input"
+								|| elemNodeName == "select"
+								|| elemNodeName == "option"
+								|| elemNodeName == "textarea") {
+									self.elementObj.value = self.response;
+								} else {
+									self.elementObj.innerHTML = self.response;
+								}
+							}
+							if (self.responseStatus[0] == "200") {
+								self.onCompletion();
+							} else {
+								self.onError();
+							}
+
+							self.URLString = "";
+							break;
+					}
+				};
+
+				this.xmlhttp.send(this.URLString);
+			}
+		}
+	};
+
+	this.reset();
+	this.createAJAX();
+}
Index: /afridex/plugins/Flutter/RCCWP_OptionsPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_OptionsPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_OptionsPage.php (revision 21)
@@ -0,0 +1,235 @@
+<?php
+include_once('RCCWP_Options.php');
+
+class RCCWP_OptionsPage
+{
+
+function Main()
+{
+	$customWritePanels = RCCWP_CustomWritePanel::GetCustomWritePanels();
+	$customWritePanelOptions = RCCWP_Options::Get();
+
+	if (function_exists('is_site_admin') && !is_site_admin())
+		update_option("Flutter_notTopAdmin", true);
+	else
+		update_option("Flutter_notTopAdmin", false);
+
+	?>
+	
+	<div class="wrap">
+
+	<h2>Flutter Options</h2>
+	
+	<form action="" method="post" id="custom-write-panel-options-form">	
+	
+
+	<h3>Write Panel Options</h3>
+	<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6"> 
+
+	<tr valign="top">
+		<th scope="row">Hide Post Panel</th>
+        	<td>
+			<label for="hide-write-post"> 
+			<input name="hide-write-post" id="hide-write-post" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['hide-write-post'])?> type="checkbox">
+			&nbsp; Hide Wordpress Post panel</label> 
+		</td>
+        </tr>
+ 
+    	<tr valign="top">
+		<th scope="row">Hide Page Panel</th>
+		<td>
+			<label for="hide-write-page"> 
+			<input name="hide-write-page" id="hide-write-page" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['hide-write-page'])?> type="checkbox">
+			&nbsp; Hide Wordpress Page panel</label> 
+ 		</td>
+        </tr>
+
+    	<tr valign="top">
+		<th scope="row">Editing Prompt</th>
+		<td>
+			<label for="prompt-editing-post"> 
+			<input name="prompt-editing-post" id="prompt-editing-post" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['prompt-editing-post'])?> type="checkbox"> 
+			&nbsp; Prompt when editing a Post not created with Custom Write Panel.</label> 
+		</td>
+        </tr>
+
+    	<tr valign="top">
+		<th scope="row">Assign to Role</th>
+        	<td>
+			<label for="assign-to-role"> 
+			<input name="assign-to-role" id="assign-to-role" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['assign-to-role'])?> type="checkbox"> 
+			&nbsp; This option will create a capability for each write panel such that the write panel is accessible by the Administrator only by default.
+			 You can assign the write panel to other roles using </label><a target="_blank" href="http://sourceforge.net/projects/role-manager">Role Manager Plugin</a>. 
+		</td>
+        </tr>
+
+    	<tr valign="top">
+		<th scope="row">Default Panel</th>
+		<td>
+		
+			<label for="default-custom-write-panel">
+			<select name="default-custom-write-panel" id="default-custom-write-panel">
+				<option value="">(None)</option>
+			<?php
+				$defaultCustomWritePanel = $customWritePanelOptions['default-custom-write-panel'];
+				foreach ($customWritePanels as $panel) :
+					$selected = $panel->id == $defaultCustomWritePanel ? 'selected="selected"' : '';
+			?>
+				<option value="<?php echo $panel->id?>" <?php echo $selected?>><?php echo $panel->name?></option>
+			<?php
+				endforeach;
+			?>
+			</select>
+			</label>
+		
+		</td>
+        </tr>
+
+	</table>
+
+
+	<br />
+	<h3>Layout Options</h3>
+	<p>Allows you to add modules to the blog.</p>
+	<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6"> 
+
+	<tr valign="top">
+		<th scope="row">Layout Tab</th>
+		<td>
+			<label for="canvas_show"> 
+			<input name="canvas_show" id="canvas_show" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['canvas_show'])?> type="checkbox"> 
+			&nbsp; Show Layout tab.</label> 
+		</td>
+        </tr>
+
+
+	<tr valign="top">
+		<th scope="row">Style Tab</th>
+		<td>
+			<label for="ink_show"> 
+			<input name="ink_show" id="ink_show" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['ink_show'])?> type="checkbox"> 
+			&nbsp; Show Style tab.</label> 
+		</td>
+        </tr>
+
+
+	<tr valign="top">
+		<th scope="row">Layout Instructions</th>
+		<td>
+			<label for="canvas_show_instructions"> 
+			<input name="canvas_show_instructions" id="canvas_show_instructions" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['canvas_show_instructions'])?> type="checkbox"> 
+			&nbsp; Display the instructions on the Layout page.</label> 
+		</td>
+        </tr>
+
+
+	<tr valign="top">
+		<th scope="row">Zones Names</th>
+		<td>
+			<label for="canvas_show_zone_name"> 
+			<input name="canvas_show_zone_name" id="canvas_show_zone_name" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['canvas_show_zone_name'])?> type="checkbox"> 
+			&nbsp; Show zones names on droppable zones.</label> 
+		</td>
+        </tr>
+
+	</table>
+
+	<?php if (version_compare(PHP_VERSION, '5.0.0') === 1){ ?>
+	<br />
+	<h3>HTML Purifier</h3>
+	<p>Ensures the post is valid XHTML. (php5 only)</p>
+	<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6"> 
+
+	<tr valign="top">
+		<th scope="row">HTML Purifier</th>
+		<td>
+			<label for="enable-HTMLPurifier"> 
+			<input name="enable-HTMLPurifier" id="enable-HTMLPurifier" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['enable-HTMLPurifier'])?> type="checkbox"> 
+			&nbsp; Enable HTML Purifier.</label> 
+		</td>
+        </tr>
+
+	<tr valign="top">
+		<th scope="row">Tidy level</th>
+		<td>
+		<select name="tidy-level" id="tidy-level">		
+			<option <?php if ('none' == $customWritePanelOptions['tidy-level']) echo "selected"; ?> value="none">none</option>
+			<option <?php if ('light' == $customWritePanelOptions['tidy-level']) echo "selected"; ?> value="light">light</option>
+			<option <?php if ('medium' == $customWritePanelOptions['tidy-level']) echo "selected"; ?> value="medium">medium</option>
+			<option <?php if ('heavy' == $customWritePanelOptions['tidy-level']) echo "selected"; ?> value="heavy">heavy</option>
+		</select>
+		</td>
+        </tr>
+	
+	</table>
+	<?php } ?>
+
+	<br />
+	<h3>Other Options</h3>
+	<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+
+    	<tr valign="top">
+		<th scope="row">Snipshot</th>
+		<td>
+			<label for="use-snipshot"> 
+			<input name="use-snipshot" id="use-snipshot" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['use-snipshot'])?> type="checkbox"> 
+			&nbsp; Use Snipshot services instead of cropper to edit photos.</label> 
+		</td>
+        </tr>
+
+	<tr valign="top">
+		<th scope="row">Edit-n-place</th>
+		<td>
+			<label for="enable-editnplace"> 
+			<input name="enable-editnplace" id="enable-editnplace" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['enable-editnplace'])?> type="checkbox"> 
+			&nbsp; Edit posts instantly from the post page.</label> 
+		</td>
+        </tr>
+
+	<tr>
+		<th scope="row">Use Flash Uploader</th>
+		<td>
+			<label for="enable-swfupload">
+			<input name="enable-swfupload" id="enable-swfupload" value="1" <?php echo RCCWP_OptionsPage::GetCheckboxState($customWritePanelOptions['enable-swfupload']) ?> type="checkbox">
+			&nbsp; Use Flash Uploader instead of Browser upload. </label>
+		</td>
+	</tr>
+
+	</table>
+
+	<br />	
+	<h3>Uninstall Flutter</h3>
+	<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6"> 
+
+    	<tr valign="top">
+		<th scope="row">Uninstall Flutter</th>
+		<td>
+			<input type="text" id="uninstall-custom-write-panel" name="uninstall-custom-write-panel" size="25" /><br />
+			<label for="uninstall-custom-write-panel">
+			&nbsp; Type <strong>uninstall</strong> into the textbox, click <strong>Update Options</strong>, and all the tables created by this plugin will be deleted</label>
+		
+		</td>
+        </tr>
+
+	</table>
+
+	<p class="submit" ><input name="update-custom-write-panel-options" type="submit" value="Update Options" /></p>
+	
+	</form>
+
+	</div>
+	
+	<?php
+}
+
+function GetCheckboxState($optionValue)
+{
+	if ($optionValue == '' || $optionValue == 0)
+		return '';
+	else 
+		return 'checked="checked"';
+}
+
+}
+
+?>
Index: /afridex/plugins/Flutter/RCCWP_Constant.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_Constant.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_Constant.php (revision 21)
@@ -0,0 +1,109 @@
+<?php
+global $wpdb;
+
+// General Constants
+define('RC_CWP_DB_VERSION', 26);
+define('RC_CWP_POST_WRITE_PANEL_ID_META_KEY', '_rc_cwp_write_panel_id');
+define('RC_CWP_OPTION_KEY', 'rc_custom_write_panel');
+
+
+// Flutter paths
+preg_match('/wp-content(.*)(RCCWP_Constant\.php)$/',__FILE__,$flutterpath);
+$flutterpath = str_replace('\\', '/', $flutterpath);
+define('FLUTTER_PLUGIN_DIR', dirname(plugin_basename(__FILE__))); // returns Flutter
+//define("FLUTTER_PATH", str_replace('/RCCWP_Constant.php', '', str_replace('\\', '/', __FILE__)));
+define("FLUTTER_PATH", dirname(__FILE__));
+define("FLUTTER_URI", get_bloginfo('wpurl').'/wp-content'.$flutterpath[1]); //returns somthing similar to "http://127.0.0.1/wp-content/plugins/Flutter/"
+define("FLUTTER_URI_RELATIVE", 'wp-content'.$flutterpath[1]); //returns somthing similar to "wp-content/plugins/Flutter/"
+
+
+
+// -- Tables names
+
+// Tables containing somehow constant data
+define('RC_CWP_TABLE_CUSTOM_FIELD_TYPES', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_custom_field_types');
+define('RC_CWP_TABLE_STANDARD_FIELDS', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_standard_fields');
+
+// Panels - Groups - Fields
+define('RC_CWP_TABLE_PANELS', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_write_panels');
+define('RC_CWP_TABLE_PANEL_GROUPS', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_module_groups');
+define('RC_CWP_TABLE_GROUP_FIELDS', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_panel_custom_field');
+
+// Extra information about panels
+define('RC_CWP_TABLE_PANEL_CATEGORY', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_panel_category');
+define('RC_CWP_TABLE_PANEL_STANDARD_FIELD', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_panel_standard_field');
+define('RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_panel_hidden_external_field');
+
+// Extra information about fields
+define('RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_custom_field_options');
+define('RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_custom_field_properties');
+
+// Modules
+define('RC_CWP_TABLE_MODULES', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_modules');
+
+// Extra information about post meta values.
+define('RC_CWP_TABLE_POST_META', $wpdb->prefix . 'rc_cwp_post_meta');
+
+//define('RC_CWP_TABLE_PANEL_MODULES', (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix)  . 'rc_cwp_panel_modules');
+
+// Field Types
+global $FIELD_TYPES;
+$FIELD_TYPES = array(
+					"textbox" => 1,
+					"multiline_textbox" => 2,
+					"checkbox" => 3,
+					"checkbox_list" => 4,
+					"radiobutton_list" => 5,
+					"dropdown_list" => 6,
+					"listbox" => 7,
+					"file" => 8,
+					"image" => 9,
+					"date" => 10,
+					"audio" => 11
+					);
+
+// Field Types
+global $STANDARD_FIELDS;
+$STANDARD_FIELDS = array();
+
+// Standard fields
+$STANDARD_FIELDS[12] = new FlutterPanelFields(12, 'Post/Page', array('postdivrich'), true, false, true, true, 1000);
+$STANDARD_FIELDS[2] = new FlutterPanelFields(2, 'Categories', array('categorydiv'), false, false, true, false, 1000);
+$STANDARD_FIELDS[14] = new FlutterPanelFields(14, 'Tags', array('tagsdiv'), true, false, true, false, 1000);
+
+// Common advanced fields
+$STANDARD_FIELDS[11] = new FlutterPanelFields(11, 'Custom Fields', array('postcustom', 'pagepostcustom', 'pagecustomdiv'), true, true, true, true, 1000);
+$STANDARD_FIELDS[3] = new FlutterPanelFields(3, 'Comments & Pings', array('commentstatusdiv', 'pagecommentstatusdiv'), true, true, true, true, 1000);
+$STANDARD_FIELDS[4] = new FlutterPanelFields(4, 'Password', array('passworddiv', 'pagepassworddiv'), true, true, true, true, 1000);
+$STANDARD_FIELDS[18] = new FlutterPanelFields(4, 'Post/Page Author', array('authordiv', 'pageauthordiv'), true, true, true, true, 1000);
+
+// Post-specific advanced fields
+$STANDARD_FIELDS[9] = new FlutterPanelFields(9, 'Excerpt', array('postexcerpt'), true, true, true, false, 1000);
+$STANDARD_FIELDS[10] = new FlutterPanelFields(10, 'Trackbacks', array('trackbacksdiv'), true, true, true, false, 1000);
+$STANDARD_FIELDS[5] = new FlutterPanelFields(5, 'Post Slug', array('slugdiv'), true, true, true, false, 1000);
+
+// Page-specific advanced fields
+$STANDARD_FIELDS[15] = new FlutterPanelFields(15, 'Page Parent', array('pageparentdiv'), true, true, false, true, 1000);
+$STANDARD_FIELDS[16] = new FlutterPanelFields(16, 'Page Template', array('pagetemplatediv'), true, true, false, true, 1000);
+$STANDARD_FIELDS[17] = new FlutterPanelFields(17, 'Page Order', array('pageorderdiv'), true, true, false, true, 1000);										
+
+// Important folders
+define('FLUTTER_UPLOAD_FILES_DIR', dirname(__FILE__)."/files_flutter/");
+define('FLUTTER_IMAGES_CACHE_DIR', dirname(__FILE__)."/cache/");
+define('FLUTTER_MODULES_DIR', dirname(__FILE__)."/modules/");
+define('FLUTTER_PURIFIER_CACHE_DIR', dirname(__FILE__)."/purifier_lib/HTMLPurifier/DefinitionCache/Serializer/HTML/");
+
+// Capabilities names
+define('FLUTTER_CAPABILITY_PANELS', "Create Flutter Panels");
+define('FLUTTER_CAPABILITY_MODULES', "Create Flutter Modules");
+define('FLUTTER_CAPABILITY_LAYOUT', "Change Flutter Layout");
+define('FLUTTER_CAPABILITY_STYLE', "Change Flutter Style");
+
+if (!defined('DIRECTORY_SEPARATOR'))
+{
+	if (strpos(php_uname('s'), 'Win') !== false )
+		define('DIRECTORY_SEPARATOR', '\\');
+	else 
+		define('DIRECTORY_SEPARATOR', '/');
+}
+?>
Index: /afridex/plugins/Flutter/Ink/ink.php
===================================================================
--- /afridex/plugins/Flutter/Ink/ink.php (revision 21)
+++ /afridex/plugins/Flutter/Ink/ink.php (revision 21)
@@ -0,0 +1,95 @@
+<?php
+
+// CSS Functions
+
+function ink_elements($element_string) {
+		global $wpdb, $table_prefix;
+		$table = $table_prefix.'ink';
+		$element_string = chop(preg_replace('/,\s+/', '^^^', 'body, a:link, a:hover, a:visited, '.$element_string));
+		$elements = explode('^^^', $element_string);
+		$theme = get_option('template');
+	
+		foreach($elements as $element) {
+			if(!$wpdb->get_var("SELECT element FROM ".$table." WHERE element = '$element' AND theme = '$theme'"))
+				$wpdb->query("INSERT INTO ".$table." (element, theme) VALUES ('$element','$theme')");
+		}
+		if(!get_option('ink_css')) write_ink_definitions();
+}
+
+function ink_create_css($element, $index, $definition) {
+	$css_element = $element.' {'."\n";
+		if($index == 'color') $css_element .= "\t".'color: #'.$definition.';'."\n";
+		if($index == 'background') $css_element .= "\t".'background-color: #'.$definition.';'."\n";
+		if($index == 'border') $css_element .= "\t".'border-color: #'.$definition.';'."\n";
+		if($index == 'font_family' && $definition != 'none') $css_element .= "\t".'font-family: '.$definition.';'."\n";
+		if($index == 'font_size') $css_element .= "\t".'font-size: '.$definition.'px;'."\n";
+		if($index == 'font_style' && strstr($definition, 'normal') && $definition != 'none') $css_element .= "\t".'font-style: normal;'."\n"."\t".'font-weight: normal;'."\n";
+		if($index == 'font_style' && strstr($definition, 'italic') && $definition != 'none') $css_element .= "\t".'font-style: italic;'."\n";
+		if($index == 'font_style' && strstr($definition, 'bold') && $definition != 'none') $css_element .= "\t".'font-weight: bold;'."\n";
+		if($index == 'other') $css_element .= "\t".$definition."\n";
+	$css_element .= '}'."\n\n";
+	
+	return $css_element;
+}
+
+function write_ink_definitions() {
+	global $wpdb, $table_prefix;
+	$table = $table_prefix.'ink';
+	$theme = get_option('template');
+	$css = '/* CSS Generated by Ink - http://freshpursuits.com/ink */'."\n\n";
+	if($values = $wpdb->get_results("SELECT * FROM ".$table." WHERE theme = '$theme'")) {
+		foreach($values as $value) {
+			$definitions = php4_clone($value);
+			unset($definitions->element, $definitions->element_id, $definitions->theme);
+			foreach($definitions as $index => $definition) {
+				if(!empty($definition)) $css .= ink_create_css($value->element, $index, $definition);
+			}
+		}
+		update_option('ink_css', '<style type="text/css" media="screen">'."\n".$css.'</style>');
+	}
+}
+
+function php4_clone($object) {
+  if (version_compare(phpversion(), '5.0') < 0) {
+   return $object;
+  } else {
+   return @clone($object);
+  }
+ }
+
+function output_ink() {
+	if(get_option('enableInk') == 'true') echo get_option('ink_css');
+	}
+
+// Admin Functions
+
+function get_ink_definition($element, $definition) {
+	global $wpdb, $table_prefix;
+	$table = $table_prefix.'ink';
+	$theme = get_option('template');
+	$value = $wpdb->get_var("SELECT ".$definition." FROM ".$table." WHERE element = '$element' AND theme = '$theme'");
+	return $value;
+}
+
+function set_ink_definition($element, $definition, $value) {
+	global $wpdb, $table_prefix;
+	$table = $table_prefix.'ink';
+	$theme = get_option('template');
+	$update = $wpdb->query("UPDATE ".$table." SET ".$definition."='$value' WHERE element = '$element' AND theme = '$theme'");
+	write_ink_definitions();
+	return $update;
+}
+
+function ink_clean_install() {
+	update_option('enableInk', 'false');
+	delete_option('ink_css');
+}
+
+function ink_highlight() {
+	if(isset($_GET["ink_highlight"])) {
+		echo '<style type="text/css" media="screen">'."\n".urldecode($_GET["ink_highlight"]).' { border: 1px solid red; } </style>';
+	}
+}
+
+require_once('ink-admin.php');
+?>
Index: /afridex/plugins/Flutter/Ink/ink-ajax.php
===================================================================
--- /afridex/plugins/Flutter/Ink/ink-ajax.php (revision 21)
+++ /afridex/plugins/Flutter/Ink/ink-ajax.php (revision 21)
@@ -0,0 +1,22 @@
+<?php
+
+if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+	if(isset($_GET["element"]) && isset($_GET["definition"]) && isset($_GET["value"])) {
+		$wpdb->query("UPDATE ".$table_prefix.'ink'." SET ".urldecode($_GET["definition"])." = '".urldecode($_GET["value"])."' WHERE element = '".urldecode($_GET["element"])."' AND theme = '".get_option('template')."'");
+		write_ink_definitions();
+	}
+	
+	if(isset($_GET["option"]) && isset($_GET["value"])) {
+		update_option($_GET["option"], $_GET["value"]);
+	}
+	
+	if(isset($_GET["restore"]) && $_GET["restore"] == 'true') {
+		ink_clean_install();
+		canvas_check_theme();
+	}
+}
+?>
Index: /afridex/plugins/Flutter/Ink/ink.css
===================================================================
--- /afridex/plugins/Flutter/Ink/ink.css (revision 21)
+++ /afridex/plugins/Flutter/Ink/ink.css (revision 21)
@@ -0,0 +1,106 @@
+#inkadmin, #inkadmin input, #inkadmin select {
+	font-size: 10px;
+	font-family: 'Arial';
+	}
+
+#inkadmin .content_wrap {
+	background: #FFF url('../images/color_gradient.jpg') top center no-repeat;
+	padding-top: 15px;
+	margin-top: 10px;
+	}
+
+#inkadmin .wrap {
+	border: none;
+	overflow: visible;
+	}
+
+#inkadmin .wrap p {
+	font-weight: bold;
+	}
+
+#inkadmin input.color {
+	border: 0;
+	background: #FFF url('../images/colorswatch.png') top left no-repeat;
+	width: 90px;
+	height: 15px;
+	padding: 5px 0 0 10px;
+	}
+
+#inkadmin form label {
+	font-weight: bold;
+	}
+
+.ink_element {
+	margin-top: 20px;
+	padding: 0 10px 0px 10px;
+	border-top: 1px solid #CCC;
+	}
+
+.ink_element h3 a {
+	font-size: 8px;
+	position: absolute;
+	margin-top: -2px;
+	color: #777;
+	}
+
+.ink_element select {
+	margin-top: 10px;
+	}
+
+#red_bar, #green_bar, #blue_bar {
+	width: 200px;
+	height: 10px;
+	clear: left;
+	margin: 0px 0px 9px 5px;
+	}
+
+.colorfields, .colorbox {
+	float: left;
+	color: #111;
+	font-size: 9px;
+	padding: 10px;
+	}
+
+.colorfields {
+	clear: left;
+	}
+
+.colorbox input {
+	display: inline;
+	border: 0;
+	background: none;
+	color: inherit;
+	padding: 1px;
+	font-size: inherit;
+	margin-bottom: 2px;
+	font-weight: bold;
+	}
+
+.colorbox input:focus {
+	color: #CCC;
+	background: #222;
+	}
+
+#red_bar {
+	background: url('../images/colorbar_red.png') top left no-repeat;
+	}
+
+#green_bar {
+	background: url('../images/colorbar_green.png') top left no-repeat;
+	}
+
+#blue_bar {
+	background: url('../images/colorbar_blue.png') top left no-repeat;
+	}
+
+.slider, #red_bad, #green_bar, #blue_bar {
+	cursor: move;
+	}
+
+#colorfield {
+	background: #000 url('../images/colorfield.png') top left no-repeat;
+	width: 59px;
+	height: 44px;
+	float: left;
+	margin-right: 5px;
+	}
Index: /afridex/plugins/Flutter/Ink/ink.js
===================================================================
--- /afridex/plugins/Flutter/Ink/ink.js (revision 21)
+++ /afridex/plugins/Flutter/Ink/ink.js (revision 21)
@@ -0,0 +1,134 @@
+Event.observe(window, 'load', watchInkFields, false);
+Event.observe(window, 'unload', Event.unloadCache, false);
+
+function watchInkFields() {
+	if($('enableInk').checked == false) { toggleForm('ink'); }
+	if($('restore')) { Event.observe('restore', 'click', restoreInk, false); }
+	updateInkColors();
+	Event.observe('enableInk', 'click', function(){ toggleForm('ink'); }, false);
+	new Form.Element.EventObserver('red', function(){ saveInkVariables($F('activeField')); ink_rgb2hex(); setSliders($F($F('activeField'))) });
+	new Form.Element.EventObserver('green', function(){ saveInkVariables($F('activeField')); ink_rgb2hex(); setSliders($F($F('activeField'))) });
+	new Form.Element.EventObserver('blue', function(){ saveInkVariables($F('activeField')); ink_rgb2hex(); setSliders($F($F('activeField'))) });
+	Form.getElements('ink').each(function(item){
+		new Form.Element.EventObserver(item, function(item){ saveInkVariables(item.id); updateInkColor(item); });
+		Event.observe(item.id, 'keyup', function(){ saveInkVariables(item.id), updateInkColor(item.id); }, false)
+	});
+	document.getElementsByClassName('color').each(function(item){
+		Event.observe(item.id, 'click', function(){ toggleColorSwatch(item.id); }, false);
+	});
+	redSlider = new Control.Slider('red_slider','red_bar', {axis: 'horizontal', onSlide:function(v){$('red').value = (v*255).toFixed(); ink_rgb2hex();}, onChange:function(){ if($F('activeField') != '') saveInkVariables($F('activeField')); }} );
+	greenSlider = new Control.Slider('green_slider','green_bar', {axis: 'horizontal', onSlide:function(v){$('green').value = (v*255).toFixed(); ink_rgb2hex();}, onChange:function(){ if($F('activeField') != '') saveInkVariables($F('activeField')); }} );
+	blueSlider = new Control.Slider('blue_slider','blue_bar', {axis: 'horizontal', onSlide:function(v){$('blue').value = (v*255).toFixed(); ink_rgb2hex();}, onChange:function(){ if($F('activeField') != '') saveInkVariables($F('activeField')); }} );
+}
+
+function toggleForm(form) {
+	if($('activeField').disabled == false) {
+		$(form).style.color = '#777777';
+		Form.disable(form);
+		updateInkOption('enableInk', 'false');
+	} else {
+		updateInkOption('enableInk', 'true');
+		$(form).style.color = '#000000';
+		Form.enable(form);
+	}
+}
+
+function toggleColorSwatch(element) {
+	if($F('activeField') != '') { $($F('activeField')).style.backgroundImage = 'url(JS_CANVASURI + "images/colorswatch.png")';
+		if($F($F('activeField')) == '') { $($F('activeField')).style.backgroundColor = ''; }
+	}
+	$('activeField').value = element;
+	$(element).style.backgroundImage = 'url(JS_CANVASURI + "images/colorswatch-active.png")';
+	if($F($F('activeField')) != '') { setSliders($F($F('activeField'))); }
+		else { resetSliders(); }
+}
+
+function setSliders(value) {
+	ink_hex2rgb(value);
+	redSlider.setValue($F('red')/255);
+	greenSlider.setValue($F('green')/255);
+	blueSlider.setValue($F('blue')/255);
+	ink_rgb2hex();
+}
+
+function resetSliders() {
+	$('red').value = '0';
+	$('green').value = '0';
+	$('blue').value = '0';
+	redSlider.setValue(0);
+	greenSlider.setValue(0);
+	blueSlider.setValue(0);
+	$('colorfield').style.backgroundColor = '#000000';
+}
+
+function saveInkVariables(element) {
+	if($F(element).length == 6 || $F(element) == '') {
+		var value = escape($F(element));
+		var string = element.split('__');
+		var pars = 'element='+string[0]+'&definition='+string[1]+'&value='+value;
+		var url = JS_CANVASURI + 'Ink/ink-ajax.php';
+		var myAjax = new Ajax.Request(url, 
+			{method: 'get', 
+			parameters: pars} 
+			);
+	}
+}
+
+function updateInkColors() {
+	document.getElementsByClassName('color').each(function(item){
+		if($F(item) != '') $(item).style.backgroundColor = '#'+$F(item);
+	});
+}
+
+function updateInkColor(item) {
+	if($F(item) != '' && ($F(item).length == 6)) { 
+		$(item).style.backgroundColor = '#'+$F(item);
+		setSliders($F(item));
+	}
+}
+
+function ink_rgb2hex() {
+	var hexindex = "0123456789ABCDEF";
+	var R = $F('red');
+	var G = $F('green');
+	var B = $F('blue');
+	R = hexindex.charAt((R - R % 16) / 16) + hexindex.charAt(R % 16);
+	G = hexindex.charAt((G - G % 16) / 16) + hexindex.charAt(G % 16);
+	B = hexindex.charAt((B - B % 16) / 16) + hexindex.charAt(B % 16);
+	$('colorfield').style.backgroundColor = '#'+R+G+B;
+	if($F('activeField') != '') {
+		$($F('activeField')).style.backgroundColor = '#'+R+G+B;
+		$($F('activeField')).value = R+G+B;
+	}
+}
+
+function ink_hex2rgb(hex) {
+    var hexindex = "0123456789ABCDEF";
+	var R = hexindex.indexOf(hex.charAt(0).toUpperCase()) * 16 + hexindex.indexOf(hex.charAt(1).toUpperCase());
+	var G = hexindex.indexOf(hex.charAt(2).toUpperCase()) * 16 + hexindex.indexOf(hex.charAt(3).toUpperCase());
+	var B = hexindex.indexOf(hex.charAt(4).toUpperCase()) * 16 + hexindex.indexOf(hex.charAt(5).toUpperCase());
+	$('red').value = R;
+	$('green').value = G;
+	$('blue').value = B;
+}
+
+function updateInkOption(option, value) {
+	var pars = 'option='+option+'&value='+value;
+	var url = JS_CANVASURI + 'Ink/ink-ajax.php';
+	var myAjax = new Ajax.Request(url, 
+		{method: 'get', 
+		parameters: pars} 
+		);
+}
+
+function restoreInk() {
+	if(window.confirm('Are you sure you restore default values? All changes will be lost.')) {
+		var pars = 'restore=true';
+		var url = JS_CANVASURI + 'Ink/ink-ajax.php';
+		var myAjax = new Ajax.Request(url, 
+			{method: 'get', 
+			parameters: pars,
+			onComplete: function() { window.location.href=window.location.href; }
+			});
+	}
+}
Index: /afridex/plugins/Flutter/Ink/ink-admin.php
===================================================================
--- /afridex/plugins/Flutter/Ink/ink-admin.php (revision 21)
+++ /afridex/plugins/Flutter/Ink/ink-admin.php (revision 21)
@@ -0,0 +1,101 @@
+<?php
+function ink_admin() {
+	global $wpdb, $table_prefix;
+	$table = $table_prefix . 'ink';
+	$theme = get_option('template');
+
+	$freshPageFolderName = (dirname(plugin_basename(__FILE__)));
+
+	// This isn't testing what you think it is...
+	if($values = $wpdb->get_results("SELECT * FROM ".$table." WHERE theme = '$theme'")) {
+		?>
+	<div id="inkadmin">
+		<div class="shelf_column" id="leftcolumn">
+			<h5 class="title">Color Mixer <span>&#8212; After selecting a color field, create your own mix.</span></h5>
+			<div class="colorfields">
+				<div id="red_bar" class="color_bar">
+					<img class="slider" id="red_slider" src="../<?php echo FLUTTER_URI_RELATIVE?>images/horiz_slider.png" alt=""/>
+				</div>
+				<div id="green_bar" class="color_bar">
+					<img class="slider" id="green_slider" src="../<?php echo FLUTTER_URI_RELATIVE?>images/horiz_slider.png" alt=""/>
+				</div>
+				<div id="blue_bar" class="color_bar">
+					<img class="slider" id="blue_slider" src="../<?php echo FLUTTER_URI_RELATIVE?>images/horiz_slider.png" alt=""/>
+				</div>
+			</div>
+			<div class="colorbox">
+				<div id="colorfield">
+				</div>
+				<input type="text" id="red" name="red" value="0" size="3"/><br/>
+				<input type="text" id="green" name="green" value="0" size="3"/><br/>
+				<input type="text" id="blue" name="blue" value="0" size="3"/>
+			</div>
+		</div>
+		<div class="content_wrap">
+		<form><label><input type="checkbox" name="enableInk" id="enableInk" <?php if(get_option('enableInk') == 'true') echo 'checked'; ?> /> Activate</label></form>
+		<form name="ink" id="ink" action="">
+		<p><strong>This portion of Flutter is experimental. This is not a final product and has not been tested extensively. Use it at your own risk.</strong></p>
+		<p>Use the form below to modify your blog's font and color settings. To modify colors, select the color field you'd like to modify and then use the RGB color mixer in the shelf below to choose your color (you can use the hexadecimal field too). If you're unsure what you're modifying, click the '?' link next to the element name to view your blog with that element highlighted.</p>
+		<input type="hidden" id="activeField" name="activeField" />
+		<?php foreach($values as $value) { ?>
+			<div class="ink_element">
+			<h3><?php echo $value->element ?> <a title="Highlight this element" href="<?php ink_highlight_url($value->element) ?>">?</a></h3>
+			<span>Background: <input class="color" onclick="toggleColorSwatch('body__background')" id="<?php echo urlencode($value->element).'__' ?>background" name="<?php echo urlencode($value->element).'__' ?>background" type="text" value="<?php echo $value->background ?>"/></span>
+			<span>Border color: <input class="color" id="<?php echo urlencode($value->element).'__' ?>border" type="text" value="<?php echo $value->border ?>" name="<?php echo urlencode($value->element).'__' ?>border" /></span>
+			<span>Font color: <input class="color" id="<?php echo urlencode($value->element).'__' ?>color" type="text" value="<?php echo $value->color ?>" name="<?php echo urlencode($value->element).'__' ?>color" /></span>
+			<br/>
+			<span>Font family: 
+							<select id="<?php echo urlencode($value->element).'__' ?>font_family" name="<?php echo urlencode($value->element).'__' ?>font_family" />
+								<option value="none">Default</option>
+								<option value="'Century Gothic', 'Arial', 'Verdana', 'Helvetica', 'Avant Garde', sans-serif" <?php is_selected("'Century Gothic', 'Arial', 'Verdana', 'Helvetica', 'Avant Garde', sans-serif", $value->font_family) ?>>"Century Gothic", "Arial", "Verdana", "Helvetica", "Avant Garde", sans-serif</option>
+								<option value="'Times New Roman', Roman, serif" <?php is_selected("'Times New Roman', Roman, serif", $value->font_family) ?>>"Times New Roman", Roman, serif</option>
+								<option value="'Lucida Grand', Algerian, fantasy" <?php is_selected("'Lucida Grand', Algerian, fantasy", $value->font_family) ?>>"Lucida Grand", Algerian, fantasy</option>
+								<option value="'Comic Sans MS', 'Brush Script MT', cursive" <?php is_selected("'Comic Sans MS', 'Brush Script MT', cursive", $value->font_family) ?>>"Comic Sans MS", "Brush Script MT", cursive</option>
+								<option value="'Courier New', Courier, monospace" <?php is_selected("'Courier New', Courier, monospace", $value->font_family) ?>>"Courier New", Courier, monospace</option>
+							</select>
+			</span>
+			<span>Font size (pixels): <input class="size" id="<?php echo urlencode($value->element).'__' ?>font_size" size="3" type="text" value="<?php echo $value->font_size ?>" name="<?php echo urlencode($value->element).'__' ?>font_size" /></span>
+			<span>Font style: 
+							<select id="<?php echo urlencode($value->element).'__' ?>font_style" name="<?php echo urlencode($value->element).'__' ?>font_style">
+								<option value="none" <?php is_selected('none', $value->font_style) ?>>Default</option>
+								<option value="normal" <?php is_selected('normal', $value->font_style) ?>>Normal</option>
+								<option value="italic" <?php is_selected('italic', $value->font_style) ?>>Italic</option>
+								<option value="bold" <?php is_selected('bold', $value->font_style) ?>>Bold</option>
+								<option value="bold italic" <?php is_selected('bold italic', $value->font_style) ?>>Bold Italic</option>
+							</select>
+			</span>
+			</div>
+		<?php } ?>
+		</form>
+		</div>
+
+	</div>
+	<div class="content_wrap">
+		<input type="button" id="restore" value="Restore Defaults" />
+	</div>
+	<img src="../<?php echo FLUTTER_URI_RELATIVE?>images/colorswatch-active.png" alt="" style="display: none;">
+		<?php
+	} else {
+		echo '<div id="inkadmin"><div class="content_wrap">The Ink table doesn\'t exist in your database. Please try reinstalling the software.</div></div>';
+	}
+}
+
+function is_selected($style, $value) {
+	if($value == $style) echo 'selected="selected"';
+}
+
+function ink_highlight_url($element) {
+	echo get_bloginfo('siteurl').'/?ink_highlight='.urlencode($element);
+}
+
+function add_ink_admin() { 
+	add_submenu_page('themes.php', 'Ink', 'Ink', 8, basename(__FILE__), 'ink_admin');
+}
+
+function add_ink_js() { ?>
+	<?php if($_GET["page"] == 'FlutterInk') : ?><script language="JavaScript" type="text/javascript" src="<?php bloginfo('wpurl') ?>/<?php echo FLUTTER_URI_RELATIVE?>Ink/ink.js"></script>
+	<link rel="stylesheet" href="<?php bloginfo('wpurl') ?>/<?php echo FLUTTER_URI_RELATIVE?>Ink/ink.css" type="text/css" media="screen" /><?php endif; ?>
+<?php
+}
+
+?>
Index: /afridex/plugins/Flutter/phpthumb.class.php
===================================================================
--- /afridex/plugins/Flutter/phpthumb.class.php (revision 21)
+++ /afridex/plugins/Flutter/phpthumb.class.php (revision 21)
@@ -0,0 +1,3624 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// See: phpthumb.readme.txt for usage instructions          //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+ob_start();
+if (!include_once(dirname(__FILE__).'/phpthumb.functions.php')) {
+	ob_end_flush();
+	die('failed to include_once("'.realpath(dirname(__FILE__).'/phpthumb.functions.php').'")');
+}
+ob_end_clean();
+
+class phpthumb {
+
+	// public:
+	// START PARAMETERS (for object mode and phpThumb.php)
+	// See phpthumb.readme.txt for descriptions of what each of these values are
+	var $src  = null;     // SouRCe filename
+	var $new  = null;     // NEW image (phpThumb.php only)
+	var $w    = null;     // Width
+	var $h    = null;     // Height
+	var $wp   = null;     // Width  (Portrait Images Only)
+	var $hp   = null;     // Height (Portrait Images Only)
+	var $wl   = null;     // Width  (Landscape Images Only)
+	var $hl   = null;     // Height (Landscape Images Only)
+	var $ws   = null;     // Width  (Square Images Only)
+	var $hs   = null;     // Height (Square Images Only)
+	var $f    = null;     // Format
+	var $q    = 75;       // jpeg output Quality
+	var $sx   = null;     // Source crop top-left X position
+	var $sy   = null;     // Source crop top-left Y position
+	var $sw   = null;     // Source crop Width
+	var $sh   = null;     // Source crop Height
+	var $zc   = null;     // Zoom Crop
+	var $bc   = null;     // Border Color
+	var $bg   = null;     // BackGround color
+	var $fltr = array();  // FiLTeRs
+	var $goto = null;     // GO TO url after processing
+	var $err  = null;     // default ERRor image filename
+	var $xto  = null;     // extract eXif Thumbnail Only
+	var $ra   = null;     // Rotate by Angle
+	var $ar   = null;     // Auto Rotate
+	var $aoe  = null;     // Allow Output Enlargement
+	var $far  = null;     // Fixed Aspect Ratio
+	var $iar  = null;     // Ignore Aspect Ratio
+	var $maxb = null;     // MAXimum Bytes
+	var $down = null;     // DOWNload thumbnail filename
+	var $md5s = null;     // MD5 hash of Source image
+	var $sfn  = 0;        // Source Frame Number
+	var $dpi  = 150;      // Dots Per Inch for vector source formats
+	var $sia  = null;     // Save Image As filename
+
+	var $file = null;     // >>>deprecated, DO NOT USE, will be removed in future versions<<<
+
+	var $phpThumbDebug = null;
+	// END PARAMETERS
+
+
+	// public:
+	// START CONFIGURATION OPTIONS (for object mode only)
+	// See phpThumb.config.php for descriptions of what each of these settings do
+
+	// * Directory Configuration
+	var $config_cache_directory                      = null;
+	var $config_cache_directory_depth                = 0;
+	var $config_cache_disable_warning                = true;
+	var $config_cache_source_enabled                 = false;
+	var $config_cache_source_directory               = null;
+	var $config_temp_directory                       = null;
+	var $config_document_root                        = null;
+
+	// * Default output configuration:
+	var $config_output_format                        = 'jpeg';
+	var $config_output_maxwidth                      = 0;
+	var $config_output_maxheight                     = 0;
+	var $config_output_interlace                     = true;
+
+	// * Error message configuration
+	var $config_error_image_width                    = 400;
+	var $config_error_image_height                   = 100;
+	var $config_error_message_image_default          = '';
+	var $config_error_bgcolor                        = 'CCCCFF';
+	var $config_error_textcolor                      = 'FF0000';
+	var $config_error_fontsize                       = 1;
+	var $config_error_die_on_error                   = false;
+	var $config_error_silent_die_on_error            = false;
+	var $config_error_die_on_source_failure          = true;
+
+	// * Anti-Hotlink Configuration:
+	var $config_nohotlink_enabled                    = true;
+	var $config_nohotlink_valid_domains              = array();
+	var $config_nohotlink_erase_image                = true;
+	var $config_nohotlink_text_message               = 'Off-server thumbnailing is not allowed';
+	// * Off-server Linking Configuration:
+	var $config_nooffsitelink_enabled                = false;
+	var $config_nooffsitelink_valid_domains          = array();
+	var $config_nooffsitelink_require_refer          = false;
+	var $config_nooffsitelink_erase_image            = true;
+	var $config_nooffsitelink_watermark_src          = '';
+	var $config_nooffsitelink_text_message           = 'Off-server linking is not allowed';
+
+	// * Border & Background default colors
+	var $config_border_hexcolor                      = '000000';
+	var $config_background_hexcolor                  = 'FFFFFF';
+
+	// * TrueType Fonts
+	var $config_ttf_directory                        = '.';
+
+	var $config_max_source_pixels                    = null;
+	var $config_use_exif_thumbnail_for_speed         = false;
+	var $allow_local_http_src                        = false;
+
+	var $config_imagemagick_path                     = null;
+	var $config_prefer_imagemagick                   = true;
+	var $config_imagemagick_use_thumbnail            = true;
+
+	var $config_cache_maxage                         = null;
+	var $config_cache_maxsize                        = null;
+	var $config_cache_maxfiles                       = null;
+	var $config_cache_source_filemtime_ignore_local  = false;
+	var $config_cache_source_filemtime_ignore_remote = true;
+	var $config_cache_default_only_suffix            = false;
+	var $config_cache_force_passthru                 = true;
+	var $config_cache_prefix                         = '';    // default value set in the constructor below
+
+	// * MySQL
+	var $config_mysql_query                          = null;
+	var $config_mysql_hostname                       = null;
+	var $config_mysql_username                       = null;
+	var $config_mysql_password                       = null;
+	var $config_mysql_database                       = null;
+
+	// * Security
+	var $config_high_security_enabled                = false;
+	var $config_high_security_password               = null;
+	var $config_disable_debug                        = false;
+	var $config_allow_src_above_docroot              = false;
+	var $config_allow_src_above_phpthumb             = true;
+	var $config_allow_parameter_file                 = false;
+	var $config_allow_parameter_goto                 = false;
+
+	// * HTTP fopen
+	var $config_http_fopen_timeout                   = 10;
+	var $config_http_follow_redirect                 = true;
+
+	// * Compatability
+	var $config_disable_pathinfo_parsing             = false;
+	var $config_disable_imagecopyresampled           = false;
+	var $config_disable_onlycreateable_passthru      = false;
+
+	var $config_http_user_agent                      = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7';
+
+	// END CONFIGURATION OPTIONS
+
+
+	// public: error messages (read-only)
+	var $debugmessages = array();
+	var $debugtiming   = array();
+	var $fatalerror    = null;
+
+
+	// private: (should not be modified directly)
+	var $thumbnailQuality = 75;
+	var $thumbnailFormat  = null;
+
+	var $sourceFilename   = null;
+	var $rawImageData     = null;
+	var $IMresizedData    = null;
+	var $outputImageData  = null;
+
+	var $useRawIMoutput   = false;
+
+	var $gdimg_output     = null;
+	var $gdimg_source     = null;
+
+	var $getimagesizeinfo = null;
+
+	var $source_width  = null;
+	var $source_height = null;
+
+	var $thumbnailCropX = null;
+	var $thumbnailCropY = null;
+	var $thumbnailCropW = null;
+	var $thumbnailCropH = null;
+
+	var $exif_thumbnail_width  = null;
+	var $exif_thumbnail_height = null;
+	var $exif_thumbnail_type   = null;
+	var $exif_thumbnail_data   = null;
+	var $exif_raw_data         = null;
+
+	var $thumbnail_width        = null;
+	var $thumbnail_height       = null;
+	var $thumbnail_image_width  = null;
+	var $thumbnail_image_height = null;
+
+	var $cache_filename         = null;
+
+	var $AlphaCapableFormats = array('png', 'ico', 'gif');
+	var $is_alpha = false;
+
+	var $iswindows = null;
+
+	var $phpthumb_version = '1.7.7-200612252156';
+
+	//////////////////////////////////////////////////////////////////////
+
+	// public: constructor
+	function phpThumb() {
+		$this->DebugTimingMessage('phpThumb() constructor', __FILE__, __LINE__);
+		$this->DebugMessage('phpThumb() v'.$this->phpthumb_version, __FILE__, __LINE__);
+		$this->config_max_source_pixels = round(max(intval(ini_get('memory_limit')), intval(get_cfg_var('memory_limit'))) * 1048576 * 0.20); // 20% of memory_limit
+		$this->iswindows = (bool) (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN');
+		$this->config_temp_directory = realpath($this->config_temp_directory ? $this->config_temp_directory : (getenv('TMPDIR') ? getenv('TMPDIR') : getenv('TMP')));
+		$this->config_document_root = (@$_SERVER['DOCUMENT_ROOT'] ? $_SERVER['DOCUMENT_ROOT'] : $this->config_document_root);
+		$this->config_cache_prefix = 'phpThumb_cache_'.@$_SERVER['SERVER_NAME'];
+
+		$php_sapi_name = strtolower(function_exists('php_sapi_name') ? php_sapi_name() : '');
+		if ($php_sapi_name == 'cli') {
+			$this->config_allow_src_above_docroot = true;
+		}
+	}
+
+	// public:
+	function setSourceFilename($sourceFilename) {
+		$this->rawImageData   = null;
+		$this->sourceFilename = $sourceFilename;
+		$this->src            = $sourceFilename;
+		$this->DebugMessage('setSourceFilename('.$sourceFilename.') set $this->sourceFilename to "'.$this->sourceFilename.'"', __FILE__, __LINE__);
+		return true;
+	}
+
+	// public:
+	function setSourceData($rawImageData, $sourceFilename='') {
+		$this->sourceFilename = null;
+		$this->rawImageData   = $rawImageData;
+		$this->DebugMessage('setSourceData() setting $this->rawImageData ('.strlen($this->rawImageData).' bytes)', __FILE__, __LINE__);
+		if ($this->config_cache_source_enabled) {
+			$sourceFilename = ($sourceFilename ? $sourceFilename : md5($rawImageData));
+			if (!is_dir($this->config_cache_source_directory)) {
+				$this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not a directory');
+			} elseif (!@is_writable($this->config_cache_source_directory)) {
+				$this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not writable');
+			}
+			$this->DebugMessage('setSourceData() attempting to save source image to "'.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).'"', __FILE__, __LINE__);
+			if ($fp = @fopen($this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename), 'wb')) {
+				fwrite($fp, $rawImageData);
+				fclose($fp);
+			} elseif (!$this->phpThumbDebug) {
+				$this->ErrorImage('setSourceData() failed to write to source cache ('.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).')');
+			}
+		}
+		return true;
+	}
+
+	// public:
+	function setSourceImageResource($gdimg) {
+		$this->gdimg_source = $gdimg;
+		return true;
+	}
+
+	// public:
+	function setParameter($param, $value) {
+		if ($param == 'src') {
+			$this->setSourceFilename($this->ResolveFilenameToAbsolute($value));
+		} elseif (@is_array($this->$param)) {
+			if (is_array($value)) {
+				foreach ($value as $arraykey => $arrayvalue) {
+					array_push($this->$param, $arrayvalue);
+				}
+			} else {
+				array_push($this->$param, $value);
+			}
+		} else {
+			$this->$param = $value;
+		}
+		return true;
+	}
+
+	// public:
+	function getParameter($param) {
+		//if (property_exists('phpThumb', $param)) {
+			return $this->$param;
+		//}
+		//$this->DebugMessage('setParameter() attempting to get non-existant parameter "'.$param.'"', __FILE__, __LINE__);
+		//return false;
+	}
+
+
+	// public:
+	function GenerateThumbnail() {
+
+		$this->setOutputFormat();
+			$this->phpThumbDebug('8a');
+		$this->ResolveSource();
+			$this->phpThumbDebug('8b');
+		$this->SetCacheFilename();
+			$this->phpThumbDebug('8c');
+		$this->ExtractEXIFgetImageSize();
+			$this->phpThumbDebug('8d');
+		if ($this->useRawIMoutput) {
+			$this->DebugMessage('Skipping rest of GenerateThumbnail() because $this->useRawIMoutput', __FILE__, __LINE__);
+			return true;
+		}
+			$this->phpThumbDebug('8e');
+		if (!$this->SourceImageToGD()) {
+			$this->DebugMessage('SourceImageToGD() failed', __FILE__, __LINE__);
+			return false;
+		}
+			$this->phpThumbDebug('8f');
+		$this->Rotate();
+			$this->phpThumbDebug('8g');
+		$this->CreateGDoutput();
+			$this->phpThumbDebug('8h');
+
+		switch ($this->far) {
+			case 'L':
+			case 'TL':
+			case 'BL':
+				$destination_offset_x = 0;
+				$destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2);
+				break;
+			case 'R':
+			case 'TR':
+			case 'BR':
+				$destination_offset_x =  round($this->thumbnail_width  - $this->thumbnail_image_width);
+				$destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2);
+				break;
+			case 'T':
+			case 'TL':
+			case 'TR':
+				$destination_offset_x = round(($this->thumbnail_width  - $this->thumbnail_image_width)  / 2);
+				$destination_offset_y = 0;
+				break;
+			case 'B':
+			case 'BL':
+			case 'BR':
+				$destination_offset_x = round(($this->thumbnail_width  - $this->thumbnail_image_width)  / 2);
+				$destination_offset_y =  round($this->thumbnail_height - $this->thumbnail_image_height);
+				break;
+			case 'C':
+			default:
+				$destination_offset_x = round(($this->thumbnail_width  - $this->thumbnail_image_width)  / 2);
+				$destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2);
+		}
+
+//		// copy/resize image to appropriate dimensions
+//		$borderThickness = 0;
+//		if (!empty($this->fltr)) {
+//			foreach ($this->fltr as $key => $value) {
+//				if (ereg('^bord\|([0-9]+)', $value, $matches)) {
+//					$borderThickness = $matches[1];
+//					break;
+//				}
+//			}
+//		}
+//		if ($borderThickness > 0) {
+//			//$this->DebugMessage('Skipping ImageResizeFunction() because BorderThickness="'.$borderThickness.'"', __FILE__, __LINE__);
+//			$this->thumbnail_image_height /= 2;
+//		}
+		$this->ImageResizeFunction(
+			$this->gdimg_output,
+			$this->gdimg_source,
+			$destination_offset_x,
+			$destination_offset_y,
+			$this->thumbnailCropX,
+			$this->thumbnailCropY,
+			$this->thumbnail_image_width,
+			$this->thumbnail_image_height,
+			$this->thumbnailCropW,
+			$this->thumbnailCropH
+		);
+
+		$this->DebugMessage('memory_get_usage() after copy-resize = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__);
+		ImageDestroy($this->gdimg_source);
+		$this->DebugMessage('memory_get_usage() after ImageDestroy = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__);
+
+			$this->phpThumbDebug('8i');
+		$this->AntiOffsiteLinking();
+			$this->phpThumbDebug('8j');
+		$this->ApplyFilters();
+			$this->phpThumbDebug('8k');
+		$this->AlphaChannelFlatten();
+			$this->phpThumbDebug('8l');
+		$this->MaxFileSize();
+			$this->phpThumbDebug('8m');
+
+		$this->DebugMessage('GenerateThumbnail() completed successfully', __FILE__, __LINE__);
+		return true;
+	}
+
+
+	// public:
+	function RenderOutput() {
+		if (!$this->useRawIMoutput && !is_resource($this->gdimg_output)) {
+			$this->DebugMessage('RenderOutput() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__);
+			return false;
+		}
+		if (!$this->thumbnailFormat) {
+			$this->DebugMessage('RenderOutput() failed because $this->thumbnailFormat is empty', __FILE__, __LINE__);
+			return false;
+		}
+		if ($this->useRawIMoutput) {
+			$this->DebugMessage('RenderOutput copying $this->IMresizedData ('.strlen($this->IMresizedData).' bytes) to $this->outputImage', __FILE__, __LINE__);
+			$this->outputImageData = $this->IMresizedData;
+			return true;
+		}
+
+		$builtin_formats = array();
+		if (function_exists('ImageTypes')) {
+			$imagetypes = ImageTypes();
+			$builtin_formats['wbmp'] = (bool) ($imagetypes & IMG_WBMP);
+			$builtin_formats['jpg']  = (bool) ($imagetypes & IMG_JPG);
+			$builtin_formats['gif']  = (bool) ($imagetypes & IMG_GIF);
+			$builtin_formats['png']  = (bool) ($imagetypes & IMG_PNG);
+		}
+		$this->DebugMessage('RenderOutput() attempting Image'.strtoupper(@$this->thumbnailFormat).'($this->gdimg_output)', __FILE__, __LINE__);
+		ob_start();
+		switch ($this->thumbnailFormat) {
+			case 'wbmp':
+				if (!@$builtin_formats['wbmp']) {
+					$this->DebugMessage('GD does not have required built-in support for WBMP output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				ImageJPEG($this->gdimg_output, null, $this->thumbnailQuality);
+				$this->outputImageData = ob_get_contents();
+				break;
+
+			case 'jpeg':
+			case 'jpg':  // should be "jpeg" not "jpg" but just in case...
+				if (!@$builtin_formats['jpg']) {
+					$this->DebugMessage('GD does not have required built-in support for JPEG output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				ImageJPEG($this->gdimg_output, null, $this->thumbnailQuality);
+				$this->outputImageData = ob_get_contents();
+				break;
+
+			case 'png':
+				if (!@$builtin_formats['png']) {
+					$this->DebugMessage('GD does not have required built-in support for PNG output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				ImagePNG($this->gdimg_output);
+				$this->outputImageData = ob_get_contents();
+				break;
+
+			case 'gif':
+				if (!@$builtin_formats['gif']) {
+					$this->DebugMessage('GD does not have required built-in support for GIF output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				ImageGIF($this->gdimg_output);
+				$this->outputImageData = ob_get_contents();
+				break;
+
+			case 'bmp':
+				$ImageOutFunction = '"builtin BMP output"';
+				if (!@include_once(dirname(__FILE__).'/phpthumb.bmp.php')) {
+					$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				$phpthumb_bmp = new phpthumb_bmp();
+				$this->outputImageData = $phpthumb_bmp->GD2BMPstring($this->gdimg_output);
+				unset($phpthumb_bmp);
+				break;
+
+			case 'ico':
+				$ImageOutFunction = '"builtin ICO output"';
+				if (!@include_once(dirname(__FILE__).'/phpthumb.ico.php')) {
+					$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__);
+					ob_end_clean();
+					return false;
+				}
+				$phpthumb_ico = new phpthumb_ico();
+				$arrayOfOutputImages = array($this->gdimg_output);
+				$this->outputImageData = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages);
+				unset($phpthumb_ico);
+				break;
+
+			default:
+				$this->DebugMessage('RenderOutput failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__);
+				ob_end_clean();
+				return false;
+		}
+		ob_end_clean();
+		if (!$this->outputImageData) {
+			$this->DebugMessage('RenderOutput() for "'.$this->thumbnailFormat.'" failed', __FILE__, __LINE__);
+			ob_end_clean();
+			return false;
+		}
+		$this->DebugMessage('RenderOutput() completing with $this->outputImageData = '.strlen($this->outputImageData).' bytes', __FILE__, __LINE__);
+		return true;
+	}
+
+	function RenderToFile($filename) {
+		if (eregi('^(f|ht)tps?\://', $filename)) {
+			$this->DebugMessage('RenderToFile() failed because $filename ('.$filename.') is a URL', __FILE__, __LINE__);
+			return false;
+		}
+		// render thumbnail to this file only, do not cache, do not output to browser
+		//$renderfilename = $this->ResolveFilenameToAbsolute(dirname($filename)).DIRECTORY_SEPARATOR.basename($filename);
+		$renderfilename = $filename;
+		if (($filename{0} != '/') && ($filename{0} != '\\') && ($filename{1} != ':')) {
+			$renderfilename = $this->ResolveFilenameToAbsolute($renderfilename);
+		}
+		if (!@is_writable(dirname($renderfilename))) {
+			$this->DebugMessage('RenderToFile() failed because "'.dirname($renderfilename).'/" is not writable', __FILE__, __LINE__);
+			return false;
+		}
+		if (@is_file($renderfilename) && !@is_writable($renderfilename)) {
+			$this->DebugMessage('RenderToFile() failed because "'.$renderfilename.'" is not writable', __FILE__, __LINE__);
+			return false;
+		}
+
+		if ($this->RenderOutput()) {
+			if (file_put_contents($renderfilename, $this->outputImageData)) {
+				$this->DebugMessage('RenderToFile('.$renderfilename.') succeeded', __FILE__, __LINE__);
+				return true;
+			}
+			if (!@file_exists($renderfilename)) {
+				$this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] did not appear to fail, but the output image does not exist either...', __FILE__, __LINE__);
+			}
+		} else {
+			$this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] failed', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	// public:
+	function OutputThumbnail() {
+		if (!$this->useRawIMoutput && !is_resource($this->gdimg_output)) {
+			$this->DebugMessage('OutputThumbnail() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__);
+			return false;
+		}
+		if (headers_sent()) {
+			return $this->ErrorImage('OutputThumbnail() failed - headers already sent');
+			exit;
+		}
+
+		$downloadfilename = phpthumb_functions::SanitizeFilename(is_string($this->sia) ? $this->sia : ($this->down ? $this->down : 'phpThumb_generated_thumbnail'.'.'.$this->thumbnailFormat));
+		$this->DebugMessage('Content-Disposition header filename set to "'.$downloadfilename.'"', __FILE__, __LINE__);
+		if ($downloadfilename) {
+			header('Content-Disposition: '.($this->down ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"');
+		} else {
+			$this->DebugMessage('failed to send Content-Disposition header because $downloadfilename is empty', __FILE__, __LINE__);
+		}
+
+		if ($this->useRawIMoutput) {
+
+			header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+			echo $this->IMresizedData;
+
+		} else {
+
+			$this->DebugMessage('ImageInterlace($this->gdimg_output, '.intval($this->config_output_interlace).')', __FILE__, __LINE__);
+			ImageInterlace($this->gdimg_output, intval($this->config_output_interlace));
+			switch ($this->thumbnailFormat) {
+				case 'jpeg':
+					header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+					$ImageOutFunction = 'image'.$this->thumbnailFormat;
+					@$ImageOutFunction($this->gdimg_output, '', $this->thumbnailQuality);
+					break;
+
+				case 'png':
+				case 'gif':
+					header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+					$ImageOutFunction = 'image'.$this->thumbnailFormat;
+					@$ImageOutFunction($this->gdimg_output);
+					break;
+
+				case 'bmp':
+					if (!@include_once(dirname(__FILE__).'/phpthumb.bmp.php')) {
+						$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__);
+						return false;
+					}
+					$phpthumb_bmp = new phpthumb_bmp();
+					if (is_object($phpthumb_bmp)) {
+						$bmp_data = $phpthumb_bmp->GD2BMPstring($this->gdimg_output);
+						unset($phpthumb_bmp);
+						if (!$bmp_data) {
+							$this->DebugMessage('$phpthumb_bmp->GD2BMPstring() failed', __FILE__, __LINE__);
+							return false;
+						}
+						header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+						echo $bmp_data;
+					} else {
+						$this->DebugMessage('new phpthumb_bmp() failed', __FILE__, __LINE__);
+						return false;
+					}
+					break;
+
+				case 'ico':
+					if (!@include_once(dirname(__FILE__).'/phpthumb.ico.php')) {
+						$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__);
+						return false;
+					}
+					$phpthumb_ico = new phpthumb_ico();
+					if (is_object($phpthumb_ico)) {
+						$arrayOfOutputImages = array($this->gdimg_output);
+						$ico_data = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages);
+						unset($phpthumb_ico);
+						if (!$ico_data) {
+							$this->DebugMessage('$phpthumb_ico->GD2ICOstring() failed', __FILE__, __LINE__);
+							return false;
+						}
+						header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
+						echo $ico_data;
+					} else {
+						$this->DebugMessage('new phpthumb_ico() failed', __FILE__, __LINE__);
+						return false;
+					}
+					break;
+
+				default:
+					$this->DebugMessage('OutputThumbnail failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__);
+					return false;
+					break;
+			}
+
+		}
+		return true;
+	}
+
+
+	// public:
+	function CleanUpCacheDirectory() {
+		if (($this->config_cache_maxage > 0) || ($this->config_cache_maxsize > 0) || ($this->config_cache_maxfiles > 0)) {
+			$CacheDirOldFilesAge  = array();
+			$CacheDirOldFilesSize = array();
+			$AllFilesInCacheDirectory = phpthumb_functions::GetAllFilesInSubfolders($this->config_cache_directory);
+			foreach ($AllFilesInCacheDirectory as $fullfilename) {
+				if (eregi('^phpThumb_cache_', $fullfilename) && file_exists($fullfilename)) {
+					$CacheDirOldFilesAge[$fullfilename] = @fileatime($fullfilename);
+					if ($CacheDirOldFilesAge[$fullfilename] == 0) {
+						$CacheDirOldFilesAge[$fullfilename] = @filemtime($fullfilename);
+					}
+					$CacheDirOldFilesSize[$fullfilename] = @filesize($fullfilename);
+				}
+			}
+			if (empty($CacheDirOldFilesSize)) {
+				return true;
+			}
+			$DeletedKeys = array();
+			foreach ($CacheDirOldFilesSize as $fullfilename => $filesize) {
+				// purge all zero-size files more than an hour old (to prevent trying to delete just-created and/or in-use files)
+				$cutofftime = time() - 3600;
+				if (($filesize == 0) && ($CacheDirOldFilesAge[$fullfilename] < $cutofftime)) {
+					if (@unlink($fullfilename)) {
+						$DeletedKeys[] = $fullfilename;
+						unset($CacheDirOldFilesSize[$fullfilename]);
+						unset($CacheDirOldFilesAge[$fullfilename]);
+					}
+				}
+			}
+			$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys).' zero-byte files', __FILE__, __LINE__);
+			asort($CacheDirOldFilesAge);
+
+			if ($this->config_cache_maxfiles > 0) {
+				$TotalCachedFiles = count($CacheDirOldFilesAge);
+				$DeletedKeys = array();
+				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
+					if ($TotalCachedFiles > $this->config_cache_maxfiles) {
+						if (@unlink($fullfilename)) {
+							$TotalCachedFiles--;
+							$DeletedKeys[] = $fullfilename;
+						}
+					} else {
+						// there are few enough files to keep the rest
+						break;
+					}
+				}
+				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys).' files based on (config_cache_maxfiles='.$this->config_cache_maxfiles.')', __FILE__, __LINE__);
+				foreach ($DeletedKeys as $fullfilename) {
+					unset($CacheDirOldFilesAge[$fullfilename]);
+					unset($CacheDirOldFilesSize[$fullfilename]);
+				}
+			}
+
+			if ($this->config_cache_maxage > 0) {
+				$mindate = time() - $this->config_cache_maxage;
+				$DeletedKeys = array();
+				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
+					if ($filedate > 0) {
+						if ($filedate < $mindate) {
+							if (@unlink($fullfilename)) {
+								$DeletedKeys[] = $fullfilename;
+							}
+						} else {
+							// the rest of the files are new enough to keep
+							break;
+						}
+					}
+				}
+				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys).' files based on (config_cache_maxage='.$this->config_cache_maxage.')', __FILE__, __LINE__);
+				foreach ($DeletedKeys as $fullfilename) {
+					unset($CacheDirOldFilesAge[$fullfilename]);
+					unset($CacheDirOldFilesSize[$fullfilename]);
+				}
+			}
+
+			if ($this->config_cache_maxsize > 0) {
+				$TotalCachedFileSize = array_sum($CacheDirOldFilesSize);
+				$DeletedKeys = array();
+				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
+					if ($TotalCachedFileSize > $this->config_cache_maxsize) {
+						if (@unlink($fullfilename)) {
+							$TotalCachedFileSize -= $CacheDirOldFilesSize[$fullfilename];
+							$DeletedKeys[] = $fullfilename;
+						}
+					} else {
+						// the total filesizes are small enough to keep the rest of the files
+						break;
+					}
+				}
+				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys).' files based on (config_cache_maxsize='.$this->config_cache_maxsize.')', __FILE__, __LINE__);
+				foreach ($DeletedKeys as $fullfilename) {
+					unset($CacheDirOldFilesAge[$fullfilename]);
+					unset($CacheDirOldFilesSize[$fullfilename]);
+				}
+			}
+
+		} else {
+			$this->DebugMessage('skipping CleanUpCacheDirectory() because config set to not use it', __FILE__, __LINE__);
+		}
+		return true;
+	}
+
+	//////////////////////////////////////////////////////////////////////
+
+	function ResolveSource() {
+		if (is_resource($this->gdimg_source)) {
+			$this->DebugMessage('ResolveSource() exiting because is_resource($this->gdimg_source)', __FILE__, __LINE__);
+			return true;
+		}
+		if ($this->rawImageData) {
+			$this->sourceFilename = null;
+			$this->DebugMessage('ResolveSource() exiting because $this->rawImageData is set ('.number_format(strlen($this->rawImageData)).' bytes)', __FILE__, __LINE__);
+			return true;
+		}
+		if ($this->sourceFilename) {
+			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->sourceFilename);
+			$this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'"', __FILE__, __LINE__);
+		} elseif ($this->src) {
+			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src);
+			$this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'" from $this->src ('.$this->src.')', __FILE__, __LINE__);
+		} else {
+			return $this->ErrorImage('$this->sourceFilename and $this->src are both empty');
+		}
+		if ($this->iswindows && ((substr($this->sourceFilename, 0, 2) == '//') || (substr($this->sourceFilename, 0, 2) == '\\\\'))) {
+			// Windows \\share\filename.ext
+		} elseif (eregi('^(f|ht)tps?\://', $this->sourceFilename)) {
+			// URL
+			if ($this->config_http_user_agent) {
+				ini_set('user_agent', $this->config_http_user_agent);
+			}
+		} elseif (!@file_exists($this->sourceFilename)) {
+			return $this->ErrorImage('"'.$this->sourceFilename.'" does not exist');
+		} elseif (!@is_file($this->sourceFilename)) {
+			return $this->ErrorImage('"'.$this->sourceFilename.'" is not a file');
+		}
+		return true;
+	}
+
+	function setOutputFormat() {
+		static $alreadyCalled = false;
+		if ($this->thumbnailFormat && $alreadyCalled) {
+			return true;
+		}
+		$alreadyCalled = true;
+
+		$AvailableImageOutputFormats = array();
+		$AvailableImageOutputFormats[] = 'text';
+		if (@is_readable(dirname(__FILE__).'/phpthumb.ico.php')) {
+			$AvailableImageOutputFormats[] = 'ico';
+		}
+		if (@is_readable(dirname(__FILE__).'/phpthumb.bmp.php')) {
+			$AvailableImageOutputFormats[] = 'bmp';
+		}
+
+		$this->thumbnailFormat = 'ico';
+
+		// Set default output format based on what image types are available
+		if (function_exists('ImageTypes')) {
+			$imagetypes = ImageTypes();
+			if ($imagetypes & IMG_WBMP) {
+				$this->thumbnailFormat         = 'wbmp';
+				$AvailableImageOutputFormats[] = 'wbmp';
+			}
+			if ($imagetypes & IMG_GIF) {
+				$this->thumbnailFormat         = 'gif';
+				$AvailableImageOutputFormats[] = 'gif';
+			}
+			if ($imagetypes & IMG_PNG) {
+				$this->thumbnailFormat         = 'png';
+				$AvailableImageOutputFormats[] = 'png';
+			}
+			if ($imagetypes & IMG_JPG) {
+				$this->thumbnailFormat         = 'jpeg';
+				$AvailableImageOutputFormats[] = 'jpeg';
+			}
+		} else {
+			//return $this->ErrorImage('ImageTypes() does not exist - GD support might not be enabled?');
+			$this->DebugMessage('ImageTypes() does not exist - GD support might not be enabled?',  __FILE__, __LINE__);
+		}
+		if ($this->ImageMagickVersion()) {
+			$IMformats = array('jpeg', 'png', 'gif', 'bmp', 'ico', 'wbmp');
+			$this->DebugMessage('Addding ImageMagick formats to $AvailableImageOutputFormats ('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__);
+			foreach ($IMformats as $key => $format) {
+				$AvailableImageOutputFormats[] = $format;
+			}
+		}
+		$AvailableImageOutputFormats = array_unique($AvailableImageOutputFormats);
+		$this->DebugMessage('$AvailableImageOutputFormats = array('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__);
+
+		if (strtolower($this->config_output_format) == 'jpg') {
+			$this->config_output_format = 'jpeg';
+		}
+		if (strtolower($this->f) == 'jpg') {
+			$this->f = 'jpeg';
+		}
+		if (phpthumb_functions::CaseInsensitiveInArray($this->config_output_format, $AvailableImageOutputFormats)) {
+			// set output format to config default if that format is available
+			$this->DebugMessage('$this->thumbnailFormat set to $this->config_output_format "'.strtolower($this->config_output_format).'"', __FILE__, __LINE__);
+			$this->thumbnailFormat = strtolower($this->config_output_format);
+		} elseif ($this->config_output_format) {
+			$this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->config_output_format ('.strtolower($this->config_output_format).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__);
+		}
+		if ($this->f && (phpthumb_functions::CaseInsensitiveInArray($this->f, $AvailableImageOutputFormats))) {
+			// override output format if $this->f is set and that format is available
+			$this->DebugMessage('$this->thumbnailFormat set to $this->f "'.strtolower($this->f).'"', __FILE__, __LINE__);
+			$this->thumbnailFormat = strtolower($this->f);
+		} elseif ($this->f) {
+			$this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->f ('.strtolower($this->f).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__);
+		}
+
+		// for JPEG images, quality 1 (worst) to 99 (best)
+		// quality < 25 is nasty, with not much size savings - not recommended
+		// problems with 100 - invalid JPEG?
+		$this->thumbnailQuality = max(1, min(99, ($this->q ? $this->q : 75)));
+		$this->DebugMessage('$this->thumbnailQuality set to "'.$this->thumbnailQuality.'"', __FILE__, __LINE__);
+
+		return true;
+	}
+
+	function setCacheDirectory() {
+		// resolve cache directory to absolute pathname
+		$this->DebugMessage('setCacheDirectory() starting with config_cache_directory = "'.$this->config_cache_directory.'"', __FILE__, __LINE__);
+		if (substr($this->config_cache_directory, 0, 1) == '.') {
+			if (eregi('^(f|ht)tps?\://', $this->src)) {
+				if (!$this->config_cache_disable_warning) {
+					$this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') cannot be used for remote images. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php');
+				}
+			} elseif ($this->src) {
+				// resolve relative cache directory to source image
+				$this->config_cache_directory = dirname($this->ResolveFilenameToAbsolute($this->src)).DIRECTORY_SEPARATOR.$this->config_cache_directory;
+			} else {
+				// $this->new is probably set
+			}
+		}
+		if (substr($this->config_cache_directory, -1) == '/') {
+			$this->config_cache_directory = substr($this->config_cache_directory, 0, -1);
+		}
+		if ($this->iswindows) {
+			$this->config_cache_directory = str_replace('/', DIRECTORY_SEPARATOR, $this->config_cache_directory);
+		}
+		if ($this->config_cache_directory) {
+			$real_cache_path = realpath($this->config_cache_directory);
+			if (!$real_cache_path) {
+				$this->DebugMessage('realpath($this->config_cache_directory) failed for "'.$this->config_cache_directory.'"', __FILE__, __LINE__);
+				if (!is_dir($this->config_cache_directory)) {
+					$this->DebugMessage('!is_dir('.$this->config_cache_directory.')', __FILE__, __LINE__);
+				}
+			}
+			if ($real_cache_path) {
+				$this->DebugMessage('setting config_cache_directory to realpath('.$this->config_cache_directory.') = "'.$real_cache_path.'"', __FILE__, __LINE__);
+				$this->config_cache_directory = $real_cache_path;
+			}
+		}
+		if (!is_dir($this->config_cache_directory)) {
+			if (!$this->config_cache_disable_warning) {
+				$this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') does not exist. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php');
+			}
+			$this->DebugMessage('$this->config_cache_directory ('.$this->config_cache_directory.') is not a directory', __FILE__, __LINE__);
+			$this->config_cache_directory = null;
+		} elseif (!@is_writable($this->config_cache_directory)) {
+			$this->DebugMessage('$this->config_cache_directory is not writable ('.$this->config_cache_directory.')', __FILE__, __LINE__);
+		}
+
+		if (!@is_dir($this->config_temp_directory) && !@is_writable($this->config_temp_directory) && @is_dir($this->config_cache_directory) && @is_writable($this->config_cache_directory)) {
+			$this->DebugMessage('setting $this->config_temp_directory = $this->config_cache_directory ('.$this->config_cache_directory.')', __FILE__, __LINE__);
+			$this->config_temp_directory = $this->config_cache_directory;
+		}
+		return true;
+	}
+
+
+	function ResolveFilenameToAbsolute($filename) {
+		if (!$filename) {
+			return false;
+		}
+
+		//if (eregi('^(f|ht)tps?\://', $filename)) {
+		if (eregi('^[a-z0-9]+\:/{1,2}', $filename)) {
+			// eg: http://host/path/file.jpg (HTTP URL)
+			// eg: ftp://host/path/file.jpg  (FTP URL)
+			// eg: data1:/path/file.jpg      (Netware path)
+
+			//$AbsoluteFilename = $filename;
+			return $filename;
+
+		} elseif ($this->iswindows && ($filename{1} == ':')) {
+
+			// absolute pathname (Windows)
+			$AbsoluteFilename = $filename;
+
+		} elseif ($this->iswindows && ((substr($filename, 0, 2) == '//') || (substr($filename, 0, 2) == '\\\\'))) {
+
+			// absolute pathname (Windows)
+			$AbsoluteFilename = $filename;
+
+		} elseif ($filename{0} == '/') {
+
+			if (@is_readable($filename) && !@is_readable($this->config_document_root.$filename)) {
+
+				// absolute filename (*nix)
+				$AbsoluteFilename = $filename;
+
+			} elseif ($filename{1} == '~') {
+
+				// /~user/path
+				if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray($filename)) {
+					$AbsoluteFilename = $ApacheLookupURIarray['filename'];
+				} else {
+					$AbsoluteFilename = realpath($filename);
+					if (@is_readable($AbsoluteFilename)) {
+						$this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.$filename.'", but the correct filename ('.$AbsoluteFilename.') seems to have been resolved with realpath($filename)', __FILE__, __LINE__);
+					} else {
+						return $this->ErrorImage('phpthumb_functions::ApacheLookupURIarray() failed for "'.$filename.'". This has been known to fail on Apache2 - try using the absolute filename for the source image (ex: "/home/user/httpdocs/image.jpg" instead of "/~user/image.jpg")');
+					}
+				}
+
+			} else {
+
+				// relative filename (any OS)
+				if (ereg('^'.preg_quote($this->config_document_root), $filename)) {
+					$AbsoluteFilename = $filename;
+					$this->DebugMessage('ResolveFilenameToAbsolute() NOT prepending $this->config_document_root ('.$this->config_document_root.') to $filename ('.$filename.') resulting in ($AbsoluteFilename = "'.$AbsoluteFilename.'")', __FILE__, __LINE__);
+				} else {
+					$AbsoluteFilename = $this->config_document_root.$filename;
+					$this->DebugMessage('ResolveFilenameToAbsolute() prepending $this->config_document_root ('.$this->config_document_root.') to $filename ('.$filename.') resulting in ($AbsoluteFilename = "'.$AbsoluteFilename.'")', __FILE__, __LINE__);
+				}
+
+			}
+
+		} else {
+
+			// relative to current directory (any OS)
+			$AbsoluteFilename = $this->config_document_root.dirname(@$_SERVER['PHP_SELF']).DIRECTORY_SEPARATOR.$filename;
+			//if (!@file_exists($AbsoluteFilename) && @file_exists(realpath($this->DotPadRelativeDirectoryPath($filename)))) {
+			//	$AbsoluteFilename = realpath($this->DotPadRelativeDirectoryPath($filename));
+			//}
+
+			if (substr(dirname(@$_SERVER['PHP_SELF']), 0, 2) == '/~') {
+				if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray(dirname(@$_SERVER['PHP_SELF']))) {
+					$AbsoluteFilename = $ApacheLookupURIarray['filename'].DIRECTORY_SEPARATOR.$filename;
+				} else {
+					$AbsoluteFilename = realpath('.').DIRECTORY_SEPARATOR.$filename;
+					if (@is_readable($AbsoluteFilename)) {
+						$this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'", but the correct filename ('.$AbsoluteFilename.') seems to have been resolved with realpath(.)/$filename', __FILE__, __LINE__);
+					} else {
+						return $this->ErrorImage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'". This has been known to fail on Apache2 - try using the absolute filename for the source image');
+					}
+				}
+			}
+
+		}
+		if (is_link($AbsoluteFilename)) {
+			$this->DebugMessage('is_link()==true, changing "'.$AbsoluteFilename.'" to "'.readlink($AbsoluteFilename).'"', __FILE__, __LINE__);
+			$AbsoluteFilename = readlink($AbsoluteFilename);
+		}
+		if (realpath($AbsoluteFilename)) {
+			$AbsoluteFilename = realpath($AbsoluteFilename);
+		}
+		if ($this->iswindows) {
+			$AbsoluteFilename = eregi_replace('^'.preg_quote(realpath($this->config_document_root)), realpath($this->config_document_root), $AbsoluteFilename);
+			$AbsoluteFilename = str_replace(DIRECTORY_SEPARATOR, '/', $AbsoluteFilename);
+		}
+		if (!$this->config_allow_src_above_docroot && !ereg('^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', realpath($this->config_document_root))), $AbsoluteFilename)) {
+			$this->DebugMessage('!$this->config_allow_src_above_docroot therefore setting "'.$AbsoluteFilename.'" (outside "'.realpath($this->config_document_root).'") to null', __FILE__, __LINE__);
+			return false;
+		}
+		if (!$this->config_allow_src_above_phpthumb && !ereg('^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', dirname(__FILE__))), $AbsoluteFilename)) {
+			$this->DebugMessage('!$this->config_allow_src_above_phpthumb therefore setting "'.$AbsoluteFilename.'" (outside "'.dirname(__FILE__).'") to null', __FILE__, __LINE__);
+			return false;
+		}
+		return $AbsoluteFilename;
+	}
+
+	function ImageMagickWhichConvert() {
+		static $WhichConvert = null;
+		if (is_null($WhichConvert)) {
+			if ($this->iswindows) {
+				$WhichConvert = false;
+			} else {
+				$WhichConvert = trim(phpthumb_functions::SafeExec('which convert'));
+			}
+		}
+		return $WhichConvert;
+	}
+
+	function ImageMagickCommandlineBase() {
+		static $commandline = null;
+		if (is_null($commandline)) {
+			$commandline = (!is_null($this->config_imagemagick_path) ? $this->config_imagemagick_path : '');
+
+			if ($this->config_imagemagick_path && ($this->config_imagemagick_path != realpath($this->config_imagemagick_path))) {
+				if (@is_executable(realpath($this->config_imagemagick_path))) {
+					$this->DebugMessage('Changing $this->config_imagemagick_path ('.$this->config_imagemagick_path.') to realpath($this->config_imagemagick_path) ('.realpath($this->config_imagemagick_path).')', __FILE__, __LINE__);
+					$this->config_imagemagick_path = realpath($this->config_imagemagick_path);
+				} else {
+					$this->DebugMessage('Leaving $this->config_imagemagick_path as ('.$this->config_imagemagick_path.') because !is_execuatable(realpath($this->config_imagemagick_path)) ('.realpath($this->config_imagemagick_path).')', __FILE__, __LINE__);
+				}
+			}
+			$this->DebugMessage('  file_exists('.$this->config_imagemagick_path.') = '.intval(  @file_exists($this->config_imagemagick_path)), __FILE__, __LINE__);
+			$this->DebugMessage('is_executable('.$this->config_imagemagick_path.') = '.intval(@is_executable($this->config_imagemagick_path)), __FILE__, __LINE__);
+			if (@file_exists($this->config_imagemagick_path)) {
+				$this->DebugMessage('using ImageMagick path from $this->config_imagemagick_path ('.$this->config_imagemagick_path.')', __FILE__, __LINE__);
+				if ($this->iswindows) {
+					$commandline = substr($this->config_imagemagick_path, 0, 2).' && cd "'.str_replace('/', DIRECTORY_SEPARATOR, substr(dirname($this->config_imagemagick_path), 2)).'" && '.basename($this->config_imagemagick_path);
+				} else {
+					$commandline = '"'.$this->config_imagemagick_path.'"';
+				}
+				return $commandline;
+			}
+
+			$which_convert = $this->ImageMagickWhichConvert();
+			$IMversion     = $this->ImageMagickVersion();
+
+			if ($which_convert && ($which_convert{0} == '/') && @file_exists($which_convert)) {
+
+				// `which convert` *should* return the path if "convert" exist, or nothing if it doesn't
+				// other things *may* get returned, like "sh: convert: not found" or "no convert in /usr/local/bin /usr/sbin /usr/bin /usr/ccs/bin"
+				// so only do this if the value returned exists as a file
+				$this->DebugMessage('using ImageMagick path from `which convert` ('.$which_convert.')', __FILE__, __LINE__);
+				$commandline = 'convert';
+
+			} elseif ($IMversion) {
+
+				$this->DebugMessage('setting ImageMagick path to $this->config_imagemagick_path ('.$this->config_imagemagick_path.') ['.$IMversion.']', __FILE__, __LINE__);
+				$commandline = $this->config_imagemagick_path;
+
+			} else {
+
+				$this->DebugMessage('ImageMagickThumbnailToGD() aborting because cannot find convert in $this->config_imagemagick_path ('.$this->config_imagemagick_path.'), and `which convert` returned ('.$which_convert.')', __FILE__, __LINE__);
+				$commandline = '';
+
+			}
+		}
+		return $commandline;
+	}
+
+	function ImageMagickVersion($returnRAW=false) {
+		static $versionstring = null;
+		if (is_null($versionstring)) {
+			$commandline = $this->ImageMagickCommandlineBase();
+			$commandline = (!is_null($commandline) ? $commandline : '');
+
+			$versionstring = array(0=>'', 1=>'');
+			if ($commandline) {
+				$commandline .= ' --version';
+				$this->DebugMessage('ImageMagick version checked with "'.$commandline.'"', __FILE__, __LINE__);
+				$versionstring[1] = trim(phpthumb_functions::SafeExec($commandline));
+				if (eregi('^Version: [^0-9]*([ 0-9\\.\\:Q/]+) (http|file)\:', $versionstring[1], $matches)) {
+					$versionstring[0] = $matches[1];
+				} else {
+					$versionstring[0] = false;
+					$this->DebugMessage('ImageMagick did not return recognized version string ('.$versionstring[1].')', __FILE__, __LINE__);
+				}
+				$this->DebugMessage('ImageMagick convert --version says "'.$matches[0].'"', __FILE__, __LINE__);
+			}
+		}
+		return @$versionstring[intval($returnRAW)];
+	}
+
+	function ImageMagickSwitchAvailable($switchname) {
+		static $IMoptions = null;
+		if (is_null($IMoptions)) {
+			$IMoptions = array();
+			$commandline = $this->ImageMagickCommandlineBase();
+			if (!is_null($commandline)) {
+				$commandline .= ' -help';
+				$IMhelp_lines = explode("\n", phpthumb_functions::SafeExec($commandline));
+				foreach ($IMhelp_lines as $line) {
+					if (ereg('^[\+\-]([a-z\-]+) ', trim($line), $matches)) {
+						$IMoptions[$matches[1]] = true;
+					}
+				}
+			}
+		}
+		if (is_array($switchname)) {
+			$allOK = true;
+			foreach ($switchname as $key => $value) {
+				if (!isset($IMoptions[$value])) {
+					$allOK = false;
+					break;
+				}
+			}
+			$this->DebugMessage('ImageMagickSwitchAvailable('.implode(';', $switchname).') = '.intval($allOK).'', __FILE__, __LINE__);
+		} else {
+			$allOK = isset($IMoptions[$switchname]);
+			$this->DebugMessage('ImageMagickSwitchAvailable('.$switchname.') = '.intval($allOK).'', __FILE__, __LINE__);
+		}
+		return $allOK;
+	}
+
+	function ImageMagickFormatsList() {
+		static $IMformatsList = null;
+		if (is_null($IMformatsList)) {
+			$IMformatsList = '';
+			$commandline = $this->ImageMagickCommandlineBase();
+			if (!is_null($commandline)) {
+				$commandline = dirname($commandline).DIRECTORY_SEPARATOR.str_replace('convert', 'identify', basename($commandline));
+				$commandline .= ' -list format';
+				$IMformatsList = phpthumb_functions::SafeExec($commandline);
+			}
+		}
+		return $IMformatsList;
+	}
+
+	function ImageMagickThumbnailToGD() {
+		// http://www.imagemagick.org/script/command-line-options.php
+
+		$this->useRawIMoutput = true;
+		if (phpthumb_functions::gd_version()) {
+			//$UnAllowedParameters = array('sx', 'sy', 'sw', 'sh', 'xto', 'ra', 'ar', 'bg', 'bc', 'fltr');
+			$UnAllowedParameters = array('xto', 'ra', 'ar', 'bg', 'bc', 'fltr');
+			foreach ($UnAllowedParameters as $parameter) {
+				if (isset($this->$parameter)) {
+					$this->DebugMessage('$this->useRawIMoutput=false because "'.$parameter.'" is set', __FILE__, __LINE__);
+					$this->useRawIMoutput = false;
+					break;
+				}
+			}
+		}
+		$outputFormat = $this->thumbnailFormat;
+		if (phpthumb_functions::gd_version()) {
+			if ($this->useRawIMoutput) {
+				switch ($this->thumbnailFormat) {
+					case 'gif':
+						$ImageCreateFunction = 'ImageCreateFromGIF';
+						$this->is_alpha = true;
+						break;
+					case 'png':
+						$ImageCreateFunction = 'ImageCreateFromPNG';
+						$this->is_alpha = true;
+						break;
+					case 'jpg':
+					case 'jpeg':
+						$ImageCreateFunction = 'ImageCreateFromJPEG';
+						break;
+					default:
+						$outputFormat = 'png';
+						$ImageCreateFunction = 'ImageCreateFromPNG';
+						$this->is_alpha = true;
+						$this->useRawIMoutput = false;
+						break;
+				}
+				if (!function_exists(@$ImageCreateFunction)) {
+					// ImageMagickThumbnailToGD() depends on ImageCreateFromPNG/ImageCreateFromGIF
+					//$this->DebugMessage('ImageMagickThumbnailToGD() aborting because '.@$ImageCreateFunction.'() is not available', __FILE__, __LINE__);
+					$this->useRawIMoutput = true;
+					//return false;
+				}
+			} else {
+				$outputFormat = 'png';
+				$ImageCreateFunction = 'ImageCreateFromPNG';
+				$this->is_alpha = true;
+				$this->useRawIMoutput = false;
+			}
+		}
+
+		// http://freealter.org/doc_distrib/ImageMagick-5.1.1/www/convert.html
+		if (!$this->sourceFilename) {
+			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because $this->sourceFilename is empty', __FILE__, __LINE__);
+			$this->useRawIMoutput = false;
+			return false;
+		}
+		if (ini_get('safe_mode')) {
+			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because safe_mode is enabled', __FILE__, __LINE__);
+			$this->useRawIMoutput = false;
+			return false;
+		}
+
+		$commandline = $this->ImageMagickCommandlineBase();
+		if ($commandline) {
+			if ($IMtempfilename = $this->phpThumb_tempnam()) {
+				$IMtempfilename = realpath($IMtempfilename);
+
+				$IMuseExplicitImageOutputDimensions = false;
+				if ($this->ImageMagickSwitchAvailable('thumbnail') && $this->config_imagemagick_use_thumbnail) {
+					$IMresizeParameter = 'thumbnail';
+				} else {
+					$IMresizeParameter = 'resize';
+
+					// some (older? around 2002) versions of IM won't accept "-resize 100x" but require "-resize 100x100"
+					$commandline_test = $this->ImageMagickCommandlineBase().' logo: -resize 1x "'.$IMtempfilename.'" 2>&1';
+					$IMresult_test = phpthumb_functions::SafeExec($commandline_test);
+					$IMuseExplicitImageOutputDimensions = eregi('image dimensions are zero', $IMresult_test);
+					$this->DebugMessage('IMuseExplicitImageOutputDimensions = '.intval($IMuseExplicitImageOutputDimensions), __FILE__, __LINE__);
+					if ($fp_im_temp = @fopen($IMtempfilename, 'wb')) {
+						// erase temp image so ImageMagick logo doesn't get output if other processing fails
+						fclose($fp_im_temp);
+					}
+				}
+
+
+				if (!is_null($this->dpi) && $this->ImageMagickSwitchAvailable('modulate')) {
+					// for raster source formats only (WMF, PDF, etc)
+					$commandline .= ' -density '.$this->dpi;
+				}
+				ob_start();
+				$getimagesize = GetImageSize($this->sourceFilename);
+				$GetImageSizeError = ob_get_contents();
+				ob_end_clean();
+				if (is_array($getimagesize)) {
+					$this->DebugMessage('GetImageSize('.$this->sourceFilename.') SUCCEEDED: '.serialize($getimagesize), __FILE__, __LINE__);
+				} else {
+					$this->DebugMessage('GetImageSize('.$this->sourceFilename.') FAILED with error "'.$GetImageSizeError.'"', __FILE__, __LINE__);
+				}
+				if (is_array($getimagesize)) {
+					$this->DebugMessage('GetImageSize('.$this->sourceFilename.') returned [w='.$getimagesize[0].';h='.$getimagesize[1].';f='.$getimagesize[2].']', __FILE__, __LINE__);
+					$this->source_width  = $getimagesize[0];
+					$this->source_height = $getimagesize[1];
+					$this->DebugMessage('source dimensions set to '.$this->source_width.'x'.$this->source_height, __FILE__, __LINE__);
+					$this->SetOrientationDependantWidthHeight();
+
+					if (!eregi('('.implode('|', $this->AlphaCapableFormats).')', $outputFormat)) {
+						// not a transparency-capable format
+						$commandline .= ' -background "#'.($this->bg ? $this->bg : 'FFFFFF').'"';
+						if ($getimagesize[2] == 1) {
+							$commandline .= ' -flatten';
+						}
+					}
+					if ($getimagesize[2] == 1) {
+						$commandline .= ' -coalesce'; // may be needed for animated GIFs
+					}
+					if ($this->source_width || $this->source_height) {
+						if ($this->zc) {
+
+							$borderThickness = 0;
+							if (!empty($this->fltr)) {
+								foreach ($this->fltr as $key => $value) {
+									if (ereg('^bord\|([0-9]+)', $value, $matches)) {
+										$borderThickness = $matches[1];
+										break;
+									}
+								}
+							}
+							$wAll = intval(max($this->w, $this->wp, $this->wl, $this->ws)) - (2 * $borderThickness);
+							$hAll = intval(max($this->h, $this->hp, $this->hl, $this->hs)) - (2 * $borderThickness);
+							$imAR = $this->source_width / $this->source_height;
+							//$zcAR = (($wAll && $hAll) ? $wAll / $hAll : $imAR);
+							$zcAR = (($wAll && $hAll) ? $wAll / $hAll : 1);
+//echo '<pre>';
+//var_dump($wAll);
+//var_dump($hAll);
+//var_dump($zcAR);
+							//if (($wAll > $borderThickness) && ($wAll > $borderThickness)) {
+							//	$zcAR = ($wAll - (2 * $borderThickness)) / ($hAll - (2 * $borderThickness));
+							//}
+//echo ($wAll - (2 * $borderThickness))."\n";
+//echo ($hAll - (2 * $borderThickness))."\n";
+//var_dump($zcAR);
+							$side  = phpthumb_functions::nonempty_min($this->source_width, $this->source_height, max($wAll, $hAll));
+							$sideX = phpthumb_functions::nonempty_min($this->source_width,                       $wAll, round($hAll * $zcAR));
+							$sideY = phpthumb_functions::nonempty_min(                     $this->source_height, $hAll, round($wAll / $zcAR));
+
+							$thumbnailH = round(max($sideY, ($sideY * $zcAR) / $imAR));
+							if ($IMuseExplicitImageOutputDimensions) {
+								$commandline .= ' -'.$IMresizeParameter.' '.$thumbnailH.'x'.$thumbnailH;
+							} else {
+								$commandline .= ' -'.$IMresizeParameter.' x'.$thumbnailH;
+							}
+//echo '<pre>';
+//var_dump($this->w);
+//var_dump($this->wp);
+//var_dump($this->wl);
+//var_dump($this->ws);
+//var_dump($wAll);
+//var_dump($side);
+//var_dump($sideX);
+//var_dump($sideY);
+//var_dump($zcAR);
+//var_dump($thumbnailH);
+//print_r($getimagesize);
+//echo '</pre>';
+
+							$commandline .= ' -gravity center';
+
+							if (($wAll > 0) && ($hAll > 0)) {
+								$commandline .= ' -crop '.$wAll.'x'.$hAll.'+0+0';
+							} else {
+								$commandline .= ' -crop '.$side.'x'.$side.'+0+0';
+							}
+							if ($this->ImageMagickSwitchAvailable('repage')) {
+								$commandline .= ' +repage';
+							} else {
+								$this->DebugMessage('Skipping "+repage" because ImageMagick (v'.$this->ImageMagickVersion().') does not support it', __FILE__, __LINE__);
+							}
+
+						} elseif ($this->sw || $this->sh || $this->sx || $this->sy) {
+
+							$commandline .= ' -crop '.($this->sw ? $this->sw : $this->source_width).'x'.($this->sh ? $this->sh : $this->source_height).'+'.$this->sx.'+'.$this->sy;
+							// this is broken for aoe=1, but unsure how to fix. Send advice to info@silisoftware.com
+							if ($this->w || $this->h) {
+								if ($this->ImageMagickSwitchAvailable('repage')) {
+									$commandline .= ' -repage';
+								} else {
+									$this->DebugMessage('Skipping "-repage" because ImageMagick (v'.$this->ImageMagickVersion().') does not support it', __FILE__, __LINE__);
+								}
+								if ($IMuseExplicitImageOutputDimensions) {
+									if ($this->w && !$this->h) {
+										$this->h = ceil($this->w / ($this->source_width / $this->source_height));
+									} elseif ($this->h && !$this->w) {
+										$this->w = ceil($this->h * ($this->source_width / $this->source_height));
+									}
+								}
+								$commandline .= ' -'.$IMresizeParameter.' '.$this->w.'x'.$this->h;
+							}
+
+						} else {
+
+							if ($this->iar && (intval($this->w) > 0) && (intval($this->h) > 0)) {
+								$commandline .= ' -'.$IMresizeParameter.' '.$this->w.'x'.$this->h.'!';
+							} else {
+//echo '<pre>';
+//print_r($getimagesize);
+//echo '</pre>';
+//echo $this->w.'x'.$this->h.'<br>';
+								$this->w = ((($this->aoe || $this->far) && $this->w) ? $this->w : ($this->w ? phpthumb_functions::nonempty_min($this->w, $getimagesize[0]) : ''));
+								$this->h = ((($this->aoe || $this->far) && $this->h) ? $this->h : ($this->h ? phpthumb_functions::nonempty_min($this->h, $getimagesize[1]) : ''));
+//echo $this->w.'x'.$this->h.'<br>';
+								if ($this->w || $this->h) {
+									if ($IMuseExplicitImageOutputDimensions) {
+										if ($this->w && !$this->h) {
+											$this->h = ceil($this->w / ($this->source_width / $this->source_height));
+										} elseif ($this->h && !$this->w) {
+											$this->w = ceil($this->h * ($this->source_width / $this->source_height));
+										}
+									}
+									$commandline .= ' -'.$IMresizeParameter.' '.$this->w.'x'.$this->h;
+								}
+							}
+						}
+					}
+
+				} else {
+
+					$this->DebugMessage('GetImageSize('.$this->sourceFilename.') failed', __FILE__, __LINE__);
+					if ($this->w || $this->h) {
+						if ($IMuseExplicitImageOutputDimensions) {
+							// unknown source aspect ration, just put large number and hope IM figures it out
+							$commandline .= ' -'.$IMresizeParameter.' '.($this->w ? $this->w : '9999').'x'.($this->h ? $this->h : '9999');
+						} else {
+							$commandline .= ' -'.$IMresizeParameter.' '.$this->w.'x'.$this->h;
+						}
+						if ($this->iar && (intval($this->w) > 0) && (intval($this->h) > 0)) {
+							$commandline .= '!';
+						}
+					}
+
+				}
+				foreach ($this->fltr as $filterkey => $filtercommand) {
+					@list($command, $parameter) = explode('|', $filtercommand, 2);
+					switch ($command) {
+						case 'brit':
+							if ($this->ImageMagickSwitchAvailable('modulate')) {
+								$commandline .= ' -modulate '.(100 + $parameter).',100,100';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'cont':
+							if ($this->ImageMagickSwitchAvailable('contrast')) {
+								$contDiv10 = round($parameter / 10);
+								if ($contDiv10 > 0) {
+									for ($i = 0; $i < $contDiv10; $i++) {
+										$commandline .= ' -contrast'; // increase contrast by 10%
+									}
+								} elseif ($contDiv10 < 0) {
+									for ($i = $contDiv10; $i < 0; $i++) {
+										$commandline .= ' +contrast'; // decrease contrast by 10%
+									}
+								} else {
+									// do nothing
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'ds':
+							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
+								if ($parameter == 100) {
+									$commandline .= ' -colorspace GRAY -modulate 100,0,100';
+								} else {
+									$commandline .= ' -modulate 100,'.(100 - $parameter).',100';
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'sat':
+							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
+								if ($parameter == -100) {
+									$commandline .= ' -colorspace GRAY -modulate 100,0,100';
+								} else {
+									$commandline .= ' -modulate 100,'.(100 + $parameter).',100';
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'gray':
+							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
+								$commandline .= ' -colorspace GRAY -modulate 100,0,100';
+								//$commandline .= ' -colorspace GRAY';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'clr':
+							if ($this->ImageMagickSwitchAvailable(array('fill', 'colorize'))) {
+								@list($amount, $color) = explode('|', $parameter);
+								$commandline .= ' -fill #'.$color.' -colorize '.$amount;
+							}
+							break;
+
+						case 'sep':
+							if ($this->ImageMagickSwitchAvailable('sepia-tone')) {
+								@list($amount, $color) = explode('|', $parameter);
+								$amount = ($amount ? $amount : 80);
+								if (!$color) {
+									$commandline .= ' -sepia-tone '.$amount.'%';
+									unset($this->fltr[$filterkey]);
+								}
+							}
+							break;
+
+						case 'gam':
+							if ($this->ImageMagickSwitchAvailable('gamma')) {
+								$commandline .= ' -gamma '.$parameter;
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'neg':
+							if ($this->ImageMagickSwitchAvailable('negate')) {
+								$commandline .= ' -negate';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'th':
+							if ($this->ImageMagickSwitchAvailable(array('threshold', 'dither', 'monochrome'))) {
+								$commandline .= ' -threshold '.round($parameter / 2.55).'% -dither -monochrome';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'rcd':
+							if ($this->ImageMagickSwitchAvailable(array('colors', 'dither'))) {
+								@list($colors, $dither) = explode('|', $parameter);
+								$colors = ($colors                ?  (int) $colors : 256);
+								$dither  = ((strlen($dither) > 0) ? (bool) $dither : true);
+								$commandline .= ' -colors '.max($colors, 8); // ImageMagick will otherwise fail with "cannot quantize to fewer than 8 colors"
+								$commandline .= ($dither ? ' -dither' : ' +dither');
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'flip':
+							if ($this->ImageMagickSwitchAvailable(array('flip', 'flop'))) {
+								if (strpos(strtolower($parameter), 'x') !== false) {
+									$commandline .= ' -flop';
+								}
+								if (strpos(strtolower($parameter), 'y') !== false) {
+									$commandline .= ' -flip';
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'edge':
+							if ($this->ImageMagickSwitchAvailable('edge')) {
+								$parameter = ($parameter ? $parameter : 2);
+								$commandline .= ' -edge '.($parameter ? $parameter : 1);
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'emb':
+							if ($this->ImageMagickSwitchAvailable(array('emboss', 'negate'))) {
+								$parameter = ($parameter ? $parameter : 2);
+								$commandline .= ' -emboss '.$parameter;
+								if ($parameter < 2) {
+									$commandline .= ' -negate'; // ImageMagick negates the image for some reason with '-emboss 1';
+								}
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'lvl':
+							@list($band, $method, $threshold) = explode('|', $parameter);
+							$band      = ($band ? ereg_replace('[^RGBA\\*]', '', strtoupper($band)) : '*');
+							$method    = ((strlen($method) > 0)    ? intval($method)                :   2);
+							$threshold = ((strlen($threshold) > 0) ? floatval($threshold)           : 0.1);
+
+							$band = ereg_replace('[^RGBA\\*]', '', strtoupper($band));
+
+							if (($method > 1) && !$this->ImageMagickSwitchAvailable(array('channel', 'contrast-stretch'))) {
+								// Because ImageMagick processing happens before PHP-GD filters, and because some
+								// clipping is involved in the "lvl" filter, if "lvl" happens before "wb" then the
+								// "wb" filter will have (almost) no effect. Therefore, if "wb" is enabled then
+								// force the "lvl" filter to be processed by GD, not ImageMagick.
+								foreach ($this->fltr as $fltr_key => $fltr_value) {
+									list($fltr_cmd) = explode('|', $fltr_value);
+									if ($fltr_cmd == 'wb') {
+										$this->DebugMessage('Setting "lvl" filter method to "0" (from "'.$method.'") because white-balance filter also enabled', __FILE__, __LINE__);
+										$method = 0;
+									}
+								}
+							}
+
+							switch ($method) {
+								case 0: // internal RGB
+								case 1: // internal grayscale
+									break;
+								case 2: // ImageMagick "contrast-stretch"
+									if ($this->ImageMagickSwitchAvailable('contrast-stretch')) {
+										$thiscommand = ' -contrast-stretch '.$threshold.'%';
+										$commandline .= (($band == '*') ? $thiscommand : ' -channel '.strtoupper($band).$thiscommand.' +channel');
+										unset($this->fltr[$filterkey]);
+									}
+									break;
+								case 3: // ImageMagick "normalize"
+									if ($this->ImageMagickSwitchAvailable('normalize')) {
+										$thiscommand = ' -normalize';
+										$commandline .= (($band == '*') ? $thiscommand : ' -channel '.strtoupper($band).$thiscommand.' +channel');
+										unset($this->fltr[$filterkey]);
+									}
+									break;
+								default:
+									$this->DebugMessage('unsupported method ('.$method.') for "lvl" filter', __FILE__, __LINE__);
+									break;
+							}
+							if (isset($this->fltr[$filterkey]) && ($method > 1)) {
+								$this->fltr[$filterkey] = $command.'|'.$band.'|0|'.$threshold;
+								$this->DebugMessage('filter "lvl" remapped from method "'.$method.'" to method "0" because ImageMagick support is missing', __FILE__, __LINE__);
+							}
+							break;
+
+						case 'wb':
+							if ($this->ImageMagickSwitchAvailable(array('channel', 'contrast-stretch'))) {
+								@list($threshold) = explode('|', $parameter);
+								$threshold = (is_float($threshold) ? $threshold : 0.1);
+								$commandline .= ' -channel R -contrast-stretch '.$threshold.'%';
+								$commandline .= ' -channel G -contrast-stretch '.$threshold.'%';
+								$commandline .= ' -channel B -contrast-stretch '.$threshold.'%';
+								$commandline .= ' +channel';
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'blur':
+							if ($this->ImageMagickSwitchAvailable('blur')) {
+								@list($radius) = explode('|', $parameter);
+								$radius = ($radius ? $radius : 1);
+								$commandline .= ' -blur '.$radius;
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'gblr':
+							if ($this->ImageMagickSwitchAvailable('gaussian')) {
+								@list($radius) = explode('|', $parameter);
+								$radius = ($radius ? $radius : 1);
+								$commandline .= ' -gaussian '.$radius;
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'usm':
+							if ($this->ImageMagickSwitchAvailable('unsharp')) {
+								@list($amount, $radius, $threshold) = explode('|', $parameter);
+								$amount    = ($amount            ? $amount    : 80);
+								$radius    = ($radius            ? $radius    : 0.5);
+								$threshold = (strlen($threshold) ? $threshold : 3);
+								$commandline .= ' -unsharp '.number_format(($radius * 2) - 1, 2).'x1+'.number_format($amount / 100, 2).'+'.number_format($threshold / 100, 2);
+								unset($this->fltr[$filterkey]);
+							}
+							break;
+
+						case 'bord':
+							if ($this->ImageMagickSwitchAvailable(array('border', 'bordercolor', 'thumbnail', 'crop'))) {
+								if (!$this->zc) {
+									@list($width, $rX, $rY, $color) = explode('|', $parameter);
+									if ($width && !$rX && !$rY) {
+										if (!phpthumb_functions::IsHexColor($color)) {
+											$color = ($this->bc ? $this->bc : '000000');
+										}
+										$commandline .= ' -border '.$width.' -bordercolor "#'.$color.'"';
+										if (ereg(' \-crop ([0-9]+)x([0-9]+)\+0\+0 ', $commandline, $matches)) {
+											$commandline = str_replace(' -crop '.$matches[1].'x'.$matches[2].'+0+0 ', ' -crop '.($matches[1] - (2 * $width)).'x'.($matches[2] - (2 * $width)).'+0+0 ', $commandline);
+										} elseif (ereg(' \-'.$IMresizeParameter.' ([0-9]+)x([0-9]+) ', $commandline, $matches)) {
+											$commandline = str_replace(' -'.$IMresizeParameter.' '.$matches[1].'x'.$matches[2].' ', ' -'.$IMresizeParameter.' '.($matches[1] - (2 * $width)).'x'.($matches[2] - (2 * $width)).' ', $commandline);
+										}
+										unset($this->fltr[$filterkey]);
+									}
+								}
+							}
+							break;
+
+						case 'crop':
+							break;
+
+						case 'sblr':
+							break;
+
+						case 'mean':
+							break;
+
+						case 'smth':
+							break;
+
+						case 'bvl':
+							break;
+
+						case 'wmi':
+							break;
+
+						case 'wmt':
+							break;
+
+						case 'over':
+							break;
+
+						case 'hist':
+							break;
+
+						case 'fram':
+							break;
+
+						case 'drop':
+							break;
+
+						case 'mask':
+							break;
+
+						case 'elip':
+							break;
+
+						case 'ric':
+							break;
+
+					}
+					if (!isset($this->fltr[$filterkey])) {
+						$this->DebugMessage('Processed $this->fltr['.$filterkey.'] ('.$filtercommand.') with ImageMagick', __FILE__, __LINE__);
+					} else {
+						$this->DebugMessage('Skipping $this->fltr['.$filterkey.'] ('.$filtercommand.') with ImageMagick', __FILE__, __LINE__);
+					}
+				}
+				$this->DebugMessage('Remaining $this->fltr after ImageMagick: ('.$this->phpThumbDebugVarDump($this->fltr).')', __FILE__, __LINE__);
+
+				if (eregi('jpe?g', $outputFormat) && $this->q) {
+					if ($this->ImageMagickSwitchAvailable(array('quality', 'interlace'))) {
+						$commandline .= ' -quality '.$this->thumbnailQuality;
+						if ($this->config_output_interlace) {
+							// causes weird things with animated GIF... leave for JPEG only
+							$commandline .= ' -interlace line '; // Use Line or Plane to create an interlaced PNG or GIF or progressive JPEG image
+						}
+					}
+				}
+				$commandline .= ' "'.str_replace('/', DIRECTORY_SEPARATOR, $this->sourceFilename).(($outputFormat == 'gif') ? '' : '['.intval($this->sfn).']').'"'; // [0] means first frame of (GIF) animation, can be ignored
+				$commandline .= ' '.$outputFormat.':"'.$IMtempfilename.'"';
+				if (!$this->iswindows) {
+					$commandline .= ' 2>&1';
+				}
+				$this->DebugMessage('ImageMagick called as ('.$commandline.')', __FILE__, __LINE__);
+				$IMresult = phpthumb_functions::SafeExec($commandline);
+				clearstatcache();
+				if (!@file_exists($IMtempfilename) || !@filesize($IMtempfilename)) {
+					$this->fatalerror = 'ImageMagick failed with message ('.trim($IMresult).')';
+					$this->DebugMessage('ImageMagick failed with message ('.trim($IMresult).')', __FILE__, __LINE__);
+					if ($this->iswindows && !$IMresult) {
+						$this->DebugMessage('Check to make sure that PHP has read+write permissions to "'.dirname($IMtempfilename).'"', __FILE__, __LINE__);
+					}
+
+				} else {
+
+					$this->IMresizedData = file_get_contents($IMtempfilename);
+					$getimagesize_imresized = @GetImageSize($IMtempfilename);
+					if (($this->config_max_source_pixels > 0) && (($getimagesize_imresized[0] * $getimagesize_imresized[1]) > $this->config_max_source_pixels)) {
+						$this->DebugMessage('skipping ImageMagickThumbnailToGD::'.$ImageCreateFunction.'() because IM output is too large ('.$getimagesize_imresized[0].'x'.$getimagesize_imresized[0].' = '.($getimagesize_imresized[0] * $getimagesize_imresized[1]).' > '.$this->config_max_source_pixels.')', __FILE__, __LINE__);
+					} elseif (function_exists(@$ImageCreateFunction) && ($this->gdimg_source = @$ImageCreateFunction($IMtempfilename))) {
+//header('Content-Type: image/png');
+//ImageSaveAlpha($this->gdimg_source, true);
+//ImagePNG($this->gdimg_source);
+//exit;
+						$this->source_width  = ImageSX($this->gdimg_source);
+						$this->source_height = ImageSY($this->gdimg_source);
+						$this->DebugMessage('ImageMagickThumbnailToGD::'.$ImageCreateFunction.'() succeeded, $this->gdimg_source is now ('.$this->source_width.'x'.$this->source_height.')', __FILE__, __LINE__);
+						$this->DebugMessage('ImageMagickThumbnailToGD() returning $IMresizedData ('.strlen($this->IMresizedData).' bytes)', __FILE__, __LINE__);
+					} else {
+						$this->useRawIMoutput = true;
+						$this->DebugMessage('$this->useRawIMoutput set to TRUE because '.@$ImageCreateFunction.'('.$IMtempfilename.') failed', __FILE__, __LINE__);
+					}
+					@unlink($IMtempfilename);
+					return true;
+
+				}
+				unlink($IMtempfilename);
+
+			} else {
+				$this->DebugMessage('ImageMagickThumbnailToGD() aborting, phpThumb_tempnam() failed', __FILE__, __LINE__);
+			}
+		} else {
+			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because ImageMagickCommandlineBase() failed', __FILE__, __LINE__);
+		}
+		$this->useRawIMoutput = false;
+		return false;
+	}
+
+
+	function Rotate() {
+		if ($this->ra || $this->ar) {
+			if (!function_exists('ImageRotate')) {
+				$this->DebugMessage('!function_exists(ImageRotate)', __FILE__, __LINE__);
+				return false;
+			}
+			if (!include_once(dirname(__FILE__).'/phpthumb.filters.php')) {
+				$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.filters.php" which is required for applying filters ('.implode(';', $this->fltr).')', __FILE__, __LINE__);
+				return false;
+			}
+
+			$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
+			if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
+				return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
+			}
+
+			$rotate_angle = 0;
+			if ($this->ra) {
+
+				$rotate_angle = floatval($this->ra);
+
+			} else {
+
+				if ($this->ar == 'x') {
+					if (phpthumb_functions::version_compare_replacement(phpversion(), '4.2.0', '>=')) {
+						if ($this->sourceFilename) {
+							if (function_exists('exif_read_data')) {
+								if ($exif_data = @exif_read_data($this->sourceFilename, 'IFD0')) {
+									// http://sylvana.net/jpegcrop/exif_orientation.html
+									switch (@$exif_data['Orientation']) {
+										case 1:
+											$rotate_angle = 0;
+											break;
+										case 3:
+											$rotate_angle = 180;
+											break;
+										case 6:
+											$rotate_angle = 270;
+											break;
+										case 8:
+											$rotate_angle = 90;
+											break;
+
+										default:
+											$this->DebugMessage('EXIF auto-rotate failed because unknown $exif_data[Orientation] "'.@$exif_data['Orientation'].'"', __FILE__, __LINE__);
+											return false;
+											break;
+									}
+									$this->DebugMessage('EXIF auto-rotate set to '.$rotate_angle.' degrees ($exif_data[Orientation] = "'.@$exif_data['Orientation'].'")', __FILE__, __LINE__);
+								} else {
+									$this->DebugMessage('failed: exif_read_data('.$this->sourceFilename.')', __FILE__, __LINE__);
+									return false;
+								}
+							} else {
+								$this->DebugMessage('!function_exists(exif_read_data)', __FILE__, __LINE__);
+								return false;
+							}
+						} else {
+							$this->DebugMessage('Cannot auto-rotate from EXIF data because $this->sourceFilename is empty', __FILE__, __LINE__);
+							return false;
+						}
+					} else {
+						$this->DebugMessage('Cannot auto-rotate from EXIF data because PHP is less than v4.2.0 ('.phpversion().')', __FILE__, __LINE__);
+						return false;
+					}
+				} elseif (($this->ar == 'l') && ($this->source_height > $this->source_width)) {
+					$rotate_angle = 270;
+				} elseif (($this->ar == 'L') && ($this->source_height > $this->source_width)) {
+					$rotate_angle = 90;
+				} elseif (($this->ar == 'p') && ($this->source_width > $this->source_height)) {
+					$rotate_angle = 90;
+				} elseif (($this->ar == 'P') && ($this->source_width > $this->source_height)) {
+					$rotate_angle = 270;
+				}
+
+			}
+			if ($rotate_angle % 90) {
+				$this->is_alpha = true;
+			}
+			phpthumb_filters::ImprovedImageRotate($this->gdimg_source, $rotate_angle, $this->config_background_hexcolor, $this->bg);
+			$this->source_width  = ImageSX($this->gdimg_source);
+			$this->source_height = ImageSY($this->gdimg_source);
+		}
+		return true;
+	}
+
+
+	function FixedAspectRatio() {
+		// optional fixed-dimension images (regardless of aspect ratio)
+
+		if (!$this->far) {
+			// do nothing
+			return true;
+		}
+
+		if (!$this->w || !$this->h) {
+			return false;
+		}
+		$this->thumbnail_width  = $this->w;
+		$this->thumbnail_height = $this->h;
+		$this->is_alpha = true;
+		if ($this->thumbnail_image_width >= $this->thumbnail_width) {
+
+			if ($this->w) {
+				$aspectratio = $this->thumbnail_image_height / $this->thumbnail_image_width;
+				$this->thumbnail_image_height = round($this->thumbnail_image_width * $aspectratio);
+				$this->thumbnail_height = ($this->h ? $this->h : $this->thumbnail_image_height);
+			} elseif ($this->thumbnail_image_height < $this->thumbnail_height) {
+				$this->thumbnail_image_height = $this->thumbnail_height;
+				$this->thumbnail_image_width  = round($this->thumbnail_image_height / $aspectratio);
+			}
+
+		} else {
+			if ($this->h) {
+				$aspectratio = $this->thumbnail_image_width / $this->thumbnail_image_height;
+				$this->thumbnail_image_width = round($this->thumbnail_image_height * $aspectratio);
+			} elseif ($this->thumbnail_image_width < $this->thumbnail_width) {
+				$this->thumbnail_image_width = $this->thumbnail_width;
+				$this->thumbnail_image_height  = round($this->thumbnail_image_width / $aspectratio);
+			}
+
+		}
+		return true;
+	}
+
+
+	function AntiOffsiteLinking() {
+		// Optional anti-offsite hijacking of the thumbnail script
+		$allow = true;
+		if ($allow && $this->config_nooffsitelink_enabled && (@$_SERVER['HTTP_REFERER'] || $this->config_nooffsitelink_require_refer)) {
+			$this->DebugMessage('AntiOffsiteLinking() checking $_SERVER[HTTP_REFERER] "'.@$_SERVER['HTTP_REFERER'].'"', __FILE__, __LINE__);
+			foreach ($this->config_nooffsitelink_valid_domains as $key => $valid_domain) {
+				// $_SERVER['HTTP_HOST'] contains the port number, so strip it out here to make default configuration work
+				list($clean_domain) = explode(':', $valid_domain);
+				$this->config_nooffsitelink_valid_domains[$key] = $clean_domain;
+			}
+			$parsed_url = parse_url(@$_SERVER['HTTP_REFERER']);
+			if (!phpthumb_functions::CaseInsensitiveInArray(@$parsed_url['host'], $this->config_nooffsitelink_valid_domains)) {
+				$allow = false;
+				$erase   = $this->config_nooffsitelink_erase_image;
+				$message = $this->config_nooffsitelink_text_message;
+$this->ErrorImage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is NOT in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')');
+exit;
+				$this->DebugMessage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is NOT in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__);
+			} else {
+				$this->DebugMessage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__);
+			}
+		}
+
+		if ($allow && $this->config_nohotlink_enabled && eregi('^(f|ht)tps?\://', $this->src)) {
+			$parsed_url = parse_url($this->src);
+			if (!phpthumb_functions::CaseInsensitiveInArray(@$parsed_url['host'], $this->config_nohotlink_valid_domains)) {
+				// This domain is not allowed
+				$allow = false;
+				$erase   = $this->config_nohotlink_erase_image;
+				$message = $this->config_nohotlink_text_message;
+				$this->DebugMessage('AntiOffsiteLinking() - "'.$parsed_url['host'].'" is NOT in $this->config_nohotlink_valid_domains ('.implode(';', $this->config_nohotlink_valid_domains).')', __FILE__, __LINE__);
+			} else {
+				$this->DebugMessage('AntiOffsiteLinking() - "'.$parsed_url['host'].'" is in $this->config_nohotlink_valid_domains ('.implode(';', $this->config_nohotlink_valid_domains).')', __FILE__, __LINE__);
+			}
+		}
+
+		if ($allow) {
+			$this->DebugMessage('AntiOffsiteLinking() says this is allowed', __FILE__, __LINE__);
+			return true;
+		}
+
+		if (!phpthumb_functions::IsHexColor($this->config_error_bgcolor)) {
+			return $this->ErrorImage('Invalid hex color string "'.$this->config_error_bgcolor.'" for $this->config_error_bgcolor');
+		}
+		if (!phpthumb_functions::IsHexColor($this->config_error_textcolor)) {
+			return $this->ErrorImage('Invalid hex color string "'.$this->config_error_textcolor.'" for $this->config_error_textcolor');
+		}
+		if ($erase) {
+
+			return $this->ErrorImage($message, $this->thumbnail_width, $this->thumbnail_height, $this->config_error_bgcolor, $this->config_error_textcolor, $this->config_error_fontsize);
+
+		} else {
+
+			$this->config_nooffsitelink_watermark_src = $this->ResolveFilenameToAbsolute($this->config_nooffsitelink_watermark_src);
+			if (is_file($this->config_nooffsitelink_watermark_src)) {
+
+				if (!include_once(dirname(__FILE__).'/phpthumb.filters.php')) {
+					$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.filters.php" which is required for applying watermark', __FILE__, __LINE__);
+					return false;
+				}
+				$watermark_img = $this->ImageCreateFromStringReplacement(file_get_contents($this->config_nooffsitelink_watermark_src));
+				$phpthumbFilters = new phpthumb_filters();
+				$phpthumbFilters->phpThumbObject = $this;
+				$opacity = 50;
+				$margin  = 5;
+				$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $watermark_img, '*', $opacity, $margin);
+				ImageDestroy($watermark_img);
+				unset($phpthumbFilters);
+
+			} else {
+
+				$nohotlink_text_array = explode("\n", wordwrap($message, floor($this->thumbnail_width / ImageFontWidth($this->config_error_fontsize)), "\n"));
+				$nohotlink_text_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_error_textcolor);
+
+				$topoffset = round(($this->thumbnail_height - (count($nohotlink_text_array) * ImageFontHeight($this->config_error_fontsize))) / 2);
+
+				$rowcounter = 0;
+				$this->DebugMessage('AntiOffsiteLinking() writing '.count($nohotlink_text_array).' lines of text "'.$message.'" (in #'.$this->config_error_textcolor.') on top of image', __FILE__, __LINE__);
+				foreach ($nohotlink_text_array as $textline) {
+					$leftoffset = max(0, round(($this->thumbnail_width - (strlen($textline) * ImageFontWidth($this->config_error_fontsize))) / 2));
+					ImageString($this->gdimg_output, $this->config_error_fontsize, $leftoffset, $topoffset + ($rowcounter++ * ImageFontHeight($this->config_error_fontsize)), $textline, $nohotlink_text_color);
+				}
+
+			}
+
+		}
+		return true;
+	}
+
+
+	function AlphaChannelFlatten() {
+		if (!$this->is_alpha) {
+			// image doesn't have alpha transparency, no need to flatten
+			$this->DebugMessage('skipping AlphaChannelFlatten() because !$this->is_alpha', __FILE__, __LINE__);
+			return false;
+		}
+		switch ($this->thumbnailFormat) {
+			case 'png':
+			case 'ico':
+				// image has alpha transparency, but output as PNG or ICO which can handle it
+				$this->DebugMessage('skipping AlphaChannelFlatten() because ($this->thumbnailFormat == "'.$this->thumbnailFormat.'")', __FILE__, __LINE__);
+				return false;
+				break;
+
+			case 'gif':
+				// image has alpha transparency, but output as GIF which can handle only single-color transparency
+				$CurrentImageColorTransparent = ImageColorTransparent($this->gdimg_output);
+				if ($CurrentImageColorTransparent == -1) {
+					// no transparent color defined
+
+					if (phpthumb_functions::gd_version() < 2.0) {
+						$this->DebugMessage('AlphaChannelFlatten() failed because GD version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+						return false;
+					}
+
+					if ($img_alpha_mixdown_dither = @ImageCreateTrueColor(ImageSX($this->gdimg_output), ImageSY($this->gdimg_output))) {
+
+						for ($i = 0; $i <= 255; $i++) {
+							$dither_color[$i] = ImageColorAllocate($img_alpha_mixdown_dither, $i, $i, $i);
+						}
+
+						// scan through current truecolor image copy alpha channel to temp image as grayscale
+						for ($x = 0; $x < $this->thumbnail_width; $x++) {
+							for ($y = 0; $y < $this->thumbnail_height; $y++) {
+								$PixelColor = phpthumb_functions::GetPixelColor($this->gdimg_output, $x, $y);
+								ImageSetPixel($img_alpha_mixdown_dither, $x, $y, $dither_color[($PixelColor['alpha'] * 2)]);
+							}
+						}
+
+						// dither alpha channel grayscale version down to 2 colors
+						ImageTrueColorToPalette($img_alpha_mixdown_dither, true, 2);
+
+						// reduce color palette to 256-1 colors (leave one palette position for transparent color)
+						ImageTrueColorToPalette($this->gdimg_output, true, 255);
+
+						// allocate a new color for transparent color index
+						$TransparentColor = ImageColorAllocate($this->gdimg_output, 1, 254, 253);
+						ImageColorTransparent($this->gdimg_output, $TransparentColor);
+
+						// scan through alpha channel image and note pixels with >50% transparency
+						$TransparentPixels = array();
+						for ($x = 0; $x < $this->thumbnail_width; $x++) {
+							for ($y = 0; $y < $this->thumbnail_height; $y++) {
+								$AlphaChannelPixel = phpthumb_functions::GetPixelColor($img_alpha_mixdown_dither, $x, $y);
+								if ($AlphaChannelPixel['red'] > 127) {
+									ImageSetPixel($this->gdimg_output, $x, $y, $TransparentColor);
+								}
+							}
+						}
+						ImageDestroy($img_alpha_mixdown_dither);
+
+						$this->DebugMessage('AlphaChannelFlatten() set image to 255+1 colors with transparency for GIF output', __FILE__, __LINE__);
+						return true;
+
+					} else {
+						$this->DebugMessage('AlphaChannelFlatten() failed ImageCreate('.ImageSX($this->gdimg_output).', '.ImageSY($this->gdimg_output).')', __FILE__, __LINE__);
+						return false;
+					}
+
+				} else {
+					// a single transparent color already defined, leave as-is
+					$this->DebugMessage('skipping AlphaChannelFlatten() because ($this->thumbnailFormat == "'.$this->thumbnailFormat.'") and ImageColorTransparent returned "'.$CurrentImageColorTransparent.'"', __FILE__, __LINE__);
+					return true;
+				}
+				break;
+		}
+		$this->DebugMessage('continuing AlphaChannelFlatten() for output format "'.$this->thumbnailFormat.'"', __FILE__, __LINE__);
+		// image has alpha transparency, and is being output in a format that doesn't support it -- flatten
+		if ($gdimg_flatten_temp = phpthumb_functions::ImageCreateFunction($this->thumbnail_width, $this->thumbnail_height)) {
+
+			$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
+			if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
+				return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
+			}
+			$background_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_background_hexcolor);
+			ImageFilledRectangle($gdimg_flatten_temp, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $background_color);
+			ImageCopy($gdimg_flatten_temp, $this->gdimg_output, 0, 0, 0, 0, $this->thumbnail_width, $this->thumbnail_height);
+
+			ImageAlphaBlending($this->gdimg_output, true);
+			ImageSaveAlpha($this->gdimg_output, false);
+			ImageColorTransparent($this->gdimg_output, -1);
+			ImageCopy($this->gdimg_output, $gdimg_flatten_temp, 0, 0, 0, 0, $this->thumbnail_width, $this->thumbnail_height);
+
+			ImageDestroy($gdimg_flatten_temp);
+			return true;
+
+		} else {
+			$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
+		}
+		return false;
+	}
+
+
+	function ApplyFilters() {
+		if ($this->fltr && is_array($this->fltr)) {
+			if (!include_once(dirname(__FILE__).'/phpthumb.filters.php')) {
+				$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.filters.php" which is required for applying filters ('.implode(';', $this->fltr).')', __FILE__, __LINE__);
+				return false;
+			}
+			$phpthumbFilters = new phpthumb_filters();
+			$phpthumbFilters->phpThumbObject = $this;
+			foreach ($this->fltr as $filtercommand) {
+				@list($command, $parameter) = explode('|', $filtercommand, 2);
+				$this->DebugMessage('Attempting to process filter command "'.$command.'('.$parameter.')"', __FILE__, __LINE__);
+				switch ($command) {
+					case 'brit': // Brightness
+						$phpthumbFilters->Brightness($this->gdimg_output, $parameter);
+						break;
+
+					case 'cont': // Contrast
+						$phpthumbFilters->Contrast($this->gdimg_output, $parameter);
+						break;
+
+					case 'ds': // Desaturation
+						$phpthumbFilters->Desaturate($this->gdimg_output, $parameter, '');
+						break;
+
+					case 'sat': // Saturation
+						$phpthumbFilters->Saturation($this->gdimg_output, $parameter, '');
+						break;
+
+					case 'gray': // Grayscale
+						$phpthumbFilters->Grayscale($this->gdimg_output);
+						break;
+
+					case 'clr': // Colorize
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Colorize() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							break;
+						}
+						@list($amount, $color) = explode('|', $parameter);
+						$phpthumbFilters->Colorize($this->gdimg_output, $amount, $color);
+						break;
+
+					case 'sep': // Sepia
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Sepia() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							break;
+						}
+						@list($amount, $color) = explode('|', $parameter);
+						$phpthumbFilters->Sepia($this->gdimg_output, $amount, $color);
+						break;
+
+					case 'gam': // Gamma correction
+						$phpthumbFilters->Gamma($this->gdimg_output, $parameter);
+						break;
+
+					case 'neg': // Negative colors
+						$phpthumbFilters->Negative($this->gdimg_output);
+						break;
+
+					case 'th': // Threshold
+						$phpthumbFilters->Threshold($this->gdimg_output, $parameter);
+						break;
+
+					case 'rcd': // ReduceColorDepth
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping ReduceColorDepth() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							break;
+						}
+						@list($colors, $dither) = explode('|', $parameter);
+						$colors = ($colors                ?  (int) $colors : 256);
+						$dither  = ((strlen($dither) > 0) ? (bool) $dither : true);
+						$phpthumbFilters->ReduceColorDepth($this->gdimg_output, $colors, $dither);
+						break;
+
+					case 'flip': // Flip
+						$phpthumbFilters->Flip($this->gdimg_output, (strpos(strtolower($parameter), 'x') !== false), (strpos(strtolower($parameter), 'y') !== false));
+						break;
+
+					case 'edge': // EdgeDetect
+						$phpthumbFilters->EdgeDetect($this->gdimg_output);
+						break;
+
+					case 'emb': // Emboss
+						$phpthumbFilters->Emboss($this->gdimg_output);
+						break;
+
+					case 'bvl': // Bevel
+						@list($width, $color1, $color2) = explode('|', $parameter);
+						$phpthumbFilters->Bevel($this->gdimg_output, $width, $color1, $color2);
+						break;
+
+					case 'lvl': // autoLevels
+						@list($band, $method, $threshold) = explode('|', $parameter);
+						$band      = ($band ? ereg_replace('[^RGBA\\*]', '', strtoupper($band)) : '*');
+						$method    = ((strlen($method) > 0)    ? intval($method)                :   2);
+						$threshold = ((strlen($threshold) > 0) ? floatval($threshold)           : 0.1);
+
+						$phpthumbFilters->HistogramStretch($this->gdimg_output, $band, $method, $threshold);
+						break;
+
+					case 'wb': // WhiteBalance
+						$phpthumbFilters->WhiteBalance($this->gdimg_output, $parameter);
+						break;
+
+					case 'hist': // Histogram overlay
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping HistogramOverlay() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							break;
+						}
+						@list($bands, $colors, $width, $height, $alignment, $opacity, $margin_x, $margin_y) = explode('|', $parameter);
+						$bands     = ($bands     ? $bands     :  '*');
+						$colors    = ($colors    ? $colors    :   '');
+						$width     = ($width     ? $width     : 0.25);
+						$height    = ($height    ? $height    : 0.25);
+						$alignment = ($alignment ? $alignment : 'BR');
+						$opacity   = ($opacity   ? $opacity   :   50);
+						$margin_x  = ($margin_x  ? $margin_x  :    5);
+						$margin_y  = $margin_y; // just to note it wasn't forgotten, but let the value always pass unchanged
+						$phpthumbFilters->HistogramOverlay($this->gdimg_output, $bands, $colors, $width, $height, $alignment, $opacity, $margin_x, $margin_y);
+						break;
+
+					case 'fram': // Frame
+						@list($frame_width, $edge_width, $color_frame, $color1, $color2) = explode('|', $parameter);
+						$phpthumbFilters->Frame($this->gdimg_output, $frame_width, $edge_width, $color_frame, $color1, $color2);
+						break;
+
+					case 'drop': // DropShadow
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping DropShadow() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						$this->is_alpha = true;
+						@list($distance, $width, $color, $angle, $fade) = explode('|', $parameter);
+						$phpthumbFilters->DropShadow($this->gdimg_output, $distance, $width, $color, $angle, $fade);
+						break;
+
+					case 'mask': // Mask cropping
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Mask() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						$mask_filename = $this->ResolveFilenameToAbsolute($parameter);
+						if (@is_readable($mask_filename) && ($fp_mask = @fopen($mask_filename, 'rb'))) {
+							$MaskImageData = '';
+							do {
+								$buffer = fread($fp_mask, 8192);
+								$MaskImageData .= $buffer;
+							} while (strlen($buffer) > 0);
+							fclose($fp_mask);
+							if ($gdimg_mask = $this->ImageCreateFromStringReplacement($MaskImageData)) {
+								$this->is_alpha = true;
+								$phpthumbFilters->ApplyMask($gdimg_mask, $this->gdimg_output);
+								ImageDestroy($gdimg_mask);
+							} else {
+								$this->DebugMessage('ImageCreateFromStringReplacement() failed for "'.$mask_filename.'"', __FILE__, __LINE__);
+							}
+						} else {
+							$this->DebugMessage('Cannot open mask file "'.$mask_filename.'"', __FILE__, __LINE__);
+						}
+						break;
+
+					case 'elip': // Elipse cropping
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Elipse() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						$this->is_alpha = true;
+						$phpthumbFilters->Elipse($this->gdimg_output);
+						break;
+
+					case 'ric': // RoundedImageCorners
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping RoundedImageCorners() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						@list($radius_x, $radius_y) = explode('|', $parameter);
+						if (($radius_x < 1) || ($radius_y < 1)) {
+							$this->DebugMessage('Skipping RoundedImageCorners('.$radius_x.', '.$radius_y.') because x/y radius is less than 1', __FILE__, __LINE__);
+							break;
+						}
+						$this->is_alpha = true;
+						$phpthumbFilters->RoundedImageCorners($this->gdimg_output, $radius_x, $radius_y);
+						break;
+
+					case 'crop': // Crop
+						@list($left, $right, $top, $bottom) = explode('|', $parameter);
+						$phpthumbFilters->Crop($this->gdimg_output, $left, $right, $top, $bottom);
+						break;
+
+					case 'bord': // Border
+						@list($border_width, $radius_x, $radius_y, $hexcolor_border) = explode('|', $parameter);
+						$this->is_alpha = true;
+						$phpthumbFilters->ImageBorder($this->gdimg_output, $border_width, $radius_x, $radius_y, $hexcolor_border);
+						break;
+
+					case 'over': // Overlay
+						@list($filename, $underlay, $margin, $opacity) = explode('|', $parameter);
+						$underlay = (bool) ($underlay              ? $underlay : false);
+						$margin   =        ((strlen($margin)  > 0) ? $margin   : ($underlay ? 0.1 : 0.0));
+						$opacity  =        ((strlen($opacity) > 0) ? $opacity  : 100);
+						if (($margin > 0) && ($margin < 1)) {
+							$margin = min(0.499, $margin);
+						} elseif (($margin > -1) && ($margin < 0)) {
+							$margin = max(-0.499, $margin);
+						}
+
+						$filename = $this->ResolveFilenameToAbsolute($filename);
+						if (@is_readable($filename) && ($fp_watermark = @fopen($filename, 'rb'))) {
+							$WatermarkImageData = '';
+							do {
+								$buffer = fread($fp_watermark, 8192);
+								$WatermarkImageData .= $buffer;
+							} while (strlen($buffer) > 0);
+							fclose($fp_watermark);
+							if ($img_watermark = $this->ImageCreateFromStringReplacement($WatermarkImageData)) {
+								if ($margin < 1) {
+									$resized_x = max(1, ImageSX($this->gdimg_output) - round(2 * (ImageSX($this->gdimg_output) * $margin)));
+									$resized_y = max(1, ImageSY($this->gdimg_output) - round(2 * (ImageSY($this->gdimg_output) * $margin)));
+								} else {
+									$resized_x = max(1, ImageSX($this->gdimg_output) - round(2 * $margin));
+									$resized_y = max(1, ImageSY($this->gdimg_output) - round(2 * $margin));
+								}
+
+								if ($underlay) {
+
+									if ($img_watermark_resized = phpthumb_functions::ImageCreateFunction(ImageSX($this->gdimg_output), ImageSY($this->gdimg_output))) {
+										ImageAlphaBlending($img_watermark_resized, false);
+										ImageSaveAlpha($img_watermark_resized, true);
+										$this->ImageResizeFunction($img_watermark_resized, $img_watermark, 0, 0, 0, 0, ImageSX($img_watermark_resized), ImageSY($img_watermark_resized), ImageSX($img_watermark), ImageSY($img_watermark));
+										if ($img_source_resized = phpthumb_functions::ImageCreateFunction($resized_x, $resized_y)) {
+											ImageAlphaBlending($img_source_resized, false);
+											ImageSaveAlpha($img_source_resized, true);
+											$this->ImageResizeFunction($img_source_resized, $this->gdimg_output, 0, 0, 0, 0, ImageSX($img_source_resized), ImageSY($img_source_resized), ImageSX($this->gdimg_output), ImageSY($this->gdimg_output));
+											$phpthumbFilters->WatermarkOverlay($img_watermark_resized, $img_source_resized, 'C', $opacity, $margin);
+											ImageCopy($this->gdimg_output, $img_watermark_resized, 0, 0, 0, 0, ImageSX($this->gdimg_output), ImageSY($this->gdimg_output));
+										} else {
+											$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.$resized_x.', '.$resized_y.')', __FILE__, __LINE__);
+										}
+										ImageDestroy($img_watermark_resized);
+									} else {
+										$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.ImageSX($this->gdimg_output).', '.ImageSY($this->gdimg_output).')', __FILE__, __LINE__);
+									}
+
+								} else { // overlay
+
+									if ($img_watermark_resized = phpthumb_functions::ImageCreateFunction($resized_x, $resized_y)) {
+										ImageAlphaBlending($img_watermark_resized, false);
+										ImageSaveAlpha($img_watermark_resized, true);
+										$this->ImageResizeFunction($img_watermark_resized, $img_watermark, 0, 0, 0, 0, ImageSX($img_watermark_resized), ImageSY($img_watermark_resized), ImageSX($img_watermark), ImageSY($img_watermark));
+										$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $img_watermark_resized, 'C', $opacity, $margin);
+										ImageDestroy($img_watermark_resized);
+									} else {
+										$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.$resized_x.', '.$resized_y.')', __FILE__, __LINE__);
+									}
+
+								}
+								ImageDestroy($img_watermark);
+
+							} else {
+								$this->DebugMessage('ImageCreateFromStringReplacement() failed for "'.$filename.'"', __FILE__, __LINE__);
+							}
+						} else {
+							$this->DebugMessage('Cannot open overlay file "'.$filename.'"', __FILE__, __LINE__);
+						}
+						break;
+
+					case 'wmi': // WaterMarkImage
+						@list($filename, $alignment, $opacity, $margin['x'], $margin['y']) = explode('|', $parameter);
+						$alignment   = ($alignment           ? $alignment   : 'BR');
+						$opacity     = (strlen($opacity)     ? $opacity     : 50);
+						$margins = array('x', 'y');
+						foreach ($margins as $xy) {
+							$margin[$xy] = (strlen($margin[$xy]) ? $margin[$xy] : 5);
+							if (($margin[$xy] > 0) && ($margin[$xy] < 1)) {
+								$margin[$xy] = min(0.499, $margin[$xy]);
+							} elseif (($margin[$xy] > -1) && ($margin[$xy] < 0)) {
+								$margin[$xy] = max(-0.499, $margin[$xy]);
+							}
+						}
+
+						$filename = $this->ResolveFilenameToAbsolute($filename);
+						if (@is_readable($filename)) {
+							if ($img_watermark = $this->ImageCreateFromFilename($filename)) {
+								// great
+								$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $img_watermark, $alignment, $opacity, $margin['x'], $margin['y']);
+								ImageDestroy($img_watermark);
+							} else {
+								$this->DebugMessage('ImageCreateFromFilename() failed for "'.$filename.'"', __FILE__, __LINE__);
+							}
+						} else {
+							$this->DebugMessage('!is_readable('.$filename.')', __FILE__, __LINE__);
+						}
+						break;
+
+					case 'wmt': // WaterMarkText
+						@list($text, $size, $alignment, $hex_color, $ttffont, $opacity, $margin, $angle, $bg_color, $bg_opacity, $fillextend) = explode('|', $parameter);
+						$text       = ($text            ? $text       : '');
+						$size       = ($size            ? $size       : 3);
+						$alignment  = ($alignment       ? $alignment  : 'BR');
+						$hex_color  = ($hex_color       ? $hex_color  : '000000');
+						$ttffont    = ($ttffont         ? $ttffont    : '');
+						$opacity    = (strlen($opacity) ? $opacity    : 50);
+						$margin     = (strlen($margin)  ? $margin     : 5);
+						$angle      = (strlen($angle)   ? $angle      : 0);
+						$bg_color   = ($bg_color        ? $bg_color   : false);
+						$bg_opacity = ($bg_opacity      ? $bg_opacity : 0);
+						$fillextend = ($fillextend      ? $fillextend : '');
+
+						if (basename($ttffont) == $ttffont) {
+							$ttffont = realpath($this->config_ttf_directory.DIRECTORY_SEPARATOR.$ttffont);
+						} else {
+							$ttffont = $this->ResolveFilenameToAbsolute($ttffont);
+						}
+						$phpthumbFilters->WatermarkText($this->gdimg_output, $text, $size, $alignment, $hex_color, $ttffont, $opacity, $margin, $angle, $bg_color, $bg_opacity, $fillextend);
+						break;
+
+					case 'blur': // Blur
+						@list($radius) = explode('|', $parameter);
+						$radius = ($radius ? $radius : 1);
+						if (phpthumb_functions::gd_version() < 2) {
+							$this->DebugMessage('Skipping Blur() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						$phpthumbFilters->Blur($this->gdimg_output, $radius);
+						break;
+
+					case 'gblr': // Gaussian Blur
+						$phpthumbFilters->BlurGaussian($this->gdimg_output);
+						break;
+
+					case 'sblr': // Selective Blur
+						$phpthumbFilters->BlurSelective($this->gdimg_output);
+						break;
+
+					case 'mean': // MeanRemoval blur
+						$phpthumbFilters->MeanRemoval($this->gdimg_output);
+						break;
+
+					case 'smth': // Smooth blur
+						$phpthumbFilters->Smooth($this->gdimg_output, $parameter);
+						break;
+
+					case 'usm': // UnSharpMask sharpening
+						@list($amount, $radius, $threshold) = explode('|', $parameter);
+						$amount    = ($amount            ? $amount    : 80);
+						$radius    = ($radius            ? $radius    : 0.5);
+						$threshold = (strlen($threshold) ? $threshold : 3);
+						if (phpthumb_functions::gd_version() >= 2.0) {
+							ob_start();
+							if (!@include_once(dirname(__FILE__).'/phpthumb.unsharp.php')) {
+								$include_error = ob_get_contents();
+								if ($include_error) {
+									$this->DebugMessage('include_once("'.dirname(__FILE__).'/phpthumb.unsharp.php") generated message: "'.$include_error.'"', __FILE__, __LINE__);
+								}
+								$this->DebugMessage('Error including "'.dirname(__FILE__).'/phpthumb.unsharp.php" which is required for unsharp masking', __FILE__, __LINE__);
+								ob_end_clean();
+								return false;
+							}
+							ob_end_clean();
+							phpUnsharpMask::applyUnsharpMask($this->gdimg_output, $amount, $radius, $threshold);
+						} else {
+							$this->DebugMessage('Skipping unsharp mask because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+							return false;
+						}
+						break;
+
+					case 'rot': // ROTate
+						@list($angle, $bgcolor) = explode('|', $parameter);
+						$phpthumbFilters->ImprovedImageRotate($this->gdimg_output, $angle, $bgcolor);
+						break;
+				}
+				$this->DebugMessage('Finished processing filter command "'.$command.'('.$parameter.')"', __FILE__, __LINE__);
+			}
+		}
+		return true;
+	}
+
+
+	function MaxFileSize() {
+		if (phpthumb_functions::gd_version() < 2) {
+			$this->DebugMessage('Skipping MaxFileSize() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+			return false;
+		}
+		if ($this->maxb > 0) {
+			switch ($this->thumbnailFormat) {
+				case 'png':
+				case 'gif':
+					$imgRenderFunction = 'image'.$this->thumbnailFormat;
+
+					ob_start();
+					$imgRenderFunction($this->gdimg_output);
+					$imgdata = ob_get_contents();
+					ob_end_clean();
+
+					if (strlen($imgdata) > $this->maxb) {
+						for ($i = 8; $i >= 1; $i--) {
+							$tempIMG = ImageCreateTrueColor(ImageSX($this->gdimg_output), ImageSY($this->gdimg_output));
+							ImageCopy($tempIMG, $this->gdimg_output, 0, 0, 0, 0, ImageSX($this->gdimg_output), ImageSY($this->gdimg_output));
+							ImageTrueColorToPalette($tempIMG, true, pow(2, $i));
+							ob_start();
+							$imgRenderFunction($tempIMG);
+							$imgdata = ob_get_contents();
+							ob_end_clean();
+
+							if (strlen($imgdata) <= $this->maxb) {
+								ImageTrueColorToPalette($this->gdimg_output, true, pow(2, $i));
+								break;
+							}
+						}
+					}
+					if (strlen($imgdata) > $this->maxb) {
+						ImageTrueColorToPalette($this->gdimg_output, true, pow(2, $i));
+						return false;
+					}
+					break;
+
+				case 'jpeg':
+					ob_start();
+					ImageJPEG($this->gdimg_output);
+					$imgdata = ob_get_contents();
+					ob_end_clean();
+
+					$OriginalJPEGquality = $this->thumbnailQuality;
+					if (strlen($imgdata) > $this->maxb) {
+						for ($i = 3; $i < 20; $i++) {
+							$q = round(100 * (1 - log10($i / 2)));
+							ob_start();
+							ImageJPEG($this->gdimg_output, '', $q);
+							$imgdata = ob_get_contents();
+							ob_end_clean();
+
+							$this->thumbnailQuality = $q;
+							if (strlen($imgdata) <= $this->maxb) {
+								break;
+							}
+						}
+					}
+					if (strlen($imgdata) > $this->maxb) {
+						return false;
+					}
+					break;
+
+				default:
+					return false;
+					break;
+			}
+		}
+		return true;
+	}
+
+
+	function CalculateThumbnailDimensions() {
+//echo $this->source_width.'x'.$this->source_height.'<hr>';
+		$this->thumbnailCropX = ($this->sx ? (($this->sx >= 1) ? $this->sx : round($this->sx * $this->source_width))  : 0);
+//echo $this->thumbnailCropX.'<br>';
+		$this->thumbnailCropY = ($this->sy ? (($this->sy >= 1) ? $this->sy : round($this->sy * $this->source_height)) : 0);
+//echo $this->thumbnailCropY.'<br>';
+		$this->thumbnailCropW = ($this->sw ? (($this->sw >= 1) ? $this->sw : round($this->sw * $this->source_width))  : $this->source_width);
+//echo $this->thumbnailCropW.'<br>';
+		$this->thumbnailCropH = ($this->sh ? (($this->sh >= 1) ? $this->sh : round($this->sh * $this->source_height)) : $this->source_height);
+//echo $this->thumbnailCropH.'<hr>';
+
+		// limit source area to original image area
+		$this->thumbnailCropW = max(1, min($this->thumbnailCropW, $this->source_width  - $this->thumbnailCropX));
+		$this->thumbnailCropH = max(1, min($this->thumbnailCropH, $this->source_height - $this->thumbnailCropY));
+
+		$this->DebugMessage('CalculateThumbnailDimensions() [x,y,w,h] initially set to ['.$this->thumbnailCropX.','.$this->thumbnailCropY.','.$this->thumbnailCropW.','.$this->thumbnailCropH.']', __FILE__, __LINE__);
+
+
+		if ($this->zc && $this->w && $this->h) {
+			// Zoom Crop
+			// retain proportional resizing we did above, but crop off larger dimension so smaller
+			// dimension fully fits available space
+
+			$scaling_X = $this->source_width  / $this->w;
+			$scaling_Y = $this->source_height / $this->h;
+			if ($scaling_X > $scaling_Y) {
+				// some of the width will need to be cropped
+				$allowable_width = $this->source_width / $scaling_X * $scaling_Y;
+				$this->thumbnailCropW = round($allowable_width);
+				$this->thumbnailCropX = round(($this->source_width - $allowable_width) / 2);
+
+			} elseif ($scaling_Y > $scaling_X) {
+				// some of the height will need to be cropped
+				$allowable_height = $this->source_height / $scaling_Y * $scaling_X;
+				$this->thumbnailCropH = round($allowable_height);
+				$this->thumbnailCropY = round(($this->source_height - $allowable_height) / 2);
+
+			} else {
+				// image fits perfectly, no cropping needed
+			}
+			$this->thumbnail_width  = $this->w;
+			$this->thumbnail_height = $this->h;
+			$this->thumbnail_image_width  = $this->thumbnail_width;
+			$this->thumbnail_image_height = $this->thumbnail_height;
+
+		} elseif ($this->iar && $this->w && $this->h) {
+
+			// Ignore Aspect Ratio
+			// stretch image to fit exactly 'w' x 'h'
+			$this->thumbnail_width  = $this->w;
+			$this->thumbnail_height = $this->h;
+			$this->thumbnail_image_width  = $this->thumbnail_width;
+			$this->thumbnail_image_height = $this->thumbnail_height;
+
+		} else {
+
+			$original_aspect_ratio = $this->thumbnailCropW / $this->thumbnailCropH;
+			if ($this->aoe) {
+				if ($this->w && $this->h) {
+					$maxwidth  = min($this->w, $this->h * $original_aspect_ratio);
+					$maxheight = min($this->h, $this->w / $original_aspect_ratio);
+				} elseif ($this->w) {
+					$maxwidth  = $this->w;
+					$maxheight = $this->w / $original_aspect_ratio;
+				} elseif ($this->h) {
+					$maxwidth  = $this->h * $original_aspect_ratio;
+					$maxheight = $this->h;
+				} else {
+					$maxwidth  = $this->thumbnailCropW;
+					$maxheight = $this->thumbnailCropH;
+				}
+			} else {
+				$maxwidth  = phpthumb_functions::nonempty_min($this->w, $this->thumbnailCropW, $this->config_output_maxwidth);
+				$maxheight = phpthumb_functions::nonempty_min($this->h, $this->thumbnailCropH, $this->config_output_maxheight);
+//echo $maxwidth.'x'.$maxheight.'<br>';
+				$maxwidth  = min($maxwidth, $maxheight * $original_aspect_ratio);
+				$maxheight = min($maxheight, $maxwidth / $original_aspect_ratio);
+//echo $maxwidth.'x'.$maxheight.'<hr>';
+			}
+
+			$this->thumbnail_image_width  = $maxwidth;
+			$this->thumbnail_image_height = $maxheight;
+			$this->thumbnail_width  = $maxwidth;
+			$this->thumbnail_height = $maxheight;
+
+			$this->FixedAspectRatio();
+		}
+
+		$this->thumbnail_width  = max(1, floor($this->thumbnail_width));
+		$this->thumbnail_height = max(1, floor($this->thumbnail_height));
+		return true;
+	}
+
+
+	function CreateGDoutput() {
+		$this->CalculateThumbnailDimensions();
+
+		// Create the GD image (either true-color or 256-color, depending on GD version)
+		$this->gdimg_output = phpthumb_functions::ImageCreateFunction($this->thumbnail_width, $this->thumbnail_height);
+
+		// Images that have transparency must have the background filled with the configured 'bg' color
+		// otherwise the transparent color will appear as black
+		ImageSaveAlpha($this->gdimg_output, true);
+		if ($this->is_alpha && phpthumb_functions::gd_version() >= 2) {
+
+			ImageAlphaBlending($this->gdimg_output, false);
+			$output_full_alpha = phpthumb_functions::ImageColorAllocateAlphaSafe($this->gdimg_output, 255, 255, 255, 127);
+			ImageFilledRectangle($this->gdimg_output, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $output_full_alpha);
+
+		} else {
+
+			$current_transparent_color = ImageColorTransparent($this->gdimg_source);
+			if ($this->bg || (@$current_transparent_color >= 0)) {
+
+				$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
+				if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
+					return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
+				}
+				$background_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_background_hexcolor);
+				ImageFilledRectangle($this->gdimg_output, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $background_color);
+
+			}
+
+		}
+		$this->DebugMessage('CreateGDoutput() returning canvas "'.$this->thumbnail_width.'x'.$this->thumbnail_height.'"', __FILE__, __LINE__);
+		return true;
+	}
+
+	function SetOrientationDependantWidthHeight() {
+		$this->DebugMessage('SetOrientationDependantWidthHeight() starting with "'.$this->source_width.'"x"'.$this->source_height.'"', __FILE__, __LINE__);
+		if ($this->source_height > $this->source_width) {
+			// portrait
+			$this->w = phpthumb_functions::OneOfThese($this->wp, $this->w, $this->ws, $this->wl);
+			$this->h = phpthumb_functions::OneOfThese($this->hp, $this->h, $this->hs, $this->hl);
+		} elseif ($this->source_height < $this->source_width) {
+			// landscape
+			$this->w = phpthumb_functions::OneOfThese($this->wl, $this->w, $this->ws, $this->wp);
+			$this->h = phpthumb_functions::OneOfThese($this->hl, $this->h, $this->hs, $this->hp);
+		} else {
+			// square
+			$this->w = phpthumb_functions::OneOfThese($this->ws, $this->w, $this->wl, $this->wp);
+			$this->h = phpthumb_functions::OneOfThese($this->hs, $this->h, $this->hl, $this->hp);
+		}
+		//$this->w = round($this->w ? $this->w : (($this->h && $this->source_height) ? $this->h * $this->source_width  / $this->source_height : $this->w));
+		//$this->h = round($this->h ? $this->h : (($this->w && $this->source_width)  ? $this->w * $this->source_height / $this->source_width  : $this->h));
+		$this->DebugMessage('SetOrientationDependantWidthHeight() setting w="'.intval($this->w).'", h="'.intval($this->h).'"', __FILE__, __LINE__);
+		return true;
+	}
+
+	function ExtractEXIFgetImageSize() {
+		$this->DebugMessage('starting ExtractEXIFgetImageSize()', __FILE__, __LINE__);
+
+		if (is_resource($this->gdimg_source)) {
+
+			$this->source_width  = ImageSX($this->gdimg_source);
+			$this->source_height = ImageSY($this->gdimg_source);
+
+			$this->SetOrientationDependantWidthHeight();
+
+		} elseif ($this->rawImageData && !$this->sourceFilename) {
+
+			$this->DebugMessage('bypassing EXIF and GetImageSize sections because $this->rawImageData is set and $this->sourceFilename is not set', __FILE__, __LINE__);
+
+		}
+
+		if (is_null($this->getimagesizeinfo)) {
+			$this->getimagesizeinfo = @GetImageSize($this->sourceFilename);
+		}
+
+		if (!empty($this->getimagesizeinfo)) {
+			// great
+			$this->getimagesizeinfo['filesize'] = @filesize($this->sourceFilename);
+		} elseif (!$this->rawImageData) {
+			$this->DebugMessage('GetImageSize("'.$this->sourceFilename.'") failed', __FILE__, __LINE__);
+		}
+
+		if ($this->config_prefer_imagemagick) {
+			if ($this->ImageMagickThumbnailToGD()) {
+				return true;
+			}
+			$this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__);
+		}
+
+		$this->source_width  = $this->getimagesizeinfo[0];
+		$this->source_height = $this->getimagesizeinfo[1];
+
+		$this->SetOrientationDependantWidthHeight();
+
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.2.0', '>=') && function_exists('exif_read_data')) {
+			$this->exif_raw_data = @exif_read_data($this->sourceFilename, 0, true);
+		}
+		if (function_exists('exif_thumbnail') && ($this->getimagesizeinfo[2] == 2)) {
+			// Extract EXIF info from JPEGs
+
+			$this->exif_thumbnail_width  = '';
+			$this->exif_thumbnail_height = '';
+			$this->exif_thumbnail_type   = '';
+
+			// The parameters width, height and imagetype are available since PHP v4.3.0
+			if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=')) {
+
+				$this->exif_thumbnail_data = @exif_thumbnail($this->sourceFilename, $this->exif_thumbnail_width, $this->exif_thumbnail_height, $this->exif_thumbnail_type);
+
+			} else {
+
+				// older versions of exif_thumbnail output an error message but NOT return false on failure
+				ob_start();
+				$this->exif_thumbnail_data = exif_thumbnail($this->sourceFilename);
+				$exit_thumbnail_error = ob_get_contents();
+				ob_end_clean();
+				if (!$exit_thumbnail_error && $this->exif_thumbnail_data) {
+
+					if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
+						$this->exif_thumbnail_width  = ImageSX($gdimg_exif_temp);
+						$this->exif_thumbnail_height = ImageSY($gdimg_exif_temp);
+						$this->exif_thumbnail_type   = 2; // (2 == JPEG) before PHP v4.3.0 only JPEG format EXIF thumbnails are returned
+						unset($gdimg_exif_temp);
+					} else {
+						return $this->ErrorImage('Failed - $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data) in '.__FILE__.' on line '.__LINE__);
+					}
+
+				}
+
+			}
+
+		} elseif (!function_exists('exif_thumbnail')) {
+
+			$this->DebugMessage('exif_thumbnail() does not exist, cannot extract EXIF thumbnail', __FILE__, __LINE__);
+			return false;
+
+		}
+
+		$this->DebugMessage('EXIF thumbnail extraction: (size='.strlen($this->exif_thumbnail_data).'; type="'.$this->exif_thumbnail_type.'"; '.intval($this->exif_thumbnail_width).'x'.intval($this->exif_thumbnail_height).')', __FILE__, __LINE__);
+
+		// see if EXIF thumbnail can be used directly with no processing
+		if ($this->config_use_exif_thumbnail_for_speed && $this->exif_thumbnail_data) {
+			while (true) {
+				if (!$this->xto) {
+					$source_ar = $this->source_width / $this->source_height;
+					$exif_ar = $this->exif_thumbnail_width / $this->exif_thumbnail_height;
+					if (number_format($source_ar, 2) != number_format($exif_ar, 2)) {
+						$this->DebugMessage('not using EXIF thumbnail because $source_ar != $exif_ar ('.$source_ar.' != '.$exif_ar.')', __FILE__, __LINE__);
+						break;
+					}
+					if ($this->w && ($this->w != $this->exif_thumbnail_width)) {
+						$this->DebugMessage('not using EXIF thumbnail because $this->w != $this->exif_thumbnail_width ('.$this->w.' != '.$this->exif_thumbnail_width.')', __FILE__, __LINE__);
+						break;
+					}
+					if ($this->h && ($this->h != $this->exif_thumbnail_height)) {
+						$this->DebugMessage('not using EXIF thumbnail because $this->h != $this->exif_thumbnail_height ('.$this->h.' != '.$this->exif_thumbnail_height.')', __FILE__, __LINE__);
+						break;
+					}
+					$CannotBeSetParameters = array('sx', 'sy', 'sh', 'sw', 'far', 'bg', 'bc', 'fltr', 'phpThumbDebug');
+					foreach ($CannotBeSetParameters as $parameter) {
+						if ($this->$parameter) {
+							break 2;
+						}
+					}
+				}
+
+				$this->DebugMessage('setting $this->gdimg_source = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data)', __FILE__, __LINE__);
+				$this->gdimg_source = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data);
+				$this->source_width  = ImageSX($this->gdimg_source);
+				$this->source_height = ImageSY($this->gdimg_source);
+				return true;
+			}
+		}
+
+		if (($this->config_max_source_pixels > 0) && (($this->source_width * $this->source_height) > $this->config_max_source_pixels)) {
+
+			// Source image is larger than would fit in available PHP memory.
+			// If ImageMagick is installed, use it to generate the thumbnail.
+			// Else, if an EXIF thumbnail is available, use that as the source image.
+			// Otherwise, no choice but to fail with an error message
+			$this->DebugMessage('image is '.$this->source_width.'x'.$this->source_height.' and therefore contains more pixels ('.($this->source_width * $this->source_height).') than $this->config_max_source_pixels setting ('.$this->config_max_source_pixels.')', __FILE__, __LINE__);
+			if (!$this->config_prefer_imagemagick && $this->ImageMagickThumbnailToGD()) {
+				// excellent, we have a thumbnailed source image
+				return true;
+			}
+
+		}
+		return true;
+	}
+
+
+	function SetCacheFilename() {
+		if (!is_null($this->cache_filename)) {
+			$this->DebugMessage('$this->cache_filename already set, skipping SetCacheFilename()', __FILE__, __LINE__);
+			return true;
+		}
+		$this->setOutputFormat();
+		$this->setCacheDirectory();
+		if (!$this->config_cache_directory) {
+			$this->DebugMessage('SetCacheFilename() failed because $this->config_cache_directory is empty', __FILE__, __LINE__);
+			return false;
+		}
+
+		if (!$this->sourceFilename && !$this->rawImageData && $this->src) {
+			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src);
+		}
+
+		if ($this->config_cache_default_only_suffix && $this->sourceFilename) {
+			// simplified cache filenames:
+			// only use default parameters in phpThumb.config.php
+			// substitute source filename into * in $this->config_cache_default_only_suffix
+			// (eg: '*_thumb' becomes 'picture_thumb.jpg')
+			if (strpos($this->config_cache_default_only_suffix, '*') === false) {
+				$this->DebugMessage('aborting simplified caching filename because no * in "'.$this->config_cache_default_only_suffix.'"', __FILE__, __LINE__);
+			} else {
+				eregi('(.+)(\.[a-z0-9]+)?$', basename($this->sourceFilename), $matches);
+				$this->cache_filename = $this->config_cache_directory.DIRECTORY_SEPARATOR.rawurlencode(str_replace('*', @$matches[1], $this->config_cache_default_only_suffix)).'.'.strtolower($this->thumbnailFormat);
+				return true;
+			}
+		}
+
+		$this->cache_filename = '';
+		$broad_directory_name = '';
+		if ($this->new) {
+			$broad_directory_name = strtolower(md5($this->new));
+			$this->cache_filename .= '_new'.$broad_directory_name;
+		} elseif ($this->md5s) {
+			// source image MD5 hash provided
+			$this->DebugMessage('SetCacheFilename() _raw set from $this->md5s = "'.$this->md5s.'"', __FILE__, __LINE__);
+			$broad_directory_name = $this->md5s;
+			$this->cache_filename .= '_raw'.$this->md5s;
+		} elseif (!$this->src && $this->rawImageData) {
+			$this->DebugMessage('SetCacheFilename() _raw set from md5($this->rawImageData) = "'.md5($this->rawImageData).'"', __FILE__, __LINE__);
+			$broad_directory_name = strtolower(md5($this->rawImageData));
+			$this->cache_filename .= '_raw'.$broad_directory_name;
+		} else {
+			$this->DebugMessage('SetCacheFilename() _src set from md5($this->sourceFilename) "'.$this->sourceFilename.'" = "'.md5($this->sourceFilename).'"', __FILE__, __LINE__);
+			$broad_directory_name = strtolower(md5($this->sourceFilename));
+			$this->cache_filename .= '_src'.$broad_directory_name;
+		}
+		if (@$_SERVER['HTTP_REFERER'] && $this->config_nooffsitelink_enabled) {
+			$parsed_url1 = @parse_url(@$_SERVER['HTTP_REFERER']);
+			$parsed_url2 = @parse_url('http://'.@$_SERVER['HTTP_HOST']);
+			if (@$parsed_url1['host'] && @$parsed_url2['host'] && ($parsed_url1['host'] != $parsed_url2['host'])) {
+				// include "_offsite" only if nooffsitelink_enabled and if referrer doesn't match the domain of the current server
+				$this->cache_filename .= '_offsite';
+			}
+		}
+
+		$ParametersString = '';
+		if ($this->fltr && is_array($this->fltr)) {
+			$ParametersString .= '_fltr'.implode('_fltr', $this->fltr);
+		}
+		$FilenameParameters1 = array('ar', 'bg', 'bc', 'far', 'sx', 'sy', 'sw', 'sh', 'zc');
+		foreach ($FilenameParameters1 as $key) {
+			if ($this->$key) {
+				$ParametersString .= '_'.$key.$this->$key;
+			}
+		}
+		$FilenameParameters2 = array('h', 'w', 'wl', 'wp', 'ws', 'hp', 'hs', 'xto', 'ra', 'iar', 'aoe', 'maxb', 'sfn', 'dpi');
+		foreach ($FilenameParameters2 as $key) {
+			if ($this->$key) {
+				$ParametersString .= '_'.$key.intval($this->$key);
+			}
+		}
+		if ($this->thumbnailFormat == 'jpeg') {
+			// only JPEG output has variable quality option
+			$ParametersString .= '_q'.intval($this->thumbnailQuality);
+		}
+		$this->DebugMessage('SetCacheFilename() _par set from md5('.$ParametersString.')', __FILE__, __LINE__);
+		$this->cache_filename .= '_par'.strtolower(md5($ParametersString));
+
+		if ($this->md5s) {
+			// source image MD5 hash provided
+			// do not source image modification date --
+			// cached image will be used even if file was modified or removed
+		} elseif (!$this->config_cache_source_filemtime_ignore_remote && eregi('^(f|ht)tps?\://', $this->src)) {
+			$this->cache_filename .= '_dat'.intval(phpthumb_functions::filedate_remote($this->src));
+		} elseif (!$this->config_cache_source_filemtime_ignore_local && $this->src && !$this->rawImageData) {
+			$this->cache_filename .= '_dat'.intval(@filemtime($this->sourceFilename));
+		}
+
+		$this->cache_filename .= '.'.strtolower($this->thumbnailFormat);
+		$broad_directories = '';
+		for ($i = 0; $i < $this->config_cache_directory_depth; $i++) {
+			$broad_directories .= DIRECTORY_SEPARATOR.substr($broad_directory_name, 0, $i + 1);
+		}
+
+		$this->cache_filename = $this->config_cache_directory.$broad_directories.DIRECTORY_SEPARATOR.$this->config_cache_prefix.rawurlencode($this->cache_filename);
+		return true;
+	}
+
+
+	function SourceImageIsTooLarge($width, $height) {
+		if (!$this->config_max_source_pixels) {
+			return false;
+		}
+		if (function_exists('memory_get_usage')) {
+			$available_memory = max(intval(ini_get('memory_limit')), intval(get_cfg_var('memory_limit'))) * 1048576;
+			$available_memory -= memory_get_usage();
+			return (bool) (($width * $height * 5) > $available_memory);
+		}
+		return (bool) (($width * $height) > $this->config_max_source_pixels);
+	}
+
+	function ImageCreateFromFilename($filename) {
+		// try to create GD image source directly via GD, if possible,
+		// rather than buffering to memory and creating with ImageCreateFromString
+		$ImageCreateWasAttempted = false;
+		$gd_image = false;
+
+		$this->DebugMessage('starting ImageCreateFromFilename('.$filename.')', __FILE__, __LINE__);
+		if ($filename && ($getimagesizeinfo = @GetImageSize($filename))) {
+			if (!$this->SourceImageIsTooLarge($getimagesizeinfo[0], $getimagesizeinfo[1])) {
+				$ImageCreateFromFunction = array(
+					1  => 'ImageCreateFromGIF',
+					2  => 'ImageCreateFromJPEG',
+					3  => 'ImageCreateFromPNG',
+					15 => 'ImageCreateFromWBMP',
+				);
+				$this->DebugMessage('ImageCreateFromFilename found ($getimagesizeinfo[2]=='.@$getimagesizeinfo[2].')', __FILE__, __LINE__);
+				switch (@$getimagesizeinfo[2]) {
+					case 1:  // GIF
+					case 2:  // JPEG
+					case 3:  // PNG
+					case 15: // WBMP
+						$ImageCreateFromFunctionName = $ImageCreateFromFunction[$getimagesizeinfo[2]];
+						if (function_exists($ImageCreateFromFunctionName)) {
+							$this->DebugMessage('Calling '.$ImageCreateFromFunctionName.'('.$filename.')', __FILE__, __LINE__);
+							$ImageCreateWasAttempted = true;
+							$gd_image = $ImageCreateFromFunctionName($filename);
+						} else {
+							$this->DebugMessage('NOT calling '.$ImageCreateFromFunctionName.'('.$filename.') because !function_exists('.$ImageCreateFromFunctionName.')', __FILE__, __LINE__);
+						}
+						break;
+
+					case 4:  // SWF
+					case 5:  // PSD
+					case 6:  // BMP
+					case 7:  // TIFF (LE)
+					case 8:  // TIFF (BE)
+					case 9:  // JPC
+					case 10: // JP2
+					case 11: // JPX
+					case 12: // JB2
+					case 13: // SWC
+					case 14: // IFF
+					case 16: // XBM
+						$this->DebugMessage('No built-in image creation function for image type "'.@$getimagesizeinfo[2].'" ($getimagesizeinfo[2])', __FILE__, __LINE__);
+						break;
+
+					default:
+						$this->DebugMessage('Unknown value for $getimagesizeinfo[2]: "'.@$getimagesizeinfo[2].'"', __FILE__, __LINE__);
+						break;
+				}
+			} else {
+				$this->DebugMessage('image is '.$getimagesizeinfo[0].'x'.$getimagesizeinfo[1].' and therefore contains more pixels ('.($getimagesizeinfo[0] * $getimagesizeinfo[1]).') than $this->config_max_source_pixels setting ('.$this->config_max_source_pixels.')', __FILE__, __LINE__);
+				return false;
+			}
+		} else {
+			$this->DebugMessage('empty $filename or GetImageSize('.$filename.') failed', __FILE__, __LINE__);
+		}
+
+		if (!$gd_image) {
+			// cannot create from filename, attempt to create source image with ImageCreateFromString, if possible
+			if ($ImageCreateWasAttempted) {
+				$this->DebugMessage(@$ImageCreateFromFunctionName.'() was attempted but FAILED', __FILE__, __LINE__);
+			}
+			$this->DebugMessage('Populating $rawimagedata', __FILE__, __LINE__);
+			$rawimagedata = '';
+			if ($fp = @fopen($filename, 'rb')) {
+				$filesize = filesize($filename);
+				$blocksize = 8192;
+				$blockreads = ceil($filesize / $blocksize);
+				for ($i = 0; $i < $blockreads; $i++) {
+					$rawimagedata .= fread($fp, $blocksize);
+				}
+				fclose($fp);
+			} else {
+				$this->DebugMessage('cannot fopen('.$filename.')', __FILE__, __LINE__);
+			}
+			if ($rawimagedata) {
+				$this->DebugMessage('attempting ImageCreateFromStringReplacement($rawimagedata ('.strlen($rawimagedata).' bytes), true)', __FILE__, __LINE__);
+				$gd_image = $this->ImageCreateFromStringReplacement($rawimagedata, true);
+			}
+		}
+		return $gd_image;
+	}
+
+	function SourceImageToGD() {
+		if (is_resource($this->gdimg_source)) {
+			$this->source_width  = ImageSX($this->gdimg_source);
+			$this->source_height = ImageSY($this->gdimg_source);
+			$this->DebugMessage('skipping SourceImageToGD() because $this->gdimg_source is already a resource ('.$this->source_width.'x'.$this->source_height.')', __FILE__, __LINE__);
+			return true;
+		}
+		$this->DebugMessage('starting SourceImageToGD()', __FILE__, __LINE__);
+
+		if ($this->ImageMagickThumbnailToGD()) {
+
+			// excellent, we have a thumbnailed source image
+			$this->DebugMessage('ImageMagickThumbnailToGD() succeeded', __FILE__, __LINE__);
+
+		} elseif (!$this->gdimg_source && $this->rawImageData) {
+
+			if ($this->md5s && ($this->md5s != md5($this->rawImageData))) {
+				return $this->ErrorImage('$this->md5s != md5($this->rawImageData)'."\n".'"'.$this->md5s.'" != '."\n".'"'.md5($this->rawImageData).'"');
+			}
+			$this->gdimg_source = $this->ImageCreateFromStringReplacement($this->rawImageData);
+			if (!$this->gdimg_source) {
+				return $this->ErrorImage('Unknown image type identified by "'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).') in SourceImageToGD()['.__LINE__.']');
+			}
+
+		} elseif (!$this->gdimg_source && $this->sourceFilename) {
+
+			if ($this->md5s && ($this->md5s != phpthumb_functions::md5_file_safe($this->sourceFilename))) {
+				return $this->ErrorImage('$this->md5s != md5(sourceFilename)'."\n".'"'.$this->md5s.'" != '."\n".'"'.phpthumb_functions::md5_file_safe($this->sourceFilename).'"');
+			}
+			switch (@$this->getimagesizeinfo[2]) {
+				case 1:
+				case 3:
+					// GIF or PNG input file may have transparency
+					$this->is_alpha = true;
+					break;
+			}
+			if (!$this->SourceImageIsTooLarge($this->source_width, $this->source_height)) {
+			$this->gdimg_source = $this->ImageCreateFromFilename($this->sourceFilename);
+			}
+
+		}
+
+		while (true) {
+			if ($this->gdimg_source) {
+				$this->DebugMessage('Not using EXIF thumbnail data because $this->gdimg_source is already set', __FILE__, __LINE__);
+				break;
+			}
+			if (!$this->exif_thumbnail_data) {
+				$this->DebugMessage('Not using EXIF thumbnail data because $this->exif_thumbnail_data is empty', __FILE__, __LINE__);
+				break;
+			}
+			if (ini_get('safe_mode')) {
+				if (!$this->SourceImageIsTooLarge($this->source_width, $this->source_height)) {
+					$this->DebugMessage('Using EXIF thumbnail data because source image too large and safe_mode enabled', __FILE__, __LINE__);
+					$this->aoe = true;
+				} else {
+					break;
+				}
+			} else {
+				if (!$this->config_use_exif_thumbnail_for_speed) {
+					$this->DebugMessage('Not using EXIF thumbnail data because $this->config_use_exif_thumbnail_for_speed is FALSE', __FILE__, __LINE__);
+					break;
+				}
+				if (($this->thumbnailCropX != 0) || ($this->thumbnailCropY != 0)) {
+					$this->DebugMessage('Not using EXIF thumbnail data because source cropping is enabled ('.$this->thumbnailCropX.','.$this->thumbnailCropY.')', __FILE__, __LINE__);
+					break;
+				}
+				if (($this->w > $this->exif_thumbnail_width) || ($this->h > $this->exif_thumbnail_height)) {
+					$this->DebugMessage('Not using EXIF thumbnail data because EXIF thumbnail is too small ('.$this->exif_thumbnail_width.'x'.$this->exif_thumbnail_height.' vs '.$this->w.'x'.$this->h.')', __FILE__, __LINE__);
+					break;
+				}
+				$source_ar = $this->source_width / $this->source_height;
+				$exif_ar   = $this->exif_thumbnail_width / $this->exif_thumbnail_height;
+				if (number_format($source_ar, 2) != number_format($exif_ar, 2)) {
+					$this->DebugMessage('not using EXIF thumbnail because $source_ar != $exif_ar ('.$source_ar.' != '.$exif_ar.')', __FILE__, __LINE__);
+					break;
+				}
+			}
+
+			// EXIF thumbnail exists, and is equal to or larger than destination thumbnail, and will be use as source image
+			$this->DebugMessage('Trying to use EXIF thumbnail as source image', __FILE__, __LINE__);
+
+			if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
+
+				$this->DebugMessage('Successfully using EXIF thumbnail as source image', __FILE__, __LINE__);
+				$this->gdimg_source   = $gdimg_exif_temp;
+				$this->source_width   = $this->exif_thumbnail_width;
+				$this->source_height  = $this->exif_thumbnail_height;
+				$this->thumbnailCropW = $this->source_width;
+				$this->thumbnailCropH = $this->source_height;
+				return true;
+
+			} else {
+				$this->DebugMessage('$this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false) failed', __FILE__, __LINE__);
+			}
+
+			break;
+		}
+
+		if (!$this->gdimg_source) {
+			$this->DebugMessage('$this->gdimg_source is still empty', __FILE__, __LINE__);
+
+			$this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__);
+
+			$imageHeader = '';
+			$gd_info = gd_info();
+			$GDreadSupport = false;
+			switch (@$this->getimagesizeinfo[2]) {
+				case 1:
+					$imageHeader = 'Content-Type: image/gif';
+					$GDreadSupport = (bool) @$gd_info['GIF Read Support'];
+					break;
+				case 2:
+					$imageHeader = 'Content-Type: image/jpeg';
+					$GDreadSupport = (bool) @$gd_info['JPG Support'];
+					break;
+				case 3:
+					$imageHeader = 'Content-Type: image/png';
+					$GDreadSupport = (bool) @$gd_info['PNG Support'];
+					break;
+			}
+			if ($imageHeader) {
+				// cannot create image for whatever reason (maybe ImageCreateFromJPEG et al are not available?)
+				// and ImageMagick is not available either, no choice but to output original (not resized/modified) data and exit
+				if ($this->config_error_die_on_source_failure) {
+					$errormessages = array();
+					$errormessages[] = 'All attempts to create GD image source failed.';
+					if ($this->fatalerror) {
+						$errormessages[] = $this->fatalerror;
+					}
+					if (ini_get('safe_mode')) {
+						$errormessages[] = 'Safe Mode enabled, therefore ImageMagick is unavailable. (disable Safe Mode if possible)';
+					} elseif (!$this->ImageMagickVersion()) {
+						$errormessages[] = 'ImageMagick is not installed (it is highly recommended that you install it).';
+					}
+					if ($this->SourceImageIsTooLarge($this->getimagesizeinfo[0], $this->getimagesizeinfo[1])) {
+						$memory_get_usage = (function_exists('memory_get_usage') ? memory_get_usage() : 0);
+						$errormessages[] = 'Source image is too large ('.$this->getimagesizeinfo[0].'x'.$this->getimagesizeinfo[1].' = '.number_format($this->getimagesizeinfo[0] * $this->getimagesizeinfo[1] / 1000000, 1).'Mpx, max='.number_format($this->config_max_source_pixels / 1000000, 1).'Mpx) for GD creation (either install ImageMagick or increase PHP memory_limit to at least '.ceil(($memory_get_usage + (5 * $this->getimagesizeinfo[0] * $this->getimagesizeinfo[1])) / 1000000).'M).';
+					} elseif (!$GDreadSupport) {
+						$errormessages[] = 'GD does not have read support for "'.$imageHeader.'".';
+					} else {
+						$errormessages[] = 'Source image probably corrupt.';
+					}
+					$this->ErrorImage(implode("\n", $errormessages));
+
+				} else {
+					$this->DebugMessage('All attempts to create GD image source failed ('.(ini_get('safe_mode') ? 'Safe Mode enabled, ImageMagick unavailable and source image probably too large for GD': ($GDreadSupport ? 'source image probably corrupt' : 'GD does not have read support for "'.$imageHeader.'"')).'), cannot generate thumbnail');
+					//$this->DebugMessage('All attempts to create GD image source failed ('.($GDreadSupport ? 'source image probably corrupt' : 'GD does not have read support for "'.$imageHeader.'"').'), outputing raw image', __FILE__, __LINE__);
+					//if (!$this->phpThumbDebug) {
+					//	header($imageHeader);
+					//	echo $this->rawImageData;
+					//	exit;
+					//}
+					return false;
+				}
+			}
+
+			//switch (substr($this->rawImageData, 0, 2)) {
+			//	case 'BM':
+			switch (@$this->getimagesizeinfo[2]) {
+				case 6:
+					ob_start();
+					if (!@include_once(dirname(__FILE__).'/phpthumb.bmp.php')) {
+						ob_end_clean();
+						return $this->ErrorImage('include_once('.dirname(__FILE__).'/phpthumb.bmp.php) failed');
+					}
+					ob_end_clean();
+					if ($fp = @fopen($this->sourceFilename, 'rb')) {
+						$this->rawImageData = '';
+						while (!feof($fp)) {
+							$this->rawImageData .= fread($fp, 32768);
+						}
+						fclose($fp);
+					}
+					$phpthumb_bmp = new phpthumb_bmp();
+					if ($this->gdimg_source = $phpthumb_bmp->phpthumb_bmp2gd($this->rawImageData, (phpthumb_functions::gd_version() >= 2.0))) {
+						$this->DebugMessage('$phpthumb_bmp->phpthumb_bmp2gd() succeeded', __FILE__, __LINE__);
+						break;
+					}
+					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on BMP source conversion' : 'phpthumb_bmp2gd() failed');
+					break;
+			//}
+			//switch (substr($this->rawImageData, 0, 4)) {
+			//	case 'II'."\x2A\x00":
+			//	case 'MM'."\x00\x2A":
+				case 7:
+				case 8:
+					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on TIFF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support TIFF source images without it');
+					break;
+
+				//case "\xD7\xCD\xC6\x9A":
+				//	return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on WMF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support WMF source images without it');
+				//	break;
+			}
+
+			if (!$this->gdimg_source) {
+				$HeaderFourBytes = '';
+				if ($fp = @fopen($this->sourceFilename, 'rb')) {
+					$HeaderFourBytes = fread($fp, 4);
+					fclose($fp);
+				}
+				if ($HeaderFourBytes == "\xD7\xCD\xC6\x9A") { // WMF
+					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on WMF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support WMF source images without it');
+				} elseif ($HeaderFourBytes == '%PDF') { // "%PDF"
+					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick and GhostScript are both required for PDF source images; GhostScript may not be properly configured' : 'ImageMagick and/or GhostScript are unavailable and phpThumb() does not support PDF source images without them');
+				} elseif (substr($HeaderFourBytes, 0, 3) == "\xFF\xD8\xFF") { // JPEG
+					return $this->ErrorImage('Image is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
+				} elseif ($HeaderFourBytes == '%PNG') { // "%PNG"
+					return $this->ErrorImage('Image is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
+				} elseif (substr($HeaderFourBytes, 0, 3) == 'GIF') { // GIF
+					return $this->ErrorImage('Image is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
+				}
+				return $this->ErrorImage('Unknown image type identified by "'.substr($HeaderFourBytes, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($HeaderFourBytes, 0, 4)).') in SourceImageToGD()['.__LINE__.']');
+
+			}
+		}
+
+		if (!$this->gdimg_source) {
+			if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
+				$this->DebugMessage('All other attempts failed, but successfully using EXIF thumbnail as source image', __FILE__, __LINE__);
+				$this->gdimg_source   = $gdimg_exif_temp;
+				// override allow-enlarging setting if EXIF thumbnail is the only source available
+				// otherwise thumbnails larger than the EXIF thumbnail will be created at EXIF size
+				$this->aoe = true;
+				return true;
+			}
+			return false;
+		}
+
+		$this->source_width  = ImageSX($this->gdimg_source);
+		$this->source_height = ImageSY($this->gdimg_source);
+		return true;
+	}
+
+
+	function phpThumbDebugVarDump($var) {
+		if (is_null($var)) {
+			return 'NULL';
+		} elseif (is_bool($var)) {
+			return ($var ? 'TRUE' : 'FALSE');
+		} elseif (is_string($var)) {
+			return 'string('.strlen($var).')'.str_repeat(' ', max(0, 3 - strlen(strlen($var)))).' "'.$var.'"';
+		} elseif (is_int($var)) {
+			return 'integer     '.$var;
+		} elseif (is_float($var)) {
+			return 'float       '.$var;
+		} elseif (is_array($var)) {
+			ob_start();
+			var_dump($var);
+			$vardumpoutput = ob_get_contents();
+			ob_end_clean();
+			return strtr($vardumpoutput, "\n\r\t", '   ');
+		}
+		return gettype($var);
+	}
+
+	function phpThumbDebug($level='') {
+		if ($level && ($this->phpThumbDebug !== $level)) {
+			return true;
+		}
+		if ($this->config_disable_debug) {
+			return $this->ErrorImage('phpThumbDebug disabled');
+		}
+
+		$FunctionsExistance  = array('exif_thumbnail', 'gd_info', 'image_type_to_mime_type', 'ImageCopyResampled', 'ImageCopyResized', 'ImageCreate', 'ImageCreateFromString', 'ImageCreateTrueColor', 'ImageIsTrueColor', 'ImageRotate', 'ImageTypes', 'version_compare', 'ImageCreateFromGIF', 'ImageCreateFromJPEG', 'ImageCreateFromPNG', 'ImageCreateFromWBMP', 'ImageCreateFromXBM', 'ImageCreateFromXPM', 'ImageCreateFromString', 'ImageCreateFromGD', 'ImageCreateFromGD2', 'ImageCreateFromGD2Part', 'ImageJPEG', 'ImageGIF', 'ImagePNG', 'ImageWBMP');
+		$ParameterNames      = array('src', 'new', 'w', 'h', 'f', 'q', 'sx', 'sy', 'sw', 'sh', 'far', 'bg', 'bc', 'file', 'goto', 'err', 'xto', 'ra', 'ar', 'aoe', 'iar', 'maxb');
+		$ConfigVariableNames = array('document_root', 'temp_directory', 'output_format', 'output_maxwidth', 'output_maxheight', 'error_message_image_default', 'error_bgcolor', 'error_textcolor', 'error_fontsize', 'error_die_on_error', 'error_silent_die_on_error', 'error_die_on_source_failure', 'nohotlink_enabled', 'nohotlink_valid_domains', 'nohotlink_erase_image', 'nohotlink_text_message', 'nooffsitelink_enabled', 'nooffsitelink_valid_domains', 'nooffsitelink_require_refer', 'nooffsitelink_erase_image', 'nooffsitelink_text_message', 'high_security_enabled', 'allow_src_above_docroot', 'allow_src_above_phpthumb', 'allow_parameter_file', 'allow_parameter_goto', 'max_source_pixels', 'use_exif_thumbnail_for_speed', 'border_hexcolor', 'background_hexcolor', 'ttf_directory', 'disable_pathinfo_parsing', 'disable_imagecopyresampled');
+		$OtherVariableNames  = array('phpThumbDebug', 'thumbnailQuality', 'thumbnailFormat', 'gdimg_output', 'gdimg_source', 'sourceFilename', 'source_width', 'source_height', 'thumbnailCropX', 'thumbnailCropY', 'thumbnailCropW', 'thumbnailCropH', 'exif_thumbnail_width', 'exif_thumbnail_height', 'exif_thumbnail_type', 'thumbnail_width', 'thumbnail_height', 'thumbnail_image_width', 'thumbnail_image_height');
+
+		$DebugOutput = array();
+		$DebugOutput[] = 'phpThumb() version          = '.$this->phpthumb_version;
+		$DebugOutput[] = 'phpversion()                = '.@phpversion();
+		$DebugOutput[] = 'PHP_OS                      = '.PHP_OS;
+		$DebugOutput[] = '__FILE__                    = '.__FILE__;
+		$DebugOutput[] = 'realpath(.)                 = '.@realpath('.');
+		$DebugOutput[] = '$_SERVER[PHP_SELF]          = '.@$_SERVER['PHP_SELF'];
+		$DebugOutput[] = '$_SERVER[HOST_NAME]         = '.@$_SERVER['HOST_NAME'];
+		$DebugOutput[] = '$_SERVER[HTTP_REFERER]      = '.@$_SERVER['HTTP_REFERER'];
+		$DebugOutput[] = '$_SERVER[QUERY_STRING]      = '.@$_SERVER['QUERY_STRING'];
+		$DebugOutput[] = '$_SERVER[PATH_INFO]         = '.@$_SERVER['PATH_INFO'];
+		$DebugOutput[] = '$_SERVER[DOCUMENT_ROOT]     = '.@$_SERVER['DOCUMENT_ROOT'];
+		$DebugOutput[] = 'getenv(DOCUMENT_ROOT)       = '.@getenv('DOCUMENT_ROOT');
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = 'get_magic_quotes_gpc()      = '.$this->phpThumbDebugVarDump(@get_magic_quotes_gpc());
+		$DebugOutput[] = 'get_magic_quotes_runtime()  = '.$this->phpThumbDebugVarDump(@get_magic_quotes_runtime());
+		$DebugOutput[] = 'error_reporting()           = '.$this->phpThumbDebugVarDump(error_reporting());
+		$DebugOutput[] = 'ini_get(error_reporting)    = '.$this->phpThumbDebugVarDump(@ini_get('error_reporting'));
+		$DebugOutput[] = 'ini_get(display_errors)     = '.$this->phpThumbDebugVarDump(@ini_get('display_errors'));
+		$DebugOutput[] = 'ini_get(allow_url_fopen)    = '.$this->phpThumbDebugVarDump(@ini_get('allow_url_fopen'));
+		$DebugOutput[] = 'ini_get(disable_functions)  = '.$this->phpThumbDebugVarDump(@ini_get('disable_functions'));
+		$DebugOutput[] = 'ini_get(safe_mode)          = '.$this->phpThumbDebugVarDump(@ini_get('safe_mode'));
+		$DebugOutput[] = 'ini_get(open_basedir)       = '.$this->phpThumbDebugVarDump(@ini_get('open_basedir'));
+		$DebugOutput[] = 'ini_get(max_execution_time) = '.$this->phpThumbDebugVarDump(@ini_get('max_execution_time'));
+		$DebugOutput[] = 'ini_get(memory_limit)       = '.$this->phpThumbDebugVarDump(@ini_get('memory_limit'));
+		$DebugOutput[] = 'get_cfg_var(memory_limit)   = '.$this->phpThumbDebugVarDump(@get_cfg_var('memory_limit'));
+		$DebugOutput[] = 'memory_get_usage()          = '.(function_exists('memory_get_usage') ? $this->phpThumbDebugVarDump(@memory_get_usage()) : 'n/a');
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = '$this->config_prefer_imagemagick            = '.$this->phpThumbDebugVarDump($this->config_prefer_imagemagick);
+		$DebugOutput[] = '$this->config_imagemagick_path              = '.$this->phpThumbDebugVarDump($this->config_imagemagick_path);
+		$DebugOutput[] = '$this->ImageMagickWhichConvert()            = '.$this->ImageMagickWhichConvert();
+		$IMpathUsed = ($this->config_imagemagick_path ? $this->config_imagemagick_path : $this->ImageMagickWhichConvert());
+		$DebugOutput[] = '[actual ImageMagick path used]              = '.$this->phpThumbDebugVarDump($IMpathUsed);
+		$DebugOutput[] = 'file_exists([actual ImageMagick path used]) = '.$this->phpThumbDebugVarDump(@file_exists($IMpathUsed));
+		$DebugOutput[] = 'ImageMagickVersion(false)                   = '.$this->ImageMagickVersion(false);
+		$DebugOutput[] = 'ImageMagickVersion(true)                    = '.$this->ImageMagickVersion(true);
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = '$this->config_cache_directory               = '.$this->phpThumbDebugVarDump($this->config_cache_directory);
+		$DebugOutput[] = '$this->config_cache_directory_depth         = '.$this->phpThumbDebugVarDump($this->config_cache_directory_depth);
+		$DebugOutput[] = '$this->config_cache_disable_warning         = '.$this->phpThumbDebugVarDump($this->config_cache_disable_warning);
+		$DebugOutput[] = '$this->config_cache_maxage                  = '.$this->phpThumbDebugVarDump($this->config_cache_maxage);
+		$DebugOutput[] = '$this->config_cache_maxsize                 = '.$this->phpThumbDebugVarDump($this->config_cache_maxsize);
+		$DebugOutput[] = '$this->config_cache_maxfiles                = '.$this->phpThumbDebugVarDump($this->config_cache_maxfiles);
+		$DebugOutput[] = '$this->config_cache_force_passthru          = '.$this->phpThumbDebugVarDump($this->config_cache_force_passthru);
+		$DebugOutput[] = '$this->cache_filename                       = '.$this->phpThumbDebugVarDump($this->cache_filename);
+		$DebugOutput[] = 'is_readable($this->config_cache_directory)  = '.$this->phpThumbDebugVarDump(@is_readable($this->config_cache_directory));
+		$DebugOutput[] = 'is_writable($this->config_cache_directory)  = '.$this->phpThumbDebugVarDump(@is_writable($this->config_cache_directory));
+		$DebugOutput[] = 'is_readable($this->cache_filename)          = '.$this->phpThumbDebugVarDump(@is_readable($this->cache_filename));
+		$DebugOutput[] = 'is_writable($this->cache_filename)          = '.(@file_exists($this->cache_filename) ? $this->phpThumbDebugVarDump(@is_writable($this->cache_filename)) : 'n/a');
+		$DebugOutput[] = '';
+
+		foreach ($ConfigVariableNames as $varname) {
+			$varname = 'config_'.$varname;
+			$value = $this->$varname;
+			$DebugOutput[] = '$this->'.str_pad($varname, 37, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = '';
+		foreach ($OtherVariableNames as $varname) {
+			$value = $this->$varname;
+			$DebugOutput[] = '$this->'.str_pad($varname, 27, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = 'strlen($this->rawImageData)        = '.strlen(@$this->rawImageData);
+		$DebugOutput[] = 'strlen($this->exif_thumbnail_data) = '.strlen(@$this->exif_thumbnail_data);
+		$DebugOutput[] = '';
+
+		foreach ($ParameterNames as $varname) {
+			$value = $this->$varname;
+			$DebugOutput[] = '$this->'.str_pad($varname, 4, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = '';
+
+		foreach ($FunctionsExistance as $functionname) {
+			$DebugOutput[] = 'builtin_function_exists('.$functionname.')'.str_repeat(' ', 23 - strlen($functionname)).' = '.$this->phpThumbDebugVarDump(phpthumb_functions::builtin_function_exists($functionname));
+		}
+		$DebugOutput[] = '';
+
+		$gd_info = gd_info();
+		foreach ($gd_info as $key => $value) {
+			$DebugOutput[] = 'gd_info.'.str_pad($key, 34, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = '';
+
+		$exif_info = phpthumb_functions::exif_info();
+		foreach ($exif_info as $key => $value) {
+			$DebugOutput[] = 'exif_info.'.str_pad($key, 26, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+		}
+		$DebugOutput[] = '';
+
+		if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray(dirname(@$_SERVER['PHP_SELF']))) {
+			foreach ($ApacheLookupURIarray as $key => $value) {
+				$DebugOutput[] = 'ApacheLookupURIarray.'.str_pad($key, 15, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
+			}
+		} else {
+				$DebugOutput[] = 'ApacheLookupURIarray() -- FAILED';
+		}
+		$DebugOutput[] = '';
+
+		if (isset($_GET) && is_array($_GET)) {
+			foreach ($_GET as $key => $value) {
+				$DebugOutput[] = '$_GET['.$key.']'.str_repeat(' ', 30 - strlen($key)).'= '.$this->phpThumbDebugVarDump($value);
+			}
+		}
+		if (isset($_POST) && is_array($_POST)) {
+			foreach ($_POST as $key => $value) {
+				$DebugOutput[] = '$_POST['.$key.']'.str_repeat(' ', 29 - strlen($key)).'= '.$this->phpThumbDebugVarDump($value);
+			}
+		}
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = '$this->debugmessages:';
+		foreach ($this->debugmessages as $errorstring) {
+			$DebugOutput[] = '  * '.$errorstring;
+		}
+		$DebugOutput[] = '';
+
+		$DebugOutput[] = '$this->debugtiming:';
+		foreach ($this->debugtiming as $timestamp => $timingstring) {
+			$DebugOutput[] = '  * '.$timestamp.' '.$timingstring;
+		}
+		$DebugOutput[] = '  * Total processing time: '.number_format(max(array_keys($this->debugtiming)) - min(array_keys($this->debugtiming)), 6);
+
+		$this->f = (isset($_GET['f']) ? $_GET['f'] : $this->f); // debug modes 0-2 don't recognize text mode otherwise
+		return $this->ErrorImage(implode("\n", $DebugOutput), 700, 500, true);
+	}
+
+	function ErrorImage($text, $width=0, $height=0, $forcedisplay=false) {
+		$width  = ($width  ? $width  : $this->config_error_image_width);
+		$height = ($height ? $height : $this->config_error_image_height);
+
+		$text = 'phpThumb() v'.$this->phpthumb_version."\n\n".$text;
+		if ($this->config_disable_debug) {
+			$text = 'Error messages disabled';
+		}
+
+		$this->DebugMessage($text, __FILE__, __LINE__);
+		if ($this->phpThumbDebug && !$forcedisplay) {
+			return false;
+		}
+		if (!$this->config_error_die_on_error && !$forcedisplay) {
+			$this->fatalerror = $text;
+			return false;
+		}
+		if ($this->config_error_silent_die_on_error) {
+			exit;
+		}
+		if ($this->err || $this->config_error_message_image_default) {
+			// Show generic custom error image instead of error message
+			// for use on production sites where you don't want debug messages
+			if ($this->err == 'showerror') {
+				// fall through and actually show error message even if default error image is set
+			} else {
+				header('Location: '.($this->err ? $this->err : $this->config_error_message_image_default));
+				exit;
+			}
+		}
+		$this->setOutputFormat();
+		if (!$this->thumbnailFormat || (phpthumb_functions::gd_version() < 1)) {
+			$this->thumbnailFormat = 'text';
+		}
+		if (@$this->thumbnailFormat == 'text') {
+			// bypass all GD functions and output text error message
+			die('<pre>'.$text.'</pre>');
+		}
+
+		$FontWidth  = ImageFontWidth($this->config_error_fontsize);
+		$FontHeight = ImageFontHeight($this->config_error_fontsize);
+
+		$LinesOfText = explode("\n", @wordwrap($text, floor($width / $FontWidth), "\n", true));
+		$height = max($height, count($LinesOfText) * $FontHeight);
+
+		$headers_file = '';
+		$headers_line = '';
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=') && headers_sent($headers_file, $headers_line)) {
+
+			echo "\n".'**Headers already sent in file "'.$headers_file.'" on line "'.$headers_line.'", dumping error message as text:**<br><pre>'."\n\n".$text."\n".'</pre>';
+
+		} elseif (headers_sent()) {
+
+			echo "\n".'**Headers already sent, dumping error message as text:**<br><pre>'."\n\n".$text."\n".'</pre>';
+
+		} elseif ($gdimg_error = ImageCreate($width, $height)) {
+
+			$background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_error, $this->config_error_bgcolor,   true);
+			$text_color       = phpthumb_functions::ImageHexColorAllocate($gdimg_error, $this->config_error_textcolor, true);
+			ImageFilledRectangle($gdimg_error, 0, 0, $width, $height, $background_color);
+			$lineYoffset = 0;
+			foreach ($LinesOfText as $line) {
+				ImageString($gdimg_error, $this->config_error_fontsize, 2, $lineYoffset, $line, $text_color);
+				$lineYoffset += $FontHeight;
+			}
+			if (function_exists('ImageTypes')) {
+				$imagetypes = ImageTypes();
+				if ($imagetypes & IMG_PNG) {
+					header('Content-Type: image/png');
+					ImagePNG($gdimg_error);
+				} elseif ($imagetypes & IMG_GIF) {
+					header('Content-Type: image/gif');
+					ImageGIF($gdimg_error);
+				} elseif ($imagetypes & IMG_JPG) {
+					header('Content-Type: image/jpeg');
+					ImageJPEG($gdimg_error);
+				} elseif ($imagetypes & IMG_WBMP) {
+					header('Content-Type: image/vnd.wap.wbmp');
+					ImageWBMP($gdimg_error);
+				}
+			}
+			ImageDestroy($gdimg_error);
+
+		}
+		if (!headers_sent()) {
+			echo "\n".'**Failed to send graphical error image, dumping error message as text:**<br>'."\n\n".$text;
+		}
+		exit;
+		return true;
+	}
+
+	function ImageCreateFromStringReplacement(&$RawImageData, $DieOnErrors=false) {
+		// there are serious bugs in the non-bundled versions of GD which may cause
+		// PHP to segfault when calling ImageCreateFromString() - avoid if at all possible
+		// when not using a bundled version of GD2
+		if (!phpthumb_functions::gd_version()) {
+			if ($DieOnErrors) {
+				if (!headers_sent()) {
+					// base64-encoded error image in GIF format
+					$ERROR_NOGD = 'R0lGODlhIAAgALMAAAAAABQUFCQkJDY2NkZGRldXV2ZmZnJycoaGhpSUlKWlpbe3t8XFxdXV1eTk5P7+/iwAAAAAIAAgAAAE/vDJSau9WILtTAACUinDNijZtAHfCojS4W5H+qxD8xibIDE9h0OwWaRWDIljJSkUJYsN4bihMB8th3IToAKs1VtYM75cyV8sZ8vygtOE5yMKmGbO4jRdICQCjHdlZzwzNW4qZSQmKDaNjhUMBX4BBAlmMywFSRWEmAI6b5gAlhNxokGhooAIK5o/pi9vEw4Lfj4OLTAUpj6IabMtCwlSFw0DCKBoFqwAB04AjI54PyZ+yY3TD0ss2YcVmN/gvpcu4TOyFivWqYJlbAHPpOntvxNAACcmGHjZzAZqzSzcq5fNjxFmAFw9iFRunD1epU6tsIPmFCAJnWYE0FURk7wJDA0MTKpEzoWAAskiAAA7';
+					header('Content-Type: image/gif');
+					echo base64_decode($ERROR_NOGD);
+				} else {
+					echo '*** ERROR: No PHP-GD support available ***';
+				}
+				exit;
+			} else {
+				$this->DebugMessage('ImageCreateFromStringReplacement() failed: gd_version says "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
+				return false;
+			}
+		}
+		if (phpthumb_functions::gd_is_bundled()) {
+			$this->DebugMessage('ImageCreateFromStringReplacement() calling built-in ImageCreateFromString()', __FILE__, __LINE__);
+			return @ImageCreateFromString($RawImageData);
+		}
+		if (ini_get('safe_mode')) {
+			$this->DebugMessage('ImageCreateFromStringReplacement() failed: cannot create temp file in SAFE_MODE', __FILE__, __LINE__);
+			return false;
+		}
+
+		switch (substr($RawImageData, 0, 3)) {
+			case 'GIF':
+				$ICFSreplacementFunctionName = 'ImageCreateFromGIF';
+				break;
+			case "\xFF\xD8\xFF":
+				$ICFSreplacementFunctionName = 'ImageCreateFromJPEG';
+				break;
+			case "\x89".'PN':
+				$ICFSreplacementFunctionName = 'ImageCreateFromPNG';
+				break;
+			default:
+				$this->DebugMessage('ImageCreateFromStringReplacement() failed: unknown fileformat signature "'.phpthumb_functions::HexCharDisplay(substr($RawImageData, 0, 3)).'"', __FILE__, __LINE__);
+				return false;
+				break;
+		}
+		if ($tempnam = $this->phpThumb_tempnam()) {
+			if ($fp_tempnam = @fopen($tempnam, 'wb')) {
+				fwrite($fp_tempnam, $RawImageData);
+				fclose($fp_tempnam);
+				if (($ICFSreplacementFunctionName == 'ImageCreateFromGIF') && !function_exists($ICFSreplacementFunctionName)) {
+
+					// Need to create from GIF file, but ImageCreateFromGIF does not exist
+					ob_start();
+					if (!@include_once(dirname(__FILE__).'/phpthumb.gif.php')) {
+						$ErrorMessage = 'Failed to include required file "'.dirname(__FILE__).'/phpthumb.gif.php" in '.__FILE__.' on line '.__LINE__;
+						$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
+					}
+					ob_end_clean();
+					// gif_loadFileToGDimageResource() cannot read from raw data, write to file first
+					if ($tempfilename = $this->phpThumb_tempnam()) {
+						if ($fp_tempfile = @fopen($tempfilename, 'wb')) {
+							fwrite($fp_tempfile, $RawImageData);
+							fclose($fp_tempfile);
+							$gdimg_source = gif_loadFileToGDimageResource($tempfilename);
+							$this->DebugMessage('gif_loadFileToGDimageResource('.$tempfilename.') completed', __FILE__, __LINE__);
+							unlink($tempfilename);
+							return $gdimg_source;
+							break;
+						} else {
+							$ErrorMessage = 'Failed to open tempfile in '.__FILE__.' on line '.__LINE__;
+							$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
+						}
+					} else {
+						$ErrorMessage = 'Failed to open generate tempfile name in '.__FILE__.' on line '.__LINE__;
+						$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
+					}
+
+				} elseif (function_exists($ICFSreplacementFunctionName) && ($gdimg_source = @$ICFSreplacementFunctionName($tempnam))) {
+
+					// great
+					$this->DebugMessage($ICFSreplacementFunctionName.'('.$tempnam.') succeeded', __FILE__, __LINE__);
+					unlink($tempnam);
+					return $gdimg_source;
+
+				} else {
+
+					// GD functions not available, or failed to create image
+					$this->DebugMessage($ICFSreplacementFunctionName.'('.$tempnam.') '.(function_exists($ICFSreplacementFunctionName) ? 'failed' : 'does not exist'), __FILE__, __LINE__);
+					if (isset($_GET['phpThumbDebug'])) {
+						$this->phpThumbDebug();
+					}
+
+				}
+			} else {
+				$ErrorMessage = 'Failed to fopen('.$tempnam.', "wb") in '.__FILE__.' on line '.__LINE__."\n".'You may need to set $PHPTHUMB_CONFIG[temp_directory] in phpThumb.config.php';
+				$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
+			}
+			@unlink($tempnam);
+		} else {
+			$ErrorMessage = 'Failed to generate phpThumb_tempnam() in '.__FILE__.' on line '.__LINE__."\n".'You may need to set $PHPTHUMB_CONFIG[temp_directory] in phpThumb.config.php';
+		}
+		if ($DieOnErrors && $ErrorMessage) {
+			return $this->ErrorImage($ErrorMessage);
+		}
+		return false;
+	}
+
+	function ImageResizeFunction(&$dst_im, &$src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH) {
+		$this->DebugMessage('ImageResizeFunction($o, $s, '.$dstX.', '.$dstY.', '.$srcX.', '.$srcY.', '.$dstW.', '.$dstH.', '.$srcW.', '.$srcH.')', __FILE__, __LINE__);
+		if (phpthumb_functions::gd_version() >= 2.0) {
+			if ($this->config_disable_imagecopyresampled) {
+				return phpthumb_functions::ImageCopyResampleBicubic($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
+			}
+			return ImageCopyResampled($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
+		}
+		return ImageCopyResized($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
+	}
+
+	function phpThumb_tempnam() {
+		$tempnam = realpath(tempnam($this->config_temp_directory, 'pThumb'));
+		$this->DebugMessage('phpThumb_tempnam() returning "'.$tempnam.'"', __FILE__, __LINE__);
+		return $tempnam;
+	}
+
+	function DebugMessage($message, $file='', $line='') {
+		$this->debugmessages[] = $message.($file ? ' in file "'.(basename($file) ? basename($file) : $file).'"' : '').($line ? ' on line '.$line : '');
+		return true;
+	}
+
+	function DebugTimingMessage($message, $file='', $line='', $timestamp=0) {
+		if (!$timestamp) {
+			$timestamp = array_sum(explode(' ', microtime()));
+		}
+		$this->debugtiming[number_format($timestamp, 6, '.', '')] = ': '.$message.($file ? ' in file "'.(basename($file) ? basename($file) : $file).'"' : '').($line ? ' on line '.$line : '');
+		return true;
+	}
+
+}
+
+?>
Index: /afridex/plugins/Flutter/canvas-globals.php
===================================================================
--- /afridex/plugins/Flutter/canvas-globals.php (revision 21)
+++ /afridex/plugins/Flutter/canvas-globals.php (revision 21)
@@ -0,0 +1,56 @@
+<?php
+/*
+
+___Canvas Globals_______________________________________________
+
+Canvas global variables and classes.
+
+________________________________________________________________
+
+*/
+
+
+// Class and variable definitions
+class importedFunction { // Used to store individual function data
+	var $functionName;
+	var $functionAuthor;
+	var $functionUri;
+	var $functionDesc;
+	var $variables;
+}
+$zones = array();
+
+// XML parsing variables
+$current_function = "";
+$current_variable = "";
+$current_tag = "";
+
+// Canvas tables
+class canvas {
+	var $main;
+	var $duplicates;
+	var $variables;
+	var $options;
+	var $zone_options;
+	var $ink;
+}
+
+$canvas = new canvas();
+global $wpdb;
+$canvas->main = $wpdb->prefix . "canvas";
+$canvas->duplicates = (isset($current_blog)?$wpdb->base_prefix:$wpdb->prefix) . "canvas_duplicates";
+$canvas->variables = $wpdb->prefix . "canvas_variables";
+$canvas->options = $wpdb->prefix . "canvas_var_options";
+$canvas->zone_options = $wpdb->prefix . "canvas_zone_options";
+$canvas->ink = $wpdb->prefix . "ink";
+
+// Canvas paths
+preg_match('/wp-content(.*)(canvas-globals\.php)$/',__FILE__,$canvaspath);
+$canvaspath = str_replace('\\', '/', $canvaspath);
+define(CANVASPATH, str_replace('/canvas-globals.php', '', str_replace('\\', '/', __FILE__)));
+define(CANVASURI, get_bloginfo('wpurl').'/wp-content'.$canvaspath[1]); //returns "http://127.0.0.1/wp-content/plugins/Flutter/"
+define(FLUTTER_URI_RELATIVE, 'wp-content'.$canvaspath[1]); //returns "wp-content/plugins/Flutter/"
+
+
+
+?>
Index: /afridex/plugins/Flutter/ajax/canvas-save-zone-options.php
===================================================================
--- /afridex/plugins/Flutter/ajax/canvas-save-zone-options.php (revision 21)
+++ /afridex/plugins/Flutter/ajax/canvas-save-zone-options.php (revision 21)
@@ -0,0 +1,25 @@
+<?php
+
+// Parse the parameters from the Ajax.Request
+
+if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+	
+	global $wpdb, $canvas;
+	
+	$theme = get_option('template');
+	foreach($_GET as $key => $value) {
+		if(!empty($value)) {
+			$option_name = 'zone_handler';
+			$zone = str_replace('zoneoption_', '', $key);
+			
+ 			if($wpdb->get_var("SELECT option_id FROM ".$canvas->zone_options." WHERE option_name ='$option_name' AND zone = '$zone' AND theme = '$theme'"))
+ 				$wpdb->query("UPDATE ".$canvas->zone_options." SET value = '$value' WHERE option_name = '$option_name' AND zone = '$zone' AND theme = '$theme'");
+ 			else $wpdb->query("INSERT INTO ".$canvas->zone_options." (zone, option_name, value, theme) VALUES ('$zone', '$option_name', '$value', '$theme')");
+		}
+	}
+}
+?>
Index: /afridex/plugins/Flutter/ajax/canvas-save-option.php
===================================================================
--- /afridex/plugins/Flutter/ajax/canvas-save-option.php (revision 21)
+++ /afridex/plugins/Flutter/ajax/canvas-save-option.php (revision 21)
@@ -0,0 +1,18 @@
+<?php
+
+//if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {	
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+	include_once('../RCCWP_Options.php');
+	if($_POST)
+	foreach($_POST as $key => $value) {
+		$key = trim(urldecode($key));
+		$value = trim(urldecode($value));
+		//if($value == 1) $value = 'true';
+		//	elseif($value == 0) $value = 'false';
+		RCCWP_Options::Set($key, $value);
+	}
+//}
+?>
Index: /afridex/plugins/Flutter/ajax/canvas-save-layout.php
===================================================================
--- /afridex/plugins/Flutter/ajax/canvas-save-layout.php (revision 21)
+++ /afridex/plugins/Flutter/ajax/canvas-save-layout.php (revision 21)
@@ -0,0 +1,75 @@
+<?php
+
+// Parse the parameters from the Ajax.Request
+
+//if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+	
+	global $wpdb, $canvas;
+	//print_r($_GET);
+	foreach ($_GET as $key => $values) {
+	  if (!empty($values)) {
+		if (substr($key, 0, 1) != "_"){
+			if ($key != "shelf"){
+				$template_name = $_GET["_".$key."_template_name"][0];
+				$template_size = $_GET["_".$key."_template_size"][0];
+				//echo $block_template;
+			}
+			else{
+				$template_name = "";
+			}
+
+			foreach ($values as $k => $value) {
+		
+			if ($key != "_") {
+			
+				/*$pos = strrpos($value, "_");
+				$temp = substr($value, 0, $pos);
+				$block_id = substr($value, $pos+1, strlen($value));
+				$pos = strrpos($temp, "_");
+				$oldlocation = substr($temp, 0, $pos);
+				$oldposition = substr($temp, $pos+1, strlen($temp));*/
+				
+				$block_id = $value;
+				$position = $k + 1;
+				$newzone = $key;
+				
+				if (!$wpdb->get_var("SELECT template_name FROM ".$canvas->main." WHERE block_id='$block_id'")){
+					// template name has not been set before
+					$template_name_query= " , template_name='$template_name' ";
+					$template_size_query= " , template_size='$template_size' ";
+				}
+					if ($newzone == "shelf")
+						$wpdb->query("UPDATE ".$canvas->main." 
+								SET 	zone='$newzone', 
+									position='$position',
+									template_name='',
+									template_size=''
+								WHERE block_id='$block_id'");
+					else
+						$wpdb->query("UPDATE ".$canvas->main." 
+								SET 	zone='$newzone', 
+									position='$position'
+									$template_size_query
+									$template_name_query
+								WHERE block_id='$block_id'");
+
+					$wpdb->query("UPDATE ".$canvas->main." SET zone='$newzone' WHERE block_id='$block_id'");
+				//}
+			}
+			}
+		}
+	  }
+	}
+
+	// Update modules info
+	require_once('../canvas-core.php');
+	$dir = CANVASPATH.'/modules/';
+	$positions = $wpdb->get_var("SELECT MAX(position) FROM ".$canvas->main." WHERE theme = '".get_option('template')."' AND zone = 'shelf'");
+	canvas_import_plugins($position, $dir);
+
+//}
+?>
Index: /afridex/plugins/Flutter/ajax/canvas-import.php
===================================================================
--- /afridex/plugins/Flutter/ajax/canvas-import.php (revision 21)
+++ /afridex/plugins/Flutter/ajax/canvas-import.php (revision 21)
@@ -0,0 +1,14 @@
+<?php
+
+// Import the layout from XML
+
+if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+	
+	canvas_import_layout($_POST['path_to_xml']);
+	if($_POST["restore"] == 'true') echo 'Success! Now, <a href="javascript:window.location.reload()">reload your Canvas</a>.';
+}
+?>
Index: /afridex/plugins/Flutter/ajax/canvas-save-plugin.php
===================================================================
--- /afridex/plugins/Flutter/ajax/canvas-save-plugin.php (revision 21)
+++ /afridex/plugins/Flutter/ajax/canvas-save-plugin.php (revision 21)
@@ -0,0 +1,63 @@
+<?php
+
+// Parse the parameters from the Ajax.Request
+
+//if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+	
+	global $wpdb, $canvas;
+	$parent = $_GET['parent'];
+	$template_name = $_GET['template_name'];
+	$template_size = $_GET['template_size'];
+	$listitems = array();
+
+	$wpdb->query("UPDATE ".$canvas->main." 
+			SET 	template_name='$template_name',
+				template_size='$template_size'
+			WHERE block_id='$parent'");
+
+	// Save the new values to the database
+	foreach ($_GET as $key => $value) {
+		$key = trim(urldecode($key));
+		$value = trim(urldecode($value));
+
+		if (($key != "_")&&($key != "parent")&&($key != "checked")) {
+			if(strstr($key, 'canvaslist')) {
+				$key = preg_replace('/^([0-9]*)canvaslist_/','',$key);
+				if(!in_array($key, $listitems)) $listitems[] = $key;
+				$$key .= $value."|";
+				continue;
+			}
+			if($var_id = $wpdb->get_var("SELECT variable_id FROM ".$canvas->variables." WHERE variable_name = '$key' AND parent='$parent'")) {
+				$wpdb->query("UPDATE ".$canvas->variables." SET value='$value' WHERE variable_name='$key' AND parent='$parent'");	 
+				$updated_vars[] = $var_id;
+			}
+		}
+	}
+
+	// Handle list items
+	if($listitems) {
+		foreach($listitems as $item) {
+			$new_value = preg_replace('/(\|*)$/', '', $$item);
+			$wpdb->query("UPDATE ".$canvas->variables." SET value='$new_value' WHERE variable_name='$item' AND parent='$parent'");
+		}
+	}
+
+	// Save any values that weren't passed in the GET string, eg. empty checkboxes
+	$variables = $wpdb->get_results("SELECT variable_id,type FROM ".$canvas->variables." WHERE parent='$parent' AND variable_id NOT IN ('".implode("','", $updated_vars)."')");
+	
+	if (!empty($variables)) {
+		foreach ($variables as $variable) {
+			// Here's where we define what the default "false" value is for each variable type
+			switch ($variable->type) {
+				case "Boolean":
+					$wpdb->query("UPDATE ".$canvas->variables." SET value='0' WHERE variable_id='$variable->variable_id'");
+				break;
+			}
+		}
+	}
+//}
+?>
Index: /afridex/plugins/Flutter/ajax/canvas-populate-listbox.php
===================================================================
--- /afridex/plugins/Flutter/ajax/canvas-populate-listbox.php (revision 21)
+++ /afridex/plugins/Flutter/ajax/canvas-populate-listbox.php (revision 21)
@@ -0,0 +1,79 @@
+<?php
+
+// Import the layout from XML
+
+//if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+	if (isset($_GET['mod_name'])){
+		$module_name = $_GET['mod_name'];
+		$selected_template_size = $_GET['template_size'];
+
+		$moduleTemplatesFolder = dirname(__FILE__)."/../modules/".$module_name."/templates/";
+
+		if ($_GET['template_name']=="") {
+			// Get first template
+			
+	
+			$templatesNamesStr = "";	
+			if ($handle = @opendir($moduleTemplatesFolder)) {
+				while (false !== ($file = readdir($handle))) { 
+					if ($file!= "." && $file!=".."){
+						$_GET['template_name'] = $file;	
+						break;
+					}	
+				}
+				closedir($handle);
+			}
+		}
+
+
+		// Load module template sizes
+		$moduleTemplateFolder = $moduleTemplatesFolder.$_GET['template_name'];
+		$otherSizesStr = "";
+	
+		if ($handle = opendir($moduleTemplateFolder)) {
+			while (false !== ($file = readdir($handle))) { 
+				$set_selected = "";
+				if (is_numeric($file)){
+					if ($selected_template_size == $file) $set_selected = " selected='selected' ";
+					$otherSizesStr = $otherSizesStr."<option $set_selected value='".$file."'>".$file."</option>";
+				}
+				else{
+					$t_size_val = 0;
+					switch($file){
+						case "small":
+							$t_size_val = -1;
+							break;
+						case "medium":
+							$t_size_val = -2;
+							break;
+						case "large":
+							$t_size_val = -3;
+							break;
+						case "full":
+							$t_size_val = -4;
+							break;
+		
+					}
+					if ($t_size_val<0){
+						if ($selected_template_size == $t_size_val) $set_selected = " selected='selected' ";
+						$otherSizesStr = $otherSizesStr."<option $set_selected value='".$t_size_val."'>".$file."</option>";	
+					}
+				}
+					
+			}
+		
+			closedir($handle);
+			echo $otherSizesStr;
+		}
+		
+
+	}
+	
+//}
+
+
+?>
Index: /afridex/plugins/Flutter/ajax/canvas-rescan.php
===================================================================
--- /afridex/plugins/Flutter/ajax/canvas-rescan.php (revision 21)
+++ /afridex/plugins/Flutter/ajax/canvas-rescan.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+
+	if($_GET["action"] == 'rescan')
+		if($message = canvas_reload()) echo '<p>The Canvas block and plugin directories were successfully scanned.</p>'."\n".$message;
+			else echo '<p>An error was encountered while rescanning. Please notify Canvas <a href="http://forums.freshpursuits.com/">support</a>.</p>';
+	if($_GET["action"] == 'reinstall') 
+		if(canvas_clean_install()) echo '<p>Canvas has been successfully reinstalled.</p>';
+			else echo '<p>An error was encountered during installation. Please notify Canvas <a href="http://forums.freshpursuits.com/">support</a>.</p>';
+	if($_GET["action"] == 'restore') 
+		if(canvas_install_options()) echo '<p>Canvas defaults have been restored.</p>';
+			else echo '<p>An error was encountered during restoration. Please notify Canvas <a href="http://forums.freshpursuits.com/">support</a>.</p>';
+}
+?>
Index: /afridex/plugins/Flutter/ajax/canvas-management_ajax.php
===================================================================
--- /afridex/plugins/Flutter/ajax/canvas-management_ajax.php (revision 21)
+++ /afridex/plugins/Flutter/ajax/canvas-management_ajax.php (revision 21)
@@ -0,0 +1,33 @@
+<?php
+
+if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
+	require( dirname(__FILE__) . '/../../../../wp-config.php' );
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+		die("Athentication failed!");
+
+	
+	global $wpdb, $canvas;
+	
+	if(isset($_GET["delete"])) canvas_delete_block($_GET["delete"]);
+	if(isset($_GET["duplicate"])) canvas_create_management_block(canvas_duplicate_block($_GET["duplicate"]),' style="display: none"');
+	
+	if($_GET["update"] == 'all') {
+		$blocks = $wpdb->get_results("SELECT * FROM ".$canvas->main." WHERE theme = '".get_option('template')."' ORDER BY module_title");
+		$delete_button = get_option('canvas_allow_deletion');
+		foreach($blocks as $block) {
+			// Get module name
+			include_once('RCCWP_CustomWriteModule.php');
+			$customWriteModule = RCCWP_CustomWriteModule::Get($block->module_id);
+			$block->module_name = $customWriteModule->name;
+
+			canvas_create_management_block($block,'',$delete_button);
+		}
+	}
+	if(isset($_POST["rename"]) && $_POST["rename"] == 'true') {
+		$module_title = urldecode($_POST["value"]);
+		$block_id = str_replace('title_','',$_POST["id"]);
+		$wpdb->query("UPDATE ".$canvas->main." SET module_title='$module_title' WHERE block_id='$block_id'");
+		echo $module_title;
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_CreateCustomFieldPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CreateCustomFieldPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CreateCustomFieldPage.php (revision 21)
@@ -0,0 +1,740 @@
+<?php
+include_once('RCCWP_CustomField.php');
+
+class RCCWP_CreateCustomFieldPage
+{
+	function Main()
+	{
+		global $FIELD_TYPES;
+		$customGroupID = $_REQUEST['custom-group-id'];
+		?>
+  	
+  		<div class="wrap">
+	  	
+  		<h2>Create Custom Field</h2>
+  		<br class="clear" />
+  		<?php
+		if (isset($_GET['err_msg'])) :
+			switch ($_GET['err_msg']){
+				case -1:
+				?>
+				<div class="error"><p> A field with the same name already exists in this write panel. Please choose a different name.</p></div>
+				<?php
+				}
+		endif;
+		?>
+  			
+  		<form action="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('continue-create-custom-field')?>" method="post" name="create_custom_field_form" id="create-custom-field-form" onsubmit="return checkEmpty();" autocomplete="off">
+
+		<?php if(isset($_GET['custom-group-id']) && !empty($_GET['custom-group-id'])) { ?>
+  			<input type="hidden" name="custom-group-id" value="<?php echo $_GET['custom-group-id']?>">
+		<?php } ?>
+		<?php if(isset($_POST['custom-group-id']) && !empty($_POST['custom-group-id'])) { ?>
+  			<input type="hidden" name="custom-group-id" value="<?php echo $_POST['custom-group-id']?>">
+
+		<?php } ?>
+		
+		
+		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+		<tbody>
+		<tr valign="top">
+			<th scope="row">Name:</th>
+			<td>  
+				<input name="custom-field-name" id="custom-field-name" size="40" type="text"  onkeyup="ajax_showOptions(this,'getFieldsByLetters',event)"/>
+				<input type="hidden" id="custom-field-name_hidden" name="custom-field-name_hidden" onchange="copyField();">
+				<p>
+					Type a unique name for the field, the name must be unique among all fields 
+					in this panel. The name of the field is the key by which you can retrieve 
+					the field value later.
+				</p>
+				<p>
+					As you type the name, Flutter will display fields from other
+					panels that are similar to the name you typed. If you selected one of them,
+					Flutter will create a field similar to the chosen one.
+				</p>
+			</td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row">Label:</th>
+			<td>
+				<input name="custom-field-description" id="custom-field-description" size="40" type="text" />
+				<p>
+					Type a label for the field. The label of the field is displayed
+					beside the field in Write Panel page.
+				</p>
+			</td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row">Can be duplicated:</th>
+			<td><input name="custom-field-duplicate" id="custom-field-duplicate" type="checkbox" value="1" <?php echo $custom_field->duplicate==1 ? "checked":"" ?>/></td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row">Order:</th>
+			<td><input type="text" name="custom-field-order" id="custom-field-order" size="2" value="0" /></td>
+		</tr>
+		
+
+		<tr valign="top">
+			<th scope="row">Required:</th>
+			<td>
+				<select name="custom-field-required" id="custom-field-required">
+					<option value="0" selected="selected">Not Required - can be empty</option>
+					<option value="1">Required - can not be empty</option>
+				</select>
+			</td>
+		</tr>
+				
+		<tr valign="top">
+			<th scope="row">Type:</th>
+			<td>
+
+				<!-- START :: Javascript for Image/Photo' Css Class and for check -->
+				<script type="text/javascript" language="javascript">
+					submitForm = false;
+					function fun(name)
+					{
+						if(name == "Image")
+						{
+							document.getElementById('divLbl').style.display = 'inline';
+							document.getElementById('divCSS').style.display = 'inline';
+						}
+						else
+						{
+							document.getElementById('divLbl').style.display = 'none';
+							document.getElementById('divCSS').style.display = 'none';
+						}
+					}
+
+					function checkEmpty()
+					{
+						if (submitForm && (document.getElementById('custom-field-name').value == "" || document.getElementById('custom-field-description').value == "")){
+							alert("Please fill in the name and the label of the field");	
+							return false;
+						}
+						return true;
+						
+					}
+				</script>
+				<!-- END :: Javascript for Image/Photo' Css Class -->
+
+				<?php
+				$field_types = RCCWP_CustomField::GetCustomFieldTypes();
+				foreach ($field_types as $field) :
+					$checked = 
+						$field->name == RCCWP_CustomField::GetDefaultCustomFieldType() ?
+						'checked="checked"' : '';
+				?>
+					<label><input name="custom-field-type" value="<?php echo $field->id?>" type="radio" <?php echo $checked?> onclick='fun("<?php echo $field->name?>");' /> <!-- Calling Javascript Function -->
+					<?php echo $field->name?></label><br />
+				<?php
+				endforeach;
+				?>
+			</td>
+		</tr>
+		<!-- START :: For Image/Photo' Css -->
+		<tr valign="top">
+			<th scope="row"><div id="divLbl" style="display:none">Css Class:</div></th>
+			<td>
+				<div id="divCSS" style="display:none">
+				<input name="custom-field-css" id="custom-field-css" size="40" type="text" value="freshout" />
+				</div>
+			</td>
+		</tr>
+		<!-- END :: For Image/Photo' Css -->
+		</tbody>
+		</table>
+		
+		
+	  	<p class="submit" >
+  			<a style="color:black" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('cancel-create-custom-field')."&custom-group-id=$customGroupID"?>" class="button"><?php _e('Cancel'); ?></a>
+  			<input type="submit" id="continue-create-custom-field" value="Continue"  onclick="submitForm=true;"/>
+  		</p>
+	  	
+  		</form>
+	  	
+  		</div>
+	  	
+  		<?php	
+	}
+	
+	function SetOptions()
+	{
+		$current_field = RCCWP_CustomField::GetCustomFieldTypes($_POST['custom-field-type']);
+		$customGroupID = $_REQUEST['custom-group-id'];
+		?>
+		
+		<div class="wrap">
+		
+		<h2>Create Custom Field</h2>
+		
+		<form action="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('finish-create-custom-field')?>" method="post" id="continue-create-new-field-form">
+		
+		<input type="hidden" name="custom-group-id" 	value="<?php echo $_POST['custom-group-id']?>" />
+		<input type="hidden" name="custom-field-name" 		value="<?php echo htmlspecialchars($_POST['custom-field-name'])?>" />
+		<input type="hidden" name="custom-field-description" 	value="<?php echo htmlspecialchars($_POST['custom-field-description'])?>" />
+		<input type="hidden" name="custom-field-duplicate" value="<?php echo htmlspecialchars($_POST['custom-field-duplicate'])?>" />
+		<input type="hidden" name="custom-field-order" 		value="<?php echo $_POST['custom-field-order']?>" />
+		<input type="hidden" name="custom-field-required" 		value="<?php echo $_POST['custom-field-required']?>" />
+		<input type="hidden" name="custom-field-type" 		value="<?php echo $_POST['custom-field-type']?>" />
+
+		<!-- Hidden value for Image/Photo' Css Class-->
+		<input type="hidden" name="custom-field-css" value="<?php echo $_POST['custom-field-css']?>" />
+
+		<h3><?php echo $current_field->name?></h3>
+		
+		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+		<tbody>
+		
+		<?php
+		if ($current_field->has_properties == "true") :
+		?>
+		
+		<?php 
+		if (in_array($current_field->name, array('Textbox', 'Listbox'))) : 
+			if ($current_field->name == 'Textbox')
+				$size = 25;
+			else if ($current_field->name == 'Listbox')
+				$size = 3;
+		?>
+		<tr valign="top">
+			<th scope="row">Size:</th>
+			<td><input type="text" name="custom-field-size" id="custom-field-size" size="2" value="<?php echo $size?>" /></td>
+		</tr>	
+		<?php endif; ?>
+		
+		<?php 
+		if (in_array($current_field->name, array('Multiline Textbox'))) : 
+			$height = 3;
+			$width = 23;
+		?>
+		<tr valign="top">
+			<th scope="row">Height:</th>
+			<td><input type="text" name="custom-field-height" id="custom-field-height" size="2" value="<?php echo $height?>" /></td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row">Width:</th>
+			<td><input type="text" name="custom-field-width" id="custom-field-width" size="2" value="<?php echo $width?>" /></td>
+		</tr>	
+		<?php endif; ?>
+		
+		<?php
+		endif; // has_properties
+		?>
+		
+		<?php
+		if ($current_field->has_options == "true") :
+		?>		
+		<tr valign="top">
+			<th scope="row">Options:</th>
+			<td>
+				<textarea name="custom-field-options" id="custom-field-options" rows="2" cols="38"></textarea><br />
+				<em>Separate each option with a newline.</em>
+			</td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row">Default Value:</th>
+			<td>
+				<?php
+				if ($current_field->allow_multiple_values == "true") :
+				?>
+				<textarea name="custom-field-default-value" id="custom-field-default-value" rows="2" cols="38"></textarea><br />
+				<em>Separate each value with a newline.</em>
+				<?php
+				else :
+				?>				
+				<input type="text" name="custom-field-default-value" id="custom-field-default-value" size="25" />
+				<?php
+				endif;
+				?>
+			</td>
+		</tr>
+		<?php endif; ?>
+
+
+		<?php if( $current_field->has_properties && $current_field->name == 'Image' ) : ?>
+		<tr valign="top">
+			<th scope="row">Options:</th>
+			<td>
+				Max Height:	<input type="text" name="custom-field-photo-height" id="custom-field-photo-height"/>
+				Max Width: <input type="text" name="custom-field-photo-width" id="custom-field-photo-width" />
+				Custom: <input type="text" name="custom-field-custom-params" id="custom-field-custom-params" />
+				<div style="color:blue;text-decoration:underline;"
+					onclick="div=document.getElementById('params');div.style.display='';"
+					>
+					Custom Options List
+				</div>
+				<div id="params"
+					style="display:none;"
+					onclick="this.style.display='none';">
+					<pre><?php echo param_list();  ?></pre>
+				</div>
+			</td>
+		</tr>
+		<?php endif; ?>
+
+		<!-- Date Custom Field -->
+		<?php if( $current_field->has_properties && $current_field->name == 'Date' ) : ?>
+		<tr valign="top">
+			<th scope="row">Options:</th>
+			<td>
+			Format:	<select name="custom-field-date-format" id="custom-field-date-format">
+					<option value="m/d/Y">4/20/2008</option>
+					<option value="l, F d, Y">Sunday, April 20, 2008</option>
+					<option value="F d, Y">April 20, 2008</option>
+					<option value="m/d/y">4/20/08</option>
+					<option value="Y-m-d">2008-04-20</option>
+					<option value="d-M-y">20-Apr-08</option>
+					<option value="m.d.Y">4.20.2008</option>
+					<option value="m.d.y">4.20.08</option>
+				</select>
+			</td>
+		</tr>
+		<?php endif; ?>
+		<!-- Date Custom Field -->
+
+		</tbody>
+		</table>
+		
+		<p class="submit" >
+			<a style="color:black" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('cancel-create-custom-field')."&custom-group-id=$customGroupID"?>" class="button"><?php _e('Cancel'); ?></a> 
+			<input type="submit" id="finish-create-custom-field" value="Finish" />
+		</p>
+			
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+	
+	function AddAjaxDynamicList(){
+		if($_GET['flutter_action']=='create-custom-field'){
+			?>
+			<style type="text/css">
+			/* Big box with list of options */
+			#ajax_listOfOptions{
+				position:absolute;	/* Never change this one */
+				width:375px;	/* Width of box */
+				height:250px;	/* Height of box */
+				overflow:auto;	/* Scrolling features */
+				border:1px solid #317082;	/* Dark green border */
+				background-color:#FFF;	/* White background color */
+				text-align:left;
+				font-size:0.9em;
+				z-index:100;
+			}
+			#ajax_listOfOptions div{	/* General rule for both .optionDiv and .optionDivSelected */
+				margin:1px;		
+				padding:1px;
+				cursor:pointer;
+				font-size:0.9em;
+			}
+			#ajax_listOfOptions .optionDiv{	/* Div for each item in list */
+				
+			}
+			#ajax_listOfOptions .optionDivSelected{ /* Selected item in the list */
+				background-color:#317082;
+				color:#FFF;
+			}
+			#ajax_listOfOptions_iframe{
+				background-color:#F00;
+				position:absolute;
+				z-index:5;
+			}
+			
+			form{
+				display:inline;
+			}
+			
+			</style>
+			<script type="text/javascript">
+				var flutter_path = "<?php echo FLUTTER_URI ?>" ;
+				var panel_id = "<?php echo $_REQUEST['custom-write-panel-id']; ?>" ;
+				
+				function copyField(){
+					selectedFieldId = document.create_custom_field_form.elements['custom-field-name_hidden'].value;
+					window.location = "<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('copy-custom-field')."&custom-group-id=".$_REQUEST['custom-group-id']?>&custom-field-id="+selectedFieldId;
+				}
+				
+			</script>
+			<script type="text/javascript" src="<?php echo FLUTTER_URI?>js/ajax-dynamic-list/ajax.js"></script>
+			<script type="text/javascript" src="<?php echo FLUTTER_URI?>js/ajax-dynamic-list/ajax-dynamic-list.js">
+			/************************************************************************************************************
+			(C) www.dhtmlgoodies.com, April 2006
+			
+			This is a script from www.dhtmlgoodies.com. You will find this and a lot of other scripts at our website.	
+			
+			Terms of use:
+			You are free to use this script as long as the copyright message is kept intact. However, you may not
+			redistribute, sell or repost it without our permission.
+			
+			Thank you!
+			
+			www.dhtmlgoodies.com
+			Alf Magne Kalleland
+			
+			************************************************************************************************************/	
+			</script> 
+					
+			<?php
+		}
+	}
+
+} //end class	
+
+	function param_list()
+	{
+		return ' 
+		 src = filename of source image
+		 new = create new image, not thumbnail of existing image.
+		       Requires "w" and "h" parameters set.
+		       [ex: &new=FF0000|75] - red background, 75% opacity
+		       Set to hex color string of background. Opacity is
+		       optional (defaults to 100% opaque).
+		   w = max width of output thumbnail in pixels
+		   h = max height of output thumbnail in pixels
+		  wp = max width for portrait images
+		  hp = max height for portrait images
+		  wl = max width for landscape images
+		  hl = max height for landscape images
+		  ws = max width for square images
+		  hs = max height for square images
+		   f = output image format ("jpeg", "png", or "gif")
+		   q = JPEG compression (1=worst, 95=best, 75=default)
+		  sx = left side of source rectangle (default = 0)
+		       (values 0 &lt; sx &lt; 1 represent percentage)
+		  sy = top side of source rectangle (default = 0)
+		       (values 0 &lt; sy &lt; 1 represent percentage)
+		  sw = width of source rectangle (default = fullwidth)
+		       (values 0 &lt; sw &lt; 1 represent percentage)
+		  sh = height of source rectangle (default = fullheight)
+		       (values 0 &lt; sh &lt; 1 represent percentage)
+		  zc = zoom-crop. Will auto-crop off the larger dimension
+		       so that the image will fill the smaller dimension
+		       (requires both "w" and "h", overrides "iar", "far")
+		       Set to "1" or "C" to zoom-crop towards the center,
+		       or set to "T", "B", "L", "R", "TL", "TR", "BL", "BR"
+		       to gravitate towards top/left/bottom/right directions
+		       (requies ImageMagick for values other than "C" or "1")
+		  bg = background hex color (default = FFFFFF)
+		  bc = border hex color (default = 000000)
+		fltr = filter system. Call as an array as follows:
+		       - "brit" (Brightness) [ex: &fltr[]=brit|&lt;value]
+		         where &lt;value is the amount +/- to adjust brightness
+		         (range -255 to 255)
+		         Availble in PHP5 with bundled GD only.
+		       - "cont" (Constrast) [ex: &fltr[]=cont|&lt;value&gt;]
+		         where &lt;value is the amount +/- to adjust contrast
+		         (range -255 to 255)
+		         Availble in PHP5 with bundled GD only.
+		       - "gam" (Gamma Correction) [ex:
+		         &fltr[]=gam|&lt;value&gt;]
+		         where &lt;value&gt; can be a number &gt;0 to 10+ (default 1.0)
+		         Must be &gt;0 (zero gives no effect). There is no max,
+		         although beyond 10 is pretty useless. Negative
+		         numbers actually do something, maybe not quite the
+		         desired effect, but interesting nonetheless.
+		       - "sat" (SATuration) [ex: &fltr[]=sat|&lt;value&gt;]
+		         where &lt;value&gt; is a number between zero (no change)
+		         and -100 (complete desaturation = grayscale), or it
+		         can be any positive number for increased saturation.
+		       - "ds" (DeSaturate) [ex: &fltr[]=ds|&lt;value&gt;]
+		         is an alias for "sat" except values are inverted
+		         (positive values remove color, negative values boost
+		         saturation)
+		       - "gray" (Grayscale) [ex: &fltr[]=gray]
+		         remove all color from image, make it grayscale
+		       - "th" (Threshold) [ex: &fltr[]=th|&lt;value&gt;]
+		         makes image greyscale, then sets all pixels brighter
+		         than &lt;value&gt; (range 0-255) to white, and all pixels
+		         darker than &lt;value&gt; to black
+		       - "rcd" (Reduce Color Depth) [ex:
+		         &fltr[]=rcd|&lt;c&gt;|&lt;d&gt;]
+		         where &lt;c&gt; is the number of colors (2-256) you want
+		         in the output image, and &lt;d&gt; is "1" for dithering
+		         (deault) or "0" for no dithering
+		       - "clr" (Colorize) [ex:
+		         &fltr[]=clr|&lt;value&gt;|&lt;color&gt;]
+		         where &lt;value&gt; is a number between 0 and 100 for the
+		         amount of colorization, and &lt;color&gt; is the hex color
+		         to colorize to.
+		       - "sep" (Sepia) [ex:
+		         &fltr[]=sep|&lt;value&gt;|&lt;color&gt;]
+		         where &lt;value&gt; is a number between 0 and 100 for the
+		         amount of colorization (default=50), and &lt;color&gt; is
+		         the hex color to colorize to (default=A28065).
+		         Note: this behaves differently when applied by
+		         ImageMagick, in which case 80 is default, and lower
+		         values give brighter/yellower images and higher
+		         values give darker/bluer images
+		       - "usm" (UnSharpMask) [ex:
+		         &fltr[]=usm|&lt;a&gt;|&lt;r&gt;|&lt;t&gt;]
+		         where &lt;a&gt; is the amount (default = 80), &lt;r&gt; is the
+		         radius (default = 0.5), &lt;t&gt; is the threshold
+		         (default = 3).
+		       - "blur" (Blur) [ex: &fltr[]=blur|&lt;radius&gt;]
+		         where (0 &lt; &lt;radius&gt; &lt; 25) (default = 1)
+		       - "gblr" (Gaussian Blur) [ex: &fltr[]=gblr]
+		         Availble in PHP5 with bundled GD only.
+		       - "sblr" (Selective Blur) [ex: &fltr[]=gblr]
+		         Availble in PHP5 with bundled GD only.
+		       - "smth" (Smooth) [ex: &fltr[]=smth|&lt;value&gt;]
+		         where &lt;value&gt; is the weighting value for the matrix
+		         (range -10 to 10, default 6)
+		         Availble in PHP5 with bundled GD only.
+		       - "lvl" (Levels)
+		         [ex: &fltr[]=lvl|&lt;channel&gt;|&lt;method&gt;|&lt;threshol&gt;d
+		         where &lt;channel&gt; can be one of "r", "g", "b", "a" (for
+		         Red, Green, Blue, Alpha respectively), or "*" for all
+		         RGB channels (default) based on grayscale average.
+		         ImageMagick methods can support multiple channels
+		         (eg "lvl|rg|3") but internal methods cannot (they will
+		         use first character of channel string as channel)
+		         &lt;method&gt; can be one of:
+		         0=Internal RGB;
+		         1=Internal Grayscale;
+		         2=ImageMagick Contrast-Stretch (default)
+		         3=ImageMagick Normalize (may appear over-saturated)
+		         &lt;threshold&gt; is how much of brightest/darkest pixels
+		         will be clipped in percent (default = 0.1%)
+		         Using default parameters (&fltr[]=lvl) is similar to
+		         Auto Contrast in Adobe Photoshop.
+		       - "wb" (White Balance) [ex: &fltr[]=wb|&lt;c&gt;]
+		         where &lt;c&gt; is the target hex color to white balance
+		         on, this color is what "should be" white, or light
+		         gray. The filter attempts to maintain brightness so
+		         any gray color can theoretically be used. If &lt;c&gt; is
+		         omitted the filter guesses based on brightest pixels
+		         in each of RGB
+		         OR &lt;c&gt; can be the percent of white clipping used
+		         to calculate auto-white-balance (default = 0.1%)
+		         NOTE: "wb" in default settings already gives an effect
+		         similar to "lvl", there is usually no need to use "lvl"
+		         if "wb" is already used.
+		       - "hist" (Histogram)
+		         [ex: &fltr[]=hist|&lt;b&gt;|&lt;c&gt;|&lt;w&gt;|&lt;h&gt;|&lt;a&gt;|&lt;o&gt;|&lt;x&gt;|&lt;y&gt;]
+		         Where &lt;b&gt; is the color band(s) to display, from back
+		         to front (one or more of "rgba*" for Red Green Blue
+		         Alpha and Grayscale respectively);
+		         &lt;c&gt; is a semicolon-seperated list of hex colors to
+		         use for each graph band (defaults to FF0000, 00FF00,
+		         0000FF, 999999, FFFFFF respectively);
+		         &lt;w&gt; and &lt;h&gt; are the width and height of the overlaid
+		         histogram in pixels, or if &lt;= 1 then percentage of
+		         source image width/height;
+		         &lt;a&gt; is the alignment (same as for "wmi" and "wmt");
+		         &lt;o&gt; is opacity from 0 (transparent) to 100 (opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         &lt;x&gt; and &lt;y&gt; are the edge margin in pixels (or percent
+		             if 0 &lt; (x|y) &lt; 1)
+		       - "over" (OVERlay/underlay image) overlays an image on
+		         the thumbnail, or overlays the thumbnail on another
+		         image (to create a picture frame for example)
+		         [ex: &fltr[]=over|&lt;i&gt;|&lt;u&gt;|&lt;m&gt;|&lt;o&gt;]
+		         where &lt;i&gt; is the image filename; &lt;u&gt; is "0" (default)
+		         for overlay the image on top of the thumbnail or "1"
+		         for overlay the thumbnail on top of the image; &lt;m&gt; is
+		         the margin - can be absolute pixels, or if &lt; 1 is a
+		         percentage of the thumbnail size [must be &lt; 0.5]
+		         (default is 0 for overlay and 10% for underlay);
+		         &lt;o&gt; is opacity (0 = transparent, 100 = opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         (thanks raynerapeï¿œgmail*com, shabazz3ï¿œmsu*edu)
+		       - "wmi" (WaterMarkImage)
+		         [ex: &fltr[]=wmi|&lt;f&gt;|&lt;a&gt;|&lt;o&gt;|&lt;x&gt;|&lt;y&gt;|&lt;r&gt;] where
+		         &lt;f&gt; is the filename of the image to overlay;
+		         &lt;a&gt; is the alignment (one of BR, BL, TR, TL, C,
+		             R, L, T, B, *) where B=bottom, T=top, L=left,
+		             R=right, C=centre, *=tile)
+		             *or*
+		             an absolute position in pixels (from top-left
+		             corner of canvas to top-left corner of overlay)
+		             in format {xoffset}x{yoffset} (eg: "10x20")
+		             note: this is center position of image if &lt;&gt;x
+		             and &lt;y&gt; are set
+		         &lt;o&gt; is opacity from 0 (transparent) to 100 (opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         &lt;x&gt; and &lt;y&gt; are the edge (and inter-tile) margin in
+		             pixels (or percent if 0 &lt; (x|y) &lt; 1)
+		             *or*
+		             if &lt;a&gt; is absolute-position format then &lt;x&gt; and
+		             &lt;y&gt; represent maximum width and height that the
+		             watermark image will be scaled to fit inside
+		         &lt;r&gt; is rotation angle of overlaid watermark
+		       - "wmt" (WaterMarkText)
+		         [ex: &fltr[]=wmt|&lt;t&gt;|&lt;s&gt;|&lt;a&gt;|&lt;c&gt;|&lt;f&gt;|&lt;o&gt;|&lt;m&gt;|&lt;n&gt;|&lt;b&gt;|&lt;O&gt;|&lt;x&gt;]
+		         where:
+		         &lt;t&gt; is the text to use as a watermark;
+		             URLencoded Unicode HTMLentities must be used for
+		               characters beyond chr(127). For example, the
+		               "eighth note" character (U+266A) is represented
+		               as "&#9834;" and then urlencoded to "%26%239834%3B"
+		             Any instance of metacharacters will be replaced
+		             with their calculated value. Currently supported:
+		               ^Fb = source image filesize in bytes
+		               ^Fk = source image filesize in kilobytes
+		               ^Fm = source image filesize in megabytes
+		               ^X  = source image width in pixels
+		               ^Y  = source image height in pixels
+		               ^x  = thumbnail width in pixels
+		               ^y  = thumbnail height in pixels
+		               ^^  = the character ^
+		         &lt;s&gt; is the font size (1-5 for built-in font, or point
+		             size for TrueType fonts);
+		         &lt;a&gt; is the alignment (one of BR, BL, TR, TL, C, R, L,
+		             T, B, * where B=bottom, T=top, L=left, R=right,
+		             C=centre, *=tile);
+		             *or*
+		             an absolute position in pixels (from top-left
+		             corner of canvas to top-left corner of overlay)
+		             in format {xoffset}x{yoffset} (eg: "10x20")
+		         &lt;c&gt; is the hex color of the text;
+		         &lt;f&gt; is the filename of the TTF file (optional, if
+		             omitted a built-in font will be used);
+		         &lt;o&gt; is opacity from 0 (transparent) to 100 (opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         &lt;m&gt; is the edge (and inter-tile) margin in percent;
+		         &lt;n&gt; is the angle
+		         &lt;b&gt; is the hex color of the background;
+		         &lt;O&gt; is background opacity from 0 (transparent) to
+		             100 (opaque)
+		             (requires PHP v4.3.2, otherwise 100% opaque);
+		         &lt;x&gt; is the direction(s) in which the background is
+		             extended (either "x" or "y" (or both, but both
+		             will obscure entire image))
+		             Note: works with TTF fonts only, not built-in
+		       - "flip" [ex: &fltr[]=flip|x   or   &fltr[]=flip|y]
+		         flip image on X or Y axis
+		       - "ric" [ex: &fltr[]=ric|&lt;x&gt;|&lt;y&gt;]
+		         rounds off the corners of the image (to transparent
+		         for PNG output), where &lt;x&gt; is the horizontal radius
+		         of the curve and &lt;y&gt; is the vertical radius
+		       - "elip" [ex: &fltr[]=elip]
+		         similar to rounded corners but more extreme
+		       - "mask" [ex: &fltr[]=mask|filename.png]
+		         greyscale values of mask are applied as the alpha
+		         channel to the main image. White is opaque, black
+		         is transparent.
+		       - "bvl" (BeVeL) [ex:
+		         &fltr[]=bvl|&lt;w&gt;|&lt;c1&gt;|&lt;c2&gt;]
+		         where &lt;w&gt; is the bevel width, &lt;c1&gt; is the hex color
+		         for the top and left shading, &lt;c2&gt; is the hex color
+		         for the bottom and right shading
+		       - "bord" (BORDer) [ex:
+		         &fltr[]=bord|&lt;w&gt;|&lt;rx&gt;|&lt;ry&gt;|&lt;&gt;c
+		         where &lt;w&gt; is the width in pixels, &lt;rx&gt;
+			 and &lt;ry&gt; are
+		         horizontal and vertical radii for rounded corners,
+		         and &lt;c&gt; is the hex color of the border
+		       - "fram" (FRAMe) draws a frame, similar to "bord" but
+		         more configurable
+		         [ex: &fltr[]=fram|&lt;w1&gt;|&lt;w2&gt;|&lt;c1&gt;|&lt;c2&gt;|&lt;c3&gt;]
+		         where &lt;w1&gt; is the width of the main border,
+			 &lt;w2&gt; is
+		         the width of each side of the bevel part, &lt;c1&gt; is the
+		         hex color of the main border, &lt;c2&gt; is the highlight
+		         bevel color, &lt;c3&gt; is the shadow bevel color
+		       - "drop" (DROP shadow)
+		         [ex: &fltr[]=drop|&lt;d&gt;|&lt;w&gt;|&lt;clr&gt;|&lt;a&gt;]
+		         where &lt;d&gt; is distance from image to shadow,
+			 &lt;w&gt; is
+		         width of shadow fade (not yet implemented),
+			 &lt;clr&gt; is
+		         the hex color of the shadow, and &lt;a&gt; is the angle of
+		         the shadow (default=225)
+		       - "crop" (CROP image)
+		         [ex:
+			 &fltr[]=crop|&lt;l&gt;|&lt;r&gt;|&lt;t&gt;|&lt;b&gt;]
+		         where &lt;l&gt; is the number of pixels to crop from the left
+		         side of the resized image; &lt;r&gt;, &lt;t&gt;,
+			 &lt;b&gt; are for right,
+		         top and bottom respectively. Where (0 &lt; x &lt; 1) the
+		         value will be used as a percentage of width/height.
+		         Left and top crops take precedence over right and
+		         bottom values. Cropping will be limited such that at
+		         least 1 pixel of width and height always remains.
+		       - "rot" (ROTate)
+		         [ex: &fltr[]=rot|&lt;a&gt;|&lt;b&gt;]
+		         where &lt;a&gt; is the rotation angle in degrees;
+			 &lt;b&gt; is the
+		         background hex color. Similar to regular "ra" parameter
+		         but is applied in filter order after regular processing
+		         so you can rotate output of other filters.
+		       - "size" (reSIZE)
+		         [ex: &fltr[]=size|&lt;x&gt;|&lt;y&gt;|&lt;s&gt;]
+		         where &lt;x&gt; is the horizontal dimension in pixels,
+			 &lt;y&gt; is
+		         the vertical dimension in pixels, &lt;s&gt; is boolean whether
+		         to stretch (if 1) or resize proportionately (0, default)
+		         &lt;x&gt; and &lt;y&gt; will be interpreted as percentage of current
+		         output image size if values are (0 &lt; X &lt; 1)
+		         NOTE: do NOT use this filter unless absolutely neccesary.
+		         It is only provided for cases where other filters need to
+		         have absolute positioning based on source image and the
+		         resultant image should be resized after other filters are
+		         applied. This filter is less efficient than the standard
+		         resizing procedures.
+		       - "stc" (Source Transparent Color)
+		         [ex: &fltr[]=stc|&lt;c&gt;|&lt;n&gt;|<x&gt;]
+		         where <c&gt; is the hex color of the target color to be made
+		         transparent; <n&gt; is the minimum threshold in percent (all
+		         pixels within <n&gt;% of the target color will be 100%
+		         transparent, default <n&gt;=5); <x&gt; is the maximum threshold
+		         in percent (all pixels more than <x&gt;% from the target
+		         color will be 100% opaque, default <x&gt;=10); pixels between
+		         the two thresholds will be partially transparent.
+		md5s = MD5 hash of the source image -- if this parameter is
+		       passed with the hash of the source image then the
+		       source image is not checked for existance or
+		       modification and the cached file is used (if
+		       available). If "md5s" is passed an empty string then
+		       phpThumb.php dies and outputs the correct MD5 hash
+		       value.  This parameter is the single-file equivalent
+		       of "cache_source_filemtime_ignore_*" configuration
+		       paramters
+		 xto = EXIF Thumbnail Only - set to only extract EXIF
+		       thumbnail and not do any additional processing
+		  ra = Rotate by Angle: angle of rotation in degrees
+		       positive = counterclockwise, negative = clockwise
+		  ar = Auto Rotate: set to "x" to use EXIF orientation
+		       stored by camera. Can also be set to "l" or "L"
+		       for landscape, or "p" or "P" for portrait. "l"
+		       and "P" rotate the image clockwise, "L" and "p"
+		       rotate the image counter-clockwise.
+		 sfn = Source Frame Number - use this frame/page number for
+		       multi-frame/multi-page source images (GIF, TIFF, etc)
+		 aoe = Output Allow Enlarging - override the setting for
+		       $CONFIG["output_allow_enlarging"] (1=on, 0=off)
+		       ("far" and "iar" both override this and allow output
+		       larger than input)
+		 iar = Ignore Aspect Ratio - disable proportional resizing
+		       and stretch image to fit "h" & "w" (which must both
+		       be set).  (1=on, 0=off)  (overrides "far")
+		 far = Force Aspect Ratio - image will be created at size
+		       specified by "w" and "h" (which must both be set).
+		       Alignment: L=left,R=right,T=top,B=bottom,C=center
+		       BL,BR,TL,TR use the appropriate direction if the
+		       image is landscape or portrait.
+		 dpi = Dots Per Inch - input DPI setting when importing from
+		       vector image format such as PDF, WMF, etc
+		 sia = Save Image As - default filename to save generated
+		       image as. Specify the base filename, the extension
+		       (eg: ".png") will be automatically added
+		maxb = MAXimum Byte size - output quality is auto-set to
+		       fit thumbnail into "maxb" bytes  (compression
+		       quality is adjusted for JPEG, bit depth is adjusted
+		       for PNG and GIF)
+		down = filename to save image to. If this is set the
+		       browser will prompt to save to this filename rather
+		       than display the image
+		
+			';
+		}
+
+		
+
+		
+?>
Index: /afridex/plugins/Flutter/canvas-admin.php
===================================================================
--- /afridex/plugins/Flutter/canvas-admin.php (revision 21)
+++ /afridex/plugins/Flutter/canvas-admin.php (revision 21)
@@ -0,0 +1,140 @@
+<?php
+function canvas_admin($requested_tool) {
+	global $wpdb, $canvas, $main_page;
+	$main_page = '';
+	canvas_reload();
+
+	if (count(RCCWP_CustomWriteModule::GetCustomModules()) == 0)
+		$nomodules = true;	
+	else
+		$nomodules = false;
+
+?>
+<div class="wrap">
+<script type="text/javascript" > var flutter_path = "<?php echo FLUTTER_URI ?>" ;</script>
+
+<script type="text/javascript">
+	var swf_authentication = "<?php if ( function_exists('is_ssl') && is_ssl() ) echo $_COOKIE[SECURE_AUTH_COOKIE]; else echo $_COOKIE[AUTH_COOKIE]; ?>" ;
+	var swf_nonce = "<?php echo wp_create_nonce('media-form'); ?>" ;
+</script>
+<script type="text/javascript" src="<?php echo FLUTTER_URI; ?>js/swfupload/swfcallbacks.js" ></script>
+<script type="text/javascript" src="<?php echo FLUTTER_URI; ?>js/swfupload/swfupload.js"></script>
+<h2> Flutter Layout Editor </h2>
+<?php 	if ($nomodules){ ?>	
+<h3><?php echo sprintf(__('You first need to create a new  <a href="%1$s">Module</a> in order to change Layout.'), "?page=FlutterManageModules"); ?></h3></div>
+<?php 	return ;}  ?>
+
+
+
+<?php if ($_GET["resetmodsmsg"]){ ?> <div class="updated fade" id="canvas_defaultmods_status" ><p>All modules were reset.</p></div> <?php } ?>
+<div class="updated fade" id="canvas_status" style="display:none"></div>
+
+
+<?php canvas_admin_menu(); ?>
+
+<?php if($wpdb->get_var("SHOW TABLES LIKE '".$canvas->main."'") == $canvas->main) : ?>
+<div id="canvas">
+<?php
+	if($_GET["content"] == 'main') 
+		canvas_admin_main($main_page);
+	elseif($requested_tool == 'ink' && function_exists('ink_admin')) 
+		ink_admin();
+	else canvas_admin_main($main_page);
+?>
+</div>
+
+<?php
+else : echo '<div class="content_wrap"><strong>Error:</strong> the Canvas tables don\'t exist in your database. Please try reinstalling.</div>';
+endif;
+}
+
+//
+// ______________________________________________________________
+//
+
+function canvas_ink_admin() {
+	canvas_admin('ink');
+}
+
+
+//
+// ______________________________________________________________
+//
+
+function canvas_admin_main($main_page) {
+	include_once('RCCWP_Options.php');
+	if (isset($_GET['t_page'])) 
+		$curr_page = $_GET['t_page'];
+	else
+		$curr_page = 'home'; // Attempt to find the main page
+	
+?>
+<input type="hidden" id="canvas_page" name="canvas_page" value="<?php echo $curr_page; ?>" />
+
+
+<!-- Shelf -->
+<div class="shelf_column">
+	<div class="titlebar">
+		<h5 class="title">Your Shelf</h5>
+	</div>
+	<span class="spaceholder"></span>
+		<div class="canvas_droppable_zone" id="shelf">
+			<?php get_canvas_draggables('shelf', $_GET['template']); ?>
+		</div>
+</div>
+
+<div class="content_wrap">
+<?php if(RCCWP_Options::Get('canvas_show_instructions')) : ?><div id="instructions">Drag and drop the modules from your shelf to the layout to create your own layout. You can select the page type to build from the menu above. To save your work, press the "Publish Changes" button. <br /><div style="text-align:right"><a href="javascript:void(0)" id="hide_instructions">don't show this message again</a> &times;</div></div><?php endif; ?>
+
+<?php canvas_build_canvas($curr_page); ?>
+
+</div>
+
+<?php } 
+
+
+function canvas_is_checked($option_name='') {
+	if(get_option($option_name) == 'true') echo 'checked="checked"';
+}
+
+function canvas_admin_menu() {
+	global $main_page;
+?>	<div id="canvas_dropdown">
+<?php	if($_GET["page"] == 'FlutterEditCanvas' && (!isset($_GET["content"]) || $_GET["content"] == 'main')) :
+			if(isset($_GET["template"])) $template = $_GET["template"];
+				else $template = get_option('template');
+			$filename = ABSPATH.'/wp-content/themes/'.$template.'/canvas.php';
+			if(file_exists($filename)) {
+				$file_data = implode('', file($filename));
+				preg_match_all("|<!-- Canvas Page:(\s*)(.*) -->|", $file_data, $pages);
+			}
+?>
+	<ul class="page_dropdown">
+		<li id="top_menu"><a href="?page=FlutterEditCanvas">Select a Page</a>
+		<div class="dropdown_menu" id="dropdown_menu">
+		<ul>
+<?php 		
+		if(!empty($pages[2])) {
+			foreach($pages[2] as $page) {
+				list($page_name, $id) = split('\s*\|\s*',$page);
+				if($main_page == '') $main_page = trim($id);
+				//echo '<li><a id="'.trim($id).'__menu" href="javascript:void(0)">'.trim($page_name).'</a></li>';
+				echo "<li><a id='".trim($id)."__menu' href='?page=FlutterEditCanvas&t_page=".trim($id)."'>".trim($page_name)."</a></li>";
+			}
+		} else {
+			echo '<li><a href="javascript:void(0)">Please select a Layout compatible theme below</a></li>';
+		} ?>
+		</ul>
+		</div>
+		</li>
+	</ul>
+	<?php endif; ?>
+	
+	<!--<a class="canvas_menu_item" href="?page=FlutterEditCanvas&content=ink">Ink Color Palette</a>-->
+	<!--<a class="canvas_menu_item" href="?page=FlutterEditCanvas&content=options">Canvas Options</a>-->
+	<!--<a class="canvas_menu_item" href="<?php bloginfo('wpurl') ?>/wp-admin/themes.php?page=Main.php&content=tips">Tips and Troubleshooting</a>-->
+	</div>
+
+<?php
+
+} ?>
Index: /afridex/plugins/Flutter/RCCWP_Menu.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_Menu.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_Menu.php (revision 21)
@@ -0,0 +1,530 @@
+<?php
+require_once ('RCCWP_Application.php');
+require_once ('RCCWP_ManagementPage.php');
+require_once ('RCCWP_CreateCustomWritePanelPage.php');
+require_once ('RCCWP_CreateCustomWriteModulePage.php');
+require_once ('RCCWP_CustomWriteModulePage.php');
+require_once ('RCCWP_CreateCustomGroupPage.php');
+require_once ('RCCWP_CreateCustomFieldPage.php');
+require_once ('RCCWP_CustomFieldPage.php');
+require_once ('RCCWP_CreatePanelModulePage.php');
+require_once ('RCCWP_ModuleDuplicate.php');
+require_once ('RCCWP_ModuleDuplicatePage.php');
+
+
+class RCCWP_Menu
+{
+	function PrepareModulesPanelsMenuItems()
+	{
+		$sub_menu_is_modules = false;
+		
+		$currentAction = $_REQUEST['flutter_action'];
+		
+		switch ($currentAction){
+			
+			// ------------ Custom Fields
+			case 'create-custom-field':
+				$page_group = 'RCCWP_CreateCustomFieldPage';
+				$page_type = 'Main';
+				break;
+
+			case 'continue-create-custom-field':		
+				if(isset($_REQUEST['custom-group-id']) && !empty($_REQUEST['custom-group-id']) )
+					$customGroupId = (int)$_REQUEST['custom-group-id'];
+				$customGroup = RCCWP_CustomGroup::Get($customGroupId);
+	
+				$current_field = RCCWP_CustomField::GetCustomFieldTypes((int)$_REQUEST['custom-field-type']);
+				if ($current_field->has_options == "true" || $current_field->has_properties == "true")
+				{
+					$page_group = 'RCCWP_CreateCustomFieldPage';
+					$page_type = 'SetOptions';
+				}
+				else if ($current_field->has_options == "false")
+				{
+					RCCWP_CustomField::Create(
+						$_POST['custom-group-id'],
+						$_POST['custom-field-name'],
+						$_POST['custom-field-description'],
+						$_POST['custom-field-order'],
+						$_POST['custom-field-required'],
+						$_POST['custom-field-type'],
+						$_POST['custom-field-options'],
+						null,null,
+						$_POST['custom-field-duplicate']);
+	
+					$page_group = 'RCCWP_CustomWritePanelPage';
+					$page_type = 'View';
+					/*if ($customGroup->name=='__default'){
+						$page_group = 'RCCWP_CustomWritePanelPage';
+						$page_type = 'View';
+					}
+					else{
+						$page_group = 'RCCWP_CustomGroupPage';
+						$page_type = 'View';
+					}*/
+				}
+				break;
+		
+			case 'delete-custom-field':
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'View';
+				break;
+					
+			case 'finish-create-custom-field':		
+			case 'cancel-edit-custom-field':
+			case 'cancel-create-custom-field':
+			case 'submit-edit-custom-field':
+			case 'copy-custom-field':
+				/*$customGroupId = false;
+				$customGroupId = (int)$_REQUEST['custom-group-id'];
+	
+				$customGroup = RCCWP_CustomGroup::Get($customGroupId);
+				if ($customGroup->name=='__default'){
+					$page_group = 'RCCWP_CustomWritePanelPage';
+					$page_type = 'View';
+				}
+				else{
+					$page_group = 'RCCWP_CustomGroupPage';
+					$page_type = 'View';
+				}*/
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'View';
+				break;
+				
+			case 'edit-custom-field':
+				$page_group = 'RCCWP_CustomFieldPage';
+				$page_type = 'Edit';
+				break;
+		
+			// ------------ Groups
+			
+			case 'create-custom-group':
+				$page_group = 'RCCWP_CreateCustomGroupPage';
+				$page_type = 'Main';
+				break;
+					
+			case 'view-custom-group':
+				$page_group = 'RCCWP_CustomGroupPage';
+				$page_type = 'View';
+				break;
+
+			case 'cancel-edit-custom-group':
+			case 'cancel-create-custom-group':
+			case 'delete-custom-group':
+			case 'submit-edit-custom-group':
+			case 'finish-create-custom-group':
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'View';
+				break;
+				
+			case 'edit-custom-group':
+				$page_group = 'RCCWP_CustomGroupPage';
+				$page_type = 'Edit';
+				break;
+				
+				
+
+			// ------------ Custom Write Panels
+
+			case 'view-custom-write-panel':
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'View';
+				break;
+				
+			case 'create-custom-write-panel':
+				$page_group = 'RCCWP_CreateCustomWritePanelPage';
+				$page_type = 'Main';
+				break;
+
+			case 'finish-create-custom-write-panel':
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'View';
+				break;
+				
+			case 'edit-custom-write-panel':
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'Edit';
+				break;
+				
+			case 'cancel-edit-custom-write-panel':
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'View';
+				break;
+				
+			case 'submit-edit-custom-write-panel':
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'View';
+				break;
+				
+			case 'import-write-panel':
+				$page_group = 'RCCWP_CustomWritePanelPage';
+				$page_type = 'Import';
+				break;
+				
+
+			// ------------ Modules
+			case 'import-module':
+				$page_group = 'RCCWP_CustomWriteModulePage';
+				$page_type = 'Import';
+				$sub_menu_is_modules = true;
+				break;
+				
+			case 'create-custom-write-module':
+				$page_group = 'RCCWP_CreateCustomWriteModulePage';
+				$page_type = 'Main';
+				$sub_menu_is_modules = true;
+				break;
+				
+			case 'prepare-export-write-module':
+				$page_group = 'RCCWP_CustomWriteModulePage';
+				$page_type = 'PrepareExport';
+				$sub_menu_is_modules = true;
+				break;
+												
+			default:
+				
+				
+				
+				if (isset($_REQUEST['assign-custom-write-panel']))
+				{
+					$page_group = 'RCCWP_ManagementPage';
+					$page_type = 'AssignCustomWritePanel';
+					$sub_menu_is_modules = false;
+				}
+				// ------- Groups
+				
+				else if (isset($_REQUEST['cancel-edit-custom-group']))
+				{
+					$page_group = 'RCCWP_CustomGroupPage';
+					$page_type = 'View';
+				}
+				
+				else if (isset($_REQUEST['view-groups']))
+				{
+					$page_group = 'RCCWP_ManagementPage';
+					$page_type = 'ViewGroups';
+				}
+				
+				// ------- Modules
+				
+				else if (isset($_REQUEST['edit-custom-write-module']))
+				{
+					$page_group = 'RCCWP_CustomWriteModulePage';
+					$page_type = 'Edit';
+					$sub_menu_is_modules = true;
+				}
+				else if (isset($_REQUEST['cancel-edit-custom-write-module']))
+				{
+					$page_group = 'RCCWP_CustomWriteModulePage';
+					$page_type = 'View';
+					$sub_menu_is_modules = true;
+				}
+				else if (isset($_REQUEST['submit-edit-custom-write-module']))
+				{
+					$page_group = 'RCCWP_CustomWriteModulePage';
+					$page_type = 'View';
+					$sub_menu_is_modules = true;
+				}
+				else if (isset($_REQUEST['view-custom-write-module']))
+				{
+					$page_group = 'RCCWP_CustomWriteModulePage';
+					$page_type = 'View';
+					$sub_menu_is_modules = true;
+				}
+				else if (isset($_REQUEST['view-modules']))
+				{
+					$page_group = 'RCCWP_ManagementPage';
+					$page_type = 'ViewModules';
+					$sub_menu_is_modules = true;
+				}
+				
+				// ------- Modules Duplicates
+				else if (isset($_REQUEST['cancel-edit-module-duplicate']))
+				{
+					$page_group = 'RCCWP_CustomWriteModulePage';
+					$page_type = 'View';
+					$sub_menu_is_modules = true;
+				}
+				else if (isset($_REQUEST['edit-module-duplicate']))
+				{
+					$page_group = 'RCCWP_ModuleDuplicatePage';
+					$page_type = 'Edit';
+					$sub_menu_is_modules = true;
+				}
+				else if (isset($_REQUEST['submit-edit-module-duplicate']))
+				{
+					$page_group = 'RCCWP_CustomWriteModulePage';
+					$page_type = 'View';
+					$sub_menu_is_modules = true;
+				}
+				// ------- Default behavior
+				else{
+					$page_group = 'RCCWP_CustomWritePanelPage';
+					$page_type = 'ViewWritePanels';
+					$sub_menu_is_modules = false;
+				}
+				
+		}
+		
+		
+		
+		
+		
+				
+
+
+		if ($sub_menu_is_modules){
+			$result->panelsMenuFunction = array('RCCWP_CustomWritePanelPage', 'ViewWritePanels');
+			$result->modulesMenuFunction = array($page_group, $page_type);
+		}
+		else{
+			$result->panelsMenuFunction = array($page_group, $page_type);
+			$result->modulesMenuFunction = array('RCCWP_ManagementPage', 'ViewModules');
+		}
+
+		return $result;
+
+
+	}
+
+	
+	function AttachFlutterMenus()
+	{
+		require_once ('canvas-admin.php');
+		require_once ('RCCWP_OptionsPage.php');
+
+		//$showPanelsModules = true;
+		//If we are Wordrpess mu and the user is not top admin, don't show panels and modules.
+		//if (function_exists('is_site_admin') && !is_site_admin()) $showPanelsModules = false;
+
+		if ((!current_user_can(FLUTTER_CAPABILITY_PANELS) && !current_user_can(FLUTTER_CAPABILITY_MODULES)) &&
+		    (!RCCWP_Options::Get('canvas_show') || !current_user_can(FLUTTER_CAPABILITY_LAYOUT)) &&
+		    (!RCCWP_Options::Get('ink_show') || !current_user_can(FLUTTER_CAPABILITY_STYLE)) )
+			return;
+
+			
+
+		//if ($showPanelsModules) 
+		$panelsAndModulesFunctions = RCCWP_Menu::PrepareModulesPanelsMenuItems();
+
+		// Add top menu
+		add_menu_page(__('Flutter > Manage'), 'Flutter', FLUTTER_CAPABILITY_PANELS, __FILE__, $panelsAndModulesFunctions->panelsMenuFunction);
+
+		// Add Flutter submenus
+		//if ($showPanelsModules) 
+			add_submenu_page(__FILE__, __('Write Panels'), __('Write Panels'), FLUTTER_CAPABILITY_PANELS, __FILE__, $panelsAndModulesFunctions->panelsMenuFunction);
+		
+		//if ($showPanelsModules) 
+			add_submenu_page(__FILE__, __('Modules'), __('Modules'), FLUTTER_CAPABILITY_MODULES, 'FlutterManageModules', $panelsAndModulesFunctions->modulesMenuFunction);		
+		
+		if (RCCWP_Options::Get('canvas_show'))
+			add_submenu_page(__FILE__, __('Layout'), __('Layout'), FLUTTER_CAPABILITY_LAYOUT, 'FlutterEditCanvas', 'canvas_admin');
+		if (RCCWP_Options::Get('ink_show'))
+			add_submenu_page(__FILE__, __('Style'), __('Style'), FLUTTER_CAPABILITY_STYLE, 'FlutterInk', 'canvas_ink_admin');
+
+	}
+
+	function AttachOptionsMenuItem()
+	{
+
+		require_once ('RCCWP_OptionsPage.php');
+		add_options_page(__('Flutter Options'), __('Flutter'), 'manage_options', 'RCCWP_OptionsPage.php', array('RCCWP_OptionsPage', 'Main'));
+	}
+
+
+	function StartPage(){
+		return;
+/*		$showPanelsModules = true;
+		//If we are Wordrpess mu and the user is not top admin, don't show panels and modules.
+		if (function_exists('is_site_admin') && !is_site_admin()) $showPanelsModules = false;
+
+		$startPageContent.= 
+<<<EOF
+	<h2> Flutter Manager </h2>
+	<p> Welcome to Flutter management pages. You can:</p>
+	<ul>
+EOF;
+
+		if ($showPanelsModules && current_user_can(FLUTTER_CAPABILITY_PANELS)) $startPageContent.= '<li><a href="">Manage Write Panels</a></li>';
+		if ($showPanelsModules && current_user_can(FLUTTER_CAPABILITY_MODULES)) $startPageContent.= '<li><a href="">Manage Modules</a></li>';
+		if (RCCWP_Options::Get('canvas_show') && current_user_can(FLUTTER_CAPABILITY_LAYOUT)) $startPageContent.= '<li><a href="">Change theme Layout</a></li>';
+		if (RCCWP_Options::Get('ink_show') && current_user_can(FLUTTER_CAPABILITY_STYLE)) $startPageContent.= '<li><a href="">Change theme style</a></li>';
+
+		$startPageContent.= "</ul>";
+		echo $startPageContent;
+*/
+	}
+
+
+
+	
+	function AttachCustomWritePanelMenuItems()
+	{
+		require_once ('RCCWP_Options.php');
+		$assignToRole = RCCWP_Options::Get('assign-to-role');
+		$requiredPostsCap = 'edit_posts';
+		$requiredPagesCap = 'edit_pages';
+
+
+		$customWritePanels = RCCWP_CustomWritePanel::GetCustomWritePanels();
+
+		foreach ($customWritePanels as $panel)
+		{
+			if ($assignToRole == 1){
+				$requiredPostsCap = $panel->capability_name;
+				$requiredPagesCap = $panel->capability_name;
+			}
+
+			if ($panel->type == "post"){
+				add_submenu_page('post-new.php', __($panel->name), __($panel->name), $requiredPostsCap, 'post-new.php?custom-write-panel-id=' . $panel->id);
+				add_submenu_page('edit.php', __($panel->name), __($panel->name), $requiredPostsCap, 'edit.php?filter-posts=1&custom-write-panel-id=' . $panel->id);
+			}
+			else {
+				add_submenu_page('post-new.php', __($panel->name), __($panel->name), $requiredPagesCap, 'page-new.php?custom-write-panel-id=' . $panel->id);
+				add_submenu_page('edit.php', __($panel->name), __($panel->name), $requiredPagesCap, 'edit-pages.php?filter-posts=1&custom-write-panel-id=' . $panel->id);
+			}
+		}
+		
+		RCCWP_Menu::SetCurrentCustomWritePanelMenuItem();
+		
+	}
+
+	function HighlightCustomPanel(){
+		global $wpdb, $submenu_file, $post; 
+		
+		$result = $wpdb->get_results( " SELECT meta_value
+						FROM $wpdb->postmeta
+						WHERE post_id = '".$post->ID."' and meta_key = '_rc_cwp_write_panel_id'", ARRAY_A );
+		$currPage = basename($_SERVER['SCRIPT_NAME']);
+		if (count($result) > 0 && $currPage =="post.php" ){
+			$id = $result[0]['meta_value'];
+			$submenu_file = "edit.php?filter-posts=1&custom-write-panel-id=$id";
+		}
+		elseif (count($result) > 0 && $currPage == "page.php" ){
+			$id = $result[0]['meta_value'];
+			$submenu_file = "edit-pages.php?filter-posts=1&custom-write-panel-id=$id";
+		}
+		
+		
+	}
+
+	function FilterPostsPagesList($where){
+		global $wpdb;
+		if (isset($_GET['filter-posts'])) {
+			$panel_id = $_GET['custom-write-panel-id'];
+			$where = $where . " AND 0 < (SELECT count($wpdb->postmeta.meta_value)
+					FROM $wpdb->postmeta
+					WHERE $wpdb->postmeta.post_id = $wpdb->posts.ID and $wpdb->postmeta.meta_key = '_rc_cwp_write_panel_id' and $wpdb->postmeta.meta_value = '$panel_id') ";
+		}
+		return $where;
+		/*$i = 0;
+		if (isset($_GET['filter-posts']) && (!empty($posts))) {
+			$panel_id = $_GET['custom-write-panel-id'];
+			foreach($posts as $my_post){
+				$result = $wpdb->get_results( " SELECT meta_value
+					FROM $wpdb->postmeta
+					WHERE post_id = '$my_post->ID' and meta_key = '_rc_cwp_write_panel_id' and meta_value = '$panel_id'", ARRAY_A );
+				if (count($result) == 0 )
+					array_splice($posts, $i ,1);
+				else
+					$i++;
+			}
+
+		}
+		return $posts;*/
+	}
+	
+	function DetachWpWritePanelMenuItems()
+	{
+		require_once ('RCCWP_Options.php');
+		global $submenu;
+
+		$options = RCCWP_Options::Get();
+		
+		if ($options['hide-write-post'] == '1')
+			unset($submenu['post-new.php'][5]);
+		
+		if ($options['hide-write-page'] == '1')
+			unset($submenu['post-new.php'][10]);	
+	}
+	
+	function SetCurrentCustomWritePanelMenuItem()
+	{
+		
+		global $submenu_file;
+		global $menu;
+		
+		require_once ('RCCWP_Options.php');
+		$options = RCCWP_Options::Get();
+		
+		if ($options['default-custom-write-panel'] != '')
+		{
+			require_once ('RCCWP_CustomWritePanel.php');
+			
+			$customWritePanel = RCCWP_CustomWritePanel::Get((int)$options['default-custom-write-panel']);
+			
+			if ($customWritePanel->type == "post")
+				$menu[5][2] = 'post-new.php?custom-write-panel-id=' . (int)$options['default-custom-write-panel'];
+			else
+				$menu[5][2] = 'page-new.php?custom-write-panel-id=' . (int)$options['default-custom-write-panel'];
+			
+		}
+		
+		if ($_REQUEST['custom-write-panel-id'])
+		{
+			$customWritePanel = RCCWP_CustomWritePanel::Get((int)$_REQUEST['custom-write-panel-id']);
+			if ($_REQUEST['filter-posts']){
+				if ($customWritePanel->type == "post")
+					$submenu_file = 'edit.php?filter-posts=1&custom-write-panel-id=' . (int)$_REQUEST['custom-write-panel-id'];
+				else
+					$submenu_file = 'edit-pages.php?filter-posts=1&custom-write-panel-id=' . (int)$_REQUEST['custom-write-panel-id'];
+			}
+			else{
+				if ($customWritePanel->type == "post")
+					$submenu_file = 'post-new.php?custom-write-panel-id=' . (int)$_REQUEST['custom-write-panel-id'];
+				else
+					$submenu_file = 'page-new.php?custom-write-panel-id=' . (int)$_REQUEST['custom-write-panel-id'];
+			}
+		}
+
+	}
+
+	function AddThickbox()
+	{
+		if ($_GET['page']=='FlutterManageModules') {
+			// Overcome bug (http://wordpress.org/support/topic/196884)
+			$thickBoxCSS = get_bloginfo('url').'/wp-includes/js/thickbox/thickbox.css';
+			?>
+			<link rel='stylesheet' href='<?php echo $thickBoxCSS?>' type='text/css' />
+			<?php
+			
+			wp_enqueue_script('prototype');
+			wp_enqueue_script('thickbox');
+		}
+		
+	}
+	
+	function ShowPanel($panel){
+		return true;
+		require_once ('RCCWP_CustomWritePanel.php');
+		global $wpdb, $canvas;
+
+		if ($panel->always_show) return true;
+
+		$custom_panel_modules = RCCWP_CustomWritePanel::GetPanelCustomModules($panel->id);
+  		foreach ($custom_panel_modules as $panel_module){
+			//echo "SELECT * FROM ".$canvas->main." WHERE module_id = $panel_module->mod_id AND zone <> 'shelf'";
+			if($wpdb->get_results("SELECT * FROM ".$canvas->main." WHERE module_id = $panel_module->mod_id AND zone <> 'shelf'"))
+				return true;
+		}
+
+		if ( 0 < $wpdb->get_var("SELECT count($wpdb->postmeta.meta_value)
+			FROM $wpdb->postmeta
+			WHERE $wpdb->postmeta.meta_key = '_rc_cwp_write_panel_id' and $wpdb->postmeta.meta_value = '$panel->id'")){
+				return true;
+		}
+
+		return false;
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_CustomWritePanel.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CustomWritePanel.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CustomWritePanel.php (revision 21)
@@ -0,0 +1,550 @@
+<?php
+
+/**
+ * @package FlutterDatabaseObjects
+ */
+
+/**
+ * Create/edit/delete write panels.
+ * @package FlutterDatabaseObjects
+ */
+class RCCWP_CustomWritePanel
+{
+
+	/**
+	 * Get all Write Panels.
+	 *
+	 * @return array of objects containing all write panels. Each object contains
+	 * 			id, name, description, display_order, capability_name, type, always_show
+	 */
+	function GetCustomWritePanels()
+	{
+		global $wpdb;
+	
+		$sql = "SELECT id, name, description, display_order, capability_name, type FROM " . RC_CWP_TABLE_PANELS;
+			
+		$sql .= " ORDER BY display_order ASC";
+		$results = $wpdb->get_results($sql);
+		if (!isset($results)) 
+			$results = array();
+	
+		return $results;
+	}
+	
+	/**
+	 * Assign a specified write panel to a role. 
+	 *
+	 * @param integer $customWritePanelId panel id
+	 * @param string $roleName role name (see roles in wordpress)
+	 */
+	function AssignToRole($customWritePanelId, $roleName)
+	{
+		$customWritePanel = RCCWP_CustomWritePanel::Get($customWritePanelId);
+		$capabilityName = $customWritePanel->capability_name;
+		$role = get_role($roleName);
+		$role->add_cap($capabilityName);	
+	}
+	
+	/**
+	 * Create a new write panel.
+	 *
+	 * @param string $name write panel name
+	 * @param string $description write panel description
+	 * @param array $standardFields a list of standard fields ids that are to be displayed in 
+	 * 							in the panel. Use $STANDARD_FIELDS defined in RCCWP_Constant.php
+	 * @param array $categories array of category ids that are checked by default when the user
+	 * 							opens Write tab for that panel.
+	 * @param integer $display_order the order of the panel in Flutter > Write Panels tab
+	 * @param string $type 'post' or 'page'
+	 * @param boolean $createDefaultGroup indicates whether to create a default group.
+	 * @return the id of the write panel
+	 */
+	function Create($name, $description = '', $standardFields = array(), $categories = array(), $display_order = 1, $type = FALSE, $createDefaultGroup=true)
+	{
+		include_once('RC_Format.php');
+		global $wpdb;
+
+		$capabilityName = RCCWP_CustomWritePanel::GetCapabilityName($name);
+		if (!$type) $type = $_POST['radPostPage'];
+		$sql = sprintf(
+			"INSERT INTO " . RC_CWP_TABLE_PANELS .
+			" (name, description, display_order, capability_name, type)" .
+			" values" .
+			" (%s, %s, %d, %s, %s)", 
+			RC_Format::TextToSql($name), 
+			RC_Format::TextToSql($description),
+			$display_order,
+			RC_Format::TextToSql($capabilityName),
+			RC_Format::TextToSql($type)	
+		);
+		
+		$wpdb->query($sql);
+		$customWritePanelId = $wpdb->insert_id;
+		
+		if (!isset($categories))
+			$categories = array();
+		foreach ($categories as $cat_id)
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_PANEL_CATEGORY .
+				" (panel_id, cat_id)" .
+				" values (%d, %d)",
+				$customWritePanelId,
+				$cat_id 
+				);
+			$wpdb->query($sql);
+		}
+		
+		if (!isset($standardFields))
+			$standardFields = array();
+		foreach ($standardFields as $standard_field_id)
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+				" (panel_id, standard_field_id)" .
+				" values (%d, %d)",
+				$customWritePanelId,
+				$standard_field_id 
+				);
+			$wpdb->query($sql);
+		}
+				
+		// Create default group
+		if ($createDefaultGroup){
+			include_once('RCCWP_CustomGroup.php');
+			RCCWP_CustomGroup::Create($customWritePanelId, '__default', false, false);
+		}
+		
+		RCCWP_CustomWritePanel::AssignToRole($customWritePanelId, 'administrator');
+		
+		return $customWritePanelId;
+	}
+	
+	/**
+	 * Delete a write panel without deleting its modules
+	 *
+	 * @param integer $customWritePanelId write panel id
+	 */
+	function Delete($customWritePanelId = null)
+	{
+		if (isset($customWritePanelId))
+		{
+			global $wpdb;
+			
+			$customWritePanel = RCCWP_CustomWritePanel::Get($customWritePanelId);
+					  	
+  			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANELS .
+				" WHERE id = %d",
+				$customWritePanel->id
+				);
+			$wpdb->query($sql);
+			
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_CATEGORY .
+				" WHERE panel_id = %d",
+				$customWritePanel->id
+				);
+			$wpdb->query($sql);
+			
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+				" WHERE panel_id = %d",
+				$customWritePanelId
+				);
+			$wpdb->query($sql);
+			
+		}
+	}
+	
+	/**
+	 * Get the properties of a write panel
+	 *
+	 * @param unknown_type $customWritePanelId
+	 * @return an object containing the properties of the write panel which are
+	 * 			id, name, description, display_order, capability_name, type
+	 */
+	function Get($customWritePanelId)
+	{
+		global $wpdb;
+	
+		$sql = "SELECT id, name, description, display_order, capability_name, type FROM " . RC_CWP_TABLE_PANELS .
+			" WHERE id = " . (int)$customWritePanelId;
+		
+		$results = $wpdb->get_row($sql);
+		
+		return $results;
+	}
+	
+	/**
+	 * Get a list of the ids of teh categories assigned to  a write panel
+	 *
+	 * @param integer $customWritePanelId write panel id
+	 * @return array of ids
+	 */
+	function GetAssignedCategoryIds($customWritePanelId)
+	{
+		$results = RCCWP_CustomWritePanel::GetAssignedCategories($customWritePanelId);
+		$ids = array();
+		foreach ($results as $r)
+		{
+			$ids[] = $r->cat_id;
+		}
+		
+		return $ids;
+	}
+	
+	/**
+	 * Get a list of categories assigned to a write panel
+	 *
+	 * @param integer $customWritePanelId write panel id
+	 * @return array of objects, each object contains cat_id and cat_name 
+	 */
+	function GetAssignedCategories($customWritePanelId)
+	{
+		global $wpdb;
+		/*
+			$sql = "SELECT rc.cat_id, cat_name FROM " . RC_CWP_TABLE_PANEL_CATEGORY . 
+			" rc JOIN $wpdb->categories wp ON rc.cat_ID = wp.cat_ID" . 
+			" WHERE panel_id = " . $customWritePanelId;
+		*/
+		
+		if( $wpdb->terms != '' )
+		{
+			$sql = "SELECT rc.cat_id, wp.name AS cat_name FROM " . 
+				RC_CWP_TABLE_PANEL_CATEGORY . "
+				rc JOIN $wpdb->terms wp ON rc.cat_ID = wp.term_id" . "
+				WHERE panel_id = " . $customWritePanelId;
+		}
+		else
+		{
+			$sql = "SELECT rc.cat_id, cat_name FROM " . 
+				RC_CWP_TABLE_PANEL_CATEGORY . "
+				rc JOIN $wpdb->categories wp ON rc.cat_ID = wp.cat_ID 
+				WHERE panel_id = " . $customWritePanelId;
+		}
+		
+
+		$results = $wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		
+		return $results;
+	}
+	
+	/**
+	 * Create a capability name for a write panel given its name. This function is 
+	 * copied from WP's sanitize_title_with_dashes($title) (formatting.php)
+	 *
+	 * @param string $customWritePanelName panel name
+	 * @return string capability name
+	 */
+	function GetCapabilityName($customWritePanelName)
+	{
+		// copied from WP's sanitize_title_with_dashes($title) (formatting.php)
+		$capabilityName = strip_tags($customWritePanelName);
+		// Preserve escaped octets.
+		$capabilityName = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $capabilityName);
+		// Remove percent signs that are not part of an octet.
+		$capabilityName = str_replace('%', '', $capabilityName);
+		// Restore octets.
+		$capabilityName = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $capabilityName);
+   
+		$capabilityName = remove_accents($capabilityName);
+		if (seems_utf8($capabilityName)) 
+		{
+			if (function_exists('mb_strtolower')) 
+			{
+				$capabilityName = mb_strtolower($capabilityName, 'UTF-8');
+			}
+			$capabilityName = utf8_uri_encode($capabilityName, 200);
+		}
+   
+		$capabilityName = strtolower($capabilityName);
+		$capabilityName = preg_replace('/&.+?;/', '', $capabilityName); // kill entities
+		$capabilityName = preg_replace('/[^%a-z0-9 _-]/', '', $capabilityName);
+		$capabilityName = preg_replace('/\s+/', '_', $capabilityName);
+		$capabilityName = preg_replace('|-+|', '_', $capabilityName);
+		$capabilityName = trim($capabilityName, '_');
+   
+		return $capabilityName;
+	}
+
+
+
+		
+
+	/**
+	 * Get a list of the standard fields of a the write panel
+	 *
+	 * @param integer $customWritePanelId panel id
+	 * @return array of ids of the standard fields (see $STANDARD_FIELDS defined in RCCWP_Constant.php) 
+	 */
+	function GetStandardFields($customWritePanelId)
+	{
+		global $wpdb;
+		$sql = "SELECT standard_field_id FROM " . RC_CWP_TABLE_PANEL_STANDARD_FIELD . 
+				" WHERE panel_id = " . $customWritePanelId;
+		$results = $wpdb->get_col($sql);
+		if (!isset($results))
+			$results = array();
+		
+		return $results;
+	}
+	
+	/**
+	 * Updates the properties of a write panel
+	 *
+	 * @param integer $customWritePanelId panel id
+	 * @param string $name write panel name
+	 * @param string $description write panel description
+	 * @param array $standardFields a list of standard fields ids that are to be displayed in 
+	 * 							in the panel. Use $STANDARD_FIELDS defined in RCCWP_Constant.php
+	 * @param array $categories array of category ids that are checked by default when the user
+	 * 							opens Write tab for that panel.
+	 * @param integer $display_order the order of the panel in Flutter > Write Panels tab
+	 * @param string $type 'post' or 'page'
+	 */
+	function Update($customWritePanelId, $name, $description = '', $standardFields = array(), $categories = array(), $display_order = 1)
+	{
+		include_once('RC_Format.php');
+		global $wpdb;
+		
+		$capabilityName = RCCWP_CustomWritePanel::GetCapabilityName($name);
+	
+		$sql = sprintf(
+			"UPDATE " . RC_CWP_TABLE_PANELS .
+			" SET name = %s" .
+			" , description = %s" .
+			" , display_order = %d" .
+			" , capability_name = %s" .
+			" , type = %s" .
+			" where id = %d",
+			RC_Format::TextToSql($name), 
+			RC_Format::TextToSql($description),
+			$display_order,
+			RC_Format::TextToSql($capabilityName),
+			RC_Format::TextToSql($_POST['radPostPage']),
+			$customWritePanelId );
+		
+		$wpdb->query($sql);
+		
+		if (!isset($categories) || empty($categories))
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_CATEGORY .
+				" WHERE panel_id = %d",
+				$customWritePanelId
+				);
+			
+			$wpdb->query($sql);
+		}
+		else
+		{
+			$currentCategoryIds = array();
+			$currentCategoryIds = RCCWP_CustomWritePanel::GetAssignedCategoryIds($customWritePanelId);
+			
+			$keepCategoryIds = array_intersect($currentCategoryIds, $categories);
+			$deleteCategoryIds = array_diff($currentCategoryIds, $keepCategoryIds);
+			$insertCategoryIds = array_diff($categories, $keepCategoryIds);
+			
+			foreach ($insertCategoryIds as $cat_id)
+			{
+				$sql = sprintf(
+					"INSERT INTO " . RC_CWP_TABLE_PANEL_CATEGORY .
+					" (panel_id, cat_id)" .
+					" values (%d, %d)",
+					$customWritePanelId,
+					$cat_id 
+					);
+				$wpdb->query($sql);
+			}
+			
+			if (!empty($deleteCategoryIds))
+			{
+				$sql = sprintf(
+					"DELETE FROM " . RC_CWP_TABLE_PANEL_CATEGORY .
+					" WHERE panel_id = %d" .
+					" AND cat_id IN (%s)",
+					$customWritePanelId,
+					implode(',', $deleteCategoryIds)
+					);
+				
+				$wpdb->query($sql);
+			}
+		}
+		
+		if (!isset($standardFields) || empty($standardFields))
+		{			
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+				" WHERE panel_id = %d",
+				$customWritePanelId
+				);
+			$wpdb->query($sql);
+		}
+		else
+		{
+			$currentStandardFieldIds = array();
+			$currentStandardFieldIds = RCCWP_CustomWritePanel::GetStandardFields($customWritePanelId);
+			
+			$keepStandardFieldIds = array_intersect($currentStandardFieldIds, $standardFields);
+			$deleteStandardFieldIds = array_diff($currentStandardFieldIds, $keepStandardFieldIds);
+			$insertStandardFieldIds = array_diff($standardFields, $keepStandardFieldIds);
+			
+			foreach ($insertStandardFieldIds as $standard_field_id)
+			{
+				$sql = sprintf(
+					"INSERT INTO " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+					" (panel_id, standard_field_id)" .
+					" values (%d, %d)",
+					$customWritePanelId,
+					$standard_field_id 
+					);
+				$wpdb->query($sql);
+			}
+			
+			if (!empty($deleteStandardFieldIds))
+			{
+				$sql = sprintf(
+					"DELETE FROM " . RC_CWP_TABLE_PANEL_STANDARD_FIELD .
+					" WHERE panel_id = %d" .
+					" AND standard_field_id IN (%s)",
+					$customWritePanelId,
+					implode(',', $deleteStandardFieldIds)
+					);
+				
+				$wpdb->query($sql);
+			}
+		}
+	
+	}
+	
+	/**
+	 * Retrieves the groups of a module
+	 *
+	 * @param integer $customWriteModuleId module id
+	 * @return array of objects representing basic information of the group, 
+	 * 				each object contains id, name and module_id   
+	 */
+	function GetCustomGroups($customWritePanelId)
+	{
+		global $wpdb;
+		$sql = "SELECT * FROM " . RC_CWP_TABLE_PANEL_GROUPS .
+			" WHERE panel_id = " . $customWritePanelId .
+			" ORDER BY name";
+
+		$results =$wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		
+		return $results;
+	}
+	
+	
+	/**
+	 * Import a write panel given the file path.
+	 * @param string $panelFilePath the full path of the panel file
+	 * @param string $writePanelName the write panel name, if this value if false, the function will
+	 * 							use the pnl filename as the write panel name. The default value is false
+	 * @return the panel id, or false in case of error.
+	 */
+	function Import($panelFilePath, $writePanelName = false)
+	{
+		include_once('RCCWP_CustomGroup.php');
+		include_once('RCCWP_CustomField.php');
+		include_once('RCCWP_Application.php');
+
+		if (!$writePanelName)
+			//use filename
+			$writePanelName = basename($panelFilePath, ".pnl");
+
+		if ($writePanelName == '') return false;
+
+		// Append a number if the panel already exists,
+		$i = 1;
+		$newWritePanelName = $writePanelName;
+		//while (file_exists(CANVASPATH.'/modules/'.$newModuleName)){
+		//	$newModuleName = $moduleName. "_" . $i++;
+		//}
+		//$moduleName = $newModuleName;
+
+		// Unserialize file
+		$imported_data = unserialize(file_get_contents($panelFilePath));
+		$types_results = RCCWP_CustomField::GetCustomFieldTypes();
+		$types = array();
+		foreach($types_results as $types_result){
+			$types[$types_result->name] = $types_result->id;
+		}
+		
+		// Prepare categories list
+		$assignedCategories = array();
+		foreach($imported_data['panel']->assignedCategories as $cat_name){
+			$assignedCategories[] = wp_create_category($cat_name);
+		}
+		
+		//Create write panel
+		$writePanelID = RCCWP_CustomWritePanel::Create($writePanelName, $imported_data['panel']->description, $imported_data['panel']->standardFieldsIDs, $assignedCategories,$imported_data['panel']->display_order, $imported_data['panel']->type, false);
+		
+		foreach($imported_data['fields'] as $groupName => $group){
+			// For backward compatability
+			if (!isset($group->fields)) {
+				$newGroup->fields = $group;
+				$group = $newGroup; 
+			}
+			
+			// Import group
+			$groupID = RCCWP_CustomGroup::Create($writePanelID, $groupName, $group->duplicate, $group->at_right);
+			
+			// Import group fields
+			foreach ($group->fields as $field){
+				$fieldOptions = @implode("\n", $field->options);
+				$fieldDefault = @implode("\n", $field->default_value);
+				RCCWP_CustomField::Create($groupID, $field->name, $field->description, $field->display_order, $field->required_field, $types[$field->type], $fieldOptions, $fieldDefault, $field->properties, $field->duplicate);
+			}
+		}
+
+
+		return $writePanelID;
+	}
+	
+
+	/**
+	 * Export a write panel to file
+	 *
+	 * @param integer $panelID
+	 * @param string $exportedFilename the full path of the file to which the panel will be exported
+	 */
+	function Export($panelID, $exportedFilename){
+		include_once('RCCWP_CustomWriteModule.php');
+		include_once('RCCWP_CustomGroup.php');
+		include_once('RCCWP_CustomField.php');
+	
+		$exported_data = array();
+
+		$writePanel = RCCWP_CustomWritePanel::Get($panelID);
+		$writePanel->standardFieldsIDs = RCCWP_CustomWritePanel::GetStandardFields($panelID);
+		$writePanel->assignedCategories = array(); 
+		$assignedCategories = RCCWP_CustomWritePanel::GetAssignedCategories($panelID);
+		foreach($assignedCategories as $assignedCategory){
+			$writePanel->assignedCategories[] = $assignedCategory->cat_name;
+		}
+		
+		$moduleGroups = RCCWP_CustomWritePanel::GetCustomGroups($panelID);
+		foreach( $moduleGroups as $moduleGroup){
+			$groupFields[$moduleGroup->name]->fields = RCCWP_CustomGroup::GetCustomFields($moduleGroup->id);
+ 			$groupFields[$moduleGroup->name]->duplicate = $moduleGroup->duplicate;
+			$groupFields[$moduleGroup->name]->at_right = $moduleGroup->at_right;
+		}
+
+		$exported_data['panel'] = $writePanel;
+		$exported_data['fields'] = $groupFields;
+		
+		$handle = fopen($exportedFilename, "w");
+		$result = fwrite($handle, serialize($exported_data));
+		@fclose($handle);
+	}
+	
+	
+}
+?>
Index: /afridex/plugins/Flutter/classes/FlutterPanelFields.php
===================================================================
--- /afridex/plugins/Flutter/classes/FlutterPanelFields.php (revision 21)
+++ /afridex/plugins/Flutter/classes/FlutterPanelFields.php (revision 21)
@@ -0,0 +1,25 @@
+<?php
+
+
+class FlutterPanelFields{
+	var $id;
+	var $displayName;
+	var $cssId;
+	var $defaultChecked;
+	var $isAdvancedField;
+	var $forPost;
+	var $forPage;
+	var $excludeVersion;
+	
+	function FlutterPanelFields($id, $displayName, $cssId, $defaultChecked, $isAdvancedField, $forPost, $forPage, $excludeVersion){
+		$this->id = $id;	
+		$this->displayName = $displayName;
+		$this->cssId = $cssId;
+		$this->defaultChecked = $defaultChecked;
+		$this->isAdvancedField = $isAdvancedField;
+		$this->forPost = $forPost;
+		$this->forPage = $forPage;
+		$this->excludeVersion = $excludeVersion;
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_HTML_Purifier.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_HTML_Purifier.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_HTML_Purifier.php (revision 21)
@@ -0,0 +1,77 @@
+<?php
+class RCCWP_HTML_Purifier
+{
+	function ApplyHTMLPurifier($data)
+	{
+		include_once('RCCWP_Options.php');
+		if (0 == RCCWP_Options::Get('enable-HTMLPurifier')) return $data;
+		
+		// We need to handle <!-- manually because HTMLPurifier removes it
+		$MORE_TAG = "<!--more-->";
+
+		$purifier = RCCWP_HTML_Purifier::get_purifier ();
+		
+		$pos = strpos($data, $MORE_TAG);
+		if ($pos === false){
+			$data =  addslashes ($purifier->purify (stripslashes ($data)));
+		}
+		else{
+			$before_more = addslashes ($purifier->purify (stripslashes (substr($data,0,$pos))));
+			$after_more = addslashes ($purifier->purify (stripslashes (substr($data,$pos+strlen($MORE_TAG)))));
+			$data = $before_more.$MORE_TAG.$after_more;
+		}
+
+		return $data;
+	}
+
+	/**
+	 * Get an HTML Purifier object with all settings already configured
+	 *
+	 * @return HTMLPurifier
+	 **/
+	
+	function get_purifier ()
+	{
+		require_once('purifier_lib/HTMLPurifier.auto.php');
+		include_once('RCCWP_Options.php');
+
+	 	$config = HTMLPurifier_Config::createDefault();
+
+		// Set base options
+		$config->set ('HTML',   'Doctype', "HTML 4.01 Transitional");
+		$config->set ('Core',   'Encoding', get_option ('blog_charset'));
+		$config->set ('HTML',   'TidyLevel', RCCWP_Options::Get('tidy-level'));
+		$config->set ('Output', 'TidyFormat', true);
+		
+		
+		// If we can write to the cache directory then set it (directory is same as used by WP-Cache and WP object cache)
+		if (is_writeable (dirname (RCCWP_HTML_Purifier::cache_directory ())))
+		{
+			if (!file_exists (RCCWP_HTML_Purifier::cache_directory ()))
+				@mkdir (RCCWP_HTML_Purifier::cache_directory ());
+				die('here');
+			$config->set ('Cache', 'SerializerPath', RCCWP_HTML_Purifier::cache_directory ());	
+		}
+		else{
+			$config->set ('Cache', 'DefinitionImpl', null);
+			$config->set ('Cache', 'SerializerPath', null);	
+		}
+		
+		
+		return new HTMLPurifier ($config);
+	}
+
+
+	/**
+	 * Return the cache directory
+	 *
+	 * @return string
+	 **/
+	
+	function cache_directory ()
+	{
+		return realpath (dirname (__FILE__).'/../../cache/html-purified');
+	}
+
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_ManagementPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_ManagementPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_ManagementPage.php (revision 21)
@@ -0,0 +1,223 @@
+<?php
+include_once('RCCWP_Application.php');
+
+class RCCWP_ManagementPage
+{
+	function AssignCustomWritePanel()
+	{
+		$postId = (int)$_GET['assign-custom-write-panel'];
+		$customWritePanels = RCCWP_CustomWritePanel::GetCustomWritePanels();
+		$customWritePanelOptions = RCCWP_Options::Get();
+		$message = 'The Post that you\'re about to edit is not associated with any Custom Write Panel.';
+		?>
+		
+		<div id="message" class="updated"><p><?php _e($message); ?></p></div>
+		
+		<div class="wrap">
+		<h2><?php _e('Assign Custom Write Panel'); ?></h2>
+		
+		<form action="" method="post" id="assign-custom-write-panel-form">
+		
+		<table class="optiontable">
+		<tbody>
+		<tr valign="top">
+			<th scope="row">Custom Write Panel:</th>
+			<td>
+				<select name="custom-write-panel-id" id="custom-write-panel-id">
+					<option value="">(None)</option>
+				<?php
+				$defaultCustomWritePanel = $customWritePanelOptions['default-custom-write-panel'];
+				foreach ($customWritePanels as $panel) :
+					$selected = $panel->id == $defaultCustomWritePanel ? 'selected="selected"' : '';
+				?>
+					<option value="<?php echo $panel->id?>" <?php echo $selected?>><?php echo $panel->name?></option>
+				<?php
+				endforeach;
+				?>
+				</select>
+			</td>
+		</tr>
+		</tbody>
+		</table>
+		
+		<input type="hidden" name="post-id" value="<?php echo $postId?>" />
+		<p class="submit" >
+			<input name="edit-with-no-custom-write-panel" type="submit" value="Don't Assign Custom Write Panel" />
+			<input name="edit-with-custom-write-panel" type="submit" value="Edit with Custom Write Panel" />
+		</p>
+		
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+	
+	function GetCustomFieldEditUrl($customWriteModuleId, $customGroupId, $customFieldId)
+	{
+		$url = '?page=' . 'FlutterManageModules' . '&edit-custom-field=' . $customFieldId . '&custom-group-id=' . $customGroupId . '&custom-write-module-id='. $customWriteModuleId ;
+		return $url;
+	}
+	
+	function GetCustomFieldDeleteUrl($customGroupId, $customFieldId)
+	{
+		$url = '?page=' . 'FlutterManageModules' . '&delete-custom-field=' . $customFieldId . '&custom-group-id=' . $customGroupId;
+		return $url;
+	}
+
+	function GetModuleDuplicateEditUrl($customWriteModuleId, $duplicateId)
+	{
+		$url = '?page=' . 'FlutterManageModules' . '&edit-module-duplicate=' . $duplicateId . '&module-duplicate-id=' . $duplicateId . '&custom-write-module-id='. $customWriteModuleId ;
+		return $url;
+	}
+	
+	function GetModuleDuplicateDeleteUrl($customWriteModuleId, $duplicateId)
+	{
+		$url = '?page=' . 'FlutterManageModules' . '&delete-module-duplicate=' . $duplicateId . '&module-duplicate-id=' . $duplicateId . '&custom-write-module-id='. $customWriteModuleId ;
+		return $url;
+	}
+	
+	function GetCustomWritePanelEditUrl($customWritePanelId)
+	{
+		$url = '?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') . '&view-custom-write-panel=' . $customWritePanelId . '&custom-write-panel-id=' . $customWritePanelId;
+		return $url;
+	}
+	
+	
+	
+	function GetCustomWritePanelDeleteUrl($customWritePanelId)
+	{
+		$url = '?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') . '&delete-custom-write-panel=' . $customWritePanelId . '&custom-write-panel-id=' . $customWritePanelId;
+		return $url;
+	}
+
+	function GetCustomWriteModuleEditUrl($moduleId)
+	{
+		$url = '?page=' . 'FlutterManageModules' . '&view-custom-write-module=' . $moduleId . '&custom-write-module-id=' . $moduleId;
+		return $url;
+	}
+	
+	function GetCustomWriteModuleDeleteUrl($moduleId)
+	{
+		$url = '?page=' . 'FlutterManageModules' . '&delete-custom-write-module=' . $moduleId . '&custom-write-module-id=' . $moduleId;
+		return $url;
+	}
+
+
+	function GetCustomGroupEditUrl($groupId, $moduleId)
+	{
+		$url = '?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') . '&flutter_action=view-custom-group&custom-group-id=' . $groupId. '&custom-write-module-id=' . $moduleId;
+		return $url;
+	}
+	
+	function GetCustomGroupDeleteUrl($groupId)
+	{
+		$url = '?page=' . 'FlutterManageModules' . '&delete-custom-group=' . $groupId . '&custom-group-id=' . $groupId;
+		return $url;
+	}
+
+	function GetCustomPanelModuleDeleteUrl($customWritePanelId, $panelModuleId)
+	{
+		$url = '?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') . '&delete-custom-panel-module=' . $panelModuleId . '&custom-write-panel-id=' . $customWritePanelId;
+		return $url;
+	}
+	
+	
+	function GetCustomWriteModuleGenericUrl($flutterAction, $moduleId = null)
+	{
+		if (empty($moduleId) && isset($_REQUEST['custom-write-module-id'])){
+			$moduleId = $_REQUEST['custom-write-module-id'];
+		}
+			
+		if (!empty($moduleId)){
+			$url = RCCWP_ManagementPage::GetModulePage() . "&custom-write-module-id=$moduleId&flutter_action=$flutterAction";
+		}
+		else{
+			$url = RCCWP_ManagementPage::GetModulePage() . "&flutter_action=$flutterAction";
+		}
+		
+		return $url;
+	}
+	
+	/**
+	 * Generates a url containing the write panel id and the action
+	 *
+	 * @return unknown
+	 */
+	function GetCustomWritePanelGenericUrl($flutterAction, $customWritePanelId = null)
+	{
+		if (empty($customWritePanelId) && isset($_REQUEST['custom-write-panel-id'])){
+			$customWritePanelId = $_REQUEST['custom-write-panel-id'];
+		}
+			
+		if (!empty($customWritePanelId)){
+			$url = RCCWP_ManagementPage::GetPanelPage() . "&custom-write-panel-id=$customWritePanelId&flutter_action=$flutterAction";
+		}
+		else{
+			$url = RCCWP_ManagementPage::GetPanelPage() . "&flutter_action=$flutterAction";
+		}
+		
+		return $url;
+	}
+	
+	function GetPanelPage(){
+		return '?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php');
+	}
+	
+	function GetModulePage(){
+		return '?page=FlutterManageModules';
+	}
+	
+	
+
+	// ----------- Modules
+	function ViewModules()
+	{
+		$customWriteModules = RCCWP_CustomWriteModule::GetCustomModules();
+		?>
+
+		<div class="wrap">
+		
+		<form action="<?php echo RCCWP_ManagementPage::GetCustomWriteModuleGenericUrl('import-module')?>" method="post"  id="posts-filter" name="ImportModuleForm" enctype="multipart/form-data">
+			<h2><?php _e('Modules'); ?></h2>
+			<p id="post-search">					
+				<input id="import-module-file" name="import-module-file" type="file" /> 
+				<a href="#none" class="button-secondary" style="display:inline" onclick="document.ImportModuleForm.submit();"><?php _e('Import a Module'); ?></a>
+				<a href="<?php echo RCCWP_ManagementPage::GetCustomWriteModuleGenericUrl('create-custom-write-module'); ?>" class="button-secondary" style="display:inline"><?php _e('+ Create a Module'); ?></a>
+			</p>	
+		</form>
+
+		<br class="clear"/>
+		<table cellpadding="3" cellspacing="3" width="100%" class="widefat">
+		<thead>
+		<tr>
+			<th scope="col" width="70%"><?php _e('Name'); ?></th>
+			<th scope="col" colspan="2" ><?php _e('Actions'); ?></th>
+		</tr>
+		</thead>
+		<tbody>
+		<?php
+		foreach ($customWriteModules as $module) :
+			$class = $class == '' ? 'alternate' : '';
+		?>
+		<tr class="<?php echo $class?>">
+			<td><?php echo $module->name ?></td>
+			<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWriteModuleEditUrl($module->id); ?>" class="edit"><?php _e('Edit') ?></a></td>
+			<td><a href="<?php echo FLUTTER_URI."RCCWP_ExportModule.php?custom-write-module-id={$module->id}";?>&amp;TB_iframe=true&amp;height=500&amp;width=700" class="thickbox" title='Export Module'><?php _e('Export'); ?></a></td>
+		</tr>
+		<?php
+		endforeach;
+		?>
+		</tbody>
+		</table>
+
+		<form style="display:none" id="do_export" name="do_export" action="<?php echo FLUTTER_URI."RCCWP_ExportModule.php" ?>" method="post" >
+			<input type="text" name="write_panels"/>
+			<input type="text" name="custom-write-module-id"/>
+		</form>	
+		
+		</div>
+		<?php 
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_CustomField.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CustomField.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CustomField.php (revision 21)
@@ -0,0 +1,430 @@
+<?php
+
+/**
+ * @package FlutterDatabaseObjects
+ */
+
+/**
+ * Create/Edit/Delete custom fields
+ * @package FlutterDatabaseObjects
+ */
+
+class RCCWP_CustomField
+{
+	/**
+	 * Create a new custom field
+	 *
+	 * @param id $customGroupId the id of the group that will contain the field
+	 * @param string $name the name of the field, the name is used to uniquely identify the field
+	 * 							when retrieving its value.
+	 * @param string $label the label of the field, the label is displayed beside the field
+	 * 							in Write tab. 
+	 * @param integer $order the order of the field when it is displayed in 
+	 * 							the Write tab.
+	 * @param integer $required_field whether this field is a required field. Required fields
+	 * 							doesn't allow users to save a post if they are null. 
+	 * 
+	 * @param integer $type the type of the field. Use $FIELD_TYPES defined in RCCWP_Constant.php
+	 * @param array $options array of strings that represent the list of the field if
+	 * 							its type is list.
+	 * @param array $default_value array of strings that represent default value(s) of
+	 * 							of the field if	its type is list.
+	 * @param array $properties an array containing extra properties of the field.
+	 * @return the new field id
+	 */
+	function Create($customGroupId, $name, $label, $order = 1, $required_field = 0, $type, $options = null, $default_value = null, $properties = null,$duplicate)
+	{
+		global $wpdb;
+
+		$name = stripslashes(stripslashes($name));
+		$name = addslashes($name);
+
+		$label = stripslashes(stripslashes($label));
+		$label = addslashes($label);
+
+		$sql = sprintf(
+			"INSERT INTO " . RC_CWP_TABLE_GROUP_FIELDS .
+			" (group_id, name, description, display_order, required_field, type, CSS, duplicate) values (%d, %s, %s, %d, %d, %d, %s, %d)",
+			$customGroupId,
+			RC_Format::TextToSql($name),
+			RC_Format::TextToSql($label),
+			$order,
+			$required_field,
+			$type,
+			"'".$_POST['custom-field-css']."'",
+			$duplicate
+			);
+		$wpdb->query($sql);
+		
+		$customFieldId = $wpdb->insert_id;
+		
+		$field_type = RCCWP_CustomField::GetCustomFieldTypes($type);
+		if ($field_type->has_options == "true")
+		{
+			if (!is_array($options)) {
+				$options = stripslashes($options);
+				$options = explode("\n", $options);
+			}
+			array_walk($options, array(RC_Format, TrimArrayValues));
+			$options = addslashes(serialize($options));
+			
+			if (!is_array($default_value)) {
+				$default_value = stripslashes($default_value);
+				$default_value = explode("\n", $default_value);
+			}
+			array_walk($default_value, array(RC_Format, TrimArrayValues));
+			$default_value = addslashes(serialize($default_value));
+			
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" (custom_field_id, options, default_option) values (%d, %s, %s)",
+				$customFieldId,
+				RC_Format::TextToSql($options),
+				RC_Format::TextToSql($default_value)
+				);	
+			$wpdb->query($sql);	
+		}
+		
+		if ($field_type->has_properties == "true")
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" (custom_field_id, properties) values (%d, %s)",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($properties))
+				);	
+			$wpdb->query($sql);	
+		}
+		
+		return $customFieldId;
+	}
+	
+	/**
+	 * Delete a field
+	 *
+	 * @param integer $customFieldId field id
+	 */
+	function Delete($customFieldId = null)
+	{
+		global $wpdb;
+		
+		$customField = RCCWP_CustomField::Get($customFieldId);
+		
+		$sql = sprintf(
+			"DELETE FROM " . RC_CWP_TABLE_GROUP_FIELDS .
+			" WHERE id = %d",
+			$customFieldId
+			);
+		$wpdb->query($sql);
+		
+		if ($customField->has_options == "true")
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);	
+			$wpdb->query($sql);	
+		}
+	}
+	
+	/**
+	 * Get the field information including properties, options and default value(s)
+	 *
+	 * @param integer $customFieldId field id
+	 * @return an object containing information about fields. The object contains 
+	 * 			3 objects: properties, options and default_value
+	 */
+	function Get($customFieldId)
+	{
+		global $wpdb;
+		$sql = "SELECT cf.group_id, cf.id, cf.name, cf.CSS, tt.id AS type_id, tt.name AS type, cf.description, cf.display_order, cf.required_field, co.options, co.default_option AS default_value, tt.has_options, cp.properties, tt.has_properties, tt.allow_multiple_values, duplicate FROM " . RC_CWP_TABLE_GROUP_FIELDS .
+			" cf LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS . " co ON cf.id = co.custom_field_id" .
+			" LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " cp ON cf.id = cp.custom_field_id" .
+			" JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " tt ON cf.type = tt.id" . 
+			" WHERE cf.id = " . $customFieldId;
+		$results = $wpdb->get_row($sql);
+			
+		$results->options = unserialize($results->options);
+		$results->properties = unserialize($results->properties);
+		$results->default_value = unserialize($results->default_value);
+		return $results;
+	}
+	
+	/**
+	 * Retrievies information about a specified type
+	 *
+	 * @param integer $customFieldTypeId the type id, if null, a list of all types will be returned
+	 * @return a list/object containing information about the specified type. The information 
+	 * 			includes id, name, description, has_options, has_properties, and
+	 * 			allow_multiple_values (whether fields of that type can have more than one default value)
+	 */
+	function GetCustomFieldTypes($customFieldTypeId = null)
+	{
+		global $wpdb;
+	
+		if (isset($customFieldTypeId))
+		{
+			$sql = "SELECT id, name, description, has_options, has_properties, allow_multiple_values FROM " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES .
+				" WHERE id = " . (int)$customFieldTypeId;
+			$results = $wpdb->get_row($sql);	
+		}
+		else
+		{
+			$sql = "SELECT id, name, description, has_options, has_properties, allow_multiple_values FROM " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES;
+			$results = $wpdb->get_results($sql);
+			if (!isset($results))
+				$results = array();
+		}
+		return $results;
+	}
+
+	/**
+	 * Retrieves the value of a custom field for a specified post
+	 *
+	 * @param integer $postId post id
+	 * @param string $customFieldName field name
+	 * @return a string containing field value
+	 */
+	/*function GetCustomFieldValue($postId, $customFieldName)
+	{
+		// TODO Get custom field id
+		$customFieldId = 1;
+		
+		// TODO Given customFieldID, $groupIndex and $fieldIndex get meta_id
+		$fieldMetaID = 1;
+		
+		// use get_post_meta_by_id to get meta value 
+		
+		return get_post_meta($postId, $customFieldName, true);
+	}*/
+	
+	
+	function GetMetaID($postId, $customFieldName, $groupIndex=1, $fieldIndex=1){
+		global $wpdb;
+		
+		// Given $postId, $customFieldName, $groupIndex and $fieldIndex get meta_id
+		return $wpdb->get_var("SELECT id FROM " . RC_CWP_TABLE_POST_META . 
+						" WHERE field_name = '$customFieldName' AND group_count = $groupIndex ". 
+						" AND field_count = $fieldIndex AND post_id = $postId" );
+		
+		
+	}
+	
+	/**
+	 * Retrieves the value of a custom field for a specified post
+	 *
+	 * @param boolean $single
+	 * @param integer $postId
+	 * @param string $customFieldName
+	 * @param integer $groupIndex
+	 * @param integer $fieldIndex
+	 * @return a
+	 */
+	function GetCustomFieldValues($single, $postId, $customFieldName, $groupIndex=1, $fieldIndex=1)
+	{
+		global $wpdb;
+		
+		$fieldMetaID = RCCWP_CustomField::GetMetaID($postId, $customFieldName, $groupIndex, $fieldIndex);
+		
+		// for backward compatability, if no accociated row was found, use old method
+		if (!$fieldMetaID){
+			//$customFieldName =  $wpdb->get_var("SELECT name FROM ". RC_CWP_TABLE_GROUP_FIELDS .
+			//									" WHERE id = '$customFieldId'");
+			return get_post_meta($postId, $customFieldName, $single);
+		}
+		
+		// Get meta value
+		$mid = (int) $fieldMetaID;
+		$meta = $wpdb->get_row( "SELECT * FROM $wpdb->postmeta WHERE meta_id = '$mid'" );
+		if (!$single) return unserialize($meta->meta_value);
+		return $meta->meta_value;
+	}
+	
+	/**
+	 * Get number of group duplicates given field name. The function returns 1
+ 	 * if there are no duplicates (just he original group), 2 if there is one
+ 	 * duplicate and so on.
+	 *
+	 * @param integer $postId post id
+	 * @param integer $fieldID the name of any field in the group
+	 * @return number of groups 
+	 */
+	function GetFieldGroupDuplicates($postId, $fieldName){
+		global $wpdb;
+		return $wpdb->get_var("SELECT count(DISTINCT group_count) FROM " . RC_CWP_TABLE_POST_META . 
+						" WHERE field_name = '$fieldName' AND post_id = $postId");
+		
+	}
+
+	/**
+	 * Get number of group duplicates given field name. The function returns 1
+ 	 * if there are no duplicates (just he original group), 2 if there is one
+ 	 * duplicate and so on.
+	 *
+	 * @param integer $postId post id
+	 * @param integer $fieldID the name of any field in the group
+	 * @return number of groups 
+	 */
+	function GetFieldDuplicates($postId, $fieldName, $groupIndex){
+		global $wpdb;
+		return $wpdb->get_var("SELECT count(DISTINCT field_count) FROM " . RC_CWP_TABLE_POST_META . 
+						" WHERE field_name = '$fieldName' AND post_id = $postId AND group_count = $groupIndex");
+		
+	}
+	
+	/**
+	 * Retrieves the id of a custom field given field name for the current post.
+	 *
+	 * @param string $customFieldName
+	 * @return custom field id
+	 */
+	function GetIDByName($customFieldName)
+	{
+		global $wpdb, $post;
+		
+		// Get Panel ID
+		$customWritePanelId = get_post_meta($post->ID, RC_CWP_POST_WRITE_PANEL_ID_META_KEY, true);
+		if (empty($customWritePanelId)) return false;
+		
+		$customFieldId = $wpdb->get_var("SELECT cf.id FROM ". RC_CWP_TABLE_GROUP_FIELDS . " cf" .
+										" WHERE cf.name = '$customFieldName' AND ".
+										" cf.group_id in (SELECT mg.id FROM ". RC_CWP_TABLE_PANEL_GROUPS . " mg ".
+														"  WHERE mg.panel_id = $customWritePanelId)");
+		return $customFieldId;
+	}
+	
+	
+	/**
+	 * @access private 
+	 */
+	function GetDefaultCustomFieldType()
+	{
+		return 'Textbox';
+	}
+	
+
+	/**
+	 * Updates the properties of a custom field.
+	 *
+	 * @param integer $customFieldId the id of the field to be updated
+	 * @param string $name the name of the field, the name is used to uniquely identify the field
+	 * 							when retrieving its value.
+	 * @param string $label the label of the field, the label is displayed beside the field
+	 * 							in Write tab. 
+	 * @param integer $order the order of the field when it is displayed in 
+	 * 							the Write tab.
+	 * @param integer $required_field whether this field is a required field. Required fields
+	 * 							doesn't allow users to save a post if they are null. 
+	 * @param integer $type the type of the field. Use $FIELD_TYPES defined in RCCWP_Constant.php
+	 * @param array $options array of strings that represent the list of the field if
+	 * 							its type is list.
+	 * @param array $default_value array of strings that represent default value(s) of
+	 * 							of the field if	its type is list.
+	 * @param array $properties an array containing extra properties of the field.
+	 */
+
+	function Update($customFieldId, $name, $label, $order = 1, $required_field = 0, $type, $options = null, $default_value = null, $properties = null, $duplicate)
+	{
+		global $wpdb;
+		
+		$oldCustomField = RCCWP_CustomField::Get($customFieldId);
+		
+		if ($oldCustomField->name != $name)
+		{
+			$sql = sprintf(
+				"UPDATE $wpdb->postmeta" .
+				" SET meta_key = %s" .
+				" WHERE meta_key = %s",
+				RC_Format::TextToSql($name),
+				RC_Format::TextToSql($oldCustomField->name)
+				);
+			
+			$wpdb->query($sql);
+		}
+		
+		$sql = sprintf(
+			"UPDATE " . RC_CWP_TABLE_GROUP_FIELDS .
+			" SET name = %s" .
+			" , description = %s" .
+			" , display_order = %d" .
+			" , required_field = %d" .
+			" , type = %d" .
+			" , CSS = '%s'" .
+			" , duplicate = %d" .
+			" WHERE id = %d",
+			RC_Format::TextToSql($name),
+			RC_Format::TextToSql($label),
+			$order,
+			$required_field,
+			$type,
+			$_POST['custom-field-css'],
+			$duplicate,
+			$customFieldId
+			);
+		$wpdb->query($sql);
+
+
+		$field_type = RCCWP_CustomField::GetCustomFieldTypes($type);
+		if ($field_type->has_options == "true")
+		{
+			if (!is_array($options)) {
+				$options = stripslashes($options);
+				$options = explode("\n", $options);
+			}
+			array_walk($options, array(RC_Format, TrimArrayValues));
+			$options = addslashes(serialize($options));
+			
+			if (!is_array($default_value)) {
+				$default_value = stripslashes($default_value);
+				$default_value = explode("\n", $default_value);
+			}
+			array_walk($default_value, array(RC_Format, TrimArrayValues));
+			$default_value = addslashes(serialize($default_value));
+			
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" (custom_field_id, options, default_option) values (%d, %s, %s)" . 
+				" ON DUPLICATE KEY UPDATE options = %s, default_option = %s",
+				$customFieldId,
+				RC_Format::TextToSql($options),
+				RC_Format::TextToSql($default_value),
+				RC_Format::TextToSql($options),
+				RC_Format::TextToSql($default_value)
+				);	
+			$wpdb->query($sql);	
+		}
+		else
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);
+			$wpdb->query($sql);	
+		}
+		
+		if ($field_type->has_properties == "true")
+		{
+			$sql = sprintf(
+				"INSERT INTO " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" (custom_field_id, properties) values (%d, %s)" .
+				" ON DUPLICATE KEY UPDATE properties = %s",
+				$customFieldId,
+				RC_Format::TextToSql(serialize($properties)),
+				RC_Format::TextToSql(serialize($properties))
+				);	
+			$wpdb->query($sql);	
+		}
+		else
+		{
+			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES .
+				" WHERE custom_field_id = %d",
+				$customFieldId
+				);
+			$wpdb->query($sql);	
+		}
+	}
+}
+?>
Index: /afridex/plugins/Flutter/ajax-list-field-names.php
===================================================================
--- /afridex/plugins/Flutter/ajax-list-field-names.php (revision 21)
+++ /afridex/plugins/Flutter/ajax-list-field-names.php (revision 21)
@@ -0,0 +1,27 @@
+<?
+
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can(FLUTTER_CAPABILITY_PANELS)))
+	die("Athentication failed!");
+
+if(isset($_GET['getFieldsByLetters']) && isset($_GET['letters'])){
+	$letters = $_GET['letters'];
+	$letters = preg_replace("/[^a-z0-9 ]/si","",$letters);
+	
+	// Search for similar fields
+	global $wpdb;
+	require_once("RCCWP_CustomField.php");
+	$sql = "SELECT id, group_id FROM " . RC_CWP_TABLE_GROUP_FIELDS .
+		" WHERE name like '%$letters%' "; 
+	$results =$wpdb->get_results($sql);
+	
+	foreach($results as $result){
+		$fieldGroup = RCCWP_CustomGroup::Get($result->group_id);
+		if ($_GET['panel_id'] != $fieldGroup->panel_id){
+			$currentField = RCCWP_CustomField::Get($result->id);
+			$fieldDescription = "<b>{$currentField->description}</b> <br />&nbsp;&nbsp; (Type: {$currentField->type}, Name: {$currentField->name})";
+			echo $result->id."###".$fieldDescription."|";
+		}
+	}
+}
+?>
Index: /afridex/plugins/Flutter/canvas-install.php
===================================================================
--- /afridex/plugins/Flutter/canvas-install.php (revision 21)
+++ /afridex/plugins/Flutter/canvas-install.php (revision 21)
@@ -0,0 +1,183 @@
+<?php
+
+/*
+
+___Canvas Installation Scripts__________________________________
+
+Creates the necessary SQL tables for a clean Canvas install.
+
+________________________________________________________________
+
+*/
+
+
+function canvas_clean_install() {
+    global $wpdb, $canvas;
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->main."");
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->variables."");
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->options."");
+	canvas_install();
+	return true;
+}
+
+function canvas_clean_deactivate() {
+    	global $wpdb, $canvas;
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->main."");
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->duplicates."");
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->variables."");
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->options."");
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->zone_options."");
+	$wpdb->query("DROP TABLE IF EXISTS ".$canvas->ink."");
+
+	return true;
+}
+
+
+
+function canvas_create_db() {
+	global $wpdb, $canvas;
+
+	${$canvas->main} = "CREATE TABLE ".$canvas->main." (
+	        block_id INT NOT NULL AUTO_INCREMENT,
+	        PRIMARY KEY(block_id),
+   			module_id INT NOT NULL,
+			type tinytext NOT NULL,
+   			zone tinytext NOT NULL,
+   			position int(6) NOT NULL,
+   			author tinytext NOT NULL,
+   			description text NOT NULL,
+   			block_group tinytext NOT NULL,
+   			uri text NOT NULL,
+   			path text NOT NULL,
+   			theme tinytext NOT NULL,
+   			ubi text NOT NULL,
+			template_name text NOT NULL,
+			template_size text NOT NULL,
+			duplicate_id INT NOT NULL,
+			page text NOT NULL
+			);";
+
+	${$canvas->duplicates} = "CREATE TABLE ".$canvas->duplicates." (
+	        duplicate_id INT NOT NULL AUTO_INCREMENT,
+	        PRIMARY KEY(duplicate_id),
+   			module_id INT NOT NULL,
+			duplicate_name text NOT NULL
+			);";
+
+
+	${$canvas->variables} = "CREATE TABLE ".$canvas->variables." (
+   			variable_id INT NOT NULL AUTO_INCREMENT,
+   			PRIMARY KEY(variable_id),
+   			variable_name text NOT NULL,
+   			parent INT NOT NULL,
+   			type text NOT NULL,
+   			value text NOT NULL,
+   			default_value text NOT NULL,
+   			description text NOT NULL
+			);";
+
+	${$canvas->options} = "CREATE TABLE ".$canvas->options." (
+   			option_id INT NOT NULL AUTO_INCREMENT,
+   			PRIMARY KEY(option_id),
+   			var_id INT NOT NULL,
+   			option_text text NOT NULL,
+   			option_value text NOT NULL,
+			option_params text NOT NULL
+			);";
+
+	${$canvas->zone_options} = "CREATE TABLE ".$canvas->zone_options." (
+   			option_id INT NOT NULL AUTO_INCREMENT,
+   			PRIMARY KEY(option_id),
+   			zone text NOT NULL,
+   			option_name text NOT NULL,
+   			value text NOT NULL,
+   			theme text NOT NULL
+			);";
+
+	${$canvas->ink} = "CREATE TABLE ".$canvas->ink." (
+	        element_id INT NOT NULL AUTO_INCREMENT,
+	        PRIMARY KEY(element_id),
+   			element text NOT NULL,
+   			theme text NOT NULL,
+   			color text NOT NULL,
+   			background text NOT NULL,
+   			border text NOT NULL,
+   			font_family text NOT NULL,
+   			font_size text NOT NULL,
+   			font_style text NOT NULL,
+   			other text NOT NULL
+			);";
+
+	// try to get around
+	// these includes like http://trac.mu.wordpress.org/ticket/384 
+	// and http://www.quirm.net/punbb/viewtopic.php?pid=832#p832
+
+	if (file_exists(ABSPATH . 'wp-includes/pluggable.php')) {
+		require_once(ABSPATH . 'wp-includes/pluggable.php');
+	} else {
+		require_once(ABSPATH . 'wp-includes/pluggable-functions.php');
+	}
+	require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
+	
+	foreach($canvas as $table)
+		dbDelta(${$table});
+	
+	return;
+}
+
+function canvas_install($DBChanged=false) {
+    	global $wpdb, $canvas; //, $installed, $canvas_external_plugins;
+	
+     	if(!$wpdb->get_var("SHOW TABLES LIKE '".$canvas->main."'") == $canvas->main ||
+			$DBChanged) {
+		canvas_create_db();
+		update_option('RC_CWP_BLOG_DB_VERSION', RC_CWP_DB_VERSION);
+		ink_clean_install();
+		//canvas_check_theme();
+		
+		
+		update_option('canvas_auto_publish','false'); // for future releases
+
+		/*if(file_exists(get_template_directory().'/canvas.php') && !$wpdb->get_results("SELECT block_id FROM ".$canvas->main." WHERE theme = '".get_option('template')."'")) {
+			$position = 1;
+			$canvas_dir = CANVASPATH.'/modules/';
+			list($plugins, $position) = canvas_import_plugins($position, $canvas_dir);
+		} elseif(file_exists(get_template_directory().'/canvas.php')) {
+			canvas_reload();
+		}*/
+
+		
+
+	}
+	
+	return true;
+}
+
+
+function canvas_reload() {
+	global $wpdb, $canvas, $canvas_external_plugins;
+	$current_blocks = $wpdb->get_results("SELECT block_id, path FROM ".$canvas->main." WHERE theme = '".get_option('template')."' AND type = 'block'");
+	$current_plugins = $wpdb->get_results("SELECT block_id, path FROM ".$canvas->main." WHERE theme = '".get_option('template')."' AND type = 'plugin'");
+	$positions = $wpdb->get_var("SELECT MAX(position) FROM ".$canvas->main." WHERE theme = '".get_option('template')."' AND zone = 'shelf'");
+	if($positions) $position = $positions->position;
+		else $position = 1;
+	$block_count = 0; $plugin_count = 0;
+	foreach($current_blocks as $block) {
+		$current_block_paths[] = $block->path;
+	}
+	foreach($current_plugins as $plugin) {
+		$current_plugin_paths[] = $plugin->path;
+	}
+
+	// Search for modules
+	$dir = CANVASPATH.'/modules/';
+	canvas_import_plugins($position, $dir);
+		
+
+	if($block_count > 0 || $plugin_count > 0) $message = '<p>Installed '.$block_count.' blocks and '.$plugin_count.' plugins.</p>';
+		else $message = '<p>No new blocks or plugins found.</p>';
+
+	return $message;
+}
+
+?>
Index: /afridex/plugins/Flutter/lightbox.css
===================================================================
--- /afridex/plugins/Flutter/lightbox.css (revision 21)
+++ /afridex/plugins/Flutter/lightbox.css (revision 21)
@@ -0,0 +1,58 @@
+/* - - - - - - - - - - - - - - - - - - - - -
+
+Title : Lightbox CSS
+Author : Kevin Hale
+URL : http://particletree.com/features/lightbox-gone-wild/
+
+Created : January 13, 2006
+Modified : February 1, 2006
+
+- - - - - - - - - - - - - - - - - - - - - */
+
+#lightbox{
+	display: none;
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	z-index: 9999;
+	width: 500px;
+	height: 400px;
+	margin: -220px 0 0 -250px;
+	border: 2px solid #000;
+	background-color: #FFF;
+/*	background: #FDFCE9; */
+	text-align: left;
+}
+#lightbox[id]{
+	position: fixed;
+}
+
+#overlay{
+	display: none;
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	z-index: 5000;
+	background-color: #000;
+	-moz-opacity: 0.8;
+	opacity: .80;
+	filter: alpha(opacity=80);
+}
+#overlay[id]{
+	position: fixed;
+}
+
+#lightbox.done #lbLoadMessage{
+	display: none;
+}
+#lightbox.done #lbContent{
+	display: block;
+}
+#lightbox.loading #lbContent{
+	display: none;
+}
+#lightbox.loading #lbLoadMessage{
+	display: block;
+}
Index: /afridex/plugins/Flutter/RCCWP_upload.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_upload.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_upload.php (revision 21)
@@ -0,0 +1,193 @@
+<?php
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can('edit_posts')))
+	die("Athentication failed!");
+?>
+
+<html>
+<head>
+
+<?php
+
+if (isset($_POST['fileframe'])) 
+{
+	$operationSuccess = "false";
+	// A file is uploaded
+	if (isset($_FILES['file']) && (!empty($_FILES['file']['tmp_name'])))  // file was send from browser
+	{
+		
+		if ($_FILES['file']['error'] == UPLOAD_ERR_OK)  // no error
+		{
+			$special_chars = array (' ','`','"','\'','\\','/'," ","#","$","%","^","&","*","!","~","â","\"","â","'","=","?","/","[","]","(",")","|","<",">",";","\\",",");
+			$filename = str_replace($special_chars,'',$_FILES['file']['name']);
+			$filename = time() . $filename;
+			@move_uploaded_file( $_FILES['file']['tmp_name'], dirname(__FILE__) . '/files_flutter/' . $filename );
+			@chmod(dirname(__FILE__) . '/files_flutter/' . $filename, 0644);
+
+// 			$result_msg = 'The file '.$_FILES['file']['name'].' was uploaded successfuly. Please remember to click the save button.';
+			$result_msg = "<font color=\"green\"><b>Successful upload!</b></font>" ;
+			$operationSuccess = "true";
+		}
+		elseif ($_FILES['file']['error'] == UPLOAD_ERR_INI_SIZE)
+			$result_msg = 'The uploaded file exceeds the maximum upload limit';
+		else 
+// 			$result_msg = 'The upload failed';
+			$result_msg = "<font color=\"red\"><b>Upload Unsuccessful!</b></font>";
+	
+	}
+
+	// If operation is success, make sure the file was created properly
+	if ($operationSuccess == "true"){
+		if ($fp_check_file = @fopen(dirname(__FILE__) . '/files_flutter/' . $filename, 'rb')) {
+			fclose($fp_check_file);
+		}
+		else{
+			$operationSuccess = "false";
+			$result_msg = "Failed to upload the file!";
+		}
+		
+	}
+
+
+	
+
+?>
+
+	<script language="javascript">
+
+		// The code that runs after the file is uploaded
+    	var par = window.parent.document;
+		var iframe = par.getElementById('upload_internal_iframe_<?php echo $_POST["input_name"]?>');
+		par.getElementById('upload_progress_<?php echo $_POST["input_name"]?>').innerHTML = '<?php echo $result_msg?>';
+		iframe.style.display="";
+
+		if (<?php echo $operationSuccess?>){
+			par.getElementById("<?php echo $_POST["input_name"]?>").value = "<?php echo $filename?>";
+			
+			//Set image
+			<?php
+				$newImagePath = FLUTTER_URI.'phpThumb.php?&w=150&h=120&src='.urlencode(FLUTTER_URI.'files_flutter/'.$filename);
+				include_once("RCCWP_WritePostPage.php") ;
+				$edit_anchor = RCCWP_WritePostPage::snipshot_anchor($newImagePath) ;
+				if (isset($_POST['imageThumbID'])){ 
+			?>
+				if( par.getElementById('<?php echo $_POST['imageThumbID']; ?>') )
+				{
+					par.getElementById('<?php echo $_POST['imageThumbID']; ?>').src = "<?php echo $newImagePath;?>";
+					var s = "<a class='thickbox' href='#impossible_location' onclick=\"call_thickbox('<?php echo $edit_anchor ?>')\" >";
+					var e = "<strong onclick=prepareUpdatePhoto('<?php echo $_POST['input_name'] ?>')>Edit</strong> </a>" ;
+					par.getElementById("photo_edit_link_<?php echo $_POST['input_name'] ?>").innerHTML = s+e ;
+				}
+			<?php } ?>
+		}
+		
+		
+		
+	</script>
+
+
+<?php
+	//exit()
+
+}
+?>
+
+<script language="javascript">
+function upload(){
+	// hide old iframe
+    	var par = window.parent.document;
+
+	var iframe = par.getElementById('upload_internal_iframe_<?php echo $_GET["input_name"]?>');
+	iframe.style.display="none";//height = '0px';
+		
+	// update progress
+	par.getElementById('upload_progress_<?php echo $_GET["input_name"]?>').style.visibility = "visible";
+	par.getElementById('upload_progress_<?php echo $_GET["input_name"]?>').style.height = "auto";
+	par.getElementById('upload_progress_<?php echo $_GET["input_name"]?>').innerHTML = "Transferring ";
+
+
+	setTimeout("transferring(0)",1000);
+	
+	// send 
+	document.iform.submit();
+	
+	//iframe.style.visibility = 'hidden';
+	//par.getElementById('upload_progress').style.visibility = "hidden";
+}
+
+function transferring(dots){
+	
+	newString = "Transferring ";
+	for (var x=1; x<=dots; x++) {
+        	newString = newString + ".";
+    	} 
+	
+	var par = window.parent.document;
+
+	// update progress
+	if (par.getElementById('upload_progress_<?php echo $_GET["input_name"]?>').innerHTML.substring(0,5) != "Trans") return;
+	par.getElementById('upload_progress_<?php echo $_GET["input_name"]?>').innerHTML = newString;
+	if (dots == 4) dots = 0; else dots = dots + 1;
+	setTimeout("transferring("+dots+")",1000) ;
+	
+}
+
+</script>
+<style>
+body {
+	padding: 0px;
+	margin: 0px;
+	vertical-align:top;
+}
+</style>
+<link rel='stylesheet' href='<?php echo get_bloginfo('wpurl');?>/wp-admin/css/global.css' type='text/css' />
+<link rel='stylesheet' href='<?php echo get_bloginfo('wpurl');?>/wp-admin/wp-admin.css' type='text/css' />
+<link rel='stylesheet' href='<?php echo get_bloginfo('wpurl');?>/wp-admin/css/colors-fresh.css' type='text/css' />
+<style>
+body {
+	background: transparent;
+}
+</style>
+
+
+</head>
+<body>
+
+
+<form name="iform" action="" method="post" enctype="multipart/form-data">
+
+	<input type="hidden" name="fileframe" value="true" />
+	
+	<?php	
+		if (isset($_GET['imageThumbID'])) {
+			echo '<input type="hidden" name="imageThumbID" value="'.$_GET['imageThumbID'].'" />';
+		}
+
+		if (isset($_GET['inputSize'])){
+			$inputSize = $_GET['inputSize'];
+		}
+	?>
+	
+
+	<table border="0" style="width:100%">
+
+		<tr>
+			<?php if($_GET['canvas']!=0){ ?>
+				<td width=17%><label for="file">File:</label><br />
+				<input id="file" type="file" name="file" onchange="upload()" size="<?php echo $inputSize; ?>"/></td>
+			<?php }else{ ?>
+				<td width=17%><label for="file">File:</label></td>
+				<td><input id="file" type="file" name="file" onchange="upload()" size="<?php echo $inputSize; ?>"/></td>
+			<?php } ?>
+		</tr>
+
+	</table>
+
+	
+	<input type="hidden" name="fileframe" value="true" />
+	<input type="hidden" name="imgnum" />
+	<input type="hidden" name="input_name" value="<?php echo $_GET["input_name"]?>" />
+	<input type="hidden" name="type" value="<?php echo $_GET["type"]?>" />
+</form>
+</body>
+</html>
Index: /afridex/plugins/Flutter/get-custom.php
===================================================================
--- /afridex/plugins/Flutter/get-custom.php (revision 21)
+++ /afridex/plugins/Flutter/get-custom.php (revision 21)
@@ -0,0 +1,290 @@
+<?php
+/*
+
+=>> Based on coffee2code: Visit the plugin's homepage for more information and latest updates  <<=
+                http://www.coffee2code.com/wp-plugins/
+
+Copyright (c) 2004-2005 by Scott Reilly (aka coffee2code)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, 
+modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+
+require_once 'RCCWP_Constant.php';
+
+/**
+ * Get number of group duplicates given field name. The function returns 1
+ * if there are no duplicates (just the original group), 2 if there is one
+ * duplicate and so on.
+ *
+ * @param string $fieldName the name of any field in the group
+ * @return number of group duplicates 
+ */
+function getGroupDuplicates ($fieldName) {
+	require_once("RCCWP_CustomField.php");
+	global $post;
+	return RCCWP_CustomField::GetFieldGroupDuplicates($post->ID, $fieldName);
+}
+
+/**
+ * Get number of field duplicates given field name and group duplicate index.
+ * The function returns 1 if there are no duplicates (just the original field), 
+ * 2 if there is one duplicate and so on.
+ *
+ * @param string $fieldName
+ * @param integer $groupIndex
+ * @return number of field duplicates
+ */
+function getFieldDuplicates ($fieldName, $groupIndex) {
+	require_once("RCCWP_CustomField.php");
+	global $post;
+	return RCCWP_CustomField::GetFieldDuplicates($post->ID, $fieldName, $groupIndex);
+}
+
+/**
+ * Get the value of an input field.
+ *
+ * @param string $fieldName
+ * @param integer $groupIndex
+ * @param integer $fieldIndex
+ * @param boolean $readyForEIP if true and the field type is textbox or
+ * 				multiline textbox, the resulting value will be wrapped
+ * 				in a div that is ready for EIP. The default value is true
+ * @return a string or array based on field type
+ */
+function get ($fieldName, $groupIndex=1, $fieldIndex=1, $readyForEIP=true) {
+	require_once("RCCWP_CustomField.php");
+	global $wpdb, $post, $FIELD_TYPES;
+	
+	$fieldID = RCCWP_CustomField::GetIDByName($fieldName);
+	$fieldObject = GetFieldInfo($fieldID);
+	$fieldType = $wpdb->get_var("SELECT type FROM ".RC_CWP_TABLE_GROUP_FIELDS." WHERE id='".$fieldID."'");
+	$single = true;
+	switch($fieldType){
+		case $FIELD_TYPES["checkbox_list"]:
+		case $FIELD_TYPES["listbox"]:
+			$single = false;
+			break;
+	} 
+	
+	$fieldValues = (array) RCCWP_CustomField::GetCustomFieldValues($single, $post->ID, $fieldName, $groupIndex, $fieldIndex);
+	$fieldMetaID = RCCWP_CustomField::GetMetaID($post->ID, $fieldName, $groupIndex, $fieldIndex);
+	
+	$results = array();
+	
+	foreach($fieldValues as $fieldValue){
+	
+		switch($fieldType){
+			case $FIELD_TYPES["audio"]:
+			case $FIELD_TYPES["file"]:
+			case $FIELD_TYPES["image"]:
+				if ($fieldValue != "") $fieldValue = FLUTTER_URI.'files_flutter/'.$fieldValue;
+				break;
+	
+			case $FIELD_TYPES["checkbox"]: 		
+				if ($fieldValue == 'true')  $fieldValue = true; else $fieldValue = false; 
+				break;
+	
+			case $FIELD_TYPES["date"]: 
+				$fieldValue = date($fieldObject->properties['format'],strtotime($fieldValue)); 
+				break;
+		}
+		
+		array_push($results, $fieldValue); 
+	}
+	
+	// Return array or single value based on field
+	switch($fieldType){
+		
+		case $FIELD_TYPES["checkbox_list"]:
+		case $FIELD_TYPES["listbox"]:
+			return $results;
+		 	break;
+	}
+	
+	// Prepare fields for EIP
+	if ($readyForEIP){
+		switch($fieldType){
+			case $FIELD_TYPES["textbox"]:
+				$results[0] = "<div class='".EIP_textbox($fieldMetaID)."' >".$results[0]."</div>";
+			 	break;
+			 	
+			case $FIELD_TYPES["multiline_textbox"]:
+				$results[0] = "<div class='".EIP_mulittextbox($fieldMetaID)."' >".$results[0]."</div>";
+			 	break;
+		}
+		
+	}
+
+	if (count($results) == 0 )
+		return "";
+	else
+		return $results[0];
+
+}
+
+// Get Image. 
+function get_image ($fieldName, $groupIndex=1, $fieldIndex=1) {
+	require_once("RCCWP_CustomField.php");
+	global $wpdb, $post, $FIELD_TYPES;
+	
+	$fieldID = RCCWP_CustomField::GetIDByName($fieldName);
+	$fieldObject = GetFieldInfo($fieldID);
+	$fieldType = $wpdb->get_var("SELECT type FROM ".RC_CWP_TABLE_GROUP_FIELDS." WHERE id='".$fieldID."'");
+	$single = true;
+	switch($fieldType){
+		case $FIELD_TYPES["checkbox_list"]:
+		case $FIELD_TYPES["listbox"]:
+			$single = false;
+			break;
+	} 
+	
+	$fieldValues = (array) RCCWP_CustomField::GetCustomFieldValues($single, $post->ID, $fieldName, $groupIndex, $fieldIndex);
+	 
+	if(!empty($fieldValues))
+		$fieldValue = $fieldValues[0];
+	else 
+		return "";
+	
+	if (substr($fieldObject->properties['params'], 0, 1) == "?"){
+		$fieldObject->properties['params'] = substr($fieldObject->properties['params'], 1);
+	}
+
+
+	if (empty($fieldObject->properties['params']) && (FALSE == strstr($fieldValue, "&"))){
+		$fieldValue = FLUTTER_URI.'files_flutter/'.$fieldValue; 
+	}
+	else{
+		$path = FLUTTER_URI.'phpThumb.php?src=files_flutter/';
+		$fieldValue = $path.$fieldValue.$fieldObject->properties['params'];
+	}
+
+	$cssClass = $wpdb->get_results("SELECT CSS FROM ".RC_CWP_TABLE_GROUP_FIELDS." WHERE name='".$field."'");
+	if (empty($cssClass[0]->CSS)){
+		$finalString = stripslashes(trim("\<img src=\'".$fieldValue."\' /\>"));
+	}
+	else{
+		$finalString = stripslashes(trim("\<img src=\'".$fieldValue."\' class=\"".$cssClass[0]->CSS."\" \/\>"));
+	}
+	return $finalString;
+}
+
+// Get Audio. 
+function get_audio ($fieldName, $groupIndex=1, $fieldIndex=1) {
+	require_once("RCCWP_CustomField.php");
+	global $wpdb, $post, $FIELD_TYPES;
+	
+	$fieldID = RCCWP_CustomField::GetIDByName($fieldName);
+	$fieldObject = GetFieldInfo($fieldID);
+	$fieldType = $wpdb->get_var("SELECT type FROM ".RC_CWP_TABLE_GROUP_FIELDS." WHERE id='".$fieldID."'");
+	$single = true;
+	switch($fieldType){
+		case $FIELD_TYPES["checkbox_list"]:
+		case $FIELD_TYPES["listbox"]:
+			$single = false;
+			break;
+	} 
+	
+	$fieldValues = (array) RCCWP_CustomField::GetCustomFieldValues($single, $post->ID, $fieldName, $groupIndex, $fieldIndex);
+	
+	if(!empty($fieldValues))
+		$fieldValue = $fieldValues[0];
+	else 
+		return "";
+		
+	$path = FLUTTER_URI.'files_flutter/';
+	$fieldValue = $path.$fieldValue;
+	$finalString = stripslashes(trim("\<div style=\'padding-top:3px;\'\>\<object classid=\'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\' codebase='\http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0\' width=\'95%\' height=\'20\' wmode=\'transparent\' \>\<param name=\'movie\' value=\'".FLUTTER_URI."js/singlemp3player.swf?file=".urlencode($fieldValue)."\' wmode=\'transparent\' /\>\<param name=\'quality\' value=\'high\' wmode=\'transparent\' /\>\<embed src=\'".FLUTTER_URI."js/singlemp3player.swf?file=".urlencode($fieldValue)."' width=\'50\%\' height=\'20\' quality=\'high\' pluginspage=\'http://www.macromedia.com/go/getflashplayer\' type=\'application/x-shockwave-flash\' wmode=\'transparent\' \>\</embed\>\</object\>\</div\>"));
+	return $finalString;
+}
+
+// This works outside "the loop"
+function c2c_get_recent_custom ($field, $before='', $after='', $none='', $between=', ', $before_last='', $limit=1, $unique=false, $order='DESC', $include_static=true, $show_pass_post=false) {
+	global $wpdb;
+	if (empty($between)) $limit = 1;
+	if ($order != 'ASC') $order = 'DESC';
+	$now = current_time('mysql');
+
+	$sql = "SELECT ";
+	if ($unique) $sql .= "DISTINCT ";
+	$sql .= "meta_value FROM $wpdb->posts AS posts, $wpdb->postmeta AS postmeta ";
+	$sql .= "WHERE posts.ID = postmeta.post_id AND postmeta.meta_key = '$field' ";
+	$sql .= "AND ( posts.post_status = 'publish' ";
+	if ($include_static) $sql .= " OR posts.post_status = 'static' ";
+	$sql .= " ) AND posts.post_date < '$now' ";
+	if (!$show_pass_post) $sql .= "AND posts.post_password = '' ";
+	$sql .= "AND postmeta.meta_value != '' ";
+	$sql .= "ORDER BY posts.post_date $order LIMIT $limit";
+	$results = array(); $values = array();
+	$results = $wpdb->get_results($sql);
+	if (!empty($results))
+		foreach ($results as $result) { $values[] = $result->meta_value; };
+	return c2c__format_custom($field, $values, $before, $after, $none, $between, $before_last);
+} //end c2c_get_recent_custom()
+
+/* Helper function */
+function c2c__format_custom ($field, $meta_values, $before='', $after='', $none='', $between='', $before_last='') {
+	$values = array();
+	if (empty($between)) $meta_values = array_slice($meta_values,0,1);
+	if (!empty($meta_values))
+		foreach ($meta_values as $meta) {
+			$meta = apply_filters("the_meta_$field", $meta);
+			$values[] = apply_filters('the_meta', $meta);
+		}
+
+	if (empty($values)) $value = '';
+	else {
+		$values = array_map('trim', $values);
+		if (empty($before_last)) $value = implode($values, $between);
+		else {
+			switch ($size = sizeof($values)) {
+				case 1:
+					$value = $values[0];
+					break;
+				case 2:
+					$value = $values[0] . $before_last . $values[1];
+					break;
+				default:
+					$value = implode(array_slice($values,0,$size-1), $between) . $before_last . $values[$size-1];
+			}
+		}
+	}
+	if (empty($value)) {
+		if (empty($none)) return;
+		$value = $none;
+	}
+	return $before . $value . $after;
+} //end c2c__format_custom()
+
+function GetFieldInfo($customFieldId)
+	{
+		global $wpdb;
+		$sql = "SELECT properties FROM " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES  .
+			" WHERE custom_field_id = " . $customFieldId;
+		$results = $wpdb->get_row($sql);
+		//$results->options = unserialize($results->options);
+		$results->properties = unserialize($results->properties);
+		//$results->default_value = unserialize($results->default_value);
+		return $results;
+	}
+
+// Some filters you may wish to perform: (these are filters typically done to 'the_content' (post content))
+//add_filter('the_meta', 'convert_chars');
+//add_filter('the_meta', 'wptexturize');
+
+// Other optional filters (you would need to obtain and activate these plugins before trying to use these)
+//add_filter('the_meta', 'c2c_hyperlink_urls', 9);
+//add_filter('the_meta', 'text_replace', 2);
+//add_filter('the_meta', 'textile', 6);
+
+?>
Index: /afridex/plugins/Flutter/phpThumb.config.php
===================================================================
--- /afridex/plugins/Flutter/phpThumb.config.php (revision 21)
+++ /afridex/plugins/Flutter/phpThumb.config.php (revision 21)
@@ -0,0 +1,256 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// See: phpthumb.readme.txt for usage instructions          //
+//      NOTE: THIS FILE HAS NO EFFECT IN OBJECT MODE!       //
+//            THIS CONFIG FILE ONLY APPLIES TO phpThumb.php //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+ob_start();
+if (!file_exists(dirname(__FILE__).'/phpthumb.functions.php') || !include_once(dirname(__FILE__).'/phpthumb.functions.php')) {
+	ob_end_flush();
+	die('failed to include_once(phpthumb.functions.php) - realpath="'.realpath(dirname(__FILE__).'/phpthumb.functions.php').'"');
+}
+ob_end_clean();
+
+// START USER CONFIGURATION SECTION:
+
+// * DocumentRoot configuration
+// phpThumb() depends on $_SERVER['DOCUMENT_ROOT'] to resolve path/filenames. This value is usually correct,
+// but has been known to be broken on some servers. This value allows you to override the default value.
+// Do not modify from the auto-detect default value unless you are having problems.
+//$PHPTHUMB_CONFIG['document_root'] = '/home/httpd/httpdocs';
+//$PHPTHUMB_CONFIG['document_root'] = 'c:\\webroot\\example.com\\www';
+//$PHPTHUMB_CONFIG['document_root'] = $_SERVER['DOCUMENT_ROOT'];
+//$PHPTHUMB_CONFIG['document_root'] = realpath((@$_SERVER['DOCUMENT_ROOT'] && file_exists(@$_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF'])) ? $_SERVER['DOCUMENT_ROOT'] : str_replace(dirname(@$_SERVER['PHP_SELF']), '', str_replace(DIRECTORY_SEPARATOR, '/', realpath('.'))));
+$PHPTHUMB_CONFIG['document_root'] = realpath((getenv('DOCUMENT_ROOT') && ereg('^'.preg_quote(realpath(getenv('DOCUMENT_ROOT'))), realpath(__FILE__))) ? getenv('DOCUMENT_ROOT') : str_replace(dirname(@$_SERVER['PHP_SELF']), '', str_replace(DIRECTORY_SEPARATOR, '/', dirname(__FILE__))));
+
+// * Cache directory configuration (choose only one of these - leave the other lines commented-out):
+// Note: this directory must be writable (usually chmod 777 is neccesary) for caching to work.
+// If the directory is not writable no error will be generated but caching will be disabled.
+$PHPTHUMB_CONFIG['cache_directory'] = dirname(__FILE__).'/cache/';                            // set the cache directory relative to the phpThumb() installation
+//$PHPTHUMB_CONFIG['cache_directory'] = $PHPTHUMB_CONFIG['document_root'].'/phpthumb/cache/'; // set the cache directory to an absolute directory for all source images
+//$PHPTHUMB_CONFIG['cache_directory'] = './cache/';                                           // set the cache directory relative to the source image - must start with '.' (will not work to cache URL- or database-sourced images, please use an absolute directory name)
+//$PHPTHUMB_CONFIG['cache_directory'] = null;                                                 // disable thumbnail caching (not recommended)
+//if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
+//	$PHPTHUMB_CONFIG['cache_directory'] = dirname(__FILE__).'/cache/'; // set the cache directory to an absolute directory for all source images
+//} else {
+//	$PHPTHUMB_CONFIG['cache_directory'] = '/tmp/persistent/phpthumb/cache/';
+//}
+
+$PHPTHUMB_CONFIG['cache_disable_warning'] = true; // If [cache_directory] is non-existant or not writable, and [cache_disable_warning] is false, an error image will be generated warning to either set the cache directory or disable the warning (to avoid people not knowing about the cache)
+
+$PHPTHUMB_CONFIG['cache_directory_depth'] = 4; // If this larger than zero, cache structure will be broken into a broad directory structure based on cache filename. For example "cache_src012345..." will be stored in "/0/01/012/0123/cache_src012345..." when (cache_directory_depth = 4)
+
+
+// * Cache culling: phpThumb can automatically limit the contents of the cache directory
+//   based on last-access date and/or number of files and/or total filesize.
+
+//$PHPTHUMB_CONFIG['cache_maxage'] = null;            // never delete cached thumbnails based on last-access time
+$PHPTHUMB_CONFIG['cache_maxage'] = 86400 * 30;        // delete cached thumbnails that haven't been accessed in more than [30 days] (value is maximum time since last access in seconds to avoid deletion)
+
+//$PHPTHUMB_CONFIG['cache_maxsize'] = null;           // never delete cached thumbnails based on byte size of cache directory
+$PHPTHUMB_CONFIG['cache_maxsize'] = 10 * 1024 * 1024; // delete least-recently-accessed cached thumbnails when more than [10MB] of cached files are present (value is maximum bytesize of all cached files)
+
+//$PHPTHUMB_CONFIG['cache_maxfiles'] = null;          // never delete cached thumbnails based on number of cached files
+$PHPTHUMB_CONFIG['cache_maxfiles'] = 200;             // delete least-recently-accessed cached thumbnails when more than [200] cached files are present (value is maximum number of cached files to keep)
+
+
+// * Source image cache configuration
+$PHPTHUMB_CONFIG['cache_source_enabled']   = false;                               // if true, source images obtained via HTTP are cached to $PHPTHUMB_CONFIG['cache_source_directory']
+$PHPTHUMB_CONFIG['cache_source_directory'] = dirname(__FILE__).'/cache/source/';  // set the cache directory for unprocessed source images
+
+// * cache source modification date configuration
+$PHPTHUMB_CONFIG['cache_source_filemtime_ignore_local']  = false; // if true, local source images will not be checked for modification date and cached image will be used if available, even if source image is changed or removed
+$PHPTHUMB_CONFIG['cache_source_filemtime_ignore_remote'] = true;  // if true, remote source images will not be checked for modification date and cached image will be used if available, even if source image is changed or removed. WARNING: cached performance MUCH slower if this is set to false.
+
+
+// * Simplified cache filename configuration
+// Instead of creating unique cache filenames for all parameter combinations, create "simple" cache files (eg: "pic_thumb.jpg")
+// If cache_default_only_suffix is non-empty, GETstring parameters (except 'src') are ignored and only $PHPTHUMB_DEFAULTS
+// parameters (set at the bottom of phpThumb.config.php) are used for processing.
+// The '*' character MUST be used to represent the source image name
+$PHPTHUMB_CONFIG['cache_default_only_suffix'] = '';           // cached in normal phpThumb manner
+//$PHPTHUMB_CONFIG['cache_default_only_suffix'] = '*_thumb';  // cache 'pic.jpg' becomes 'pic_thumb.jpg' (or 'pic_thumb.png' if PNG output is selected, etc)
+//$PHPTHUMB_CONFIG['cache_default_only_suffix'] = 'small-*';  // cache 'pic.jpg' becomes 'small-pic.jpg' (or 'small-pic.png' if PNG output is selected, etc)
+
+$PHPTHUMB_CONFIG['cache_prefix'] = 'phpThumb_cache_'.str_replace('www.', '', @$_SERVER['SERVER_NAME']);
+//$PHPTHUMB_CONFIG['cache_prefix'] = 'phpThumb_cache';                         // allow phpThumb to share 1 set of cached files even if accessed under different servername/domains on same server
+
+$PHPTHUMB_CONFIG['cache_force_passthru'] = true;  // if true, cached image data will always be passed to browser; if false, HTTP redirect will be used instead
+
+
+
+// * Temp directory configuration
+// phpThumb() may need to create temp files. Usually the system temp dir is writable and can be used.
+// Leave this value as NULL in most cases. If you get errors about "failed to open <filename> for writing"
+// you should change this to a full pathname to a directory you do have write access to.
+//$PHPTHUMB_CONFIG['temp_directory'] = null;                               // attempt to auto-detect
+//$PHPTHUMB_CONFIG['temp_directory'] = '/tmp/persistent/phpthumb/cache/';  // set to absolute path
+$PHPTHUMB_CONFIG['temp_directory'] = $PHPTHUMB_CONFIG['cache_directory'];  // set to same as cache directory
+
+
+// NOTE: "max_source_pixels" only affects GD-resized thumbnails. If you have ImageMagick
+//       installed it will bypass most of these limits
+// maximum number of pixels in source image to attempt to process entire image in GD mode.
+// If this is zero then no limit on source image dimensions.
+// If this is nonzero then this is the maximum number of pixels the source image
+// can have to be processed normally, otherwise the embedded EXIF thumbnail will
+// be used (if available) or an "image too large" notice will be displayed.
+// This is to be used for large source images (> 1600x1200) and low PHP memory
+// limits. If PHP runs out of memory the script will usually just die with no output.
+// To calculate this number, multiply the dimensions of the largest image
+// you can process with your memory limitation (e.g. 1600 * 1200 = 1920000)
+// As a general guideline, this number will be about 20% of your PHP memory
+// configuration, so 8M = 1,677,722; 16M = 3,355,443; 32M = 6,710,886; etc.
+if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.2', '>=') && !defined('memory_get_usage') && !@ini_get('memory_limit')) {
+	// memory_get_usage() will only be defined if your PHP is compiled with the --enable-memory-limit configuration option.
+	$PHPTHUMB_CONFIG['max_source_pixels'] = 0;         // no memory limit
+} else {
+	// calculate default max_source_pixels as 1/6 of memory limit configuration
+	$PHPTHUMB_CONFIG['max_source_pixels'] = round(max(intval(ini_get('memory_limit')), intval(get_cfg_var('memory_limit'))) * 1048576 / 6);
+	//$PHPTHUMB_CONFIG['max_source_pixels'] = 0;       // no memory limit
+	//$PHPTHUMB_CONFIG['max_source_pixels'] = 1920000; // allow 1600x1200 images (2Mpx), no larger (about 12MB memory required)
+	//$PHPTHUMB_CONFIG['max_source_pixels'] = 2795000; // 16MB memory limit
+	//$PHPTHUMB_CONFIG['max_source_pixels'] = 3871488; // allow 2272x1704 images (4Mpx), no larger (about 24MB memory required)
+}
+
+
+// ImageMagick configuration
+$PHPTHUMB_CONFIG['prefer_imagemagick']        = true;  // If true, use ImageMagick to resize thumbnails if possible, since it is usually faster than GD functions; if false only use ImageMagick if PHP memory limit is too low.
+$PHPTHUMB_CONFIG['imagemagick_use_thumbnail'] = true;  // If true, use ImageMagick's "-thumbnail" resizing parameter (if available) which removes extra non-image metadata (profiles, EXIF info, etc) resulting in much smaller filesize; if false, use "-resize" paramter which retains this info
+if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
+	// Windows: set absolute pathname
+	$PHPTHUMB_CONFIG['imagemagick_path'] = 'C:/ImageMagick/convert.exe';
+} else {
+	// *nix: set absolute pathname to "convert", or leave as null if "convert" is in the path (location detected with `which`)
+	//$PHPTHUMB_CONFIG['imagemagick_path'] = '/usr/local/bin/convert';
+	$PHPTHUMB_CONFIG['imagemagick_path'] = null;
+}
+
+
+
+// * Default output configuration:
+$PHPTHUMB_CONFIG['output_format']    = 'jpeg'; // default output format ('jpeg', 'png' or 'gif') - thumbnail will be output in this format (if available in your version of GD). This is always overridden by ?f=___ GETstring parameter
+$PHPTHUMB_CONFIG['output_maxwidth']  = 0;      // default maximum thumbnail width.  If this is zero then default width  is the width  of the source image. This is always overridden by ?w=___ GETstring parameter
+$PHPTHUMB_CONFIG['output_maxheight'] = 0;      // default maximum thumbnail height. If this is zero then default height is the height of the source image. This is always overridden by ?h=___ GETstring parameter
+$PHPTHUMB_CONFIG['output_interlace'] = true;   // if true: interlaced output for GIF/PNG, progressive output for JPEG; if false: non-interlaced for GIF/PNG, baseline for JPEG.
+
+// * Error message configuration
+$PHPTHUMB_CONFIG['error_image_width']           = 400;      // default width for error images
+$PHPTHUMB_CONFIG['error_image_height']          = 100;      // default height for error images
+$PHPTHUMB_CONFIG['error_message_image_default'] = '';       // Set this to the name of a generic error image (e.g. '/images/error.png') that you want displayed in place of any error message that may occur. This setting is overridden by the 'err' parameter, which does the same thing.
+$PHPTHUMB_CONFIG['error_bgcolor']               = 'CCCCFF'; // background color of error message images
+$PHPTHUMB_CONFIG['error_textcolor']             = 'FF0000'; // color of text in error messages
+$PHPTHUMB_CONFIG['error_fontsize']              = 1;        // size of text in error messages, from 1 (smallest) to 5 (largest)
+$PHPTHUMB_CONFIG['error_die_on_error']          = true;     // die with error message on any fatal error (recommended with standalone phpThumb.php)
+$PHPTHUMB_CONFIG['error_silent_die_on_error']   = false;    // simply die with no output of any kind on fatal errors (not recommended)
+$PHPTHUMB_CONFIG['error_die_on_source_failure'] = true;     // die with error message if source image cannot be processed by phpThumb() (usually because source image is corrupt in some way). If false the source image will be passed through unprocessed, if true (default) an error message will be displayed.
+
+// * Off-server Thumbnailing Configuration:
+$PHPTHUMB_CONFIG['nohotlink_enabled']           = false;                                    // If false will allow thumbnailing from any source domain
+$PHPTHUMB_CONFIG['nohotlink_valid_domains']     = array(@$_SERVER['HTTP_HOST']);            // This is the list of domains for which thumbnails are allowed to be created. Note: domain only, do not include port numbers. The default value of the current domain should be fine in most cases, but if neccesary you can add more domains in here, in the format "www.example.com"
+$PHPTHUMB_CONFIG['nohotlink_erase_image']       = true;                                     // if true thumbnail is covered up with $PHPTHUMB_CONFIG['nohotlink_fill_color'] before text is applied, if false text is written over top of thumbnail
+$PHPTHUMB_CONFIG['nohotlink_text_message']      = 'Off-server thumbnailing is not allowed'; // text of error message
+
+// * Off-server Linking Configuration:
+$PHPTHUMB_CONFIG['nooffsitelink_enabled']       = false;                                       // If false will allow thumbnails to be linked to from any domain, if true only domains listed below in 'nooffsitelink_valid_domains' will be allowed.
+$PHPTHUMB_CONFIG['nooffsitelink_valid_domains'] = array(@$_SERVER['HTTP_HOST']['www.freshoutmedia.com']);              // This is the list of domains for which thumbnails are allowed to be created. The default value of the current domain should be fine in most cases, but if neccesary you can add more domains in here, in the format 'www.example.com'
+$PHPTHUMB_CONFIG['nooffsitelink_require_refer'] = false;                                      // If false will allow standalone calls to phpThumb(). If true then only requests with a $_SERVER['HTTP_REFERER'] value in 'nooffsitelink_valid_domains' are allowed.
+$PHPTHUMB_CONFIG['nooffsitelink_erase_image']   = false;                                      // if true thumbnail is covered up with $PHPTHUMB_CONFIG['nohotlink_fill_color'] before text is applied, if false text is written over top of thumbnail
+$PHPTHUMB_CONFIG['nooffsitelink_watermark_src'] = '/demo/images/watermark.png';                // webroot-relative image to overlay on hotlinked images
+$PHPTHUMB_CONFIG['nooffsitelink_text_message']  = 'Image taken from '.@$_SERVER['HTTP_HOST']; // text of error message (used if [nooffsitelink_watermark_src] is not a valid image)
+
+
+// * Border & Background default colors
+$PHPTHUMB_CONFIG['border_hexcolor']     = '000000'; // Default border color - usual HTML-style hex color notation (overidden with 'bc' parameter)
+$PHPTHUMB_CONFIG['background_hexcolor'] = 'FFFFFF'; // Default background color when thumbnail aspect ratio does not match fixed-dimension box - usual HTML-style hex color notation (overridden with 'bg' parameter)
+
+// * Watermark configuration
+$PHPTHUMB_CONFIG['ttf_directory'] = dirname(__FILE__).'/fonts'; // Base directory for TTF font files
+//$PHPTHUMB_CONFIG['ttf_directory'] = 'c:/windows/fonts';
+
+
+// * MySQL configuration
+// You may want to pull data from a database rather than a physical file
+// If so, modify the $PHPTHUMB_CONFIG['mysql_query'] line to suit your database structure
+// Note: the data retrieved must be the actual binary data of the image, not a URL or filename
+
+$PHPTHUMB_CONFIG['mysql_query'] = '';
+//$PHPTHUMB_CONFIG['mysql_query'] = 'SELECT `picture` FROM `products` WHERE (`id` = \''.mysql_escape_string(@$_GET['id']).'\')';
+
+// These 4 values must be modified if $PHPTHUMB_CONFIG['mysql_query'] is not empty, but may be ignored if $PHPTHUMB_CONFIG['mysql_query'] is blank.
+$PHPTHUMB_CONFIG['mysql_hostname'] = 'localhost';
+$PHPTHUMB_CONFIG['mysql_username'] = '';
+$PHPTHUMB_CONFIG['mysql_password'] = '';
+$PHPTHUMB_CONFIG['mysql_database'] = '';
+
+
+// * Security configuration
+$PHPTHUMB_CONFIG['high_security_enabled']    = false;  // if enabled, requires 'high_security_password' set to at least 5 characters, and requires the use of phpThumbURL() function (at the bottom of phpThumb.config.php) to generate hashed URLs
+$PHPTHUMB_CONFIG['high_security_password']   = '';     // required if 'high_security_enabled' is true, must be at least 5 characters long
+$PHPTHUMB_CONFIG['disable_debug']            = false;  // prevent phpThumb from displaying any information about your system. If true, phpThumbDebug and error messages will be disabled
+$PHPTHUMB_CONFIG['allow_src_above_docroot']  = false;  // if true, allow src to be anywhere in filesystem; if false (default) only allow src within document_root
+$PHPTHUMB_CONFIG['allow_src_above_phpthumb'] = true;   // if true (default), allow src to be anywhere in filesystem; if false only allow src within sub-directory of phpThumb installation
+$PHPTHUMB_CONFIG['allow_parameter_file']     = false;  // if true, allow use of 'file' parameter; if false (default) the 'file' parameter is disabled/ignored
+$PHPTHUMB_CONFIG['allow_parameter_goto']     = false;  // if true, allow use of 'goto' parameter; if false (default) the 'goto' parameter is disabled/ignored
+
+
+// * HTTP UserAgent configuration
+//$PHPTHUMB_CONFIG['http_user_agent'] = '';                                                                                      // PHP default: none
+//$PHPTHUMB_CONFIG['http_user_agent'] = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)';                                    // Windows XP, Internet Explorer
+$PHPTHUMB_CONFIG['http_user_agent'] = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7'; // Windows XP, Firefox
+
+
+// * Compatability settings
+$PHPTHUMB_CONFIG['disable_pathinfo_parsing']        = false;  // if true, $_SERVER[PATH_INFO] is not parsed. May be needed on some server configurations to allow normal behavior.
+$PHPTHUMB_CONFIG['disable_imagecopyresampled']      = false;  // if true, ImageCopyResampled is replaced with ImageCopyResampleBicubic. May be needed for buggy versions of PHP-GD.
+$PHPTHUMB_CONFIG['disable_onlycreateable_passthru'] = true;   // if true, any image that can be parsed by GetImageSize() can be passed through; if false, only images that can be converted to GD by ImageCreateFrom(JPEG|GIF|PNG) functions are allowed
+
+
+// * HTTP remote file opening settings
+$PHPTHUMB_CONFIG['http_fopen_timeout']              = 10;   // timeout (in seconds) for fopen / curl / fsockopen
+$PHPTHUMB_CONFIG['http_follow_redirect']            = true; // if true (default), follow "302 Found" redirects to new URL; if false, return error message
+
+
+// * Speed optimizations configuration
+$PHPTHUMB_CONFIG['use_exif_thumbnail_for_speed'] = false; // If true, and EXIF thumbnail is available, and is larger or equal to output image dimensions, use EXIF thumbnail rather than actual source image for generating thumbnail. Benefit is only speed, avoiding resizing large image.
+$PHPTHUMB_CONFIG['allow_local_http_src']         = true; // If true, 'src' parameter can be "http://<thishostname>/path/image.ext" instead of just "/path/image.ext"; if false then display warning message to encourage more efficient local-filename calling.
+
+// END USER CONFIGURATION SECTION
+
+///////////////////////////////////////////////////////////////////////////////
+
+// START DEFAULT PARAMETERS SECTION
+// If any parameters are constant across ALL images, you can set them here
+
+$PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE = true;  // if true, any parameters in the URL will override the defaults set here; if false, any parameters set here cannot be overridden in the URL
+$PHPTHUMB_DEFAULTS_DISABLEGETPARAMS  = false; // if true, GETstring parameters will be ignored (except for 'src') and only below default parameters will be used; if false, both default and GETstring parameters will be used (depending on $PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE). Will be auto-set true if !empty($PHPTHUMB_CONFIG['cache_default_only_suffix'])
+
+//$PHPTHUMB_DEFAULTS['w']    = 200;
+//$PHPTHUMB_DEFAULTS['fltr'] = array('blur|10');
+//$PHPTHUMB_DEFAULTS['q']    =  90;
+
+
+// END DEFAULT PARAMETERS SECTION
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Function for generating hashed calls to phpThumb if 'high_security_enabled'
+// example:
+//   require_once($_SERVER['DOCUMENT_ROOT'].'/phpThumb/phpThumb.config.php');
+//   echo '<img src="'.phpThumbURL('src=/images/pic.jpg&w=50').'">';
+
+function phpThumbURL($ParameterString) {
+	global $PHPTHUMB_CONFIG;
+	return str_replace(@$PHPTHUMB_CONFIG['document_root'], '', dirname(__FILE__)).DIRECTORY_SEPARATOR.'phpThumb.php?'.$ParameterString.'&hash='.md5($ParameterString.@$PHPTHUMB_CONFIG['high_security_password']);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+?>
Index: /afridex/plugins/Flutter/phpthumb.ico.php
===================================================================
--- /afridex/plugins/Flutter/phpthumb.ico.php (revision 21)
+++ /afridex/plugins/Flutter/phpthumb.ico.php (revision 21)
@@ -0,0 +1,119 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// phpthumb.ico.php - .ICO output format functions          //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+
+class phpthumb_ico {
+
+	function phpthumb_ico() {
+		return true;
+	}
+
+
+	function GD2ICOstring(&$gd_image_array) {
+		foreach ($gd_image_array as $key => $gd_image) {
+
+			$ImageWidths[$key]  = ImageSX($gd_image);
+			$ImageHeights[$key] = ImageSY($gd_image);
+	    	$bpp[$key]          = ImageIsTrueColor($gd_image) ? 32 : 24;
+	    	$totalcolors[$key]  = ImageColorsTotal($gd_image);
+
+			$icXOR[$key] = '';
+			for ($y = $ImageHeights[$key] - 1; $y >= 0; $y--) {
+				for ($x = 0; $x < $ImageWidths[$key]; $x++) {
+					$argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y);
+					$a = round(255 * ((127 - $argb['alpha']) / 127));
+					$r = $argb['red'];
+					$g = $argb['green'];
+					$b = $argb['blue'];
+
+					if ($bpp[$key] == 32) {
+						$icXOR[$key] .= chr($b).chr($g).chr($r).chr($a);
+					} elseif ($bpp[$key] == 24) {
+						$icXOR[$key] .= chr($b).chr($g).chr($r);
+					}
+
+					if ($a < 128) {
+						@$icANDmask[$key][$y] .= '1';
+					} else {
+						@$icANDmask[$key][$y] .= '0';
+					}
+				}
+				// mask bits are 32-bit aligned per scanline
+				while (strlen($icANDmask[$key][$y]) % 32) {
+					$icANDmask[$key][$y] .= '0';
+				}
+			}
+			$icAND[$key] = '';
+			foreach ($icANDmask[$key] as $y => $scanlinemaskbits) {
+				for ($i = 0; $i < strlen($scanlinemaskbits); $i += 8) {
+					$icAND[$key] .= chr(bindec(str_pad(substr($scanlinemaskbits, $i, 8), 8, '0', STR_PAD_LEFT)));
+				}
+			}
+
+		}
+
+	    foreach ($gd_image_array as $key => $gd_image) {
+			$biSizeImage = $ImageWidths[$key] * $ImageHeights[$key] * ($bpp[$key] / 8);
+
+	    	// BITMAPINFOHEADER - 40 bytes
+			$BitmapInfoHeader[$key]  = '';
+			$BitmapInfoHeader[$key] .= "\x28\x00\x00\x00";                              // DWORD  biSize;
+			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageWidths[$key], 4);      // LONG   biWidth;
+			// The biHeight member specifies the combined
+			// height of the XOR and AND masks.
+			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageHeights[$key] * 2, 4); // LONG   biHeight;
+	    	$BitmapInfoHeader[$key] .= "\x01\x00";                                      // WORD   biPlanes;
+	   		$BitmapInfoHeader[$key] .= chr($bpp[$key])."\x00";                          // wBitCount;
+			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // DWORD  biCompression;
+			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($biSizeImage, 4);            // DWORD  biSizeImage;
+	    	$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // LONG   biXPelsPerMeter;
+	    	$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // LONG   biYPelsPerMeter;
+	    	$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // DWORD  biClrUsed;
+	    	$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                              // DWORD  biClrImportant;
+		}
+
+
+		$icondata  = "\x00\x00";                                      // idReserved;   // Reserved (must be 0)
+		$icondata .= "\x01\x00";                                      // idType;       // Resource Type (1 for icons)
+		$icondata .= phpthumb_functions::LittleEndian2String(count($gd_image_array), 2);  // idCount;      // How many images?
+
+		$dwImageOffset = 6 + (count($gd_image_array) * 16);
+		foreach ($gd_image_array as $key => $gd_image) {
+	    	// ICONDIRENTRY   idEntries[1]; // An entry for each image (idCount of 'em)
+
+	    	$icondata .= chr($ImageWidths[$key]);                     // bWidth;          // Width, in pixels, of the image
+	    	$icondata .= chr($ImageHeights[$key]);                    // bHeight;         // Height, in pixels, of the image
+	    	$icondata .= chr($totalcolors[$key]);                     // bColorCount;     // Number of colors in image (0 if >=8bpp)
+	    	$icondata .= "\x00";                                      // bReserved;       // Reserved ( must be 0)
+
+	    	$icondata .= "\x01\x00";                                  // wPlanes;         // Color Planes
+			$icondata .= chr($bpp[$key])."\x00";                      // wBitCount;       // Bits per pixel
+
+			$dwBytesInRes = 40 + strlen($icXOR[$key]) + strlen($icAND[$key]);
+			$icondata .= phpthumb_functions::LittleEndian2String($dwBytesInRes, 4);       // dwBytesInRes;    // How many bytes in this resource?
+
+		    $icondata .= phpthumb_functions::LittleEndian2String($dwImageOffset, 4);      // dwImageOffset;   // Where in the file is this image?
+			$dwImageOffset += strlen($BitmapInfoHeader[$key]);
+			$dwImageOffset += strlen($icXOR[$key]);
+			$dwImageOffset += strlen($icAND[$key]);
+	    }
+
+	    foreach ($gd_image_array as $key => $gd_image) {
+			$icondata .= $BitmapInfoHeader[$key];
+			$icondata .= $icXOR[$key];
+			$icondata .= $icAND[$key];
+	    }
+
+	    return $icondata;
+	}
+
+}
+
+?>
Index: /afridex/plugins/Flutter/RCCWP_CustomWritePanelPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CustomWritePanelPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CustomWritePanelPage.php (revision 21)
@@ -0,0 +1,428 @@
+<?php
+include_once('RCCWP_CustomWritePanel.php');
+
+class RCCWP_CustomWritePanelPage
+{
+	function Content($customWritePanel = null)
+	{
+		$customWritePanelName = "";
+		$customWritePanelDescription = "";
+		$write_panel_category_ids = array();
+		$defaultTagChecked = 'checked="checked"';
+		$showPost = true;
+		if ($customWritePanel != null)
+		{
+			$customWritePanelName = $customWritePanel->name;
+			$customWritePanelDescription = $customWritePanel->description;
+			$customWritePanelDisplayOrder = $customWritePanel->display_order;
+			$customWritePanelType = $customWritePanel->type;
+			if ($customWritePanelType == 'page') $showPost = false;
+			$customWritePanelCategoryIds = RCCWP_CustomWritePanel::GetAssignedCategoryIds($customWritePanel->id);
+			$customWritePanelStandardFieldIds = RCCWP_CustomWritePanel::GetStandardFields($customWritePanel->id);
+			$defaultTagChecked = '';
+			?>
+			<input type="hidden" name="custom-write-panel-id" value="<?php echo $customWritePanel->id?>" />
+			<?php
+		}
+		
+  		?>
+
+
+		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+		<tbody>
+		<tr valign="top">
+			<th scope="row">Placement</th>
+        		<td>
+				<!-- START :: Javascript for Image/Photo' Css Class -->
+				<script type="text/javascript" language="javascript">
+					jQuery(document).ready( function() {
+							<?php if ($showPost){ ?>
+								showHide("flutter_forpost", "flutter_forpage");
+							<?php } else { ?>
+								showHide("flutter_forpage", "flutter_forpost");
+							<?php } ?>
+						});
+						
+					function showHide(showClassID, hideClassID)
+					{
+						jQuery( function($) {
+							$("."+showClassID).css("display","");
+							$("."+hideClassID).css("display","none");
+							});
+					}
+				</script>
+				<!-- END :: Javascript for Image/Photo' Css Class -->
+				<input type="radio" name="radPostPage" id="radPostPage" value="post" <?php if($customWritePanelType == 'post' || $customWritePanel == null){?> checked="checked" <?php } ?> onclick='showHide("flutter_forpost", "flutter_forpage");' /> <strong>Post </strong> &nbsp; &nbsp; &nbsp; 
+				<input type="radio" name="radPostPage" id="radPostPage" value="page" <?php if($customWritePanelType == 'page'){?> checked="checked" <?php } ?> onclick='showHide("flutter_forpage", "flutter_forpost");' /> <strong>Page</strong>
+			</td>
+		</tr>
+
+
+		<tr valign="top">
+			<th scope="row"  align="right">Name:</th>
+			<td>
+				<input name="custom-write-panel-name" id="custom-write-panel-name" size="40" type="text" value="<?php echo $customWritePanelName?>" />
+			</td>
+		</tr>
+
+	
+		<tr valign="top"  id="catText">
+			<th scope="row"  align="right"><div id="catLabel" style="display:inline;">Assigned Categories:</div></th>
+			<td>
+				
+				<?php
+				$cats = RCCWP_Application::GetWpCategories();
+	
+				foreach ($cats as $cat) : 
+					$checked = "";
+					if(isset($customWritePanel->id) && !empty($customWritePanel->id))
+					{
+						if (in_array($cat->cat_ID, $customWritePanelCategoryIds))
+						{
+							$checked = "checked=\"checked\"";
+						}
+					}
+				?>
+					<input type="checkbox" name="custom-write-panel-categories[]" value="<?php echo $cat->cat_ID?>" <?php echo $checked?> /> <?php echo $cat->cat_name ?> <br/>
+				<?php
+				endforeach;
+				?>
+				
+			</td>
+		</tr>
+
+
+		<tr valign="top">
+			<th scope="row" align="right">Standard Fields:</th>
+			<td>
+				<?php 
+					global $STANDARD_FIELDS, $wp_version;
+					foreach ($STANDARD_FIELDS as $field) :
+						if ($field->excludeVersion <= substr($wp_version, 0, 3)) continue;
+						if ($field->isAdvancedField) continue;
+						
+						$checked = "";
+						$classes = "";
+						if ($customWritePanel != null)
+						{
+							if (in_array($field->id, $customWritePanelStandardFieldIds))
+							{
+								$checked = "checked=\"checked\"";
+							}
+						}
+						else
+						{
+							if ($field->defaultChecked)
+							{
+								$checked = "checked=\"checked\""; 
+							}
+						}
+						
+						if ($field->forPost && !$field->forPage) $classes = $classes . " flutter_forpost";
+						if ($field->forPage && !$field->forPost) $classes = $classes . " flutter_forpage";
+				?>
+					<div class="<?php echo $classes?>"> 
+						<input type="checkbox" name="custom-write-panel-standard-fields[]" value="<?php echo $field->id?>" <?php echo $checked?> /> 
+						<?php echo $field->displayName?> 
+						<br />
+					</div>
+				<?php
+					endforeach;
+				?>
+			</td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row" align="right">Advanced Fields:</th>
+			<td>
+				<?php 
+					global $STANDARD_FIELDS, $wp_version;
+					foreach ($STANDARD_FIELDS as $field) :
+						if ($field->excludeVersion <= substr($wp_version, 0, 3)) continue;
+						if (!$field->isAdvancedField) continue;
+						
+						$checked = "";
+						$classes = "";
+						if ($customWritePanel != null)
+						{
+							if (in_array($field->id, $customWritePanelStandardFieldIds))
+							{
+								$checked = "checked=\"checked\"";
+							}
+						}
+						else
+						{
+							if ($field->defaultChecked)
+							{
+								$checked = "checked=\"checked\""; 
+							}
+						}
+						if ($field->forPost && !$field->forPage) $classes = $classes . " flutter_forpost";
+						if ($field->forPage && !$field->forPost) $classes = $classes . " flutter_forpage";
+						
+				?>
+					<div class="<?php echo $classes?>"> 
+						<input type="checkbox" name="custom-write-panel-standard-fields[]" value="<?php echo $field->id?>" <?php echo $checked?> /> 
+						<?php echo $field->displayName?> 
+						<br />
+					</div>
+				<?php
+					endforeach;
+				?>
+			</td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row" align="right">Order:</th>
+			<td><input name="custom-write-panel-order" id="custom-write-panel-order" size="2" type="text" value="<?php echo $customWritePanelDisplayOrder?>" /></td>
+		</tr>
+
+		<?php
+		if (!isset($customWritePanel)) :
+		?>
+		<tr>
+			<th scope="row" align="right">Custom Fields:</th>
+			<td>Add custom fields later by editing this custom write panel.</td>
+		</tr>
+		<?php
+		endif;
+		?>
+		</tbody>
+		</table>
+		
+		<?php
+	}
+	
+	function Edit()
+	{
+		$customWritePanel = RCCWP_CustomWritePanel::Get((int)$_REQUEST['custom-write-panel-id']);
+		?>
+		<div class="wrap">
+		
+		<h2>Edit <?php echo $customWritePanel->name ?> Write Panel</h2>
+		
+		<form action="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('submit-edit-custom-write-panel')?>" method="post" id="submit-edit-custom-write-panel">
+		
+		<?php
+		RCCWP_CustomWritePanelPage::Content($customWritePanel);
+		?>
+		
+		<p class="submit" >
+			<a  style="color:black" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('cancel-edit-custom-write-panel')?>" class="button"><?php _e('Cancel'); ?></a> 
+			<input type="submit" id="submit-edit-custom-write-panel" value="<?php _e('Update'); ?>" />
+		</p>
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+	
+	function GetAssignedCategoriesString($customWritePanel)
+	{
+		$results = RCCWP_CustomWritePanel::GetAssignedCategories($customWritePanel);
+		$str = '';
+		foreach ($results as $r)
+		{
+			$str .= $r->cat_name . ', ';	
+		}
+		$str = substr($str, 0, strlen($str) - 2); // deletes last comma and whitespace
+		return $str;
+	}
+	
+	function GetStandardFieldsString($customWritePanel)
+	{
+		$results = RCCWP_CustomWritePanel::GetStandardFields($customWritePanel);
+		foreach ($results as $r)
+		{
+			$str .= $r->name . ', ';	
+		}
+		$str = substr($str, 0, strlen($str) - 2); // deletes last comma and whitespace
+		return $str;
+	}
+	
+	
+	/**
+	 * View groups/fields of a write panel
+	 *
+	 */
+	function View()
+	{
+		//if(isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id']) )
+			
+		//if(isset($_POST['custom-write-panel-id']) && !empty($_POST['custom-write-panel-id']) )
+			//$customWritePanelId = (int)$_POST['custom-write-panel-id'];
+			
+		$customWritePanelId = (int)$_REQUEST['custom-write-panel-id'];
+
+		$customWritePanels = RCCWP_CustomWritePanel::GetCustomWritePanels();
+		$customWritePanel = RCCWP_CustomWritePanel::Get($customWritePanelId);
+		$custom_groups = RCCWP_CustomWritePanel::GetCustomGroups($customWritePanelId);
+		
+		// get default group id
+		foreach ($custom_groups as $group){
+			if ($group->name == '__default'){
+				$customDefaultGroupId = $group->id;
+				break;
+			}
+		}
+			
+		
+		?>
+
+		<script type="text/javascript" language="javascript">
+			function confirmBeforeDelete()
+			{
+				return confirm("Are you sure you want to delete this write panel?");							
+			}
+		</script>
+		<div class="wrap">
+
+		<form action="<?php echo RCCWP_ManagementPage::GetPanelPage() . "&flutter_action=view-custom-write-panel"?>" method="post"  id="posts-filter" name="SelectWritePanel">
+			<h2>
+				<?php echo $customWritePanel->name?>
+				<span style="font-size:small">
+					&nbsp; &nbsp;
+					<a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('edit-custom-write-panel', $panel->id); ?>" ><?php _e('Edit'); ?></a>|
+					<a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('delete-custom-write-panel', $panel->id); ?>" onclick="return confirmBeforeDelete();"><?php _e('Delete'); ?></a>|
+					<a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('export-custom-write-panel', $panel->id); ?>" ><?php _e('Export'); ?></a>
+				</span>
+			</h2>
+			<p id="post-search" style="margin-top:6px">
+				<strong>
+					<?php _e('Choose a Write Panel')?>
+					<select name="custom-write-panel-id" style="margin-top:-2px" onchange="document.SelectWritePanel.submit()">
+						<?php
+						foreach ($customWritePanels as $panel) :
+						?>
+							<option <?php echo ($customWritePanelId==$panel->id?' selected ':''); ?> value="<?php echo $panel->id?>"><?php echo $panel->name;?></option>
+						<?php
+						endforeach;
+						?>
+					</select>
+				</strong>
+				<a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('create-custom-group')?>" class="button-secondary"><?php _e('+ Create a Group')?></a>
+				<a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('create-custom-field')."&custom-group-id=$customDefaultGroupId"?>" class="button-secondary"><?php _e('+ Create a Field')?></a>
+			</p>
+		</form>
+		
+		<br class="clear"/>
+
+  		<table cellpadding="3" cellspacing="3" width="100%" class="widefat">
+  		<thead>
+	  		<tr>
+	  			<th width="60%" scope="col">Name</th>
+	  			<th width="20%" scope="col">Type</th>
+				<th width="20%" scope="col">Actions</th>
+			</tr>
+  		</thead>
+  		<tbody>
+	  		<?php
+	  		foreach ($custom_groups as $group) :
+				if ($customDefaultGroupId != $group->id){			
+	  		?>
+		  			<tr>
+		  				<td><strong><a style="color:#D54E21" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('edit-custom-group')."&custom-group-id={$group->id}"?>"><?php echo $group->name?></a></strong>&nbsp;&nbsp;(<a style="font-size:very-small" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('create-custom-field')."&custom-group-id={$group->id}"?>"><?php _e('create field'); ?></a>) </td>
+		  				<td>Group</td>
+		  				<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('delete-custom-group')."&custom-group-id={$group->id}"?>"><?php _e('X Delete'); ?></a></td>
+		  				
+		  			</tr>
+	  		<?php
+	  				RCCWP_CustomWritePanelPage::DisplayGroupFields($group->id, true);
+				}
+	  		endforeach;
+	  		RCCWP_CustomWritePanelPage::DisplayGroupFields($customDefaultGroupId);
+	  		?>
+  		</tbody>
+  		</table>
+		</div>
+		<?php
+	}
+	
+	function DisplayGroupFields($customGroupId, $intended = false){
+		$custom_fields = RCCWP_CustomGroup::GetCustomFields($customGroupId);
+		foreach ($custom_fields as $field) :
+		?>
+			<tr>
+				<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('edit-custom-field')."&custom-field-id=$field->id"?> " ><?php if ($intended){ ?><img align="top" src="<?php echo FLUTTER_URI; ?>images/arrow_right.gif" alt=""/> <?php } ?><?php echo $field->description?></a></td>
+		  		<td><?php echo $field->type?></td>
+		  		<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('delete-custom-field')."&custom-field-id=$field->id"?>" ><?php _e('X Delete'); ?></a></td>
+		  		
+			</tr>
+			
+		<?php
+		endforeach;
+	}
+	
+	function Import()
+	{
+		
+		include_once('RCCWP_CustomWritePanel.php');
+		
+		if(isset($_FILES['import-write-panel-file']) && !empty($_FILES['import-write-panel-file']['tmp_name']) ) {
+			$filePath = $_FILES['import-write-panel-file']['tmp_name'];
+		}
+		else {
+			die("Error uploading file!");
+		}
+
+		$writePanelName = basename($_FILES['import-write-panel-file']['name'], ".pnl");
+		$panelID = RCCWP_CustomWritePanel::Import($filePath, $writePanelName);
+		unlink($filePath);
+		
+		
+		echo "<div class='wrap'><h3>The Write Panel was imported successfuly.</h3>";
+		echo '<p><a href="' . RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('view-custom-write-panel', $panelID).'"> Click here </a> to edit the write panel. </p>';
+		echo "</div>";
+		
+	}
+	
+	function ViewWritePanels()
+	{
+
+		$customWritePanels = RCCWP_CustomWritePanel::GetCustomWritePanels();
+		?>
+
+		<div class="wrap">
+
+		<form action="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('import-write-panel')?>" method="post"  id="posts-filter" name="ImportWritePanelForm" enctype="multipart/form-data">
+			<h2><?php _e('Custom Write Panel'); ?></h2>
+			<p id="post-search">					
+				<input id="import-write-panel-file" name="import-write-panel-file" type="file" /> 
+				<a href="#none" class="button-secondary" style="display:inline" onclick="document.ImportWritePanelForm.submit();"><?php _e('Import a Write Panel'); ?></a>
+				<a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('create-custom-write-panel'); ?>" class="button-secondary" style="display:inline"><?php _e('+ Create a Write Panel'); ?></a>
+			</p>	
+		</form>
+				
+		<br class="clear"/>
+		
+		<table cellpadding="3" cellspacing="3" width="100%" class="widefat">
+			<thead>
+				<tr>
+					<th scope="col" width="60%"><?php _e('Name'); ?></th>
+					<th colspan="4" style="text-align:center"><?php _e('Actions'); ?></th>
+				</tr>
+			</thead>
+			<tbody>
+				<?php
+				foreach ($customWritePanels as $panel) :
+				?>
+					<tr">
+						<td><?php echo $panel->name ?></td>			
+						<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('view-custom-write-panel', $panel->id)?>" ><?php _e('Edit Fields/Groups') ?></a></td>
+						<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('edit-custom-write-panel', $panel->id)?>" ><?php _e('Edit Write Panel') ?></a></td>
+						<td><a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('export-custom-write-panel', $panel->id); ?>" ><?php _e('Export'); ?></a></td>		
+					</tr>
+				<?php
+				endforeach;
+				?>
+			</tbody>
+		</table>
+
+		</div>
+		<?php 
+	}
+	
+	
+}
+?>
Index: /afridex/plugins/Flutter/Flutter_Documentation.pdf
===================================================================
--- /afridex/plugins/Flutter/Flutter_Documentation.pdf (revision 21)
+++ /afridex/plugins/Flutter/Flutter_Documentation.pdf (revision 21)
@@ -0,0 +1,3360 @@
+%PDF-1.4
+%âãÏÓ
+
+1 0 obj
+<</Type /Catalog
+/Pages 2 0 R
+/ViewerPreferences <</PrintScaling /None>>
+/Outlines 15 0 R>>
+endobj
+
+17 0 obj
+<</Title (Installation on Wordpress)
+/Parent 16 0 R
+/Dest [3 0 R /XYZ 0 631 0]
+/Next 18 0 R>>
+endobj
+
+18 0 obj
+<</Title (Installation on Wordpress MU)
+/Parent 16 0 R
+/Dest [3 0 R /XYZ 0 512 0]
+/Prev 17 0 R
+/Next 19 0 R>>
+endobj
+
+19 0 obj
+<</Title (Flutter: Differences between Standard and MU Versions)
+/Parent 16 0 R
+/Dest [3 0 R /XYZ 0 369 0]
+/Prev 18 0 R
+/Next 20 0 R>>
+endobj
+
+20 0 obj
+<</Title (What are Write Panels?)
+/Parent 16 0 R
+/Dest [3 0 R /XYZ 0 165 0]
+/Prev 19 0 R
+/Next 21 0 R>>
+endobj
+
+21 0 obj
+<</Title (Creating a Write Panel)
+/Parent 16 0 R
+/Dest [4 0 R /XYZ 0 642 0]
+/Prev 20 0 R
+/Next 22 0 R>>
+endobj
+
+22 0 obj
+<</Title (Using fields)
+/Parent 16 0 R
+/Dest [4 0 R /XYZ 0 364 0]
+/Prev 21 0 R
+/Next 23 0 R>>
+endobj
+
+23 0 obj
+<</Title <FEFF004300720065006100740069006E00670020004C00610079006F00750074002D0043006F006D00700061007400690062006C00650020005400680065006D006500530069006E0063006500202018004600720065007300680050006F007300742019002000770065201900760065002000720065002D0061007000700072006F006100630068006500640020007400680065002000770061007900200077006500200063007200650061007400650020006600690065006C0064007300200066006F0072002000770072006900740065002000700061006E0065006C0073>
+/Parent 16 0 R
+/Dest [5 0 R /XYZ 0 626 0]
+/Prev 22 0 R
+/Next 24 0 R>>
+endobj
+
+24 0 obj
+<</Title (Adding Modules to Themes)
+/Parent 16 0 R
+/Dest [6 0 R /XYZ 0 720 0]
+/Prev 23 0 R
+/Next 25 0 R>>
+endobj
+
+25 0 obj
+<</Title (Retrieving Fields value)
+/Parent 16 0 R
+/Dest [6 0 R /XYZ 0 552 0]
+/Prev 24 0 R
+/Next 26 0 R>>
+endobj
+
+26 0 obj
+<</Title (Using Fields Values in query_posts Function)
+/Parent 16 0 R
+/Dest [7 0 R /XYZ 0 720 0]
+/Prev 25 0 R
+/Next 27 0 R>>
+endobj
+
+27 0 obj
+<</Title (Canvas.php structure)
+/Parent 16 0 R
+/Dest [7 0 R /XYZ 0 470 0]
+/Prev 26 0 R
+/Next 28 0 R>>
+endobj
+
+28 0 obj
+<</Title (Modules and Layout)
+/Parent 16 0 R
+/Dest [8 0 R /XYZ 0 720 0]
+/Prev 27 0 R
+/Next 29 0 R>>
+endobj
+
+29 0 obj
+<</Title (configure.xml)
+/Parent 16 0 R
+/Dest [8 0 R /XYZ 0 358 0]
+/Prev 28 0 R
+/Next 30 0 R>>
+endobj
+
+30 0 obj
+<</Title (EIP \(Edit-n-place\))
+/Parent 16 0 R
+/Dest [11 0 R /XYZ 0 462 0]
+/Prev 29 0 R
+/Next 31 0 R>>
+endobj
+
+31 0 obj
+<</Title (canvas_define_zone)
+/Parent 16 0 R
+/Dest [12 0 R /XYZ 0 720 0]
+/Prev 30 0 R
+/Next 32 0 R>>
+endobj
+
+32 0 obj
+<</Title (canvas_zone)
+/Parent 16 0 R
+/Dest [13 0 R /XYZ 0 550 0]
+/Prev 31 0 R
+/Next 33 0 R>>
+endobj
+
+33 0 obj
+<</Title (get_module)
+/Parent 16 0 R
+/Dest [13 0 R /XYZ 0 296 0]
+/Prev 32 0 R
+/Next 34 0 R>>
+endobj
+
+34 0 obj
+<</Title (mod_var)
+/Parent 16 0 R
+/Dest [14 0 R /XYZ 0 720 0]
+/Prev 33 0 R>>
+endobj
+
+16 0 obj
+<</Title (Usage Documentation)
+/Parent 15 0 R
+/Dest [3 0 R /XYZ 0 704 0]
+/First 17 0 R
+/Last 34 0 R
+/Count 18>>
+endobj
+
+15 0 obj
+<</Type /Outlines
+/First 16 0 R
+/Last 16 0 R>>
+endobj
+
+2 0 obj
+<</Type /Pages
+/Kids [3 0 R 4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
+/Count 12>>
+endobj
+
+41 0 obj
+<</Length 42 0 R
+/Filter /FlateDecode>>
+stream
+xÍWÛn7}×WÌ£Z$Ôò²·<Ž@â\
+ÄšË5 ÖZJÚvµTÜ(Îg&?Ôá^€¥D;VûRÃ°ï3g8ÃÃ%þÀj³n5|(éèµ,ÜÇ%DÇÂÅ>. IÅðqÿÝM>0ÂìüüäTCÈ"ÂìB°>*;
+²]O>tÑ1pÄ
+4=!pøhÎÉ(#±¥`©ýÏ¿(#$àc
+!=%x9ß·!J"ió|bÛ8{Cö
+/'Ó­$\šE³ÉL¡ª`þçäõÜa)á=Ëø¥Ò&+ËÃiJïÂÛìpüœUuŸ­¥Ö#
+°®šËÎ:"48¡µ«Jå`ÖRjd­ÀMõ¥Ø¶OE)5F×Ù
+²*ŒVÝÆleKUæ²¢2Ê>IA$âèH
+u€|yÅãvénû|¡*{<ÛÍªšô·!&<r0Çw"7U
+R1¯Þ]þzq
+cN%NðI%l±4l
+ã1?¹Ò9Ä)OÏÓöäã²lõ7Ä8Ùìšš¯7Kã*áFbÇ1 €GlÛŠ.
+¬?Å¿yèjûðwóË÷W=ÂFoãA¹ÑrYT
+ïWv3g×².²²øâbh
+zÉ)ÛM£®
+ÿ`h
+güÓa
+ðñìŠGxtÐùŠoÜ8'$	m¶Qà±T¿«ôZ5ewÐ	@KC<&bO^x®ÀåO»Ðfÿ>âJyïcÙ4Ï}V×lHyìÀÎ°*~ªÏ°ö+y4Ï¬Ä)êQ+q"°í»V"Xzÿ®8ÑgZÉI&¯£Î¶üàtz7Ã¶t8ØÏMâoÄ$t 'nâÍ
+Øã «íýþø:ì42á KyÏÛìhÆâXž4?"|Óåíz; ;N£ãün
+FíkÎQìÛ!òÓ
+qŸ$ Nàpr¯¿ÑNÞïÍž(KYËj!5Ò\<Y·n{y¿uì}UäøB< õFÕ
+úTÇ÷º
+m°
+
+ýãàJÕ¬<`Ì
+k?HÀ©ªÊ{ÈGEeµ|á
+ø0(Sûîçm&msÁ{&Ë7E5hmÉU%a·V°È*XÔ¯­vd^o0ÝçöØ¶Y%KMŒ0xŒ,ÕJwRt·Ñ²Ç£ÈAE%w°UÚ<ÀÉÃ'sÐw µeßašñåûì^5ÀktLËUÃ»ºÆÃ[
+¯f|sÀ~Z¶tÐ{Æ	¥£±
+}mÂœx+±CZŒûw
+YïÇæ¶.PóU»3>jŒiê?·ëÌØaq8~öÍEEBøGþvç+µÅEGõ\aW ¿è5ÏÚg6KYl
++ßŸ±ì€,sÝØ³vÏm#ÁŠìŠËøGÑ^ÊŽ
+ô
+D|[KliS·Š¡vºÕùågDàFîç®
+XœºÝ^_f{÷
+íàõè»}Ç[sñ×ú,5	 _×Y^(žCiè'@iýÐŽíÔdx7ûÿ
+=m
+endstream
+endobj
+
+42 0 obj
+1272
+endobj
+
+3 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 41 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+43 0 obj
+<</Length 44 0 R
+/Filter /FlateDecode>>
+stream
+x¥WÛÛ6}÷WÌCŒ
+«QhÙŒ4Až(žd¶È¢«Ë:þÍù)Ù&µŽÛ$A²ÑZ3Ã9gfÇ$ñl&1íNÿsôµ¬¿?Û%$H
+cÇðíÂ "øGøéaòT? Cù$åA¯ADQÿÀé`%XïÑ^OÞôÖŸ
+!¢©~`É`Fã/
+Á£~[² rB°ôKCpÒ?|}'qBDäÿ
+Àò2íHÓ
+.â¯LbM£ocì[© 
+Qáñë|BBÚ
+ãM
+y>Ññì	û&¯'Ó*ÛÉlGYA»ÍZÐÀQu°Ê*ÚÖ
+|øJÂcVvÔº·[²Ìa­jónÕÕµ¬ZØ«Š|UÇ;¿<;Ù4<g3m`Ÿ-À¿¹ª$,xlYÕ2hörUd%¬»jÕª2©åVXœ;a=àF¶éâÎvÃiä%#7 þ8èß
+À9BÆé«Å
+Çt®{î	¬=
+3{yQËU[
+¡šLðn]²Våh¥_ŽÚê¥Ê;|X¡Oú'X&g ôÑ}-³¶¯ÈuÑJø=«diçGEÀ1IpÌØ\Ä>ðí1}¡ U¶H4l×¥ù¡ìÚVÖð³\cEÁ¹GÒRr3»¬ÊaU«÷vÌÄÍã
+9î»ŠU»+üpŒs·ùY"UÍŒŽ¢ôIÔÏ£m2?î¥C£*¬­m4Æñq.qpMælò°/¢(#ßyi@#
+'=8&ö:^àÅD"¬í0óc²MîGÅÑ HÊžcõ€7ªh¶¶W*Áð"wœ|ßlUú¶4YË
+»Ó`3š@æ8UûlX·²ŸHfÓfuëÅòÆå¹
+)#¢ØKü2ùÜKcr.ÛêJ/gð ³rº ÏùØÛ¥m-ö
+|ŽýPh§"Ñ·;»aY)¡äæqÀj»s¬ÞRNn}ÇÂKGÕõ²Û(ÏÇÙ uÃ[èréNW[¥ú+€g]u¿«÷Kõ!q­öŠ 8QÔJUšÉôwÊwt@äo
+Ëãº»qÝô9£ª#žžÌNVÝÌLÎ0nÔýWÉ4ÝòýúÉ¥ý ¢QºÙ\ÔÌöÐïÐ)a=LœŒ,
+á
+¥Vï-vŠOîTõWábÐËT{8í¶ë;%Éå¬ÕÐ,ÃÒ5ŠrœR×é¿&žÒ&"
+¹ù×ž]v ³áÜnmx¢[§µaó+kÅÍE -×$
+öZ_œp^©Ã(ŒE9èðeÂ=G¡X×zÌŽŽªpG2ê7ýîºÍv8ìK$Ž®Vc'sïÔQaÝWþuÇ1y~J\ïlÎäDžÖãjU.?ûíÞi¯#
+9öuÖEU"W~8øpÖ0ÿÖáüçœ0&Á; 
+ã1:Yß-Šßõ¿5øÍ£Ú?ŒµÛñ4uÃMû*þÔÖüŒžûÑuãã]NÞáöœôB8ã%×ïb
+©î«ÅÝâÎK,ùÕióoB¶n~P{iÊËRm`«vÒÈWnŽœ÷,A=º3ôœ&ß Œ;#b
+zbc²5êñZïFÆÈÇisÿ-Ã¬[_P­ô©(×]áe\êÐpøs¹Æ2ËþŸ/³U~×B0æWÃ¡ŠÏ,áV
+u©×ÄþÕÉý_º»
+endstream
+endobj
+
+44 0 obj
+1390
+endobj
+
+4 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 43 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+45 0 obj
+<</Length 46 0 R
+/Filter /FlateDecode>>
+stream
+x¥WÛnÛF}×WLßäÂZq/Œ¹NÄ©M ".5¹ÙP\€¬ºèWÖÉÿt€$.C)Mã{IÏ3·
+Râà¬&>kO»¿ÂÚ'ºC©&Ëï÷rÔ¡Ú4 áAÐ!çùðŸo·÷0s¢;¿ñrAü?x!m<èÄß*"Ü»ÉûVü4pµ xø.w^žô+!~«©oàŸKüo`Äâ?Cž>á
+5©ç¶±`¡yójêL|1#ÏÊ<"šÃÀwP0àñÄTöü!J[£ådJÉ,þüžhŽkÑúZÓjÝòB
+·€æ×ÔÂœÎ6u­Ês8àð§ðZÇLU}I×%óØÀížÌc²4úÐWÄÀºÔñOzvU*Y+á:÷Q;Cb¶Ý;ä¥óó~<v±ö<FÜ
+16XKdñP(+®Š
+ÂÀzÿÏºñ>öi3J<×cÅAŒ*š¹\+Xê²yh±`JXŠyZ%~ÓšœÉbžS£<\çÀá¶²UuÒÇ-äJõ£°
+Dp$ºãÇÙIóü,ôŠ]éu/înšußæâa[XxÓwi
+)øx]ª*y««úlÕ§{
+
+2EQj%ÆÑD9æ`Àv@[ùªµå²LUWMä¶eZr¹Êª±ØžØçÇªç§
+4Fª4ÚT
+º6éØÂmmÐósÀGXoªyoõópÔ&
+£EæºüÐ`hÑ€d©³LoM`«Zh ©mÓ,(ùªA-ºV õ
+bµ¬nm@£·{F-
+z°xÒÇ]¯ž8Éö4>,×ò	C€ÐË
+¥Ø08iÊ]KsØ@
+5kjpÂ}ÔìÄmrÛÇóðNÚoY€óZåõŒ	E5ïA ŸïŽkz³TM×Šõ`ï%ðKtgYŒ±Làõñ!Ö×Ÿ)TWa©5L¡Ÿí]U?dD5ý
+Oçy'
+Šá
+ï1JOZ;.8	]ošmlË­
+YŠ%6iæøš/F£Åý£
+5#
+~Áì^À¯Ø}oî{ÑußÍô³®¿9K!,BžÍGSØ×8Ü&
+Í
+÷CËQ'H$ó{Y")úéÇ@ôà\O«Ö4&Ù^TmÀiyk
+Œ¡HÇ&
+Ø1VFX0²ž_ž<[wåÎåw³\5là-Þ_ðJcÌþÄg³§£šXD;ÔéeÞC?y4ÐÔùÇ8­
+äu¹ÎÕã8
+§=ïeYUO
+ÛøÖi¡ñÈÜ­°PkÄ«ÕÅåŒG£aÍP{-Ó
+ëøá(
+ÇÐè\žC¥ÛHgu>î:Ýfâî;¯ËWí4Œ'î8FÐâ3¬'h©ß¶µqûFîfúÑõ¶ßÁžW7àNæ¹*?Ýý ÏÆÍùýhÏížëÚá>ìâ£mhkØ-u|°zÄ	pIé+OS
+ûÏ
+4LZ]Ú	ÎØîÇñ`h&^®äÒl0ûï.<»Föéa7Ör
+3®6íßÀíÚ_oÊ]ûg:ÂÁ§ó3«ÍEÍGýýÐJ¿B0/\÷; Kä
+ú«m·í÷cìæ-ÞÑ2~ ÐßïdT§÷f°êŸÞ9¬4ÜIë»å`ñ9Øodçxçh]Yuø®ïSKiXD/T®rxÚ.Ïýk3ÃLõ¬ÆpC¢%Ÿ,õº
+Ñ)^}z9JÒy/ÓLî÷ÁjéHc
+endstream
+endobj
+
+46 0 obj
+1408
+endobj
+
+5 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 45 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+47 0 obj
+<</Length 48 0 R
+/Filter /FlateDecode>>
+stream
+x¥WÛnÛF}×WÌC
+äÂYs/ŒhÔ
+M€B".
+Z\IL(®Àm}Pšý¡Î"¹»¡äÔ	ŠfÎ9sÙ%
+þõ,dÝSÿ¿¢î!RÎV?vSŠ¡EÁÐÃ®ñ!ôñíÃì#L?Q?œ?ÁtF¹ ~OÐ<:ñžsDžßf:ó~
+ÏÀ žv'
+×AøLuáü»âàaLï`!ÑKA9~hIAQ¯ ~^ãEH;`ÎtE]\Q Ï«ÙüufÅÞ©ŽÉeµÅFneuÏ³·
+EY@õ!'QÄÆºžb@œÓC°é1¿VoZ1£;žÔÂœÊº%üd82@ÄÔ&4ÿ%Ù«Š6ÍOXû'yzAµQMB%%$p§
+¡H¶25°x€ÁOrÝ¶"RÃM0F°ãŒ)5Ô¬jÏNtRËÁŠ¥ÚAVCV ¹ÝÕûÖ¹)RY"!XD-wø»q.UQËÂJšÏ¹ãë°¬ö\Ùti&.Ë<[~q*E°8Žqß7wyVmàrk«b±3ï3ÇÀï|hõºËÕÀnækùXCR·&5
+¬Vúq=Ò
+wÉZBì±^Á?Gü[©­ì×²eRËîöŠŒ²idM'@ù§ìþ5µí{ÞÑv=ßJÒr}K}+YÊb)§ BÄ12>ÈºÌäœ
+WÌÓ
+îŒ1Q"¯NŸç!ŠëŠg¡VÈ¢«ŠXÖ*pm0º-IM%û*¯õdUŠŸR%ºXkÙ¥¬¥¡À²©jµE[ä7ALÄ| 6OªJ-³6?ŠMÂÔ°SUýÊL?^#LÄèïãÔíýÿ*ÓÂ-oä{;ÑrûçÌ9ŒXªÙÝ`ë>þHñ×ö£á×ºläíY81ö¥s­ïKž>ÈR$_0Ž^I²ØèC8Ú¥%@&#÷°NGnXLDþW¶Åvùöø­È
+:ž,]eÄ>æ±7Ó
+«+Ë®e	ucbÑ[¬*žëðö;ïdâéAoºÑßhLÓ)H
+ãžŽNÌ×>ú8~2ŠÅT&ÍÔ3³ëY98YøIàÑíCõ°]ìeùÀVP	ÕUàµÞ©«»Àq:~0"è©Üà<5çö)ðþdQ0X¹TÆÀKS
+líÀŸãk3ªpã^âÍ7ž~ìÔÑÛËP
+§1&ßæèHEì8Û<2Ü
+Ö8K¹+e
+SSÑÏZŒ.óÐ 3»L*iŸ© mvxåëLŠxì	ùZÞViž0±å:·õÅ
+R/6øsâ*vëðæÉž°}ÆUÊ$Ý_©òíÍ{6Ö¶¯
+X HÈp÷±ÑÀVí­¢WM°î$ÍZ/-zT%öY^gyVÈþåykjèP®·²<Çvjn4È
+ÞEØÜf÷ÝE¶±Â
+ÏÃh'ÛÅ7¿AwüÖ¢w
+spÖúAÁVÒÜ&Æ¡h¶w­mWy«n}²TYÃ¿9
+zØ2Ïš!kØ²wŒ@ñìvKz3
+GÇ®=eqcQL
+ô©[~«Kä¬³{Y|µ
+Ìil
+mIt}9 tå~¿]1guS¢Œ²¶
+PóÿÊ<ývþYjgîFmš2[gEw\oÏÎÈX#
+kpd€YV
+_NfÁ3-&2v­81+IøÍð(
+ÀÓIräµÔÎîVêIÚŸÑÏJŠpPºå1©Žu6'
+Ãoúº
+ÎÊWYV
+VÏm·ûqL¿?å?9óäÃ
+endstream
+endobj
+
+48 0 obj
+1472
+endobj
+
+6 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 47 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+49 0 obj
+<</Length 50 0 R
+/Filter /FlateDecode>>
+stream
+xWknÛFþ¯SL¢Q
+kErùTí¥$hÔE\+re² H
+KZVÑëôBU|Îr)j)SÎ#@ìµ<óÍ7ï±Iü×ÏR¯ýwÛõÕÃµGÁË[¹À"fWÎòHp3ïº
+ŒÂçà§ÁXòeüjü	Dâ¯Áu)±åÃv=%
+hµ
+¢œ|PÒ}&µ/5  |P¿sÌ¯
+ .1Ÿ	"hlßVQÙ`8_@&-ÿ
+Ø#XË.¯õskÖç³CÍ>qiàÃ,ÈZOM0ßÍáo"É®að4ð;K+. ÉàcÅí|RÀŽÊÂ2É³Ç0ûkðr&Ñ}C1t=J|Z}<µÀ4ÈÓŽ*K^ KÓ|#`WPæ°LRù¡B^0Á#È3+Qæ+üeMãŠŠA`óÃÿå&
+ÛcÆÉ€&±ì IaÍ
+©á¿ºm©Xéu@tÚ5ŒâžIÊ5ä)v*µPÛ4Œ#Ôn0FðLj%ae bv)!c+
+õÒŽ¬#ÿu$°|ÃìH
+;²ÚÎk|]Ïwëö^êîX!»2æ°.ø2¹Õ ¬zXÆŠÝ>ÒU°ZšíyZÅÄ&%°,ªã3ËÎVŠ\«F/É5éÂ"€\
+¿W?RfÜÝÎ;\}B
+Çé 
+WºD°=³+q?ÖÔòI`¹~Wî¢,*~wõø§ŸÂu<Ø'âÓ[Ç]
+®ËoH/ã¶ÕX§ÙtÂHÃ4mú oªcÇñ[Àþ:Ö%zêX1çÒ%=
+x=R¿WÎú0q^!×ô2EB§x<TC:)ëZvkš<š
+Xl/nç§4UKÆŽÜ.a­ôCýõâÙûK,
+¹±gnžÄ;?0S!Ë0MØ^Hã_ÊöS
+Ø"¯ÊNäØô±qí4øÍ7³¹C®ï
+-.Ëõd<óß
+º#E×ãN*mbckuLg|µNYÉç3v-ÆÅœf/__
+ðeãÎÛèÔ)ñ,êÂj@Ý w(>ÛÏR\²<KÍ¶ž4úÀi¯0ç°Y·ê%Ën ëx
+X"UXVï
+šuzŸÎ N:`ªÔ4^q9
+~aØ|åè2_­1É¹Â0ÏJduSŠü~O¶M³µ<[ºré&BéF\E²À@nŽ¶	ùRívÍÅû$9|bBãw»>Co¿ÖÐßáÀ(ø­×EÎÂgÃÝaÁ1Ûû3@úœ)RZËx*êíç^+x
+¹Œåé¢ÉÁÏ@	:+¬²-¢KyúêØªéµå9[r[åÇT©ÎZåQÖÇKÄóñ;VGî¬¯óÕÈ ·^Sî¡>Rªú^®,syXÉRDèô+Îßêâ,òU^v§ŒÎ¿@Õ)ŒCä	Ì³$\QóàÅÛ7ðúFOúøÉexjê(~úÆ¢äFRºÀªl	ŒÎDÉ`*PJÅŠÓûŠi¯uß9Ì
+Û&ŸY¹ÇÀRã¬«¯þTúE`ùâ·ÉèD³×¹­!9Éb¢É§j3ø9_©ÎPÊ²PzcÙ_ûÊI,ßGiQåoPç2êIt±;
+EÛ_ì¢D`Sl'xbe|×bÊÂ	q±SÞ²NvO$£ÚÙý×ýh§¡I×ÞÈõ<¶§Ü0çÈÂæaV«¬:ZfììÖµåK
+<+ÏÇ±ÓáéA|*Çžr}®ò>ÿ#w5ü$Ãª/pjÚ$0
+ÚX°,ã
+<úài¿9Göœí³œÄöÅ7Üçw
+îl
+
+ºF5W{æçW}Ñüñ·òüê1Ô^ãøðõ¬Ç¯6S7,+{¬·öÊþ
+endstream
+endobj
+
+50 0 obj
+1516
+endobj
+
+7 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 49 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>
+/Annots [51 0 R]>>
+endobj
+
+51 0 obj
+<</Type /Annot
+/Rect [102.7500 492.6641 369.4736 503.8359]
+/Border [0 0 0]
+/Subtype /Link
+/A <</Type /Action
+/S /URI
+/URI (http://codex.wordpress.org/Template_Tags/query_posts)>>>>
+endobj
+
+52 0 obj
+<</Length 53 0 R
+/Filter /FlateDecode>>
+stream
+xWËÛ6Ýû+ž§pDêÝ]R4EŽ]¢È-QI4DÊ
+7è?íõRÔ
+Mé,<2uyîëð\`þÐaSû4þ¢Ä>DÁðÐòMñÍdGŒG®!ñq0z8¢ý q«çÍ{©y">œßrçÃky°Ù<Ql­ØîŽûÍ{ký9ÐOqì äëHcß7=áýÊ & òl8/óðá!ròÃ¯CH4
+JÐÁ*ò_@#[	áÅ
+ Idó¡©Yù_Ad+ñb1CÒôyŒÙÍì÷
+ùivùÆ»·áÝ®ØlyWq
+X£wì";ý
+íþØ|¿3 7T;öqøÔÜœ¥ÅðkÉÄPÝã ¡Ð±bÏ4«kNL!Ñ ]ò
+Ùß"¡QÍY£à
+ÓèmÕiÍ[tU
+*Ér»lQ×"ÈÑö!ðìÖ¥ñð&yÕ
+£w&Ã
+ãè»^EU]Îû
+ »¬«4>Çµt Ó4$:`ÞYgêî­0úÕü¯»O;^CôÓï÷âOø0Úÿj®Ôk?]³ªg
+À
+iib
+GçRd%Ò#¶i®|cqhÙ
+í/}yLYLºC÷\kXÒA1![ÊíZ4CÈ*}ìtÙrTÍL!àî_ŽâPj$þËÐœ|
+ay€(úD
+;ÅûhúÖ®ÅAffnE&[è³²ÒâhÛºF
+už@ÎB6PþšÑ?CÞ¿
+µú÷á^qÆ&£×ùÄ+š_{ÛG«JÙUÝ
+mÙSÉÈ,[ÞäŒUËŽ¯)f± k!Dt!k¹é©2gÕÜäBVÖ:P2õ¥\ÞèÉZYÙ=è
+kÞ">_(¬ª
+=$à!°çÛÅnºMI?ž<
+LB
+!œFMWï!uä(.#/E®Ë5Ø Q<FE1ºYÅ	œžF=5VÔ0ãÀµº¹u (	p€©kTó\tõÒ,€8eÿ<L.
+âíU¬uN:
+»	üè
+
+ÉÖóœáuEç€èÃ€Bråñf¥ÉO°÷ÌÿrçÂ
+Ã°u
+Èðš©«ºtd-ØkÃÉB¶
+Û1°I§¶¬-Ë2®Ø/VÖË#ÔPC*7Z<§nÀÖ\1úíÙs@}'çOSXäÏËþÇuÿZ©®æ}qJv€Vûi8;ŒöÏMÕ>ÀTÊ
+¹ ×0Z:±ª3Z£DîÈþz3
+±¢k2ÓÔµ À8<±öa{3ºŸqårŒpøpÇIÇõeÃ)Ãò¬zÔ^b|2Ý
+ekÔÊ0gdÙp¥(@Ö0í,ÛèØy®.fôí±UsÛPwJ#þ(àS4«ÑxÑÓ\.MžÑR;Š×÷¬Zk.
+4É[
+[{Éét4CUÖ~]ÍXŠÖ£I2'væ0%kÀp1¢ÍdÇèÍeäÑ-n\=ûì@bšáçÑ¯\-(£ÙïPÓ¢=Sf`³i
+K§Å
+·\
+?ÛÒ
+ žz$pŒ\÷JóÁÌþå>øUEQw[vŒMSmûÃ­©5Îl¶ñ*W«#€Ñ€Î×Q³NÒá]
+ªÂQwóßœsSŠOq&ïaŒ~×û]K¶Rs«­k!<KÏµ¢?)*ÈÜPXø*PK8ó JÂ=Ì
+ÎàÖ;~eýBùÿäwtE
+endstream
+endobj
+
+53 0 obj
+1413
+endobj
+
+8 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 52 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+54 0 obj
+<</Length 55 0 R
+/Filter /FlateDecode>>
+stream
+x¥UËnÛ0Œë+ö-RÔ+zè#EmÆè%)
+Ù¢
+zÕÄÙOêR²-ÒŠ õÁ&ÙÙÙå?°q"6¿<ÇCÈ÷V8ù	àÇÆç8#ø¯áÑ¹c©
+`?ÁÌ¡AD|?ÀèÀu0HwëÜðç)FèRx
+á
+Œ"ácùAzÕ³=êµE
+8Šö3Œ[:4£ª
+cÁ2sT+7š7ve;uZKXþv>.8ÌD)åFÜâ1aù @EBÄóŽ-ÒU)Nu(!±ùG*3}&ºu[leÑÔZ(cØðÇ'¡§UšÌÁëAãâbänkxÇ$àI|sZÅºéËV€xîð¶"u¡š¥ØÖ
+UÓ"­]hÓ¬h\èD)ÖÒµÜTEnyQ¢Ñ-E'
+MØ¢¡÷AD	Ñô¥ÉúRtWðcï
+|Ìê¬D>%|Fè×Š­Ò²Ü¹ðø jX£DYÔH!kmÖ<Öpq°
+W{¡÷ªúA;¬z)±Sy!Ð³	o-&Çb.FŸãþÒ
+Ú\
+Ê íåÒW}'¡Ûuï°Šº¯V¢UÑr!G
+ë;ŒÍŸ6ÈÛŠ²êESúOiµ-Åf1vK-º($¿_Tœ5) Q8ìD®AÕ#ÒaS0Ã
+uuM+­²¯j+%÷4ÊÅ)çÃr'örìPMÓ-V ëºiÑÁjgMàéÚ3p?Ñ4^«[ñcMÞw5V²P·8epAh§})íy¹Šæó+ÏLÁgléMÂùÜŠ=µ§a6:FÏ­|:Öó\f±3€LpäÕN`¡®'+ºmîÔò³£¹&èý8G;0L°Ÿ!íEjÆ·WöÂ£lŠÄ3
+Ò9×Á%õ× -6
+ÆfxÆOjì
+DS}=
+ýüjþOhsAŒHnìyG©Ÿ!ê¹ÜæN÷ÔXKÃ
+{çÀiÿD,;
+endstream
+endobj
+
+55 0 obj
+806
+endobj
+
+9 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 54 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+56 0 obj
+<</Length 57 0 R
+/Filter /FlateDecode>>
+stream
+x¥WmoÛ6þî_ÁosTš÷"(°õe°~Y]C;D'\dQ%;þ+úö²eJaÒ+ÒV±ywÏ=w÷
+x>þëEJ§ãÿQ
+Itxhùbýãx.œtz.LŒðtÎ÷²$IÉ/xHc|º[|¢
+ÕOý?OÊE
+yŸ1õýÑÃ©8ìàíýâÓpú1q@Iø4Qê{þÿÂN<ùÓ<P<Ly' qäå4}¢?ôèwÓøyµhæenÀ÷P JVåB7Óù[JðéÕz±Œ8ß²V°«¿|FV-Þ¬¹O·hÏÏóåeMº¡ªEÓðîì8¹a[NJŸ5/	³\Ð8ôòÄÏ'.Îßè-+
+Ž²ÀéÌh
+¹Û7
+Qmxi¹ÓÈó|îb÷«m÷¥U¿©¿ZÆQcÆ÷Èe­DÉAwrÇ'ÎdÓ	Y+Ò±ë®n'Éáó3"kN
+VU ¯`
+¿í\Ÿ&¬._ét=gÔ(<E]ÝpÑ£ªç°v3M=J5ÅÁ=~._ÛŽP_ç3)-@l¹Ê
+£³õúÁ£Z®^tbË«œMÅQÿÌeº#Ÿe_qõ|P¢ŸÖ$¯YÇ®BY$ùCö-ù©Ü²õr7Î|tšØoŒûé`{²=ÙÁöYr 
+adc#(ëÈNT
+\íIÓã©ªSdÝÊ
+ÎihkîiXN,ø#e!û
+šnMRœâ-YËÖüb÷ÕçåîF7úó=Ù j-;r[Ë
+¹Á_ÀÅ$
+Í¡ãäZWñŸc^wÀ êB¶-ÊUíáþÕ­h<²Ò:2@Ÿâ€i%FyÃ:Q±6y]>×I8cJÇà¥äªþvÈaÓ»}gºloÁš"rM>Ê¶lÐ@Ê9C±yÑšv(Ò Š+àÖíaøw¢CLR	ÕélËDeéÀÀÂ®žMŠK|æ
+åô{ ò«·L
+­r]±ÍRJSUôwha@CŒš=zöÎhrÁ Ô
+FtD'YßIÃ?äfO®9xz+­£õãÜTBÛ.t?äº9Nr£÷
+K YÑ)^ñ£widÇÉÔD/®í®o!žåfœ°å^æÓtk.íÝ³Ï0±9G/K¡í;~×œ0rê2	³üA±:4z=@×IÀ_tÛÚ0i
+
+¡
+dkUÇÏËJrcNåDZ+»æŒäg=qì	vT'Ó3eK4n
+YE3ËiÖãvFŸ:Ãn\Õ&ïã|¢}ÖâØ$ù»çš6jXÕ
++ãôzñÝ33!³ºàz ŸzRí;B¢onXN¶ã{Ùóvc
+æ0JÃhfDïS6';Â%Í§ÑÜrÎ*®øá
+8²m€V5².ähç="&œw¯ÕÎÌÕCšá€[:Â(8ÕcSh
+òÇ«£Nµ€leSÊ]­w¹ð±ÇzÀEªdC`®ŽÔ×±SÚm9öŸÑáwLKµ);$dZwçžTÓ|âd^y¿g±ÐÄÙÌ¯«q«Æ8éÎÌkpÿcpÚ+É`nüûu°6~äÚÀ§Ü¢±Z.­÷VtZÖõ"Z÷-<a?áÎ&Ö{Ôùã¯o~£5q8âw;ÎíbkAÓ ž×é¢a-Ûš³AKü8ÙÍGÔÉ>¥šóž°Z>lÀ±}AB¯aw%ÀÒ+J®íÖ€Ÿ.ùìÚU
+9Ãá)l¯"®eUÉ
+bØË&x7	$|
+ÉESR\osÇq£áòB÷¹ód<
+
+šëLZÎtÆÖ±«¢Šé)­å«9¡#?Á.ëÀÎíü;±r+Åý>é:Y9ŸÇ£0ÿI
+È
+endstream
+endobj
+
+57 0 obj
+1533
+endobj
+
+10 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 56 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+58 0 obj
+<</Length 59 0 R
+/Filter /FlateDecode>>
+stream
+xWÛnÛ8}÷W:
+CÔÍêŠ)Ò­Ó
+ÐÝ­>4h[tã¿ß!u1å0û°(ÒÌ3g.vàÚÏBÚ]
+¿œ`Õ]^QñÙîõhQìº¡kØÑð£·sð*Bô.B
+f?(Šê`øéü
+ÌBàÂ `\?Âa€â¹#û6ûÑÛBD
+^)GBìûÑÞÊu
+
+
+£1ÄûÍ)UB |¡M2S,o=DîÙf7_-cÉ¯/ÐæÙzczQ4zÎ¯²
+¬­«ð¡Õ.ô
+»µv\e4kx××åüºþy÷Áæâ#òòùÐ®RÉ*Û3¢FFœE÷óoëOëß7(.ZÙÜÏ_ß_ Û¯ÿD/ËïÄãKÍ­a
+,ò#Ê÷Ð÷?Ö_×¶èÄÐHpÙ:HÆ¯»ž€Üÿ4àþ*&¯î/lh!çÍ_:?< ŒEÆ_Ö ~4÷yÕüÐTwù|#øAøÔÐ
+Ò÷§
+­ÛfÚÀÇ([êbgåRe»Œ¥.¿Û¡cÑ¢©µ5GMÊQ­Ž¬Í%ÚÚMª$¢.3vløcLô=ÝU+2ÌØ<W81g"5üäš¥*[ÄjT·qúÆçê¬é4ÞòL21Ÿ6«0:ó=SAQœÚ#?&¢h÷Ú(Ž	9Ñå ²m¹39©7šËžVêY`gHÛò¬Æú{Ÿ#7ZJÐS6ë»/04ëD4òXÅ|ÒŸÄ¡ÝnõHÉ3Üfm¢b·ãE;Î¶â}Æ
+@`}eYq ¶IpRVºÔeQ7 TÅã&;Z	9ÞHhŸ«\»m³bJ¶ç}O¹ÞCPµ¢ä8
+í·Þ
+`²çÉ%
+Q)C&mš.
+f#jÇ{ ÎPF]4Èc`~P-ònn(ÎDüKÈýéY#ð
+îXñWÎ	?.dä1ú,cÞEäÉBûë`ËÞ ÁvdÀ..rè%ÅHÓT0Þ
+šÆ©,9«&=tBÓ{DÞ D}7¥Cg°  Yu U0RÓ[§=Àçåý&Tu@UâA°L	ýKÄ[+§ZñK²bÂëœPcµÆtÏm¹kTzÐl^'°|@QH)+ÑØC|`QU¶³_ X_ãS4p°bS£ÊPìáŒ
+.a*Z±Zs×¢³=â2Îzè§Ftíÿs%z3¡êÖùªÑŽB;FëŽh³DõuÅAsþ Ó(»VÆjahHpèEÓ0çË&ÿ§fx?ìð&Ò3çé®Yè®Úõc2v,±j$­bKF§56×;WïšÉru0
+­ÞÄÔB}¥>v¹O°v<
+ÞJëCnÃP
+žjmëU©°<FTýo H)ÛâQ·Y#2èÅán·­ØÑp
+NAí¹Nnt¡&P/:è¶Æ¬àÓº&
+Ú'úÄc°ìY»FAn!!eØj6ÎŠæ~~HEª
+Ðu{Z¢«^«) ® Ï%¬¹Á	¥I±BSó)ÔY€Z[+fŸQá(BD	5JzöÒn²ÞŸžzWŠ%tùoèÝõkÔ=:Ó'èj	Ø(|©$²5Ñ:ÂFIxN%tø?*Ð)ü§Jð-
+pšÁùL% ñT&cuöÌ4xù¢Óï@øì_oU Ç²
+Šyubåáq­ß|¹|ÿþ0 7
+endstream
+endobj
+
+59 0 obj
+1415
+endobj
+
+11 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 58 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+60 0 obj
+<</Length 61 0 R
+/Filter /FlateDecode>>
+stream
+x¥]oÚ:ÇïùÏE/ØÔãÅó6©J«J U+Ül=B
+1)$È1eÝ§v¶ï³Çy9]µJ-nðÿççÝ¡ÄÁXôV­OîÕÂçõBÉÞüu»:iHØn£CBßàO·œO0³¢ÿ:ÿq/â$Ä¯Á÷]ÂÍ³z*
+Òî{ªÝ6õâºA)	Âõ"DžÍþÑáçþÎ
+râÌPïeFpÎÿF8DFŒáz.a?
+f`A@Œæøeð ŸÔê0BµâÝx× NH|7
+a
+÷LcŒ¹Š@ëïÆó^&²GQLc9O29ýgò¿ôc1§p?pIºÌPÞ\ûhÆp%JÖ:É³·ûZß#rBkmRÈ¬Hb	ÕÑdœ\ãÐKi~WæyK:Ê*%lRIXåñ&
+õ(¬±ö(ÄÃg	±Ê×ëòPbóÌáäNå:×OkiuÄãši@p
+Æþùfb%ÏáLËÕ:ºú.¬|×iùýTÿ·¯-o§Ž8­ZíèýÕd8£»áåx0œ¿ý8/?Ü&Ò4ßNsµ*yÅ	 ãïã#­MVø+ú«­ócÇr]l¡pÿÅ¯¯lâGµw'SKUX3Æ©GÜ;«Sfò±§e8šçð
+h¿ÃCÿ^«$[< tÙ2tX­
+8BÊÊØ·ûÄÅÓQ?cIY^ùŒj§lZi¡Ägbµ
+ßfUuáQ€	Im)ÚõÌ%Ñ³ŸJëFÙõÂúØ·ÛLË
+TèÜ
+:Pnk9Kæ	öô#&
+ÇNí¯Õ:ŒE~¿±ÍOEÑN~º­*`ÊçVNúÔièYŸIc
+UV
+Ÿ®°F÷XMž]çVµëïÔVíhpu;ÙÅ?#®GJ®¬rŒeþ_~=­%
+¯]ûNØ®Ä×dµYÁ6õ²©ïzé¥ÐPO{ë¬
+öøÔRîEŽŸ¬û0òÞ©º>¢ûµïk;Ap\Ûïò<"ÃÚŸÃSŸìcŽ
+uœÑ öªÛjæ¶¡÷ETš
+¹;1:æöiçxÆeHvm]HmÎ«ŽæbþÛ¡OSø²)4,GŒa+TÀ{mRà=×ì+·H¥rUöÒòF³ÚŒüä ¢
+endstream
+endobj
+
+61 0 obj
+958
+endobj
+
+12 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 60 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+62 0 obj
+<</Length 63 0 R
+/Filter /FlateDecode>>
+stream
+x¥UMsÚ0œûWì!§Q,YòGoÍŽéôÔÎB'#°êÛcË0äWöÒÿÓõ6ÂÁÈbõöí{»?°°|Ö®vßÜÚ
+Ç»E¡¬øCzÆ1xCCÏóá+.|»ëV¯(Á§ó
+,*|âºOû!	0XXï`¶îÁzjÃß 
+¡	!<NøE,BÞÒç¡KD³`]óÏ$Á9m±\ÉÃKH8¬Uâr®Ç8¢õï¹]S\^
+VÅ€Øÿü}$pVþ-ÄÝÄ¢ ÖC²Àe0¬z8oï) ÍMbËŸT,«Ä<¯²šJÔ5L~Y_&5ã>áÜs nïÙÁñß0µL¡ÓÅ.u	$0«w ÊAÂ\Fê^á÷œÐéñíÙ:Fd4V8Dô\JIm3³TEWN¹_/Ð6
+aŒ®ç.Ë%S,è[¥2`2ºDNëÇ(37èQm­°ä¹L!¯ÈŽáÔI ºÁ2sEà{ýÓF¿ÍªQdæ
+Èå2«(ýc`Š@ÎÙ­²µj0ÛP;a-
+`°QQEààš¥Ù
+éèP:Ü=:öF_|²Y"EiºQãQQ/GgÏû4¶44«,n¡ÆkYB€×û¥íþZâ»aÐWÆíÉç,=HÍÞp\öÕyÕ}VåŒÐ¹ÑYúq2ÇË¿ïèÇ²)²ÔnkÖ+±n;!+"ìô0ÒeÈm=1mIl6µ®¬Ç*ÚÄÑlbO»Q+£ }+ý(2m®FkpQÃSM±'ÝÔŸêÞR¹RÓëX sÂ
+¹yzºxîÝ<çy¡Ôÿô`QÈY7j®cºt¡æ&Ùe¢áÞäoŽÁ	5
+Jª ¶VÅ:ÅLf×ë­b$_æ£.Q
+ÿ¶KpiÐMjI7peÔ
+»Ätï£ÀßÛC|©_öLþ÷(w4
+endstream
+endobj
+
+63 0 obj
+786
+endobj
+
+13 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 62 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+64 0 obj
+<</Length 65 0 R
+/Filter /FlateDecode>>
+stream
+x¥QËNÃ0Œû+|àÐrØÆo#rE"ZTEÓµMäž þ
+/IZDöhwfv=aÄná
+n©mŽìw€Œ>ñøIdöÕÚÐÕw²àÀ1gòBZTmªµ @òÅM§nOdÑ±Ç
+2 Z0-JÁ€&Ò¿YK(|ÜÿÀXZð+¿XÜfçŒZ€fÁÿ03Êú^VÉŸ.Vo¹ÒìÜgšd	ï
+µ`­à(Í5eÉYvçÚµ¯PÕïZ­3Ô
+	¬×N[WÐêÐV
+£aëhYí
+-\wm{_õžË1vÊzW8ÅÚ#5ÝžÑI1ËÓ€¶qëª¬âžÞ«É}ŸwÁy{¡²
+ä }ôušÃGãF€dÔÂ`aô.'W§q«CŒÓÁéÂ.¶C
+endstream
+endobj
+
+65 0 obj
+339
+endobj
+
+14 0 obj
+<</Type /Page
+/Parent 2 0 R
+/MediaBox [0 0 612 792]
+/Contents 64 0 R
+/Resources <</Font <</F1 35 0 R
+/F2 36 0 R
+/F3 37 0 R
+/F4 38 0 R
+/F5 39 0 R
+/F6 40 0 R>>
+/ProcSet [/PDF /Text]>>>>
+endobj
+
+35 0 obj
+<</Type /Font
+/Subtype /TrueType
+/BaseFont /Arial,Bold
+/Encoding /MacRomanEncoding
+/FirstChar 32
+/LastChar 255
+/Widths [277 333 474 556 556 889 722 237 333 333 389 583 277 333 277 277 556 556 556 556 556 556 556 556 556 556 333 333 583 583 583 610 975 722 722 722 722 666 610 777 722 277 556 722 610 833 722 777 666 777 722 666 610 722 666 943 666 666 610 333 277 333 583 556 333 556 610 556 610 556 333 610 610 277 277 556 277 889 610 610 610 610 389 556 333 610 556 777 556 556 500 389 279 389 583 0 722 722 722 666 722 777 722 556 556 556 556 556 556 556 556 556 556 556 277 277 277 277 610 610 610 610 610 610 610 610 610 610 556 399 556 556 556 350 556 610 736 736 1000 333 333 0 1000 777 0 548 0 0 556 576 0 0 0 0 0 370 365 0 889 610 610 333 583 0 556 0 0 556 556 1000 277 722 722 777 1000 943 556 1000 500 500 277 277 548 0 556 666 166 556 333 333 610 610 556 277 277 500 1000 722 666 722 666 666 277 277 277 277 777 777 0 777 722 722 722 277 583 333 552 333 333 333 333 333 333 333]
+/FontDescriptor 66 0 R>>
+endobj
+
+66 0 obj
+<</Type /FontDescriptor
+/FontName /Arial,Bold
+/Ascent 905
+/Descent -211
+/ItalicAngle 0
+/StemV 0
+/CapHeight 0
+/Flags 32
+/FontBBox [-627 -376 2033 1047]
+/FontFile2 67 0 R>>
+endobj
+
+67 0 obj
+<</Length 68 0 R
+/Filter /FlateDecode
+/Length1 35976>>
+stream
+x|	`EŸwUõ}ÌLÏ}$ÌdÉ1¹H&$Há ûð 
+ºYE.qUA/pÑ@8Ê®ëµo?o]]Ù]uÍÊsQï_=}oß÷œLª«ªÏªÿùûWU7Â!u"&¹~úS<ôé,öüBØ1}ñ-¡žO"$ÔÌZ0ûÆGž|qÃgßpë¬»~!k;BÍ·Ì9uÆëþM~îÑìpüVý;B×ªPÏsã-K3öBœ
+îY|ÃüéSŒq
+Bm×BœüÆ©K¥ö³]·
+ÎÝ4õÆ¿k|õ§
+Y0ÿæ[Eèýì=Ÿ`áÌyîp#t{&BÚ
+°#ÚÚ#yôÿü£'Ä°
++Õb5Ø4»ÃéBn×çddÊÊ
+sP$7/_
+èI1T\RZVÞ¯¢2ªúW×ÔXwÅ z}ð¡
+ÃPãÿûùÿû¿á#FúßÏ
+EfÚ2Ù(:%Oõ%cnò=Fsò%P#Jé¿ýèiôG\CšÿŒè<öã~h$bÑwA{QÝ\èjŽ;P.ò ñh$fáZ·''¿@W _¢Gñ
+É=p|züÅšóÇ£èæSÔÜDŽ)h º
+{ÐTô>üŸ
+6ÜîC¿Á·'ÏÃS]èž_
+'_H^@Eh=»;!
+DÐ³ONOÎEY(­#±äûÉOPµ¢ÇÐÓÐŠ>Î@at=ºmÅ~æe(Ý
+GVI3{
+4M@7¡%h
+Ú^ÇÜÂàÎ$<xäDÐŠ¹è\
+É¬
+üMFGÐ«Ð_ú;ÎNfwsúäCÉ
+Æ2~¿ÀUp¿Hü<ùHòÈ(ê
+ÏîD/ ×Ð¢oÈä
+4'ÿqGâï?YN3ï RèmŽvÚº#GÑ³èÐæOè$ú»p
+
+§áMø¢ä
+f;syÅì¯Þ4º=>ÿœ9ž9nÁóð|Œ?O.òùÙ;ÙÙ5N?&G'¿E>@W¢ÛÐ
+ ícš
+@ÿœŸAÿDç°kð
+üîÂ'ñWD"9dY@6'È¯ÑÌ&æ¶
+Â^Ïþý[ÅÝ-L»{_o%'ßÙ±Âý£ sÑÏA*@Ï£wàî Ñ_©üÀýâIø:xÊÍx
+Ÿÿÿ¿
+¿^"óCxê|²èt¹ÜO~oÉÇäïä[crþLóÓÅô0o2±eKÙ~ìvÎTpÃ¹qÜÜSÜÜŸÁ/à?îVÿ(JüÙ@Æ
+£ËèÙAnJ<
+¹? <x
+(ú Å'ÑYàB q>Ž»7â&Ü¯Á×âøŒÿoÅÛñ£øèôÐöLÆ©d&YIV{Èø
+%¯÷É	Ò-÷2&ÆôcF2ÉÌMÐ[åÌJ ì&fóósùé®yÙ,v{û »=ÀŸÅ]ÉÝ¿G¹ç¹ãÜ[ÜîOø Éñóø'ù¿
+ŒÐ_hÖ
+ï
+ÿàL\-]j-t0ì!.vî
+
+AÌ"ô<|ZñOTÏÀ+=ms?ë€Wò:Û×ßEUøwhO0®ìIŽDN²¿%W ÷p;ö³»ž×I=Öh#y< €L 2DŒ/E÷áëñÍè)Üàež¯@ï3¯DuÉG	%<AÐôsvºî¶ž}Ÿ0
+f-ìí`zÐfàèÓèü+ôæ_ucÀM+³
+äý.D­^èÙ
+ÐG?Xø7ÐêQj~{:ŸG_pGA¢%=mÌefÿ¬N¡'Aïæ á 1
+:­].-© ­nAÐŽ¬ÞŠdWòÁäÉ[óÑïáÚp1þïè+êÐ«ðÛ>Àw
+ÿßØþþè8úûp
+® }èås¹=Üî7Üø~@íh;Hô_AeèÁtôú}Eà£8Ž·Ú>Ý@Zch(  ³`Ç€{r3Üå Þ ÏÇ@7ÎžýÀ{¡GÓáù"Ü§	è<ÎÞ
+ŒwÃ`µÐß¡ßV\Cnçép§Í`µC>Bµf»Á.4à	p¯ïÐ5h<¡?jÁûPcòXªÑšù w.ÖÐëÚAC­(j¹¿aÑÉ29>&	ûw÷Ê@Wàh
+
+ú@n<UW¡b]×ë]Q7p@mMuUŒ²¢_yYiIq¬š° ?É	²³¿Ïëq»»f³ZTEDçX`T<,ÒØ
+ê¶w±ÑÈ%Ž
+
+;Š^²£œ+»/?§+ÔnºüL
+Îõ/gê©3õgb-TêJCÃ"¡®?4DB=xÒØPŸ§!Òêê5ËÍfy£Y¶@9
+BÃ|sB]ž=4¬«qñuÃÚàvûyhdèL¹€í(*PêòFìÃÞAØ,ï°û-Ðš®@€aX?Ò@[ÐÅä
+:£«eìÄa
+ápkIq
+:=2­EtÙbæ)hšù.~h`>&4öÝ
+ÚW||Ýú
+
+Mk©3"3Š^;±ÚJaÁsºŒ·òýT
+;N\}éÑfÝ0ßÜ­®[·:Ôu|ìÄKé¶µî×ŒÆöuðèõ@ÄŠq!x¹«ubŸ
+¢=¡œJõofdÝÓ>/Ô%EDæ¬×¬	¬ëBWÝÞèG'Q`XhÝÕ#á®úHëÔÌ}.Žîª[»ýzÈùâ}=EØ}V[º Z.-ÌŒxÌ,§ÓRÓU)i"#A ºBÓCÐèS
+ÝÌ¬AëŠ×Àið×áª®À¹]ÒÐöuÚ º^ßÅåiÐºoH@€÷«Ë÷LMïáóŽo-R9¹(jpŒ¯ÜuQO¡ÌzUIñâ
+Ò?²@AäC-@Û©­Êüá0eðÝ=:®Î±Sõ±ée±Ö.ÒN
+ï;â
+Otö
+¹xy{$ù	ëÝ]bôâ¿Mó8ÍÐ
+=ÿÃá©ãMã"Mc'M
+[×ŠmÓÕÕRÇk.
+KºC'2$]"yòÚ'ÓÊDµÍÞê]¥¹»Žö©m«
+ÿÛkzñzgèUföÓeéVv
+]^xYý²Ö©ëh/%MWOZ·NŸìX# uë#¡ÆuíëŠö$;§EBZdÝ²ì^·`X{C{GïÎèj\ß
+ $ Ôæà
+V@ClðB©×c
+Ék`äyÎ Ìs8$ >äiçêu£µ³uÍ:Teílúía{
+l08ë!æøC?¢{>¡1YÉúørBùš8ôµ¬óx
+¹Ù
+nŒc²g7;Ž[äX
+ZT²»Ë±:tWÊG=Ñ(®vT4âá%ã9q±ãçâFÖì¥^ÎÇ?&¥%ùQ»CÔ'£ À+a9ÇfÕBÙ
+Lòz
+
+KKìÍ&C éd &ùÑpÀ«¬?ž€%g
+Å )
+ïvxâš'yüP +v
+ Ø
+EC»|GÁQðÈÃ^É²ÄfÅÖ
+
+Ö4
+j:YŠÕkc4FûJÓ}as_ž><&Ì¿{È€Ã²ÀIoø°¯äè%¢ÿyÿ~2Å?ßOlþlœYáßà'þXUW,ñ
+Ö7¬XlkX{ÈFÝbY
++==Z\Ø(Ï
+êìÓrC¹$×<u»%_³I°K².
+Ï
+e,z\[Ü$eA
+ÔUK<è/ëÁÍû^ôÅ©±¶æÞØÙ¶ØÖÎu4÷Æ:ÎövtÄìÚ
+Õ%Ú(Û¿êmÅŽlÎ¶ª?õU/­lwxkiB©Bí%É[ËpZµþ¬ÝÜ ¶6ÜgF«âý««*Ý
+WæÛ=à¿yÁ
+©Fóó<
+'Ï»]
+¯³ÿªx4ñÒá7ÞŽã¹@Â;õÌ3úíŠ_ÈÂ6±ÿÀF}ðŠŠëÆLú ?Vó·GÿÄ>}º³{sxqqëà7
+=x>óÙCî¿Ël¹"×öU3ÖùMC§šNJ~ÈíâÞhµÕÇúRÅ.ìþ²êìêòrwYå"ô«Ü]¥ªÄò?ëä1EÁhžŠüŸH/óY@ÉÌ¿ßÊÊÊkj,²)®É0l4Ä_S	3~TU8Õ«ó
+VP`@t»æ)óO^©[äâí6-[ÛšíÔX­gé¶í69[.Ù_Û|»É²¶æD¢­XÑ;ZûÕ×7÷Ö÷Úœµ@oŽ¶ZëH4¢Sy>Ê7	o¿²Â$º³µçWSjGrô^Æd@eEÿjn×e7·ŸºÎøÇºëÜÝtå{¿ûÍûó
+ùÜ­	Ý^yõØaC¯Ô§¶/1ä¶Ý\q}ËÛl¿ç/ãZq××·îiüS=`õ²¢â¹:`°ÞÿÊ¡ÅñQÆ~ëFLŸ9^ÎàZfþwLÔ Ý³û#<728cî,ALu
+ÆÎÿ
+o
+ÚGD œ¯­íWv8ª°ÙT
++Jáöóç¯éÓõÆX2x­¡FÝZ`ÛÍQÂHÒC<s0l¹O¥ªÛCl9šSÙÜmâzjÛzg{µ^ žV§IqDIÄ«$Äírx=dæì>aåñµ³¯šcOãoŸ<fŒe\óÇ'·Ï¢-
+-ÑÍÔ}ù$_MfË[À?i$°lðïÐhXa³MÄrÛUÚÇŒ¡Ž5œS7Æ9©ŠÒãp»Â×0 sÖÚç·ì
+ÒôŽ1vÿoÎ²èøWžìFÖù·Ÿ6Î?Ò,2à'06ëJ¢ÂË»þA\£ÈòB
+rm(Ìx9Xy¿:{qä§œègØzOéî$ñB~ÿþÕõØ_ŽhRõød
+ö¿vÛ=B·dNO7¯&sÉNÐŒ
+=\u`d5è¡Ære8Í|üì7ÐgjkÖ>kCeœmðð8Ió~ã4œÛœ°yZÏ \ÝMjL¢ŽœØZS@úWÂõ÷Òa"óêd"y.0šFóGÆE
+Àâ¿ Çü
+îr¯Ù³Í nç¡×uõu«¹ÒØ2í¥~å®ÄŸþ
+cûêmÓ£Ð(w
+Äi.]O~Fî[²=ž°{
+¹
+rÝaQâ0R%ô,Â€M·pÍfClË²~ù(Þ
+QgªéuÍÔû=ÛÖ[K9
+¶óBUÿÜêJ&jÞöÖMb#%s_[E[P	ñŸ
+-â>å ïPàHÆëì+Ÿ7}oúßC3f
+Nðogï÷íawe| 
+øêÀvšošh@Ìõåús'ÊN`×ø
+Ìx0óÁàÌ=AÑZ0ì\
+\Ü|?(©³ñžÜñ ÑT[2Pêë@Â>'K
+é&Xµõà	z$[-SJ
+ŠºËÉI'Àä&²m'Ž%ÄõNÊ-5¥Ôîš«kŠ2uàkëš
+ÇöÊX`±#(<Ÿß^KÛ°ßffºU«eE­íÛkcæ_+UŠ±¡2!'kjjZ1x¢6l÷wT÷ï3|B^ÿÜŽUäY^`ÕùÚÎ¯~0³uâ
+ÑøÜÅ?8?Œ¹Ò87Ü9ãÇû°ô§}õ×¿næŒe~þúÏLï6ølKr¢9yÍ N¢·ô²{<÷x_s3?ËŒ;ìb~Åív
+br\ú>ö
+ËÄb¯ÓÎ¶hªÜsuI·l°{ º-ÛYæ$NJ<ç®Aj 5 ]Ðõ
+ØÍîÊ·t©ÇÂªG;±"{CöìœÙÏgsÙ'
+crqn æ9á]O Q¥{S€Œ­×^[Ö&7ÝÐj/5«TÇ
+µ© &
+µ9ÁGs0©&T{.o©¬ ¡ž@ý9ää6cÍ²pì5K^Õ¿){áÒ#GÌRDÆ¿œõe³ßYŸÅøìíWð]á97­\0ïv÷§ÌÜkFMÑ^|×É+oXóÂÍÏÝõqæSÐ *Û ôœŠ×ª!K­€úÕ:Nœ^ý«Ê÷Z0ÏzØ<¶À2Â2Ù²ÛrØò²EÂDD*o8Y±HU-
+üÞÙÅÉ!*ka, [[ÞÊ³ž  
+B, 
+<ñ ·AÆ2eCvÏ°Õ¿õ(Ÿ05öT8fÐ[ªŽõ =Q
+kBfFM¶Ãf³Q±BÆ CWáJ{¥;bÇvL'$·uèqÆØóÏ1]žî;ã
+¿5 Ák Xe£&/Õ3Ìó5¬,íeá£8ÄsÛ+þá)ÓgQž^wÕFHÙOwûkÔb?c¡ù
+Šì!A{À|hÞÙtÙ¢ÊV
+Þ^¡.!S~«±Éöœu!ÃEr{5ú€OõjçL¿ Ï£ÝFŠg œÄ;Œz|H5ÃPÓ~bÆ8ª9SÐnöJ0<ºN×x0 dµÇIOò€®@¡Î¶PÁ:]â«¡á<Ï2I):wG`¿ÆusoÀšj 1œÔÆbž­#WÑ1{å'ÓJíÆ¿ÁÝÆßÀC~I©2
+ßv°Â€÷(=Ær$"Ô³c ÷Õ<÷SÆÀyÕ$åÎx J=øEh`ÄA
+ÎõB­ã"Q gñI3 iÃhÀHõÛÌ&¡ÿ³tÍF°Dü€
+0<í?Gûo¡ýÇø<ÃT³2®³Kîq<%±c×Th£ÝoÖ@^ä«\Jj 
+»§ß$0ÿØ
+3°ê~£ùôm
+èÝ*î5ÎõNœ×¬Î8§iø ß ¿ÎwÏ
+<%ðÇtvº`&eÕdÚzÈœûµm[nÑs8SÎÄÙBK€IaÍØÛÂ5è>4GÍl
+µeœÉ®úÄÙSZâ³¶I8³œ
+^ÿÅf³ŠÍæ *ù:¯:çË»sþõ3CÁ}³æ>ÝKû¿­Zœ}ç«Ùý^µè¶e¡îO¶íÚ0já0³uÏ T
+ABò.U×ÆùØftUPçuØ@íÞÎc°)DElW ©5š«Wç¡yd&3#Î?gl£xL'#K+H ÈÆ²À2Çñ¢¬dú%ËyaxVêÁÏéV^ 
+Ëb$ª^o |êT]ÉÆæ4R' 
+Î"[ÂåR§D€£$±pÄáW®ÞüçÚ: Ôó%FÙ Iå¡Ÿ®¹€âŒXÌ6«œŽºÔG3B¶Õ/ÌÁM]ÊžŠ®¬±Àë2Ic¿ÈÊGæÂ>­©I;Ó»
+C ÌqØÉ0ÜóÆo:n5^&qmÑë/ãf£;za
+	%NFmJOJ;7£zý"<ÇºŽè3ö
+ËJa·Äó<l÷7)wïu·ÛÉÉs8Å+#¿ïä	ßT¿WÅ*
+
+y[è²T/m)m/]PÚYº±tg©*-/%¥®
+9ËÁö»»KúëZ	P¶s±+4G:h¢N°Ãîdçþ`­Â Í:÷9)Âh
+L[r)RÙTûä
+ºÊpE¡Òiž €\@LEu*ŽF@dÓhd3õÌS«'Í²jcÛ#G\ðâ¯®ŒŠiTñ[{°cglÈ8ýÖ×¹£Ák2ûéXþs+f
+ë°}Ùø5']3ŒaŒÄ%K%µmôkš5<Í]H7ÞÕWIk]k=;ÐVþé]æ]å[FÊ
+ÔK¡«Ð³[$­âDÁ)xœN¯·1yPÀÕã1ønôó;EÀWit%µâv9Ò-[ Çt¯¯­ºÕ·6M±á16lÓÝŸ8 À=ÇQ"3¶¯­Ð×nIp <gºów
+Ø&dåàVyÝËÓlé æ Ú³x*FsZH
+µ á8]Cá×ãå¢Ô(Ø5
+OØz=ÄøÃWÆGÆ|cË3*?XüØï_Ý¹xÉ|æŒOÂ7áûw\×ÕžpåÆÆ_mŠ¶ÿ>Ï© XŽze(÷pïLvŠÊykœ#<­9
+®ÖÛ?cuÆÜf
+Ë¶S¡t:òlèÏß+`!-ŽSº³3Cáò0	Û
+ Z¹F4*¡ÿV/
+ íeŠBäõÁ
+O
+"Tj@î#ÁÃí?ïi/©Õ|çŽÇïào¯
+1¥®îqrG3£/§ÿÏÁ;wNo*Êf_ŒPeuLøÝ=f9¬TBîzª ú"Ç
+b
+ïÈæp9·à'1l
+ÁDò$
+|CFÈHÁJ d)·è X))ð
+©öÈä_]óÙº³uÿRq MÁZpi TÜeJEêÌšÍ
+N§ûÙú_SÉ
+=o<ûÑñ
+Ž~Ž~%Ž^Bõzh=Ïå	!±\|^üDdËÄ"Eêí¯çÇÍžG!¥\!Êåíÿ»ö·¥Â°D6þ¿kß@ÉÄŽmOOl¢ºwt/ö­±&«)k°X\¬Þ%®Tïò®Ìx/áð:2
+ìŸ@A8BÌ^-MRæ±?coóÝ8d=€œbyYû£vZ³2|*
+šW KØYÂKªoŠ1Nì€Êæ€ÊVä)±1ØüS`wŸcÉ
+èrNyÉñçïqßèUºðò
+)
+íŒv¶·Ãô)åÝ£AB]¢#Vg;Sq€,@}F¥*ClZÝU1õdy±ãàgÆ§¹çm@ÅÆÙOuŸøéçÏµ=;d|èŽö<ûOñ)#?}œúeçŸ1~4~
+?
+ý€¢ÈÏÇô<ådHr
+ëØË``Ç+QéäÄÿÕ<r·£[Z,íf¥ÓBššîÏZböq¥âºèr\x®-=Úm:HØPòÊòÊ€ ÍþE^ûDââo3. 
+žÀ8x;xþ¡ü<±úŽ
+Äã ôAóM=è®SlwŒ;gæzœËGÎµpÜIËæÚ¹ÜíäÀpù #Ô
+À
+§ævêMš±è&¶_3Š»Rou,ÖÒö­ÇÜÑ¡
+ö<Á=8t
+
+hèœY°úHd¹@KïwäRÅ0è}é[œv?ý=É=óãÈïšô
+ð~è£Ö
+Qb.ëÔ¥Ìq94``\@ÛÎõÇ3Ka/lxIÿ&}%e'Éd5)[b6$É³É
+vŠ4O^B²K{äÒQùôìÙÁnvÈ/K¯É$'Ø÷¥äÓäsöSéKÙ²DZ*ßIÖ³wJëåDšÌ$óØÙÒ
+y1¹HÛ 5É××HeÁ'Yãd ÊõVŒ$Én`œõz	 ¹+JRE*$,W0DFe	Qe {mÎ
+XºéôQRcòzr[ÇÞqWÇ¹
+AVX<¶HsL	)*é!5º«ÃHPE6E8pWíl
+¯ûh^Kt$:ê>:"; 
+ŸhŠì:ŒµºQã@ÅäÉ}Jb·6óÏuPVb
+6ã6lßÅ2ðsF¯ñ1Fçc>ÿ¡œãÇå4·~FšÆ»t«Äð¢ñ¬$Xº
+J=ÕÚkëEÐ!ŠB] 2"!#¹TK;ÌÒ³üæÙÝº_WZv
+Y t*d§r\!)«-JéJæàÏžqq©â2Í/Ñl· Û}Ê
+5S'ÌAZ]J;êW>ÔÔóÎCJØ©T
+Ÿ"PÇÁc<L£3l#s8â~ñÃ¿ÄŒ!~(2!ŠL3Å1â/
+âNf¯ØÅ</*© ¡²*NôJ3hð­¬"NBt#žª`Ï]
+ÆÉÕ°1ÏnÌ
+A
+6"aŒB1ÉJa4Ñ
+kÉAr¡¶	O	¿'ÏÉiá{¢äa°TX#<Mxªïc}šÅ­Èä0ÕWlßCd"vLìÆ0ïüÐÈ<w¡âVð]§ÁwÙ ò{T¿
+Û"nU·ZYVÑ&øò}K¥%a}©{»V\«®²ÞåXëZã^ã]ã[Pp8àv\; 8K,¿D`<ù{edM¥<
+*êÁöà`gpgÏIPËß0
+%ËM^®ïÎ\þÛîÉDIm&J2G6@; gÇ«iPX»
+}#T|ëÐ_Ï^Ûð]ÆrãqÄXû}¶oßß>>|ø$y÷äÖûcmÆCÆ| sŸ7Éä
+ó?R:Ptt
+€ÒaÇsG\G|ÌpÏæÞçÃg±ZQFñ
+
+ÿü<ÙÁòtÿž f»Ô¢f^þ.b¿4Ðø	ÿÃ ÜŠHÄO kéøá~ü'lœjùi[FÏ{í
+G÷.
+zÝªÜQOøãœ«{æÚÝ?²/í¥Ó·Ì±Èð`êMÁ!7
+£óú
+µ¶¶kyÊ<uŽÛº3rÈzByœ¢GîomŽ6ÚQì.«ËæÒú[ûÛÛYoÕÞ¥ÒRÿâàiU<.IµYÇYYWZï³>få¬!ê²XTê¶x=yNÍ
+Û];]ÄåB¡0%ÎD+
+qóE³Ë»ù;ù.þ8ÿ&Ïò«Dp(R
+!°ûRªåôþÕLYHÇlŠÍû	¢Ú
+Ýf]Šœíé
+@KG%hEjÎÑãñ:ÃL)Dìöš
+¡Øü¿¿×ùâíËæu¿¿ðêëfÕýéœyucFä
+8Í
+
+óú
+Oü1³fÕSÆ_qýS­áÄÌèÜCFMV9êùF%?c¿Ý)Æ]úGì=ÁC/³X¹!°rûb3¹·ðK-·| ŸQ[åñÖñ9­9ê,ÇìðÜÙÅK«Ãª#BœcVvæúL >6glä
+"lGNGäç9?ü%ç/>&Yrsr#µx€In²4äÌ³ÌÜj¹-g­e]Î.y·åÉ
+§$K>øe¿Å#äDdœ|º?ïÃó};|ÄwÌD`
+T 8£ÄÅ ¥PsµàvŒïÄ]ø8ñ?X=P«±-)|_'œØ«;œqo
+fçïÔº ÒiÂ_ÛSôŒùŠq÷!œŠÕžÍé³±
+4Î£3Ò±S©|aì8±é2!XÐ##8èñf:ÿÛ~gm2šœ¶ßAkoê6G­%äšÍd£û>×­*ì³ÔÊ>µ±KÿZûB­CÛùæ5]µõ°ŠäÐšsv¬Þ°é+ãGþÑŸzÅ×¿Â.ìÎeË~>²¬žwœ±h}=o|iŒ?ÎÜŽæÖ±ñÒn}fÁog}óº¥czUNm<¯lÖÇî^þÑõSù)sÄ
+
+ZšGÊ€r¶kHÒFIà1GòXHŒÞ »úI\¢ËŒÂåhÕšÚkY@:ÉFÂ¿x:Mõ±÷ z]j6Ãf6JÛ:ÊÑ!iÑàOfö
+c4ûâùó?¢3nàr¡U~ŽN¯DA40Òpqž$\#MÐ6k[ì[ÝÛ=»µÃ?º?åÏñEU!(òª,oP,dBóöfAFg	egìÌ8Áf`À°!¹ÿžñSEü[hÞk*»	ia;°Äcª.ø4ÍJ"94(®º(Î
+·/ïàòxæí»àä>;V3éÆÙabãü[§n¿ü
+LeŒúÇ£¯»E:jMžzu`@üžø>~|À~Àq4.å¶àÍäv+·C€ðe"Šíâ,ø/DQ~$Î_\d	aäæòÌÅ?ŠLÓ
+¢ð&ÜQ2± h º
+W°ì'ìIe{°¢Ë+Nææ$ hsJ­hñ(V¡c}åc¿pÉXhPÛÙ¶¶¯÷"ìœ
+
+ŠŠrÚP4c
+ù0Qgñ`|3$þÉ
+ýñ·ìf  $Üm"ùÇtGñ!¥Ra
+Âð@¡nÈKòýþ*Àe§ueúa£öÕ­ÃØ×yq6À5¯[*Dyð
+|ZýNú^þNå^á^_Q?Dï_ý}*IO±qOÉOšÏ²ÝÜ³òAõUV*esž29€ngïå¶Ë÷«â
+Ë
+zåÝ<Àôd
+~-ÔÐ%l!j¬Pd¢È/!Qr¢Ä*ªô
+(
+Ìª'+$ò¢ piNÐ
+<š_ ÷
+\®Ë!þrL/£á
+TÕPj¡ßÒÇ¿9Ñð%¢Í×7bþõÍØkSsFŸ7_ÊŽË3°Z&úóÆxtÓA¹én:M®âÆ£žìc¬ÕÆÁEÆÆËÆGÆÇ 	væë.ñcù\Ñï þF€T
+1Ë°
+#°
+Ëp©r`ÁÜâ
+J0+ îXÌ@óE<ÐÏlo 4DEò³Ä÷æ÷  ÿ0Ç{_tì³º%J+:oå×NA8ÊÀ6¥	tîTj2ÍnRÆnDpîûÌ ºÕ"Ç ÙfCâIÆq+Ÿ
+O|Jæ2cÏ¿N< ²í[{Ì^ltŒÿa«v*ŽŸ#³÷š(OÈöóÜqÍð#ORãR 
+©(l9	-L;(ëU85#â/<ÝgÌ Ñ!90ZU÷¹íSÑTDfâŠïÐyN{Gð-xÏ/À+øN,Pž .èp
+éIîÚo¯RèéU?>ZhŠ¹ÂB²LžG8H€ ÑêIy:hÖNyrléQ|:É°0V	ôOÃnCãÙ~>f`
+9|þ³á'{ëÙeÉ0ô®âg	pÈÏ^ïþ*£+/è
+'»ÑžþÐ!§G&?gKÙA(*ðh}3¹ '0*cDæÈŒ?iØ¥þþFÿ5ÑYþÙÑUÑ_úï
+ì
+
+Éx%ðjÊó·÷{òùBw«	YEvñùyõùø	æVô³[rõXi<WÏ)?{!ä6+Ê­¶øALW2t¿²Á`1®D:ìM1i|XÏŽ×õ
+6Ÿ@<ÜCn9È
+ªE.Š
+9
+6s8£Îné.%«_T,
+,­Ùê@°x]·zâj`L
+ÇÛAÎ~AteaxâÅcŒSŒóœ×_9wpßX
+  Þ¶hµSŠ¯n{ ÕÄE&ºÑyt
+c®¬3m9E/ùýéª.qyŒa
+_x
+Œajº¬:aBk»Ìuxf2ööÏõ41yÆ&0#
+o{üØí¿üÝ-ó®Æ×õÿ2·zbÃÃ*5
+üµtÛ}­k=ëïº2³Ú/66î_3éŠÌŒPæØa·
+Ÿüº*¢Õ¹3)¯W¯ï3c§LôÐäH×û)µÕÃ3c?AààkÍüNà«ØÎªal¥É9,ã>áIV­`AQ HŒ\ÒNE±!Ù²pVHš­ì-@TËõ)jvÔ5÷&ê>
+1U*¢ê¥Á\ÛNÔYü,ygon&×±9*Žs@ðËw;T\Wcÿ
+û_4ÄÉûtG|ä­mw®=sw4qæ>ãŽñœqÆøprë€è1v<uè(}¯9÷£¿èc'ÚZ
+­9¶¹¹eŸ[ý[Èõeíeßµ÷}_ð__8¿pç5Î÷(Ç(O£¯U«
+
+Õj³[b[Í­²­õ?éØí9â8ä¬ŠüeÄ­&pÅ­ºÇ7s=n9Y$Í
+vép*Òá<T¹€ð(x@
+ŒŠ{qYhÁ
+-!]þÀÄÁ?­}jkîíÑà¶S±Ô$Ì)ê~šÔMÓCŸé%Têh²Èö3þn>fî²×·ÌrcWììŸ0þ=œ/~JŸªwõŠ=Ç
+<¿ì7/â(f±óvS¹¹h75-7õG+ß*·:RÒ²DãŒ$-ÈêÌ"ž:À
+÷bÔQîÿä2ÅE¡R£[ÁjVÈÞB«%©€Øl(°ÊNXô'Ö]ìaÇ¹ÄH85®`F +¹ü\y®#%-|[k8\î £²ÂkãKE
+jü8xß€ÃÆÆûïÀþ£¬á¶©kVÎ±úÁÉ­8"&+ößGŽö\yÓ~dôw0ô7dÅ
+2ñcGzÒšÔ> m³lÖävËÏJÏZz¢èÂ#ÈpŸQ
+õ€å(ðüªúŸ|B=/|g±dÚ2ÝzF0îÖ­öžÍýŒû
+7ã6¥!«ÞÌ­^ÈÉ=:ék»X}ê]ù3âžÒaÎãC©ùŒÂT
++IåŸL3×m`,wÒÅû4{Ãdîf;WP¹SBT5%k~Ö,6Ëu-OÛºØe{œØé.^àª÷éY6ØõQKlÆeõ	3ðs@#àmäHbïï;õl 8àš¥Þï¥YW·$2«Ãõ&Tj=EMhùx«T²ÒZéã­:ËS­æä8Î*Í¬æÒki©#&lÆÎTè%?`_ÿ/ö¿k.vœÓ
+|Bgî:dR>³tÂµuu_U¶í>Y¯ÇÝ=ßpÛ¡CoŠvÃ
+ð÷à
+œ¢?Ø²·²>Nd÷·ÇN\ÝêŽ!ÍêÄH#.IŽ)xTB!óØnóà€{h5Kû[óN,UÖcÄŽ2û;±÷`V·XQâvzÓõÈ  h<~ïÒ#d.J/¯kŠkÿ/ŽAÐè?
+| &tR=lj+l©UMÔ9+Íš¹Â+VÁMW7
+íßµ,Zzstè +ªÞ~Û8ý mYµr\îKZíØŠ/
+fFºoeÛM|P+ôö%ÁÕAâP-ú­²töcC8B"L9®$¡L«­ÕÕ7¡p°êŒýŒÓ>ÐRéXPYÜdið44Q^ùàÕ¢©|«Çë.±šÈûr©ü4åßs«ÝnEMåE)ñä¥ò~ñHîÓ©Oáš¹É¶åÓÌ*Pr+nÁçç
+hÀGMä÷úá~`ztUæ
+þò¶çlÚúhœZâT«JMÜáôòúÔ²zpÓn¬ÃŽM¶¹®¹y³gÅæñÔy9·Ï¹WJ©·
+âdC®ìÕ­x°,pSuÓ²üøûËŠaüüï:±0hÁ³oþzáÎöÙ¿X3gæù5î¬°§_äºíOÜð
+Vpà×÷_þÜÑyuG~a%wþê¡G
+~bçC@_nÛíAûõ
+gãZÊ,m
+bÿ3þ
+KçárÉDû
+;ÐÚé²;`%]$Yv¹e å
+%=ß+á$Àl &]oßèÛé#|g|äkö!WÔã6M»ÓÏž±Ûï­O·ca,=¥séiãiàÕ[KËS%	`ñ)È"n×žéÒxZÄO­96õÁ1Aãthì7U§ÁõºcÄ5H¿ÝªÖ®J|ù5×%3{ZrIt.Ï.×ëRD:¥.éžôŠôµÄeKíÒ
+i'ìà^@K§ÒÍ<µîá9^`e"_4%.gýbº_?õ£ÞTÁŠAÆúÝZmÇ
+Â¬qáÇQlôÇCkCSÌùÔ¿Ð¹»-vsF_æ/Æ8ù|i¿W~^~Uúœü¡,XXÔÈ_#.æ¹CÒ'l/{ýçF£ÅYü2v=»}ÛÆo¶r6ëàcl+â"±ÌÒÄ6q2 OID%gåékI"
+2#Ë
+ÛCnÔ\X-`aŠ
+(QÜè1äWëÉŽß~í\ô&µB,5ìì¢1`tN`a[j¶3
+öµØGâIÆýø.ã-ãÛ;!>·'®Ã¯5î[Snòn9+«RÎq-
+éäºžãÜÜ×©©ØÜNØÁ¥
+ÅšGÿü
+¥¹^y]¿ì\>
+xÂÕmð,ð+ª÷šq&.Æ}ñH&ó5DÔSV8Nj/ì,ÜQø8¿[Ø¥
+äª]
+o,Ž¢Â²Â8ð|á'
+|¡
+È×CœÓ<È	aV©#Ø/aÓ°f·çgdfFóe4uØõIUív<ÄŠ4ê¶@F4	ûægâöL	ûäE£ùCíG(ßR=ÍõþÐî|85_©Rn~<_pEŒ,ÿüOò[~v~g>òCùåùÉ|6ß_ð·ºŸ '=ü²uçÀ9hÝOjô­*M­ŽZ£Ça7
+yŒfàãõQqÒáå¹ûø¬Íå^»èÑÐä`þØsJÓYõýÏ)1N³ÑM¿ºzüø«§\Û°5ÑJŠ<\Z7âîÍ!Û'7®| q!5£Í¶Ï<hî^ç$qÈö°ž¥5
+¶/47
+]°ZxUQ |
+õ ÓAÄL×»ÿC&+QÕJék±šíÏçºÜú/&-¥}ž5|3fm5Nç­
+yKÌw÷;mÛÆd¬§gÖŽ¬Üod³Ñ³ògÔ]tôÔñË}ÄçøŽøó;7û
+ù#?çH«6Á9ÁÓêÛB¶ò[Å-jô
+ù÷ôz;ÍnÑv¿'ÿÁÿV|Yåkù"c7¥PñR¹XÁU+Ú3dk]p€Â¶ïóuÒ\m ð¹>SGÛqt¥VGó.ñjW­K<ø8nŒöÕ/ïÖáÐænºÿþnÚLrÖc~ñÊ×ÿiüveòÉ|rçO>Iû{·q»ú«AÄ±M/­qpG©µÔ:ã
+ÌHËHgCÆ÷Zû"sÂ÷"èÏ¥ªGQ4µ/BµZ­¶šŠ¡ò¯1jso
+0R;õ_¢TÓQïN£ÔK"ºÐM%
+¥ÃT|üÔë»1_ùÌŒ#LÜ0XìùÅ¬iw¬>{
+°¶eñg#a3>h
+ø9ÒýÔCÝ»¥ÑÇdèû4è»
+ÑCzµ£Ä-qW]æ(Ò`ipÊdã èöÆ[¹VùËg«·50!žKÞy^:gùÎ¥Ú5UÜ©0]°iŒB¬,G!ÄQ»ÝÓ¥
+ÖÙ)èsîþýîÇ:ÒËÍg9çzçúg ØÎ&[RLã?ÌÈêÇ§
+\Ž3Ççm¯Ãqæ®³Ö®:õÆ
+Ä3|Ü
+XÃàQ&M~èFæÀc;
+íÚ»ý¹W#ÄTÜR/ØÂaÉÇq³žE
+Sæhc]à`eÉŠf«dTIœ:F%jY¢
+h8Cx¹ IT.-X)°Â±ÃAŠ8V8ö:Þt°
+Eéš0H !x'
+¶×Ášošâ¢Bkó7§ 5ô»¶"%
+š©Ë;®©«Ê\YZQÓjŸ	
+Èæíx'Õé¡×7Ž·^3üW±Ñ-×7T}[:xñÐÇrÐh
+úXDnÒæí|DÌ÷Úœ­­®-ù÷I«ÑE
+ÏZX_	9o9ÃZÆ[fZîW¶8vç
+Q
+Á=·!:;gFtµcµkUÎ¹Rutßš²±5ä9¹ùÑjµ*\S©Êx³Ka%_ÍÉÉ¹9zñÍêR×­îÅ
+ÖžWmsß_t ç@ÄÒ7x×û
+(úUQW1Óü=EÞátcNæÒúÉîìÜTÝ0ëz®·àþ99[-÷åŒónÎQ-,@il*)ÊïöÔãthÖsòRc«Að§f6ÙvÜÏ`€ÐyNÖ<Óé31Ö OaÏ°m,P<:ÜÚSéÕáŸ^
+nêÕ«ªã^:úèÕó
+a÷µy³Í>Ö;> Å·pK  F§à
+{ôp$îÑ3³ãÙ
+ü	Dkbž%oC
+ÉÓ}Áx^ Ø\$îµ¥ã²b\.
+ªÄavÁ©Užr}
+H  ±¥=T².[5Ó®Â\ÜL
+.dm3gYÓ!­ÒÕµSUpôM)€"ºøkK­ËMŸŠK£ÞV àÀW,µªK­¥Åý*gýrRkÎ.6éHÏšÒõúùÑüÜüô«§MšÒwèHe98n~cuË=Òxzòò?ýðÝã;ûóËCQüBëÄ³_Àe±«Æd
+Ü.{Ó 	¬{îw÷4$ÛÉrgÎÕŽêowÄg'?'žÀýA/!
+åBÛ ë(k«Mð»ñž×áta¯ž°YP}Ñ6äÝéíò2í
+÷2^÷»15ðÝÈMß¿E·ªT&!R§FÓ ºÀÇDœñîz××^Óîêtmtœé:ãâKs
+\å.Öå,ÝÙ}ºªA§ë]ÉãtRöBjNV;kFØœæ¬ zŸ~U°Û0Ó.Š^>=ÙiTUVåÙÉmÇüÌüQŸi·_y[­"ýüç8ÀFOWßËÌø°šrì°~÷ã7NŸóž±èsXqlÐÌº÷ûlûfx?_GêìM€É~fTfgÝ.,ñNWÔíFÔY=&ŠI
+3üF/±øï³Cø,ÓnFéô®ë§^fôcs¯ßs%ög_U?baöï?íº=ÉNÃwræÀ1Náãî@?@m 
+úîæ
+eqnxºéÝV(0 ŸÅ<£¢¬*Mr*Q^QTPŽ3zA0§ž_ÉCEJ
+
+PV#IA2«ÈDæ¡,ÕÒÑXÝYW,ÙæjiÖâõ4¹^
+c.*×Ô*,}£arºM­B8Á~õ%?¯¹×|÷ÛoÎ
+u!kæUzº#F_+Ã©·1pØé¥CÿNWãüWxy«ö:@Ä_ó,3* 
+©*©øSœR°xB¿b<$µsÁ%eÉvU5'#J-SË`Fð[­Œ9
+­/-
+DQXc%EfÕ`=KòËnU 6+
+ä|µªæIh8ÎFJKÐRv	·TZ*/QW£5ìjnŽF^­~>`ßãÞ>ßS¿D_²§žSÒò)õ{ô={;/ŸÏ©%\Oò
+]Êg£°z5ÖÔŸcÖxs|@j
+¢
+ôlqsz¶øJœÎÿO3À|jX.³Ö[	KØB@²IG¶
+a¿å¥#8rôt8=ÿmûÿ 6M0JSÓŠaFÔPtK-ôçü~]¢p
+Ì°¢«tÏ0ÃL*ãéº
+ÖNöåXjrÏów:é?3n5º°ýÃØ¶ï÷Øm<e|sø ÈÒCÓ§ãAFTÐ€vªIžS¿»@x%[
+#ø#üpÆÂBõñ|5ªGàV|;^$ÈQ
+úãB#
+%lUÎóç)
+Er
+ eGË¿eÅ+å«ÙVy{£Œ/ïc7Gå÷Øä²
+aA=l-+Ùz¹Ü¬_
+ ¯w³Ù×äs,]{ŠÛá£ú{¢p*K[µÇ1+,å!d"Dº õä¡ÂxÒ\|R·yrãLH.B$Wôá3
+ŠEÝ(â\qt
+ð¢JÎõ÷ó
+±PÄc,;,'-
+¡»I¥Bw;Î€ÉS¯£ÏüI;|tÈÏß¬µ3Kšìâ:s:ûëèàOðÅk€Ä ­£c!ŠJlòSî©x
+±	_óÜËx±¯5vøDc|s
+)ñ
+i
+ŠvÐje¯î9qü£ÃNÚEj
+-.Ð
+O7öÔâ
+Ï³ÅÊk9yÖIè
+t(¹
+pHÞ«;¥ÌZBîrw»¡Ão&ºÆÍQ9GfVÜM×)Õ2ºÏ_a.È×%bÖ&ŽæÀµHÏìO¯sœö±ÔRjûR¯­U6kgOAlØVÒÜ÷²©7Õ
+M«K[S.v žØý¬&+É3û
+¯­¥_Bù\·ZìõNÍéÃWÏQq
+Í÷C=u¯Ö²V"|s¢ËcÆy
+1ÖÍzÍ±£ýCªŠ]çÅ±o.#mÓ®È±d¹¹R?b÷ú.8àxÅ,z?Í
+1l,Êä;|^&ÕÊpyž:O$sñ\²Â!~ÂŸ©~ÍTY¹}DxÜD$ã«ìMÂeê#Š¶i7°; ¢¥ 8v@+5['ÚÌe
+(Ú4[ÈVnÓm+lŒ- Ž?Q
+qb%êT7ÒÀÄB{ªÖpl?Æÿnª 7íKÖ7i(Ó[š§NWkÑ=gëzc©¥K|{
+¶œ4Ç
+éžZêU NUVjM
+d1MU+ª¬Âý«y£þawþÅŽ²~-ÆZæ&cÞEžûOøµe&_Œboèû@uÆXò9G¿Ú¹S÷,-YSÂø³?
+Pnœm œ6
+¶ÚË#zD ¬»s¹5D_V³»ã¶9åeNÐ²é (Qy&	ÛýÄÊ²q6œÎ+ÚCh>Èì²y©
+ÔAÛ
+ÚÙ^ÀqÓu
+©7ÛàŸÙj~­ÄülGÕ Bq±¹p¹ÒMk\Yb:îHN)¡ž9Z7rTYÌœnÒwVÍ¯Ïž¢ÁC6ðBhð
+U«7NÏÑ<­xÛ6wŒ^ŸäÅÚ2
+_û+ÕwMÿYK,gò
+7î*
+Ï8qïÃ=Õ-Ãš< > : œº1YMÞ{Þ UN¹U7¿FŽé°ÅI¿lDÂŽËr=c-·`ÚÈ@.ËÒ9lžéJÑí 2
+7
+(¥Š»
+
+·!ßPyI{	)Sö¥vÍÿÙRÿÿhb ß·ºªhÿ©å¥°ùÂ¥×ãñFªRÄÈuj×ÉÆ"W<²vTN]á°pnZÄæt-ÞfnñÛpg2*o{zÎ²kÂå¥Cf5þ£©"ãë¢÷>|žŠe^ÉÌaÖr¯ô¢ÝÊËÃãŽ©X­ñ~é€oËK:ç\£|i~N%Â;\$/$X¡/@?cqæª
+V×UÛý³ß2?éœíÕNiçÔŠ5÷ëŽÄ9 Ýàß_âB¿ØSyYù!ãtìIÕW$kð{P^Ôj9_­7N¿ö³õ}oÙ4ie:ÌY /Z®Ëé~ð7û |©Ô ;h-æüÒ[£Ò:ÿÔKûQRËÕ¯Õ€Êª=d`·ïÅ©æüwê§\Z&±?ÕŒÚ(øé«ÜÑ×o[ßZ=ÁþEú&
+3¿jòŠ^Wx, xò#\ŒÙt!WÁÑœtÖB)ZÛPw
+êçRßŒø:ïû¶Ð3D	ø	î#ÐìCH2¿kWî'ýYë·ŸõÈ??b«ûVôæW@ý[ùuçWìã.$Žaâ4}÷}5¶Â c4ª¡~0ÆjÃÒûúkâa©
+'Þ¹WÐ$|]Kö ë!
+
+ý ¿ò{Im2ç<
+©R3€(
+{
+Ò
+öi4Òx3ÿ÷A6C
+é>nºöoákÑ4ºî·
+êñ{Ð&šo
+ý­ô
+ó	h
++òœÜdRž	'`œ9ÙÏ
+	åÕüjÈC|fùoèŽ­°o-Í
+ Zû6Aº
+ÒÝ&3AóÚrž&ê÷@YçK«¬,+8§R	WB÷Ú
+iaf7€HÝI¿
+i
+D<PçWBzš®Aés$žN~!åeD"õi,ý!An
+­ ÀÝ/²ÿ³!W&$6Ýò<w#B>€[
+\P&ë²d²oB(ü4¹¡H.8¥AE!Ï T0Ž«î^Ãy%#T¶Ì&€yUB6VE!Aªª)GšÚ9 Ú10¡:Ð ³éÐ?ýBC }4Ày@áw 4âKF9@F!t%äW~PóúýxSÚÐ7šMGÔÞiš
+
+5k7 ÆüBÂö6D¿f~ÔÖÜ2ŠÜÊxPºLÈý9]fÐDîxºÌ"÷vºÌ!÷yºÌ#ïKô_.(*tŠËZgy2]ÙÍ'Ó²ŠY+ÒeÍ²Þ.[øüÙtÙ®µþpQVØ&\Ô5ÎömºLëèÓ5;ú§Ë,
+õé2TÇštòŽtùÿ¶võ±q\Çýíîw¢Eú0y,ïÊ4I¿Î)Ùy'rF"#R.4wKÞB÷Û=i'Âe#Ú EÓ S ªS7Öñ®vÈH*iØAEÙÂý#šhÿ
+õ7³ï>(ÊvÝôÛ{o~3ófæœ·KëîübfGJò±sg³ä·áæNÉ×«fãO%ÿ èoNW>%ÿæEÉ7hÞ±&ùm¢§>JñQÔ·µ4HÞ'v·(ÌSvê[:$ïÍ-{¯C]ËÇ%ï;Zú÷S^ZF%\Ž
+g>þ­-QÉûD°åwß"óëñ^~=ÞË¯Ç{ùõx/¿
+ïå×ãœüzŒ_÷òëñ^~=ÞË¯Ç{ùõx/¿
+ïå×ãœü_Ï±zQò«4óôé9;ZŸ&yxŽåó[9&%ÉSLþùmTù-/yx€åÌ7±äI'¿cþÉSÌÿù]ìÏ]É?ÿÅüCèß|Xò>aœü6|ðšä!ìfþa<É?Ëü
+ª #yÔ@pùœìÏšäÉ/×:Ë_<ÉùÇ©W$~ùNOð;G|o2ßÍz~$yÒsø@Mü5ñÔÌ+P3¯­5ò[kä·Öäek9/bAd%f
+œÉÂ³õ·Ð&DùSžO¡¹RÊÇñ*®&úm0Ð ŸÜ÷¿¡ŠÞgÇHBä*2úNzöúÅQüöáÛãBÜ"z9øà2êô9hY1k}HaÌÉ'YØ5 eJKŒ@4ŠÄA¶B#&[J]ô}
+
+2Éiqxd6F\³-º+-8<Ã(c]
+O±¢äS}°å\2¬<²W[£1õüÏ±5-Ôze³~ã)~gÝqiÝ²iÖåÙ.÷'X·+#Å+/2÷Ê¹ÐiqTlPOwTöä8Ò«j€9/Yhñä)UGR¢Ê¢Vm9Só¢YÂ,$I×[«-£3±Y>Ç¯ªYužbìÝýk¢ŒrÊ\h,Éúª:²°sNzkÊøG¹Š
+Y÷åÅØö
+÷zø<FlCI ÷^€qÃØŒ¶§¡ºMÎW
+Ç0*çosÖ,áuæUcÞLj«Û®Tñçefì
+ÕŠ7G®äDÅ$¿ªV¯{Ï~ãÜ3¿šŽ1Ãr
+éØÚŽÄçÑ_,ÕvŽ2ÃY®mkày­Ãuçr6æ*Y'ßœõNké`e59²Êªû7äâÆ{^Þ(V+Í³
+ãhex,TfQ¶Mø<
+¬ŽAkÈ¢Ëø²Çeí®¡$ï¡eßz6í«OmÈÚ1Þ9cè}NZ*ï²!þ|ÞžýÐAÑÏòJðVÐ-Ý-§PÛÕþ·žÖ³rí'¹~ÎUòüÿÝ÷œÜÌÉÝÐ{\u¯òŽÅ`1Æ¢íÂu¶g¹zËQ£út8âq©­GÜNhÇ1#âGÑKøž~ûÑ3+­gqrã÷÷NþÄÄz¥-×âœçh¹ßóØË^Fæ·º
+6ÇÇ;÷ÒA+$ÎÒåùwÿrMÍðèäsÑÊ>êÅ.ÇØêþgÉB»TuÏöö
+[îÏÜ?æXUÙ)¶SÒí$órß©|M÷"S®²|e'Žäê¶*ë'Ë{+÷YYû÷WyÅSÄ¬-Õ
+c³œ¬/ªåÞ
+=¯gdfRRóý2ÔÁ³Ú)o÷ß\-÷QÚ1MŸ«1a5!£íÈýêýl÷pí§jöô
+M¹°ä
+MíÊñN
+=Êpdéì²yœ}xÎ
+Y©}ŽlV#m×XÙ»®élMÝVï>8Rä]õë*œA_ó³Y»÷âªd²Þ>ãþxe>_µÕ»·oUed}Twù5ôA3ªÖÇIûæÌïóè|³äÝ 7ïÞ2ÊYMÝì=ñ®jŠù¥ùî'&÷ÕyŸËÚ;¹Ï~Y·&-y¿±ñT.ëÛG/ZÕ»ã(ëÜŒË3ïõìGò¶åÍ6Þ[lôÈwÌ.ÎÊ²:e"èítJ
+Ä NF×~ŒêÆ3Ç!Ž>AÏÙgÅìÃh?FI~@<Fš'Åa<P#íqŸ/ÉÀ^/~óüÛÃçûÆåïýÎ	âxuæ+uá¶ÜmÉ§3ŒC{gèiy¯wñŽ>œ4Ë#6g`
+×ê¹AUEOWtÇðÑüîeù$lõâêòA¹êå³çs\%ÞýDOEò·k!Ï÷ ¬õ[±R
+ëœ§
++º'2Ö¬µoqË8N¥]tÇÓÙL:kºv:edÑ
+cÈtÍê%eÆx:£
+Ç8®ÿèÑŸn\B=F$0ÎØsq×1ÎX·bÇÓ)×Jìá ¡ß5bcÏ¥¬m&(€LÉtÖ2â¹€²
+×ÆÍ¬up\;ênÜL[0Ò³
++¬³¢ã€³aŠb	ý¹hÜ°¥*;ež¹eäm7ž
+ÞtÐÄ'LØ Þ3å>7o¥\ÛtL.»ÐcpHÒóVÖÄôÜ¬eºI Ã
+2æ€gá&»0K$À²¯0LÃå
+§êž	«6
+¬XÙ€blú
+Ôð?¡{³Í¹4çã6f·D$mÌÙópM#pI±KÙQ
+0Š¢xá¶)Xõ<&Žææ É	ÒŽ
+^WÖ#íE±cÅŒhZÏ³¹(ÅßMcÊÐI¹®£©g-äÝuR
+ë/æùjË
+ôxÌv2	sL:eåkÁE×vH1g²édµõkõ)ojÇÒØSÏD%ê92ö²£Ù4%è tÈ©	æß0&²È~ÒÌ£9Píc6s(CÇUÑ³ãÆéíÆÄ)ctv¶]³C¬çôèÄÉ'G&N6FO<y|øôø°yöÌðð©áÓ
+õ
+õq$£
+kJ)Æô0oóPñk/=53ñ¶CåOY0Ò9BF©Fá].ãúCU €ž²Q6êâæ\Ö²š~{)Àâ&'=CHw3²<¡
+t[¬uQ
+³~Õ/J|zÎb.
+	EÍÏä\ši¬Ã	u8e§PþPTÀT£ÆŒÈ3šKÓA]Õ¢{³)®ô
+ò,0'
+,
+Óp2VÔµ£gn )®QÂ±M9Fídyë:HÝY-ï	÷8°6MFX.ÎsŒÒæ*æÎt
+5IØNì@î$Êþ#UÃ+y¡8
+'g«£=ïó9Ëa3Ø-£V6%g~³°Oç1ÔêŒmåœMnÓôIŽ°oÄªcep·ãš[Í1MÌ^ÏÞ_-»\ÈÝB*
+Ó}ÎGncÿCþ#Ý}úú¶l9;ÎŸþþCp
+xbÀxòðÑÃGêã®yª·7Ï÷$ËŠµkÂ2²fb%§ éLz+ô4v­4¶øŽH³vÔ6q×3ëHè}t÷ÆÝd¢7éÒ÷"÷&ÏŽOôPçÿ·èµ>
+B¯ze
+YzÃKNó
+²üfnqENiÀ1ÿó
+œ³|ÛXÛsBþ¹©ŠO{E»©}_»
+ëÒ}­Ù¬}÷XæÑÜÑgùv¯üšHD=ø9è9ñk þÚ±çQÛó;Lçy&GÆä r|ãæG÷ó~>Ý÷ïißqßŸ#Ÿ°ïãŸßÑ
+ÈûÆršÒþœÞêÎmŽ¡lÿ®µâVkcÔÒò§òÿ£»
+ôÚnj"úÇì«Úv
+ÂËÚ×K;CáHö51ŠvJ¬¢©"­ýžŠB|€ØÝZ!ŠT¿-Ôù×â×ÄE4M,âªðë0É¿VÚÙLê¿Øžq/ûyL©)ìÒfi)Ñ*tí<è£ QÐœ 3ZägžÔØº{Ô
+Â®EŽfÜëÚ¶ŸK b¹â6ÏN®ž¿3©×kAiÔðH¢kÍ_éÆu-OÃÚ+¥-¯
+
+ÝÔ^Öüb€.BªEoŒ©Õ^4ÉDiKCèrd«6iN ,ºFÿ{ö
+_ÃZªE°7¬="1vNÛ+
+=¡=Z|H_œ®}ÅþŽÀÞ3ÅÀDJ
+ÛB«-}ZA{
+­].µ	H»¶_ô¡©êpôIlMÚ«à^E^Ej^Ej^
+¯:äýF.AŠW{AdŽŒžvŒ**"+Ì<Ÿ?Ž¢=¬ŠëÞÝ¥-ÛÈ³`qÇN¶n
+ÞÔ
+1ŠÂy·Ô¥¯k<¥à
+d[¶"t-^. lŠ
+ÜÔ
+Ñ
+åHìå":^+¢QÓ
+¢þ­ºFÑQÿQý)åŸàé$ý±€çÑ»«êZ	VÂËê?]<¢þœU^ýqª^WoãiSWÿY]&/Ô©+bô^Ç@W@ ýnqßõeu¹ß¿QlhŠÉª·]œÑÛ$Ó²G2;C6õoÔïG â@ýº*
+œ]U]ñCÐ·ÕÃâiÐ¿ôûê
+ªiõ;ê;xÔÕRq¹P(ú\+Öy«(ŒWcœú
+õ-õM±¢ß.¶ïFï¥öÇõÆëÐ§š®ºÅœúHœúMeRùo-;DÅõÏ€ärñ¡¯šÕËáà@ž-Ü
+Ÿªõµõu÷]Õ6UÆU#Ò€Ÿ.
+@ð°`Õ×pÅ#ºêA£]V/}
+Èÿ`N4/U\Äu¹i\3Ì	\*£¿bnP}Y¢©Ðq
+íÚEŽ×Ð^DûÚ¹ÇEË¡å±}dÈ "Ã 2@daë94BL1
+Ä4Óbi ŠAþN1Í1 Æbc@1Ä#Æba Â@3"D0aFfD}@ôÑÇ> úè¢}@ôÑÇ#  F4ÑDMh¢	& ÑÄùÉ¡b
+u ÖXgÄ:ë@¬±Îu ÖXWóKÚZä¬²ÈCÖ Yd
+5¬²ÈºËÁPQ6çÑ. ]D#ì*°«À®»ÊØU.¯
+a@( Q`D 
+( Q ¢ÀE Xb@,±Ä"#¹pshøèEùS£Ÿ€LpžªL/÷w~Q,1ýžÊôEñ%Š/ŠyÑÎúºB(E} 1Ò-`íshiŽ+h×Ðn¡ùû	Ú¿¢ÝUó5úGýWü×ü·ü\ó¯ûÕÆºÑº+u×ênÕ=p­nœN5"{ÔÞG±µ¯ðõ®¿DÃ!ë sê!Ø=}ö0~©ÂÛaü²SùI§r«S¹Ö©|¥SlQU|ŒÓb@
+ãÊdxkû3ú
+Žög°3œþÎ{-z±ýI}Y¹áá.Ð÷ÐÐ®¢}	m -ÖÖŠs_'ä'ÃI7Ð:Ðö¡dB47ãÞfÇö@xEmP®~Ð è«n
+û»^ìèY.v|§Ø1£G¶(ïº
+RÞFæÞœVÔßÅð·=òWEý:ÈEýÈg
+= )vüX4(g
+î#è€ã7Ñ3Eý9}ªšÐé¹wŽt'µaô2)ÞmšÇ=K­EýiÇúQJŒR'ºÙœÐj%8ôËeÒ§Ô¡UðÿD`Q
+?3} ?i£ïÅ«×otÿ)#z1ROò8$-}[¿ÚvIÿt)mïè_×{ô×»èþ2üŸÄ&úeõÍðNý¢Þ§»Ýïêþ	ÝÔÏèmCQÿ=ý¹)ŠIõÍwô1(<YŽõgÛÙÅú
+Ö;ô£Æ
+¯8âé
+èŸA!ÏúAÄ·³mjüìÀ²²=Üéÿÿ²ÿ3þcþ§ý­þÇüú÷úwvÛ[õ@ .àšØEï+è¢t·«>HÔùèêcŸI¥«Êÿ&OšJ@
+Ú:2~L)¬FÅÈQøõxë²Rÿ©O
+h=Švc
+#]#Ëþ»g
+]#ÿØg&åõ)ôÔW11¹¬Ü¥®÷Ðw/)âå/ïYòðË_ÁæùÁààg¶=1tËŽŒÖ|Pp°Ý[ø£ñÉÂ_î*¹»wj€ð}sùÚš6­šÛLM®ø2jãðê÷eŠ ö.¡·ALtXà0HûÉ1C<¹vÀ!·äêD;Ëµ×7°O!¹¥;ÆðÐa°LwXæNšAÅ ;ŽÔÞÎR­2IRÊd«Á
+`EºnEð€³"]ac
+ÞªH9\9Ì¶4¥*£{2»öevíL×oøc
+ëRJý¹ó·éËà§[-ŽéÂkóñ`áâa,ÏÉooÆV!×j
+Î·Ký·ï3|û[ÄíáÉ¥ÛakšØîn5ŠJl°u©bkòc÷Qö1R6I¶#÷Ðð Ù­Ù²­aê~lr) ÑûÔëQÃÓ{öM
+knÊ<CœòôŸàù=ßõ	å
+ñ`×Takë±B
+uGº#4uFCÛÐÝ(çÞ·ç»Êrš	ÝÛ[UÞ!(H>o`€°oüÓT*
+°yÿ9ôÃÃA1lá?Œv¹á·VR8÷ýqï÷Ëå
+ºäº
+!F
+ã#
+'éÓü~
+B_O¹OÓžoiËáå»«ìKæëRèáÂõô>u±nÑ¯Ò£[Úœ7ŸüãÔ|±Õ|é±6z~qKœ=çS¢ÅÝûBôš@¶y4ŒœÌå¶ËÝÛ»èŠï\E§~ÒbïUMž]N9`Ý)á}P
+ì}³øÈ^6ŒHLW×TÃï9÷ºò6Ð®j`
+©Õaõn9!^¿#<ao°+Wå$sû¿88
+endstream
+endobj
+
+68 0 obj
+23445
+endobj
+
+36 0 obj
+<</Type /Font
+/Subtype /TrueType
+/BaseFont /Arial
+/Encoding /MacRomanEncoding
+/FirstChar 32
+/LastChar 255
+/Widths [277 277 354 556 556 889 666 190 333 333 389 583 277 333 277 277 556 556 556 556 556 556 556 556 556 556 277 277 583 583 583 556 1015 666 666 722 722 666 610 777 722 277 500 666 556 833 722 777 666 777 722 666 610 722 666 943 666 666 610 277 277 277 469 556 333 556 556 500 556 556 277 556 556 222 222 500 222 833 556 556 556 556 333 500 277 556 500 722 500 500 500 333 259 333 583 0 666 666 722 666 722 777 722 556 556 556 556 556 556 500 556 556 556 556 277 277 277 277 556 556 556 556 556 556 556 556 556 556 556 399 556 556 556 350 537 610 736 736 1000 333 333 0 1000 777 0 548 0 0 556 576 0 0 0 0 0 370 365 0 889 610 610 333 583 0 556 0 0 556 556 1000 277 666 666 777 1000 943 556 1000 333 333 222 222 548 0 500 666 166 556 333 333 500 500 556 277 222 333 1000 666 666 666 666 666 277 277 277 277 777 777 0 777 722 722 722 277 469 333 552 333 333 333 333 333 333 333]
+/FontDescriptor 69 0 R>>
+endobj
+
+69 0 obj
+<</Type /FontDescriptor
+/FontName /Arial
+/Ascent 905
+/Descent -211
+/ItalicAngle 0
+/StemV 0
+/CapHeight 0
+/Flags 32
+/FontBBox [-664 -324 2028 1037]
+/FontFile2 70 0 R>>
+endobj
+
+70 0 obj
+<</Length 71 0 R
+/Filter /FlateDecode
+/Length1 34148>>
+stream
+xŒ	`EÚ\U}÷tÏôÜG&ÉL&39&{ F ÈÂ
+"AîK.AQÊ!¢à
+âx¢®¬7ëµê§°·QŸY2ó¿Õ3
+îîÿÿßÌtWõ]õÏ[õö RPbë§ÌÜŽ¿ó¯óaÏÛaÛ%þ¶ê'âŠ7ÍwMÎ_5žÖsMUÿÏLÔ
+œyæŽÉSßÙÐÄ~pò°Ã6O9ÛËa;{æŒEKõoÒÓ`{+Ü³rîü)|â[þÛUó&/mbO+w!4Éç®<oÚËxFl"Š6Í_ž(6!t-œ iÁŽŠv6ÛS»`FŽ?ŽGóèÿ÷CO&1,ýDI6)ªÙ wVÝáDÈåöx}iþô2Á¬ÊGrP.ÊË§GQA·î
+E=KJËÊ+*c={U]Ö»Zïsyß~ýkÐÿÿç+þçÁg 4xHíÿý|àRòÂâãD^6<%Ÿå[ZÆg%Ÿ¥ÇiIŸÛSBÛÑN<íD/¡Cø\µ
+@mèuäFýÐh9º­A<{nA#àËÁþ»±7Ñ
+Ñ6ÄÀrÎ
+n@
+È
+=ïÐ
+Žy®Z
+T
+ú ah>º
+I,FÑqö&T «QnIKÜž3ñz
+
+`^Ot!ò¡)ð=øû{âP7žâ
+Ž
+ÇwJûOi3BÐýL3@èhjÑ|DáîÓÐ7Ø3}á.&Zá,?j@3Ñýšá$ÈMLÔ& <c)Üu3ÚöÃ·
+œ>Á
+w*ñXâò¢`Ä
+ Ç;ø ïZ¯Š*å¡
+þ^CÇp¿Læs
+WÌéÜµ÷õ@£¡µOÂ_ãà»y­I\Ì@;(µÑ_Ñ?°â:<äùäafá=à;Ízßwÿ
+Gñ~¢£Ì£ìÓìY>=~"aDÐè!ô2V¡§Œß?Ä_Ÿdy|ÁÜÍî`ÿ&L^_æ¡ÛÐÓè_Ø+ñp<ÏÄËñ|Þàcø[Ò"sÈÏÌLŠyœ
+Ÿ#Ù
+ìMÜjîVþÛøžøáø»ñ%«ÑpÐú{ÐÃÐ³è(úŸÇÑÃ&lo ñh|
+|oÀ·áGðvŒ·ÁSá/ðwøøW|
+€ Éo, ×»Éä(|ÉïÉb¢LSÅÔ3ó¡UkðÝÇüõ±GÙÐ¹ÛÄmá¶sOsžSŒ"Ü("ñísvåw}
+GñµñMñ=ñ¶Ä?xè*d¢*hýdøÎ~oÛ
+ÞÃ
+ÐÎóqo<(3	ÏÆÍx)Pòf|?~Ühû³ø ÒGøgh³JüF»2r9©ïdi&É€|Hþ`ÆÄX'Ï`iÌ"f³ieÞf>cŸ`Î0çà`e6Íb#l
+ÀNb³³ß°ßp¹·ž¯xÇ¯æÛùÿÊ
+ÞÂ0ažÐ löï ¯ }è¹Ku
+`V2ý}èvRÂzÉ;ä
+çIh*SK@RÉvŒ\ÛH6·ïEzá¡èZ¿J¶3€Sãh6éŒï`¢}u²/@ßÞ;/å|ùWÐ
+HùWŠ2o¡OãX`·¡OY»q'yRð"ÛÌèYŠ_öþ`±ÏëAâ§À.ÂÅø7&2€šùÝæ¿£NÐãµè^<nG%x9ú=ZÇ]ÍçóNüÅ®#vÜ»zÃÙá
+èfÜÀÜÏÿL>FÑQVF3Ï@ëgZö7Ï
+ž
+­FÍh7ý<
+Ù`Ý3ÅlÊ`U&MÛÚÝv S{< 9C@.F
+žŸ÷`Af+öjãGv43c°:±oÅG ñ'ÐæÄtuâNÔ
+ìÁÄržãvôÚ¶ãUñëPÊ Íù
+ájÈQ®&Ñ¬#dÓùÔcú
+ŸÏ¢Ô{
+­c?B#Qub}âî\°°ÑUh:	œü	psÄÝŠ	ú{
+
+O<ÈÄ2êÐèqC
+šÞ§^Ýû²ª^=ce¥%Å=
+»w+æçåæDÂÙ¡¬` 3#ÝæózÜ.§Ãn³j³ªdIxeFýC5ÖHc+	]qE7º
+;&_²£±5 »jþ|Nk Ñ8-ðç3u8sú¿©'ÏÔ/µ@ªêVè
+Ž
+é
+ŽãñÃÇAý¶~¡ú@k§Q¯5êº
+õ`.ô÷ÌìhÅþ­5Kf®ëßØn·Û$÷
+õ&w+@»eTMPkuvcwolT»ÏÝ*4ªÕê×¿ÕêG[ÐÊûOÚ:løžþýÒÁún­žïÐU­(ty«%júiåû¶
+Æc³hoÐ­Ý×­o×ÐUQejhêäãZÉõôÖ(<·_«ûÚps[ßqk.=Æ¬ëï ëÖ­	Žn
+>îÒ£Aº®¯{Àµ$\Óž®
+œ
+8xd FVÕkÅ«àÚÚ«dÿŠ
+úÓ=³­RèòÐÌu³5Ÿu­hÄ²à
+O?8|ýëF[«ÓBõûùw;ÐºËözõ÷ÏGºìÖ¬IÂî6[REœŽ2íÂ1£fNkG\ ,Š-
+
+h
+L	@KÆ
+ Ot5­­R	§Á§
+ÃU­S#³Z¥ŸëŽt?œŸk¡Àº_H@šóÇ?ïÚÃµ_­R9¹ jpü|œ5mÍÏ§""ôB{ÛeÝ
+ŽPšI@äCÃ¶ë{ùAÊà[ÛutlŽ¶Ü «Òö œ0ZßJéç8GÓ#-ç\žŒ1ÜfàYg«¹ð³h.{ÿ=[±ëÿãðŽäñÁ#Cè¿®1EÛÁ£þŽ<^yáXªÖjï;I#©Ic£ /L7Æ)­l~Œ!ÔSÛ€ÒØ5­ZãÉuœ
+þ/jO¢WÅÅËRÍlíýóv¯?mÿ©yÊ:NpðšñëÖÉ:¢|àÀTFú¶¢Ñ aøµ'VÒ¥>­Uõ¥'ü%w¥6ÿtbZª^*Ý
+jÀÐ­[W
+Ô¬k\7¹=ÑrU( 
+Ö
+ È¡uMýÏN{¢ãÖŽÖõõ@«žg7D°>!a.o#ø$/ŽÍº
+qìIÉ{#¯Ès'	ó8u	 ^wäjgªºªj§«j»ªP5Ôµs°êQŽ­aXapiçÌÁs:Î¢ {>a`¢kçû |@£ø}=Œ<³Eåå
+5ÁÑÁa
+
+å³)
+×2×^WŸ&ØRx{¹Ö£=ñùsŠXF ]ZÐ8¡B¥³ÅÊ|RpåMÈ©TÀ³"XTôŠRáP"%XÁºùvòØþa
+æ:±¿<ÒÈÎœîÌ÷¢ížL
+NWÛíàP~;®ØåbØÛ÷
+Vý
+žNœoOÙÂZm±¢
+i0v2B·ä»ÜîÌÌ@ ²²ž8/®ŸKw!§Ãöèa2ÉrÄ-è (ºÄu_8_[¡­ßx\©[ª¹:n·c9oìµ[=Q dCmçéæN(«Žß®K7ªà`Žót'ª>
+ûOWUÓÖuÒø5ŽÚÜ±5æîÑ5æë[àÓ£ÈÓw>(X¡Ø³Ã¡pVám³EµŸ"XVKraU`ï^X+ëp0PQYS\J»Y£p8jwZzÀ)=¢mŠVÉGáÍ_¹j(Úž²î$§Âåv¹­H€¬Ž¢Œ¢¬ÄIwHÕåÎ N/8w:\n{yyYi$Ç×ìžNÞç,2{þ¢1
+k'ìýÐÄ%mÚžµ£fÇ~zqö¬e3®=ëÉwŒ×f
+{h}Ö
+ýMä2g¢§æ
+ŒfmÌKíUOûg7Ûº~Ï²gß9úù?€ý|®¶¶aâòpK}páU×Ò }<³ç@äÊ¡îD
+¹bVðFLðlŸùI*Ý
+@æNlµÅb=ìôYÛýH\iûõ×øOpåñá€{¢ýËt9ÇfDMkÇ%{Ñ³¥n¶¯DÆyÆúÐzãÆ]g:µ3pwà_"Ü#ÄJTÂðuj¿çÚñ/¬\sY
+þþ
+ú€ëì±úu1üÛó\«IÖ0²IŽòÓXÐæJ9ÓLÌÏØþûóí!d-Í^å é¢ŒÌº,çÚ/¯=OàŒp`Óºñ;ÛõÉOñÆExúSñÏñM!Ëhè>LÇÓ|;
+ŠG0SEq	¯zÖAô0°ðV øVÓ¶û §NÔ:AÈT°;µ.Ø=JÔ 
+9ååû[
++g
+iŸ5Rë<Û·Ùd
+ØÝÛDRká!D|\àen£út²AûÖvö(BÍÐÉ² ³ÉÃíûöQ	èÕh=ÂºÐÆV%ž±[áøVÖhåC
+ê8rä1äøÄîy 1Ï÷8b,
+pÄîe0a¶0»Â,AØgyÊ|È·À
+ðpvïµ
+ª;§;µ$ÖpÝ£
+×k)/¢Q'.ÁxÇÆø8/÷ã:Œ4:ñ
+kåÓÑŸ6>àÕüà²öé/ \°Ø`±]Œå×µŠµ7Ì$<€¿}s·oÚ(ûDçDïŽ9Â
+Óû\ç
+ocÚ2r
+¿Ät­e
+°I{Ãó	ùÿÐô©ÅçË`9GªºJz0TZ$a$i6fZ"jÍ°7  Ôv¢ Q
+ÍQJ,J.ÜÐP%ý`Xì­Œ€Øå²pñ¡¬]s[µH(KàGÏyoë=.ýÞ¶÷Ýq`Çòå;vÜ°|Py³ø²g&í'>Çã¯ìŒï9üPüÞOáxöO³VS^
+ÚÈh¡
+`tÕZ:]A6Í"û%Äs8¬üŠlŽÝF{0ÕÂéª¥;ß¥"8#×Ô«ð*¢æhÔè[ÒÏU»cØ£=D
+Ñ`ÈÊóBHj	9ÛÖçœQ÷~Qžœ®÷òÌgŒ9¶¯
+ø-@û2Ð7zy/®ÿ<÷ÿŒðø_šÔ+£Ìs©ækm×Úo±œ`ûÊ÷UÚ)òé9;IÓüZº¡ñIB0XRJÒ}²&òü~Ãï÷~Èèó3j®no[Û±gn-£<¯[0Qä
+î÷ =øy²^H±î«&È|²°€d£LŒaw¡ g¢TH
+×^Ýr/øŒ1À&õçr%ë¿ ìF*"UeŒãÆ¬p®žÃÞÿóöÍ×Ýø >`ÿíÝ÷Î\ñä¡G&fìÜÙ§jÊÁ5}Î]®³ýøûãzá±µ{ %Ç$Ÿf]@É(zOÏåTÚ_]­²ý­c­KÒ®¹ÚlÇT×bucµºÎqKÚãªÌ
+LtLpHU0%7{
+ÓÁ6œ¢8YÅ^2SÏvfø96#Oµ- aaÄÐF-B"»yÚqå
+ï{cºšíøÎÝçõátJ#N7$¢è+ìLúé$
+Ar HžÙ^á¢aLšžP=O=J>®Q(+2Š-ó9+v=r}ÉÍŽ°}õìYë
+mÁï]úæéSoÜÿöÃø&Ïæ5­7.ßæx,œ~Ê7ß
+Ø÷Ú=S'=Ø=ãÅÛÆýZìéÔÀÊHE§õrÛ8eŠr¿²CyCá0CÔ»YÆ²
+8ÙÄHQTõMu0Ëš(*+0Ïç0r«.#
+SÐ2ÛNŠ?Çq²Y*ŽÒUAÏ
+
+-Á2a£
+PSUG)"	ì3·ãõå~l êE£§AôŸÖ}sŠÊxM÷(g`èþ RÁþÚbj{â}ÝTc²ºÅ6=œ¢z ,£;ÝSZÅ=S²üPvE3 Šák3de¬lêº<t×«¯¶ÅËð€Çýç=
+ßªqO×
+jÜ Ëßëé}ËÒ×¥o²?iEùPù4Mì
+sŸž"Sš+¢§Ùe§ÍnÓlqí@^ ºÝ,g8uóVðÈfîÄN§ßjú
+ÅïQÙåÕCl_µNÒ(hÜ ±È¡ÇCF
+ÍC<¶p²à{@+÷÷ý7yÌü³<^HêtA«;A"¬°û=¹Fì
+åžÈÐjC¡qsÃ¥	Òh:H$`],ydôÎÍsolÛ¹~ìúÜ
+·»«»ùX\tÛé×»p¶îÖÃÜ¿§®ÚEþ÷øñ3ïŸvÇÔ2NÙsqOä5éæÃ*fáGDV¹¢F«`VRÔ
+ChÇë3ÅE\(ýêð$<0ÕPÌÇ+ÀMxÍ©ŸR
+Ý\U{ºsšvÚlêc©YcIsEûXG/Êm¶ÉÌŸõñÎÁåÌ¿ÜÂþ±sý=q[ülû§;ñ÷øµ©> 
+^ÍF©R°
+âðU,S
+y°C!ªFúmb
+Ý4Óç7Ä6!)å ÀŠþÈsO I
+eÜÛVé
+M7î2=j:eâ	Gä
+¹F
+#O÷É_ÈI6ôBÏsfÖôŽL1W«bf¬@ªX¹ÒÔ+d«Y`1»ÍrŸIU ¹x¶MëêêÔÈËh$ÒÞ ÄAÏ7ô;aç[}
+AÐl/ö:£+p·
+Ï{¿è|
+b`í§ñÓøD¶ÊpÁ
+±ã
+ù¬¢üBÞB_m'|<ŸT«šDÀ/ìÄØÙÄŒu.Î²r;Ÿoüpüåœ7ð6¶'ËŒ çp<f	
+
+ffY>¢Ž
+
+%ŒÄ!œ"}&xö$<NQTP	QËd{«d^§såö®¶ÿG[ÈìûHh!æ
+
+s:Š=æ=æð_Å#(4Õã¯ ¿UµŽËÿ¥ÇÑÛë\Ï%·ÈÊøäœžWíO^'ºÕõ`¹dÆúB­H!ÎµµÌFí
+îUþ vJ3\=
+Ci3M­Ú/Ê/ê/fUX53&YâX
+£ÈuW¶AqÀÂ0VqÀRÇ<at.!QùN'lBt@ÓfÄ0ö({e6Œµc¬)
+ã
+³QÁ
+ÝÖ,ÂQ¬Z"Üeùð£€<zazîójÈS]åë¬>YEN
+Á
+­éîŠ°eÎíðaóáÃkžd	ŒÜj9ž5cøøqm¬
+
+ÀI(ñµ_õxAsC u	2ö ÉáŒKÆ}öt×Û>Æÿ»¹&Ë_ÂuüQ_÷#ãñŠ×Üv+ÈÒ&°Nß}­¹óÑJ}ËÖÆŠJ7Kü,ßb®IUån2ñ9.ñäägžÒ%ÉnËÈÏÏËCþô RfF?*
+Q|éCè¢œ&&
+©í<²`LcþX¡5FQB$+±/AfÂÁâ$Ä@T+zd}lkáô«6myy}ü.|ÙÊÊAkn|8þ)we€ïø£îYßÉuÔvå%9/ŽÌØÝØauM¯
+8?ïìVA©S3bY*ëÓßpK ÒIGízã2;
+l\±:t`Qzº9}#º{y\=ÀŽ©¯©ÇÐÉô_Ò­f[º5=Éçs­ùþ@æ uc¬sw&7'ý:Û­¶ûÍæûýÛñcd»õ³
+9Osh>P{rcbÜfAM³g(LZ+iË 	`}îH@Ä¢7cÊÄó£&@Ä$gY
+E£
+4Á°gCYÙ@
+[vI1K À§ÃFÝÛvè²ø+_uÆ?z`î{èpA¯JÝµãËóŸ^ýèôøùìËøê¿}
+Gï>ñV·­w>ÿùçãß­{,ÏÃ ãAF,@õH ÷·j$BC%,ù2Óµß3.ò=9Œbz¢ŸËôr&MyYåœ
+ð&YUá.Ëîbø4Æ
+Ä63¬<¢?]²5¢t&>+±!$námN	S0<ãaüûÓãoš_Žpèµw
+Yßcw<Þ£íœsî¿Íu8Ó\?zøÉx|Çäâå=ú÷Ä×ÿÊÏ Rðèç7¡	ºç2DQÃÒÊR	å_³
+£A9 Ù§²Rª×J¯	IFÑ Ê`ÕéÑx°­ -RË#lö¹è¹¹ñêgâêNÚ0Àì*hëùFK6øBc !f7â3]xºÜkâ¿=ýdPèóïOÞÎ|vî+ÒÚ5>µçÎ®épy @Âèïzÿ4G4æà+E;¶1ÙÙ(hs0§cÞafŒq$'
+6ÚÓeAKÎId,{#S&ÚZ­D¡@Cv)1Íd£xD£Jóûü^?Ã+-ìdFÄ0	
+=jz¹,ö ì°ØÊâÂAì78¬°ÊAÍÀ
+QÀ²bÚÿP©-)[ÿ€%.·ÐÐá(E©°2CÈŒ
+ñc[ÿ
+ßÒ¶ûtÆwFv¯Ú?Õ¡kk0¹ãSœIõ3žëÄ
+ðÿ/lÑ~wQSKíðëÖn9
+ÿ­er¶RN>ºeÈÔLáAåíÎRÉä­ò1È
+!&! |CUbJ2Î¹
+š8 SÕ&íUï64Cè`(WÃ*#x 	5£ ó­AXB°~ìùãÐ¡.ëèzÿ£ìíª
+¿M[	­bÐÝûš<:6°·ò2c`oIi²ìV,sóe(,Ó3¥Ç
+S(TµÒ ·ÛÅ,oÝ¶¢VÄ8£S³`çFxÜ#ìõixeOxÖúæU]
+çùD!*š%ÖQom¥
+*ßÃ,ÑLv
+)Þk'Oêf0)à/1:_7$õ.y« sÓ!ò7žÝ/;áÄûâ-p?
+/ÖW b
+$Md(«×FR*-L
+VÌã	ìu©y*'ÆÔrs
+Ìôt±VœÜ,ßG63MâvæI·Ù\ÄÇ¢Ç"Nªš°À: 	&MTÕlÖ(F[Ø:Èvkzìáb;î¡Ë$te
+	:È@<&8BÚ~H&
+kídÌs®ká®lßk¥á¥Ã
+U
+èº0 î»°q²ðFuÕÆ×
+žcÍõîÐØEñ"RgøØŸÜªÀ±\8FÅø·ÝfîMEŠïïÆÌA#:Ý_3WÕ}Ý`o*ÖBAÍ
+ñÆ.wyâ¶Þ³ñ"BQÌ=³+>ë8ûÏ;®ö sîö­³eì³ªM€p]ü~·ÍDe¯TD€(NÏD`Qb	e=4L8`fj45ZLIi(WŠ,gR$£F5Ÿ R6c»'	£Ðß6Q¯1pý51Q/NVcB×
+Ýï
+jq²J÷CŠŠPL0;`±ÓíÓûíPMOVÓ¡ê€Õßv;SŽJN6ÐiJ8aLÕ[|!
+¯yV²+4-g[À?OËý÷>ÄUih
+Þè³`æp€¹ÓÒXVc
+&·)ÝáÞo~ÕÌžÝ4H×­uö:·îÇÆj£­ìãÝ<c|cÓnuo&7al&É	Ûñµ€ãtKÒÊë¿4P4ré@(@»Å,uÌ­ÐÄÐÖR`MÁkqù[žæé¶øþÆ;¶¿Ó?ú§-ûîwâ7ñ<üÐ¡øãÿs<Ÿußëxü_âÿÅ¥8m/6Ýÿ
+%ÛüWÕËŠYç8È`m°c6ÁÁPAäö$}€-"ú>?GMÙï¥Ž¹áLmç4)jÌÜ`Ð
+õžäÝY;÷ÎúâoÄ×âë^xžaHã·p
+fÛŽýówu=Ãàõ+&ÞäT¡¥Û@R|B;³ðÝb3±­Ü?>sº8/µµ'ŸØkóByjoVN©n§çj©Ò*áøß÷ŠGÇá|-UÒãúBšÍü#Mýóü€¥æeUòZËœêK»å[ó7Í¬(«ÅaµZ¬E²¥ Ï%ó6«Š*G\n7ÃíFÁ,f
+Åb3"æù@vSvK6åIÑ.ÔkûEŒ÷ôPdG­I4ä§É±SîÂ`ê
+§EÝ³h=­¶TŸq³aFÌ &>oÌ
+dÅ¬ûcZLX.hFý%@@{éN;!SÆèMpYwøíkß|¯6wôÄéC£¯
+Û-8øxÛªMCï}4^ÄuÔœŸìÁÓÃÙCÇq×W®ÅLIÅ²3±öoØ 	¡žþàf
+»YÄ²á2&æïË€÷Ïì]3©&ŠÍœÅnÎU#Ù$É	[JCýÂýÇÆFçf«sÌÓ
+Ó<ËL×ª×Z®×g/¯fÖnQ×YnÓVeßŸSÝdÙäÌgU\&
+<Ë
+³³`À¿Žn@;]šxnÄMx#æq;nÕÃÝ22\ÑMJøIó|ÅÁ
+Gl£íq ôO1
+€
+å4
+Çg4ôÄ©ÈŒ4Ú^AJSÈ:ÛãLÒŠ¢§ÃíbÝ7xÐöÈÄçÔI¯_?ÿ©Ã&öÏ
+>kÆ
+ÿŒûÑßWs
+;Z·Å*ñÇãZ®]}ö¡×â¿lÆiWß6öò
+ýúÏ¹'G+
+6ÿå©³Þ^iŸõöêJJæäöÚ·dñÑ
+Ÿ>Þw£ uºÊ 2Òš€v²po ?Ç0)d0õ}8	¢àšžsRç	ç®
+_kÆl\õùIü2=Î®§qêÎüB¥`XUÜ
+šY#qì8ñ
+uQ×á×QÊökØAâËÜ·AAÄJóüŒä\Ã\€ÑÕäjq1.Õ@ÅôZ	®Ôç O¢
+
+74IRÃàkÊåw#V¶ñÐÔøÙ÷ßÿÑthÀÎë?ÜÏuÛýYüÜ£·cõ;ŠîÜö]uÈC^%Ôúàõz¿<±æÙ"*·Ælåhu mg
+k
+gëÑîï³àÂEe¢HªÙbQ
+vÍér{<ÎöDÕ^yŽTlVZêãàK XpšÅ§Çátzl$e8mPµY% Y
+fµIèqr«Ôâ
+Çx4EDõñØlV+}n·Oë#áá(X;aÑïÐ°ØëmÇ·îNY"·¶pNÏÛåÚZ¿¯/Ø£ó8#:r~o^{)êùsöeY;|VUÏ×.]² ²Úc=í3Ilù6B4Y%
+€Ì°g¯¢s:]ÐÄ%vúØmPØK þÐ¡_÷Úñl_¥Ýßÿ­.äïöõ+ñ«¿#ž
+ñ7ÑÕ÷ÞóC6óy/þã/·¶1ÏóoX6àì£ß<xà·K÷ROí%µ'ŸÙksPêfš°^X1t%Q¯â	ÒC×k ÂæÂÊaóÄ|¹ÐÌÎÄ3ùŠÏycç%²É!Ë&á%ø°xÆ+&
+*bS;ñê,KÅ4·.)Ò]n§
+ïÓUI	 fD
+Ù@ Ú}{0ÕNÏ~Õ|(Øì¡úÙImiñ5ÕÏ*õaM²pM÷šÞ£¢5tlLÕàV7ÚOGÅDERØÄiÄ$NÃúõÆ£ás$	|pl·ºún)hÅ%IL
+HôêzëG
+
+Öÿò+±ÿ®çÈ<Š6^³|ùÂx×¹œ]wQzç=j¥ôF_ï¶©Z÷T­¥Wàâ#&<Ía0f%e9
+,Íñï`eaå\¿ÓeÃ^(YÊ;ÝpOLrDùyŒ·ÓP$ê")Õ#øÌqüpäU©
+šìjÄþUµ§«ŽÚ¹ÃUÖ*9øf*ìf-%Ó¹šA%aO02òù>@ŽY¥Àëáò
+j)±tæ]qî
+Öwîzf{óôÔA;wf@tøG|Ñ ÔóAŽVMsíÛçè
+EÜ(ÝuãÕ
+J|ËúÙÞ(UàtývIòœª/?OÍÏËYÖ3`~Ú?[ßXŽN]w¿ëßÕù÷©ÜýÞçs{æþÍùY®ØÏ
+3ÝhA~id¯(#ÖG§³¢K5ÊÊïêïQkE©³Zav©»8èðLÊGòü
+æjóósÂÌm1ï2ÿlfÌf?ãn'Oé.Ï=¿_@ýsäb?cÊ¬MFá`v; k9:0
+D"»"\€G
+CfFšŽ(v0F¶ÆpÌ
+ödf¿ÄåI&_ÍŸG%¢³TÀ3
+§«ºŸúZÿç'Oáhsrp$vq€Oaêg
+/\a|ËJsÓUœá]Nh0Œ`KÝÄTM=0{×^Q6çž€ÿÚËÒ[=W»eíSÃ4Éõß}ÕáùçÍùH$ýŠÑ5O¯ºršÃ¬ú²ÃòÕÝ.«oö4ß:X<šûÒSgW]V?Ëõk¹µ
+W4Nš»ìààjà ËhæÆú3S,Ù\×ãª3[3Iff¿Ä¹¿)sc&ßÓ^åªò
+q
+ñ5
+ê8KëJßlq®:ÓrµëjßÁÌOÜx¿°ÿèþÑûeúÌDŠ7ÀZ
+
+E\µEçXqÓ¹OÒeÿÐÍifyÒü`Ce§ßlòd3aÍ€Cž×bbM pB%LÓVÜOa6Wã:Ì`oÆÔDÚ:>Egù¬ÎjøÓIÒŒ 5Càü@d ¡PVØçÂ|!îödÛÝWíjÖãÿ|ñ
+9€tô
+Ky|ñgž®_7ÔmxsaüçøáM/ŸõÈ[Ç^=
+=,ñ-Ó	RïCGô3ý}í}Ý#í#ÝöF÷äæ~õ1í1"ª^y6ÅÌæ+Mjú²OÚ/ïS²Zù0æ¬IùÆ©°,2Æ]QÚ¶¢èË	LÍo<~Öä·`K¶9+
+Zmf)Ï:ÐïÌ>*àL¡Z BŽÒÃjjŠs©R©¡@ÁÁœu.8Ý¹àü85Všl8y
+4b7RùTH©Úþó³Äÿµà»[vþOæ.ïñkzìæÙ·ãUîçât,?ÉÊ]ÛÒæÌ}åœÝUT:øPZ&¬VKÕ~*Wæ(ó%£äþd*7MâhôÌ|ûÀþ÷+ûWÝ?x¿2$ÈõQ±ì£2(t'ÙjwWORŠ&ýÕÇ@ÿXy:CýÿÆõ>mÖ°14HI°"-Æä)Á(lµ5íkVÝÚhm±²ÖE¶ì£Âq!!°vu#x3J¥«¶DÊÈúª:i <º\-ªÔÁ2ªÔ ÕIaÇEÑb*§
+^ñÁâÙïßÔž©poWàÅK
+ß~ÝÒm«^öÑ-Y7Œ1ÿQClo¿ùò«Œ}h6Ž1$Ë	4û\üN2ià€ÑŠiÌ
+nŸ4Í$jHÃÉ±}Ìýá8ãzØzz{øûØj}}üÃmœ#ümó|ýKù¥Î3äGC.lQÝîa.
+iß²QÛªMcÓü²šàIø
+;[W
+_ÚªbÕI
+ÃRZêéÔ2fâLW-èÙù¥,¥ÑÚ®"hšž+5_ÕÕ\eNFêžyÁyaK38 q0bØEæÊ|ÿ;þçlÆçŸ÷¬²Ÿë2\©
+sËò
+xûÑ6	¶@Á¹ñÏã¿k]
+3ñ=«ûÎ|úy;ž§Ýh¯á°Å[è-òêÞ&ïÊêUô©¹j«÷ õÒÞåú2KÓEQ,~;IÔag
+É[
+Ø°ë¬;Ì"Üñ§œ=*Kq(ÙYºõšÇûî@AtË"ŽW:cÛÙD4C)fM;4+/	Œ.EÐ
+YyKâd*Ökš¬Š¥\
+R5tÒ=[¶Ø}7-21­²xD¿£Gû×7Ï)­k{H®iŒjý¹é CÇ3ße |tJo48G)ìbêïà¥toz)â(ÅLåAŠÇaiŠéùW§¹{š §wšwÎ[ò`y^uA©&Ø?oTpTÞ,aJpJ^cAKÁ'9ß
+ýcu»xg;ÙÝë·ÓðQûÕ¢cJ×õzÎï·Èý³üìrKä°ÇsÌ5·înt·žY÷"£¬Ìì,G-Ç-	i©¶ÔUôF©BF
+yœÍ4<CóCNŠRDN&£¯f°bn:¹døÎ
+.ÔLwDbÿµ_¢Ówû.º~­ÇŽ~zêêwo{áÚ'Š}ºõ/ßo~âúåÛw^»tû8ßðpñÔñ­·âªÏîÃxý}-çfÿvtéÓLþ»_zûW_î¯AùÖ6w@.6ätÙ2Š?Ó¡²F~W¶Û[ê­ÕÁpYüà0ÉJXÒKÊK>(aišºKËK[]§\€ÉµÕÕêJžXqSS
+pò)Û Ê@,ê
+0ÌJü3Æ?£§ùUIHÁ·!nfÞ,ÍŒU
+ÑAÊ4ÚpH&oYCV*ŒÓºŠíK
+Ü¶xÎ°ÛªÀ
+þóÎÇ
+ìD¶­¹näí×w=2¶ŸËlÌBèzœ¡NÚ(mZ¥Òqé$ )SjZ€-©]'€$gJà«0Ü qÇ³2/9Äna·²­ìAöËdO±±öl±ìPñ|TVU©tçdÊ3nHe@/Ö¶µµ±?
+=zÖÉFÎ~Bó
+Ç=6ÚÐfœåÂ\/¶[Íqnã%,gGX5Æ¡°VÎ$ÐvxÁoµlœhVQÔ°,o4áLSµ©ÎÄŒvÇÎàóiÌ
+ÕhøÚªk)ö0fÄ.4ÑZR²F§fQ³DDMNÃYHCI&Ð€ß'NfÓÑñ±Žº->3«<³¢Œ­€ÏœÙïÞ}÷÷ë6ÞÉN<»õpíTjÝþÌotn
+¿­û~?^b,ê/ÜÍ\#°aâ©œ¶
+6jÒÆ;ÆýfØÃ³*òÒ  ßM
+'_Ã,?aŸä
+'x
+â#BXñRµZ§Ö³õü8¡^º]Æm^åÿÆ~Èä¿þÅÿ.:m²Ì1Kx^$8IÃïaÙ0';8bLØ1ð££&ÙvlÙÃePè¡_|ÁõÂ÷!Cê@ÞŒúàéén5K
+
+{vÇhDð ô) p@¬bu2%O¥ô$Š§WñtJ?=Åû{F±;JŒ3æ<Q4jqò{ÆÁ
+->ß£Åødal)F±Ût~ÎlôQ¶ÏX,:\ð4£ÊXÁUgöxèÅ?îNKê
+°JÕæV° ú.>¿ôy|Û
+®ãÜž5Ÿ€k*ÉŒ6NsÌo1š0€{ýÄSªšLNí%Ë¢
+É2+9õ§Á*YžLnwcë`uc2¹&®
+Kp,X0ICCïd
+x -J.±:ìFZißFOhnjKÍÿeä#àBèÕH¹IËx=)ýÃýUû; n1<iaB~Þé7
+b>äójò±0ÞÞ&aÐEsx£6Ü°ÏÞÓ Š{)	
+ñ1)^&JŒÙávŒtïEE
+¡ë$
+8ÝÐe6AX@}2C¬îKç²ÍÃ
+q(Ö4lSçÍ¥LMÓ0aT·/lØLÃ9_j=·?1{Éœ7ŒùðS{C{7ÝÝ6nê=ÙÈ=C']5®c×þ®
+òÐÜI=ïy¬ë^²géÒa÷ßÑõqÊ|
+Ôr¡·u;Çðv²]k×ŸdŸ±bÎØyêl à2
+ß§
+óð$<l@t
+.8Ì»TY5+æláUL~Š¡
+Ô«xNyHg«§ÕsÐÃzRât¥
+í?
+ûŒS9]tÁ­#¯UÔÄ]ð+.Þ*É¢,È¯E¬Œ9
+[d[`tÚ
+Çigy*Äœ`k
+YüYã¶aÜ?ç
+O²{wõoª-ŸŸk!Y}õŒ>wŸÝEóWú
+ÎšÈ^ÖlìUðWcøzq?KKµ¶®2Om°m°«¿g"7Q¡5Ø\#<óžyÒTmmkªçìxNÀâFÉ¹Ì4n<WÝ~V°È9²{vžŽHÀHÐ @Û
+Ç© Á~/¿P7g#
+N¡FPŸÉ7£
+g.Ÿ\@£cXf$7Rº»JbAÇíZE2/Õ@Ÿb~Ýò×O±ëºn=
+ï<°gÍê={W­ÙCì8çö%ñt
+ùáFÕ·ßzûÝ¿Ÿõ&<zM|ºØ å
+ÕWŽnÚeÚ`­ŽHf O	¥;Ó/Oo
+l=Ý=Ó¹¥Õîi³Å9Ê,m{NÚÁÀ{Ï<ùÞË8é8q"žBlT:ËØZ
+;H¯}eú!=®¬f
+h°Î» XGfoö1k².7Ê-2+/ÂöRb#ô_ÃõL×ñ×Ý»4\·W2ÓA_ÅäXKHµæ±wÎ\{löâã×ßÐÝúÄ¥O?¹háîø,îÅuÃ¯OÜ÷hüì­Czve
+;rø­Þzó# ×ñYÌ	 üè/ú}&%ù^d0YŠðÕÎjï`ïÆ­\©œ4­:£œ_óiSìSÒ3Z2Þç?°}Í§|ïÑòHuÆH2Ô(ãÉ,ò±ò©çK×wÞ¯ÓÎfUâN3ïp
+ÝæD£NÖ,º¥ÑÒba-¬ÿ%êLÏøÎMÜÓUÿIÔ­© œ<
+lÿräß;úÅøÏóß»á¯ÍtYºð]K?EÄ^Cqw,lßôÄíôev
+9òÊkïøE« .œ
+Ô±¢ô^
+v¬±8Ä²}ÙìtvËKVQ%ÕnTÄØd¥Ü"³vl'YÖÿWjpøJ=©5^@³h§bçSæö}vqÍ2Hò?÷`+V=Ò{Võ+{_~y¯+
+ld[ó=ÌPÝž ë}ÚþêÄ·ÌnhþX¿ÍrdõIý²ÇdMËZ.Ý.ÝýýéC*¹}
+wÑàÝ\MVeÏDq¢4QhšLTg³¥ÙòlÓle¶ÚiË±Ð¹³ìŒòìñrœijdjî¢Ð¢ìì»ä;sï-ž§è1yòhÎc¹{#žÒiº-#6^Ì	+2ëD¬©{ºFþLoµ·Î;É»Ë{ÔË[ŒÞùÞã^6Ó»ÁKŒÏÑñ#?i4ãDÃÇ %a
+jž×á*5R3ÌÖR»OLNÒýNõw7eú°/Û«Û=¥Þv2ag>ç
+ËÇùŸbzU¢ùÆâÅ€ºž¥kãlÈ¶d
+¿ ®zàké_FÆð§£©á¢fã£`Í»àä
+0wÒè9Ý2BhF¬M³k¥Ò+€a®¬2
+°4ÒPVHUÄ<Á¹9ÌGÙ4©¥S§œøæfêÍ+iÒLaþÅ$ÿHNwëÊ+þc>ÓzmÚê=[®[ŸŽ,|×«ëúTæß1òúÇ[[
+³Ïv¹
+Ón~éÞ1³^œþèÇø2ÿÓú]ò®
+:`YnfôëfxFL
+Qò§Ûåì>Ë'ß2ö*iÙ|n3r£HŠU
+Šê} Òâ
+GQeÌ &E-2JÆdÑ²PVma'±¿Ô¿QhZÀÇlZ
+Â1:ÈläÁå»§'Åxõ¢ºÔ
+tÒy j  0rà¢Ñ°;9öDG
+¬Æû!Æl#Ñ|Cª®[póÍ{÷í³Gs3¶mÑzO{LY
+¹ñÛÖwÝU[à£}¹	ŽæñI/
+@>:îì.€pJÏ³9J£v-Ú]
+¶»L ðVè*q
+=nbžñA7võjO!ï4ù¶úZ}	ëøöA ïç€c	²ÒPï
+°µó<º Ë`ÝVÅR¯@HùXÍ¬ZT:³IsNc°JREk2xÊÏ_	N%ár"F å6ÄÄŠêå\ùhfj3Y¯
+>üö^m¶]1¯®l!¹³kïm=¹a-A°é[FÌ·@_ù\èYÖLµYµÆ$W¥"]öÄ÷{¡Ä©RŠÓ>RF°åÂ
+¶ŸÕ%@ÛÈ+ØúDßÛœ`eQòP®cšLŸ
+Çà1€^
+'MÇÓÉ,qŽ]¯!ËÄ¥Ò5òŒ¬fnÖë€Ð}Ò
+ò3èùEô°[~ýUþ} ÿŸÏ¢Ór8Ù\r.¢¯aÔ!l8Ýæ*åt 2YaIvHñ1×qncâdA+T°%êº1;ÚqÚ>
+ÂÂAMDÇYŠïÿFYÖéóv5t5ø<'R/\Ÿ¬±ÿÈJÀŠùbDr~
+ÕQÎ³ñ¹9ÎôD<¿tÝ<cþš%d-Þó¢ÏGld·®Y
+8ÍÉ ëëíVÆJåSÊjþôdt«ïÌÌ.eyE²ói×Æ±åMÉ,Ú4dg
+_L3¥xùbÔ\Êb/s?f ¯µâ`S_Ë ë ÛËÛ
+aª8Ã¶¿VX$
+à;,ûm¿òg¥\5åª9æ\K­ÐQ*l×«Åû{'ñv²Ýô²íç;Ì¯CTü±ô-û­åÛiþÉocÉydY4)¬Y­ _÷rÈhOÔ§Ësà« «Íå³¬(aÕìPU³hµX¢²èËé}`ÁÆ«bVe«Ì26UQhÖ3e«ÍB³©dÇMÅ4õŽEeÔvü€.êd<_^AgvÉh]ª³âùÖVþ0Z7i
+n4âAÿä>|Æ~fºáŒµ§<`öáG ÁóßgëSa5ÖÿÉzÁ¬UÑeMj>spkæÈqmj@	'Å8Ö,[{â1/lÌ
+n-
+	!¹8¶[ oÁàÈÁ­%Æ<8±[$÷ÚRI4EéØ~KÞ[lO
+Û#Ñ;îA€#ù€7¿pÛžÎ8±W°Æ©|§÷÷Ûbš :``7Š€°'I
+Üq»ÛÈ`r<8þ|Çj¶dÇ-eíßo{~GÞG ôŽŸI®îºï­#dúÙOÈò}çô[À
+ý/H¿¯yÎbÃ,¯1Ø ï÷ÆÆ[6±ÄÍæû-¹üAá-dÑ]1cªO+Ã=M+ñí&±Ð6­êMãÌ÷âûäûLÏvåuÓæ·µO€wÕOµ¯dçA$ÌóÍ0Y,`tUl±š	l6QM¢ÉŒ
+XdíUôªDŽ0
+IQ_U±V¢0²á;á5€Éu6lšÞ dÉÉŒt.!yNçñ-ÆK:}us¹dÕAGZNœÌkØ0-ÚWÚéN#
+ø¢sß)jHœ ³XÖà$×PPiº0ÊÓfö€ÇLF
+gzLÉrÇXèö`L3ï1Iºÿ|å" : ö©ÄM-U
+ar°ß
+ßüG»ûÂ{?ßoýìñïH.ÿ> èò³q¥ë
+<š>Þ@­W0>ù	øçÃköZüØB[ñ?ëcÙ%3ºªA¹E¥]	ds©
+[)GÉQËrµÌŒÙjÊµåÚ¯pÕÛêíõÎY¶YöYÎeüuõZÇµÎUê:ëzÛzû-ûäíŠŽç­
+ïåo
+¿ª]Úï?L¢=ËïuØíaì
+#l
+&l·ÙÅÄ3~¯ù5?)ô¿ä'þvRœÏb×mº£ÒMÕ6ÝF&Ù^²[;Ÿ|¿g¡þi2=d³Lº
+P:
+Š$¢À{-ÐYRÝXÆÃçÕºèëSÀUÎìÑNôÒ×Õ;}
+­Óš!7çY,^:nGyŒÆ`(X3h€4òy€$ŸEŠÄ·ø}t$>ß_³*bfpÂû1k*ë°Ÿ*	üŽç$§*
+¢¯{²V8zT]á¶F8S|Þ¡Ï¢YÑ/Ûâsûd-S±CËÍNcIgs»6/^¹|	söõ]×€|Î=}ølÆkuÕÖNÞ
+'wÞÑ%šàÞÆ@ì!}TòH®TšÅpL
+kH8PªÓ&âQd8^ŠÍÅSÈA®ÃÄë€[ñ*ñéw|€yÅÎ£RL|\üTzÓ¥,D_+Î(NzJ2e9	8é{sd2
+.ÊU€FÍ2iÇ6p
+Oó
+¥n5cdÖÍæó)3g^ä0Þ
+p
+t(Í¢-
+Rœ8ðJÓÆìÖeü3ÆWN¿2&FS@3/8Ñ¹¬hr t_
+4IE€D­CÏQòP%_þh®7~šÿ|ÅHfIß>DWÚeÔÝïqÓ]¿é²+F
+°ø\5ž€ó¡dŸKyIÐK
+[8.^ÇLízyþ²Ùø;¿ó®+¯
+ þÅâÃÉrî=®ÓkXp®Íb-%t%xLæÒ°
+V®
+ÊËÏW2Ã;Ø;+žÌÀ³i6kóöLGÜ³ù9=#ÏæçOB my>oñ$ýuïŽ®Jþ1%Ú
+DKöÙÞØ*zò@ñËhô_èð³tD<n¬vrpR+*#c*3gö3ÙÆ6\=
+ûLÊ¬Œª@å%Éµd*©[~&?4øÆøýç,êÖýÅ^	÷+HsÏxÏ\7Pé-Þ/0z¿þKÈO{¡ßyíÉ2$ßœÑýIAÅëFy«pÐj«ö` `VµùzúÃ9¶0Ó3÷YÌöäEÃpà¬n3^¢¬ºH
+ÿAëy*8Íf"ð!hh Gÿ¡Î­ç@GwApgs¹È#€5[x%'\J2õQ2ìø²ÑÓ.ÓL
+ŸéŸóW
++ö}ôJ€oÙCkæÃñûnFßÂÇÈLæ-dB@<R7KüÛt ÅÊØóÿ±SØÙ£È^ú5æÝ3sÖ=÷ÌyygÖÝwÏ:P3q¿ÉÎ'@i2t.£ùSF¯ñg*þ×Í¡bÙ
+øÍ;î 9T#_Èxà	¢Ê¿ë·xwyÉÏÂÏvr\8n'G
+£vòðìvÙÉal6ØÉ
+Â
+vrV<ë sÅ¹2^
+ï š8Ã.
+nÅbBåw3ó;1«+U*ªR1}»Ð>_X!lÛ+
+UfU©žš»}¥æÅXš«FU³`âõÐ¿J&S	öÑ?Ü0jšŠâwjzA;ù6PÑææfÜúàìÿ²äÉ^RÇù
+*J|÷ù{øÝÇWW
+Ë«qO{±À|GroúTjPêxÊA°
+äpÂN	Çìä pÐNZ
+V;yDxÄNîîŽ
+í€Ih²iâ4)LQÊ¢äxÚNi£š@23OtGT
+±ÙR¥ œrTwoEQ)¹ÔÅ0UHh~ëlOêORï°SR%?ùBôïÎ&Ö:5ÓwÛs!ùj{É%õ±/gF'1?_aõ7À5iäÅÕíæ<ÜªôòëÄõ"QEÜ%àÏ
+ò"}ÕùáYæ
+D@X Ø=+éßlÀ!-Nîet±Ÿ-2¡<Žì¢L@ïhçðIzm]ìíÎ¥ÜîóßxOË|/38»HŒ'|)ÝÂ+ù§ï·d¡x£HFÓ h±üJ±'_h6ºÀjf]þ:/€TJ/%øòÿFÛÔßh¿ûãmË'Yª~ÓDã_Jù2Çøì}íyý]]3ŽâØ.üK7¬
+Þñ¡š¯þØ/ÑzŠö_üåQòÿeÙ
+h ^Æ§Ðrc¡§ ìeGêøhXÃRËX|©}áøz>ûje-¹1.nÚÄœŠÃò0ÔÿÖ®=8®êŒ÷®,­dëadeíõKÆŒ+Ë2z¬%[Ø2ìX®}µ{¥œx_ìœ«ELË€h
+fÒ)¥4Êdy­eÆ
+­Ý8
+Ž¥ŽC$Î$ŠŸÔßùÎÙ,I-ß9ß9ç{ïûÎwÏ"y÷Ñs¥ÅøYÐ_ ï¥ÏÓóAÐ|
+øÐmÀË
+¥0.Åxc3÷xö~¿è ú]AÈZŸðö2=¬œ<÷ÖÑÓ§!ÿ!1èWýmØ×Xïß:?
+üFè*E_
+X
+žIÿ
+uúw)
+œÿÀnÈÌ "Ê	úžu fá©_ü;Ñ¢ÀÓD¥/!GŒ_Dxþh1înKŠ*× î#ªºðWPïŒ@Žïrjo$Z×ç®o ªsW Î­(%ºá0ý_V¢¯
+
+ë;,
+ãßÖŒNŽn/ÑúàI¢
+G°µÇ6BÇŠ5·mn&ò(ð8QKÑ¢­µ èhû1Ñ¶_µc®£šóN¢.ØqËÃD·í87bï}ïõgvAÇmö
+"Ú~ï³7n­û kßÛâsì9÷£éŠ -Bm­¡ õÁcK*þO0\hg?ÏC?ÿÃ­ó¹BÛ¡pªœ©pÝ¹è¢ÂKhÑE?Sx)U6(ŒŸYÚ¬p/5ýÂËé*UxEÉ7X³ÀÓx_áKh¢êq
+WŸPúÂ«èhÕÏóçëþêáü\TýŸÂu*«íUžµ­
+/)¢YDKj÷(ŒôŠÂËhŒ6¬p/-[V£ðrš[§ð
+Ý¬þ
+/Š-uvþÓú·Ö=¥ðJÏÚË
+¯"ø7­Dx}IÝ2ÎYŸñR1¿|%ãe<¿q/ãí«I\ÆHâ2F1xIÄe$.c$q#ËI\ÆHâ2F1žÄe^QŽßÅŒÝ/)¯âœñ±å/^»<ÅøuEô×9
+¯+¿yb|%ë2WÑøðuLÿÇobüÏßÌøi{ì÷éZR4¿$·QŠY4A&ÎE}	0JaÆ÷áÍVà**vb.Zó6Sß¬çÍÿ§€@Þ2F°¡TÆÁÜ
+ôRßêÄ«
+6+¬g{ÁA?IØà2×0ä9$M¡
+±
+1¬YÍ[^TŠÒ$émxÈ àcÔÌZÄÉJÉebaXe6V\Š³.áuWipxAæuy=ÆRD/l³
+¶ÚKelÃÚÄ q/íO±65[e³|ë1
+§YvXi·meIÝ¹ùËvGIÏ\MçBŠÅ^±ÑKÙA5bOX²$ÎqI²G#Ì/,ÙU\9
+AæRZmµS±&œYðÂ(
+49[ð«­ŒW;±>Å£BT
+ÎØ[wíÈ
+'¿±eyIè9¡¬5ÿÓÊûÏB¬{g%+¶¡  ö2Gâh'±6¥Œ-%Î²É±Ùa°jÿ6G-Â4	>g2cÌ)wRÝv>³¬ß£"ekDnÊž9ê$GòvDyTÈ^÷ªzã\µ¿ Ò1ÎRìéÐŒÜŽènÌç<âo'ÉípsÛà
+ž}ëpÞ¹
+É|Ô
+íòŒ³Ô?MÊ²B=«QI÷2¿ŽZÈ
+òj!Ó€ö{+Á§d:¿nÁæu=T:Ä^t?gqNzs(Ê54gA]í5Qï&9ÿEt»èÒ«µ¢Vv 5è&H1Hòyçhc¬}ÈëÂèkçIuî£,ýD>Æÿ×/ã2©*¡¥ê[¡NI©ñ<0è óÔÈúö¡
+î	ÎÜÇDn:ìí°æ§ý ÅÓc`'v$ð!Ì
+þ]hoçùÌ g`7Œ8×>
+¥JþÄÇ
+Þ¥­ÎáÕÏÐÜŒŽXF.¡b[8ý#yqø ÉÙfêÜ~r?OãŒ:
+úT^g0_C¥ïRÌ[š}:
+¢Bêµ¬¶ªÍª
+,ÅÊ×^áÛ1¥MT)U³ÇóO=©ÓýÏär+¯:ÙVþì$¹N¹ªnLšŒ¿¿r§]xÌ*Ršõ
+T~\
+ç
+,­
+W)É×ÐÞÕ|OÉÊ¿0+jÎÕPQ-MŸÑÐQÞvT­ú(Ý~ÎýXQ=^KÝfO|JlQ=+[6·O¹¡r1VTCszÅé±§í¢§U²èÆÕ§NåmáðñÖEY~.¯âóä¥9þ'8ÅÕ$WqÐÊ:bùáü~€]ÅÙ
+U[ú_ªÊB
+C·£B~ìáœ/\î'mº	ÊÝÈ{e£»*É«ü],öçÊRuuï`i*ŸÅ}rôsòäŽÔ]cþ9'oa
+¥·
+7ã Ë\xs3¯òõÄ¯emÁË5Ì¿WÌ·ÈR·eOÈñéÅìfÏÆj£v<
+Ž[0Ú÷mï9Ò ¢láo
+kÃKâíŽ žnŠmxo"@Hó$}ŒÒüòó³}þråûšçÀúùtŠóy!¶ª¶ÂŠa®Ðòº_Ý³âê/Î§|&yÅæ -<7DVwVâðëÙ
+`ú(tÐº\!D¬üì9ÆY"ïþ<åoVCï ÖúhÉ­®ÊÇŒìÑé5a-ãKÆhØ2öÅcqSÆÎx2O®
+HÐoô®ù	D!ÌGRbÆ1öÄÀ·¥³³e3V¿ÑÃödØuaË±SVhg<æZQ!$9m8&0oO!Ë±'cÍFoÒ6#FTŠÅh<iáTÔÙkÃfÒº`p\;ènØX6â
+-€²ãÄaÆB	ù©`Ø°(;fž©e€m7v³ñàxÄð0&7çŠ­k[ I%§ý»$>e%MlÏMZŠÅ`Š°EG(sâ0MHE"@ÙVšÆ¡Ä
+RË[uÜéUì	
+Gh±Q;ÆÉø	5a0E1¶,dq±ÛØaØ$àž1iOYLÀQ6ÜaD-ø.fAn&ÜZP"ÝmgÖ=ØLÔLØ Gš
+a÷º*o
+¥/qËH9VHzÓº;%M
+ÿ8¶ØëÚ±I±õ€
+ž»N³q
+a5'Í{íD[n°Y:
+ì!ÛIDÌi¡BpÇ¬Ž00
+$!èÚ,ÈÉx4ÎÒü¹\í[¶&S3Ùu|"k[ý
+­ÆMûì`2.bŽ©ör÷1Dì£fòØñÇe>ö2$ŽoS =8b
+0]£ÑÝgMLøÙ0+âXé0ÈüûF÷ìÚ³³wtÏÐ~chqûûGÞÝÃûöVVTV§EX`l»v9
+y{pòâI3f="ù
+Æ§éxJpEÂºT,ÄÙ@Bq^#'ld3ÈÍÉ€eìõc`Høž8zàtç#Œ)h!ØNÒ
+ºÈ	øŸ`{|ÒbN<ÂO¹
+3ã8
+EÚàäBòç]gjL9¬4
+dU1·ß8ã<Îí{RÁÁ0
+'aí	;žpçŒãŒf(d#s\žÅt}Ëá*£"vÔŠKÇ'
+ØÃ<O#gRãÛ	=%Ý
+ErÃ~*1mÈW
+¯ý±g¢°9QñîNY«A­ZÉÚARÙÍÄN8«S¶%nÁö
+"i¡j
+e1¿GÅÅ8èb,6f*«'®-MÎ3šZ¡Aév	#œÆfãŠ¶öFûÍ-m--åå1Ù²eK[Úö­íFûÍÛ:·uVV]7Ñ€Ói4ø`<Z|&,£?iŠ
+/pa$
+ÇÇqB÷£fÅQàÅ!MÚAÛ4FL>X
+­!;v£@Ôß6
+:ÇLQ'übòWdH[ÌZÌ"FåGŠÆe(ÎoÅ$Æ]ŒÔ*ñ0¿ãwù*[áË¢žKKÈóçŽç/= ç<ç=_)eòÅ 7~e[ótYó€±Œ-%%»KnEÛ	jß"Ôu$¬eµ/x¯xâÂ$ùz&däFsÄ§.üé]KÕåt0ðm 088	8(e:1Üž xWz<Ëg>»µgÝgž;sW€Š
+
+ým
+ùÔì÷Ý!ûþ=¬KmiÓþ>Ùoh}íúÖè+*[/öÖyêè{
+ñZMÿ&UkùèiÏõèR5Óã©=³®±õÔO	i
+Ý£ÁcŸ¹
+mŠriko
+>§_¡Zòé?Ó*Wô©ZÚzªw¯þCú:àÀ£ÿ¯wôwè~ýmñ®h»§  ¯ ® Jõ·ñz¯7õ7Aõ
+ ºÇ §  W eúÐÖèoß€q+ðn®¿¶Fÿ>¶õ}ŽÕúëÀ^×_iÿ4ÓÞÙzŠB|ë²|¥BjëZgõùp£oVÿÑ£É÷toþ*e:œ
+á¯8 8H Jœì5Ê 
+<
+ÈJÁóx^ÏeÀw ¯Q p àÕ¿75³ú+3}ŸÞ:ýïõi9ú]ýo¹ÿþ-îÿNÿî¿Ÿýeý[3
+>ê]uO
+úô¬/ÒÿúÌºZß\ïRýÜãC t Ç '¥ú}ÍLÈW!/Òe/rÞåþôzîòõ4îD¢iìºSÆ©Fœ§ñ?ÅP4}hïŠñÞŠ12L4¡»ŠñÈ1`¢i
+fVÿü_¬Ûàk:¡œÕz^JÃKix)M%zZŒèÃaÛÍlÚ=ÕÓŽq/s^ËŒ€eµÌ3ZÆÒ2÷iŽÌ-ZæN-Ó€eêµLéÑ2/j
+â¯cŽæ
+;{VhËZæ«ZÆÑ2ZfœY§e­œgV_=³g+wÜéç
+ý­;Z«aãjxt5Òz5ýŽ¯ æxÔ"c$Ÿ¡AôkÎlêcWkŒ÷6ý/!è-@	t	it	B.A@5ÚnÀ1ÀEÀÀ.ŸÙá-}
+?Ém5Ú  pp?à
+ Í¹Ð)®Lü:PF~	/ñÔ«õÕ=«jêkjnó¬×ªŽ¡¹œêêPâjzÎjgQùË_TRyo¹þ~V!«þäÌ«|³Ú3/úz¯×þJuZ'5jëÑwÃãmTï}Õë_Fß:SÈ'> ±Ùw^«\g}ÖÿØ÷ný¬ô'õ/úþÅ-Ñf|ÿ/õœZÿïÛY/f^jÕÐ7ô\}ï«ô,<5ã»Otg}¿[¿Ûw¢,¹p§QOµožñï6Èë¯÷õ8yÖ×]§ïIµMðõµÀ&n±ëYéÚx°}V÷4=Qvžlšìæ²Ö²æ²ÕeŸ²Ue+Ë®óÖzkŒUÞ%Þ
+¯×[ê-ñê^ò^'>€IüaÂu¥5ü¯]JD[Âx
+ÿmŠÎ·@ºæÕi/eyõÁ>m0{1HãFöç#kgµ;d­íÓ²µ48Úíh-Î¶7
+fËüÖáÓöØf³úÃ³
+ÕæÄÔ+Å×é#M[úà£+EÓÑº©îÝµ;vîê¿Fs\µEÿ gÅ<|UöÁÃÙçWe[2·jl0ûGâûvÏihï
+ôÓÞÝØás
+ÚÃbÞ³£llpV;Ätdhïó>ÓyÈtdx$ÝSn=øA·Nt +/§õL·ŸŒéJ4AwÚY7ÐzÝ:ŠYnÃ4Îr£æòzÐ¬_Ï4uºÌ4ë2&»IêëAÒPÏ$ÚTÏ$õÚLrš@P$äI
+aM
+­@S/i*ßÎÑTŸ
+Š_õÇêkjÒÎl
+ßU||í8ýÌTxE63n§cêKã
+.zÓÊ­µú³ÁµýÆéíG¯±|T,o_Û
+>}ŽÇêÙÞ³}`­Ù?vf÷¶öyº
+Éëj;p
+a°6¡kwû5ÛÅòn¡«]èjºv÷ìf]Ä9~àði/õí<*û3úâ
+äëñ«Çúêj;8y·¯^qßÊóž<GÆ²KÖöe+bisïæ^±3%ªÄR«¥÷m_œòŒöZªÁôÒµ}ÔäŠ­°ûå~0åŠÃeÛä|ÔÖ²=f¿ã
+f7f»ï8røtYf-e»rsÌÎ]~LvI'O(ænsååpaüSªç¿Ïè/Ñz41O¶apTG)Ußü{
+×%ñxpÆ°AGkÒ6Ô¿ÉûÍRò«zÉ'çüxPªþª
+
+endstream
+endobj
+
+71 0 obj
+22902
+endobj
+
+37 0 obj
+<</Type /Font
+/Subtype /TrueType
+/BaseFont /Verdana
+/Encoding /MacRomanEncoding
+/FirstChar 32
+/LastChar 255
+/Widths [351 393 458 818 635 1076 726 268 454 454 635 818 363 454 363 454 635 635 635 635 635 635 635 635 635 635 454 454 818 818 818 545 1000 683 685 698 770 632 574 775 751 420 454 692 556 842 748 787 603 787 695 683 616 731 683 988 685 615 685 454 454 454 818 635 635 600 623 520 623 595 351 623 632 274 344 591 274 972 632 606 623 623 426 520 394 632 591 818 591 591 525 634 454 634 818 0 683 683 698 632 748 787 731 600 600 600 600 600 600 520 595 595 595 595 274 274 274 274 632 606 606 606 606 606 632 632 632 632 635 541 635 635 635 545 635 620 1000 1000 976 635 635 0 984 787 0 818 0 0 635 641 0 0 0 0 0 545 545 0 955 606 545 393 818 0 635 0 0 644 644 818 351 683 683 787 1069 981 635 1000 458 458 268 268 818 0 591 615 361 635 454 454 625 625 635 363 268 458 1521 683 632 683 632 632 420 420 420 420 787 787 0 787 731 731 731 274 818 635 635 635 635 635 635 635 635 635]
+/FontDescriptor 72 0 R>>
+endobj
+
+72 0 obj
+<</Type /FontDescriptor
+/FontName /Verdana
+/Ascent 1005
+/Descent -209
+/ItalicAngle 0
+/StemV 0
+/CapHeight 0
+/Flags 32
+/FontBBox [-49 -206 1446 1000]
+/FontFile2 73 0 R>>
+endobj
+
+73 0 obj
+<</Length 74 0 R
+/Filter /FlateDecode
+/Length1 3588>>
+stream
+xÕV{lS×ÿÎ¹Ç&	Ny4ÜcT(!11
+:-àD¶Òæ&vâ@ÛDIÕuéÔðHik¡UÖ2öè*Z4.3IF
+-¥ëº¶iÒ€j°©*MÀ€UZ-ÞwNn @Pµ?w¬sîïû}¿ïq}ï=÷ ô<ØÔ®
+Ð
+d~
+@²:cŒæm×__Ãùzsž¥ýÆª9µ JÚ«[Úº}0ñ Ó1f}0 ù?ª¡}mgGÍ[LÑ^lu¥]M5ž€Ž
+4PñŠ:ŽÍíZWÝ€ëÑ~
+mÞ¡µN<z$ös8÷
+CÑXr`íÙLøÃ@8Û5Ÿ
+íE {"œ8# føÆAŸYò>ÌÍ6Ó-ÓUö
+Û¡| É¿'¯OtMø'êWAM&á(Œ#p	~4Áyì8Ã'Éi^Wágxü	nÞæ^cð.èwé$ûS8?!
+ÈíÃÈŸ'§éB°Áà8ü,0žÔF&;ø,ô*~È
+"pÃS
+ça/öuÔ ·¹ÍÈFð®ø>²#p9yß@Õ6Ø;¡N£â}É-A¶üÈ"gèvÃ³p ~oÃ{Ø×³ØÙa!ßÔNíÏ1Û¯Èz	NbL¯Ùix^w4Û!Vü·¯Lø_â3ÓHÿELÃ)ºj\UÎóÕìtÑ"®+ù
+Ç¡ûž'Èû
+î÷Ò"¯o»Çg·×/-âH»¹NžG¯ìÎïó]šÓ|;u×K
+
+n»Ý;áäøÁi®ÓµuÛEI1\gèK
+2FÂlÀÕáÆfäïj±ÒQÙÐ×Wéà}
+}Úp²§ÑÁ3
+}§œÞŸ°§ë°y»N})O¯<X¯g6É<3ÑD¥o{y=³xk
+Þ-ßÚÎ=}
+F^Y-- âøp+x°5Sb.ÿô³Oå²â{=+ª¯zLðµ81Èä
+ûÇê±®ýòR$ýÎóîq·ÿåÝäàDýìWR2šñ ¡2{Â[KQrðëßÏ~åŸÍŠ E¯H,w5© Ð}î¢LX«ñÞ8BÊ±{á]Lþv;OLmH#*L?n`ã·!ßc`îwûlF~ª·Yšy,ð±ÄüAb³ä?xäÿ)qÀHö,È ¯"~ËÀ
+<MÌ NéM0ª6#¿ÊÀ³àiê8Mô@"ñlQØ"ùógHüÄ¢.ý£Ä9³édÿ6©ù·ÄsD
+Ex®à
+±Êd?yR³TâR³NbUâjIýVJÜ$pìYÙ-ñdþïläH,ûWÔuÍZSk
+~î×b?ÁW9ù­MP4Ô
+ã¡H8Ñb­¡e|C[÷µ¶cQîDÎ[ â×:4_ eO¹
+·Æà¹ávsð|ñmQ!ò/[Yzá­Q®ñXDóÚµÈ.
+j¹5x7ÅŽm¡€C@n«_àŒã«Å­QCÆküÊ rZ9§Ç9¢*'qÛí0F6£¿	ÜPs!b9Ÿ68¬2ü9=­š`(ÎfÔp|@DÅ°\ET+¢X
+ØC
+}Èµ@}Qið@u'®~|%ìµ5ém='zŸ¿Þ{ôü)ÔèCtÏañ
+îÉto\1ÃJ(Qu§z«<'bòüýèoìBNTú_þµéWöVëŸëwéÚ°öôk.¯úr¶¡Š{ºÍ²ÌË6²užÝU¡ó>(ËžvÊówÞä#x-:dãÓ*G²F|[Þ?ðýéZ/(vfÆyÜß
+Ç{âÇãzüJüZ<m<~+N
+$üyóªX·©ÛèŠ­Ïl¥¡:òÃºSutKí<æ«Ëj}sØãÕ>VYœm¬.fU8«KËØÚòb¶®|
+[_ngåØcå>¶§gyi1+^ég+KKXiI
++)]È®\+¹U¢'oÉ¯r'¯
+Étàñ+ýLªÕy&·uí
+Â¶n
+
+IÅW®äPê"ç­
+ØÃÂmá.j}ãÏÇšëÍ¹9]oÌÍsº^èèŒ<çÞÞ
+Õú¢µ×Úo=d
+P_TûÕCËû{z{ö:<Ð;°o`¿ÕõœÔL§5¢FškwªÅim'ü2áòK7/Qþ¡ëC|ïhÌl€.ížF­ß&KmY¬ÈÏ
+mel-Øæ0Õ¶Ùyã¶µìã\ËÍÛÈòr×²\[1º
+l7ÛË²pmÄeÛPáŽf,QÁLÒ/zUË¯6îUSqÆŒ*;çU¯JGœ*IxU8ëU/^X¢_¢sm³«£#võlÂ®^žøAúùñ÷ÓÇÎýÒ22ú%qvØ9Ö3F]#=#Ô(OlJ|7Á¬åCÏ'~H&RÒRW1K:ÅO6
+âÝl"Ã$ÙûòËô£ø)€÷,šN/~\ô×ë)ÞZB¡ÑX4Z8ÃÐnö5ÝìpG
+!üØÊðèV­w!Ñm nCt_èÔ(ÎÉBr=3ÕœÄp-ü/uæD
+endstream
+endobj
+
+74 0 obj
+2040
+endobj
+
+38 0 obj
+<</Type /Font
+/Subtype /TrueType
+/BaseFont /CourierNew,Bold
+/Encoding /MacRomanEncoding
+/FirstChar 32
+/LastChar 255
+/Widths [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 0 600 0 0 600 600 0 0 0 0 0 600 600 0 600 600 600 600 600 0 600 0 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]
+/FontDescriptor 75 0 R>>
+endobj
+
+75 0 obj
+<</Type /FontDescriptor
+/FontName /CourierNew,Bold
+/Ascent 832
+/Descent -300
+/ItalicAngle 0
+/StemV 0
+/CapHeight 0
+/Flags 33
+/FontBBox [-46 -710 701 1221]
+/FontFile2 76 0 R>>
+endobj
+
+76 0 obj
+<</Length 77 0 R
+/Filter /FlateDecode
+/Length1 42204>>
+stream
+xŽœ	|åù üŸ3³÷5{_ÙÝÙ3Ù+»ÙìæNvB $"
+Q#AQŽ'±
+X°ï£¶ÐÃµNØB«µµJë¶
+µµJ[Àªd÷{ÞÙ
+bÛÿ÷ý~_yïyçyû}f@!€E.ÞuÉû
+ê	@ËoÂŠK®¹{è£ëå#)>YÒé÷õ?
+úîK_·dé Ÿ!öCµ\¶xá¢7w#ô
+žFÍeÐ ù>õ>š.»âêUu6ÿï ~\YŸâ
+HÓé@è;¡®Œbáª~åj\ëÚ
+0»ráï~è±@}!ç/ûW\uu!àü»Wþþû
+ª ê!dn6Èó'BXþçŠh$+ðPj
+ÒêÒ
+k4-Èj³;.ä.CÈãõq~
+K§GPyE4O$+S¥«2ÕY«©­«G
+MšùßþÿË_Kÿ3ù {r/}1ò"T<,
+ïnú­
+Qš·að~éÿ ýž
+êä÷ z
+Ò
+8Ö 5øìZïE!œ
+ÝîžÐjD£AQh?âè<ô ü>	œýûÿ@SÐëh®0ŸÚ
+úKø&ÊCù
+ö3aô:.2cýº¯Æÿ¢Àõ+šœÅ4Ý
+U&ÏôytºÝŸ
+8PŒ²xÉ
+îÝ^|¬økŽz· aüº¹©žÎ<]îAÛq%ÓÇŒ2úÂ­ÅÅ? ®|=5ØOÁd±â<TêP
+]^
+sLtŽXx·ž®G­p¥Õp×»Ñ/Ðô<¿ÎDdšŸâ«ÅwµÀ¹÷c~,àiøiÊN¿FdÈŠÃÙ¢ÅèRŽ­DOÀïå§8sx*5ê¥î€î§^€ïenbnYö R38y<ÆÀ h]GßT@0·
+µ£³P/<ïzX©_³>F1,Á+ðMø»xÞÿLœDÏef0oCäqM /?*dm5éAÏ¢mhýgž£æ^óð|ß€Î¢®¡³t7}}#œ~~Ç<[Èþ^ŒœøâÅ·,Àõ(h&@z.n»ý®úsô6ú'â)øJüM|þ!þ	~¿ßÂJG=M×Ð÷Ò;ÌðÌýÌËcáG
+áÂ§ÅöbOñ4<ßÅèVt'`ÛÐãqÛájñt|>ûàwàoá'ðø¡.€vÐzŸŸŸ>Éë7e×z÷vÓÅ«`Æwÿs5 'ªE0ÓÐRÀ~t
+ZsŸ`þMùíÂïÛð?{>ö \ OÐI¬Â:¬Ç
+_
+n§¯Æwáññûøü
+a&qªM]
+ëùê%êuêÏô\úúúuúuÆÆÌbÎ,|yVdFy³ò·_
+:ýÜèC£šBŽÐ[TÝÅ²âôâsÅÊåPðr6ÐÔh`Í0¬Ô«`­ÿ> 
+ŸqGð,|!Ÿ }ÀúQü#ømÌyÃïøíÃ¿Ä úoã#ø¯øKÈKEšÌøBj	uõ$õSêEª@kh7
+x6ÑŠ7ÑkèÇáþ@ÿþÑ3f&Â42{§3/eÓe³d×Êò»äë%Î±"7ÂíT®Oá
+ -@|õ2ØÿÿÃï[ø3ôk<ý~· æQmø`Òq-Ÿÿ¢©Å0vÚ~@?ß¢nEwõW¢!ÅÔežßI7ŒÚþ±èåÔt(ïv ýô~Üþÿ¿>
+gé£¬èRüTïÄSÑr*èjŒ0þd<e ¿œð^æ~êoÔýøS4Ú(Ìù.ŒmÂQÀ·ýøô
+u©a~
+X:
+šÔ£çPr|
+àæ£ ^ÜÝt6šâA ÞM@'­0ë
+t5jÃg#?Ã*dÄßl¿(ó[0§ÑÓx.ÀœŠwÇTðü~20**~ý_tŒ
+«Ñ£èÏè,úcqñÈÚTábt°x6ú
+p,~Í@ÄëoÌ@ï`únqy1Øž¿ØóŒ
+]ÎµÊŒÀRËÑÏäïÉäUr,»^¶H6G6SÖ&«UÉ¢2¿Ì)3ÈÔÌ§Ì»ÌægÌoíV2VFK¿üsý0œ^AÏ¢ót%à€fšÏ©¿SQ¢RûšÍÔjzp¢_.vµEs¡P8Yx±ðlá»
+ûß)ú}£/~÷ôë§·~=üëçø7
+/A|£x~ñ¬â) 7KñÞbsámŒ
+1FŸ~|õ^Xlçã©Et Þþ]èIÀ±kQ:O>uÁzG2o°q1ðÚ' FÃZ@äâgÁ\(àÒå i_BÏ@×Ø"ËÔk+ü¹äÓLôÜþ¿íhûè#p·'åOÀ]wÉ7£òïÑ_òç;÷9gwwÍîìÈ·4756Ô×Õæ²Õªtª2Ç¢åp(ðs>¯§Ìír:ì6«Ål2²œN«Q«
+¹¡)íÁi}ÜP€ogÌHzp!4,ÐÐ7ÄAÓŽÉcž>a7y$#|e$/äÇFbkBMÉ×
+äöO
+rÃøü³çCùÛS=ÜÐP%PÑAÅï3žvÇeS¹!ÜÇµM»æ²µí}Sáz[4ê¶`Ûbu2¶š5PÔ@iÈ
+ìßí-X(Pöö-Rê`VC®àÔö!gp*Â
+n_žhšûìùíSÝ~O21Û.	^<Sqajn3$oR·áÇAëž-}kïfÑÅ}qí¢à¢
+Î¢ö{ãpß©Cöë:Æ«pqSÛü5{ÝôÚvÇRT×®]Ã
+m:{þÄ^?I{zàp.Ö·vÜú.EG
+&BŠO
+E|šÅÁvÒÒ·R§/[»¬ÄµvÍ¹Î¿Õåâw W;·vîü (ïö,Z¶ÅÖÎ¹nç{-¬QæœA*huÇú0fÎ'&3
+v qp0ùAx:,®Ck/©að×á¬¡E°KTm}kÙÒNÎ
+Ù ·ö$e|2¹e¡Ô"³@£²PB0è/âñ¡Xà
+¢
+æØ"ÔsÉÄ5ÃÔþ`?ËAàCÝóáŽÀÜï'«ºnGCehðìùbC»·">ï¢úHÏŸRõ\Ò3Xê;œ/è»]°[¬CÊÈØ?k3·_Ö0mÿ¥{±Ø?óàÌ³ÏÏµ¯í`;sî€Ø_7Ö'Ìmói7%(7-ô&^86Tæk0üŒhX¡TZ07mí!Š=j¿ÿÿxÒpñ89KÈÆOŠ9Ô\oT4=íZ&ÌDšsÏ_»V=yê³ãCÚð*X1€é
+²9ŒÕŠ?7Î
+éûÂÀ@c)I0{îü7Üþ
+n>747¥Éq<uŒišÈ}H|%©LžAHuÂE­á!{ØÙŠÓMõÍ)Çãd:LnoRex
+
+²-ŒÕi$30
+÷6¥$AgÌLmúßs0ÿìá!gØØ&åi$ÍEàCX~÷ü>÷Â
+Byä,|îü!¹ ^?a£ŒôÂ-XáxÙ¹@·C]qøTÚsH~ñŽ	p:ÙÆd"%$žHþAAJ®È0Œ¶Î
+ô÷}«
+  úÂ
+é^ÛÅàÐ91ÒáÜÀú"=p
+c§(Y»vZ¶¶oíÂáâàÅA
+®ÝEÛhÛÚþöŸw¯sM»«ðò2Ü
+PèÚ$°5ò^¹â8ŽÉã4RËeÇir©ÌqÊ78â³ÙM³Ff³§f±£M(ß4ÚDªŽßè7!ÁA§9zßi^ŸD
+³îñBñ}¥Áò yŒF¥Q(ë3Lð^wUcºu\Xbó,Ã²ýìö{±Ãxù¶Ž«_ ÔÈgãJä³§zGz²GQ~ôXŸ*V`Z."åt$­©Î §-€A¬QÅzŠ.«cŠµ/LihÒÚØØÊŽ>ÎåÒÔ²aöŸÃ¯'ñæ&%õœy)aæSø^v©U*¥Þ¬©2nVœ7SUÊÍ)Ðk(ä4]µ§ €ÞY'FO:1
+,
+öKs
+/(±²Uæw¹9^DÌ[Äë
+ãÅ
+çûÅ÷ji
+líZ°mwðNG ÈTx
+,ì§Ãât:Rá|
+OµrîŽrOm€3>+¶ã-ŒÚQ¡\mÄBµ5Òd2ŸÜBªŠºL*OP+*\í(Ãj±4ëLNSò+ŒÎ©Ëvaø³NôÂ2 *H%XH
+=ÚÿÖè+ã7±/!£É^I °&l$[-¶êLL&¶×ÖÔÅÕSÈéÿ0f6¯ZõÔS«VmŸ¹¯mêŸ¶¶>Ü?ª-ÓÙCzu:QŠsõjóSâ §Vä ÿ²j39sóuS,
+m?©x¬Fö€ªÜm3ê
+í«6?uíØÀ4ÃŠÖQ1GÊy*-9j7ãóÇW
+V{dâ2ÿYX]·Ó/c
+ù|Qâ\óöÑÉ`»¶òë¬0ÚMjËiÃpqß6§!oŠæÛ4âAmîGräôÍÝ
+ß+Ýh4~(Ð
+÷b±Ä(«ÅDTWž{yü(OCüüÖH¢É±üK;á²)µøÏm»íWçb-
+ßlÜTøÍro¬ÉŒ~ËUCÄcøÏÂ>êgÂ,sŒÎnT,êÓŸ4?m
+Tõh/»ÔÑö-ß&s;5qn0
+ŒŽªŠ\*×Òn#?êgÿyfÛoûÁÕ5M1où\³i#®ÙRánØ²rÙãx+é<ËyÇÌ¡Êr³ŠÜcÖ 2^<šTÚî3 nŸ9Q%ŸäyóçfÙÏ5Š=Ô0¯ÍÔÚŽŠ[CiÀêßVµìaà''zFXñ'p@±GEŽ­7ÕCD *>]b1ÖZòr
+B{€Œ|ì¡ñÞ-y££1ìHm©¯¬¬Û¶E£-[®šhù¢ÜWúbÞXs­¬·Ø=¡X3[¿»3ïÜ]Ï6ÅÂÕÖúk¹x+ñÀ	wsÂWKüü!c¡~ŒÚÅk)`ß.v2Øe¡Ô,ýr~ÆrúIz>þpÁà×ilp
+òóZÕ^ZÃÂ¥Þ;µ»±LàŠGzÜ
+O 93cÆ
+r\Ú0{v
+d&,{
+Ñ
+<kSî-5­Óq€[
+SçónµÆ¢VkÔÐÀB&ÓËL.«ò5Ë0~y÷±Ùd¶©_X£U_£¿é%l¬®Î]0ìàX#Ñ®pZ\áX³º×ivbMÌ©/VåXkSÄ­BcÄMæS.K!znr÷ñ¬Ó·»ž®uÑVWØE¹@ÅÚjsfäx­
++UjV§7°ÆÝÔ#Ôw©Gy·}¥V«·ÃžÚ-üô
+³>ÉÂC]±ÉTJ­ýgÔLÄàžäŠ.æUÆnv»¥ÙšNäF÷âC¢ðÈüH<
+ï­ÇAÅzã  ¡~qªjj©×^#ø1z?I_{>a+¯µÉR_~¿Ÿ2YOz^ZÛŠL +@íBjxÊÆé¹ëžÕÁÕ¡ÃLX
+ÆCÓCw^V¿€QÌT£Å¡ÃëÑ©°Â`l
+
+
+
++u1µä³$ç
+
+¹þ}º:zP
+Ì
+ÆG·aÆÃøíò`Ù)ÍvvºWŠ"gMò³Îò­ssªajÖNŠ+ZÝnêRäÇúýr,w[©KyëJÄ·ftºw9ôðõ)×+)¥3²ß/"š"ìÔ¿BS~dÄX_ÙÑæúh'A
+¬$ÀÆœÀ
+ötìÏ¡Ú
+1ãL«ŠDÛrüþyØª=}>»ªáÖ+®ìØõU@²+ÞŒéÿŸfÙ~oËÌáWnmkyh
+*¶/ÿøsï
+\«qHo
+8V}Á¬t¢2+êbJLª·:²øîéhŒÆônüòQ­Áz]Á<â
+H\n ~|[(È^t/¯µNWé¯(_]NïÆº7õÉûåò
+rŠ2JîJà= é q¯ñ*X].ÒArŸÙ§KéutLZÇëútŽÎßóøN² LW®P
+9
+Lbô( @rt wä( ³í:Þas1Jãc§Ì
+Çv
+5âq
+ßrÀ[ & š? ñQ Î%°ý"òãÝ+{úÅháËcwÌnç$ùE»ïŒõÒßá
+Fê*B LëP¡ðÛ?|:/ÓmnÓ¯œáºµ3|5ÕMhhoû¢iz 3åx-œW%·î5šEµÆÇ«Mµ#tLf'^(Hò	ÜÐ<3âg€JxähyYRøŸb9ÝG¯XQý.ÄRða
+>Àø7ÐfmQ7LíÜ@;sÚvãsLÄ³H}eØ%®H÷Í1}ÎÙpëÂXV8\ævT¡avWC}×ìÑâ)¯QgCžx1Ì
+Ì:àØwîBš8ÈÇ@çÌ Æ¢€ZeQ#³Ze&eàÛ«5f
+V1
++ÌÞZÞÔos
+5>f¡AgíytAœ@M©©ßm³ÎžA #£œ ú®ÑŠ£õ 4ë1áìÊJG\JàXÐ^
+c€Š`\"3Œz(ãoºkNéwk\±QØPÄó^*BE<-ñ@¢4í(æ'èH<µq§2I)a³ê6CÒfÕ 'vº<ÉYvÝ°h¶¶RM³ë=EJ®"ÚÑj÷ô}Ñèå$PŠ~Xbž
+ë*+ël<PþHòÍ|è¿ÕuX¥­NÍÌzŽ úÝÔ}ê~õ Q»¬34«ÿ3xqDRS0.É:?zÀ6æª¡ß=
+,Á÷Ø¶xFß}ÇÛaûâ_óGä;ã^ÞÄx-^ê0Â7Á#*hÎþ³ç]æÓºé.?mj
+ÆÞí
+wNœ·È9EZÁ+ºýAÅ&ü±,œÛ=®€%#ÔvdÃ6gbõNQ"
+Få,`×)Âq²'ÛnÔ1A«GœÆ+²ç\Ä8DµÔE<dõœ&¯%ëÿµè,%+öœg{ñ·ool=»mJ×ì6|š!ïŒªwQ¶cYÖÛoé|¬3}dóª'îTcc
+à#Å?,=ÍüEQº¿<ÎŒùHŒ6~iüæøúø+qy04þ&ª~}ËxÐyÐõAì3§zóRj©qó*Œ*vMbñÎÛâ·'
+=ÐÉ:§*&KÔk<n¥Út­ìãyÔ|£.æëÉM,ÃÅ÷xœ»,Ë 1ÄIB
+ìÎ(1ÆaW8åØ:çVÝ«NP± ±x'âq·âr3ŠÝ<ïíà#Œ·tŒ×ò:
+?
+#åQÔÊã@÷ï(î#
+ÀáÃ(M:ùž[Œ€
+áº^GûÏ'ªS¢{Ýír9Ç§Oã5L]Î«Y£fèG£€Ê¹@Ñs9	gÖAbdJcLàÂDÓ
+å5ªß¯y8÷©
+^ÅÄ¯S DIM\¬+äZàb\{š JDQ=ÏÇÙiì/@Šþ±-¹¡Nä[ä0Õ;Sœœ~b xØváü](kÍ'&wò1¯°ùV yüõ°â91ñtNL:'&tNþèÈ(À7œŽš2d¬w]
+¶©IÑD\ñÞ¡%ÞÛ+h.Ø¯€)Ú±õ2±OÒ 5œ° Ïd+|Òrr©à=ø¡öÊ¡5ß¹ã±ÇqçM"s«l è dõøŽõ×üwâ
+ËÀúpE<ÁFÅßäY
+
+ÏQ"§Ë]FâAþïj4ïí0ø³Õ< š¬gu"%,©dÓF?6ø}þþþÃ~)æý]P=ìûySÎ?LÝÌkã±2p))Lç#ÛŒ,äŒ:ÉªéÈ05×°Y-À{I×îOïŠæ#`°QeÎöúMÉUóû³Ùø0u
+¯ÆibØçæ<_R-G| 
+8>kÀ,Øžµxf¥°ä~qÉýp«çá9üŒÚHÖ·§	tS¢ÊRs0£vXÌV³Íl73òÊp(aÓ!:éHp
+:ÂA}$DW«CM!T®pX!œ±MDý"zùåªÖ(»6Á
+@4Y	ÌÕQÁõ%^âÈ²ù
+Éæ.º"Õ÷­}Œ3Ïwüùºî%+
+'ñÅ7U?²¡ðý"6ìÿvÛ5ï^<»­ÚË	8ñïæhsÍÙ§ße[VìxÐuïùQ^çý«j±°£åh)^TŸ(º¬ö|­õêòUµ»;=T`HÊ8ÍŒÎ\£Õ¿ )wy\
+ä)}^ß¥_ _¡_­ë÷P=HÔ­ÛÂ®z°µzPè»$M÷í4²Ÿ4Nã·Õ]ùž Sâê:ëDÓH/;"(uù£DòÏ
+2t_ÇY«2LQrkž:âJ8bÈµÇ3å![Æ
+ÃqÎÊœqÔE13ÁÅBüRr«àèQš\.k
+ñIsÒT:Z
+5zoç
+\óöÞ]Y.d¶Äûnytç=×<~?VÞ×ó]æN«¥óÙÎŒÝÙ5ÝÛoŒýŸ}Š
+gnÅÒÓ*jf6aúáu°õ(È(äŒ
+(²(òÏŒkÿí+ú<ÿŒò;ut;âµHžÖ$Ê,æ8ejœ
+Þ=PPveÀÛ°¬å
+\
+±ûXým"æ!€bµ&(E|qþ6bâÉHÀ*!žO ®×ø*TV^«×èÕzF
+	)¹Éh6Z4Ø\WšB1ì³c8¬ÆpÀè!ÑÁYÔ[kU
+^¬·ê2²5 w²4ÁG[[ÑýdÑÞÄôüõùE ë±îë[ÎnžûÚž>	®³>þÀ}_üÍWV{SÚ
+8Xù
+;VKü
+^ðrp
+õo6D|Ã5Ž	ü&s	f
+^Íkh±úÓ1yÚ):
+©ªL5è,šU;±yà(ìZ
+sŠÕy;ÑøõÞIZENRSâ6<~žtRÈÛiªIDLCØÎ»ÂÂÃÃ
+R%E&Lj c*¡	&\Fi(ó¥Êèb.k1š0«âTý*Z5¯äÕŒ3W$I$*Iô®&]Ã×t×ôÕÈL5ÃØŸÂaD4óóx9mÚd¢Ööš.69kç³r`É²§òÎâe¬Žé ±ë!±h!î-JKÁ ³ñœG{%òºNÞæU|XºBÎº
+|+°æžÈÉM<°gHtÄŽöBãïÎ!^XåŒ§­!CkÈÐ2Ž«ñÒP1«J#rÆCÎLrFÊR.rQÁ<PàÈhIŒ$ñÛùI"^A']A']A']A'^ÁöÄKø×üõ 3ú%ç¥qÌÅTÒ
+*í7Vgk%>'ò±
+ŸÒvºÈN
+ôCÄü)¶|õÉz«©÷ð
+×WÆKKJÔ!
+¿&ºõå
+Ýöª|ç'möTýl ©E@S[Šªñà1!_BgºâV	äDŽ`"-aŠéLu5Ú;XêLå0Ÿqõk	H		BÁÒIqo§?á0
+ÕŠ`¢:ºÐŽ1D¢3ÒA±	J© Ñµ#R16ä«NU÷Uï«fªy7¯Þ¯D~èÐzëÊA =ÔyÈXXP­-Ë
+
+
+×ž}YÄŠí:³»ñÓø/h cªIìÅ ú*Q4;'ZJI
+äÈAÌ­BŸEÄàÚG€!idhÒÈ1\ï=Ó[ùßPIÂ€wþ àGëDú%ÁÑ""ÕWÖ
+}^xcÌ¯)!Ñ¯-/Œ-"Qg^Ä¡$àÐÓC­Ô{új£^æµSG¬ÑhjõRONXä(
+1q±	L2õrpTÀC_Â¢zc÷vSÇ°HqNWUev7àËU£|*5Xe 8ŸOñõy¯×Õ7465·äùVÔê/ó}ÝLôÒlÄh&
+gà³ÉXIPÉ€YqŠ×;MŒ	ªÉß{é
+­ÞS Lþ(Ó>Œ oÄ±óžKª*6lzÇ³Îl,æ êë=j%lßUåË€2ùLWfAFáê|ÆÕÆùý gH·ÆŽÏÓFltN³{`Ö
+eéÑ£©øJEpvðdÿðžòœêc(œ#@ºRw`Bw'm5mêŽ©XÀÿ"§yXÌ	¡ à!DFÔ_.å¥q
+Ò[F7IZ€-œf)¯qrÈHúË¥\ž	
+
+Ò[F7IŸ§Dý%JöÛìdZþi-
+£d%J­ÕôôB§@_a9f×Ôæ;Oª
+Þ)4C¥¶ŠPÞáâS
+·E­ó±Èêb@£@x'­«ÕÉN¿'5ñÉbô$PkšÕ>æí.ÞÉKUòqÆßjÄ",QJtCðZ¢DÿßÏ9S"8·
+æ"ÎäL÷
+ÚÛAüÀáU#ÄRçíDð>L	þ{è*Øåàï« ¬ û6åJöù€ØLížµ#©ËÎøéé÷ŸÂ		$§$HúÑ	€xžqxœ|\'ý#§Äñ4¢éDôòo(ª'×Q}Š"êôv
+|®t×q¥Ž4Âçí eÒ%Ê$Ñ
+Ó*
+4ÈAÕBå
+j¡ÇL¬3¥MŒ©¬ñAÓ*MôÃÀÉïNõª7ëš(~Hÿ³
+#Õw*K@òÚkÇ%šþBB_,.Ÿp
+žfèì@ÖA9M".~ž³!WåhÈAq'ÿ] È*Bó2¿Î
+ÌÐ2»Æiµ»ËnuÆìa'cJó
+YD4Ù.D$Ió.JvÒ¬sðÅzŽ>œ®j]f#Ú~žêáÌ³èÙô3UÏdö¢œé#ãvqÕ²Ìí0àÞªG2?ªz*óVÕ¡ú
+û
+tŸSu8#Óün%ÙE×Ø6Š°tzÞÀœª¬
+éjùúS>XÃÄÖR­+µªnÒUÓ«pqÐTd²UÙ1Ï9õGŸÌáŽ8
+N;ÊÌÈàÃ2<Éð0 Cd
+v`wV¥3væÏÉmC
+èÀ^Q
+>X'Òµe]Y'U§ý×à²¢Œ(f°G¶öåì
+1ë³ibÖ$dÛŠŽeIÎ×ÖÕgÅ±Èqc»ãšãCaq
+s
+·	
+/9Þp(C,4€ªpÀZŠÈcµX,©T>E¥ûôðSÈÏú9ÚÏøùhÎ??äÙ,â
+Ó0§dád
+üö¬%²ëAæRÊà¬~åqI@
+Ú_
+
+á6M£ 69AåcJ	ÈQ
+<:Õ?qâ(ñ«ÈŸÁÊ8üÃB±~M%: 9
+¥¢cL.©-y
+éè éŽŒxÙôþÖ|Ä!ìS&íÐ	"ØTaŠ
+²u	@I§ØåÑ.-é$ãÐ I$Â°åi ÃJ®Ò3äÞ@ïJáiãgè¢%ñÄÌé t
+\à_ùV"ñþíØôÎ|]ž
+ÿìÈç;f~ŒN~onôAjYIÂ5$Å>êÑãÀAùpÞŸH"ŸQ00!Q8»ï€Ã(æÌ¡'ãF'UãšMqÌÍqôÆ8ÆWš&PòO'R%q
+ÜôÑ¯pw0³äŠ)Xþõ<
+á^jµS`y#o&lA6
+?A:P,Æ5äb&íbšÇ¢Îõ@3rhÐÃÅaÞÕsb¬=õûíT4#4èJ	Ý9 » ;öUôWVÐ|cS¶Âøp,øªê¬O0x³#»Å¬3ÎCÜ©Ç+Ð$ÅÊ­QØHÀ?1ª`ÿŸ­%Ž ûTRðW1L@ AM¡ŽBÖAÿ*]?z9?ý7ÔRIÞ$NÉnfñ}æqæHhžß/{Dõ°îÃ#æ­:3©vY»jŠ¶:W6WuVÙl,×ÐörZ¥ŠæoC.·r^çSt)(h
+ÓÕxŽ,x?G€ÝìCºîëøJìTEdaÚB¬VÂ6J
+Jf¥1
+:H¬r{91$6§Ypœa3ÎÈ«ÑÂòÇ
+ÝÿØoûØþ£øýïoXÿþûë7ŒO]TøŽpÿ{pÇ^x°páï{NÚœûä	xòëRŸ@q\ÉÛ<Š¡¢Ê	Vý$<7LÐ`D]ö¿¬üR«ßÛÁqqJ °C
+T©ž°É£å
+®,]Æuõõ
+m(ÛTŠÚP¶¯*s%ã2ÁXç y¶ícûÙA!ÊGœÝÇ
+`i fbO4
+sª·,FÓHþè(ðcâ!
+LÄdc@Üðº¢^ÏÃyhyÔU
+Âa?$îXG<¡PÉz"ÑÿM¥Tè©`¹[ÄÇ²0uÜâÆü+ê»ûÁñ³;Ÿ·úOðŠ×¿¢fþåGçw7}£qÿuçÎX.ùÿ
++Du¿8â­ñNóçý ê³*ymÕŽª¹Uçe.ÉÈìÚÄôÄŒÄ­ÛÔ<^³û5§ÖÇõ	E&
+'êÓÉóóâWKìLü*aXøNªTÓP0èä§
+C
+œ_ÅÐ_ÿ§gsÛJÞêÃs}ØGhco«n ù£;SìKbÏ·õÝüIò§I:É·åÜô
+,~pñd"%ú©J"*+Ú³N@£X<N]Pº>än«Ìd
+:ÜäŒ.>Ü|)H÷wÇûâq:
+ÛÏÉÅÿ%(h)¡ÞŸ2YÙg
+ JnTµ1M°«~NÝäý)=Äuç
++ð qaað?³r 	U	ñžtË©®ªŠä5®l§œäÜµ!\íI
+Xd×	!+
+à©ÿaÔ(HgÉÈ€ÊËE·Œ÷\ó	øxàÐ§ý­³g;-ßøüÇ¯nêvQë×~_+Ÿo?Ò÷œæúúëX_]²
+ß2{¿;Ô]Þp	¡ÕÀKç /¿£Ü
+gÃ µóú`8
+úºÇæ$
+2Ü"â)3¶ØŠÜæŸ=ž¶ùŸ)j°ÙóÀ³e¬ÖU¹4eL3
+ËödÈ¿ÇG@_q Yº$ouèAçCM;«_1Œ\}ÐðVõ±æÏÝ§õÄæç«á
+¹gs£ÃÍXë
+
+à$°Í-&
+9
+Â14y.Wî§óÉ®äääúäá€<ÄÉ_D(^Ý­îSÓêaLo¥UË§ª|¡TSGxGx
+2vgv#šÓ<HIe(PoáÕQC·XÑÇ÷óüá]3ô3òÛàlRß	goÐc3·ì`@Ü«
+¬óRz{)oÉÓò×äŽü³éOïå\é MPp`èhbqî BÃÈ ¥£ùÞüÈ 
+GI
+š(g€Pq`vD%YTjÃRä`övÙ€=ÍòHy%%Mt5Js ì6FÜ
+óq£ÛïHÕ×§BU.œçŸùß_WÓ\±îÉgúe×Ú
+²K)³Ù«ÏZqK®:7÷_þ?û,gÊw¬íÌGzXÏ×GÚloþŠÞ{ß
+Êó
+tæíÆ%¶HöÒºÈtŸ¥óûW<tÁ*óæ ÿ\ü3y?á]`³;åéÇmhÍ®o³š#Æ÷PkÙ$?wDòtØápHý²õmÚ3yåø=Æß{,õyœA`Ù§3Ü.âq9ï
+±(ÏÛtyç¬»L€%TÃCKÙxÜQÂ[  ,
+Ä4ûñŒÀ2â6ÙFŒ&ÃÔ7·
+_ök%#n©ÇxPH¬aSSÓô£Há?Æ13õµwøÊ·ï	Ç$_`ÿÃl
+€ðbño°F
+5Pá]ñ2óbË²È5øFó7,«"Jä&Ñ&«à£[5'>
+ Ñ/È
+FL¬ôaTD)eö[òæŽe5ŸÀ<×2/Ô
+>d~Óò9uÒø
+ùåÛ©°Ã©HJ»šÙfÞÂËÅÑ@.þ@ÁLZ#gØ9éz
+r3ilUIë[ZkÑ§#C%ÓDXÚ
+'¶|;45Ñ X©üÔÊÆß?ô2[žá¢{Â=o9[(
+y¬6UT-FZ
+õ1-&Ñ,Ä_Î×ÌP5m©Y)#GŸa6Y"VdBV:b
+Ž`ËQ7«Îz<**SVöà +¢ñOvžáõSÛêMØùó,ê&ßn
+ï@åxÝO
+q'KÇHïH¯ËIâ6Jæ±àM&rÔ(Æú«ØØ©W0Çcr&g.%kŽ·W°-ÄöØL¬``MP$Bõ!§È«á5 p ±P8 	q?O!ŸŽ	:qGo d[ÛìÉ< Š[¥]P"Xå¥p/&_0hÎÍæj³y²§Ô¥?;v}*
+Žb$rùÔŒ¹iªàÄlg>ºæ}jÚènúSy.n|h£/âwAªY2rÊë`åàâs£Ë"
+%`åx"I4.ãÔãª-OœÂ øN¿É|ÿËGšß%
+ùÑÅ°K!Êõßc
+¢@QTÂ0át%ÕØj5ùX>O4'¯ÐßšWÉ8+÷ òEù+Üò£òS5JÆÞû.áœÅÛa4§cµïâh¬6«5©É
+ŠŒ\ÑòwPM©ýž+š
+Of²Å&ÖïUDÕYõ3
+Šæm÷/à@ÉpÕÛ×€©sy°àSøH°Õêò;¥
+œY£#cû
+ òyaûÌÞäùyð`VòôÄÉöÞÊâÊñoAjiÅÕÒöZÚöPO6PŒý€îÂNÞ 
+èõç€È÷PuÆ>!*;[Sk)±_Zè³ZøíZÚ7k÷Yý+>ìœŸkÅw×x£õPýYßç÷Ÿ
+$«vüËn:¿Î×¹§#
+>·ìw-U
+!]c¥+bg­ÎÇÖÎV8Ë+<&CQÅdFt1Œ¯Q*ZKïTŸ¬ü@ù
+ñQ¬Öb#)ÓŠB\äãÈÇ±ÓòÓ\1€( {(xµ&+Ô
+PpóLÀÍWš#J$:É-V}";DcünÂÓU1ÆüÂ ®ð«5>² €`¥hWùQ`
+po£:¯p%PÔ§yŒÅl-m{ÍvØö©­hSlŽa[iÍ¿äFâb8èÄèwøg¬õªÔuW4Æùe*¿ÌçÃQ$<àÃ1e
+"îoA
+s¯ãÙH¹FÑVrM8Ž:z<z!Q ,HC/
+Çz~öþ 6¥GV"/7Y_Ñõñ ^4ó»]oâÂ±æ<4ý8ÑîÂúÓçnœepëŠ{îùÌXÈVUÞ9ð«ÂÉX4#âk'ý#«n
+žûnÐVE¯£OùY¯[Þ
+*ÿÈtÌr,ôQù/j¥E€jL[T|©kŽØÔaUÞcz×r(ô±å£ÂåÔiLnvºmZ
+«bÝØ=ýÛèú(,ÈÛYT¡ÆŒÛü|¶wXxU(×ï=â¥ºœ@±u%ÍÑöG0pt€?ÂDßÝ(9b¥°÷š]FÂÙ^BZÄ²4Y"xVÉëly5IT$qªVPrdê+êk	Žãaeâ»D=
+.O y¬Ê³oŸû¹'_
+<;}^0ÖÜ»¶pêÓÛ·ãÐÇçÞC_ÌwÜÖÙâ0­p§úæªu.vVKljóÜþÁ±#
+Žèì#Îæó)µF©YézÌj|VÇÆ8MÊâžØ#	Tf
+åN
+¡«ÂPàÍ	5lW	éÊ	ñgø2Ï$5·C}Òf&TfV(ÊtÐô}*BBŽ4Hj>)ìJÉÊž|l»]Áö³ÙOÙ"«Ü+ø;ÖÞ`^
+l"KÔÄ~"#X®7Ma*×C2!EI€hL£i¢Z_kÔéó[¬èÈo^«íëèšŽA+pY$.¢='Ñy¢#­9ØÁ×öð
+ÇpÅÝÌè((ÑÝ?¿|PM&Ã_
+Àªª"	a§"
+MU]TäA!ü]^=l¶<ïþÑý2ovµÜò+ùÛòÎäZ>tþ:)ÿéŽåßŸÏùt9eZjYêXæZæ[ŒÚèÛ|Æ÷ãàNG!£5æÑI±¬RÐÿì ò:®lÛaòò@UP/æœ^jœ{±Ï!ÞDÞlòC¡,çCØºÐk.(R°œA
+7÷Øóûm
+ÆÏjŒÃTßVt­(9Áiy!!9Ü?Ê
+Ñ`+ºTË>ÞZWÎgî7Sf^gÈáå"%âöšÔ/éÆãÄ)×K^	 ðòNÞ+ø­+Ä
+&ít8o­(IÖ¿3lSé]gQ€o ·ç}	HÃÅ7¶=¬< _4d%gÆq~VÑU0KO?ÏýhÝÊÎöFëŒ
+ß¬?U8ónú}õ÷ÔCK/{(/êŸžÊÒš(·aÛ«±a~uçg-ºfþŒyóŠ÷@ï:¯Æ³x¿Âmw»kÝÌÃLXS5"Vè×î[[@Ø]f&ødµ<qjÉâÔV #ÆÅdªì°»¿Æa×êíñíÓ³!~Ö9ä Å*ŽžÂïAŠEÉjŽ(H°Žwt;ú
+¹CnX€RQjO
+ã¿ñZ²³Fù]¹86bÂ08k]¥-°d+a»}ÝË2íì5aÙa·X9àÁQ¶iD
+öfá$j ÚFIad€€w#p&òåR÷õ°jÉU!1tBàVi(µÆ·ü¯¿€¿²÷ÜÃùºš9²úâ%³p°· /­ø$}Ç_]"Õšp6'gJ0Ðza.ósú)FUø|œŸª¢Z»Öoª2M1
+ûý¿öýÚÿyùçUÖísîTøí	ßþ/Ê¿H¬Òî]UÒÊ¡Wm>
+
+|0êæS Hä
+)!T©'8Ö-öÉbs¥<¶!ê*Ðõ§ü
+A¹;a9¶ª°*AõFÕsªœª×TL¿jêö©Rª.­rUwúLéå Xùž.n·ëçd{9Ì93
+úè±^áDÑ:ÚtÄææE§I4æ$«ôbUz!û£>ô¥ŸÊáÓUî²*w*È€Ë"AìvMâðúPEy8U /€Ÿ	ì=WbïF|<ª&SÈî'ryzÆq§_ðÂ5Âû]6õÛ¿ïúýìoçøÆ7 ÝÕ,ÇÙÿàjÂâÌ^H`ûþßa{²Ð1ðøã+{x\@ÝÏ u×¡OxçaËmòr9M<;šæÚ
+çnêíñýzaç?NW!Å/_©U*åDÍXŽÄ
+þñË 9&|MK»ÝÛQžaŒ7à
+P£F#«v:Èê³Ê.î8BŒùI£>
+¬ÂŸ*\å¬ïºt,
+wMDH4ØGNä¥
+;ÁôS ±IM$ *+Pâöæz/kÛ²Ç¯íH·tþ­#vÎ
+¥.ºŽGrúH€.è¬3#áßœŠ°z,>Åøª.._Ð&ª²@°3a-hT^ÜÆïŸm]EÈÇ
+
+n&®ªŠc±Nf¢¯ÿôØÄE`Æ>P6ag6Eå©.8pæüÜÇëôõyÃíå°²=ÐjAP ^ÅÕÊYÒ>mqzÎÿñØ[Ô£'€ž¥ü HÀÒ®(P5ûî~jèfO	Ìl:IO=FŽñÐ¡Â
+£­àé- ³ 6t3¯ùpß¯eH@Ž3dìI«_qYâ ž=Ùëc7&ï,= 2K
+ï¿eQû¿æiD
+kÜ`E}!ŠDSèiŒí°?.ÚótbgwOb¿çÕÒ$Äj»Y!HÌo
+fM+|+*WûVW®÷­¯ÜèÛXyØwžR]¥<\w8OåÉh>['D	BÁÄC²ÂÇs~JmLŠ¥=|ŸU?±OØÿ'± &1. Ž)Yéw>ï×5ÇðZ¬ôqŒÊänqšÿ)­<ù~^S£ÔºÕÛaØÅGø2o2Ã).¿:æg®U+ŠÈsÙl8lUÃÚÂÚì°ÛøLN0wÝ÷6dKv0Ógë·
+Ú6Ømç^ÎöQ>²>²`dÏÛYnÀ#>ÉEDG>#Le
+`s"ä\i+JV©ÓÄ6
+VI$·±75
+n<ÒxŒQöP 
+ÎÄùÉ|#ßÏ6ò­S²mÓ¡4c&Î:JÝs!¹ 7ÛèlËK
+€ôGnfÎ¿mE#nÜEPàY`k"AÑ5·#BâB§Í#ú%tpÃúÉK&`Œ§IX ï@ì°pÙáh#mä	mãnÀ
+á ±Ç<!a£`}Ö·O
+©ÚØ>T±pªòJÜœâÃ£Ô
+µa·ÊëA
+¯RáÔØ=Ø£tyhÎåÁ|$W¯ðêlÀÌ×À $U$	ðSKIò£ÉÔRòMJI<8
++#Ûâê[!Ç`ò
+ qGøJÕXšÒw&ÕÇ«RÄÕc³sÉù±Üõ+§_Ä·ŽtŸ<áPCÓªx ê]$`ÄøÐw5T
+DŒ¹û
+
+	ä¡Ö€B&g{á±RN¶ep±j}	ÖUÓŒÚ=ôaÕa%ÈÛ_8ÆeDµKVVŠÎ·¥Mªgt.JB·®D¢©J-zÈ5ân¹99X,kPÛmü*0ÄTbTqNŸV¹jœÈ'àÊÊÁö¥pÊY3Y 7	zÖïp)jF
+±~eÉ€5ùz<¶VÖ­éÚ¹l.YaAB©Š_>§$Ó.q
+ÚÓéóï)Ü:&JnmóUÔön5xJ¯ëB¡óÀêœÖÅ ÷üEÏSÃò·ÔôïÞŽŸåxÓùû`ÙúÏšÏåº/»)Óùšõóc7óã­²šäÇÔë?2)9=&{Bõžæ)ÝÅRj|±úrý2Ó"Üâ×*\~FÃ%UÙ=Ž:	Ëf§Î}Þ§L+ûŽrŽxÈÖ<+|
+KüŠDo¯°QÀkÜ*o"PäNÈ·B>î€·0$}±)²åü7ÜZýö]EtÇÅuwaú¶ýÓ~ÝîŸµöŒãwoœåœën¹sÝÇ7]rNÿÖoô=ñ¢¥p?À'²xõFG'Ò'²r¹[
+¡vú_öŸü0z,)÷¹ÙHÊÍESXib*õq7	%ÿ16Czæÿáòv8
+èPÜïqt®ò(
+ò?êŸ@9æx®£Çr
+wcÀ
+pÕž¯w¹Nù'È*ApJ9¯IÎ ÅFâÊIŸUÉµÚtØnìQÑ"rW¬¿öÙ±8ÕÙ7"1o¹W!©ðÅ}8ëÏ	ÖtU8dLU8 O²LJwY2\ée2è/Ù¢H`iñ°ŽÀöÁ
+KÁ*I$ÖY%IJlØ
+œáÿ€ÁT[%§­žãHþzÁK{Ù
+ÂþèýãþÙã3
+9ûžâ¹{ïz²LýöÏo~îo>;PhÌZøÊÐŽªtá/Ã¯Þve%þNüö«º:æ|÷àË¬â%Ïs?$Ç&Aå	»rj¶¥c3Dg&ìvËÆ_çú/aò5sæ®ŽÀÿÎØ»qz;Ž:¥ÉÈ%SY#ß:
+8kÔ»±ÎJyIä;-Î,ê5ÃØÃûõÄD»j€äp»}@Œò
+J¬tÅ1BFÁ,c 	»Ýþ>¿_îwÆ&ø$çâlöØÊ^A}52"F×©h
+&¢YJhc±FÁñ^BÞŒ
+%RßHA£5€Žå7Æp
+MoÇþÕtËý­.âã¹þøÓÜB
+}×`oKg&h>kùòÂ«_QØauÚ«;ú9Sä9§É[Ÿ#ý@úÇÛ+_¬|Kõú­ªcªªNhO¥j¬)TtMjztZJI^ãûI°
+žS#VkQKt§P0TKMKM_Sõ`Õçšÿ
+TdZ«JiÓvEëqø®Ž©ávÍºô4LéÕÿ¹áóÍÙq:d§«+µjÄÄ!¿MëLS
+¬t$ZÏ]Éª¥|`kCN-fBoMœØ9éÝÖ}NV-åBgØ¹pötrön1;ÂkÚriž9SÚ€{W¹Ê³
+MŽV­
+ŠóíéJK:]IûkŸöÕí¶Óö®vÊ×Ûù`8ÛÎ×äÚßjnnÛyw2k_Åv
+ñÓÈ÷Sþ·\êr¿EÃ#âôl
+'¬Ð(±ûØ#¬uu(öPç>¢úx×3ÛWÍU§«éjÁãf«3ºÖKNOò"<;"EâÎðÞ£qòÆ  ËæGÆ¿ÙkªÊû[i·5àß°sHÕ%ŒIÛFi$i'ÉT€×TCRÎI¹_Ü7ÐåÓDÕ6YÉ^x*`p ­÷HxPzI6 å~)ç	
+6Â
+§dIÚ¥O.ù×k8\#c[äW
+
+*Z4Y$Ÿà_`ŽÞüN §f®lºv/Æ­øm÷Ò×œ×ó`Þ0¥vÂ}ê¶óŸ=;Ë=þÙ9çôÞüÛé·6ýúX
+ËÕëšG}Ÿr#L5
+ï9ûÊÎË}^>ßÙÞf*¢	£Âå2¹:;.¿²c»L]6îZÜÍü%Ð3[Jÿ0ÞÊûÂ6äÃ
+¹ê€ÌoÔô;±ÓiIF£ž_{DKiÐÆ»*Ã!ÑPxŒVd!
+nK¥ß2dÙg9b9nQ³ÐH-23¹c+}ÄŠ ;g³÷ëS$®xÝ,ò90Á;zT¬ÉL14(Ø(³Ì€Ï
+œâ^îHî+nÒqßhÖ
+[þœÛ«=\Uáð%{÷
+|ªSàJ7H^ÑÅS¬þ6WSŒÂêzl~tî"}»$? õ0}¢Ïù: 7çU<$jœK¿Tœ;ÅÉ¢ú:ýÍÃø áC|XúàD0÷ùñž¬ tŒ·òÞÌx£ÔE^ž4h'
+š·Kn¿7ê+Ô~¢š«U'œ~­FøÉV
+õãçðLMfWÜ¿R!rÕÜh
+4ay'*6Y5?&h0b(
+cù&)håDîLÜs"ž<s£ }7q]ûÅùf,Ÿ/îµ|kËú¥m%
+vª¢âÜ5gcÆ£ßom®âñ%xÿUçMw ?²ÓÀ"3e>^gÑè³6ñª	_Yü£Â¢.àP	Zì` ëAã§bûø)gŒo¶ÝÛÉÇ§æñ#k1¢
+u09Éír%G°ÛM&UPØÉw³ÇY
+Ôy¯ækš×  Â/0c3ig¹nd®»i[põz1°hÀ5Úër kí
+q9
+¢ø¶`-ÈWºâ²ÒGºH¡9DL}\zA×CóÄ0æíI©	
+¬ŒA#ü*ÙB
+aVásp6¬ÐD÷ÍÒ·÷$Râ`é»A~2ÔeïÎ±gòØIV>PØüVa³G÷1wMvÎçøeœ¯AÔF[©œ¢ŸPï5 Ä 
+š§.Ü
+Åyc[ÎÀåsŽô.7Ij%ÌOÂrk¡`Kü"qØðfáxSöŠúöéMjï€¿0hfÎ7¬3ŒjøeBæëÎç<|>'ø
+ò¹úáâ_¹rNNÞZŒT¿J/»Ì÷²b§Çé{
+¿£ÏóïõzåkÜ¥Y Y¯yN³W#ÓhôÂ+Z-V{6J"3æÍÝæAó&óYf6³Q
+:¢Q`^õl"
+OÏÖ{=I
+ë}ŸÁse{I 8ÈwS
+[Ðê
+iãÁ
+Òü[oaÙz¬g=^¥]]ÜßåófÂòßèÉŸ
+%yZ$òIå=ñï%7ë`_Ðoc·Ç÷$«})þjRk{þxe²:Þl\§¿œ#Ÿ6©=O>{^üüäý2vI|Yòaý¬Ú®w°ž;Ù©ÆÊÊ¹dzzU6ÀÑc¹9!Ëžjâ9_
+ë&`šÁÅ\SÃ~9D²]f!fù
+
+2eQ6»³ÓžŽìÎ­wmt=çÚëzÍ%s¥zãÕ)cu*.|€ª:Õ; óEô®\90@ôâÑ¿þUøXxSùÜUo)N(àW;Rq£p~µT "Y)œÄY¢NK_>nW®TÅáŽùBÚ^;^+hkëê÷B²üáGSÑÎ:óuu8]øW®6ß¹*7^
+×ñÁÂIº>$ÁÂµêÛnÓfÊS$â¢ÆÚcL}:áKW±­g
+ý$Ð@ŠøÛŠ£«wÙÎD$9Ïs÷ÃíÞû=2Ã i| Ã y=ôHú
+÷~hØ
+¶^6ÈÉÇ÷ÖèÐÿÊ+KáJ}}Às÷ÇÞWÑËc^µi#Â¡ÊÐjcXnxýÄð)úÔ 
+rÕhµá
+Ã|V¿VK°O~aá|v÷rŽT©÷ztœ~3ÚløýÍð92ºY³7ú:Ã4ÃTïAôŠWeâTÓpZN2¥Ì)KÊªJóÆŒ)oÎ[òÖ.sEÙeì2uYXX
+@L²bT0VH¬_oø
+ç×béËD
+wâ÷>XÞ@
+#Ò»©\gè
+âË¬âèñ7S¿ò.óØ×
+Xzüÿz ã^kÐc
+"Â&ÃÔx×cñz=zÔ<[5,CÊšJM$¬Mi3òRñ¬-\.GZ³É
+=ô~þZ»è¶+=ï=Ù-)vü?=ÙIÄ±e;!KþIL"ã$	¶,=Ûjd=Gm=IvOé¡ÝCñ²m9eO7¡-
+({ÀQZêP(é¶Û
+--i	mO=À[
+¥íBãì7÷d9îVÎ7sßÌûÝ¹sçé)ò]þÒr§»
+9
+ßñ
+ÿôÏ¬5H/?üÖÁÃÐgT_É§;ØgE³OØ+©Û©/ió-õzã?èÿãôºÈÁ?pVÔô_­Ëù3êüÒŒºOæËú
+lÌÃ;åÑ3£íÄ
+RÞ1^€Ø+üÎßT/q¢þí#0ç±ÎŸD0>ÇD{{EWI{1»¬°°4s7^z§:éüÌWk76ìü.nß}§O:ßßÑ°éêu»W·iØùYÎÞ²Æø èÌZIÜ
+ »ï/ýÆ{qü{Üü×LØ%RÐ³r ÷¿såâÜe%ò/r_Ë­-%?bOYM/²7­rAÉßhø·R«}£Õ¢­Î-³v±=%U+1_áVYüæ3³¿NÆÖã¢#Ïž
+¶åäØ¬+JŠå÷áí^ŸïÉa+°)Ì7LKNO~5ßê³êxŽŸd5ÃÞ×±
+(Œ	W®9í9J×CK
+œ#õÑ³Wpqþ œ÷]ÿÖkôYæ×.Ôó;Š>ºŒØž4Þ
+ðXKç5Xñåä5^
+-l°ÒíRïÿ[¡(¡WaE
+þ]Ü\^:÷»žÆG®rnJÝJYråovKºuUuù|ÕÌè3ÁJû³uu
+;ùŠ\\Ä¿aûòýü®øÒÌù'YX1«úN6ÂÊ6)üvây6[qþ©Æ+%%w};ý^å+Æþü}Œ"súòv£ñQ¹E%°eÙ\Z¶Ù
+º¬µiÇõ¡¢ëÖ©!ÓIrñÏ¿='¿`ª0ýå°B¶ËS²ÀtYss±ÔíÎ
+Öï-|ÿúÄŽ<à)ŸÇ
+ïdæjsyÄl2?\ôðWRßŽ<aæà+ÆÛ§³?ø°!u·Þéóyféò¥kräÞdúÃLÁÚEIß¿oû|r¯éUøµÊSd>Ø÷€ÇrîÌ7IŠ¥Ö»îMÝöü ü#Ó
+©êÎH·[V-]³Ôôê²4¯žŽöÖÈ·É?%{å·gÌÇaî5Ëæ%¹ño§çòVÊbÚëÇ¥/]e6œzálMiqÞRúé:@þÙïWîœþÕÞŒú÷,9ú®¯wë^^{Û©ïùàú¬¯æü;tsÒ¿D2û_Á
+}öâ=«³Ÿ*Úg^Ó³lô`ü/š_6=+/ü.pÀû~úðŽ=|Ö_ÃñZ ø9¶ð ÜÎ
+ T÷ÍÐùðw³Ð;
+t ýMàaÈ=šÿužhŸ$ê¥À1àï ó[Ô=ßF
+îŸ(ž1?V!psP?ßg5"A¿ß})*ÅÈÅüþYeý+ð;DŽ8Ëùå@?÷WY è³}1ûµ-Èg,¯
+62VÔÎXñ[¥fìª3üçesN3VœòÆÖþ=cëÐ·~5cU?a¬f±
+¹üglèo¹1÷ÕËm;ÇXÃXº2Öô$c-]ma¬ÕÍXÛ&Æ®Ãø]`¬œ±øÐýc{0füÚ
+cáOûoc, {ØŒÉC²Ã¢EñâKfùÌÅöbŠw6a[@õnfüWløcJ
+ò¬DÊ²Ì,Q
+$-.ÖÉb¥~!g³ÕRÊŠÝ'²
+­.ä
+vå!çÊ+rJleaëF!ÛØ55Öý-y»°¶ìtÞµõ§÷ÕöŠef²œ/d
+ÕÚ^²å§u²Í^(älVhwÙÌ:íµB¶ ýÇBÎA$
++åáeÈV¶iá_Ò¿Œ¡ 5Ö®ì·ß*ä¬ª@'G}AÁB6±ÕÿD2_Ü	ÙÄJNöì?	ÙÄVüd3_Â
+BÆZüÉŽÛ
+klb+#Ö×õ5dc}
+ÙX_C6Ö×õ5dc}
+ÙX_C6Ö×õ5dc}
+ÙX_C6Ö×õ5dc}¹ËcUØ-dÄªpÉVþ[³
+·ÙÄÖH¶QLî2ü/Œä<ó*d[Sø
+óÉÎœBævýB
+óÂÿ2b^$¹ûST,dîÏÑ^TŽUÈ&¶ŸšäÒ2ô|$/!ýÛ
+ÌõGI^Æs èŽE÷ŒüY(dÆZ;Hÿ×Bæú? y%Ï¢BFœJò:
+b§â
++¹b¯a§x--ñ·dÄß1/KÆŒlú¶}[ÆºØRëÒÅ&ØÓØ ó³ j= t±!w1EžÐRYýÂñ~ŽHCEKã« 5S»ÿÿiÉöLeè	³ÑŽNm;P|5¬Õ¬RHµÔêÅ0êqÕ{1 ÊÆP/óÊM^¢?DZ*Ûz
+u#ùÆõÄÒþrŸ-(U¶öBð*0 »kÙyôç2<íkekb0GËYJåñ
+âxu¯×Tâú¿ÇZE+ÿíêŒ7<6*ý~²JY¥M
+s"h1Œa>»ì;Øv 	±ç²­*Êí(¯£öŽt¢ä«Ã1»»šµÙY.Ï!Dk¿,3SíÆ,G(Î#Â»t.œI:fÈg?ñ\Û-cFnRfš¬z'h)N>ç±ÈÒX#CRþ&}ÃŸÂe­Fmd
+¯FQäÙÚ#ØÐ?Fz:üHÅÜàDdRÙ6NÁ[4×ð1#Þ
+@[æ7@ÑŸbŒt1/
+1-ÃÊž°y%Ÿ È
+ýŽW
+¯ûÅÊDå+­P9Íjn€Œº<+.g6Úy¬ÇPòó¬aíYÏËÍ£ß01Æ2V~v-u»/xtÖÙ	 ufðIÖ\¹¡œÁÑ,/ßÛAŽ±Kýt
+fÇÖ§µ£ykÌ/þ±âÞ
+ýT^ésìÓú¢ÕÌ<WŒÕÔ¡kEF)âÜþPz>_ÙÍÏW<ø»jDäG*K/Í¡Ñl~ì ¹_Ÿr<ÂÜþaŽkd;5 ÕÆ¹-rÉD/÷¬e>?.
+)rÜ1:g>Éê§ì{ïÕ1±³{,eïòu4¢eÌ Nçø÷qjÅüÄzà¯òv6Ê3(Â|kydÌg;m¡ç/Z+¶ÜÂ6²ÍxTQÖàšÏâjÆ¯\»YÐ¬Fo
+z6
+y3Û ðQØÕxÆçàÖùjÅáW.ÄÿUa
+îø ù®¥üå1å~¶ÑY"Nç(®a4zÆ
+L}ýé³LÊÎ8åH\gÏÅ©šïÀóø.Ú«^Sk©3'?8òòR[HÄv;dãÚg0ÍÉÀ¯4ò; öN²FËx~VÉjÊ÷­[,
+ØMb#4 å^0cþëiçŠb:×ã»Æ>}FÁ6?Ïz1Àf¯&RûnD<÷ñóolÎ¹çÞ(ÙH®q¢2BålL¢dY§kãL'_{ç·Yã»!:€"V £R»`öLXõWæôaÕ
+2NgtnÕE×
+œâz*
+gUzÌßk2ÅÐÕþ&,©>×%gŽí®mÀÐÔÔ®!MÝ¥Gô8Ô&=:¢Gýñ
+QGÂ*µÙ÷S;õð(o©;"WSWW]¢¶JõÃjGhp(
+S;Ž
+Ó)Sî&}4Ò¢ênm\mÔÃA÷q»µU[jÕ5»Bš
+Óâk÷dŽAÓÞYÉGíêMßT»¢þ 6ì
+RõôZj¡X\jA5QZ4îçµ>ÃT¬j·¯kÇö
+MÞ®
+ŸÝªo»zÝŠÝ-ª·µ£¥eWËî.{®=·k(Sã©`r#Q}æ&žizIúG&TÆhLSû'Ô	}ècäÌh$p;pn8ÆøÕp( E îjÚ°W©=6äÓTœ{ñ9Îð°û£ª
+`,ªCQ-OšQ}xÖ/
+\ú F*ãÐ
+Dx¢¡þÑ8LÃM=¢eNš<r
+±J"=²_
+óGýýaž
+iñÌÑUjw$¬Åb4yæ$Ö"®chlDBËg®"x(2HcýÁ`/©?¬F)ÇÖóæ(Å|ñK
+C|B !œq=z(7²b ± F}
+)2ÚÅ8lá
+öOšðK52Á7¡¹D
+³óG&ÔÃ£ZhzÙ3
+¿I96€HÍ±6ÏË§Ïõ°ZûÈX1®#ÜAÜÏ®1_x=pe³ärz@ÀQûµ!ðøãn®ÐÝéU+Õ5[6n^«n®ÙRYœ±º:'§»
+Õ557¢ÜŒa³ºyÓÕuW×Ùsâñ·Ë5>>^5Zø>|­Ojµh³
+òôõóá:ãQ,QT¥,æ®ïhÜµ^M)Æ¡Æ3êÇ"!-œÁhÞnâì3ÈGÔN-t"pÊáûYUœÜz(TÝÂP<0€œJ
+ò$ÇY`\ãkB5ö÷:MðµÁîS»cFiÃ£a?OYÇõÑøÈh<j8çð€ûû¡gäÙk¡9Ô£|	(	«æk(>
+v
+Ç#þaÍ5
+ë
+áhãUŒç×ÂhÕ>~?r$!íyÿ×ÆÅ):O+ÎF%;ÞWc.6æëÝ..çéWnS
+W~š<òäÇzúHO¯CñRE'ÍÑy5[é1õR_$Íïýx">ÄÞÕ7Ð3Þ
+²4_ïµt4F_«]ü
+ñg\M|¢ÌëœÉaºÆŽÕÔdÚdÚbò¶ÚLuóZìúØunã³j 3¿q¹xh~€
+ìwJ.dæ_E^žúSïÙðÿà/cÏwç<N_<£ŒwÊ]_û·HyýTæ¡t
+žxÈf
+å|"ÇVËÇOÔ¹kœ¹\bõlÃÅcš;QãøÔõ
+µï
+44 >w>dÁîyÖL|š	lçÁp
+Þæ-0ñnbXÞMìîªõîæ;KÖßeÏúQMÔõ­¢zHÔ{DÝ)êkDœMÔõ¢®ušWºTÔªš
+Tÿ1Ñ¹aý#×§ŒÁFcÂÚQf¶L')àð
+ï
+eÊ;dáuè¿ý×ÉÂësZ&ÀpxNy=S z=ÊgX5ÀëvÀQwcÔÝu7FÝ2PjÀŽÙèy=/0œ€<ÏÞdŽ=¶ç¡ý<ŽöóXŒÌ#EùÜLs(ß&bÀ'°à	øþr¶Î­³°u£ÏbôY>K¶feB	:ŠK4ñêû§AG·Fiù&äL&ÔÄß!P€3(_däN#za€r#ËRZ
+¶#ëå=x-îP¶â×ne=Õu¢Þ¢T$®O©R
++ÕÈÂj8§ãš
+GåtŽG+qŽnV£\åš7 ^­c,¢(\B«&«PU[ûžâ»ÙVRqji­íóZ«à'ÿmØre{Ñ¹,QSKÃ%¶·
+áúÎZïBe
+&®bù=€C)Bœu¡š
+ÓWÞU`È#¢mCšl¯
+¡±am
+Ž6d
+aC
+ÙG6Ó<²ZPPàJ¬ÜpüQùIö¶ü€§[VÒñ¬·³äãŠ·MòqåmE>.¿-ËOd?a
+Ù
+ÙœÙzö
+ÙYs¹×¬ï0g5È
+Oö)&u
+Zª«ëÕÖ¬üùÎüÒüòüõù­ÙœÞ|Ø+ÿIòodÝÂ0­cò¯ÑŠÊ/¢¬FédÖr€c('I:r€3€ÍÇ
+£ãüô8®ù
+ð P;_ÃÄŠÊçÀrÚç"ï§Ö|ùôð}ÀËjÀŽ&ùùnÒ¹_þ%ÎüKù6CþEbcÃ{Aþ
+ŒÁßñ÷4þÂßh
+áiÕSðý)vPXÚû`8d!:Ocn'ägPºPz>ë?Íî  ô=	<Vn«¥ÄÊf7Ë'ÁtTŸ	 nnÁ:*ÇQ`§à0bÔ SË>
+B8tpèàÐÁ¡
+
+:8tâÐÁ¡CN
+:8tpèàÐC
+
+:qìò&`žž
+ÚãÀ(0SËp1j	Ã@Ð©ež}7ÙwÃŸöÝ°ï&ûnØwÃŸöÝdß
+ûnØwÃŸì»aß
+ûnØwËúIÛ{nžAà&ž@à."pÀ\Dà.žÀ\ pÑ\°ï}ì»È~ì'a?	ûIØOý$ì'a?	ûI²ý$ì'a?Iö°ý$ì'É~ö°ý$Ù?*"
+
+Br
+@Ðêïú ?ÐO-7 ÀÔ²Øô û©¥èº=ŽôìSàÑG
+
+<:ñèàÑÁ£G'
+
+<:xtðèÄ£G
+xtðèàÑÁ£O/xzåØ~ùAÚ, hÀ õ÷}è§ÀAàFjÙìzýÔÒ	ty/¢ì8L>0í$&|`òÉGL>0ùÀä|`òÉ&1ùÀäL>bòÉùÀã#ðžÁ!C
+ A@š¯èü@?µÜ 
+ 7RË^`Ðì§N èöPÞ
+²uÄá
+.pžÃ8\àp
+.pžÀá"8\àpÃE
+.pžÀá8àøq$ÁG
+IâH#	$8ÄG
+Ip$#	$8àHG
+Ip$Áä
+ò§¥ûä[€¥Ø%b·|]söÆ	ìãØ+AìœØ­Ø!MØ)õØ1ÕØØë±OÊ±_VaWbw8±KTìò lÀŠÆ>ôÁëàý=ðñ|=ð}/<l
+§Mðž
+WÃ¿Jø¹
+þÃïUð®^:á­*wz¬øòÏQà0PTÓÒRÏÕž2ú8Žõ@5P¬JX°þù©
+wŒMÆu ³KSy_ rÊëšl¥ÒíYÔnŒÝþùv»Þnïm·÷ŽÛ··ÛÝíöïJ3ì4þÓ³üýKGì=b?pÄŸóœñÝ{Ä^wÄŸéÝY~/ÕCñkT~Êä%ûÊ?Sù7RYO¥Jå
+©>ag9ÓÒ{	ç6Ìû|ÂéCõVÂÙês£ã1é>æ4IÌ!}#áŒ­_O8;P
+&W£H8kP5&MšŒßrV;>pN$OãegÔñsçNÇ³ÎqoK8SÕuV84ç:GÐhÞkTMŒzÄ±Íù £ÒhYoŽtææLNK§=Ìÿaì3OV'+ÌëÌ«Í+Íóärs¥ÀoY`±Yr-K¶Åd-ÌRÄ¿Ýº¿*+ÊÎçU6}áÁDrŸÌKÙxÑ&Kíd}ÊÛp°í€ŒyªPiÛ:¥¶©3ÖÖ¯NœßY6-å^¿*«¬Q*hcm]±ÅmSK:ÛŠ:¯ß¿oZÞ6u¬¹MÅcjI
+iîZMâŽÄ ×
+ÙÙ-äc[
+ý©ÍmÓæ
+S[*ÚŠrÚoØwRŸÐ£)ù6XéÚ7-]äM·.*hÚwIãÖÛñúâ­·÷ô°±Å
+×,¬ÛÞ|
+¢O^<+rîö	ÍñÙÑbvl0;ÊÌŒœ­'[ÌX£qñò©/·uîºž
+BV­S=°ïŽÜ oki>-_Ã«}§ZøÍ]À$ÓzØ
+ÐÃÞlzl×c«.Ñ+¯ázåŒ2ôJI¯tÞÉVgKóI§3¥ÓJ:­suNÌÕ9A:'bè83t
+·0'é8·\ŠSú	tÊ¯šsÅ/þòÖ8oWæC:Í:€äÉ­c-ZYK_YôMýÃØÐâ©cýªzmŒKRV÷õxí×Š¥dÖ<µµ¬Y=Ù1vyÿÔïî(k>ÉÆZºö
+óhÍOGK¿¹ço°!<îs)º
+W06È5p._ø
+ÝaÞíã\aÎæ\>žZB|÷µï;ia=MúlÍEÖ÷-sö4ä\C[`«sñet?³VôLÙÊ§ì ïªôVzy6>ïZæ<ÑµøÈVç²G¥ûEW>52lË
+-Íû¿8=bàñI4Yª?Ÿž%Ôù6uEŒ"£iC8a
+ñXýŽ­¯Œo}_«Ò·¢Ï)Çb=ŒñqŒªâ¯zøë+	mR¥b©ÅVqsüN0
+»S2Š
+
+)
+"+>R
+€õ¿1Òx
+endstream
+endobj
+
+77 0 obj
+25531
+endobj
+
+39 0 obj
+<</Type /Font
+/Subtype /TrueType
+/BaseFont /CourierNew
+/Encoding /MacRomanEncoding
+/FirstChar 32
+/LastChar 255
+/Widths [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 0 600 0 0 600 600 0 0 0 0 0 600 600 0 600 600 600 600 600 0 600 0 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]
+/FontDescriptor 78 0 R>>
+endobj
+
+78 0 obj
+<</Type /FontDescriptor
+/FontName /CourierNew
+/Ascent 832
+/Descent -300
+/ItalicAngle 0
+/StemV 0
+/CapHeight 0
+/Flags 33
+/FontBBox [-21 -679 637 1020]
+/FontFile2 79 0 R>>
+endobj
+
+79 0 obj
+<</Length 80 0 R
+/Filter /FlateDecode
+/Length1 12568>>
+stream
+xÕZ{|[Õ}ÿs®lù¡ØqÞ1±¯¢ŒåWÞãÄrüH
+§m°RH¯ml	I¶	PÑQZ'!ŽŽl°1ºòH#;)sBWÂJ»±Bi
+µðùÀ6º®eu
+î¶ö=ç^Ùr{ü3Ýs~çw~ç÷>¿s¥åÒÉc>ÌD¬ 0×}Ôÿ+ÀoeÞÙ
+éé»ðÆ¿Å²ºl;zBºøs¿G4ãIìôþ®çÂEùDy×`Ÿ¡ÛøfÌ[1_ÒÛ¿%ëAêÁüÌí¡pÀŒóH-úü·DŽºÜÌa®÷ûûÜµ?Ä
+üçcñeÔFŽl\DÈû^s`ÞKýMàI{€EÄ2è?ìIþŽ£}
+1N*"JŸiµ¿žÃ\O&ùk nµùiÅóUÕ·²]æH]ô*õÑèw[Ë~Hò\ßNÕt
+Ñ_Q[òWÀ:ézJhõ&'h&
+Š	öYzqâØUI¯A÷òjáÖÞA(V±
+ñ»JÁ¥î§yô8®Jfc~/âÕØÕJ?í%Éä¿°ÚóÉNúCVÍ/ißDÆŸËk4ñ¹äÑäÉä)AïEãßM®NöaWùhnGè÷éEæå[ø3É/B§vèpþ~ÀÜi>* ý þ
+zÎÓwè%úkúc,­`GØ+ìU?7ñ\²)ÙSíŠœt«ØRVËâŽxmüï&ÞJw+
+Ò-t {é	z~B¯3Á³y+o§©¶Ðê7ïNÑóô&³³u¬yØçÙ|PãÏ æÀ;÷¿D'áÓGé=G?¢ç¯àSÁ07kc
+ì³ì.vû
+{=ÉŸÉÞá6þ×BßÖŸ¯œ3q)|0ùäÒ5€ÓJDŠ®E<_€_ÂŸU¬Õ°¹ŠåOL¬MnO
+N~/ù¹h9h·P=lÞE×CëCô9zŸœ/Òéïé_á%Á²Y|¡3ÛÏZØ Ž8ÍÞcã|.âWÉC|¿*ÜâEízíãç&æLNŒ7L>L$¿|AÅwäÔ!¿E©}rŸGoÓ/èÈÈ`ÅÐukœÿì#€ßÁäI±EÜ+×hLìèx`âlr]rrKÐ:<UÈŠ6ò÷ðæ#ô8"sÙsþÍgE¬5±ëX;ó±^fv3»Ý¯>ÆÎ±§Ù%ö:û'®ñ>~ró ¿ßÇÏñçø%þ¶ Ñ"ÚÅÍâ6q8'~$þAË×JŽ
+mæÓi·ÚÈ&2æÚ_øhÞG}ããw¢l¢~âŠ£ÏN\øÛdNòäÏ(* £u2F
+ý§{è!äÇãÐñoèçôbþ/ð
+`Yl!4.Vq«Þ» ùõÌËºñô²áÿ#ì	6ÊŸÍ.²gÙóììeö{3h_g3NAï
+ò'xÿÏüßÅ2Q"Öµb«ðÁ»Å`Ïï7ÄÏ4®ÍÑVk-ÚaíÏmÂÖe»ßvÒöí/l¿ÌÈÏøU#ZÓëx?«m!zör!~É_æÕì³üCöÇ|{Òœb/¯Ã-ÁÙÓÈò>y2Ãáä³)?Ó'yð¯ñRqœ¶LäR
+çøþyî£¯³oÓ|2mPŒÈæÅIíËÚVö
+Lâöª¥Z¶±{
+nFJÅí£Í.>²õqGòníç6.^F
+ÜÂžøKvœËöò¹ðÖf~¹0ÏgïblÂ	ü	2ÿ<»*µ·Ä1Ÿ¿\îcÏÂÆ§)ÄfžTâ<FÙ^vJ¬Š;ØÍðÆ&º
+ó_|n£_³;Ù
+Ü%Œ4áàz{õ±^Æî@öÑQ6L%l]€øh3Äw>Z0Ÿ³Þe#b°µçµç¹NÏÂš
+
+dÈ#šm8N±YSI6^üÿ-TÀki&ÿÝÎCd_°Gy-í!CÄx#»â­V¬
+Ç. Ôel²­Ú¶H[ÿ¶"qogôjoÚî°xEŒô&m3&Þ [á
+šnGqvÐOÙ\vÛ§%y³L^GOð3ÚÉy,9éÇI°o±j¶$©³9l2üÇÆ¿Š
+ÕîÒŽÛq7}ªùyú2=HÛäpo-¯
+7;P{ž#*h
+­u[iªRÖöÒuš§>TÉnê§Qy€ÜPÍðÇ
+Ø×M7Ã
+uÝó7
+C
+žŸN?æóò ý~*þ\xØuôªöEí0µÐÚÇfAòFD©û%_ŽTê¿§y|'y)ùñÀïëÐýËÛè:"ÇS³uKõæªM×¯[»fuEyYi{ÕÊË-]âZìÔ]SžpÁüysçÌU03?o#7';ËaÓgTÒàjôéeŸ¶ÌµcG©»ü@øÓŸTãtîSdútJ(»/£ôIJ¯WSuiÞàÒ/Ö»ô1v`_;àãõ.¯xWÁ»|¯
+NlÐæ÷Öë	æÓœÃ
+Ÿz°ÉÉ®sÕÙ¥%40Pb+2Âæme
+àóªF8Ù
+P*±ÐUßXàª$ÄÒWbïŸöúB§Ó[Z`uWg\ÛynEBuJL"£.©ÄèAi
+
+ÕGJ.Ë§N;·ËÕåïhO¿WÊéÜúÄŒ[ß?5óºö»ÓWÅpÃü .§ÃÃwë÷µ§¯:eïõöò¥ŸáF>'6·èÆïò¶'Ø]©KK€UŠ}«Ab|7ê,×6Wïð>fápör.\è9|6èÃ­í.g¢ŠÐåõ×_32÷:»À£/ŸRZ2?ÓtìÈ<Èu€Æä¹÷OzI\MH
+Ð¡I»6UÊÎš€á@%Èðñ2ìJt!"ÁDVo8¿JâåþmiŸKþ®wÿq:Æoa2æ@y2jXOÁ	·;±jLÌ:Ä:nUóõ¥%c<èäëà>ÚßúœUåp¿Ó)|tÌC$ìk7ç:u§ÜíMp\¹ZÓ&W€V&·û\Èäsê+Â}Ùä¿Œü¹³z«lîÇ,æzs«yßvœaØgù¶¹uÚÌ\¯\³ Ä¬ºvQÈ-
+µ€ì$öÜ¶ÿ2TRweÚ
+ÃôÆDŸoÙ{³ÎO¹i,ùÏrŠ¶Yj&ªÜÓç§Í§©;, °¶7·
+
+Î¶Ö
+4<ÜèÒ}Ãþ±äNï>÷eÃ_*¢cÉGÇŒ0¢Uâ
+AÞÝxpeRãHFæË=¬M ì§à³2%î)Fì{nïÞÿ~õ®ñêÝù¿©Þ?^M5ÕãÕ²­®pÎtÎ\áEû#]\üÈc£I×._ùÿ¿ôëóª?°gÙÕ;Í#K÷Éï­ô­-g³¯þÇœ¶
+v7ŠYß>ÑËûo/&_MŸ¬}xÅM®œH=h©OD5%-Æm
+ò©
+÷1ßÇw[9Q£Ø-_ªÔ	óKÉËò-Ó|k2ajÂµ4ÍgÝA×à}Ã3ñNõ ÛiÁ³èûûÍ²R|r(SfÁ¹ÔÚëÈ8Ç=<:r§ŸhÎíôQNîÛÌ)Óa·`A¥¹¯[°Fc£\GgÐG¡gRc¥ÛiãÏ,8fä_²àlâCfþzòWµ©œqÀq»Ï ²hÂ4éõÜa«<šà
+\Á
+ÿí
+þŸ³¬°#6cdÂfLXK£1cdÂfLØ	12a3F&lÆÈÍ°#6cdÂf$fo²åsÓð3ü÷
+Î¶üFÁ³ ÌÒ<;~âcÂsÓðäÞYs\šhLÒhÓà%~W)xK¬l±§éoOÏMÙÒï°|ûï&?0êx[ÖíUð.
+ã-2o
+&o¥aËêýÀ
+LûË Õ+ŒÿÉ©|R3
+ïaà&ibÀ5a4å­Æ;ñ&Œ	ZÐ:
+­ÅÆýØÓ
+âj×~ð¡Ei}×ZU)­°
+TT:íÆ8ü Ç&µ\)èuZ.AèÅJ­ÜV*I=àmQŒ_}÷tiŠ¬œ°WþÒ4mÍ¹XùRzªó>Åõ&à€Œÿ¹u`¥AèW:H¯èKÂÈXŠæR£~`L­b°b7Ÿ;µ"huðº÷ «£oD­ÂËßZÐËžloÔ/(ÛJÊVMÚTQ_)ŒieDù:biwhÒWZoæPJë#Ø/©ý 2­4³b@å®~ÏÑÑ€)ÒæÁ4Ïšœfn€ô1=×§èMMdöTV*_
+
+ëQ\dôåE§^KZ/Ö]z€|nÊgR7€2BbeW¯¥cf .€ìëVÞë»ª¿Â]ÒcF!çÕäuYÙ#s¢SRSëN+2ýç«Eh¹²jº§ÌŒº2+®lâ¥¯ÑË
+áÔåíâÿ/eKï·Rci
+§éçBzÇS|Àv+>MÌu+ûÕYìÇlJ®<Û]ÊÓæ)õ«
+M«`%ÔÑŽŒ5í¢§€v}*¯ÂÓø
+©øß€¢^+º­Œ¢Ö¬"Êãï€=Š^éÙ-ëÌÓÿæ©XùÊÒËsèã,Ê&eû
+üoÞPŒSÖÔhÖ¶þËbœÌßS¥}aUÏ»¬ª9šjàPZ
+ø4ÑOñ3Ï€<«V4ŠÎXßq4œeZW5 ~Õsÿ2_wÿ·ŽòòÊÃòWhdÚ#3šjCê-°¥$oÌJÜÒqKêèWc&ÉZVAò
+®-Ê
+¬®V¿XðFZ&wm õžëeÜeŽâÐ¬
+ï
+åð|Ê`Çå'> *ß¿Ò§RÏfU%âªDñöbš{ºg²úú'«LÏÊžU§jqÊëMŽ
+
+gõò·![ªrÊJ0dùQFšVáoo==²Ò%È7#Cé
+°ÎN@ev?ëkJ÷ [Hq
+Ò-
+eM@å^Wý%êäŠ|ªäæ»ÀÊ]óLÝš1õŸÓŠE7MœM€Î]ÄºûdýM«E2÷ÌwŠTžÇÃÊ+ÕOù$ª8Õ;Y)ãJÔ;ØT}Ò7®|×«ê@Ê3] 
+`WêLUÂ²ÿf+ú>p-GW]r-Wï
+­÷©Tvô+;Ë&÷üßÊRbÒÿ'RRkåUIÞ­"F·?`èé­œŸ+Ü¥×
+£pÔûõH(PŠ×ûãþO *Ìôph@bbzS?ö­ÞŽ©¢Ýº2œ6Ò÷{zã1}¿3¢FWUU]x 4¢úncšjÐÆ$Ë5ekô»h8î¯ÜoôüÑëÒ­mØµ·eW«5{\oú»>ô&=Üý±*ëQ£'Q£Köë#÷Ë1<Ð«XÙî=­MMuµ­M{vë{õkêv·4èµÛ÷74ìjØÝêÈvd·öcz<åI	Cd$Ý!©Â€xx(ÜõGzéþ~+bÞyH?
+;áA¥Ì@Œ!ù@¹Ÿdâ×CÁÑrOÔ0úþxîÅ¶^ÿ ¡;¥æØŠtÜ?jèFÌ¢zW0jâ¡Czw4Ü7¥W²Â="åÔŸ.ž'ì
+5Ô÷é-¥¯&]1¹°_ôü!š
+ñôÝez[ÈÅñÊ
+ØdÅ"
+ÆÖXÄ»+-×áÅþx°¿GíõwueHý!=ª¬D¢£Ê·¿\©P°/(
+E7ÞYÑ
+_(dx)2Ð
+Æz¥
+ð2ÝÝç?€C*rH:nÊCÓ)4uOçï?€ß<`Ä@žÙÖoYµôVÄ±Þð@š©94T\iŸ€C$ 1I7i#Ôž?±4ÌoiÝ}u¶JåÉ
+¿Þi€A?^%	ÚZjõR}Eåº+õ«+K+ÖUTdeµ5Y±zõºuè7®ÝšoÜ°~ÓúMìÞx<RU^>44TÖ
+| Ü·#M»ôf#
+Ñz#ìéë)#i¢QTWY,UoÚ¶«DO!Éäú$€emW4m£(==r¹Ao1BH÷(2õFg]¯Ü€Jwðã^œKÉ/Ñ2ÉQuPc!§bÑ­Ê]§OoYdô¡2ÉR<<Ä&Q5G&eÜß)+Ê7Å7nzû2]áÀJÂ²ÿÂgåœñŸPy_\þœQy_ì`ÀtG¿1T&W>å®!#¬ñÉ[ä¬ÜJEM»ÕíÜ§ná~õw"æÀr#æ¿P÷QjœEÝËòWoqâA1"þT<v^\OŠñò«Û)5ÿÅÛ&ËÆMñÓŽÕZ³¶]Û~šýêfu'ö²ûAêC-è£Ö¯"~ë7ip©ÿÿgýÞMçÅÅÑ¶µ1Uj8;cÉ#rÌqšq4kmMm¹žHŽ3h/¡itýa#š}
+ÄPë§)víGhsÀ\ æ05bøñÔèb>wvÁ5ïÕ.g)ÆÅÄQr÷
+ÖxÐO`\
+ñ^k<.n.Î«ÍÂÑ{èh
+¶ÝŸgÍyl¬VÀÉæäY`kSÐêŽ:­NA«÷Ð3p=	üIàORáOS¬+-Vpj4o®
+P-Œâ:ZíÖxœžntMñ3µ>ÑÖgTÿ°hEBõU¿GõÕêa
+Vpk,Xöåi}±êód/ö|)ûÄN5î
+Žã
+Ìåž[4©qØ®Æk±twF5oÂŒ
+ãÌåž]4ÖWÔF0?5y_
+ê¡S=$1'Ð
+F{Sa¢?öPLÔã©ÃS+j±Ã
+
+¬xH<[ÅV¬líô
+Q­l¬U5$UÃWÕà\ðT#<Õ)ªÑëb=U yÐö¢ùÐlàS}%Ð«JD)-/'?F³1êÖXÌRÆ"~tŽšØSÅÏÑ^4Zí?7j+È«
+:I[¶í ÚaŽÐÎ Ù©Æ\ñäð^#öð=BCv¯<[]œFk7ã5Ì1wáŒÚšX	7­€ÐT^	WÂÔÔ¬#uÓ3h/¡œ&
+Ÿ
+ÎXg,Ë±¹¢ÊPtï¡%Ñh9øO§±©ÝÅhåi\$v0+0[=+@»Ø7Ñ3µC®ïE;öµ¶X%óbÁk1Ž-G_£ <ôÅbñ(ÏÊYU^íFø}ùqxó8üv\fž
++5Å	Ž3h6ÔÒób%åxVàYÇGÇ"Dï^<'ðÜç8cx"³ÏžqóëÃë¯?±þ¡õgÖ?³>óiîÇãã>O6ÍX0ÓŸ°6kÔAöª?­úšê=ªçYØáx»Ãñ
+¯u8ŸÚáhïpìîp4v8Ê;
+c¬Ó3ÏíxÝíž×ížÎíØàv¬w;Öº
++ÝÚÌË®'}GõÛT¿FõU¿]?ê ¬o³ÏÓgËÏ9»øgÎ1Î9fÇp§9û9lÈ§+=Å%&f9,qþ©ÔÆ€Læöd>y0Ó¹)³,³4sEæòLWfqæl{=ß>ÃkÏ¶ÛívÍÎíd=|Ëã÷Åì|9dh²×ÏeÏÍë3;§%ysË6Öž æN=ñ×ËÞw asmcfjnÝ6?±ÑÝ<Üšt7'²ö~Š}±{Œ%øÆµ¶±€DÝU(ÿÒã<1Vr×ñBkôzåö
+?î¥¹5ók
+¶ÎÜÔXÎgõî©Ï|wú,JÜßÜÒx|7±FÉEÞfxNþaÈy^É74Ôçåàm?}W6ìøì#õÞ):Ò¯?ON9(:Ò%
+éÑñn©
+Lº"EW4nd³¡~ÄéLÑlQ4[ŠÓôL§éQ4=0ii4oSÑ83ßºŠèSÐ,œ*M7mîù°óŽ]©»UþUÏÕ` ùG{ç'têúyªc¬?žYæëÄë)F¿1Æ.¹úD«^Ùyëë[åòNWýÝÚÐÚ>r«ÇšÝéÙÙàò×{Ïn÷¯:=MÜSâFVù¯ÂÌ/­²¶ŸÊòi¹Œ]Ê:-e²¶{¶+Y*ëvÚæ­ë0Ç³<'	ì+tz·ÍÍlUÙŒÙ9ÿÂ±oPÛÈumK8ÐäRimi­\Â)K3äOYKóïØì,ŒÀŸa-å=Óµæ7ëñ/³Où/ÅoÝ£ú É0Éÿr,šÍU÷[1ª±¬ÍGÑ©-b1oTLc$¹Åe7Å| gKO]þá&³]lJXicXJLè?Òö.þ
+endstream
+endobj
+
+80 0 obj
+6734
+endobj
+
+40 0 obj
+<</Type /Font
+/Subtype /TrueType
+/BaseFont /Arial,Italic
+/Encoding /MacRomanEncoding
+/FirstChar 32
+/LastChar 255
+/Widths [277 277 354 556 556 889 666 190 333 333 389 583 277 333 277 277 556 556 556 556 556 556 556 556 556 556 277 277 583 583 583 556 1015 666 666 722 722 666 610 777 722 277 500 666 556 833 722 777 666 777 722 666 610 722 666 943 666 666 610 277 277 277 469 556 333 556 556 500 556 556 277 556 556 222 222 500 222 833 556 556 556 556 333 500 277 556 500 722 500 500 500 333 259 333 583 0 666 666 722 666 722 777 722 556 556 556 556 556 556 500 556 556 556 556 277 277 277 277 556 556 556 556 556 556 556 556 556 556 556 399 556 556 556 350 537 610 736 736 1000 333 333 0 1000 777 0 548 0 0 556 576 0 0 0 0 0 370 365 0 889 610 610 333 583 0 556 0 0 556 556 1000 277 666 666 777 1000 943 556 1000 333 333 222 222 548 0 500 666 166 556 333 333 500 500 556 277 222 333 1000 666 666 666 666 666 277 277 277 277 777 777 0 777 722 722 722 277 469 333 552 333 333 333 333 333 333 333]
+/FontDescriptor 81 0 R>>
+endobj
+
+81 0 obj
+<</Type /FontDescriptor
+/FontName /Arial,Italic
+/Ascent 905
+/Descent -211
+/ItalicAngle -12
+/StemV 0
+/CapHeight 0
+/Flags 96
+/FontBBox [-517 -324 1081 1024]
+/FontFile2 82 0 R>>
+endobj
+
+82 0 obj
+<</Length 83 0 R
+/Filter /FlateDecode
+/Length1 20408>>
+stream
+xœ|	|TÕœÿ9÷ÞÙ·;KfÉ6w2I$#ÈÂÂ&`$É$I2ÉÌ$!šuAŽjmµ "¢¶8ÀZÐ§U«TÛ××W»š}Ö¢Ö¢¯UüçÜ;ÉlûÞÿÿ>ÿî¹¿{Îïlßßz.#Žhb'wzýý-®Ú5¯#M¡pÙg®×~!ÙòÎþ®ÞŽ±EaÅð<¿«g€³÷¡U0@?B¿èú;^ìžK@(¿Æ(ï
+­»¯
+çìîÞØÙjè¿û{Â?ÂÛçX¯K?ó¶R	Ï7Â³Ðçï
+>ŸpV.<ñûÃÑØd&ñ€öþH°ÿûßçjä×@
+Fd?dGËÑ?ýàOLÍNQ\R­Éã**µF«Óâ&³¥XmvG*JûçsÿÿùÈN!œ¢T.Ùü ®É}"4ù1iOþóÀ|\ºÄÏ³è4ÚFÑAø
+A<æPA·Á÷yôGŽ=öâc(¶¢@?cúÑÐ$êGÿ1;ù&úºë 1zEëÑÞÉ;±iÕ¢:ÉŸÂþûäÇž÷øiš­F'ØÑ¯0Ç\.³Ë¢
+ ¹
+ýeÁº(U %h9ºÖô¬õ%ô'«|¹­GÐ
+èô|'dì+²ußY`$%ÊE
+(\Q4Ÿûø«±?ßcíÜMüm4
+ÍBehªG°Ñkè-ô
+ú+^;³íçd\×€uò¬9 ¥ðmBëPºmÄ
+DGGØÝ/Nü(ßBXuûß XE¿ÆFìÀ9x^×àÞ¿bLs=sù/VÆæÁ·}=Îþ}ý3·ÛÂœ/×LæM6NvOnÜ7yzò÷©å¡e0æè*ä]
+£ëÑ
+èÖð}íC¢hD§Ð¿¢wÐïÑgè¿°
+àËp5îÄ=x>ã§ñøçL+ãg
+fÎ²nvÌ}C\
+·r?@»'LütR?ytòåÉ?MNÀ<-DÍ(3ßö¢ûaÆ'ÑS(ßSè7è·è
+Ÿ<¶`ÎÆ³q!öârŒ¯Âpáü-|ÞïÇà8
+
+Õü¿?ÄâÏ Ñ0ÆÉd1L!SÄ,gºÌ
+æÌqæYøŸÉüùóæ=æÏÌßX#koË.f²W²av;Ânc<_cßå8Ëã
+ž¹G¹§ž7žž¿É4²;dwËŸ#{Oö
+Éyùåòònù=ò1ù[
+V±JÑ©ØŠØ®øâ)ÝÊ £`
+G`§IæJôúWü#ô6>ÈZ'ñJæ1|/Ö³vŽý
+þ¬ÝÊT3qÜÄXÙ¿à!<RØÇñt`8æWØÃ=÷£gÁv3-_Á=Îã÷seÎ¡ÌÇd
+¹
+{fÿÕçÕ
+zÑ÷z9 R@/ ïËUÌ
+û(Yæâ%D6Ì'è#°#®AWãGd1æ!ŒýÑ¢õxy_&¡N9®Ç£Ìrö5|,ïYÐFÜÍTáv4ÞÇã÷uš¹=ÂuÉ~=x¹¬ôqï²KØNÆÌ<s#z
+
+K8±¯ +ñ]`ýgZÂÑìsøèŸëb»a[ß ¶ð4Ê.æ4h!:Æ
+C?ÂØ_bzÛûðÝõã­èsùAî0{DVÎ¥Oþdâ·øQüæä)æÏšbò'ìº.ü ç »Œ¬7iÐÐÿðš
+°Ç;@_SÀ·©ÀÊÀs-CWáÏÀbn Êq
+ZÎd¡ÍÌ
+ ·³
+Ïç«yõeóª*+æÌ)öxògçÍÊÍÉvg¹gfFzZªÃn³ŠXÌ&#oÐëŽµJ©Ë@Ô»Úxn[Ëu/^\HÝ~šð'UŽÅšjÉÚ(0ÓqúDNß'æ
+jT]X Ô»
+øÙ:·07¬júö:w?Oé&Js¹ôA.ôêíÝuB
+·	õñ¡î]õmu0ÞºÖ]T #j
+ â6wÿl)ÁØêç
+aR«§ºëêãw
+YBÍ©÷wÄW®j®¯Ks¹Z
+âž6àn#÷ÂžÁCYP-&.¯+è4BlÝ&
+)8³k÷ÚÛ<ÚwÿÊæ8ëo!s=0o]Ü¶õ}ú7Õ6ïLnMcwÕÛCyÜµk§ß¿ª9¹ÕEÊú29
+m»`êÝE»BO¶"n*è®'5mWq{¡»{×Õm Ô]qŽzÄu45Õwrò]Z/ìZÛìvÅkÒÜ-þºô#ŽkõÈšÃ'8f¶
+á"GôÐêàT¥(;¡WOÁÉÜK@
+âB@4»a#€V¢]J`O^ñC(®ªmÛÅÏ#õ€\Ã»
+]#»ûüfÖø¥yÿ9"$Q)ö
+÷xâùùD/µ HXã|ú<·°`h)w÷óÜ >Ž²ºµÌóæ.êmc>Ôñ
+«Ågµ§
+E>¯§%ÎŽ3u€eG¢eª{Ô÷MòRâÊÜ©?Þj®ïÇÖÐ
+Û×žWmhêwµIØ6®ñ$¶WNµITÜ\ÛÌŠ1Å€±Ž4ñÊ)fòÐ¬s9ðGN5¹cL¡U€5XhómÅ²Eírý7;M~JzÑÛt7iñyÏÍx±<í.Ìå2k7ìÚ¥ÑÖ ~g×®·Ð°«mlrG»[àÝ»N22îê¯oKHtlòÔmiñÝ-°n<¯I·dÒ,R ¥Gü.QÁT
+E2n
+cZAã9rigkGUd÷ð_TW/ç/T7W£ ù¯¡Sì2º9P`ÈØ¿Ø3_ûdè+$pgHObeD9Nàz€fêñŸ0ÊÍ{Œp
+ÕS\ZQªò-8{
+¡pBÆLV9Û§AõX.«çhGæª£òzÙsËšr^,è<
+azåy^ÏØ£±-gÏ~ý8æFØùQÈž
+H@wùÚ±,È"X-qD|Àš°&ÝÓÒ{Å"x>aAØ)ž0ÎcsïÊ3 ejŸrU?>£yaW?ÆG
+«ì
+¯§ºT­Mçó_4Á"«=ãè\§GÕ²Eëøç£VÜÚ:àñbW-I±0r¹BîÎ)-©('ßò¹e¹³rÝ®Ú·vËë7ÿð¹ëÛïø÷Üº¬|ÑŒe¯m[tïÄžìÍ¹ïãoøÝï®ËŽ
+Ì0¹¶µÿèi§
+VÖ»¿î>}ÏWy{ÌÌç·o5qz>Õ`æõ|J1ŠÒvŒK§×#Gª+Á!6<Oû²RòœòÈÐÚäýò
+r¹<Å0y	lŸUÜý­Mãï#Ø³žûjSôEÚ<2ª0\UÆ*E5(nE­+o= žX³«"  4ãRŠcßŸî¥±
+·ïØ{í>\ûlüêÊÎœ·ÈN-9Ò·ñÙ­ó
+®ñ2[u°uñFhÞ'ß«`×vt¯¯DÅ«e³x!ucJ(eoIÃî25ZF£Å
+iE«éŽ1yB*O7/;®Ï¿Yµ@ùT>ÍJ
+4X3Ætùô©øa.ÿfûa­cøËQGÙ·Bk
+?r8wO
+:ï©©®®Þ©/òÈ`Óp·Sì×KYyÅZ
+»¬0Ípî¬ÜÜ¹eøÞµºœÿùþÂÇ_
+-ºwâøÁ¿Ìúúþò¡=]¯üõõßNL~¯ï
+kØ7L~Èsóáü4=ï[µ±çšs4nmNÁ<ŒËœÊ*å®.WV¯áŒy¹:Ör2Ýy
+Ö¬S€æy<jE­ÖY³6l[mvŠ*rÕ%NVck6X1ìð_|^A[n2Q3ïîw3îÉLÑT2ùÌp&ù,³Z¹PÚ=Ëù÷[=M_ŽçAý/4EÌŽæüxë9
+^R*r-0lUðGTÖ
+¹
+[žgW
+
+dÉ³ÊËKKHr§ žÙÜ¹f°=#AÈòW
+Ü}lÕÍþËñº¥)E5#œ®§+ÿrò¥h³ã²tëÓËs¯èüþ·ü¶Ýžªñ;[n]cÒê3Î©É.	¶òß?tUCÿºþ¿n[QrU~ßÀ«ô«ªµozø:Àx1è¹Ñ×ŸÎTAo,Ë$çZ3²Ëøžñ€Q>ÛèÍ©ÉY²>¥3EŸÕ
+Y%%Ë4±éÙ¬Üif7FL
+ÎšÙN§\aÎCj»Ó Q	ŠtÒœé5é+Ò?I¥§Ó"ž
+tQÍcžÜçP#ìE5hÚDÞ¬ÏS
+²­Ú=
+O5öl]Î
+	SkÄ³µé÷@xøjìõlåÏz«Ø6 ÑlŽ?
+WxZ[0±Óqâ¢vÊ-ÔVµSÿ"ùHîx«RX*¥0+šL`¬DPò:\rŒwýîe÷ŒŒrhÛöæ÷âoù7íëŒ~Sû
+Ðäñ+ŒýÛÿžo7
+yËºåpêðÍßŸïÁA°©(`m}NC»}ê*6déJ»OÎÙÇ&ßõ­ãeUêÛÌi!þZÕ¿R&·X-³UµžiVÊ
+Ùú5]ÚÐ
+ò6395
+Î3Í.Wõ©Þ*(rÓ
+ÍHÏë}cFe#QÙ/š³ŸÐt
+ÌW*à°ZÏñãçZÂÆaû6I3MÙdû\²]IY×ñº¯öÿðßwaüè¯
+ÅÑ«z÷oÜÒÜüŸÁüòóïŸz¯|êù}Ú`d×ÄßºåA£z`¯R¿ìDN¢ÉwGas&²ËM W*V®ç2
+lH;Š;¡WXõÙ
+wÊ"ýz¹Ågyº eœºS-KÔÕ)x¡ziÜn0h5JÒ*
+A¯¶8îu}³öuÞ°É6ì7p1}ÂÅ²\!÷$Î¡&{ÆªÖŠsãçÈî«á
+ðîäõ×QÙ·â"ý
+	bæRìÆà8IŒ¢²×3,ÿœÇï{uß'[~
+Ürlâ§M\œôoìX°9Žø»GßùÅxÁþÓÌe_6àçÂ;ÖíxâËmwÌ»íÄÂ®<Ô
+(9\
+ q
+PX	*Íy^ÖmöÛ
+Ý±(Q ã
+l.[ NÝÊ!ÂÒRkÂFCÊæ!AfæñJ 8ŒKK-0î1í71&'8µ
+hi¹Ëf¹îÁà³	eÈÀú
+ï Ró³sçêáõC*ÉÆÁ×Ó@òªšJd `ò®Hþ\TÂ%y{ìÁ³+ã¹<7ôR×CÝó£ÿÔýwk õØD6³ß²9vL7ü©÷ÍãEûþôúòÕNÇ=nÅ[Óµ·ìÝVÒ
+©\­ì_œàërCêgkªïß±Èì8×Tnb9p1\
+kJ±Z@#V£e5*œÑju#Äy^PaSÀ[Ác6ÆCÔ7ÅRRTVk3Rq1U^±1c6Õk»ÁÕlå?#<÷
+<üxBÑxÏIq'ßqÐ%âTnßTÅ¿ªñÕÕ
+^ý$øÝ¥óP+õ"R
+m}þáöÒh þ×óçVXì?ÉøÉóìwwß7Ð± ãûö¹Èî¯;öÌž»Ž'â(WŽŠ9câœèO
+ñÖs,sÎ^¥ZÌÝ1Wã)ËärRjPµô£ÀfS«
+yº<»ÃáVÛ *Úp6B<L1ÆÜá+Õy
+=Ï&ÏsêÔrgÁnW9
+Í*à°T¶í6ìŽym;loØžM6 19ÇTš£1ü/îta×K|n#5VåÙxµM]ŠÎÝšòÕàÃ=üÖü>ÄÍ÷øqÏÖÏ K`8oÉ
+¿Ioqšrxš$
+ü<hê¹DÂá!¹SÏ¿šÝ7µ`Í&·ÚšÑVT$YsE)«gD5[lÖi®gæ=ÍdåÌY~`±7ÏtûŸÞzòO×ýl ûà/Ý×nÚqrã)áºxïÞÍ¯Ý\Ñf?ßh]WuzýçßÅ÷ÿøðW?×œpûj³Š·¢iÕuX>|Ã÷íý	9!ÔŸäiØâ^Äá\vj:HÆsKpò
+v§ò§FEb«r+ÄØgÏåÓ3
+d¬ÍÎ0v»[ÌZUZ­[Ç[t:ÞqDX
+(ŽJõÍ:Õ<æyWW£Û®{CÇñººMº°ÓéÆë|
+©fUv;š»	y|quš0âugu¶õóI€õåN¶ ü­r§,a*ÖJ-|B`v¯h
+T^	Ë»Ä]
+^4
+øDPë@RŽU°n³ä:ä
+ÙÁàá+oŒKžáøÎÅuíGù Žm_w[€òÞñÛvg-ì}y¢÷r0lÀÎUØëS?ÆþýýåTcg|ËŒe+T;ToªX§Ê«Ú§zJuZ5©ÃÁÃ,@0Ç(n[HM`+Éyp2cT
+E§â)jàFÈvp÷&Çp>¡TBÂIÀI y<ä!Ç9_SQ
+íŠªÉ­á|ósèÓhc®X«_àZK
+&·ØQ,ÞÓœâÝ&±ª,5c}:êpÕx?-ÈHòS€</%>Äh²$ïä 'KÈÑqœçg¢îíãosçÏýÊÌå~õk¢ÃåmÅvÂ·®MWÈvÈÞ±JìyeûdOÉNË&e
+e%=¥¹h&@Ç²}	
+3¡Óè
+Äì@ož|È­»91³Û AC>»©%@C4ú€O¯' °HÕš«BŒH(" ÑZ 	IhûÓH£Û4¹)è.Bn*]€HE@[q9(æÄOe§Ÿl djï ¯ø.o0âJ]¶¯à÷é7l¯º?`ä÷Ûîw¶
+ÎzªàŒ^¿^¹N
+©S¿œ@®ÂYÊ,ý\e©ŸA)/ _¡ãËØÙùOãyD& 4#3Óí,©°Ó)L&·Ùb±
+6-9N¹Ã©ÕR¯ Ïwf€»`ÿÜ§³TŠfÌŒüz³OçÌà3Á];y'ã$5NÄðùÍöë= wZx3QjÑÞÅûáÖ4
+H&S/bGø$b$
+àTž ì3Éià!NÁ¡²TA]í
+C2]ûöñàŸÖÀM)«F7Ýd»óø]æ
+Õ«µº{ßË/(kzüê¬ûÔÀúÐU
+m9ãk­Ï)«nß÷èø8sv³Ì×þÔÃjÉSW,mèßÊlÅ\ãf²åLr=s
+¶Qn1>a<
+úuå«F=kµ1el6*+_ÕOe%9i
+*"<|5;'|FÁò<­M§žH­j?}TÛÌÃÍnO»ìgë Odð©£¶f<OùÌI²±O;iúæt&#9ñÙ4/©9rðçìÔK,"NòrðÞL$5øà·ÌÚ|ÊýÝ©;ß²€þ¶·J»žÜœ
+»/Û>~
+óP»wîÂWþ2aîìv5 §GÚr!YùH9BÎRá¶¬þ,F.KK±d²-
+)ë3×;Ã)mNy­Çø!Ë5©[3±²t'§Æ  _¡·åº
+Rð~«fåN2ŠÄË1#äEìÌÌWÛ_6Tt>3œw<}ßçg>úöÄ'÷]ûÚæã{Âó"íõ)Îœ}ëvÌÅwã×}úúÓ/
+ºú
+œ÷~ÏÛvÍ¢ÀÆ=ûV=ðž¿É&BÜbØ¹ÐŸ¬zçzî*ÃÍÙŒ¹Îz®É°$EÃ<)\µAÆM~ê[¥'/â h±oÁ#ö[ñ}è¯.¹Ã«­Äqßm+]ØddØc4Jîçõb|Ûz1éUB*JÝÊ€1._691«Få·PÏÊÔu
+eåw²Ô¯ÍOJµ°Jµ^ŠÃm
+ûy)Vd#ŠÏÈ % $4³CR+§T
+!§ÅY¬L%r©\ú³õîÕßy¥oÿõ§C[
+ÆÎ\ßV?\8=÷mãï~zpâË_?Í..Z°ozzçÝKöþ\ŽBv#àl@|×šØUw+÷ª8¹Îª;š|û÷%+ÏeòžJ\Î,Æ#øV¬ÐVÃ$	É	>pöéù2€"a
+*&GgÈyÛP?£OÁªDûbÑzÆ€×Nâj4>k¥NÓI X\RšÑÛ è¬b)ŽÑûÑL)ºŽHiç3£DDF	AŠ(X0Áý
+«\]Ÿb©·rÓ+UžÜ·®u(ëç'ÖØŒ
+ìŒ
+ÐgÇ5ù8OMþrî,98žG÷ë,vÝêžWsÓ
+ÈÁî.ÂUÄMìmö:®
+ÑÜ¢aóµÙ®,w¥Ž6"Ü9lË¶®6£lg§:Í
+s¶&CðAÞÃä©ÂÍI+19HùøBò¶íS¥rø
+&çS+AÌ=»Yy+ó®[/ZÿŒhÒŠ­ãçZw60õŒ¢0Úª°Q|
+Ä$Ÿªñ+èñŸCËER{7Ä7) äb¡»îxñõë·lÉÎø}^mÝ+Ç^ùwÛ1xUwaæuo¯÷¿ŒsìúëñfÍòŸ¶Þüük
+³Ã·
+;y¶­}IInjù²5Ã+Ÿ³qãFúçcæ.Ù!nñå/5t;
+ßÑßo~LO?þ¡B
+fÃL£¢3«1|j8pNá	dfÒF-Í*ívTÓ<Ë€²Š!€É. eåUwªXÕsçhZå(yýÚê¹pîàAJñ-=ôÀIŽ'GA÷9·¬
+6Í,9b§rüQæË{|Å©×ßqgÅ«f
+¹Æ_}÷·sóêÝÛÐn,»nbÛîñãýV!ö·ôjË
+(4át(
+ª{4''Ô€ŒgWš*ÕÚí÷(îQ?É>.WÎRWØCêvÐ./À^ŸÊžÄÈ¥8ì
+­báv·ÕA£LiQCpTbL«J	§MÑ8ìjYjÕ¡ñ¶f+	{{sóM°sÀIf4
+,DÌt­P,ÃoÊÞ}*cœ²#sØd6YªºòyÉý-'FÛtþyé5 7Á%X|w-º8 él è)E?õ÷­Ò!ú;ñ>ŸÑv³Ó¯\s_Öãw,Z¶7ä²fŽ
+û±3¿ºœ³ö&8Þ²Î[]»tÛú]ø5H€1z2ÀT
+ô5f±¶AíÓ®ÔÞªžEµCû(>š~kä2ÚÊÍRW"
+íJ2R)œ)RñÍªJ©$N~ÍHÉ+@#úþïSÌñÁSžŸ®&&5þ9yéW-Ÿ± nGt$£þ
+\Û1¥Úd+Ã[jì«²]Œòp|£ÁZ¶
+NïçrÇn{kà.f;ÝÏ>È~+`?©0î{lH'íXä6ßÌîÐÜ¬ýù&ûÔûTÿfù@õ¡úC³>
+n\eäî«ÕðeŒA¯7êŽ>Åj³YìÔTùM\M~Õ !&éÍ6ÉhÔhlµìÜ¬X,©ò@*RO1
+ÈÂNM·ÙRMÍÆSø$Ò0
+£gÔX=O2ÍbfÇšø$üŒOeéH»}7q;çŸxÿ¢uÂñ
+}Ü±Œ>X÷ŸœÿâcÀê<@u^Dì<
+
+ªä>}eøâpK$õŽ V#šŒQdsgÉëÃÍ_4þ[É;>þ·gó8fvåÄ\î{âBæÆÎÞ ?~~ä'7ÿã«_3áy6_;~/ýáÄ*î:ÀÞŸã[#X5>­ÏºS-Si5:«ÊŠÎ×TêäJ¥J§×+NAf¬d
+<_ªÐ[
+œN¯Vð¬N	è«Õ*¹RÍ
+fŒ
+ÃœºY
+O1w¡Ì
+ øsÞó^pËô/9(	ú771â{/ZÃs/*«©J+€·0T@ÖTÎ­È*(wäèJ»ÿæ¹ñíß	ÔLt>Á;\»¹ÙãìÛÇ^ñUS<"þùÙ®^÷ö
+Õ+
+JúG"/&÷ãýíW¿ïR>Š,
+T5õ+M(ó'£ZåhoT>9bÆ¯ÒåÀÄT!r
+Á5({m­GÍpÿ1EàªãÂœ®«§îsI=~]W¹ü	TKù¢šN~Ä<Aû,^ÒoÐÃxû~é®p}	ý+¬àcÈx~M~÷
+Öô¬|BÊý©ÞDHý6BZ·n!ÃÈX ¯ÉyB«Jù1BÖ/²÷!äXP*x4 .ýcò»YF:5hB<d9`ÆÙòk Ó!?KÝÄmE¥NÐ¥ø©Éï¿(Í ­ìcfQìDs<œDË]öµDËÛ:V åm­D¹žD«Ð.Ý¯$ZÍœ@g&ŽµëÛ%Z:õ/JŽN~L-Ñzt¥!ê¶Û
+7OÉ\ÆçH4äŠë$EåŠNæÖôDËŸQ¢åHo:$Ñ
+Ôn:!ÑJd6_%Ñ*ToHŽñó2Ö 9Ö§§~
+\jýP¢uì³]¢õšÈ+Á
+A]o['Ñ
+Jµ-¡ŽÖ%Ô(-'øÛnhÀÜv
+¥Žþ!&õ÷PZIëhRÒ*IŸ"-ÊW€EùŽ(_å+Ò¢|EZ¯HòiQŸ"-ÊW€EùŽ(_å+Ò¢|EZ/¡ÕIX©°Ò@œEÚ»êiïZ¢á¶·$Cé¶³ÖCœÒö¹DsÐ÷&¯ŒõvDñÇ)mŠõnz»Ò$Ì-I§PþË%ðÏ¡ŽÖ_!Ñ€ŸÒ2}@¢a
+{ÒiÿV&ü¢¬3æÍH×IÇyD¢É8÷Q:óDq¢t>­ÿ7&õ¯RºóDqþÐÊ$üIø+ö¥LÚ6_Ä¯M6!µhN^AÔü( w=×ZÔMé&F}pÅ$.ÕÂShRú¡>D9šéþE@ÕÑzÿÿãHÞ©	h
+Žô Á)(Ô-»8ß
+TßbT(Q%ŽvôèûjèÓkÑ^«aŒ(\4e]CŽQïÔJ"0¯ \~i&?	Ðô'#öÁ9ÌBZütŠ4jÄœtD²nX}/
+1-1ÊÝMç"šÇ€¢tÚ7FÛûè(äNÖŠkI{é§cèª¢t6ÒBø;è]\ÿ M 3$¯*DÇA{}
+ŠcwK³%Þ0
+K;QßCÇIàIDæbŸ€šà.
+j)ÒDVÓZŠrPD{h²R¢
+œR¯ÄÚH5$íŽhN£Ð	d4±v×nXÚIòÒ§i©F©ÆöÐÕ}³N$,':µ ý52ozÌ³YZ­_Â?@uZô>Y»Öý¡%$ÉðôÐß@z¡ì¶!	mqi[öSYÚ!PÒþCTj=§Úš}Ž§ždíMi í[$ÉôÒÕÝå,¹gj
+œôiZ{cùèEûHsŽÓ)Ò
+3t3 >,ÑíÀÔ;©nT¶Pl£TïbT]SR'kíØRÁ5E%-öGbk/m¥ýÅUqŽuZÓÄÙ;(ZýÔJFŠvôŠí~DDØböO¬81z?Õ¡^êCk+ºÄ¯Î!5âïºšþûéÊÐœõPŽÉ¿VgOx^â9+¡P
+Kx"Ô:D«=cääŠëgÔ7-L·
+Š¶|E/]Ñæ)œø¿¢,»$ï|âŽoG]1D@+iåÒù \swRmO Lô9J%Ô-VßZØY\µ°#B¯ZÒ¿ÊeŽŸ
+jÖ@Iìf _ß&Z»é^k©ŠG¿Á©zqÅ¢Žû%}¶Kñãd0PêŠÜý$¢EBÛiëðNÍò»"vŽïŽ¿JEŒÚŽ}KHòçQÉßtÑQSþ`Û"ÍF<ÏäçÛ§"¥8gì ÐÀá)ÏŒApÊÞ"Ô·Å$_Ó)ÙÊ7áð±`Ò(Ó
+æÒù:$ý"ºÜNœ¶žêvI2}ÒÈß$¡YtW3£Å¥ZqéÌ	¿K<¬fA~µGB;*ù·¿7wÕýŸ€0r,Rl9bdñÓõSdI¬Q{ûç2$]ìKò»yõwP€CI.¥LqGôv:¯øÇHÕõÒñz1Þ0ÿf*ÍdoðÝÓaàýÌ Eß=µq]ÉÚÝ+y{Ñªú%ý
+3uèíhZ?Ðœ_*¹D^HâaPÊ
+ÅÝ¹hJµï"D.Â{zd²¿0
+_
+¢yÛ0JÎüþ¹ôã6òQ<1Þ¥rÑÎŠtÌKí8!1ÿEXwþV;ò¥3ÌÌEf®((eØ1£HY µ
+DÐJT* j
+PÎ§B8£ÁUÈ¹|
+j8¡uŽIt*
+ô*Gsá<C.2z7Ícúa>/|é·æ3->@=ßßª£Ö9<¥bIÞ¬i5õÐb].åfa)ë'ö)FÒm	Q	¬r:n­"§1MüÏÖí¥üœ0ÊõDV^{6Q-ó¢)ÎÿÝi òÿWfIŽy/ÒÇ©±×ô;ý ðž°¶;(4
+ûÂ1šjÃþpÄ
+ûþ@Pçùÿ	&¬	÷š°€úÍ©ª*.¢€HXÐÓ#¬uuÇ¢Âê`4
+vÔûbÁ^2HdDú¡Ô:
+`4ÔÕW ,ü=B žü!hì
+GB÷`¯¿/	nÄAh,
+±n m#BžSÁ,ý`G0FÃšàïëü0þ` [IC
+úØ`_PÅº¡{jÃ
+€7¡{ü0ô÷Ãbu±á`_,î "B
+
+Fü°œX$èõBé-FÉdÑp',.¡s°§HºVŸ7ú:£1ºÕhl€'NÌôú(G$ŒõÃú0Q]YGÈß&íÃÝ!Øaw°§	]¡¡ e Rö= ÐìúB`÷÷÷ÆŸ@&á°àØLo°gDœEAÈ=dÞP
+7&éMT/ =ÚÂ`4Ø!¢
+$
+ü
+Î0lFMÅb¡Ÿ.²õHä
+1E2ªGðØëïòo
+õÁÐÁX @
+ºw¢ý=þ2éÝ
+öûûaiÀÒK
+¢d`ÂÞ	÷éhE	]'nmu°k°ÇÄü=¡ÀŒõÐ(oIQe×
+DÂDT³EfÒ
+"ÝŽ>
+ÖF@+zýÍd°Ë.PÏ h"Õ6`]·FXé	¹ÂÚ&aEgg]r°'
+î¶¢å+Ö.iXR»`íË
+
+Â²%µõË×Ô­®¯oª_ŸV§Ö©×v2 #Ã¶ÏÔzÀ&Ã]÷A°}D	¢»°ºÁŸª - jTãA[B çÀîïD¯èÖí¥
+·£±! åÜ"Á@ŽŠ€2œ.¢á® e¡
+3Õ
+¶Ð>¡aa°Ï€
+Í&f1ÅTg¢»Â¿gÐßúêŸ%÷.ÖõQIìö$	Å/DûPg(péÎ@±ê.éëïèDšK+ Õ-õ-ª'Ô"I(ßp8²9*ª<ÕnZ
+lï	E»É<0w/š=¬DÕ?"Š !4s"ÇÎéÍ_80ÒiÀ>iiÝ9Ú
+
+ìé ]
+
+EçwÉö	H2þ€cÚaNíEÝt 6-c²1¿ŽêÎo
+.yªäE€`
+l
+aX·fP(äUUÌ*æT«Të¡²xÎ²2(+J+ò¹Us«têîX¬×;<<\Ô| ÜlA¡.â&X	Â¢`€Õáv°ÐåàÍÂàúFB_Xã§¶
+XVYòwÆövÇz{Œœ1òÿÊx{£üÄOÊÿfá`Ôÿyòäp€Ü3^ÔÒ4;J_ÿÍŽe+:ÿwZÄ>ÉõIõa>&·³ûÙgØ°£ìIöÈßyA#
+Ûêð*žÇ.Zs8ioÞ
+9ÓÚFhú~ñì¿Ò£q©\-çãp\É7õ
+Èàâ©lÑc%
+c?=€ô]Žâ>zž	¡?R*¹¥
+ÆÚJÓz¿ôw¬àEþÏ¡K?Lh;s=×iž>CÅP®k\,ò1ÞYêÛ&z]Ÿªd¹/k*¡ÏŸÅâ]­ïªyâœžð
+­ßBÌóçÏÙ9%ÛðÌabR ôÂU×vž8üÀhJØMe!Ý
+MM+1f
+G ß#tøÔÐlZ!_¡`>YP?"?¡åvZn¢e
+-œŽ4H­$³Óò4-¢¥5Ž\AË0-)?>ß?Á÷#øþÿÑgB91_y'ö`Ä*¬9ZæÜ;5Ÿ2gPë,«TXä,»®kò;árå×9+0"¿·ÁR"
+$c2*}cøOOìÔïÔ!Õ®9¿Ì¹@
+ç¡S
+®
+®ïÂÅ
+Í8œúÀ<yÔùUá^Ôù¥sL:ÿæ
+c°Ïìü«óó¿Ï8?w.uŸÿ€ó$p}÷šsÌ9Æ×þü1æIÁys5,îs³ÇÙ'ÐŠ
+Ü|g :mÈßàlÆÈ,Ë:Ë"'sÂYuùcpú·:Ki×Òõs3â,rÒé
+ÄéfkË#·ÎY0Y¥Þ¹N§Ò©*öüV±çbÏAÅm={.Sì)Wì«ØS¬ØãUìñ(öä(öd(,JWêZ¥Z©TÊQ"¥
+üÚÐCþ²Þ"çÉMÎ£4Ï¡¬dÐR7³Lã
+ž1~&Û
+økÜcXœjC\æ^ãŠFÔžv¡=^éi
+SL®Wxã`|GÔÆ[Æ0ZÛ<
+€êŠ4òv©:nº=Ü'oºœ¥Yjì5ŠùÆªºo(Ú€2éØgþëÆ#'AÊÍ£
+çå
+x\{Èã
+òhÏßÛžŠ9þDFKŒ-ñ»×W6ÄñêëNâ[KóI¶ ®_MêÙºF
+åµ?Løð)jªQþòqXäsS>P;Ï* 7ås[
+|ø/ÜÏö.Ê€|¶wør××
+q»c¢<§Ä±âÕÅé²©8)3¥a¥Pb)b)¢3±xÇ)òèÌäùo}=úÑÍGhaKíâÝÊ÷Ï§r×9æ?v
+ýýi<-qµ{a\ã^jjìôßËµq9T)à"Ü¹ìÛÒNq¢ÜZšÖIM
+
+&Ð^Ò€'ÿœÔdßv+í>$5ñPm9Ö
+ÂÙëCuS¢ÒgPºÇPc<McŒfÕæ#
+E}Ü×V×uÅ:Š~lòXYÕ€e§§êT*Ð8±¢ ¯pâ
+XB'
+KEÑÿ](šã
+endstream
+endobj
+
+83 0 obj
+12474
+endobj
+
+84 0 obj
+<</Producer (Prince 6.0 \(www.princexml.com\))>>
+endobj
+
+xref
+0 85
+0000000000 65535 f 
+0000000016 00000 n 
+0000002895 00000 n 
+0000004392 00000 n 
+0000006079 00000 n 
+0000007784 00000 n 
+0000009553 00000 n 
+0000011366 00000 n 
+0000013291 00000 n 
+0000014393 00000 n 
+0000016223 00000 n 
+0000017936 00000 n 
+0000019191 00000 n 
+0000020274 00000 n 
+0000020910 00000 n 
+0000002831 00000 n 
+0000002702 00000 n 
+0000000124 00000 n 
+0000000235 00000 n 
+0000000362 00000 n 
+0000000514 00000 n 
+0000000635 00000 n 
+0000000756 00000 n 
+0000000867 00000 n 
+0000001414 00000 n 
+0000001537 00000 n 
+0000001659 00000 n 
+0000001801 00000 n 
+0000001920 00000 n 
+0000002037 00000 n 
+0000002149 00000 n 
+0000002269 00000 n 
+0000002387 00000 n 
+0000002498 00000 n 
+0000002608 00000 n 
+0000021111 00000 n 
+0000045890 00000 n 
+0000070117 00000 n 
+0000073484 00000 n 
+0000100351 00000 n 
+0000108410 00000 n 
+0000003023 00000 n 
+0000004370 00000 n 
+0000004592 00000 n 
+0000006057 00000 n 
+0000006279 00000 n 
+0000007762 00000 n 
+0000007984 00000 n 
+0000009531 00000 n 
+0000009753 00000 n 
+0000011344 00000 n 
+0000011583 00000 n 
+0000011781 00000 n 
+0000013269 00000 n 
+0000013491 00000 n 
+0000014372 00000 n 
+0000014593 00000 n 
+0000016201 00000 n 
+0000016424 00000 n 
+0000017914 00000 n 
+0000018137 00000 n 
+0000019170 00000 n 
+0000019392 00000 n 
+0000020253 00000 n 
+0000020475 00000 n 
+0000020889 00000 n 
+0000022144 00000 n 
+0000022332 00000 n 
+0000045867 00000 n 
+0000046919 00000 n 
+0000047102 00000 n 
+0000070094 00000 n 
+0000071148 00000 n 
+0000071333 00000 n 
+0000073462 00000 n 
+0000074516 00000 n 
+0000074707 00000 n 
+0000100328 00000 n 
+0000101378 00000 n 
+0000101564 00000 n 
+0000108388 00000 n 
+0000109446 00000 n 
+0000109638 00000 n 
+0000122202 00000 n 
+0000122225 00000 n 
+
+trailer
+<</Info 84 0 R
+/Size 85
+/Root 1 0 R>>
+startxref
+122291
+%%EOF
Index: /afridex/plugins/Flutter/docs/classtrees_FlutterDatabaseObjects.html
===================================================================
--- /afridex/plugins/Flutter/docs/classtrees_FlutterDatabaseObjects.html (revision 21)
+++ /afridex/plugins/Flutter/docs/classtrees_FlutterDatabaseObjects.html (revision 21)
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Class Trees for Package FlutterDatabaseObjects</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+		</div>
+
+<div id="body">
+	    	<h1>Class Trees for Package FlutterDatabaseObjects</h1>
+<hr />
+<div class="classtree">Root class RCCWP_CustomField</div><br />
+<ul>
+<li><a href="FlutterDatabaseObjects/RCCWP_CustomField.html">RCCWP_CustomField</a></li></ul>
+
+<hr />
+<div class="classtree">Root class RCCWP_CustomGroup</div><br />
+<ul>
+<li><a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html">RCCWP_CustomGroup</a></li></ul>
+
+<hr />
+<div class="classtree">Root class RCCWP_CustomWriteModule</div><br />
+<ul>
+<li><a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">RCCWP_CustomWriteModule</a></li></ul>
+
+<hr />
+<div class="classtree">Root class RCCWP_CustomWritePanel</div><br />
+<ul>
+<li><a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">RCCWP_CustomWritePanel</a></li></ul>
+
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/errors.html
===================================================================
--- /afridex/plugins/Flutter/docs/errors.html (revision 21)
+++ /afridex/plugins/Flutter/docs/errors.html (revision 21)
@@ -0,0 +1,435 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>phpDocumentor Parser Errors and Warnings</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+		</div>
+
+<div id="body">
+	    	<a href="#Post-parsing">Post-parsing</a><br>
+<a href="#canvas-admin.php">canvas-admin.php</a><br>
+<a href="#canvas-ajax.js.php">canvas-ajax.js.php</a><br>
+<a href="#canvas-core.php">canvas-core.php</a><br>
+<a href="#canvas-globals.php">canvas-globals.php</a><br>
+<a href="#canvas-import.php">canvas-import.php</a><br>
+<a href="#canvas-import_functions.php">canvas-import_functions.php</a><br>
+<a href="#canvas-install.php">canvas-install.php</a><br>
+<a href="#canvas-management_ajax.php">canvas-management_ajax.php</a><br>
+<a href="#canvas-plugin_form.php">canvas-plugin_form.php</a><br>
+<a href="#canvas-plugin_info.php">canvas-plugin_info.php</a><br>
+<a href="#canvas-populate-listbox.php">canvas-populate-listbox.php</a><br>
+<a href="#canvas-rescan.php">canvas-rescan.php</a><br>
+<a href="#canvas-save-layout.php">canvas-save-layout.php</a><br>
+<a href="#canvas-save-option.php">canvas-save-option.php</a><br>
+<a href="#canvas-save-plugin.php">canvas-save-plugin.php</a><br>
+<a href="#canvas-save-zone-options.php">canvas-save-zone-options.php</a><br>
+<a href="#canvas-xml.php">canvas-xml.php</a><br>
+<a href="#cropper.php">cropper.php</a><br>
+<a href="#default.php">default.php</a><br>
+<a href="#Flutter.php">Flutter.php</a><br>
+<a href="#FlutterPanelFields.php">FlutterPanelFields.php</a><br>
+<a href="#get-custom.php">get-custom.php</a><br>
+<a href="#index.php">index.php</a><br>
+<a href="#ink-admin.php">ink-admin.php</a><br>
+<a href="#ink-ajax.php">ink-ajax.php</a><br>
+<a href="#ink.php">ink.php</a><br>
+<a href="#Main.php">Main.php</a><br>
+<a href="#phpthumb.bmp.php">phpthumb.bmp.php</a><br>
+<a href="#phpthumb.class.php">phpthumb.class.php</a><br>
+<a href="#phpThumb.config.php">phpThumb.config.php</a><br>
+<a href="#phpthumb.filters.php">phpthumb.filters.php</a><br>
+<a href="#phpthumb.functions.php">phpthumb.functions.php</a><br>
+<a href="#phpthumb.gif.php">phpthumb.gif.php</a><br>
+<a href="#phpthumb.ico.php">phpthumb.ico.php</a><br>
+<a href="#phpThumb.php">phpThumb.php</a><br>
+<a href="#phpthumb.unsharp.php">phpthumb.unsharp.php</a><br>
+<a href="#RCCWP_Application.php">RCCWP_Application.php</a><br>
+<a href="#RCCWP_Constant.php">RCCWP_Constant.php</a><br>
+<a href="#RCCWP_CreateCustomFieldPage.php">RCCWP_CreateCustomFieldPage.php</a><br>
+<a href="#RCCWP_CreateCustomGroupPage.php">RCCWP_CreateCustomGroupPage.php</a><br>
+<a href="#RCCWP_CreateCustomWriteModulePage.php">RCCWP_CreateCustomWriteModulePage.php</a><br>
+<a href="#RCCWP_CreateCustomWritePanelPage.php">RCCWP_CreateCustomWritePanelPage.php</a><br>
+<a href="#RCCWP_CreatePanelModulePage.php">RCCWP_CreatePanelModulePage.php</a><br>
+<a href="#RCCWP_CustomFieldPage.php">RCCWP_CustomFieldPage.php</a><br>
+<a href="#RCCWP_CustomGroupPage.php">RCCWP_CustomGroupPage.php</a><br>
+<a href="#RCCWP_CustomWriteModulePage.php">RCCWP_CustomWriteModulePage.php</a><br>
+<a href="#RCCWP_CustomWritePanelPage.php">RCCWP_CustomWritePanelPage.php</a><br>
+<a href="#RCCWP_EditnPlace.php">RCCWP_EditnPlace.php</a><br>
+<a href="#RCCWP_EditnPlaceResponse.php">RCCWP_EditnPlaceResponse.php</a><br>
+<a href="#RCCWP_ExportModule.php">RCCWP_ExportModule.php</a><br>
+<a href="#RCCWP_GetDuplicate.php">RCCWP_GetDuplicate.php</a><br>
+<a href="#RCCWP_GetFile.php">RCCWP_GetFile.php</a><br>
+<a href="#RCCWP_HTML_Purifier.php">RCCWP_HTML_Purifier.php</a><br>
+<a href="#RCCWP_ManagementPage.php">RCCWP_ManagementPage.php</a><br>
+<a href="#RCCWP_Menu.php">RCCWP_Menu.php</a><br>
+<a href="#RCCWP_ModuleDuplicate.php">RCCWP_ModuleDuplicate.php</a><br>
+<a href="#RCCWP_ModuleDuplicatePage.php">RCCWP_ModuleDuplicatePage.php</a><br>
+<a href="#RCCWP_Options.php">RCCWP_Options.php</a><br>
+<a href="#RCCWP_OptionsPage.php">RCCWP_OptionsPage.php</a><br>
+<a href="#RCCWP_Post.php">RCCWP_Post.php</a><br>
+<a href="#RCCWP_Processor.php">RCCWP_Processor.php</a><br>
+<a href="#RCCWP_Query.php">RCCWP_Query.php</a><br>
+<a href="#RCCWP_SnipshotCallback.php">RCCWP_SnipshotCallback.php</a><br>
+<a href="#RCCWP_SWFUpload.php">RCCWP_SWFUpload.php</a><br>
+<a href="#RCCWP_upload.php">RCCWP_upload.php</a><br>
+<a href="#RCCWP_WritePostPage.php">RCCWP_WritePostPage.php</a><br>
+<a href="#RC_Format.php">RC_Format.php</a><br>
+<a name="Post-parsing"></a>
+<h1>Post-parsing</h1>
+<h2>Warnings:</h2><br>
+<b>Warning</b> - 
+duplicate define element "FLUTTER_URI_RELATIVE" in file /var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Constant.php will be ignored.
+Use an @ignore tag on the original if you want this case to be documented.<br>
+<a name="ajax-list-field-names.php"></a>
+<h1>ajax-list-field-names.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 26</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax-list-field-names.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-admin.php"></a>
+<h1>canvas-admin.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 139</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/canvas-admin.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-ajax.js.php"></a>
+<h1>canvas-ajax.js.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 558</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/js/canvas-ajax.js.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-core.php"></a>
+<h1>canvas-core.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 557</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/canvas-core.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-globals.php"></a>
+<h1>canvas-globals.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 13</b> - no @package tag was used in a DocBlock for class importedFunction<br>
+<b>Warning on line 28</b> - no @package tag was used in a DocBlock for class canvas<br>
+<b>Warning on line 55</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/canvas-globals.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-import.php"></a>
+<h1>canvas-import.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 13</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax/canvas-import.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-import_functions.php"></a>
+<h1>canvas-import_functions.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 362</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/canvas-import_functions.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-install.php"></a>
+<h1>canvas-install.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 182</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/canvas-install.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-management_ajax.php"></a>
+<h1>canvas-management_ajax.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 32</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax/canvas-management_ajax.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-plugin_form.php"></a>
+<h1>canvas-plugin_form.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 363</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/canvas-plugin_form.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-plugin_info.php"></a>
+<h1>canvas-plugin_info.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 70</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/canvas-plugin_info.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-populate-listbox.php"></a>
+<h1>canvas-populate-listbox.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 78</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax/canvas-populate-listbox.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-rescan.php"></a>
+<h1>canvas-rescan.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 18</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax/canvas-rescan.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-save-layout.php"></a>
+<h1>canvas-save-layout.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 74</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax/canvas-save-layout.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-save-option.php"></a>
+<h1>canvas-save-option.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 17</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax/canvas-save-option.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-save-plugin.php"></a>
+<h1>canvas-save-plugin.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 62</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax/canvas-save-plugin.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-save-zone-options.php"></a>
+<h1>canvas-save-zone-options.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 24</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/ajax/canvas-save-zone-options.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="canvas-xml.php"></a>
+<h1>canvas-xml.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 191</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/canvas-xml.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="cropper.php"></a>
+<h1>cropper.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 201</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/cropper.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="default.php"></a>
+<h1>default.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 4</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/modules/mod_to_export/templates/default/small/default.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<b>Warning on line 4</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/modules/all_fields_1/templates/default/small/default.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<b>Warning on line 4</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/modules/test/templates/default/small/default.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<b>Warning on line 4</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/modules/all_fields_1/templates/dark/400/default.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<b>Warning on line 6</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/modules/all_fields_1/templates/test_vars/small/default.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<b>Warning on line 15</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/modules/all_fields_1/templates/for_single/small/default.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<b>Warning on line 44</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/modules/all_fields_1/templates/for_single_duplicate/small/default.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="Flutter.php"></a>
+<h1>Flutter.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/wpmu/Flutter.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="FlutterPanelFields.php"></a>
+<h1>FlutterPanelFields.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 3</b> - no @package tag was used in a DocBlock for class FlutterPanelFields<br>
+<b>Warning on line 24</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/classes/FlutterPanelFields.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="get-custom.php"></a>
+<h1>get-custom.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 278</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/get-custom.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="index.php"></a>
+<h1>index.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 3</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/cache/index.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="ink-admin.php"></a>
+<h1>ink-admin.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 100</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/Ink/ink-admin.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="ink-ajax.php"></a>
+<h1>ink-ajax.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 21</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/Ink/ink-ajax.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="ink.php"></a>
+<h1>ink.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 94</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/Ink/ink.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="Main.php"></a>
+<h1>Main.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 354</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/Main.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpthumb.bmp.php"></a>
+<h1>phpthumb.bmp.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 20</b> - no @package tag was used in a DocBlock for class phpthumb_bmp<br>
+<b>Warning on line 873</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpthumb.bmp.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpthumb.class.php"></a>
+<h1>phpthumb.class.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 17</b> - no @package tag was used in a DocBlock for class phpthumb<br>
+<b>Warning on line 3623</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpthumb.class.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpThumb.config.php"></a>
+<h1>phpThumb.config.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 255</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpThumb.config.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpthumb.filters.php"></a>
+<h1>phpthumb.filters.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 10</b> - no @package tag was used in a DocBlock for class phpthumb_filters<br>
+<b>Warning on line 1376</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpthumb.filters.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpthumb.functions.php"></a>
+<h1>phpthumb.functions.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 10</b> - no @package tag was used in a DocBlock for class phpthumb_functions<br>
+<b>Warning on line 962</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpthumb.functions.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpthumb.gif.php"></a>
+<h1>phpthumb.gif.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 161</b> - no @package tag was used in a DocBlock for class CGIFLZW<br>
+<b>Warning on line 376</b> - no @package tag was used in a DocBlock for class CGIFCOLORTABLE<br>
+<b>Warning on line 471</b> - no @package tag was used in a DocBlock for class CGIFFILEHEADER<br>
+<b>Warning on line 548</b> - no @package tag was used in a DocBlock for class CGIFIMAGEHEADER<br>
+<b>Warning on line 619</b> - no @package tag was used in a DocBlock for class CGIFIMAGE<br>
+<b>Warning on line 791</b> - no @package tag was used in a DocBlock for class CGIF<br>
+<b>Warning on line 1167</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpthumb.gif.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpthumb.ico.php"></a>
+<h1>phpthumb.ico.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 11</b> - no @package tag was used in a DocBlock for class phpthumb_ico<br>
+<b>Warning on line 118</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpthumb.ico.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpThumb.php"></a>
+<h1>phpThumb.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 591</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpThumb.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="phpthumb.unsharp.php"></a>
+<h1>phpthumb.unsharp.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 39</b> - no @package tag was used in a DocBlock for class phpUnsharpMask<br>
+<b>Warning on line 161</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/phpthumb.unsharp.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_Application.php"></a>
+<h1>RCCWP_Application.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RCCWP_Application<br>
+<b>Warning on line 613</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Application.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_Constant.php"></a>
+<h1>RCCWP_Constant.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 105</b> - 
+duplicate define element "DIRECTORY_SEPARATOR" in file /var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Constant.php will be ignored.
+Use an @ignore tag on the original if you want this case to be documented.<br>
+<b>Warning on line 107</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Constant.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CreateCustomFieldPage.php"></a>
+<h1>RCCWP_CreateCustomFieldPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 4</b> - no @package tag was used in a DocBlock for class RCCWP_CreateCustomFieldPage<br>
+<b>Warning on line 739</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CreateCustomFieldPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CreateCustomGroupPage.php"></a>
+<h1>RCCWP_CreateCustomGroupPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 3</b> - no @package tag was used in a DocBlock for class RCCWP_CreateCustomGroupPage<br>
+<b>Warning on line 28</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CreateCustomGroupPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CreateCustomWriteModulePage.php"></a>
+<h1>RCCWP_CreateCustomWriteModulePage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 3</b> - no @package tag was used in a DocBlock for class RCCWP_CreateCustomWriteModulePage<br>
+<b>Warning on line 28</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CreateCustomWriteModulePage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CreateCustomWritePanelPage.php"></a>
+<h1>RCCWP_CreateCustomWritePanelPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 3</b> - no @package tag was used in a DocBlock for class RCCWP_CreateCustomWritePanelPage<br>
+<b>Warning on line 28</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CreateCustomWritePanelPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CreatePanelModulePage.php"></a>
+<h1>RCCWP_CreatePanelModulePage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 5</b> - no @package tag was used in a DocBlock for class RCCWP_CreatePanelModulePage<br>
+<b>Warning on line 59</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CreatePanelModulePage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CustomFieldPage.php"></a>
+<h1>RCCWP_CustomFieldPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RCCWP_CustomFieldPage<br>
+<b>Warning on line 257</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CustomFieldPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CustomGroupPage.php"></a>
+<h1>RCCWP_CustomGroupPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 4</b> - no @package tag was used in a DocBlock for class RCCWP_CustomGroupPage<br>
+<b>Warning on line 190</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CustomGroupPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CustomWriteModulePage.php"></a>
+<h1>RCCWP_CustomWriteModulePage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 4</b> - no @package tag was used in a DocBlock for class RCCWP_CustomWriteModulePage<br>
+<b>Warning on line 184</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CustomWriteModulePage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_CustomWritePanelPage.php"></a>
+<h1>RCCWP_CustomWritePanelPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 4</b> - no @package tag was used in a DocBlock for class RCCWP_CustomWritePanelPage<br>
+<b>Warning on line 427</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_CustomWritePanelPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_EditnPlace.php"></a>
+<h1>RCCWP_EditnPlace.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 13</b> - no @package tag was used in a DocBlock for class RCCWP_EditnPlace<br>
+<b>Warning on line 135</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_EditnPlace.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_EditnPlaceResponse.php"></a>
+<h1>RCCWP_EditnPlaceResponse.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 30</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_EditnPlaceResponse.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_ExportModule.php"></a>
+<h1>RCCWP_ExportModule.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 140</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_ExportModule.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_GetDuplicate.php"></a>
+<h1>RCCWP_GetDuplicate.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 23</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_GetDuplicate.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_GetFile.php"></a>
+<h1>RCCWP_GetFile.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 139</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_GetFile.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_HTML_Purifier.php"></a>
+<h1>RCCWP_HTML_Purifier.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RCCWP_HTML_Purifier<br>
+<b>Warning on line 76</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_HTML_Purifier.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_ManagementPage.php"></a>
+<h1>RCCWP_ManagementPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 4</b> - no @package tag was used in a DocBlock for class RCCWP_ManagementPage<br>
+<b>Warning on line 222</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_ManagementPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_Menu.php"></a>
+<h1>RCCWP_Menu.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 15</b> - no @package tag was used in a DocBlock for class RCCWP_Menu<br>
+<b>Warning on line 563</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Menu.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_ModuleDuplicate.php"></a>
+<h1>RCCWP_ModuleDuplicate.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 8</b> - no @package tag was used in a DocBlock for class RCCWP_ModuleDuplicate<br>
+<b>Warning on line 108</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_ModuleDuplicate.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_ModuleDuplicatePage.php"></a>
+<h1>RCCWP_ModuleDuplicatePage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 4</b> - no @package tag was used in a DocBlock for class RCCWP_ModuleDuplicatePage<br>
+<b>Warning on line 66</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_ModuleDuplicatePage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_Options.php"></a>
+<h1>RCCWP_Options.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RCCWP_Options<br>
+<b>Warning on line 46</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Options.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_OptionsPage.php"></a>
+<h1>RCCWP_OptionsPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 4</b> - no @package tag was used in a DocBlock for class RCCWP_OptionsPage<br>
+<b>Warning on line 233</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_OptionsPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_Post.php"></a>
+<h1>RCCWP_Post.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RCCWP_Post<br>
+<b>Warning on line 280</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Post.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_Processor.php"></a>
+<h1>RCCWP_Processor.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RCCWP_Processor<br>
+<b>Warning on line 494</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Processor.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_Query.php"></a>
+<h1>RCCWP_Query.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 6</b> - no @package tag was used in a DocBlock for class RCCWP_Query<br>
+<b>Warning on line 82</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_Query.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_SnipshotCallback.php"></a>
+<h1>RCCWP_SnipshotCallback.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 110</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_SnipshotCallback.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_SWFUpload.php"></a>
+<h1>RCCWP_SWFUpload.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RCCWP_SWFUpload<br>
+<b>Warning on line 65</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_SWFUpload.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_upload.php"></a>
+<h1>RCCWP_upload.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 192</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_upload.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RCCWP_WritePostPage.php"></a>
+<h1>RCCWP_WritePostPage.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RCCWP_WritePostPage<br>
+<b>Warning on line 1183</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RCCWP_WritePostPage.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+<a name="RC_Format.php"></a>
+<h1>RC_Format.php</h1>
+<h2>Warnings:</h2><br>
+<b>Warning on line 2</b> - no @package tag was used in a DocBlock for class RC_Format<br>
+<b>Warning on line 36</b> - File "/var/www/debugphp/o-a4/public_html/wp-content/mu-plugins/Flutter/RC_Format.php" has no page-level DocBlock, use @package in the first DocBlock to create one<br>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:48:00 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/media/print.css
===================================================================
--- /afridex/plugins/Flutter/docs/media/print.css (revision 21)
+++ /afridex/plugins/Flutter/docs/media/print.css (revision 21)
@@ -0,0 +1,25 @@
+BODY {
+	margin:		1em;
+}
+#header {
+}
+#nav {
+	display:	none;
+}
+#packagePosition {
+	text-align:	right;
+}
+#packageTitle {
+	display:	inline;
+	margin:		5px;
+}
+#packageTitle2 {
+	display:	none;
+}
+#elementPath {
+	display:	inline;
+	margin:		5px;
+}
+#navLinks {
+	display:	none;
+}
Index: /afridex/plugins/Flutter/docs/media/layout.css
===================================================================
--- /afridex/plugins/Flutter/docs/media/layout.css (revision 21)
+++ /afridex/plugins/Flutter/docs/media/layout.css (revision 21)
@@ -0,0 +1,83 @@
+#header {
+	z-index:	100;
+	position: 	absolute;
+	top:		0px;
+	left:		0px;
+	width:		100%;
+	height:		30px;
+}
+#nav {
+	z-index:	200;
+	position: 	absolute;
+	top:		30px;
+	left:		0px;
+	width:		220px;
+	height:		1600px;
+	clip:		auto;
+	overflow:	auto;
+
+}
+#body {
+	position: 	absolute;
+	top:		40px;
+	left:		245px;
+	
+}
+#content {
+	clear:		both;
+	top:		-1px;
+}
+#packagePosition {
+	position:	absolute;
+	right:		5px;
+	top:		0px;
+	width:		35%;
+	height:		100%;
+}
+#packageTitle {
+	position:	absolute;
+	right:		0px;
+	margin-top:	5px;
+}
+#packageTitle2 {
+	position:	absolute;
+	right:		-3px;
+	top:		-2px;
+}
+#elementPath {
+	position:	absolute;
+	right:		0px;
+	bottom:		0px;
+}
+#navLinks {
+	position:	absolute;
+	top:		0px;
+	left:		10px;
+	height:		100%;
+	margin-top:	5px;
+}
+.leftCol {
+	width:		auto;
+	float:		left;
+}
+.middleCol {
+	width:		auto;
+	float:		left;
+}
+.rightCol {
+	width:		auto;
+	float:		left;
+}
+#credit {
+	margin-top:	20px;
+	margin-bottom:	50px;
+}
+
+/** Fixed layout for nav on mozilla */
+head:first-child+body div#header {
+	position:	fixed;
+}
+head:first-child+body div#nav {
+	position:	fixed;
+	height:		94%
+}
Index: /afridex/plugins/Flutter/docs/media/style.css
===================================================================
--- /afridex/plugins/Flutter/docs/media/style.css (revision 21)
+++ /afridex/plugins/Flutter/docs/media/style.css (revision 21)
@@ -0,0 +1,244 @@
+BODY {
+    background: #FFFFFF;
+	font-family:		Arial;
+	margin:			0px;
+	padding:		0px;
+}
+A {
+	color:                  #0000CC;
+	text-decoration:	none;
+	font-weight:		normal;
+	font-size:		100%;
+}
+A:Hover {
+	color:			white;
+	background-color:	#334B66;
+	font-weight:		bold;
+	text-decoration:	none;
+}
+	
+#packageTitle {
+	font-size:		110%;
+	font-weight:		bold;
+	text-align:		right;
+	color:			#444444;
+}
+#packageTitle2 {
+	font-size:		160%;
+	font-weight:		bold;
+	text-align:		right;
+	color:			#334B66;
+	background-color:       #6699CC;
+	display:		none;
+}
+#packageLinks {
+	background-color:       #6699CC;
+}
+#header {
+	background-color:       #E0ECFF;
+	border-bottom:		solid #C3D9FF 4px;
+}
+#nav {
+	background-color:       #E0ECFF;
+	padding:		4px;
+	border-right:		solid #C3D9FF 4px;
+}
+#index {
+	padding:		18px;
+}
+hr {
+	width:			80%;
+	background-color:	#6699CC;
+	color:			#6699CC;
+	margin-top:		15px;
+	margin-bottom:		15px;
+	clear:			both;
+}
+.links {
+	text-align:		left;
+	width:			98%;
+	margin:			auto;
+}
+UL {
+	margin:			0px;
+	padding:		0px;
+	padding-left:		5px;
+	list-style-type:	none;
+}
+li {
+	text-indent:		-15px;
+	padding-bottom:		2px;
+	padding-left:		14px;
+}
+dd {
+	margin-bottom:		.5em;
+}
+.small {
+	color:			#444444;
+	font-weight:		bold;
+	font-size:		80%;
+}
+h3 {
+}
+.middleCol {
+	margin-left:		-1px;
+	border-right:		dotted gray 1px;
+	border-left:		dotted gray 1px;
+	padding:		5px;
+}
+.leftCol {
+	border-right:		dotted gray 1px;
+	padding:		5px;
+}
+.rightCol {
+	margin-left:		-1px;
+	border-left:		dotted gray 1px;
+	padding:		5px;
+}
+#elementPath {
+	font-size:		14px;
+	font-weight:		bold;
+	color:			#334B66;
+}
+.constructor {
+	/*border:			dashed #334B66 1px;*/
+	font-weight:		bold;
+}
+#credit {
+	text-align:		center;
+	color:			#334B66;
+	font-weight:		bold;
+}
+div.contents {
+	border:			solid #334B66 1px;
+	padding:		3px;
+	margin-bottom:		5px;
+	clear:			all;
+}
+H1 {
+	margin:			0px;
+}
+H2 {
+	margin:			0px;
+	margin-bottom:		2px;
+}
+H3 {
+	margin:			0px;
+}
+H4 {
+	margin:			0px;
+}
+#classTree {
+	padding:		0px;
+	margin:			0px;
+}
+div.indent {
+	margin-left:		15px;
+}
+.warning {
+	color:			red;
+	background-color:	#334B66;
+	font-weight:		bold;
+}
+code {
+	font-family:		fixed;
+	padding:		3px;
+	color:			#334B66;
+	background-color:	#dddddd;
+}
+.type {
+	color:                  #334B66;
+}
+.value {
+	color:                  #334B66;
+	border:			dotted #334B66 1px;
+}
+.top {
+	color:                  #334B66;
+	border-bottom:		dotted #334B66 1px;
+	padding-bottom:		4px;
+}
+.php-src, .php, .listing {
+	font-family:		fixed;
+	padding:		3px;
+	color:			#334B66;
+	background-color:	#dddddd;
+	font-family: 'Courier New', Courier, monospace; font-weight: normal;
+}
+DIV#nav DL {
+	margin:			0px;
+	padding:		0px;
+	list-style-type:	none;
+}
+div.classtree {
+	font-size:		130%;
+	font-weight:		bold;
+	background-color:			#CC6633;
+	border:		dotted #334B66 2px;
+}
+span.linenumber,p.linenumber {
+	font-weight:		bold,italic;
+}
+span.smalllinenumber {
+	font-weight:		bold,italic;
+	font-size:		9pt;
+}
+ul {
+	margin-left:		0px;
+	padding-left:		8px;
+}
+/* Syntax highlighting */
+
+.src-code { background-color: #f5f5f5; border: 1px solid #ccc9a4; padding: 0px; margin : 0px}
+.src-line {  font-family: 'Courier New', Courier, monospace; font-weight: normal; }
+/*.src-code pre {	}*/
+
+.src-comm { color: green; }
+.src-id {  }
+.src-inc { color: #0000FF; }
+.src-key { color: #0000FF; }
+.src-num { color: #CC0000; }
+.src-str { color: #66cccc; }
+.src-sym { font-weight: bold; }
+.src-var { }
+
+.src-php { font-weight: bold; }
+
+.src-doc { color: #009999 }
+.src-doc-close-template { color: #0000FF }
+.src-doc-coretag { color: #0099FF; font-weight: bold }
+.src-doc-inlinetag { color: #0099FF }
+.src-doc-internal { color: #6699cc }
+.src-doc-tag { color: #0080CC }
+.src-doc-template { color: #0000FF }
+.src-doc-type { font-style: italic }
+.src-doc-var { font-style: italic }
+
+.tute-tag { color: #009999 }
+.tute-attribute-name { color: #0000FF }
+.tute-attribute-value { color: #0099FF }
+.tute-entity { font-weight: bold; }
+.tute-comment { font-style: italic }
+.tute-inline-tag { color: #636311; font-weight: bold }
+
+/* tutorial */
+
+.authors {  }
+.author { font-style: italic; font-weight: bold }
+.author-blurb { margin: .5em 0em .5em 2em; font-size: 85%; font-weight: normal; font-style: normal }
+.example { border: 1px dashed #999999; background-color: #EEEEEE; padding: .5em; }
+.listing { border: 1px dashed #999999; background-color: #EEEEEE; padding: .5em; white-space: nowrap; }
+.release-info { font-size: 85%; font-style: italic; margin: 1em 0em }
+.ref-title-box {  }
+.ref-title {  }
+.ref-purpose { font-style: italic; color: #666666 }
+.ref-synopsis {  }
+.title { font-weight: bold; margin: 1em 0em 0em 0em; padding: .25em;
+	border: 2px solid #CC6633; background-color: #6699CC }
+.cmd-synopsis { margin: 1em 0em }
+.cmd-title { font-weight: bold }
+.toc { margin-left: 2em; padding-left: 0em }
+
+#packages{
+	margin-top: 15px;
+}
Index: /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomField.html
===================================================================
--- /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomField.html (revision 21)
+++ /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomField.html (revision 21)
@@ -0,0 +1,559 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Docs For Class RCCWP_CustomField</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="../media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="../media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="../media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="../classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="../elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="../elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="../li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="../FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    		<h1>Class: RCCWP_CustomField</h1>
+	<p style="margin: 0px;">Source Location: /RCCWP_CustomField.php</p>
+	
+
+<div class="leftcol">
+	<h3><a href="#class_details">Class Overview</a> <span class="smalllinenumber">[line 12]</span></h3>
+	<div id="classTree"><pre></pre>
+</div>
+	<div class="small">
+	<p>Create/Edit/Delete custom fields</p>
+		<h4>Author(s):</h4>
+	<ul>
+			</ul>
+	<h4>Version:</h4>
+	<ul>
+			</ul>
+
+	<h4>Copyright:</h4>
+	<ul>
+			</li>
+	</div>
+</div>
+
+<div class="middlecol">
+	<h3><a href="#class_vars">Variables</a></h3>
+	<ul class="small">
+			</ul>
+	<h3><a href="#class_consts">Constants</a></h3>
+	<ul class="small">
+			</ul>
+</div>
+<div class="rightcol">
+	<h3><a href="#class_methods">Methods</a></h3>
+	<ul class="small">
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodCreate">Create</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodDelete">Delete</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodGet">Get</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetCustomFieldTypes">GetCustomFieldTypes</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetCustomFieldValues">GetCustomFieldValues</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetFieldDuplicates">GetFieldDuplicates</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetFieldGroupDuplicates">GetFieldGroupDuplicates</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetIDByName">GetIDByName</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html#methodUpdate">Update</a></li>
+			</ul>
+</div>
+
+<div id="content">
+<hr>
+	<div class="contents">
+	</div>
+
+	<div class="leftCol">
+    	<h2>Inherited Variables</h2>
+		<h2>Inherited Constants</h2>
+		</div>
+
+	<div class="rightCol">
+	<h2>Inherited Methods</h2>
+		</div>
+	<br clear="all">
+	<hr>
+
+	<a name="class_details"></a>
+	<h2>Class Details</h2>
+	<p align="center"><strong>Create/Edit/Delete custom fields
+</strong></p>
+	<p class="small" style="color: #334B66;">[ <a href="#top">Top</a> ]</p>
+
+	<hr>
+	<a name="class_vars"></a>
+	<h2>Class Variables</h2>
+	
+	<hr>
+	<a name="class_methods"></a>
+	<h2>Class Methods</h2>
+	
+	<a name="methodCreate"></a>
+	<p></p>
+	<h3>Create</h3>
+	<div class="indent">
+		<p>
+		<code>the Create(
+id
+$customGroupId, string
+$name, string
+$label, [integer
+$order = 1], [integer
+$required_field = 0], integer
+$type, [array
+$options = null], [array
+$default_value = null], [array
+$properties = null], 
+$duplicate)</code>
+		</p>
+	
+		<p class="linenumber">[line 35]</p>
+		<p align="center"><strong>Create a new custom field
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - new field id</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">id</span>
+		<b>$customGroupId</b> 
+		- 
+		the id of the group that will contain the field</li>
+			<li>
+		<span class="type">string</span>
+		<b>$name</b> 
+		- 
+		the name of the field, the name is used to uniquely identify the field                              when retrieving its value.</li>
+			<li>
+		<span class="type">string</span>
+		<b>$label</b> 
+		- 
+		the label of the field, the label is displayed beside the field                              in Write tab.</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$order</b> 
+		- 
+		the order of the field when it is displayed in                              the Write tab.</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$required_field</b> 
+		- 
+		whether this field is a required field. Required fields                              doesn't allow users to save a post if they are null.</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$type</b> 
+		- 
+		the type of the field. Use $FIELD_TYPES defined in RCCWP_Constant.php</li>
+			<li>
+		<span class="type">array</span>
+		<b>$options</b> 
+		- 
+		array of strings that represent the list of the field if                              its type is list.</li>
+			<li>
+		<span class="type">array</span>
+		<b>$default_value</b> 
+		- 
+		array of strings that represent default value(s) of                              of the field if    its type is list.</li>
+			<li>
+		<span class="type">array</span>
+		<b>$properties</b> 
+		- 
+		an array containing extra properties of the field.</li>
+			<li>
+		<span class="type"></span>
+		<b>$duplicate</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodDelete"></a>
+	<p></p>
+	<h3>Delete</h3>
+	<div class="indent">
+		<p>
+		<code>void Delete(
+[integer
+$customFieldId = null])</code>
+		</p>
+	
+		<p class="linenumber">[line 107]</p>
+		<p align="center"><strong>Delete a field
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customFieldId</b> 
+		- 
+		field id</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGet"></a>
+	<p></p>
+	<h3>Get</h3>
+	<div class="indent">
+		<p>
+		<code>an Get(
+integer
+$customFieldId)</code>
+		</p>
+	
+		<p class="linenumber">[line 138]</p>
+		<p align="center"><strong>Get the field information including properties, options and default value(s)
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - object containing information about fields. The object contains              3 objects: properties, options and default_value</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customFieldId</b> 
+		- 
+		field id</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetCustomFieldTypes"></a>
+	<p></p>
+	<h3>GetCustomFieldTypes</h3>
+	<div class="indent">
+		<p>
+		<code>a GetCustomFieldTypes(
+[integer
+$customFieldTypeId = null])</code>
+		</p>
+	
+		<p class="linenumber">[line 162]</p>
+		<p align="center"><strong>Retrievies information about a specified type
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - list/object containing information about the specified type. The information              includes id, name, description, has_options, has_properties, and              allow_multiple_values (whether fields of that type can have more than one default value)</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customFieldTypeId</b> 
+		- 
+		the type id, if null, a list of all types will be returned</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetCustomFieldValues"></a>
+	<p></p>
+	<h3>GetCustomFieldValues</h3>
+	<div class="indent">
+		<p>
+		<code>a GetCustomFieldValues(
+boolean
+$single, integer
+$postId, string
+$customFieldName, [integer
+$groupIndex = 1], [integer
+$fieldIndex = 1])</code>
+		</p>
+	
+		<p class="linenumber">[line 214]</p>
+		<p align="center"><strong>Retrieves the value of a custom field for a specified post
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">boolean</span>
+		<b>$single</b> 
+		- 
+		</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$postId</b> 
+		- 
+		</li>
+			<li>
+		<span class="type">string</span>
+		<b>$customFieldName</b> 
+		- 
+		</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$groupIndex</b> 
+		- 
+		</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$fieldIndex</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetFieldDuplicates"></a>
+	<p></p>
+	<h3>GetFieldDuplicates</h3>
+	<div class="indent">
+		<p>
+		<code>number GetFieldDuplicates(
+integer
+$postId, 
+$fieldName, 
+$groupIndex, integer
+$fieldID)</code>
+		</p>
+	
+		<p class="linenumber">[line 262]</p>
+		<p align="center"><strong>Get number of group duplicates given field name. The function returns 1  if there are no duplicates (just he original group), 2 if there is one  duplicate and so on.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - of groups</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$postId</b> 
+		- 
+		post id</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$fieldID</b> 
+		- 
+		the name of any field in the group</li>
+			<li>
+		<span class="type"></span>
+		<b>$fieldName</b> 
+		- 
+		</li>
+			<li>
+		<span class="type"></span>
+		<b>$groupIndex</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetFieldGroupDuplicates"></a>
+	<p></p>
+	<h3>GetFieldGroupDuplicates</h3>
+	<div class="indent">
+		<p>
+		<code>number GetFieldGroupDuplicates(
+integer
+$postId, 
+$fieldName, integer
+$fieldID)</code>
+		</p>
+	
+		<p class="linenumber">[line 246]</p>
+		<p align="center"><strong>Get number of group duplicates given field name. The function returns 1  if there are no duplicates (just he original group), 2 if there is one  duplicate and so on.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - of groups</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$postId</b> 
+		- 
+		post id</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$fieldID</b> 
+		- 
+		the name of any field in the group</li>
+			<li>
+		<span class="type"></span>
+		<b>$fieldName</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetIDByName"></a>
+	<p></p>
+	<h3>GetIDByName</h3>
+	<div class="indent">
+		<p>
+		<code>custom GetIDByName(
+string
+$customFieldName)</code>
+		</p>
+	
+		<p class="linenumber">[line 275]</p>
+		<p align="center"><strong>Retrieves the id of a custom field given field name for the current post.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - field id</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">string</span>
+		<b>$customFieldName</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodUpdate"></a>
+	<p></p>
+	<h3>Update</h3>
+	<div class="indent">
+		<p>
+		<code>void Update(
+integer
+$customFieldId, string
+$name, string
+$label, [integer
+$order = 1], [integer
+$required_field = 0], integer
+$type, [array
+$options = null], [array
+$default_value = null], [array
+$properties = null], 
+$duplicate)</code>
+		</p>
+	
+		<p class="linenumber">[line 320]</p>
+		<p align="center"><strong>Updates the properties of a custom field.
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customFieldId</b> 
+		- 
+		the id of the field to be updated</li>
+			<li>
+		<span class="type">string</span>
+		<b>$name</b> 
+		- 
+		the name of the field, the name is used to uniquely identify the field                              when retrieving its value.</li>
+			<li>
+		<span class="type">string</span>
+		<b>$label</b> 
+		- 
+		the label of the field, the label is displayed beside the field                              in Write tab.</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$order</b> 
+		- 
+		the order of the field when it is displayed in                              the Write tab.</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$required_field</b> 
+		- 
+		whether this field is a required field. Required fields                              doesn't allow users to save a post if they are null.</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$type</b> 
+		- 
+		the type of the field. Use $FIELD_TYPES defined in RCCWP_Constant.php</li>
+			<li>
+		<span class="type">array</span>
+		<b>$options</b> 
+		- 
+		array of strings that represent the list of the field if                              its type is list.</li>
+			<li>
+		<span class="type">array</span>
+		<b>$default_value</b> 
+		- 
+		array of strings that represent default value(s) of                              of the field if    its type is list.</li>
+			<li>
+		<span class="type">array</span>
+		<b>$properties</b> 
+		- 
+		an array containing extra properties of the field.</li>
+			<li>
+		<span class="type"></span>
+		<b>$duplicate</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+
+	<hr>
+	<a name="class_consts"></a>
+	<h2>Class Constants</h2>
+	</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html
===================================================================
--- /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html (revision 21)
+++ /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html (revision 21)
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Docs for page RCCWP_CustomWriteModule.php</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="../media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="../media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="../media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="../classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="../elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="../elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="../li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="../FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    		<h1>Procedural File: RCCWP_CustomWriteModule.php</h1>
+	<p style="margin: 0px;">Source Location: /RCCWP_CustomWriteModule.php</p>
+	
+<br>
+<br>
+
+<div class="contents">
+<h2>Classes:</h2>
+<dl>
+<dt><a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">RCCWP_CustomWriteModule</a></dt>
+	<dd>Create/edit/delete modules</dd>
+</dl>
+</div>
+
+<h2>Page Details:</h2>
+<hr>
+<hr>
+<div id="global">
+</div><hr>
+<div id="define">
+</div><hr>
+<div id="function">
+</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomField.php.html
===================================================================
--- /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomField.php.html (revision 21)
+++ /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomField.php.html (revision 21)
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Docs for page RCCWP_CustomField.php</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="../media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="../media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="../media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="../classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="../elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="../elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="../li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="../FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    		<h1>Procedural File: RCCWP_CustomField.php</h1>
+	<p style="margin: 0px;">Source Location: /RCCWP_CustomField.php</p>
+	
+<br>
+<br>
+
+<div class="contents">
+<h2>Classes:</h2>
+<dl>
+<dt><a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">RCCWP_CustomField</a></dt>
+	<dd>Create/Edit/Delete custom fields</dd>
+</dl>
+</div>
+
+<h2>Page Details:</h2>
+<hr>
+<hr>
+<div id="global">
+</div><hr>
+<div id="define">
+</div><hr>
+<div id="function">
+</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomWritePanel.html
===================================================================
--- /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomWritePanel.html (revision 21)
+++ /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomWritePanel.html (revision 21)
@@ -0,0 +1,603 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Docs For Class RCCWP_CustomWritePanel</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="../media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="../media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="../media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="../classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="../elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="../elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="../li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="../FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    		<h1>Class: RCCWP_CustomWritePanel</h1>
+	<p style="margin: 0px;">Source Location: /RCCWP_CustomWritePanel.php</p>
+	
+
+<div class="leftcol">
+	<h3><a href="#class_details">Class Overview</a> <span class="smalllinenumber">[line 11]</span></h3>
+	<div id="classTree"><pre></pre>
+</div>
+	<div class="small">
+	<p>Create/edit/delete write panels.</p>
+		<h4>Author(s):</h4>
+	<ul>
+			</ul>
+	<h4>Version:</h4>
+	<ul>
+			</ul>
+
+	<h4>Copyright:</h4>
+	<ul>
+			</li>
+	</div>
+</div>
+
+<div class="middlecol">
+	<h3><a href="#class_vars">Variables</a></h3>
+	<ul class="small">
+			</ul>
+	<h3><a href="#class_consts">Constants</a></h3>
+	<ul class="small">
+			</ul>
+</div>
+<div class="rightcol">
+	<h3><a href="#class_methods">Methods</a></h3>
+	<ul class="small">
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodAssignToRole">AssignToRole</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodCreate">Create</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodDelete">Delete</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodExport">Export</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGet">Get</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetAssignedCategories">GetAssignedCategories</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetAssignedCategoryIds">GetAssignedCategoryIds</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCapabilityName">GetCapabilityName</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCustomGroups">GetCustomGroups</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCustomWritePanels">GetCustomWritePanels</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetStandardFields">GetStandardFields</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodImport">Import</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodUpdate">Update</a></li>
+			</ul>
+</div>
+
+<div id="content">
+<hr>
+	<div class="contents">
+	</div>
+
+	<div class="leftCol">
+    	<h2>Inherited Variables</h2>
+		<h2>Inherited Constants</h2>
+		</div>
+
+	<div class="rightCol">
+	<h2>Inherited Methods</h2>
+		</div>
+	<br clear="all">
+	<hr>
+
+	<a name="class_details"></a>
+	<h2>Class Details</h2>
+	<p align="center"><strong>Create/edit/delete write panels.
+</strong></p>
+	<p class="small" style="color: #334B66;">[ <a href="#top">Top</a> ]</p>
+
+	<hr>
+	<a name="class_vars"></a>
+	<h2>Class Variables</h2>
+	
+	<hr>
+	<a name="class_methods"></a>
+	<h2>Class Methods</h2>
+	
+	<a name="methodAssignToRole"></a>
+	<p></p>
+	<h3>AssignToRole</h3>
+	<div class="indent">
+		<p>
+		<code>void AssignToRole(
+integer
+$customWritePanelId, string
+$roleName)</code>
+		</p>
+	
+		<p class="linenumber">[line 40]</p>
+		<p align="center"><strong>Assign a specified write panel to a role.
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWritePanelId</b> 
+		- 
+		panel id</li>
+			<li>
+		<span class="type">string</span>
+		<b>$roleName</b> 
+		- 
+		role name (see roles in wordpress)</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodCreate"></a>
+	<p></p>
+	<h3>Create</h3>
+	<div class="indent">
+		<p>
+		<code>the Create(
+string
+$name, [string
+$description = ''], [array
+$standardFields = array()], [array
+$categories = array()], [integer
+$display_order = 1], [string
+$type = FALSE], [boolean
+$createDefaultGroup = true])</code>
+		</p>
+	
+		<p class="linenumber">[line 62]</p>
+		<p align="center"><strong>Create a new write panel.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - id of the write panel</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">string</span>
+		<b>$name</b> 
+		- 
+		write panel name</li>
+			<li>
+		<span class="type">string</span>
+		<b>$description</b> 
+		- 
+		write panel description</li>
+			<li>
+		<span class="type">array</span>
+		<b>$standardFields</b> 
+		- 
+		a list of standard fields ids that are to be displayed in                              in the panel. Use $STANDARD_FIELDS defined in RCCWP_Constant.php</li>
+			<li>
+		<span class="type">array</span>
+		<b>$categories</b> 
+		- 
+		array of category ids that are checked by default when the user                              opens Write tab for that panel.</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$display_order</b> 
+		- 
+		the order of the panel in Flutter &gt; Write Panels tab</li>
+			<li>
+		<span class="type">string</span>
+		<b>$type</b> 
+		- 
+		'post' or 'page'</li>
+			<li>
+		<span class="type">boolean</span>
+		<b>$createDefaultGroup</b> 
+		- 
+		indicates whether to create a default group.</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodDelete"></a>
+	<p></p>
+	<h3>Delete</h3>
+	<div class="indent">
+		<p>
+		<code>void Delete(
+[integer
+$customWritePanelId = null])</code>
+		</p>
+	
+		<p class="linenumber">[line 126]</p>
+		<p align="center"><strong>Delete a write panel without deleting its modules
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWritePanelId</b> 
+		- 
+		write panel id</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodExport"></a>
+	<p></p>
+	<h3>Export</h3>
+	<div class="indent">
+		<p>
+		<code>void Export(
+integer
+$panelID, string
+$exportedFilename)</code>
+		</p>
+	
+		<p class="linenumber">[line 516]</p>
+		<p align="center"><strong>Export a write panel to file
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$panelID</b> 
+		- 
+		</li>
+			<li>
+		<span class="type">string</span>
+		<b>$exportedFilename</b> 
+		- 
+		the full path of the file to which the panel will be exported</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGet"></a>
+	<p></p>
+	<h3>Get</h3>
+	<div class="indent">
+		<p>
+		<code>an Get(
+unknown_type
+$customWritePanelId)</code>
+		</p>
+	
+		<p class="linenumber">[line 165]</p>
+		<p align="center"><strong>Get the properties of a write panel
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - object containing the properties of the write panel which are              id, name, description, display_order, capability_name, type</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$customWritePanelId</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetAssignedCategories"></a>
+	<p></p>
+	<h3>GetAssignedCategories</h3>
+	<div class="indent">
+		<p>
+		<code>array GetAssignedCategories(
+integer
+$customWritePanelId)</code>
+		</p>
+	
+		<p class="linenumber">[line 201]</p>
+		<p align="center"><strong>Get a list of categories assigned to a write panel
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - of objects, each object contains cat_id and cat_name</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWritePanelId</b> 
+		- 
+		write panel id</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetAssignedCategoryIds"></a>
+	<p></p>
+	<h3>GetAssignedCategoryIds</h3>
+	<div class="indent">
+		<p>
+		<code>array GetAssignedCategoryIds(
+integer
+$customWritePanelId)</code>
+		</p>
+	
+		<p class="linenumber">[line 183]</p>
+		<p align="center"><strong>Get a list of the ids of teh categories assigned to  a write panel
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - of ids</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWritePanelId</b> 
+		- 
+		write panel id</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetCapabilityName"></a>
+	<p></p>
+	<h3>GetCapabilityName</h3>
+	<div class="indent">
+		<p>
+		<code>string GetCapabilityName(
+string
+$customWritePanelName)</code>
+		</p>
+	
+		<p class="linenumber">[line 240]</p>
+		<p align="center"><strong>Create a capability name for a write panel given its name. This function is  copied from WP's sanitize_title_with_dashes($title) (formatting.php)
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - capability name</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">string</span>
+		<b>$customWritePanelName</b> 
+		- 
+		panel name</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetCustomGroups"></a>
+	<p></p>
+	<h3>GetCustomGroups</h3>
+	<div class="indent">
+		<p>
+		<code>array GetCustomGroups(
+
+$customWritePanelId, integer
+$customWriteModuleId)</code>
+		</p>
+	
+		<p class="linenumber">[line 428]</p>
+		<p align="center"><strong>Retrieves the groups of a module
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - of objects representing basic information of the group,                  each object contains id, name and module_id</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWriteModuleId</b> 
+		- 
+		module id</li>
+			<li>
+		<span class="type"></span>
+		<b>$customWritePanelId</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetCustomWritePanels"></a>
+	<p></p>
+	<h3>GetCustomWritePanels</h3>
+	<div class="indent">
+		<p>
+		<code>array GetCustomWritePanels(
+)</code>
+		</p>
+	
+		<p class="linenumber">[line 20]</p>
+		<p align="center"><strong>Get all Write Panels.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - of objects containing all write panels. Each object contains              id, name, description, display_order, capability_name, type, always_show</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetStandardFields"></a>
+	<p></p>
+	<h3>GetStandardFields</h3>
+	<div class="indent">
+		<p>
+		<code>array GetStandardFields(
+integer
+$customWritePanelId)</code>
+		</p>
+	
+		<p class="linenumber">[line 281]</p>
+		<p align="center"><strong>Get a list of the standard fields of a the wrie panel
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - of ids of the standard fields (see $STANDARD_FIELDS defined in RCCWP_Constant.php)</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWritePanelId</b> 
+		- 
+		panel id</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodImport"></a>
+	<p></p>
+	<h3>Import</h3>
+	<div class="indent">
+		<p>
+		<code>the Import(
+string
+$panelFilePath, [string
+$writePanelName = false])</code>
+		</p>
+	
+		<p class="linenumber">[line 450]</p>
+		<p align="center"><strong>Import a write panel given the file path.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - panel id, or false in case of error.</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">string</span>
+		<b>$panelFilePath</b> 
+		- 
+		the full path of the panel file</li>
+			<li>
+		<span class="type">string</span>
+		<b>$writePanelName</b> 
+		- 
+		the write panel name, if this value if false, the function will                              use the pnl filename as the write panel name. The default value is false</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodUpdate"></a>
+	<p></p>
+	<h3>Update</h3>
+	<div class="indent">
+		<p>
+		<code>void Update(
+integer
+$customWritePanelId, string
+$name, [string
+$description = ''], [array
+$standardFields = array()], [array
+$categories = array()], [integer
+$display_order = 1], string
+$type)</code>
+		</p>
+	
+		<p class="linenumber">[line 306]</p>
+		<p align="center"><strong>Updates the properties of a write panel
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWritePanelId</b> 
+		- 
+		panel id</li>
+			<li>
+		<span class="type">string</span>
+		<b>$name</b> 
+		- 
+		write panel name</li>
+			<li>
+		<span class="type">string</span>
+		<b>$description</b> 
+		- 
+		write panel description</li>
+			<li>
+		<span class="type">array</span>
+		<b>$standardFields</b> 
+		- 
+		a list of standard fields ids that are to be displayed in                              in the panel. Use $STANDARD_FIELDS defined in RCCWP_Constant.php</li>
+			<li>
+		<span class="type">array</span>
+		<b>$categories</b> 
+		- 
+		array of category ids that are checked by default when the user                              opens Write tab for that panel.</li>
+			<li>
+		<span class="type">integer</span>
+		<b>$display_order</b> 
+		- 
+		the order of the panel in Flutter &gt; Write Panels tab</li>
+			<li>
+		<span class="type">string</span>
+		<b>$type</b> 
+		- 
+		'post' or 'page'</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+
+	<hr>
+	<a name="class_consts"></a>
+	<h2>Class Constants</h2>
+	</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomGroup.html
===================================================================
--- /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomGroup.html (revision 21)
+++ /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomGroup.html (revision 21)
@@ -0,0 +1,323 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Docs For Class RCCWP_CustomGroup</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="../media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="../media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="../media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="../classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="../elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="../elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="../li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="../FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    		<h1>Class: RCCWP_CustomGroup</h1>
+	<p style="margin: 0px;">Source Location: /RCCWP_CustomGroup.php</p>
+	
+
+<div class="leftcol">
+	<h3><a href="#class_details">Class Overview</a> <span class="smalllinenumber">[line 11]</span></h3>
+	<div id="classTree"><pre></pre>
+</div>
+	<div class="small">
+	<p>Create/Edit/Delete groups. Groups are just a collection of fields.</p>
+		<h4>Author(s):</h4>
+	<ul>
+			</ul>
+	<h4>Version:</h4>
+	<ul>
+			</ul>
+
+	<h4>Copyright:</h4>
+	<ul>
+			</li>
+	</div>
+</div>
+
+<div class="middlecol">
+	<h3><a href="#class_vars">Variables</a></h3>
+	<ul class="small">
+			</ul>
+	<h3><a href="#class_consts">Constants</a></h3>
+	<ul class="small">
+			</ul>
+</div>
+<div class="rightcol">
+	<h3><a href="#class_methods">Methods</a></h3>
+	<ul class="small">
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodCreate">Create</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodDelete">Delete</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodGet">Get</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodGetCustomFields">GetCustomFields</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodUpdate">Update</a></li>
+			</ul>
+</div>
+
+<div id="content">
+<hr>
+	<div class="contents">
+	</div>
+
+	<div class="leftCol">
+    	<h2>Inherited Variables</h2>
+		<h2>Inherited Constants</h2>
+		</div>
+
+	<div class="rightCol">
+	<h2>Inherited Methods</h2>
+		</div>
+	<br clear="all">
+	<hr>
+
+	<a name="class_details"></a>
+	<h2>Class Details</h2>
+	<p align="center"><strong>Create/Edit/Delete groups. Groups are just a collection of fields.
+</strong></p>
+	<p class="small" style="color: #334B66;">[ <a href="#top">Top</a> ]</p>
+
+	<hr>
+	<a name="class_vars"></a>
+	<h2>Class Variables</h2>
+	
+	<hr>
+	<a name="class_methods"></a>
+	<h2>Class Methods</h2>
+	
+	<a name="methodCreate"></a>
+	<p></p>
+	<h3>Create</h3>
+	<div class="indent">
+		<p>
+		<code>the Create(
+unknown_type
+$customWritePanelId, unknown_type
+$name, unknown_type
+$duplicate, unknown_type
+$at_right)</code>
+		</p>
+	
+		<p class="linenumber">[line 23]</p>
+		<p align="center"><strong>Create a new group in a write panel
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - id of the new group</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$customWritePanelId</b> 
+		- 
+		</li>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$name</b> 
+		- 
+		group name</li>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$duplicate</b> 
+		- 
+		a boolean indicating whether the group can be duplicated</li>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$at_right</b> 
+		- 
+		a boolean indicating whether the group should be placed at right side.</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodDelete"></a>
+	<p></p>
+	<h3>Delete</h3>
+	<div class="indent">
+		<p>
+		<code>void Delete(
+[integer
+$customGroupId = null])</code>
+		</p>
+	
+		<p class="linenumber">[line 46]</p>
+		<p align="center"><strong>Delete a group given id
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customGroupId</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGet"></a>
+	<p></p>
+	<h3>Get</h3>
+	<div class="indent">
+		<p>
+		<code>an Get(
+integer
+$groupId)</code>
+		</p>
+	
+		<p class="linenumber">[line 75]</p>
+		<p align="center"><strong>Get group properties
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - object representing the group</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$groupId</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetCustomFields"></a>
+	<p></p>
+	<h3>GetCustomFields</h3>
+	<div class="indent">
+		<p>
+		<code>an GetCustomFields(
+integer
+$customGroupId)</code>
+		</p>
+	
+		<p class="linenumber">[line 92]</p>
+		<p align="center"><strong>Get a list of the custom fields of a group
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - array of objects containing information about fields. Each object contains              3 objects: properties, options and default_value</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customGroupId</b> 
+		- 
+		the group id</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodUpdate"></a>
+	<p></p>
+	<h3>Update</h3>
+	<div class="indent">
+		<p>
+		<code>void Update(
+
+$customGroupId, unknown_type
+$name, unknown_type
+$duplicate, unknown_type
+$at_right, unknown_type
+$customWritePanelId)</code>
+		</p>
+	
+		<p class="linenumber">[line 123]</p>
+		<p align="center"><strong>Update the group
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$customWritePanelId</b> 
+		- 
+		</li>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$name</b> 
+		- 
+		group name</li>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$duplicate</b> 
+		- 
+		a boolean indicating whether the group can be duplicated</li>
+			<li>
+		<span class="type">unknown_type</span>
+		<b>$at_right</b> 
+		- 
+		a boolean indicating whether the group should be placed at right side.</li>
+			<li>
+		<span class="type"></span>
+		<b>$customGroupId</b> 
+		- 
+		</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+
+	<hr>
+	<a name="class_consts"></a>
+	<h2>Class Constants</h2>
+	</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html
===================================================================
--- /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html (revision 21)
+++ /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html (revision 21)
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Docs for page RCCWP_CustomWritePanel.php</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="../media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="../media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="../media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="../classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="../elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="../elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="../li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="../FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    		<h1>Procedural File: RCCWP_CustomWritePanel.php</h1>
+	<p style="margin: 0px;">Source Location: /RCCWP_CustomWritePanel.php</p>
+	
+<br>
+<br>
+
+<div class="contents">
+<h2>Classes:</h2>
+<dl>
+<dt><a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">RCCWP_CustomWritePanel</a></dt>
+	<dd>Create/edit/delete write panels.</dd>
+</dl>
+</div>
+
+<h2>Page Details:</h2>
+<hr>
+<hr>
+<div id="global">
+</div><hr>
+<div id="define">
+</div><hr>
+<div id="function">
+</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomWriteModule.html
===================================================================
--- /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomWriteModule.html (revision 21)
+++ /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/RCCWP_CustomWriteModule.html (revision 21)
@@ -0,0 +1,369 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Docs For Class RCCWP_CustomWriteModule</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="../media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="../media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="../media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="../classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="../elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="../elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="../li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="../FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    		<h1>Class: RCCWP_CustomWriteModule</h1>
+	<p style="margin: 0px;">Source Location: /RCCWP_CustomWriteModule.php</p>
+	
+
+<div class="leftcol">
+	<h3><a href="#class_details">Class Overview</a> <span class="smalllinenumber">[line 12]</span></h3>
+	<div id="classTree"><pre></pre>
+</div>
+	<div class="small">
+	<p>Create/edit/delete modules</p>
+		<h4>Author(s):</h4>
+	<ul>
+			</ul>
+	<h4>Version:</h4>
+	<ul>
+			</ul>
+
+	<h4>Copyright:</h4>
+	<ul>
+			</li>
+	</div>
+</div>
+
+<div class="middlecol">
+	<h3><a href="#class_vars">Variables</a></h3>
+	<ul class="small">
+			</ul>
+	<h3><a href="#class_consts">Constants</a></h3>
+	<ul class="small">
+			</ul>
+</div>
+<div class="rightcol">
+	<h3><a href="#class_methods">Methods</a></h3>
+	<ul class="small">
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodCreate">Create</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodDelete">Delete</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGet">Get</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGetByName">GetByName</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGetCustomModules">GetCustomModules</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodImport">Import</a></li>
+				<li><a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodUpdate">Update</a></li>
+			</ul>
+</div>
+
+<div id="content">
+<hr>
+	<div class="contents">
+	</div>
+
+	<div class="leftCol">
+    	<h2>Inherited Variables</h2>
+		<h2>Inherited Constants</h2>
+		</div>
+
+	<div class="rightCol">
+	<h2>Inherited Methods</h2>
+		</div>
+	<br clear="all">
+	<hr>
+
+	<a name="class_details"></a>
+	<h2>Class Details</h2>
+	<p align="center"><strong>Create/edit/delete modules
+</strong></p>
+	<p class="small" style="color: #334B66;">[ <a href="#top">Top</a> ]</p>
+
+	<hr>
+	<a name="class_vars"></a>
+	<h2>Class Variables</h2>
+	
+	<hr>
+	<a name="class_methods"></a>
+	<h2>Class Methods</h2>
+	
+	<a name="methodCreate"></a>
+	<p></p>
+	<h3>Create</h3>
+	<div class="indent">
+		<p>
+		<code>the Create(
+string
+$name, string
+$description, [boolean
+$create_folders = true])</code>
+		</p>
+	
+		<p class="linenumber">[line 44]</p>
+		<p align="center"><strong>Create a new module
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - id of the module or -1 if the module name already exist</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">string</span>
+		<b>$name</b> 
+		- 
+		module name, the name will be sanitized.</li>
+			<li>
+		<span class="type">string</span>
+		<b>$description</b> 
+		- 
+		module description</li>
+			<li>
+		<span class="type">boolean</span>
+		<b>$create_folders</b> 
+		- 
+		whether to create a folder for the module containing sample template</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodDelete"></a>
+	<p></p>
+	<h3>Delete</h3>
+	<div class="indent">
+		<p>
+		<code>void Delete(
+[integer
+$customWriteModuleId = null])</code>
+		</p>
+	
+		<p class="linenumber">[line 148]</p>
+		<p align="center"><strong>Deletes a module and all its child fields as well as the module folder.
+</strong></p>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWriteModuleId</b> 
+		- 
+		module id.</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGet"></a>
+	<p></p>
+	<h3>Get</h3>
+	<div class="indent">
+		<p>
+		<code>an Get(
+integer
+$customWriteModuleId)</code>
+		</p>
+	
+		<p class="linenumber">[line 209]</p>
+		<p align="center"><strong>Retrieves the basic information of the module.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - object containing id, name and description</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWriteModuleId</b> 
+		- 
+		module id</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetByName"></a>
+	<p></p>
+	<h3>GetByName</h3>
+	<div class="indent">
+		<p>
+		<code>an GetByName(
+string
+$customWriteModuleName)</code>
+		</p>
+	
+		<p class="linenumber">[line 227]</p>
+		<p align="center"><strong>Retrieves the basic information of the module given module name.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - object containing id, name and description</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">string</span>
+		<b>$customWriteModuleName</b> 
+		- 
+		module name</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodGetCustomModules"></a>
+	<p></p>
+	<h3>GetCustomModules</h3>
+	<div class="indent">
+		<p>
+		<code>array GetCustomModules(
+)</code>
+		</p>
+	
+		<p class="linenumber">[line 20]</p>
+		<p align="center"><strong>Get all modules
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - of objects containing all modules.</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodImport"></a>
+	<p></p>
+	<h3>Import</h3>
+	<div class="indent">
+		<p>
+		<code>the Import(
+string
+$zipFilePath, [string
+$moduleName = false])</code>
+		</p>
+	
+		<p class="linenumber">[line 301]</p>
+		<p align="center"><strong>Import a module given the zip file path. Importing a module means inserting it in  the database and copying its folder to modules folder. If a module with the same  name already exists, the function will append a number to the module name. You must  have either php ZipArchive extension or unzip program installed.
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - module id, or false in case of error.</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">string</span>
+		<b>$zipFilePath</b> 
+		- 
+		the full path of the zip file</li>
+			<li>
+		<span class="type">string</span>
+		<b>$moduleName</b> 
+		- 
+		the module name, if this value if false, the function will                              use the zip filename as the module name. The default value is false</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+	<a name="methodUpdate"></a>
+	<p></p>
+	<h3>Update</h3>
+	<div class="indent">
+		<p>
+		<code>the Update(
+integer
+$customWriteModuleId, string
+$name, string
+$description)</code>
+		</p>
+	
+		<p class="linenumber">[line 248]</p>
+		<p align="center"><strong>Updates the basic information of a module
+</strong></p>
+<h4>Tags:</h4>
+<ul>
+	<li><b>return</b> - id of the module or -1 if the module name already exist</li>
+</ul>
+		
+	
+	<h4>Parameters:</h4>
+	<ul>
+			<li>
+		<span class="type">integer</span>
+		<b>$customWriteModuleId</b> 
+		- 
+		the id of the module that will be updated</li>
+			<li>
+		<span class="type">string</span>
+		<b>$name</b> 
+		- 
+		new name</li>
+			<li>
+		<span class="type">string</span>
+		<b>$description</b> 
+		- 
+		new description</li>
+		</ul>
+	</div>
+	<p class="top">[ <a href="#top">Top</a> ]</p>
+
+	<hr>
+	<a name="class_consts"></a>
+	<h2>Class Constants</h2>
+	</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html
===================================================================
--- /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html (revision 21)
+++ /afridex/plugins/Flutter/docs/FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html (revision 21)
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Docs for page RCCWP_CustomGroup.php</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="../media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="../media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="../media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="../classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="../elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="../elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="../li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="../FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="../FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="../FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="../FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    		<h1>Procedural File: RCCWP_CustomGroup.php</h1>
+	<p style="margin: 0px;">Source Location: /RCCWP_CustomGroup.php</p>
+	
+<br>
+<br>
+
+<div class="contents">
+<h2>Classes:</h2>
+<dl>
+<dt><a href="../FlutterDatabaseObjects/RCCWP_CustomGroup.html">RCCWP_CustomGroup</a></dt>
+	<dd>Create/Edit/Delete groups. Groups are just a collection of fields.</dd>
+</dl>
+</div>
+
+<h2>Page Details:</h2>
+<hr>
+<hr>
+<div id="global">
+</div><hr>
+<div id="define">
+</div><hr>
+<div id="function">
+</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:59 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/elementindex.html
===================================================================
--- /afridex/plugins/Flutter/docs/elementindex.html (revision 21)
+++ /afridex/plugins/Flutter/docs/elementindex.html (revision 21)
@@ -0,0 +1,189 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Element Index</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    		[ <a href="elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2"></div>
+		<div id="packageTitle"></div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+		</div>
+
+<div id="body">
+	    	<a name="top"></a>
+<h1>Index of All Elements</h1>
+	<a href="elementindex.html#a">a</a>
+	<a href="elementindex.html#c">c</a>
+	<a href="elementindex.html#d">d</a>
+	<a href="elementindex.html#e">e</a>
+	<a href="elementindex.html#g">g</a>
+	<a href="elementindex.html#i">i</a>
+	<a href="elementindex.html#r">r</a>
+	<a href="elementindex.html#u">u</a>
+
+	<a name="a"></a>
+	<a href="elementindex.html#top">top</a><br>
+	<div>
+		<h2>a</h2>
+		<dl class="lettercontents">
+							<dt>AssignToRole</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodAssignToRole">RCCWP_CustomWritePanel::AssignToRole()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Assign a specified write panel to a role.</dd>
+					</dl>
+	</div>
+	<a name="c"></a>
+	<a href="elementindex.html#top">top</a><br>
+	<div>
+		<h2>c</h2>
+		<dl class="lettercontents">
+							<dt>Create</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodCreate">RCCWP_CustomWritePanel::Create()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a new write panel.</dd>
+							<dt>Create</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodCreate">RCCWP_CustomWriteModule::Create()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a new module</dd>
+							<dt>Create</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodCreate">RCCWP_CustomGroup::Create()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a new group in a write panel</dd>
+							<dt>Create</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodCreate">RCCWP_CustomField::Create()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a new custom field</dd>
+					</dl>
+	</div>
+	<a name="d"></a>
+	<a href="elementindex.html#top">top</a><br>
+	<div>
+		<h2>d</h2>
+		<dl class="lettercontents">
+							<dt>Delete</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodDelete">RCCWP_CustomWritePanel::Delete()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Delete a write panel without deleting its modules</dd>
+							<dt>Delete</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodDelete">RCCWP_CustomWriteModule::Delete()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Deletes a module and all its child fields as well as the module folder.</dd>
+							<dt>Delete</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodDelete">RCCWP_CustomGroup::Delete()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Delete a group given id</dd>
+							<dt>Delete</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodDelete">RCCWP_CustomField::Delete()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Delete a field</dd>
+					</dl>
+	</div>
+	<a name="e"></a>
+	<a href="elementindex.html#top">top</a><br>
+	<div>
+		<h2>e</h2>
+		<dl class="lettercontents">
+							<dt>Export</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodExport">RCCWP_CustomWritePanel::Export()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Export a write panel to file</dd>
+					</dl>
+	</div>
+	<a name="g"></a>
+	<a href="elementindex.html#top">top</a><br>
+	<div>
+		<h2>g</h2>
+		<dl class="lettercontents">
+							<dt>Get</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGet">RCCWP_CustomField::Get()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get the field information including properties, options and default value(s)</dd>
+							<dt>Get</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGet">RCCWP_CustomWritePanel::Get()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get the properties of a write panel</dd>
+							<dt>Get</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodGet">RCCWP_CustomGroup::Get()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get group properties</dd>
+							<dt>Get</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGet">RCCWP_CustomWriteModule::Get()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the basic information of the module.</dd>
+							<dt>GetAssignedCategories</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetAssignedCategories">RCCWP_CustomWritePanel::GetAssignedCategories()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get a list of categories assigned to a write panel</dd>
+							<dt>GetAssignedCategoryIds</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetAssignedCategoryIds">RCCWP_CustomWritePanel::GetAssignedCategoryIds()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get a list of the ids of teh categories assigned to  a write panel</dd>
+							<dt>GetByName</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGetByName">RCCWP_CustomWriteModule::GetByName()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the basic information of the module given module name.</dd>
+							<dt>GetCapabilityName</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCapabilityName">RCCWP_CustomWritePanel::GetCapabilityName()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a capability name for a write panel given its name. This function is  copied from WP's sanitize_title_with_dashes($title) (formatting.php)</dd>
+							<dt>GetCustomFields</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodGetCustomFields">RCCWP_CustomGroup::GetCustomFields()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get a list of the custom fields of a group</dd>
+							<dt>GetCustomFieldTypes</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetCustomFieldTypes">RCCWP_CustomField::GetCustomFieldTypes()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrievies information about a specified type</dd>
+							<dt>GetCustomFieldValues</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetCustomFieldValues">RCCWP_CustomField::GetCustomFieldValues()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the value of a custom field for a specified post</dd>
+							<dt>GetCustomGroups</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCustomGroups">RCCWP_CustomWritePanel::GetCustomGroups()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the groups of a module</dd>
+							<dt>GetCustomModules</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGetCustomModules">RCCWP_CustomWriteModule::GetCustomModules()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get all modules</dd>
+							<dt>GetCustomWritePanels</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCustomWritePanels">RCCWP_CustomWritePanel::GetCustomWritePanels()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get all Write Panels.</dd>
+							<dt>GetFieldDuplicates</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetFieldDuplicates">RCCWP_CustomField::GetFieldDuplicates()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get number of group duplicates given field name. The function returns 1  if there are no duplicates (just he original group), 2 if there is one  duplicate and so on.</dd>
+							<dt>GetFieldGroupDuplicates</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetFieldGroupDuplicates">RCCWP_CustomField::GetFieldGroupDuplicates()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get number of group duplicates given field name. The function returns 1  if there are no duplicates (just he original group), 2 if there is one  duplicate and so on.</dd>
+							<dt>GetIDByName</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetIDByName">RCCWP_CustomField::GetIDByName()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the id of a custom field given field name for the current post.</dd>
+							<dt>GetStandardFields</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetStandardFields">RCCWP_CustomWritePanel::GetStandardFields()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get a list of the standard fields of a the wrie panel</dd>
+					</dl>
+	</div>
+	<a name="i"></a>
+	<a href="elementindex.html#top">top</a><br>
+	<div>
+		<h2>i</h2>
+		<dl class="lettercontents">
+							<dt>Import</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodImport">RCCWP_CustomWritePanel::Import()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Import a write panel given the file path.</dd>
+							<dt>Import</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodImport">RCCWP_CustomWriteModule::Import()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Import a module given the zip file path. Importing a module means inserting it in  the database and copying its folder to modules folder. If a module with the same  name already exists, the function will append a number to the module name. You must  have either php ZipArchive extension or unzip program installed.</dd>
+					</dl>
+	</div>
+	<a name="r"></a>
+	<a href="elementindex.html#top">top</a><br>
+	<div>
+		<h2>r</h2>
+		<dl class="lettercontents">
+							<dt>RCCWP_CustomField</dt>
+				<dd>in file RCCWP_CustomField.php, class <a href="FlutterDatabaseObjects/RCCWP_CustomField.html">RCCWP_CustomField</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create/Edit/Delete custom fields</dd>
+							<dt>RCCWP_CustomField.php</dt>
+				<dd>procedural page <a href="FlutterDatabaseObjects/_RCCWP_CustomField.php.html">RCCWP_CustomField.php</a></dd>
+							<dt>RCCWP_CustomGroup</dt>
+				<dd>in file RCCWP_CustomGroup.php, class <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html">RCCWP_CustomGroup</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create/Edit/Delete groups. Groups are just a collection of fields.</dd>
+							<dt>RCCWP_CustomGroup.php</dt>
+				<dd>procedural page <a href="FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">RCCWP_CustomGroup.php</a></dd>
+							<dt>RCCWP_CustomWriteModule</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, class <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">RCCWP_CustomWriteModule</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create/edit/delete modules</dd>
+							<dt>RCCWP_CustomWriteModule.php</dt>
+				<dd>procedural page <a href="FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">RCCWP_CustomWriteModule.php</a></dd>
+							<dt>RCCWP_CustomWritePanel</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, class <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">RCCWP_CustomWritePanel</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create/edit/delete write panels.</dd>
+							<dt>RCCWP_CustomWritePanel.php</dt>
+				<dd>procedural page <a href="FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">RCCWP_CustomWritePanel.php</a></dd>
+					</dl>
+	</div>
+	<a name="u"></a>
+	<a href="elementindex.html#top">top</a><br>
+	<div>
+		<h2>u</h2>
+		<dl class="lettercontents">
+							<dt>Update</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodUpdate">RCCWP_CustomWritePanel::Update()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Updates the properties of a write panel</dd>
+							<dt>Update</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodUpdate">RCCWP_CustomWriteModule::Update()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Updates the basic information of a module</dd>
+							<dt>Update</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodUpdate">RCCWP_CustomGroup::Update()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Update the group</dd>
+							<dt>Update</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodUpdate">RCCWP_CustomField::Update()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Updates the properties of a custom field.</dd>
+					</dl>
+	</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:58 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/elementindex_FlutterDatabaseObjects.html
===================================================================
--- /afridex/plugins/Flutter/docs/elementindex_FlutterDatabaseObjects.html (revision 21)
+++ /afridex/plugins/Flutter/docs/elementindex_FlutterDatabaseObjects.html (revision 21)
@@ -0,0 +1,218 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Package FlutterDatabaseObjects Element Index</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    	<a name="top"></a>
+<h1>Element index for package FlutterDatabaseObjects</h1>
+	<a href="elementindex_FlutterDatabaseObjects.html#a">a</a>
+	<a href="elementindex_FlutterDatabaseObjects.html#c">c</a>
+	<a href="elementindex_FlutterDatabaseObjects.html#d">d</a>
+	<a href="elementindex_FlutterDatabaseObjects.html#e">e</a>
+	<a href="elementindex_FlutterDatabaseObjects.html#g">g</a>
+	<a href="elementindex_FlutterDatabaseObjects.html#i">i</a>
+	<a href="elementindex_FlutterDatabaseObjects.html#r">r</a>
+	<a href="elementindex_FlutterDatabaseObjects.html#u">u</a>
+
+	<a name="a"></a>
+	<a href="elementindex_FlutterDatabaseObjects.html#top">top</a><br>
+	<div>
+		<h2>a</h2>
+		<dl class="lettercontents">
+							<dt>AssignToRole</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodAssignToRole">RCCWP_CustomWritePanel::AssignToRole()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Assign a specified write panel to a role.</dd>
+					</dl>
+	</div>
+	<a name="c"></a>
+	<a href="elementindex_FlutterDatabaseObjects.html#top">top</a><br>
+	<div>
+		<h2>c</h2>
+		<dl class="lettercontents">
+							<dt>Create</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodCreate">RCCWP_CustomWritePanel::Create()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a new write panel.</dd>
+							<dt>Create</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodCreate">RCCWP_CustomWriteModule::Create()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a new module</dd>
+							<dt>Create</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodCreate">RCCWP_CustomGroup::Create()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a new group in a write panel</dd>
+							<dt>Create</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodCreate">RCCWP_CustomField::Create()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a new custom field</dd>
+					</dl>
+	</div>
+	<a name="d"></a>
+	<a href="elementindex_FlutterDatabaseObjects.html#top">top</a><br>
+	<div>
+		<h2>d</h2>
+		<dl class="lettercontents">
+							<dt>Delete</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodDelete">RCCWP_CustomWritePanel::Delete()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Delete a write panel without deleting its modules</dd>
+							<dt>Delete</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodDelete">RCCWP_CustomWriteModule::Delete()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Deletes a module and all its child fields as well as the module folder.</dd>
+							<dt>Delete</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodDelete">RCCWP_CustomGroup::Delete()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Delete a group given id</dd>
+							<dt>Delete</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodDelete">RCCWP_CustomField::Delete()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Delete a field</dd>
+					</dl>
+	</div>
+	<a name="e"></a>
+	<a href="elementindex_FlutterDatabaseObjects.html#top">top</a><br>
+	<div>
+		<h2>e</h2>
+		<dl class="lettercontents">
+							<dt>Export</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodExport">RCCWP_CustomWritePanel::Export()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Export a write panel to file</dd>
+					</dl>
+	</div>
+	<a name="g"></a>
+	<a href="elementindex_FlutterDatabaseObjects.html#top">top</a><br>
+	<div>
+		<h2>g</h2>
+		<dl class="lettercontents">
+							<dt>Get</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGet">RCCWP_CustomField::Get()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get the field information including properties, options and default value(s)</dd>
+							<dt>Get</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGet">RCCWP_CustomWritePanel::Get()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get the properties of a write panel</dd>
+							<dt>Get</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodGet">RCCWP_CustomGroup::Get()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get group properties</dd>
+							<dt>Get</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGet">RCCWP_CustomWriteModule::Get()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the basic information of the module.</dd>
+							<dt>GetAssignedCategories</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetAssignedCategories">RCCWP_CustomWritePanel::GetAssignedCategories()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get a list of categories assigned to a write panel</dd>
+							<dt>GetAssignedCategoryIds</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetAssignedCategoryIds">RCCWP_CustomWritePanel::GetAssignedCategoryIds()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get a list of the ids of teh categories assigned to  a write panel</dd>
+							<dt>GetByName</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGetByName">RCCWP_CustomWriteModule::GetByName()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the basic information of the module given module name.</dd>
+							<dt>GetCapabilityName</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCapabilityName">RCCWP_CustomWritePanel::GetCapabilityName()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create a capability name for a write panel given its name. This function is  copied from WP's sanitize_title_with_dashes($title) (formatting.php)</dd>
+							<dt>GetCustomFields</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodGetCustomFields">RCCWP_CustomGroup::GetCustomFields()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get a list of the custom fields of a group</dd>
+							<dt>GetCustomFieldTypes</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetCustomFieldTypes">RCCWP_CustomField::GetCustomFieldTypes()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrievies information about a specified type</dd>
+							<dt>GetCustomFieldValues</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetCustomFieldValues">RCCWP_CustomField::GetCustomFieldValues()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the value of a custom field for a specified post</dd>
+							<dt>GetCustomGroups</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCustomGroups">RCCWP_CustomWritePanel::GetCustomGroups()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the groups of a module</dd>
+							<dt>GetCustomModules</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodGetCustomModules">RCCWP_CustomWriteModule::GetCustomModules()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get all modules</dd>
+							<dt>GetCustomWritePanels</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetCustomWritePanels">RCCWP_CustomWritePanel::GetCustomWritePanels()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get all Write Panels.</dd>
+							<dt>GetFieldDuplicates</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetFieldDuplicates">RCCWP_CustomField::GetFieldDuplicates()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get number of group duplicates given field name. The function returns 1  if there are no duplicates (just he original group), 2 if there is one  duplicate and so on.</dd>
+							<dt>GetFieldGroupDuplicates</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetFieldGroupDuplicates">RCCWP_CustomField::GetFieldGroupDuplicates()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get number of group duplicates given field name. The function returns 1  if there are no duplicates (just he original group), 2 if there is one  duplicate and so on.</dd>
+							<dt>GetIDByName</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodGetIDByName">RCCWP_CustomField::GetIDByName()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Retrieves the id of a custom field given field name for the current post.</dd>
+							<dt>GetStandardFields</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodGetStandardFields">RCCWP_CustomWritePanel::GetStandardFields()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Get a list of the standard fields of a the wrie panel</dd>
+					</dl>
+	</div>
+	<a name="i"></a>
+	<a href="elementindex_FlutterDatabaseObjects.html#top">top</a><br>
+	<div>
+		<h2>i</h2>
+		<dl class="lettercontents">
+							<dt>Import</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodImport">RCCWP_CustomWritePanel::Import()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Import a write panel given the file path.</dd>
+							<dt>Import</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodImport">RCCWP_CustomWriteModule::Import()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Import a module given the zip file path. Importing a module means inserting it in  the database and copying its folder to modules folder. If a module with the same  name already exists, the function will append a number to the module name. You must  have either php ZipArchive extension or unzip program installed.</dd>
+					</dl>
+	</div>
+	<a name="r"></a>
+	<a href="elementindex_FlutterDatabaseObjects.html#top">top</a><br>
+	<div>
+		<h2>r</h2>
+		<dl class="lettercontents">
+							<dt>RCCWP_CustomField</dt>
+				<dd>in file RCCWP_CustomField.php, class <a href="FlutterDatabaseObjects/RCCWP_CustomField.html">RCCWP_CustomField</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create/Edit/Delete custom fields</dd>
+							<dt>RCCWP_CustomField.php</dt>
+				<dd>procedural page <a href="FlutterDatabaseObjects/_RCCWP_CustomField.php.html">RCCWP_CustomField.php</a></dd>
+							<dt>RCCWP_CustomGroup</dt>
+				<dd>in file RCCWP_CustomGroup.php, class <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html">RCCWP_CustomGroup</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create/Edit/Delete groups. Groups are just a collection of fields.</dd>
+							<dt>RCCWP_CustomGroup.php</dt>
+				<dd>procedural page <a href="FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">RCCWP_CustomGroup.php</a></dd>
+							<dt>RCCWP_CustomWriteModule</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, class <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">RCCWP_CustomWriteModule</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create/edit/delete modules</dd>
+							<dt>RCCWP_CustomWriteModule.php</dt>
+				<dd>procedural page <a href="FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">RCCWP_CustomWriteModule.php</a></dd>
+							<dt>RCCWP_CustomWritePanel</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, class <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">RCCWP_CustomWritePanel</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Create/edit/delete write panels.</dd>
+							<dt>RCCWP_CustomWritePanel.php</dt>
+				<dd>procedural page <a href="FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">RCCWP_CustomWritePanel.php</a></dd>
+					</dl>
+	</div>
+	<a name="u"></a>
+	<a href="elementindex_FlutterDatabaseObjects.html#top">top</a><br>
+	<div>
+		<h2>u</h2>
+		<dl class="lettercontents">
+							<dt>Update</dt>
+				<dd>in file RCCWP_CustomWritePanel.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html#methodUpdate">RCCWP_CustomWritePanel::Update()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Updates the properties of a write panel</dd>
+							<dt>Update</dt>
+				<dd>in file RCCWP_CustomWriteModule.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html#methodUpdate">RCCWP_CustomWriteModule::Update()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Updates the basic information of a module</dd>
+							<dt>Update</dt>
+				<dd>in file RCCWP_CustomGroup.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html#methodUpdate">RCCWP_CustomGroup::Update()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Update the group</dd>
+							<dt>Update</dt>
+				<dd>in file RCCWP_CustomField.php, method <a href="FlutterDatabaseObjects/RCCWP_CustomField.html#methodUpdate">RCCWP_CustomField::Update()</a><br>&nbsp;&nbsp;&nbsp;&nbsp;Updates the properties of a custom field.</dd>
+					</dl>
+	</div>
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:58 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/index.html
===================================================================
--- /afridex/plugins/Flutter/docs/index.html (revision 21)
+++ /afridex/plugins/Flutter/docs/index.html (revision 21)
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Flutter API Documentation</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    		[ <a href="elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">default</div>
+		<div id="packageTitle">default</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+							</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+							</div>
+		</div>
+	</div>
+
+<div id="body">
+	    	<div align="center"><h1>Flutter API Documentation</h1></div>
+<b>Welcome to default!</b><br />
+<br />
+This documentation was generated by <a href="http://www.phpdoc.org">phpDocumentor v1.4.2</a><br />
+	<div id="credit">
+		<hr>
+		Documentation generated on Wed, 30 Jul 2008 12:13:19 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/docs/li_FlutterDatabaseObjects.html
===================================================================
--- /afridex/plugins/Flutter/docs/li_FlutterDatabaseObjects.html (revision 21)
+++ /afridex/plugins/Flutter/docs/li_FlutterDatabaseObjects.html (revision 21)
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+	<title>Flutter API Documentation</title>
+	<link rel="stylesheet" type="text/css" id="layout" href="media/layout.css" media="screen">
+	<link rel="stylesheet" type="text/css" href="media/style.css" media="all">
+	<link rel="stylesheet" type="text/css" href="media/print.css" media="print">
+</head>
+
+<body>
+<div id="header">
+	<div id="navLinks">
+        
+                                    
+                            	        [ <a href="classtrees_FlutterDatabaseObjects.html">Class Tree: FlutterDatabaseObjects</a> ]
+            [ <a href="elementindex_FlutterDatabaseObjects.html">Index: FlutterDatabaseObjects</a> ]
+        		[ <a href="elementindex.html">All elements</a> ]		
+	</div>
+	<div id="packagePosition">
+		<div id="packageTitle2">FlutterDatabaseObjects</div>
+		<div id="packageTitle">FlutterDatabaseObjects</div>
+		<div id="elementPath"> &middot; </div>
+	</div>
+</div>
+
+<div id="nav" class="small">
+	<div id="packages">
+		Packages:
+					<p><a href="li_FlutterDatabaseObjects.html">FlutterDatabaseObjects</a></p>
+			</div>
+
+				<div id="index">
+			<div id="files">
+								Files:<br>
+								<a href="FlutterDatabaseObjects/_RCCWP_CustomField.php.html">		RCCWP_CustomField.php
+		</a><br>
+			<a href="FlutterDatabaseObjects/_RCCWP_CustomGroup.php.html">		RCCWP_CustomGroup.php
+		</a><br>
+			<a href="FlutterDatabaseObjects/_RCCWP_CustomWriteModule.php.html">		RCCWP_CustomWriteModule.php
+		</a><br>
+			<a href="FlutterDatabaseObjects/_RCCWP_CustomWritePanel.php.html">		RCCWP_CustomWritePanel.php
+		</a><br>
+				</div>
+			<div id="interfaces">
+							</div>
+			<div id="classes">
+				Classes:<br>
+								<a href="FlutterDatabaseObjects/RCCWP_CustomField.html">		RCCWP_CustomField
+		</a><br>
+			<a href="FlutterDatabaseObjects/RCCWP_CustomGroup.html">		RCCWP_CustomGroup
+		</a><br>
+			<a href="FlutterDatabaseObjects/RCCWP_CustomWriteModule.html">		RCCWP_CustomWriteModule
+		</a><br>
+			<a href="FlutterDatabaseObjects/RCCWP_CustomWritePanel.html">		RCCWP_CustomWritePanel
+		</a><br>
+				</div>
+		</div>
+	</div>
+
+<div id="body">
+	    	<div align="center"><h1>Flutter API Documentation</h1></div>
+<b>Welcome to FlutterDatabaseObjects!</b><br />
+<br />
+This documentation was generated by <a href="http://www.phpdoc.org">phpDocumentor v1.4.2</a><br />
+	<div id="credit">
+		<hr>
+		Documentation generated on Tue, 09 Sep 2008 12:47:58 +0000 by <a href="http://www.phpdoc.org">phpDocumentor 1.4.2</a>
+	</div>
+</div>
+</body>
+</html>
Index: /afridex/plugins/Flutter/phpthumb.bmp.php
===================================================================
--- /afridex/plugins/Flutter/phpthumb.bmp.php (revision 21)
+++ /afridex/plugins/Flutter/phpthumb.bmp.php (revision 21)
@@ -0,0 +1,874 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.bmp.php                                      //
+// module for analyzing BMP Image files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// Modified for use in phpThumb() - James Heinrich 2004.07.27  //
+//                                                             //
+/////////////////////////////////////////////////////////////////
+
+
+class phpthumb_bmp {
+
+	function phpthumb_bmp() {
+		return true;
+	}
+
+	function phpthumb_bmp2gd(&$BMPdata, $truecolor=true) {
+		$ThisFileInfo = array();
+		if ($this->getid3_bmp($BMPdata, $ThisFileInfo, true, true)) {
+			$gd = $this->PlotPixelsGD($ThisFileInfo['bmp'], $truecolor);
+			return $gd;
+		}
+		return false;
+	}
+
+	function phpthumb_bmpfile2gd($filename, $truecolor=true) {
+		if ($fp = @fopen($filename, 'rb')) {
+			$BMPdata = fread($fp, filesize($filename));
+			fclose($fp);
+			return $this->phpthumb_bmp2gd($BMPdata, $truecolor);
+		}
+		return false;
+	}
+
+	function GD2BMPstring(&$gd_image) {
+		$imageX = ImageSX($gd_image);
+		$imageY = ImageSY($gd_image);
+
+		$BMP = '';
+		for ($y = ($imageY - 1); $y >= 0; $y--) {
+			$thisline = '';
+			for ($x = 0; $x < $imageX; $x++) {
+				$argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y);
+				$thisline .= chr($argb['blue']).chr($argb['green']).chr($argb['red']);
+			}
+			while (strlen($thisline) % 4) {
+				$thisline .= "\x00";
+			}
+			$BMP .= $thisline;
+		}
+
+		$bmpSize = strlen($BMP) + 14 + 40;
+		// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
+		$BITMAPFILEHEADER  = 'BM';                                                           // WORD    bfType;
+		$BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String($bmpSize, 4); // DWORD   bfSize;
+		$BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String(       0, 2); // WORD    bfReserved1;
+		$BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String(       0, 2); // WORD    bfReserved2;
+		$BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String(      54, 4); // DWORD   bfOffBits;
+
+		// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
+		$BITMAPINFOHEADER  = phpthumb_functions::LittleEndian2String(      40, 4); // DWORD  biSize;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( $imageX, 4); // LONG   biWidth;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( $imageY, 4); // LONG   biHeight;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       1, 2); // WORD   biPlanes;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(      24, 2); // WORD   biBitCount;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       0, 4); // DWORD  biCompression;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       0, 4); // DWORD  biSizeImage;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(    2835, 4); // LONG   biXPelsPerMeter;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(    2835, 4); // LONG   biYPelsPerMeter;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       0, 4); // DWORD  biClrUsed;
+		$BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String(       0, 4); // DWORD  biClrImportant;
+
+		return $BITMAPFILEHEADER.$BITMAPINFOHEADER.$BMP;
+	}
+
+	function getid3_bmp(&$BMPdata, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) {
+
+	    // shortcuts
+	    $ThisFileInfo['bmp']['header']['raw'] = array();
+	    $thisfile_bmp                         = &$ThisFileInfo['bmp'];
+	    $thisfile_bmp_header                  = &$thisfile_bmp['header'];
+	    $thisfile_bmp_header_raw              = &$thisfile_bmp_header['raw'];
+
+		// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
+		// all versions
+		// WORD    bfType;
+		// DWORD   bfSize;
+		// WORD    bfReserved1;
+		// WORD    bfReserved2;
+		// DWORD   bfOffBits;
+
+		$offset = 0;
+		$overalloffset = 0;
+		$BMPheader = substr($BMPdata, $overalloffset, 14 + 40);
+		$overalloffset += (14 + 40);
+
+		$thisfile_bmp_header_raw['identifier']  = substr($BMPheader, $offset, 2);
+		$offset += 2;
+
+		if ($thisfile_bmp_header_raw['identifier'] != 'BM') {
+			$ThisFileInfo['error'][] = 'Expecting "BM" at offset '.intval(@$ThisFileInfo['avdataoffset']).', found "'.$thisfile_bmp_header_raw['identifier'].'"';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['bmp']);
+			return false;
+		}
+
+		$thisfile_bmp_header_raw['filesize']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+		$thisfile_bmp_header_raw['reserved1']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+		$offset += 2;
+		$thisfile_bmp_header_raw['reserved2']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+		$offset += 2;
+		$thisfile_bmp_header_raw['data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+		$thisfile_bmp_header_raw['header_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+
+
+		// check if the hardcoded-to-1 "planes" is at offset 22 or 26
+		$planes22 = $this->LittleEndian2Int(substr($BMPheader, 22, 2));
+		$planes26 = $this->LittleEndian2Int(substr($BMPheader, 26, 2));
+		if (($planes22 == 1) && ($planes26 != 1)) {
+			$thisfile_bmp['type_os']      = 'OS/2';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif (($planes26 == 1) && ($planes22 != 1)) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
+			$thisfile_bmp['type_os']      = 'OS/2';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 4;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 5;
+		} else {
+			$ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['bmp']);
+			return false;
+		}
+
+		$ThisFileInfo['fileformat']                  = 'bmp';
+		$ThisFileInfo['video']['dataformat']         = 'bmp';
+		$ThisFileInfo['video']['lossless']           = true;
+		$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+		if ($thisfile_bmp['type_os'] == 'OS/2') {
+
+			// OS/2-format BMP
+			// http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
+
+			// DWORD  Size;             /* Size of this structure in bytes */
+			// DWORD  Width;            /* Bitmap width in pixels */
+			// DWORD  Height;           /* Bitmap height in pixel */
+			// WORD   NumPlanes;        /* Number of bit planes (color depth) */
+			// WORD   BitsPerPixel;     /* Number of bits per pixel per plane */
+
+			$thisfile_bmp_header_raw['width']          = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['height']         = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['planes']         = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['bits_per_pixel'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+
+			$ThisFileInfo['video']['resolution_x']    = $thisfile_bmp_header_raw['width'];
+			$ThisFileInfo['video']['resolution_y']    = $thisfile_bmp_header_raw['height'];
+			$ThisFileInfo['video']['codec']           = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			$ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
+
+			if ($thisfile_bmp['type_version'] >= 2) {
+				// DWORD  Compression;      /* Bitmap compression scheme */
+				// DWORD  ImageDataSize;    /* Size of bitmap data in bytes */
+				// DWORD  XResolution;      /* X resolution of display device */
+				// DWORD  YResolution;      /* Y resolution of display device */
+				// DWORD  ColorsUsed;       /* Number of color table indices used */
+				// DWORD  ColorsImportant;  /* Number of important color indices */
+				// WORD   Units;            /* Type of units used to measure resolution */
+				// WORD   Reserved;         /* Pad structure to 4-byte boundary */
+				// WORD   Recording;        /* Recording algorithm */
+				// WORD   Rendering;        /* Halftoning algorithm used */
+				// DWORD  Size1;            /* Reserved for halftoning algorithm use */
+				// DWORD  Size2;            /* Reserved for halftoning algorithm use */
+				// DWORD  ColorEncoding;    /* Color model used in bitmap */
+				// DWORD  Identifier;       /* Reserved for application use */
+
+				$thisfile_bmp_header_raw['compression']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['bmp_data_size']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_h']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_v']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['colors_used']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_units'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['reserved1']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['recording']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['rendering']        = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['size1']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['size2']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['color_encoding']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['identifier']       = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+
+				$thisfile_bmp_header['compression']          = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
+
+				$ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			}
+
+		} elseif ($thisfile_bmp['type_os'] == 'Windows') {
+
+			// Windows-format BMP
+
+			// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
+			// all versions
+			// DWORD  biSize;
+			// LONG   biWidth;
+			// LONG   biHeight;
+			// WORD   biPlanes;
+			// WORD   biBitCount;
+			// DWORD  biCompression;
+			// DWORD  biSizeImage;
+			// LONG   biXPelsPerMeter;
+			// LONG   biYPelsPerMeter;
+			// DWORD  biClrUsed;
+			// DWORD  biClrImportant;
+
+			$thisfile_bmp_header_raw['width']            = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['height']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['planes']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['bits_per_pixel']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['compression']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['bmp_data_size']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['resolution_h']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['resolution_v']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['colors_used']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+
+			$thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
+			$ThisFileInfo['video']['resolution_x']    = $thisfile_bmp_header_raw['width'];
+			$ThisFileInfo['video']['resolution_y']    = $thisfile_bmp_header_raw['height'];
+			$ThisFileInfo['video']['codec']           = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			$ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
+
+			if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
+				// should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
+				$BMPheader .= substr($BMPdata, $overalloffset, 44);
+				$overalloffset += 44;
+
+				// BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
+				// Win95+, WinNT4.0+
+				// DWORD        bV4RedMask;
+				// DWORD        bV4GreenMask;
+				// DWORD        bV4BlueMask;
+				// DWORD        bV4AlphaMask;
+				// DWORD        bV4CSType;
+				// CIEXYZTRIPLE bV4Endpoints;
+				// DWORD        bV4GammaRed;
+				// DWORD        bV4GammaGreen;
+				// DWORD        bV4GammaBlue;
+				$thisfile_bmp_header_raw['red_mask']     = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['green_mask']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['blue_mask']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['alpha_mask']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['cs_type']      = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_red']   =                         substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_green'] =                         substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_blue']  =                         substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_red']    = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_green']  = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_blue']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+
+				$thisfile_bmp_header['ciexyz_red']   = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
+				$thisfile_bmp_header['ciexyz_green'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
+				$thisfile_bmp_header['ciexyz_blue']  = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
+			}
+
+			if ($thisfile_bmp['type_version'] >= 5) {
+				$BMPheader .= substr($BMPdata, $overalloffset, 16);
+				$overalloffset += 16;
+
+				// BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
+				// Win98+, Win2000+
+				// DWORD        bV5Intent;
+				// DWORD        bV5ProfileData;
+				// DWORD        bV5ProfileSize;
+				// DWORD        bV5Reserved;
+				$thisfile_bmp_header_raw['intent']              = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['profile_data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['profile_data_size']   = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['reserved3']           = $this->LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+			}
+
+		} else {
+
+			$ThisFileInfo['error'][] = 'Unknown BMP format in header.';
+			return false;
+
+		}
+
+		if ($ExtractPalette || $ExtractData) {
+			$PaletteEntries = 0;
+			if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
+				$PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
+			} elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
+				$PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
+			}
+			if ($PaletteEntries > 0) {
+				$BMPpalette = substr($BMPdata, $overalloffset, 4 * $PaletteEntries);
+				$overalloffset += 4 * $PaletteEntries;
+
+				$paletteoffset = 0;
+				for ($i = 0; $i < $PaletteEntries; $i++) {
+					// RGBQUAD          - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
+					// BYTE    rgbBlue;
+					// BYTE    rgbGreen;
+					// BYTE    rgbRed;
+					// BYTE    rgbReserved;
+					$blue  = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					$green = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					$red   = $this->LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
+						// no padding byte
+					} else {
+						$paletteoffset++; // padding byte
+					}
+					$thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | ($blue));
+				}
+			}
+		}
+
+		if ($ExtractData) {
+			$RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
+
+			$BMPpixelData = substr($BMPdata, $thisfile_bmp_header_raw['data_offset'], $thisfile_bmp_header_raw['height'] * $RowByteLength);
+			$overalloffset = $thisfile_bmp_header_raw['data_offset'] + ($thisfile_bmp_header_raw['height'] * $RowByteLength);
+
+			$pixeldataoffset = 0;
+			switch (@$thisfile_bmp_header_raw['compression']) {
+
+				case 0: // BI_RGB
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 1:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
+									$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+									for ($i = 7; $i >= 0; $i--) {
+										$paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+										$col++;
+									}
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 4:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
+									$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+									for ($i = 1; $i >= 0; $i--) {
+										$paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+										$col++;
+									}
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 8:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$paletteindex = ord($BMPpixelData{$pixeldataoffset++});
+									$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 24:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+									$pixeldataoffset += 3;
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 32:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+									$pixeldataoffset += 4;
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 16:
+							// ?
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 8:
+							$pixelcounter = 0;
+							while ($pixeldataoffset < strlen($BMPpixelData)) {
+								$firstbyte  = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								$secondbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								if ($firstbyte == 0) {
+
+									// escaped/absolute mode - the first byte of the pair can be set to zero to
+									// indicate an escape character that denotes the end of a line, the end of
+									// a bitmap, or a delta, depending on the value of the second byte.
+									switch ($secondbyte) {
+										case 0:
+											// end of line
+											// no need for special processing, just ignore
+											break;
+
+										case 1:
+											// end of bitmap
+											$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
+											break;
+
+										case 2:
+											// delta - The 2 bytes following the escape contain unsigned values
+											// indicating the horizontal and vertical offsets of the next pixel
+											// from the current position.
+											$colincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$rowincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
+											$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
+											$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
+											break;
+
+										default:
+											// In absolute mode, the first byte is zero and the second byte is a
+											// value in the range 03H through FFH. The second byte represents the
+											// number of bytes that follow, each of which contains the color index
+											// of a single pixel. Each run must be aligned on a word boundary.
+											for ($i = 0; $i < $secondbyte; $i++) {
+												$paletteindex = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+												$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+												$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+												$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+												$pixelcounter++;
+											}
+											while (($pixeldataoffset % 2) != 0) {
+												// Each run must be aligned on a word boundary.
+												$pixeldataoffset++;
+											}
+											break;
+									}
+
+								} else {
+
+									// encoded mode - the first byte specifies the number of consecutive pixels
+									// to be drawn using the color index contained in the second byte.
+									for ($i = 0; $i < $firstbyte; $i++) {
+										$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+										$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
+										$pixelcounter++;
+									}
+
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+
+				case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 4:
+							$pixelcounter = 0;
+							while ($pixeldataoffset < strlen($BMPpixelData)) {
+								$firstbyte  = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								$secondbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								if ($firstbyte == 0) {
+
+									// escaped/absolute mode - the first byte of the pair can be set to zero to
+									// indicate an escape character that denotes the end of a line, the end of
+									// a bitmap, or a delta, depending on the value of the second byte.
+									switch ($secondbyte) {
+										case 0:
+											// end of line
+											// no need for special processing, just ignore
+											break;
+
+										case 1:
+											// end of bitmap
+											$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
+											break;
+
+										case 2:
+											// delta - The 2 bytes following the escape contain unsigned values
+											// indicating the horizontal and vertical offsets of the next pixel
+											// from the current position.
+											$colincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$rowincrement = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
+											$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
+											$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
+											break;
+
+										default:
+											// In absolute mode, the first byte is zero. The second byte contains the number
+											// of color indexes that follow. Subsequent bytes contain color indexes in their
+											// high- and low-order 4 bits, one color index for each pixel. In absolute mode,
+											// each run must be aligned on a word boundary.
+											unset($paletteindexes);
+											for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
+												$paletteindexbyte = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+												$paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
+												$paletteindexes[] = ($paletteindexbyte & 0x0F);
+											}
+											while (($pixeldataoffset % 2) != 0) {
+												// Each run must be aligned on a word boundary.
+												$pixeldataoffset++;
+											}
+
+											foreach ($paletteindexes as $dummy => $paletteindex) {
+												$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+												$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+												$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+												$pixelcounter++;
+											}
+											break;
+									}
+
+								} else {
+
+									// encoded mode - the first byte of the pair contains the number of pixels to be
+									// drawn using the color indexes in the second byte. The second byte contains two
+									// color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
+									// The first of the pixels is drawn using the color specified by the high-order
+									// 4 bits, the second is drawn using the color in the low-order 4 bits, the third
+									// is drawn using the color in the high-order 4 bits, and so on, until all the
+									// pixels specified by the first byte have been drawn.
+									$paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
+									$paletteindexes[1] = ($secondbyte & 0x0F);
+									for ($i = 0; $i < $firstbyte; $i++) {
+										$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+										$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]];
+										$pixelcounter++;
+									}
+
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				case 3: // BI_BITFIELDS
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 16:
+						case 32:
+							$redshift   = 0;
+							$greenshift = 0;
+							$blueshift  = 0;
+							if (!$thisfile_bmp_header_raw['red_mask'] || !$thisfile_bmp_header_raw['green_mask'] || !$thisfile_bmp_header_raw['blue_mask']) {
+								$ThisFileInfo['error'][] = 'missing $thisfile_bmp_header_raw[(red|green|blue)_mask]';
+								return false;
+							}
+							while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
+								$redshift++;
+							}
+							while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
+								$greenshift++;
+							}
+							while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
+								$blueshift++;
+							}
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$pixelvalue = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
+									$pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
+
+									$red   = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask'])   >> $redshift)   / ($thisfile_bmp_header_raw['red_mask']   >> $redshift))   * 255));
+									$green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255));
+									$blue  = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask'])  >> $blueshift)  / ($thisfile_bmp_header_raw['blue_mask']  >> $blueshift))  * 255));
+									$thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue));
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				default: // unhandled compression type
+					$ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data';
+					break;
+			}
+		}
+
+		return true;
+	}
+
+	function IntColor2RGB($color) {
+		$red   = ($color & 0x00FF0000) >> 16;
+		$green = ($color & 0x0000FF00) >> 8;
+		$blue  = ($color & 0x000000FF);
+		return array($red, $green, $blue);
+	}
+
+	function PlotPixelsGD(&$BMPdata, $truecolor=true) {
+		$imagewidth  = $BMPdata['header']['raw']['width'];
+		$imageheight = $BMPdata['header']['raw']['height'];
+
+		if ($truecolor) {
+
+			$gd = @ImageCreateTrueColor($imagewidth, $imageheight);
+
+		} else {
+
+			$gd = @ImageCreate($imagewidth, $imageheight);
+			if (!empty($BMPdata['palette'])) {
+				// create GD palette from BMP palette
+				foreach ($BMPdata['palette'] as $dummy => $color) {
+					list($r, $g, $b) = $this->IntColor2RGB($color);
+					ImageColorAllocate($gd, $r, $g, $b);
+				}
+			} else {
+				// create 216-color websafe palette
+				for ($r = 0x00; $r <= 0xFF; $r += 0x33) {
+					for ($g = 0x00; $g <= 0xFF; $g += 0x33) {
+						for ($b = 0x00; $b <= 0xFF; $b += 0x33) {
+							ImageColorAllocate($gd, $r, $g, $b);
+						}
+					}
+				}
+			}
+
+		}
+		if (!is_resource($gd)) {
+			return false;
+		}
+
+		foreach ($BMPdata['data'] as $row => $colarray) {
+			@set_time_limit(30);
+			foreach ($colarray as $col => $color) {
+				list($red, $green, $blue) = $this->IntColor2RGB($color);
+				if ($truecolor) {
+					$pixelcolor = ImageColorAllocate($gd, $red, $green, $blue);
+				} else {
+					$pixelcolor = ImageColorClosest($gd, $red, $green, $blue);
+				}
+				ImageSetPixel($gd, $col, $row, $pixelcolor);
+			}
+		}
+		return $gd;
+	}
+
+	function PlotBMP(&$BMPinfo) {
+		$starttime = time();
+		if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) {
+			echo 'ERROR: no pixel data<BR>';
+			return false;
+		}
+		set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000)));
+		$im = $this->PlotPixelsGD($BMPinfo['bmp']);
+		if (headers_sent()) {
+			echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds<BR>';
+			ImageDestroy($im);
+			exit;
+		} else {
+			header('Content-Type: image/png');
+			ImagePNG($im);
+			ImageDestroy($im);
+			return true;
+		}
+		return false;
+	}
+
+	function BMPcompressionWindowsLookup($compressionid) {
+		static $BMPcompressionWindowsLookup = array(
+			0 => 'BI_RGB',
+			1 => 'BI_RLE8',
+			2 => 'BI_RLE4',
+			3 => 'BI_BITFIELDS',
+			4 => 'BI_JPEG',
+			5 => 'BI_PNG'
+		);
+		return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid');
+	}
+
+	function BMPcompressionOS2Lookup($compressionid) {
+		static $BMPcompressionOS2Lookup = array(
+			0 => 'BI_RGB',
+			1 => 'BI_RLE8',
+			2 => 'BI_RLE4',
+			3 => 'Huffman 1D',
+			4 => 'BI_RLE24',
+		);
+		return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid');
+	}
+
+
+	// from getid3.lib.php
+
+	function trunc($floatnumber) {
+		// truncates a floating-point number at the decimal point
+		// returns int (if possible, otherwise float)
+		if ($floatnumber >= 1) {
+			$truncatednumber = floor($floatnumber);
+		} elseif ($floatnumber <= -1) {
+			$truncatednumber = ceil($floatnumber);
+		} else {
+			$truncatednumber = 0;
+		}
+		if ($truncatednumber <= 1073741824) { // 2^30
+			$truncatednumber = (int) $truncatednumber;
+		}
+		return $truncatednumber;
+	}
+
+	function LittleEndian2Int($byteword) {
+		$intvalue = 0;
+		$byteword = strrev($byteword);
+		$bytewordlen = strlen($byteword);
+		for ($i = 0; $i < $bytewordlen; $i++) {
+			$intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
+		}
+		return $intvalue;
+	}
+
+	function BigEndian2Int($byteword) {
+		return $this->LittleEndian2Int(strrev($byteword));
+	}
+
+	function BigEndian2Bin($byteword) {
+		$binvalue = '';
+		$bytewordlen = strlen($byteword);
+		for ($i = 0; $i < $bytewordlen; $i++) {
+			$binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
+		}
+		return $binvalue;
+	}
+
+	function FixedPoint2_30($rawdata) {
+		$binarystring = $this->BigEndian2Bin($rawdata);
+		return $this->Bin2Dec(substr($binarystring, 0, 2)) + (float) ($this->Bin2Dec(substr($binarystring, 2, 30)) / 1073741824);
+	}
+
+	function Bin2Dec($binstring, $signed=false) {
+		$signmult = 1;
+		if ($signed) {
+			if ($binstring{0} == '1') {
+				$signmult = -1;
+			}
+			$binstring = substr($binstring, 1);
+		}
+		$decvalue = 0;
+		for ($i = 0; $i < strlen($binstring); $i++) {
+			$decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
+		}
+		return $this->CastAsInt($decvalue * $signmult);
+	}
+
+	function CastAsInt($floatnum) {
+		// convert to float if not already
+		$floatnum = (float) $floatnum;
+
+		// convert a float to type int, only if possible
+		if ($this->trunc($floatnum) == $floatnum) {
+			// it's not floating point
+			if ($floatnum <= 1073741824) { // 2^30
+				// it's within int range
+				$floatnum = (int) $floatnum;
+			}
+		}
+		return $floatnum;
+	}
+
+}
+
+?>
Index: /afridex/plugins/Flutter/RCCWP_CreateCustomGroupPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CreateCustomGroupPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CreateCustomGroupPage.php (revision 21)
@@ -0,0 +1,29 @@
+<?php
+include_once('RCCWP_CustomGroupPage.php');
+class RCCWP_CreateCustomGroupPage
+{
+	function Main()
+	{
+		?>
+
+		<div class="wrap">
+
+		<h2><?php _e('Create Custom Group'); ?></h2>
+		
+		<form action="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('finish-create-custom-group')?>" method="post" id="create-new-group-form">
+		
+		<?php RCCWP_CustomGroupPage::Content(); ?>
+		
+		<p class="submit" >
+			<a style="color:black" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('cancel-create-custom-group')?>" class="button"><?php _e('Cancel'); ?></a> 
+			<input type="submit" id="finish-create-custom-group" value="<?php _e('Finish'); ?>" />
+		</p>
+		
+		</form>
+
+		</div>
+
+		<?php
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_CustomFieldPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CustomFieldPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CustomFieldPage.php (revision 21)
@@ -0,0 +1,258 @@
+<?php
+class RCCWP_CustomFieldPage
+{
+	function Edit()
+	{
+		global $FIELD_TYPES;
+		$custom_field = RCCWP_CustomField::Get((int)$_GET['custom-field-id']);
+		$customGroupID = $custom_field->group_id;	
+		
+		if (in_array($custom_field->type, array('Image'))) $cssVlaue = $custom_field->CSS;
+		
+  		?>
+	  	
+  		<div class="wrap">
+  		<h2>Edit Custom Field - <?php echo $custom_field->description ?></h2>
+  		
+  		<br class="clear" />
+  		<?php
+		if (isset($_GET['err_msg'])) :
+			switch ($_GET['err_msg']){
+				case -1:
+				?>
+				<div class="error"><p> A field with the same name already exists in this write panel. Please choose a different name.</p></div>
+				<?php
+				}
+		endif;
+		?>
+  		
+	  	
+  		<form action="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('submit-edit-custom-field')."&custom-group-id=$customGroupID"?>" method="post" id="edit-custom-field-form"  onsubmit="return checkEmpty();">
+  		<input type="hidden" name="custom-field-id" value="<?php echo $custom_field->id?>">
+		
+		
+		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+		<tbody>
+		<tr valign="top">
+			<th scope="row">Name:</th>
+			<td><input name="custom-field-name" id="custom-field-name" size="40" type="text" value="<?php echo htmlspecialchars($custom_field->name)?>" /></td>
+		</tr>
+		<tr valign="top">
+			<th scope="row">Label:</th>
+			<td><input name="custom-field-description" id="custom-field-description" size="40" type="text" value="<?php echo htmlspecialchars($custom_field->description)?>" /></td>
+		</tr>
+
+		<tr valign="top">
+			<th scope="row">Can be duplicated:</th>
+			<td><input name="custom-field-duplicate" id="custom-field-duplicate" type="checkbox" value="1" <?php echo $custom_field->duplicate==0 ? "":"checked" ?>/></td>
+		</tr>
+		<tr valign="top">
+			<th scope="row">Order:</th>
+			<td>
+				<input name="custom-field-order" id="custom-field-order" size="2" type="text" value="<?php echo $custom_field->display_order?>" />
+			</td>	
+		</tr>
+		
+		<?php if (in_array($custom_field->type_id, 
+							array(  $FIELD_TYPES['textbox'],
+									$FIELD_TYPES['multiline_textbox'],
+									$FIELD_TYPES['dropdown_list'],
+									$FIELD_TYPES['listbox'],
+									$FIELD_TYPES['file'],
+									$FIELD_TYPES['image'],
+									$FIELD_TYPES['audio']
+							))){  ?>
+		<tr valign="top">
+			<th scope="row">Required:</th>
+			<td>
+				<select name="custom-field-required" id="custom-field-required">
+					<option value="0" <?php echo ($custom_field->required_field == 0 ? 'selected="selected"' : ''); ?> >Not Required - can be empty</option>
+					<option value="1" <?php echo ($custom_field->required_field == 1 ? 'selected="selected"' : ''); ?> >Required - can not be empty</option>
+				</select>
+			</td>	
+		</tr>
+		
+		<?php } ?>
+		
+		<?php if (in_array($custom_field->type, array('Textbox', 'Listbox'))) : ?>
+		<tr valign="top">
+			<th scope="row">Size:</th>
+			<td><input type="text" name="custom-field-size" id="custom-field-size" size="2" value="<?php echo $custom_field->properties['size']?>" /></td>
+		</tr>	
+		<?php endif; ?>
+
+		<?php if (in_array($custom_field->type, array('Multiline Textbox'))) : ?>
+		<tr valign="top">
+			<th scope="row">Height:</th>
+			<td><input type="text" name="custom-field-height" id="custom-field-height" size="2" value="<?php echo $custom_field->properties['height']?>" /></td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row">Width:</th>
+			<td><input type="text" name="custom-field-width" id="custom-field-width" size="2" value="<?php echo $custom_field->properties['width']?>" /></td>
+		</tr>	
+		<?php endif; ?>
+
+		<?php if (in_array($custom_field->type, array('Date'))) : ?>
+		<tr valign="top">
+			<th scope="row">Format:</th>
+			<td>
+				<select name="custom-field-date-format" id="custom-field-date-format">
+					<option value="m/d/Y" <?php if ($custom_field->properties['format'] == "m/d/Y" ) echo " selected ";?>>4/20/2008</option>
+					<option value="l, F d, Y" <?php if ($custom_field->properties['format'] == "l, F d, Y" ) echo " selected ";?>>Sunday, April 20, 2008</option>
+					<option value="F d, Y" <?php if ($custom_field->properties['format'] == "F d, Y" ) echo " selected ";?>>April 20, 2008</option>
+					<option value="m/d/y" <?php if ($custom_field->properties['format'] == "m/d/y" ) echo " selected ";?>>4/20/08</option>
+					<option value="Y-d-m" <?php if ($custom_field->properties['format'] == "Y-m-d" ) echo " selected ";?>>2008-04-20</option>
+					<option value="d-M-y" <?php if ($custom_field->properties['format'] == "d-M-y" ) echo " selected ";?>>20-Apr-08</option>
+					<option value="m.d.Y" <?php if ($custom_field->properties['format'] == "m.d.Y" ) echo " selected ";?>>4.20.2008</option>
+					<option value="m.d.y" <?php if ($custom_field->properties['format'] == "m.d.y" ) echo " selected ";?>>4.20.08</option>
+				</select>
+			</td>
+		</tr>	
+		<?php endif; ?>
+
+
+		<?php
+		if ($custom_field->has_options == "true") :
+			$options = implode("\n", (array)$custom_field->options)
+		?>
+		<tr valign="top">
+			<th scope="row">Options:</th>
+			<td>
+				<textarea name="custom-field-options" id="custom-field-options" rows="2" cols="38"><?php echo htmlspecialchars($options)?></textarea><br />
+				<em>Separate each option with a newline.</em>
+			</td>
+		</tr>
+		<tr valign="top">
+			<th scope="row">Default Value:</th>
+			<td>
+				<?php
+				$default_value = implode("\n", (array)$custom_field->default_value);
+				if ($custom_field->allow_multiple_values == "true") :
+				?>
+				<textarea name="custom-field-default-value" id="custom-field-default-value" rows="2" cols="38"><?php echo htmlspecialchars($default_value)?></textarea><br />
+				<em>Separate each value with a newline.</em>
+				<?php
+				else:
+				?>
+				<input type="text" name="custom-field-default-value" id="custom-field-default-value" size="25" value="<?php echo htmlspecialchars($default_value)?>" />
+				<?php
+				endif;
+				?>
+			</td>
+		</tr>
+		<?php
+		endif;
+		?>
+		
+		<tr valign="top">
+			<th scope="row">Type:</th>
+			<td>
+
+				<!-- START :: Javascript for Image/Photo' Css Class -->
+				<script type="text/javascript" language="javascript">
+					submitForm = false;
+					function fun(name)
+					{
+						if(name == "Image")
+						{
+							document.getElementById('divCSS').style.display = 'inline';
+							document.getElementById('divLbl').style.display = 'inline';
+							document.getElementById('lblHeight').style.display = 'inline';
+							document.getElementById('txtHeight').style.display = 'inline';
+							document.getElementById('lblWidth').style.display = 'inline';
+							document.getElementById('txtWidth').style.display = 'inline';
+						}
+						else
+						{
+							document.getElementById('divCSS').style.display = 'none';
+							document.getElementById('divLbl').style.display = 'none';
+							document.getElementById('lblHeight').style.display = 'none';
+							document.getElementById('txtHeight').style.display = 'none';
+							document.getElementById('lblWidth').style.display = 'none';
+							document.getElementById('txtWidth').style.display = 'none';
+						}
+					}
+					function checkEmpty()
+					{
+						if (submitForm && (document.getElementById('custom-field-name').value == "" || document.getElementById('custom-field-description').value == "")){
+							alert("Please fill in the name and the label of the field");	
+							return false;
+						}
+						return true;
+						
+					}
+				</script>
+				<!-- END :: Javascript for Image/Photo' Css Class -->
+
+				<?php
+				$field_types = RCCWP_CustomField::GetCustomFieldTypes();
+				foreach ($field_types as $field) :
+					$checked = 
+						$field->name == $custom_field->type ?
+						'checked="checked"' : '';
+				?>
+					<label><input name="custom-field-type" value="<?php echo $field->id?>" type="radio" <?php echo $checked?> onclick='fun("<?php echo $field->name?>");'/>
+					<?php echo $field->name?></label><br />
+				<?php
+				endforeach;
+				?>
+			</td>
+		</tr>
+		<!-- START :: For Image/Photo' Css -->
+		<?php
+			$isDisplay = $custom_field->type == "Image" ? 'display:inline;' : 'display:none;';
+		?>
+		<?php 
+			$size = explode("&",$custom_field->properties['params']);
+
+			if (substr($size[1],0 ,1) == "h"){
+				$h = substr($size[1], 2);
+			}
+			elseif (substr($size[1],0 ,1) == "w"){
+				$w = substr($size[1], 2);
+			}
+
+			if (substr($size[2],0 ,1) == "h"){
+				$h = substr($size[2], 2);
+			}
+			elseif (substr($size[2],0 ,1) == "w"){
+				$w = substr($size[2], 2);
+			}
+			
+			$cssVlaue = $custom_field->CSS;
+		?>
+		<tr valign="top">
+			<th scope="row"><span id="lblHeight" style="<?php echo $isDisplay;?>">Max Height:</span></th>
+			<td><span id="txtHeight" style="<?php echo $isDisplay;?>"><input type="text" name="custom-field-photo-height" id="custom-field-photo-height" size="3" value="<?php echo $h; ?>" /></span></td>
+		</tr>	
+		<tr valign="top">
+			<th scope="row"><span id="lblWidth" style="<?php echo $isDisplay;?>">Max Width:</span></th>
+			<td><span id="txtWidth" style="<?php echo $isDisplay;?>"><input type="text" name="custom-field-photo-width" id="custom-field-photo-width" size="3" value="<?php echo $w; ?>" /></span></td>
+		</tr>
+		<tr valign="top">
+			<th scope="row"><div id="divLbl" style="<?php echo $isDisplay;?>">Css Class:</div></th>
+			<td>
+				<div id="divCSS" style="<?php echo $isDisplay;?>">
+				<input name="custom-field-css" id="custom-field-css" size="40" type="text" value="<?php echo $cssVlaue?>" />
+				</div>
+			</td>
+		</tr>
+
+		<!-- END :: For Image/Photo' Css -->		
+		</tbody>
+		</table>
+		
+		<input name="flutter_action" type="hidden" value="submit-edit-custom-field" />
+  		<p class="submit" >
+  			<a style="color:black" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('cancel-create-custom-field')."&custom-group-id=$customGroupID"?>" class="button"><?php _e('Cancel'); ?></a> 
+  			<input type="submit" id="submit-edit-custom-field" value="Update" onclick="submitForm=true;" />
+  		</p>
+	  	
+  		</form>
+	  	
+  		</div>
+	  	
+  		<?php
+	}
+}
+?>
Index: /afridex/plugins/Flutter/cropper.php
===================================================================
--- /afridex/plugins/Flutter/cropper.php (revision 21)
+++ /afridex/plugins/Flutter/cropper.php (revision 21)
@@ -0,0 +1,202 @@
+<?php
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can('edit_posts')))
+	die("Athentication failed!");
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+
+	<title>
+		<?php //bloginfo('name') ?> &rsaquo; 
+		<?php //echo wp_specialchars( strip_tags( $title ) ); ?> 
+		&#8212; WordPress
+	</title>
+
+	<?php //wp_admin_css(); ?>
+    	<link href="../../../wp-admin/wp-admin.css" rel="stylesheet" type="text/css" media="all" />
+
+	<script type="text/javascript" src="js/greybox.js"></script>	
+	<script type="text/javascript" src="js/prototype.js"></script>	
+ 	<script type="text/javascript" src="js/scriptaculous/scriptaculous.js"></script>
+	<script type="text/javascript" src="js/cropper.js"></script>
+	<script type="text/javascript">
+	function MM_swapImgRestore() { //v3.0
+	  var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
+	}
+	
+	function MM_preloadImages() { //v3.0
+	  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
+		var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
+		if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
+	}
+	
+	function MM_findObj(n, d) { //v4.01
+	  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
+		d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
+	  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
+	  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
+	  if(!x && d.getElementById) x=d.getElementById(n); return x;
+	}
+	
+	function MM_swapImage() { //v3.0
+	  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
+	   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
+	}
+	//-->
+	</script>
+	<script type="text/javascript" charset="utf-8">
+		// setup the callback function
+		function onEndCrop( coords, dimensions ) {
+			$( 'x1' ).value = coords.x1;
+			$( 'y1' ).value = coords.y1;
+			$( 'x2' ).value = coords.x2;
+			$( 'y2' ).value = coords.y2;
+			$( 'width' ).value = dimensions.width;
+			$( 'height' ).value = dimensions.height;
+		}
+		
+		// basic example
+		Event.observe( 
+			window, 
+			'load', 
+			function() { 
+				new Cropper.Img( 
+					'testImage',
+					{
+						onEndCrop: onEndCrop 
+					}
+				) 
+			}
+		); 		
+		
+		
+		if( typeof(dump) != 'function' ) {
+			Debug.init(true, '/');
+			
+			function dump( msg ) {
+				Debug.raise( msg );
+			};
+		} else dump( '---------------------------------------\n' );
+	</script>
+	<script type="text/javascript" charset="utf-8">
+
+		function imageCrop()
+		{
+			if(document.getElementById('x1').value != '0')
+			{
+				x1 = document.getElementById('x1').value;
+				y1 = document.getElementById('y1').value;
+				x2 = document.getElementById('x2').value;
+				y2 = document.getElementById('y2').value;
+
+				w = document.getElementById('width').value;
+				h = document.getElementById('height').value;
+
+				sourceImage = document.getElementById('sourceImage').value;
+
+				if(sourceImage.indexOf("&sw=") != "-1")
+				{
+//					cropValue1 = document.getElementById("cropValues2").value;
+//					cropValue2 = document.getElementById("cropValues3").value;
+					cropValue3 = document.getElementById("cropValues4").value;
+					cropValue4 = document.getElementById("cropValues5").value;
+
+					x1 = parseInt(cropValue3) + parseInt(x1);
+					y1 = parseInt(cropValue4) + parseInt(y1);
+
+					sourceImage = sourceImage.substring(0, sourceImage.indexOf("&sw="));
+				}
+
+				requiredString = sourceImage+"&sw="+w+"&sh="+h+"&sx="+x1+"&sy="+y1;
+			}
+			else
+				requiredString = document.getElementById('sourceImage').value;
+				
+			window.parent.parent.document.getElementById("<?php echo $_GET["input_name"]?>").value = requiredString;
+			
+			requiredString = "<?php echo CANVASURI.'phpThumb.php?src='.CANVASURI.'files_flutter/' ?>" + requiredString; 
+
+			document.getElementById('tempSrc').value = requiredString;
+			window.parent.parent.exchangeValues(requiredString, document.getElementById('imageThumbId').value); 
+//			parent.$("img_thumb_1").src = requiredString + "&w=150&h=120";
+			window.parent.parent.$(document.getElementById('imageThumbId').value).src = requiredString + "&w=150&h=120";
+			window.parent.parent.document.getElementById(document.getElementById('imageThumbId').value).src = requiredString + "&w=150&h=120";
+			window.parent.parent.GB_hide();
+			//console.log(requiredString);
+		}
+	</script>
+	<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
+	<style type="text/css">
+		label { 
+			clear: left;
+			margin-left: 50px;
+			float: left;
+			width: 5em;
+			}
+		
+		html, body { 
+			margin: 0;
+			}
+		
+		#testWrap {
+			margin:8px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
+			}
+	</style>
+</head>
+<body onload="MM_preloadImages('./images/ajax-loader.gif')">
+	<div align="center" id="freshpostProgressBar" name="freshpostProgressBar" style="position:absolute; bottom:50%; left:30%; z-index:0; display:inline; background:#fff; border:1px solid #666;">
+		Loading...
+		<div><img src="./images/ajax-loader.gif" /></div>
+	</div>
+	<form name="frmCroper" action="" method="post">
+	<?php 
+		if(isset($_GET['sw'])) {
+			$finalSrc = $_GET['id']."&sw=".$_GET['sw']."&sh=".$_GET['sh']."&sx=".$_GET['sx']."&sy=".$_GET['sy'];
+		}
+		else {
+			$finalSrc = $_GET['id'];
+		}
+		if (isset($_GET['url']))
+			$url = $_GET['url']."&post=".$_GET['post'];
+			
+		$j = 0;
+		$tmpArr = explode('&', $finalSrc);
+		$pureImage = CANVASURI.'files_flutter/'.$tmpArr[0];
+			
+	?>
+		<div id="testWrap" style="z-index:1;">
+			<img src="<?php echo $pureImage; ?>" alt="test image" id="testImage" onload="javascript: document.getElementById('freshpostProgressBar').style.display = 'none';" />
+		</div>
+
+<?php
+
+$finalSrc = $tmpArr[0];
+foreach(explode("&", $finalSrc) as $item)
+{
+	if($j++ == 0)
+		continue;
+	$cropValueArr = explode("=",$item);
+?>
+	<input type="hidden" name="cropValues<?php echo $j; ?>" id="cropValues<?php echo $j; ?>" value="<?php echo $cropValueArr[1]; ?>" />
+<?php } ?>
+
+		<input type="hidden" name="sourceImage" id="sourceImage" value="<?php echo $finalSrc; ?>" />
+		<input type="hidden" name="url" id="url" value="<?php echo $url; ?>" />
+		<input type="hidden" name="tempSrc" id="tempSrc" />
+
+		<input type="hidden" name="imageThumbId" id="imageThumbId" value="<?php echo $_GET['imageThumbId']; ?>"/>
+
+		<p align="right" style="position:fixed; top:-10px; right:0px; z-index:1000;">
+			<input type="button" value="Crop It" onclick="javascript: imageCrop();" />
+		</p>
+		<input type="hidden" name="x1" id="x1" />
+		<input type="hidden" name="y1" id="y1" />
+		<input type="hidden" name="x2" id="x2" />
+		<input type="hidden" name="y2" id="y2" />
+		<input type="hidden" name="width" id="width" />
+		<input type="hidden" name="height" id="height" />
+	</form>
+</body>
+</html>
Index: /afridex/plugins/Flutter/canvas-import_functions.php
===================================================================
--- /afridex/plugins/Flutter/canvas-import_functions.php (revision 21)
+++ /afridex/plugins/Flutter/canvas-import_functions.php (revision 21)
@@ -0,0 +1,363 @@
+<?php
+/*
+
+___Canvas Import Functions______________________________________
+
+Imports function name and variable definitions from an XML file.
+
+________________________________________________________________
+
+*/
+
+
+function canvas_delete_block($block_id) {
+	global $wpdb, $canvas;
+	$wpdb->query("DELETE FROM ".$canvas->main." WHERE block_id = '$block_id'");
+	if($variables = $wpdb->get_results("SELECT variable_id, type FROM ".$canvas->variables." WHERE parent = '$block_id'")) {
+		foreach($variables as $variable) {
+			$wpdb->query("DELETE FROM ".$canvas->variables." WHERE parent = '$block_id'");
+			if($variable->type == 'Radio' || $variable->type == 'Image' || $variable->type == 'Select')
+				$wpdb->query("DELETE FROM ".$canvas->options." WHERE var_id = '".$variable->variable_id."'");
+		}
+	}
+}
+
+function canvas_delete_block_by_mod_ID($customWriteModuleId) {
+	global $wpdb, $canvas;
+
+	if ($blocks = $wpdb->get_results("SELECT block_id FROM ".$canvas->main." WHERE module_id = ".$customWriteModuleId)){
+		foreach($blocks as $block) 
+			canvas_delete_block($block->block_id);
+	}
+}
+
+
+
+
+function canvas_import_form() {
+?>
+<div class="lbContent">
+<h3>Import from XML
+<p>If you're using a correctly built Canvas-compatible theme, you can import layout schemes from other users.</p>
+<a id="cancel" href="javascript:void(0)" class="lbAction" rel="cancel">Cancel</a>
+</h3>
+<div>
+	<form id="canvas_export">
+		<p>Canvas can load layout and Canvas plugin data through a special XML file. To import a layout, be sure to use an XML file for the theme that you are currently working with.</p>
+		<p>Importing from XML is still experimental, and may result in loss of Canvas-related data.</p>
+		<label class="textbox">Path to the XML file on your server:<input class="text" type="text" id="path_to_xml" name="path_to_xml" value="" /></label>
+		<input type="button" id="import_canvas" name="import_canvas" value="Import" onclick="Canvas.importXml()"/>
+	</form>
+	<p id="export_message"></p>
+</div>
+</div>
+
+<?php
+}
+
+function canvas_import_layout($filename) {
+	if(!file_exists($filename)) { echo 'File does not exist.'; return; }
+	global $wpdb, $canvas, $imported_theme, $imported_blocks, $imported_variables, $id, $current_tag;
+	$imported_theme = array();
+	$imported_blocks = array();
+	canvas_reload();
+
+	if(!($xmlparser = xml_parser_create()))
+		$error = "ERROR: Cannot create parser for ".$filename;
+	xml_set_element_handler($xmlparser, "startElement", "endElement");
+	xml_set_character_data_handler($xmlparser, "canvas_import_layout_XML");
+
+	if(!($fp = fopen($filename, "r")))
+		$error = "ERROR: Cannot open ".$filename;
+	while($data = fread($fp, 4096)) {
+		$data = eregi_replace(">"."[[:space:]]+"."<","><",$data);
+        $data = eregi_replace(">"."[[:space:]]+",">",$data);
+        $data = eregi_replace("[[:space:]]+"."<","<",$data);
+		
+		if (!xml_parse($xmlparser, $data, feof($fp)))
+			$error = "XML error parsing ".$filename;
+	}
+	xml_parser_free($xmlparser);
+
+	$block_ids = $wpdb->get_results("SELECT block_id FROM ".$canvas->main." WHERE theme = '".get_option('template')."'");
+	foreach($block_ids as $block_id) {
+		canvas_delete_block($block_id->block_id);
+	}
+	canvas_install(); // clean install
+
+	foreach($imported_blocks as $key => $block) {
+		$old_block = $wpdb->get_results("SELECT block_id, zone FROM ".$canvas->main." WHERE ubi = '".$block['ubi']."'");
+		$old_block = $old_block[0];
+		//if($old_block->zone != 'shelf') $old_block = canvas_duplicate_block($old_block->block_id);
+		
+		$wpdb->query("UPDATE ".$canvas->main." SET zone = '".$block['zone']."', position = '".$block['position']."' WHERE block_id = '".$old_block->block_id."'");
+		if($imported_variables && $block['type'] == 'plugin') {
+			foreach($imported_variables[$key] as $variables) {
+				$wpdb->query("UPDATE ".$canvas->variables." SET value = '".addslashes($variables['value'])."' WHERE variable_name = '".addslashes($variables['name'])."' AND parent = '".$old_block->block_id."'");
+			}
+		}
+	}
+}
+
+function canvas_import_plugin($moduleID, $filename, $position, $modTitle, $template_page, $duplicate_id, $block_id = -1) {
+
+	global $wpdb, $canvas;
+	if(substr($filename, -13) == 'configure.xml') {
+		$plugin = canvas_get_plugin($filename, $modTitle);
+		$path = $plugin->functionPath;
+		list($plugin_id, $position) = canvas_install_plugin_data($moduleID, $plugin, $position, $template_page, $duplicate_id, $block_id);		
+		if (!empty($plugin->variables)) {
+
+			// Import or update variables
+			foreach($plugin->variables as $variable) {
+				//$name = $path.'_'.str_replace(array(' ','.'), '_', addslashes($variable['name']));
+				$name = str_replace(array(' ','.'), '_', addslashes($variable['name']));
+				$type = $variable['type'];
+				$default = addslashes($variable['default']);
+				$description = addslashes($variable['description']);
+				$options = $variable['option'];
+				canvas_install_variable_data($name, $plugin_id, $type, $default, $description, $options);
+			}
+		}
+		// Delete obselete variables
+		$blockVars = $wpdb->get_results("SELECT * FROM ".$canvas->variables." WHERE parent = '$plugin_id'");
+		foreach($blockVars as $blockVar){
+			$found = false;
+			if (!empty($plugin->variables)){
+				foreach($plugin->variables as $variable) {
+					//$name = $path.'_'.str_replace(array(' ','.'), '_', addslashes($variable['name']));
+					$name = str_replace(array(' ','.'), '_', addslashes($variable['name']));
+					if ($name == $blockVar->variable_name) $found = true;
+				}
+			}
+			if (!$found){
+				$wpdb->query("DELETE FROM ".$canvas->variables." WHERE variable_id = ". $blockVar->variable_id);
+			}
+		}
+		
+	}
+	return $plugin;
+}
+
+
+function get_template_pages($template) {
+	$result = array();
+	$filename = ABSPATH.'/wp-content/themes/'.$template.'/canvas.php';
+
+	if(file_exists($filename)) {
+		$file_data = implode('', file($filename));
+		preg_match_all("|<!-- Canvas Page:(\s*)(.*) -->|", $file_data, $pages);
+
+		if(!empty($pages[2])) {
+			foreach($pages[2] as $page) {
+				list($page_name, $id) = split('\s*\|\s*',$page);
+				$result[] = trim($id);
+			}
+		}
+	}
+
+	return $result;
+}
+
+function canvas_import_plugins($position, $dir='') {
+	global $wpdb, $canvas;
+
+	include_once "RCCWP_Application.php";
+	$customModules = RCCWP_CustomWriteModule::GetCustomModules();
+	$currDuplicates = $wpdb->get_results("SELECT * FROM ".$canvas->duplicates);
+
+	$template = get_option('template');
+	$template_pages = get_template_pages($template);
+
+
+	
+	$currModules = $wpdb->get_results("SELECT * FROM ".$canvas->main);
+	foreach($currModules as $currModule){
+
+		// Delete obselete modules
+		$found = false;
+		foreach($customModules as $customModule){
+			if ($customModule->id == $currModule->module_id) $found = true;
+		}
+		if (!$found){
+			canvas_delete_block_by_mod_ID($currModule->module_id);
+			$wpdb->query("DELETE FROM ".$canvas->duplicates." WHERE module_id = '$currModule->module_id'");
+		}
+
+		// Delete obselete duplicates
+		$found = false;
+		foreach($currDuplicates as $currDuplicate){
+			if ($currDuplicate->duplicate_id == $currModule->duplicate_id) $found = true;
+		}
+		if (!$found && $currModule->duplicate_id != 0){
+			canvas_delete_block($currModule->block_id);
+		}
+
+
+		
+	}
+
+
+
+	// Insert/Update modules in canvas table	
+	foreach($customModules as $customModule){
+	
+		foreach($template_pages as $template_page){
+	
+			$currModules = $wpdb->get_results("SELECT * FROM ".$canvas->main." WHERE module_id = '$customModule->id' AND theme = '".get_option('template')."' AND page='$template_page'"); 
+
+
+			// --- The module doesn't exist in the database, insert it for the first time
+			if (!$currModules){	
+				$configFile = $dir.$customModule->name."/configure.xml"; 
+				if (file_exists($configFile)){
+					$plugin = canvas_import_plugin($customModule->id, $configFile, $position, $customModule->name, $template_page, 0);
+					$position++;
+				}
+			}
+
+			// --- If a duplicate doesn't exist, insert it
+			$currDuplicates = $wpdb->get_results("SELECT * FROM ".$canvas->duplicates." WHERE module_id = '$customModule->id'");
+			foreach($currDuplicates as $currDuplicate){ 
+				if (!$wpdb->get_results("SELECT * FROM ".$canvas->main." WHERE module_id = '$customModule->id' AND theme = '".get_option('template')."' AND page='$template_page' AND duplicate_id = $currDuplicate->duplicate_id")){
+					$configFile = $dir.$customModule->name."/configure.xml"; 
+					if (file_exists($configFile)){
+						$plugin = canvas_import_plugin($customModule->id, $configFile, $position, $currDuplicate->duplicate_name, $template_page, $currDuplicate->duplicate_id);
+						$position++;
+					}
+				}
+			}
+
+
+			// --- Update each instance of this module
+			foreach($currModules as $currModule){
+				$configFile = $dir.$customModule->name."/configure.xml";
+
+				// Get module name
+				include_once('RCCWP_CustomWriteModule.php');
+				$customWriteModule = RCCWP_CustomWriteModule::Get($currModule->module_id);
+				$currModule->module_name = $customWriteModule->name; 
+
+				$modID = $currModule->block_id;
+				$modName = $currModule->module_name;
+				$modTemplateName = $currModule->template_name; // $wpdb->get_var("SELECT template_name FROM ".$canvas->main." WHERE module_name = '$customModule->name' AND theme = '".get_option('template')."'");
+				$modTemplateSize = $currModule->template_size; //$wpdb->get_var("SELECT template_size FROM ".$canvas->main." WHERE module_name = '$customModule->name' AND theme = '".get_option('template')."'");
+
+				// Check whether there is a custom configure.xml defined for this module
+				$overidConfig = get_module_template_folder($modName, $modTemplateName, $modTemplateSize)."configure.xml";
+				
+				if (file_exists($overidConfig))
+					$configFile = $overidConfig; 
+
+				if (file_exists($configFile)){
+					$plugin = canvas_import_plugin($customModule->id, $configFile, $position, $modName, $template_page, $currDuplicate->duplicate_id, $modID);
+					$position++;
+				}
+			}
+			
+		}
+	}
+
+	
+	return array($plugin, $position);
+}
+
+
+function canvas_install_plugin_data($moduleID, $plugin, $position, $template_page, $duplicate_id, $block_id = -1) {
+	global $wpdb, $canvas;
+	//$name = addslashes($plugin->functionName);
+	$author = $plugin->functionAuthor;
+	$uri = $plugin->functionUri;
+	$description = addslashes($plugin->functionDesc);
+	$group = $plugin->functionGroup;
+	$path = $plugin->functionPath;
+	$ubi = md5($name.$author.$uri.$description.$group.$path);
+	if ($block_id == -1){
+	//if (!$wpdb->get_var("SELECT module_id FROM ".$canvas->main." WHERE module_id = $moduleID AND theme = '".get_option('template')."' AND page='$template_page'") ) {
+		// install module for the first time
+		$wpdb->query("INSERT INTO ".$canvas->main." (module_id, type, zone, position, author, description, block_group, uri, path, theme, ubi, duplicate_id, page) VALUES ($moduleID, 'plugin','shelf','$position','$author','$description','$group','$uri','$path','".get_option('template')."','$ubi', $duplicate_id, '$template_page')");		
+    		$position++;
+		$plugin_id = mysql_insert_id($wpdb->dbh);
+    	}
+	else{
+		
+		//if ($wpdb->get_var("SELECT block_id FROM ".$canvas->main." WHERE block_id = $block_id"))
+			//If this the main module, not a duplicate, update its name
+			$wpdb->query("UPDATE ".$canvas->main.
+				" SET author = '$author', description = '$description', path ='$path'".
+				" WHERE block_id = $block_id");		
+		//..else
+		//	$wpdb->query("UPDATE ".$canvas->main.
+		//		" SET author = '$author', description = '$description', path ='$path'".
+		//		" WHERE block_id = $block_id");
+
+		$plugin_id = $wpdb->get_var("SELECT block_id FROM ".$canvas->main." WHERE block_id = $block_id");
+		$position =  $wpdb->get_var("SELECT position FROM ".$canvas->main." WHERE  block_id = $block_id");
+	}
+    	
+
+    	return array($plugin_id,$position);
+}
+
+function canvas_install_variable_data($name, $parent, $type, $default, $description, $options) {
+	global $wpdb, $canvas;
+	
+	if (!$wpdb->get_var("SELECT variable_name FROM ".$canvas->variables." WHERE variable_name = '$name' AND parent = '$parent'") ) {
+		$wpdb->query("INSERT INTO ".$canvas->variables." (variable_name,parent,type,value,default_value,description) VALUES ('$name','$parent','$type','$default','$default','$description')");
+		$var_id = $wpdb->get_var("SELECT variable_id FROM ".$canvas->variables." WHERE variable_name = '$name' AND parent = '$parent'");
+		
+		if (!empty($options)) {
+		  foreach($options as $option) {
+			$option_name = addslashes($option['name']);
+			$option_value = addslashes($option['value']);
+			$option_params = addslashes($option['params']);
+			$wpdb->query("INSERT INTO ".$canvas->options." (var_id, option_text, option_value, option_params) VALUES ('$var_id','$option_name','$option_value','$option_params')");
+          	  }
+		}
+	}
+	else{ 
+		$extraParam = '';
+		if ('__RESETDEFAULT__' == $wpdb->get_var("SELECT default_value FROM ".$canvas->variables." WHERE variable_name = '$name' AND parent = '$parent'"))
+			$extraParam = ", value = '$default' ";
+			
+		$wpdb->query("UPDATE ".$canvas->variables.
+				" SET type = '$type', default_value = '$default', description = '$description' ". $extraParam.
+				" WHERE variable_name = '$name' AND parent = '$parent'");
+		$var_id = $wpdb->get_var("SELECT variable_id FROM ".$canvas->variables." WHERE variable_name = '$name' AND parent = '$parent'");
+		$wpdb->query("DELETE FROM ".$canvas->options." WHERE var_id = '$var_id'");
+		
+		if (!empty($options)) {
+		  foreach($options as $option) {
+			$option_name = addslashes($option['name']);
+			$option_value = addslashes($option['value']);
+			$option_params = addslashes($option['params']);
+			$wpdb->query("INSERT INTO ".$canvas->options." (var_id,option_text,option_value, option_params) VALUES ('$var_id','$option_name','$option_value','$option_params')");
+          	  }
+		}
+	}
+}
+
+
+
+function canvas_restore_form($xml_path) {
+?>
+<div class="lbContent">
+<h3>Restore from XML
+<p>Attempt to restore this theme's layout to the default settings.</p>
+<a id="cancel" href="javascript:void(0)" class="lbAction" rel="cancel">Cancel</a>
+</h3>
+<div>
+	<form id="canvas_export">
+		<p>Canvas will attempt to restore your current theme's layout and plugin settings to those specified by the author in the original XML file.</p>
+		<p>Importing from XML is still experimental, and may result in loss of Canvas-related data.</p>
+		<input class="text" type="hidden" id="path_to_xml" name="path_to_xml" value="<?php echo $xml_path; ?>&restore=true" />
+		<input type="button" id="import_canvas" name="import_canvas" value="Restore" onclick="Canvas.importXml()"/>
+	</form>
+	<p id="export_message"></p>
+</div>
+</div>
+
+<?php
+}
+
+?>
Index: /afridex/plugins/Flutter/RC_Format.php
===================================================================
--- /afridex/plugins/Flutter/RC_Format.php (revision 21)
+++ /afridex/plugins/Flutter/RC_Format.php (revision 21)
@@ -0,0 +1,37 @@
+<?php
+class RC_Format
+{
+	function BoolToSql($value)
+	{
+		$sql = $value == true ? "'true'" : "'false'";
+		return $sql;
+	}
+	
+	function GetInputName($fieldName)
+	{
+		$name = 'rc_cwp_meta_' . str_replace(' ', '_', $fieldName);
+		$name = attribute_escape(str_replace('.', '$DOT$', $name));
+		return $name;
+	}
+	
+	function GetFieldName($inputName)
+	{
+		$fieldName = str_replace('rc_cwp_meta_', '', $inputName);
+		//$fieldName = str_replace('_', ' ', $fieldName);
+		$fieldName = str_replace('$DOT$', '.', $fieldName);
+		return $fieldName;
+	}
+	
+	function TextToSql($value)
+	{
+		$value = trim($value);
+		$sql = $value == '' ? 'NULL' : "'$value'";
+		return $sql;
+	}
+	
+	function TrimArrayValues(&$value, $key)
+	{
+		$value = trim($value);
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_Processor.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_Processor.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_Processor.php (revision 21)
@@ -0,0 +1,475 @@
+<?php
+class RCCWP_Processor
+{
+	function Main()
+	{
+		require_once('RC_Format.php');
+		global $CUSTOM_WRITE_PANEL;
+		
+		
+		
+		if (isset($_POST['edit-with-no-custom-write-panel']))
+		{
+			
+			wp_redirect('post.php?action=edit&post=' . $_POST['post-id'] . '&no-custom-write-panel=' . $_POST['custom-write-panel-id']);
+		}
+		else if (isset($_POST['edit-with-custom-write-panel']))
+		{
+			
+			wp_redirect('post.php?action=edit&post=' . $_POST['post-id'] . '&custom-write-panel-id=' . $_POST['custom-write-panel-id']);
+		}
+		
+		$currentAction = $_REQUEST['flutter_action'];
+		switch ($currentAction){
+			
+			// ------------ Write Panels
+			case 'finish-create-custom-write-panel':
+				include_once('RCCWP_CustomWritePanel.php');
+					
+				$customWritePanelId = RCCWP_CustomWritePanel::Create(
+					$_POST['custom-write-panel-name'],
+					$_POST['custom-write-panel-description'],
+					$_POST['custom-write-panel-standard-fields'],
+					$_POST['custom-write-panel-categories'],
+					$_POST['custom-write-panel-order']);
+
+				wp_redirect(RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('view-custom-write-panel', $customWritePanelId));
+				break;
+				
+			case 'submit-edit-custom-write-panel':
+				include_once('RCCWP_CustomWritePanel.php');
+
+				RCCWP_CustomWritePanel::Update(
+					$_POST['custom-write-panel-id'],
+					$_POST['custom-write-panel-name'],
+					$_POST['custom-write-panel-description'],
+					$_POST['custom-write-panel-standard-fields'],
+					$_POST['custom-write-panel-categories'],
+					$_POST['custom-write-panel-order']);
+				
+				RCCWP_CustomWritePanel::AssignToRole($_POST['custom-write-panel-id'], 'administrator');
+				break;
+				
+				
+			case 'export-custom-write-panel':				
+				require_once('RCCWP_CustomWritePanel.php');	
+				$panelID = $_REQUEST['custom-write-panel-id'];
+				$writePanel = RCCWP_CustomWritePanel::Get($panelID);
+				$exportedFilename = $tmpPath = sys_get_temp_dir().DIRECTORY_SEPARATOR. $writePanel->name . '.pnl';
+				
+				RCCWP_CustomWritePanel::Export($panelID, $exportedFilename);
+	
+				// send file in header
+				header('Content-type: binary');
+				header('Content-Disposition: attachment; filename="'.$writePanel->name.'.pnl"');
+				readfile($exportedFilename);
+				unlink($exportedFilename);
+				exit();	
+				break;
+				
+			case 'delete-custom-write-panel':
+				include_once('RCCWP_CustomWritePanel.php');
+				RCCWP_CustomWritePanel::Delete($_GET['custom-write-panel-id']);
+				//wp_redirect('?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php'));
+				break;
+
+				
+			// ------------ Modules
+			
+			// ------------ Groups
+			case 'finish-create-custom-group':
+				include_once('RCCWP_CustomGroup.php');
+				$customGroupId = RCCWP_CustomGroup::Create(
+						$_POST['custom-write-panel-id'], $_POST['custom-group-name'], $_POST['custom-group-duplicate'], $_POST['custom-group-at_right']);
+				break;
+				
+			case 'delete-custom-group':
+				include_once('RCCWP_CustomGroup.php');
+				$customGroup = RCCWP_CustomGroup::Get((int)$_REQUEST['custom-group-id']);
+				RCCWP_CustomGroup::Delete($_GET['custom-group-id']);
+				break;
+
+			case 'submit-edit-custom-group':				
+				include_once('RCCWP_CustomGroup.php');
+				RCCWP_CustomGroup::Update(
+					$_REQUEST['custom-group-id'],
+					$_POST['custom-group-name'],
+					$_POST['custom-group-duplicate'],
+					$_POST['custom-group-at_right']);
+				break;
+										
+			// ------------ Fields
+			case 'copy-custom-field':
+				include_once('RCCWP_CustomField.php');
+				$fieldToCopy = RCCWP_CustomField::Get($_REQUEST['custom-field-id']);
+				
+				if (RCCWP_Processor::CheckFieldName($fieldToCopy->name, $_REQUEST['custom-write-panel-id'])){
+					$newURL = RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('create-custom-field').'&custom-group-id='.$_REQUEST['custom-group-id'].'&err_msg=-1';
+					wp_redirect($newURL);
+					exit;
+				}
+								
+				RCCWP_CustomField::Create(
+					$_REQUEST['custom-group-id'],
+					$fieldToCopy->name,
+					$fieldToCopy->description,
+					$fieldToCopy->display_order,
+					$fieldToCopy->required_field,
+					$fieldToCopy->type_id,
+					$fieldToCopy->options,
+					$fieldToCopy->default_value,
+					$fieldToCopy->properties,
+					$fieldToCopy->duplicate
+					);
+				
+			case 'continue-create-custom-field':
+				if (RCCWP_Processor::CheckFieldName($_POST['custom-field-name'], $_REQUEST['custom-write-panel-id'])){
+					$newURL = RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('create-custom-field').'&custom-group-id='.$_REQUEST['custom-group-id'].'&err_msg=-1';
+					wp_redirect($newURL);
+					exit;
+				}
+				break;
+				
+			case 'finish-create-custom-field':
+				include_once('RCCWP_CustomField.php');
+				
+				if (RCCWP_Processor::CheckFieldName($_POST['custom-field-name'], $_REQUEST['custom-write-panel-id'])){
+					$newURL = RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('create-custom-field').'&custom-group-id='.$_REQUEST['custom-group-id'].'&err_msg=-1';
+					wp_redirect($newURL);
+					exit;
+				}
+					
+				$current_field = RCCWP_CustomField::GetCustomFieldTypes((int)$_REQUEST['custom-field-type']);
+				
+				if ($current_field->has_properties)
+				{
+					$custom_field_properties = array();
+					if (in_array($current_field->name, array('Textbox', 'Listbox')))
+					{
+						$custom_field_properties['size'] = $_POST['custom-field-size'];
+					}
+					else if (in_array($current_field->name, array('Multiline Textbox')))
+					{
+						$custom_field_properties['height'] = $_POST['custom-field-height'];
+						$custom_field_properties['width'] = $_POST['custom-field-width'];
+					}
+					else if (in_array($current_field->name, array('Date')))
+					{
+						$custom_field_properties['format'] = $_POST['custom-field-date-format'];
+					}
+					else if( in_array( $current_field->name, array('Image') ) )
+					{
+						$params = '';
+						if( $_POST['custom-field-photo-height'] != '' && is_numeric( $_POST['custom-field-photo-height']) )
+						{
+							$params .= '&h=' . $_POST['custom-field-photo-height'];
+						}
+	
+						if( $_POST['custom-field-photo-width'] != '' && is_numeric( $_POST['custom-field-photo-width']) )
+						{
+							$params .= '&w=' . $_POST['custom-field-photo-width'];
+						}
+						
+						if( $_POST['custom-field-custom-params'] != '' )
+						{
+							$params .= '&' . $_POST['custom-field-custom-params'];
+						}
+	
+						if( $params )
+						{
+							$custom_field_properties['params'] = $params;
+						}
+					}
+					else if (in_array($current_field->name, array('Date')))
+					{
+						$custom_field_properties['format'] = $_POST['custom-field-date-format'];
+					}
+				}
+				
+				RCCWP_CustomField::Create(
+					$_POST['custom-group-id'],
+					$_POST['custom-field-name'],
+					$_POST['custom-field-description'],
+					$_POST['custom-field-order'],
+					$_POST['custom-field-required'],
+					$_POST['custom-field-type'],
+					$_POST['custom-field-options'],
+					$_POST['custom-field-default-value'],
+					$custom_field_properties,
+					$_POST['custom-field-duplicate']
+					);
+				break;
+				
+			case 'submit-edit-custom-field':
+				
+				include_once('RCCWP_CustomField.php');
+				
+				
+				$current_field_obj = RCCWP_CustomField::Get($_POST['custom-field-id']);
+				if ($_POST['custom-field-name']!=$current_field_obj->name && RCCWP_Processor::CheckFieldName($_POST['custom-field-name'], $_REQUEST['custom-write-panel-id'])){
+					$newURL = RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('edit-custom-field').'&custom-field-id='.$_POST['custom-field-id'].'&err_msg=-1';
+					wp_redirect($newURL);
+					exit;
+				}
+				
+				$current_field = RCCWP_CustomField::GetCustomFieldTypes((int)$_POST['custom-field-type']);
+				
+				if ($current_field->has_properties)
+				{
+					$custom_field_properties = array();
+					if (in_array($current_field->name, array('Textbox', 'Listbox')))
+					{
+						$custom_field_properties['size'] = $_POST['custom-field-size'];
+					}
+					else if (in_array($current_field->name, array('Multiline Textbox')))
+					{
+						$custom_field_properties['height'] = $_POST['custom-field-height'];
+						$custom_field_properties['width'] = $_POST['custom-field-width'];
+					}
+					else if( in_array( $current_field->name, array('Image') ) )
+					{ 
+						$params = '';
+						
+						if( $_POST['custom-field-photo-height'] != '' && is_numeric( $_POST['custom-field-photo-height']) )
+						{
+							$params = '&h=' . $_POST['custom-field-photo-height'];
+						}
+	
+						if( $_POST['custom-field-photo-width'] != '' && is_numeric( $_POST['custom-field-photo-width']) )
+						{
+							$params .= '&w=' . $_POST['custom-field-photo-width'];
+						}
+						
+						if( $_POST['custom-field-custom-params'] != '' )
+						{
+							$params .= '&' . $_POST['custom-field-custom-params'];
+						}
+	
+						if( $params )
+						{
+							$custom_field_properties['params'] = $params;
+						}
+					}
+					else if (in_array($current_field->name, array('Date')))
+					{
+						$custom_field_properties['format'] = $_POST['custom-field-date-format'];
+					}
+				}
+				
+				RCCWP_CustomField::Update(
+					$_POST['custom-field-id'],
+					$_POST['custom-field-name'],
+					$_POST['custom-field-description'],
+					$_POST['custom-field-order'],
+					$_POST['custom-field-required'],
+					$_POST['custom-field-type'],
+					$_POST['custom-field-options'],
+					$_POST['custom-field-default-value'],
+					$custom_field_properties,
+					$_POST['custom-field-duplicate']
+					);
+					
+				break;
+				
+			case 'delete-custom-field':
+				
+				include_once('RCCWP_CustomField.php');
+				
+				if(isset($_REQUEST['custom-group-id']) && !empty($_REQUEST['custom-group-id']) )
+					$customGroupId = (int)$_REQUEST['custom-group-id'];
+	
+				$customGroup = RCCWP_CustomGroup::Get($customGroupId);
+	
+				RCCWP_CustomField::Delete($_REQUEST['custom-field-id']);
+	
+				break;
+
+			default:
+								
+				if (RCCWP_Application::InWritePostPanel())
+				{
+					include_once('RCCWP_Menu.php');
+					include_once('RCCWP_WritePostPage.php');
+					
+					$CUSTOM_WRITE_PANEL = RCCWP_Post::GetCustomWritePanel();
+					
+					
+					if (isset($CUSTOM_WRITE_PANEL))
+					{
+								
+						ob_start(array('RCCWP_WritePostPage', 'ApplyCustomWritePanelAssignedCategories'));
+													
+						add_action('admin_head', array('RCCWP_WritePostPage', 'ApplyCustomWritePanelHeader'));
+						
+						// Allows fields to be added to right
+						add_action('submitpost_box', array('RCCWP_WritePostPage', 'CustomFieldCollectionInterfaceRight'), 5); 
+						add_action('submitpage_box', array('RCCWP_WritePostPage', 'CustomFieldCollectionInterfaceRight'), 5);
+						
+						// Allows fields to be added to the post edit body
+						add_action('simple_edit_form', array('RCCWP_WritePostPage', 'CustomFieldCollectionInterface'), 5);
+						add_action('edit_form_advanced', array('RCCWP_WritePostPage', 'CustomFieldCollectionInterface'), 5);
+						add_action('edit_page_form', array('RCCWP_WritePostPage', 'CustomFieldCollectionInterface'), 5);
+
+					}
+					else if (!isset($_REQUEST['no-custom-write-panel']) && isset($_REQUEST['post']))
+					{
+						include_once('RCCWP_Options.php');
+						$promptEditingPost = RCCWP_Options::Get('prompt-editing-post');
+						if ($promptEditingPost == 1)
+						{
+							wp_redirect('?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') . '&assign-custom-write-panel=' . (int)$_GET['post']);
+						}
+					}
+				}
+				
+				
+				else if (isset($_POST['finish-create-custom-write-module']))
+				{
+					include_once('RCCWP_CustomWriteModule.php');
+					$customWriteModuleId = RCCWP_CustomWriteModule::Create(
+							$_POST['custom-write-module-name'], $_POST['custom-write-module-description']);
+		
+					//RCCWP_CustomWritePanel::AssignToRole($customWritePanelId, 'administrator');
+					if ($customWriteModuleId == -1){
+						//$_POST['create-custom-write-module'] = 1;
+						$modulesURL = '?page=' . 'FlutterManageModules' . '&view-modules=1&create-custom-write-module=1&err_msg=-1';
+						wp_redirect($modulesURL);
+						
+					}
+					else
+						wp_redirect(RCCWP_ManagementPage::GetCustomWriteModuleEditUrl($customWriteModuleId));
+				}
+				
+				else if (isset($_POST['submit-edit-custom-write-module']))
+				{
+						include_once('RCCWP_CustomWriteModule.php');
+						
+						$customWriteModuleId = RCCWP_CustomWriteModule::Update(
+							$_REQUEST['custom-write-module-id'],
+							$_REQUEST['custom-write-module-name'],
+							$_REQUEST['custom-write-module-description']);
+		
+						if ($customWriteModuleId == -1){
+							$customWriteModuleId = $_REQUEST['custom-write-module-id'];
+							$modulesURL = '?page=' . 'FlutterManageModules' . "&edit-custom-write-module=1&view-custom-write-module=$customWriteModuleId&custom-write-module-id=$customWriteModuleId&err_msg=-1";
+							wp_redirect($modulesURL);
+							
+						}
+						
+							
+						//RCCWP_CustomWritePanel::AssignToRole($_POST['custom-write-panel-id'], 'administrator');
+				}
+				
+		
+				
+				
+				else if (isset($_POST['update-custom-write-panel-options']))
+				{
+					if ($_POST['uninstall-custom-write-panel'] == 'uninstall')
+					{
+						RCCWP_Application::Uninstall();
+						wp_redirect('options-general.php');
+					}
+					else
+					{
+						include_once('RCCWP_Options.php');
+						
+						$options['hide-write-post'] = $_POST['hide-write-post'];
+						$options['hide-write-page'] = $_POST['hide-write-page'];
+						$options['prompt-editing-post'] = $_POST['prompt-editing-post'];
+						$options['assign-to-role'] = $_POST['assign-to-role'];
+						$options['use-snipshot'] = $_POST['use-snipshot'];
+						$options['enable-editnplace'] = $_POST['enable-editnplace'];
+						$options['enable-swfupload'] = $_POST['enable-swfupload'] ;
+						$options['default-custom-write-panel'] = $_POST['default-custom-write-panel'];
+						$options['enable-HTMLPurifier'] = $_POST['enable-HTMLPurifier'];
+						$options['tidy-level'] = $_POST['tidy-level'];
+						$options['canvas_show_instructions'] = $_POST['canvas_show_instructions'];
+						$options['canvas_show_zone_name'] = $_POST['canvas_show_zone_name'];
+						$options['canvas_show'] = $_POST['canvas_show'];
+						$options['ink_show'] = $_POST['ink_show'];
+		
+						
+						RCCWP_Options::Update($options);
+					}
+				}
+				else if (isset($_REQUEST['create-module-duplicate']))
+				{
+					include_once('RCCWP_ModuleDuplicate.php');
+					$moduleID = $_REQUEST['custom-write-module-id'];
+					RCCWP_ModuleDuplicate::Create($moduleID);
+					wp_redirect(RCCWP_ManagementPage::GetCustomWriteModuleEditUrl($moduleID));
+				}
+				else if (isset($_POST['submit-edit-module-duplicate']))
+				{
+					include_once('RCCWP_ModuleDuplicate.php');
+					$moduleID = $_REQUEST['custom-write-module-id'];
+					RCCWP_ModuleDuplicate::Update(
+						$_REQUEST['module-duplicate-id'],
+						$_REQUEST['module-duplicate-name']);
+					wp_redirect('?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php') . '&view-custom-write-module=' . $moduleID . '&custom-write-module-id=' . $moduleID);
+				}
+				else if (isset($_REQUEST['delete-module-duplicate']))
+				{
+					include_once('RCCWP_ModuleDuplicate.php');
+					$moduleID = $_REQUEST['custom-write-module-id'];
+					RCCWP_ModuleDuplicate::Delete($_REQUEST['module-duplicate-id']);
+					wp_redirect(RCCWP_ManagementPage::GetCustomWriteModuleEditUrl($moduleID));
+				}
+				
+				else if (isset($_POST['delete-custom-write-module']))
+				{
+					include_once('RCCWP_CustomWriteModule.php');
+					$moduleID = $_REQUEST['custom-write-module-id'];
+					RCCWP_CustomWriteModule::Delete($moduleID);
+					wp_redirect('?page=' . urlencode(FLUTTER_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'RCCWP_Menu.php'). '&view-modules=1');
+				}
+		
+		}
+		
+	}
+	
+	
+	
+	function FlushAllOutputBuffer() 
+	{ 
+		
+		while (@ob_end_flush()); 
+		
+	} 
+	
+	function Redirect($location)
+	{
+		global $post_ID;
+		global $page_ID;
+
+		
+		if (!empty($_REQUEST['rc-cwp-custom-write-panel-id']))
+		{
+			if (strstr($location, 'post-new.php?posted=') || strstr($location, 'page-new.php?posted='))
+			{
+				$id = ($post_ID=="")?$page_ID:$post_ID;
+				$location = $_REQUEST['_wp_http_referer'] . '&posted=' . $id;
+			}
+		}
+		return $location;
+	}
+	
+	function CheckFieldName($fieldName, $panelID){
+		global $wpdb;
+		
+		$sql = "SELECT id, group_id FROM " . RC_CWP_TABLE_GROUP_FIELDS .
+				" WHERE name='$fieldName' ";
+		$results =$wpdb->get_results($sql);
+	
+		foreach($results as $result){
+			$fieldGroup = RCCWP_CustomGroup::Get($result->group_id);
+			if ($panelID == $fieldGroup->panel_id){
+				return true;
+			}
+		}
+		return false;
+	}
+
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_ModuleDuplicate.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_ModuleDuplicate.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_ModuleDuplicate.php (revision 21)
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * Create/edit/delete module duplicates 
+ *
+ */
+
+class RCCWP_ModuleDuplicate
+{
+	/**
+	 * Get all module duplicates
+	 *
+	 * @param integer $modulelId
+	 * @return array of objects containing module duplicates
+	 */	
+	function GetCustomModulesDuplicates($modulelId)
+	{
+		global $wpdb, $canvas;
+
+		$results = $wpdb->get_results("SELECT * FROM ".$canvas->duplicates." WHERE module_id = '$modulelId'");
+
+		return $results;
+	}
+	
+	
+	
+	/**
+	 * Create a duplicate. 
+	 *
+	 * @param integer $customWriteModuleId
+	 * @param string $duplicate_name the name of the duplicate, if false,
+	 *           the name "[MODULE NAME] copy [x] will be given to the duplicate. 
+	 */ 
+	function Create($customWriteModuleId, $duplicate_name=false)
+	{
+		global $wpdb, $canvas;
+		
+		// Get module name
+		$customModule = RCCWP_CustomWriteModule::Get($customWriteModuleId);
+		
+		if (!$duplicate_name){
+			// go ahead and rename, then duplicate
+			$duplicate_name = $customModule->name;	
+			
+			if($other_blocks = $wpdb->get_results("SELECT duplicate_name FROM ".$canvas->duplicates." WHERE duplicate_name LIKE '".preg_replace('/\scopy\s[0-9]*/', '', $duplicate_name)." %' ORDER BY duplicate_id DESC")) {
+				$duplicate_name = $other_blocks[0]->duplicate_name;
+				$testcase = substr($duplicate_name, -1, 1);
+				$duplicate_name[strlen($duplicate_name) - 1] = intval($testcase) + 1;
+			}
+			else
+				$duplicate_name .= ' copy 2';
+		}
+				
+		$wpdb->query("INSERT INTO ".$canvas->duplicates." (module_id, duplicate_name) VALUES ($customWriteModuleId, '$duplicate_name')");
+
+		return $wpdb->insert_id;
+	}
+	
+	/**
+	 * Delete duplicate
+	 *
+	 * @param integer $duplicateId
+	 */
+	function Delete($duplicateId)
+	{
+		global $wpdb, $canvas;
+			
+		$sql = sprintf(
+			"DELETE FROM " . $canvas->duplicates .
+			" WHERE duplicate_id = %d",
+			$duplicateId
+			);
+		$wpdb->query($sql);
+	}
+	
+	/**
+	 * Get duplicate
+	 *
+	 * @param integer $duplicateId
+	 * @return an object containing the duplicate properties
+	 */
+	function Get($duplicateId){
+		global $wpdb, $canvas;
+
+		$results = $wpdb->get_row("SELECT * FROM ".$canvas->duplicates." WHERE duplicate_id = $duplicateId");
+		return $results;
+	}
+
+	/**
+	 * Update duplicate
+	 *
+	 * @param integer $duplicateId
+	 * @param string $name new duplicate name
+	 */
+	function Update($duplicateId, $name){
+		global $wpdb, $canvas;
+		
+		$sql = sprintf(
+			"UPDATE " . $canvas->duplicates .
+			" SET duplicate_name = '%s'".
+			" where duplicate_id = %d",
+			$name,
+			$duplicateId);
+		
+		$wpdb->query($sql);
+	}
+
+}
+?>
Index: /afridex/plugins/Flutter/FlutterDatabaseObjects.pkg
===================================================================
--- /afridex/plugins/Flutter/FlutterDatabaseObjects.pkg (revision 21)
+++ /afridex/plugins/Flutter/FlutterDatabaseObjects.pkg (revision 21)
@@ -0,0 +1,43 @@
+   1.
+      <refentry id="{@id}">
+   2.
+       <refnamediv>
+   3.
+        <refname>User Guide for My Application</refname>
+   4.
+        <refpurpose>To demonstrate the various tasks available in My app</refpurpose>
+   5.
+       </refnamediv>
+   6.
+       <refsynopsisdiv>
+   7.
+        <author>
+   8.
+         My Name
+   9.
+         <authorblurb>
+  10.
+          {@link mailto:myself@myhost.net My Name}
+  11.
+         </authorblurb>
+  12.
+        </author>
+  13.
+       </refsynopsisdiv>
+  14.
+       {@toc}
+  15.
+       <refsect1 id="{@id intro}">
+  16.
+        <title>User Guide</title>
+  17.
+        <para>
+  18.
+         This will be the User Guide for My app. 
+  19.
+        </para>
+  20.
+       </refsect1>
+  21.
+      </refentry>
+ 
Index: /afridex/plugins/Flutter/releasenotes.txt
===================================================================
--- /afridex/plugins/Flutter/releasenotes.txt (revision 21)
+++ /afridex/plugins/Flutter/releasenotes.txt (revision 21)
@@ -0,0 +1,113 @@
+CHANGELOG and HISTORY for Flutter
+_________________________________
+
+
+= KEY ===================
+    # Breaks back-compat
+    ! Feature
+    - Bugfix
+      + Sub-comment
+    . Internal change
+==========================
+
+0.2.26, released 2008-09-15
+- Fixed role management bugs.
+
+0.2.25, released 2008-09-14
+! Applied EIP for text fields and text areas.
+
+0.2.24, released 2008-09-09
+- After creating a panel, the user is redirected to the panel page instead of panels page.
+- Fixed the fields list of Write Panels of type Page.
+
+0.2.23, released 2008-09-07
+! Fields and groups are transfered from Modules to Write Panels.
+! Groups can be added at the right side of the post write menu.
+! If you are creating a new field and the name of the field already exist, you will be able to copy to the current panel. 
+
+0.2.22, released 2008-08-27
+! Create an option in Flutter settings that allows the user to switch between flash and browser upload.
+- Saving a post in a panel with no fields raises errors.
+- flutter was over-ridding the main rich text editor settings.
+- Saving a post without changing date field doesnât save the date, it should save todayâs date (default date).
+
+
+0.2.21, released 2008-08-21
+! Enhancements to Edit-n-Place including proper rendering on any theme.
+! Supporting flash-based upload.
+- Saving a post in a panel with no fields raises errors.
+
+0.2.20, released 200die8-08-14
+! The ability to duplicate fields and groups.
+! Passing module name as parameter to get function.
+- The image thumbnail/edit button show up on the right side instead of left.
+
+0.2.19, released 2008-08-7
+- Retrieving Checkbox-list and listboxes as array in get() function.
+
+0.2.18, released 2008-08-6
+! The ability to mark a field as Required.
+! Automatically update image thumbnail when it is uploaded.
+! Upload files once it is selected.
+- A bug in SQL statement in Main.php.
+- Adding apostrophes in fields options list.
+
+0.2.17, released 2008-07-29
+! A complete Flutter API with PHPdoc is generated in /docs/.
+
+0.2.16, released 2008-07-28
+! Checks whether folder permissions are set properly.
+! Display messages if the user has no modules.
+! Use php zip library to unzip modules if possible
+! Integrate more closely with the Role Manager plugin by creating roles for Flutter functionalities.
+- Removing '<?=' from RCCWP_CustomWriteModulePage.php
+- Fixing bugs in Layout tab on IE6
+
+0.2.15, released 2008-07-21
+- Fixed a bug in changing module template in Module Settings overlay.
+
+0.2.14, released 2008-07-20
+- Fixed a bug on installing Flutter on php4 which says: "Fatal error: Cannot redeclare add_canvas_pages() "
+
+0.2.13, released 2008-07-16
+- Tested on wordpress 2.6.
+- Upgrading Flutter tables when a new Flutter version is installed.
+- Cleaning canvas obselete code.
+
+0.2.12, released 2008-07-15
+- Hide the "Style" tab by default
+! Use "echo" for printing from PHP to avoid php backward incompatability.
+
+0.2.11, released 2008-07-13
+- Default template is not set automatically in Layout tab.
+
+0.2.10, released 2008-07-10
+! Styling Flutter admin pages.
+
+0.2.9, released 2008-06-25
+! The ability to import modules automatically by copying them into 'default_modules' folder.
+
+0.2.8, released 2008-06-19
+- Upload form was not displayed properly with wp-post-rating plugin.
+
+0.2.7, released 2008-06-16
+! A new function named get_module to get the module directly without the need define it Layout.
+! A new option is added to Write Panel that always shows the write panel in management menu.
+
+0.2.6, released 2008-06-14
+! Hide Canvas and Hide Ink options in the options page.
+! Add a "reset modules" button.
+! Module description is transfered from configure.xml to the modules page.
+- Populate template sizes combo-box based on selected item in template names in module settings.
+- Tags area doesn't dissapear in write panels for WPMU even though you uncheck it.
+- If there are certain zones that have modules assigned to them but are no longer present in the canvas.php file, the modules should go back to the shelf.
+
+0.2.5, released 2008-06-10
+! Added a new variable type named 'fileupload' to configure.xml that allows uploading files .
+! The audio player is bundled with Flutter.
+- Solved two bugs in date field, adding multiple dates and editing date format.
+
+0.2.3, released 2008-06-07
+! New improvements to Canvas admin interface.
+! HTML Purifier is integrated into Flutter.
+- Adding underscore to field name makes the field value unaccessible.
Index: /afridex/plugins/Flutter/canvas-plugin_info.php
===================================================================
--- /afridex/plugins/Flutter/canvas-plugin_info.php (revision 21)
+++ /afridex/plugins/Flutter/canvas-plugin_info.php (revision 21)
@@ -0,0 +1,71 @@
+<?php
+
+/*
+
+___Canvas Plugin Form___________________________________________
+
+Outputs the Lightbox form for plugins, building individual
+form elements based upon the plugin definitions.
+
+________________________________________________________________
+
+*/
+
+require( dirname(__FILE__) . '/../../../wp-config.php' );
+if (!(is_user_logged_in() && current_user_can('edit_posts')))
+	die("Athentication failed!");
+
+
+global $wpdb, $canvas;
+
+$block_id = $_GET['block_id'];
+
+?>
+
+<div class="lbContent">
+
+	<?php
+		global $wpdb, $canvas;
+		$block = $wpdb->get_row("SELECT * FROM ".$canvas->main." WHERE block_id = '$block_id'");
+
+		if($block->uri != '') $link = '<a href="'.$block->uri.'">'.$block->author.'</a>';
+			else $link = $block->author;
+
+		// Get module name
+		include_once('RCCWP_CustomWriteModule.php');
+		$customWriteModule = RCCWP_CustomWriteModule::Get($block->module_id);
+		if ($block->duplicate_id ==0 )
+			$block->module_title = $customWriteModule->name;
+		else
+			$block->module_title = $wpdb->get_var("SELECT duplicate_name FROM ".$canvas->duplicates." WHERE duplicate_id = ".$block->duplicate_id);
+
+		//echo '<h3>'.$block->module_title.'</span> by '.$link.'.</h3><p>'.wptexturize($block->description).'</p>';
+
+		$content = '<h3>'.$block->module_title.'';
+		if($block->author != '' && $block->uri != '')
+			$content .= '<span>by <a target="new" href="'.$block->uri.'">'.$block->author.'</a></span>';
+		if($block->description != '')
+			$content .= '<p>'.$block->description.'</p>';
+
+		echo $content;
+		
+		
+	?>
+
+	<a id="cancel" href="javascript:void(0)" class="lbAction" rel="cancel">Close</a></h3><div class="lbContent_inside">
+
+<form id="lightbox_form">
+	<?php	
+
+		echo ''.
+				'<strong>Module Folder:</strong> modules/'.$customWriteModule->name.
+				'<br/><br/><strong>Module ID:</strong> '.$block->block_id.
+				'<br/><br/><strong>Zone:</strong> '.$block->zone.
+				'<br/><br/><strong>Position:</strong> '.$block->position.
+				'<br/><br/><strong>Author:</strong> '.$block->author.''.
+				'<br/><br/><strong>Author URI:</strong> '.$block->uri.'';
+		
+	?>
+</form>
+</div>
+</div>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.php (revision 21)
@@ -0,0 +1,234 @@
+<?php
+
+/*! @mainpage
+ * 
+ * HTML Purifier is an HTML filter that will take an arbitrary snippet of
+ * HTML and rigorously test, validate and filter it into a version that
+ * is safe for output onto webpages. It achieves this by:
+ * 
+ *  -# Lexing (parsing into tokens) the document,
+ *  -# Executing various strategies on the tokens:
+ *      -# Removing all elements not in the whitelist,
+ *      -# Making the tokens well-formed,
+ *      -# Fixing the nesting of the nodes, and
+ *      -# Validating attributes of the nodes; and
+ *  -# Generating HTML from the purified tokens.
+ * 
+ * However, most users will only need to interface with the HTMLPurifier
+ * and HTMLPurifier_Config.
+ */
+
+/*
+    HTML Purifier 3.1.0 - Standards Compliant HTML Filtering
+    Copyright (C) 2006-2008 Edward Z. Yang
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * Facade that coordinates HTML Purifier's subsystems in order to purify HTML.
+ * 
+ * @note There are several points in which configuration can be specified 
+ *       for HTML Purifier.  The precedence of these (from lowest to
+ *       highest) is as follows:
+ *          -# Instance: new HTMLPurifier($config)
+ *          -# Invocation: purify($html, $config)
+ *       These configurations are entirely independent of each other and
+ *       are *not* merged (this behavior may change in the future).
+ * 
+ * @todo We need an easier way to inject strategies using the configuration
+ *       object.
+ */
+class HTMLPurifier
+{
+    
+    /** Version of HTML Purifier */
+    public $version = '3.1.0';
+    
+    /** Constant with version of HTML Purifier */
+    const VERSION = '3.1.0';
+    
+    /** Global configuration object */
+    public $config;
+    
+    /** Array of extra HTMLPurifier_Filter objects to run on HTML, for backwards compatibility */
+    private $filters = array();
+    
+    /** Single instance of HTML Purifier */
+    private static $instance;
+    
+    protected $strategy, $generator;
+    
+    /**
+     * Resultant HTMLPurifier_Context of last run purification. Is an array
+     * of contexts if the last called method was purifyArray().
+     */
+    public $context;
+    
+    /**
+     * Initializes the purifier.
+     * @param $config Optional HTMLPurifier_Config object for all instances of
+     *                the purifier, if omitted, a default configuration is
+     *                supplied (which can be overridden on a per-use basis).
+     *                The parameter can also be any type that
+     *                HTMLPurifier_Config::create() supports.
+     */
+    public function __construct($config = null) {
+        
+        $this->config = HTMLPurifier_Config::create($config);
+        
+        $this->strategy     = new HTMLPurifier_Strategy_Core();
+        
+    }
+    
+    /**
+     * Adds a filter to process the output. First come first serve
+     * @param $filter HTMLPurifier_Filter object
+     */
+    public function addFilter($filter) {
+        trigger_error('HTMLPurifier->addFilter() is deprecated, use configuration directives in the Filter namespace or Filter.Custom', E_USER_WARNING);
+        $this->filters[] = $filter;
+    }
+    
+    /**
+     * Filters an HTML snippet/document to be XSS-free and standards-compliant.
+     * 
+     * @param $html String of HTML to purify
+     * @param $config HTMLPurifier_Config object for this operation, if omitted,
+     *                defaults to the config object specified during this
+     *                object's construction. The parameter can also be any type
+     *                that HTMLPurifier_Config::create() supports.
+     * @return Purified HTML
+     */
+    public function purify($html, $config = null) {
+        
+        // :TODO: make the config merge in, instead of replace
+        $config = $config ? HTMLPurifier_Config::create($config) : $this->config;
+        
+        // implementation is partially environment dependant, partially
+        // configuration dependant
+        $lexer = HTMLPurifier_Lexer::create($config);
+        
+        $context = new HTMLPurifier_Context();
+        
+        // setup HTML generator
+        $this->generator = new HTMLPurifier_Generator($config, $context);
+        $context->register('Generator', $this->generator);
+        
+        // set up global context variables
+        if ($config->get('Core', 'CollectErrors')) {
+            // may get moved out if other facilities use it
+            $language_factory = HTMLPurifier_LanguageFactory::instance();
+            $language = $language_factory->create($config, $context);
+            $context->register('Locale', $language);
+            
+            $error_collector = new HTMLPurifier_ErrorCollector($context);
+            $context->register('ErrorCollector', $error_collector);
+        }
+        
+        // setup id_accumulator context, necessary due to the fact that
+        // AttrValidator can be called from many places
+        $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
+        $context->register('IDAccumulator', $id_accumulator);
+        
+        $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);
+        
+        // setup filters
+        $filter_flags = $config->getBatch('Filter');
+        $custom_filters = $filter_flags['Custom'];
+        unset($filter_flags['Custom']);
+        $filters = array();
+        foreach ($filter_flags as $filter => $flag) {
+            if (!$flag) continue;
+            $class = "HTMLPurifier_Filter_$filter";
+            $filters[] = new $class;
+        }
+        foreach ($custom_filters as $filter) {
+            // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
+            $filters[] = $filter;
+        }
+        $filters = array_merge($filters, $this->filters);
+        // maybe prepare(), but later
+        
+        for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
+            $html = $filters[$i]->preFilter($html, $config, $context);
+        }
+        
+        // purified HTML
+        $html = 
+            $this->generator->generateFromTokens(
+                // list of tokens
+                $this->strategy->execute(
+                    // list of un-purified tokens
+                    $lexer->tokenizeHTML(
+                        // un-purified HTML
+                        $html, $config, $context
+                    ),
+                    $config, $context
+                )
+            );
+        
+        for ($i = $filter_size - 1; $i >= 0; $i--) {
+            $html = $filters[$i]->postFilter($html, $config, $context);
+        }
+        
+        $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
+        $this->context =& $context;
+        return $html;
+    }
+    
+    /**
+     * Filters an array of HTML snippets
+     * @param $config Optional HTMLPurifier_Config object for this operation.
+     *                See HTMLPurifier::purify() for more details.
+     * @return Array of purified HTML
+     */
+    public function purifyArray($array_of_html, $config = null) {
+        $context_array = array();
+        foreach ($array_of_html as $key => $html) {
+            $array_of_html[$key] = $this->purify($html, $config);
+            $context_array[$key] = $this->context;
+        }
+        $this->context = $context_array;
+        return $array_of_html;
+    }
+    
+    /**
+     * Singleton for enforcing just one HTML Purifier in your system
+     * @param $prototype Optional prototype HTMLPurifier instance to
+     *                   overload singleton with, or HTMLPurifier_Config
+     *                   instance to configure the generated version with.
+     */
+    public static function instance($prototype = null) {
+        if (!self::$instance || $prototype) {
+            if ($prototype instanceof HTMLPurifier) {
+                self::$instance = $prototype;
+            } elseif ($prototype) {
+                self::$instance = new HTMLPurifier($prototype);
+            } else {
+                self::$instance = new HTMLPurifier();
+            }
+        }
+        return self::$instance;
+    }
+    
+    /**
+     * @note Backwards compatibility, see instance()
+     */
+    public static function getInstance($prototype = null) {
+        return HTMLPurifier::instance($prototype);
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.safe-includes.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.safe-includes.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.safe-includes.php (revision 21)
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * @file
+ * This file was auto-generated by generate-includes.php and includes all of
+ * the core files required by HTML Purifier. This is a convenience stub that
+ * includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT
+ * EDIT THIS FILE, changes will be overwritten the next time the script is run.
+ * 
+ * Changes to include_path are not necessary.
+ */
+
+$__dir = dirname(__FILE__);
+
+require_once $__dir . '/HTMLPurifier.php';
+require_once $__dir . '/HTMLPurifier/AttrCollections.php';
+require_once $__dir . '/HTMLPurifier/AttrDef.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform.php';
+require_once $__dir . '/HTMLPurifier/AttrTypes.php';
+require_once $__dir . '/HTMLPurifier/AttrValidator.php';
+require_once $__dir . '/HTMLPurifier/Bootstrap.php';
+require_once $__dir . '/HTMLPurifier/Definition.php';
+require_once $__dir . '/HTMLPurifier/CSSDefinition.php';
+require_once $__dir . '/HTMLPurifier/ChildDef.php';
+require_once $__dir . '/HTMLPurifier/Config.php';
+require_once $__dir . '/HTMLPurifier/ConfigDef.php';
+require_once $__dir . '/HTMLPurifier/ConfigSchema.php';
+require_once $__dir . '/HTMLPurifier/ContentSets.php';
+require_once $__dir . '/HTMLPurifier/Context.php';
+require_once $__dir . '/HTMLPurifier/DefinitionCache.php';
+require_once $__dir . '/HTMLPurifier/DefinitionCacheFactory.php';
+require_once $__dir . '/HTMLPurifier/Doctype.php';
+require_once $__dir . '/HTMLPurifier/DoctypeRegistry.php';
+require_once $__dir . '/HTMLPurifier/ElementDef.php';
+require_once $__dir . '/HTMLPurifier/Encoder.php';
+require_once $__dir . '/HTMLPurifier/EntityLookup.php';
+require_once $__dir . '/HTMLPurifier/EntityParser.php';
+require_once $__dir . '/HTMLPurifier/ErrorCollector.php';
+require_once $__dir . '/HTMLPurifier/Exception.php';
+require_once $__dir . '/HTMLPurifier/Filter.php';
+require_once $__dir . '/HTMLPurifier/Generator.php';
+require_once $__dir . '/HTMLPurifier/HTMLDefinition.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule.php';
+require_once $__dir . '/HTMLPurifier/HTMLModuleManager.php';
+require_once $__dir . '/HTMLPurifier/IDAccumulator.php';
+require_once $__dir . '/HTMLPurifier/Injector.php';
+require_once $__dir . '/HTMLPurifier/Language.php';
+require_once $__dir . '/HTMLPurifier/LanguageFactory.php';
+require_once $__dir . '/HTMLPurifier/Lexer.php';
+require_once $__dir . '/HTMLPurifier/PercentEncoder.php';
+require_once $__dir . '/HTMLPurifier/Strategy.php';
+require_once $__dir . '/HTMLPurifier/StringHash.php';
+require_once $__dir . '/HTMLPurifier/StringHashParser.php';
+require_once $__dir . '/HTMLPurifier/TagTransform.php';
+require_once $__dir . '/HTMLPurifier/Token.php';
+require_once $__dir . '/HTMLPurifier/TokenFactory.php';
+require_once $__dir . '/HTMLPurifier/URI.php';
+require_once $__dir . '/HTMLPurifier/URIDefinition.php';
+require_once $__dir . '/HTMLPurifier/URIFilter.php';
+require_once $__dir . '/HTMLPurifier/URIParser.php';
+require_once $__dir . '/HTMLPurifier/URIScheme.php';
+require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php';
+require_once $__dir . '/HTMLPurifier/VarParser.php';
+require_once $__dir . '/HTMLPurifier/VarParserException.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/Text.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/URI.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/AlphaValue.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Background.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv6.php';
+require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/BdoDir.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/BgColor.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/BoolToCSS.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
+require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
+require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
+require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php';
+require_once $__dir . '/HTMLPurifier/ChildDef/Required.php';
+require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php';
+require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php';
+require_once $__dir . '/HTMLPurifier/ChildDef/Table.php';
+require_once $__dir . '/HTMLPurifier/ConfigDef/Directive.php';
+require_once $__dir . '/HTMLPurifier/ConfigDef/DirectiveAlias.php';
+require_once $__dir . '/HTMLPurifier/ConfigDef/Namespace.php';
+require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php';
+require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php';
+require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php';
+require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
+require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/List.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Strict.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Transitional.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php';
+require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php';
+require_once $__dir . '/HTMLPurifier/Injector/Linkify.php';
+require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php';
+require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php';
+require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php';
+require_once $__dir . '/HTMLPurifier/Strategy/Composite.php';
+require_once $__dir . '/HTMLPurifier/Strategy/Core.php';
+require_once $__dir . '/HTMLPurifier/Strategy/FixNesting.php';
+require_once $__dir . '/HTMLPurifier/Strategy/MakeWellFormed.php';
+require_once $__dir . '/HTMLPurifier/Strategy/RemoveForeignElements.php';
+require_once $__dir . '/HTMLPurifier/Strategy/ValidateAttributes.php';
+require_once $__dir . '/HTMLPurifier/TagTransform/Font.php';
+require_once $__dir . '/HTMLPurifier/TagTransform/Simple.php';
+require_once $__dir . '/HTMLPurifier/Token/Comment.php';
+require_once $__dir . '/HTMLPurifier/Token/Tag.php';
+require_once $__dir . '/HTMLPurifier/Token/Empty.php';
+require_once $__dir . '/HTMLPurifier/Token/End.php';
+require_once $__dir . '/HTMLPurifier/Token/Start.php';
+require_once $__dir . '/HTMLPurifier/Token/Text.php';
+require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
+require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';
+require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';
+require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
+require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';
+require_once $__dir . '/HTMLPurifier/URIScheme/http.php';
+require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
+require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
+require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
+require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
+require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
+require_once $__dir . '/HTMLPurifier/VarParser/Native.php';
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.kses.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.kses.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.kses.php (revision 21)
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Emulation layer for code that used kses(), substituting in HTML Purifier.
+ */
+
+require_once dirname(__FILE__) . '/HTMLPurifier.auto.php';
+
+function kses($string, $allowed_html, $allowed_protocols = null) {
+    $config = HTMLPurifier_Config::createDefault();
+    $allowed_elements = array();
+    $allowed_attributes = array();
+    foreach ($allowed_html as $element => $attributes) {
+        $allowed_elements[$element] = true;
+        foreach ($attributes as $attribute => $x) {
+            $allowed_attributes["$element.$attribute"] = true;
+        }
+    }
+    $config->set('HTML', 'AllowedElements', $allowed_elements);
+    $config->set('HTML', 'AllowedAttributes', $allowed_attributes);
+    $allowed_schemes = array();
+    if ($allowed_protocols !== null) {
+        $config->set('URI', 'AllowedSchemes', $allowed_protocols);
+    }
+    $purifier = new HTMLPurifier($config);
+    return $purifier->purify($string);
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.includes.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.includes.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.includes.php (revision 21)
@@ -0,0 +1,189 @@
+<?php
+
+/**
+ * @file
+ * This file was auto-generated by generate-includes.php and includes all of
+ * the core files required by HTML Purifier. Use this if performance is a
+ * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
+ * FILE, changes will be overwritten the next time the script is run.
+ * 
+ * @version 3.1.0
+ * 
+ * @warning
+ *      You must *not* include any other HTML Purifier files before this file,
+ *      because 'require' not 'require_once' is used.
+ * 
+ * @warning
+ *      This file requires that the include path contains the HTML Purifier
+ *      library directory; this is not auto-set.
+ */
+
+require 'HTMLPurifier.php';
+require 'HTMLPurifier/AttrCollections.php';
+require 'HTMLPurifier/AttrDef.php';
+require 'HTMLPurifier/AttrTransform.php';
+require 'HTMLPurifier/AttrTypes.php';
+require 'HTMLPurifier/AttrValidator.php';
+require 'HTMLPurifier/Bootstrap.php';
+require 'HTMLPurifier/Definition.php';
+require 'HTMLPurifier/CSSDefinition.php';
+require 'HTMLPurifier/ChildDef.php';
+require 'HTMLPurifier/Config.php';
+require 'HTMLPurifier/ConfigDef.php';
+require 'HTMLPurifier/ConfigSchema.php';
+require 'HTMLPurifier/ContentSets.php';
+require 'HTMLPurifier/Context.php';
+require 'HTMLPurifier/DefinitionCache.php';
+require 'HTMLPurifier/DefinitionCacheFactory.php';
+require 'HTMLPurifier/Doctype.php';
+require 'HTMLPurifier/DoctypeRegistry.php';
+require 'HTMLPurifier/ElementDef.php';
+require 'HTMLPurifier/Encoder.php';
+require 'HTMLPurifier/EntityLookup.php';
+require 'HTMLPurifier/EntityParser.php';
+require 'HTMLPurifier/ErrorCollector.php';
+require 'HTMLPurifier/Exception.php';
+require 'HTMLPurifier/Filter.php';
+require 'HTMLPurifier/Generator.php';
+require 'HTMLPurifier/HTMLDefinition.php';
+require 'HTMLPurifier/HTMLModule.php';
+require 'HTMLPurifier/HTMLModuleManager.php';
+require 'HTMLPurifier/IDAccumulator.php';
+require 'HTMLPurifier/Injector.php';
+require 'HTMLPurifier/Language.php';
+require 'HTMLPurifier/LanguageFactory.php';
+require 'HTMLPurifier/Lexer.php';
+require 'HTMLPurifier/PercentEncoder.php';
+require 'HTMLPurifier/Strategy.php';
+require 'HTMLPurifier/StringHash.php';
+require 'HTMLPurifier/StringHashParser.php';
+require 'HTMLPurifier/TagTransform.php';
+require 'HTMLPurifier/Token.php';
+require 'HTMLPurifier/TokenFactory.php';
+require 'HTMLPurifier/URI.php';
+require 'HTMLPurifier/URIDefinition.php';
+require 'HTMLPurifier/URIFilter.php';
+require 'HTMLPurifier/URIParser.php';
+require 'HTMLPurifier/URIScheme.php';
+require 'HTMLPurifier/URISchemeRegistry.php';
+require 'HTMLPurifier/VarParser.php';
+require 'HTMLPurifier/VarParserException.php';
+require 'HTMLPurifier/AttrDef/CSS.php';
+require 'HTMLPurifier/AttrDef/Enum.php';
+require 'HTMLPurifier/AttrDef/Integer.php';
+require 'HTMLPurifier/AttrDef/Lang.php';
+require 'HTMLPurifier/AttrDef/Text.php';
+require 'HTMLPurifier/AttrDef/URI.php';
+require 'HTMLPurifier/AttrDef/CSS/Number.php';
+require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php';
+require 'HTMLPurifier/AttrDef/CSS/Background.php';
+require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
+require 'HTMLPurifier/AttrDef/CSS/Border.php';
+require 'HTMLPurifier/AttrDef/CSS/Color.php';
+require 'HTMLPurifier/AttrDef/CSS/Composite.php';
+require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
+require 'HTMLPurifier/AttrDef/CSS/Filter.php';
+require 'HTMLPurifier/AttrDef/CSS/Font.php';
+require 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
+require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
+require 'HTMLPurifier/AttrDef/CSS/Length.php';
+require 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
+require 'HTMLPurifier/AttrDef/CSS/Multiple.php';
+require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
+require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
+require 'HTMLPurifier/AttrDef/CSS/URI.php';
+require 'HTMLPurifier/AttrDef/HTML/Bool.php';
+require 'HTMLPurifier/AttrDef/HTML/Color.php';
+require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php';
+require 'HTMLPurifier/AttrDef/HTML/ID.php';
+require 'HTMLPurifier/AttrDef/HTML/Pixels.php';
+require 'HTMLPurifier/AttrDef/HTML/Length.php';
+require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php';
+require 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
+require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
+require 'HTMLPurifier/AttrDef/URI/Email.php';
+require 'HTMLPurifier/AttrDef/URI/Host.php';
+require 'HTMLPurifier/AttrDef/URI/IPv4.php';
+require 'HTMLPurifier/AttrDef/URI/IPv6.php';
+require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
+require 'HTMLPurifier/AttrTransform/BdoDir.php';
+require 'HTMLPurifier/AttrTransform/BgColor.php';
+require 'HTMLPurifier/AttrTransform/BoolToCSS.php';
+require 'HTMLPurifier/AttrTransform/Border.php';
+require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
+require 'HTMLPurifier/AttrTransform/ImgRequired.php';
+require 'HTMLPurifier/AttrTransform/ImgSpace.php';
+require 'HTMLPurifier/AttrTransform/Lang.php';
+require 'HTMLPurifier/AttrTransform/Length.php';
+require 'HTMLPurifier/AttrTransform/Name.php';
+require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
+require 'HTMLPurifier/ChildDef/Chameleon.php';
+require 'HTMLPurifier/ChildDef/Custom.php';
+require 'HTMLPurifier/ChildDef/Empty.php';
+require 'HTMLPurifier/ChildDef/Required.php';
+require 'HTMLPurifier/ChildDef/Optional.php';
+require 'HTMLPurifier/ChildDef/StrictBlockquote.php';
+require 'HTMLPurifier/ChildDef/Table.php';
+require 'HTMLPurifier/ConfigDef/Directive.php';
+require 'HTMLPurifier/ConfigDef/DirectiveAlias.php';
+require 'HTMLPurifier/ConfigDef/Namespace.php';
+require 'HTMLPurifier/DefinitionCache/Decorator.php';
+require 'HTMLPurifier/DefinitionCache/Null.php';
+require 'HTMLPurifier/DefinitionCache/Serializer.php';
+require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
+require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
+require 'HTMLPurifier/HTMLModule/Bdo.php';
+require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
+require 'HTMLPurifier/HTMLModule/Edit.php';
+require 'HTMLPurifier/HTMLModule/Hypertext.php';
+require 'HTMLPurifier/HTMLModule/Image.php';
+require 'HTMLPurifier/HTMLModule/Legacy.php';
+require 'HTMLPurifier/HTMLModule/List.php';
+require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
+require 'HTMLPurifier/HTMLModule/Object.php';
+require 'HTMLPurifier/HTMLModule/Presentation.php';
+require 'HTMLPurifier/HTMLModule/Proprietary.php';
+require 'HTMLPurifier/HTMLModule/Ruby.php';
+require 'HTMLPurifier/HTMLModule/Scripting.php';
+require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
+require 'HTMLPurifier/HTMLModule/Tables.php';
+require 'HTMLPurifier/HTMLModule/Target.php';
+require 'HTMLPurifier/HTMLModule/Text.php';
+require 'HTMLPurifier/HTMLModule/Tidy.php';
+require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
+require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
+require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
+require 'HTMLPurifier/HTMLModule/Tidy/Strict.php';
+require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php';
+require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php';
+require 'HTMLPurifier/Injector/AutoParagraph.php';
+require 'HTMLPurifier/Injector/Linkify.php';
+require 'HTMLPurifier/Injector/PurifierLinkify.php';
+require 'HTMLPurifier/Lexer/DOMLex.php';
+require 'HTMLPurifier/Lexer/DirectLex.php';
+require 'HTMLPurifier/Strategy/Composite.php';
+require 'HTMLPurifier/Strategy/Core.php';
+require 'HTMLPurifier/Strategy/FixNesting.php';
+require 'HTMLPurifier/Strategy/MakeWellFormed.php';
+require 'HTMLPurifier/Strategy/RemoveForeignElements.php';
+require 'HTMLPurifier/Strategy/ValidateAttributes.php';
+require 'HTMLPurifier/TagTransform/Font.php';
+require 'HTMLPurifier/TagTransform/Simple.php';
+require 'HTMLPurifier/Token/Comment.php';
+require 'HTMLPurifier/Token/Tag.php';
+require 'HTMLPurifier/Token/Empty.php';
+require 'HTMLPurifier/Token/End.php';
+require 'HTMLPurifier/Token/Start.php';
+require 'HTMLPurifier/Token/Text.php';
+require 'HTMLPurifier/URIFilter/DisableExternal.php';
+require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
+require 'HTMLPurifier/URIFilter/HostBlacklist.php';
+require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
+require 'HTMLPurifier/URIScheme/ftp.php';
+require 'HTMLPurifier/URIScheme/http.php';
+require 'HTMLPurifier/URIScheme/https.php';
+require 'HTMLPurifier/URIScheme/mailto.php';
+require 'HTMLPurifier/URIScheme/news.php';
+require 'HTMLPurifier/URIScheme/nntp.php';
+require 'HTMLPurifier/VarParser/Flexible.php';
+require 'HTMLPurifier/VarParser/Native.php';
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.auto.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.auto.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.auto.php (revision 21)
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * This is a stub include that automatically configures the include path.
+ */
+
+set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
+require_once 'HTMLPurifier/Bootstrap.php';
+require_once 'HTMLPurifier.autoload.php';
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.autoload.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.autoload.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.autoload.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * Convenience file that registers autoload handler for HTML Purifier.
+ */
+
+if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) {
+    // We need unregister for our pre-registering functionality
+    HTMLPurifier_Bootstrap::registerAutoload();
+    if (function_exists('__autoload')) {
+        // Be polite and ensure that userland autoload gets retained
+        spl_autoload_register('__autoload');
+    }
+} elseif (!function_exists('__autoload')) {
+    function __autoload($class) {
+        return HTMLPurifier_Bootstrap::autoload($class);
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef.php (revision 21)
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * Base class for all validating attribute definitions.
+ * 
+ * This family of classes forms the core for not only HTML attribute validation,
+ * but also any sort of string that needs to be validated or cleaned (which
+ * means CSS properties and composite definitions are defined here too).  
+ * Besides defining (through code) what precisely makes the string valid,
+ * subclasses are also responsible for cleaning the code if possible.
+ */
+
+abstract class HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Tells us whether or not an HTML attribute is minimized. Has no
+     * meaning in other contexts.
+     */
+    public $minimized = false;
+    
+    /**
+     * Tells us whether or not an HTML attribute is required. Has no
+     * meaning in other contexts
+     */
+    public $required = false;
+    
+    /**
+     * Validates and cleans passed string according to a definition.
+     * 
+     * @param $string String to be validated and cleaned.
+     * @param $config Mandatory HTMLPurifier_Config object.
+     * @param $context Mandatory HTMLPurifier_AttrContext object.
+     */
+    abstract public function validate($string, $config, $context);
+    
+    /**
+     * Convenience method that parses a string as if it were CDATA.
+     * 
+     * This method process a string in the manner specified at
+     * <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
+     * leading and trailing whitespace, ignoring line feeds, and replacing
+     * carriage returns and tabs with spaces.  While most useful for HTML
+     * attributes specified as CDATA, it can also be applied to most CSS
+     * values.
+     * 
+     * @note This method is not entirely standards compliant, as trim() removes
+     *       more types of whitespace than specified in the spec. In practice,
+     *       this is rarely a problem, as those extra characters usually have
+     *       already been removed by HTMLPurifier_Encoder.
+     * 
+     * @warning This processing is inconsistent with XML's whitespace handling
+     *          as specified by section 3.3.3 and referenced XHTML 1.0 section
+     *          4.7.  Compliant processing requires all line breaks normalized
+     *          to "\n", so the fix is not as simple as fixing it in this
+     *          function.  Trim and whitespace collapsing are supposed to only
+     *          occur in NMTOKENs.  However, note that we are NOT necessarily
+     *          parsing XML, thus, this behavior may still be correct.
+     */
+    public function parseCDATA($string) {
+        $string = trim($string);
+        $string = str_replace("\n", '', $string);
+        $string = str_replace(array("\r", "\t"), ' ', $string);
+        return $string;
+    }
+    
+    /**
+     * Factory method for creating this class from a string.
+     * @param $string String construction info
+     * @return Created AttrDef object corresponding to $string
+     */
+    public function make($string) {
+        // default implementation, return a flyweight of this object.
+        // If $string has an effect on the returned object (i.e. you
+        // need to overload this method), it is best
+        // to clone or instantiate new copies. (Instantiation is safer.)
+        return $this;
+    }
+    
+    /**
+     * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
+     * properly. THIS IS A HACK!
+     */
+    protected function mungeRgb($string) {
+        return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/PH5P.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/PH5P.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/PH5P.php (revision 21)
@@ -0,0 +1,3906 @@
+<?php
+
+/**
+ * Experimental HTML5-based parser using Jeroen van der Meer's PH5P library.
+ * Occupies space in the HTML5 pseudo-namespace, which may cause conflicts.
+ * 
+ * @note
+ *    Recent changes to PHP's DOM extension have resulted in some fatal
+ *    error conditions with the original version of PH5P. Pending changes,
+ *    this lexer will punt to DirectLex if DOM throughs an exception.
+ */
+
+class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex {
+    
+    public function tokenizeHTML($html, $config, $context) {
+        $new_html = $this->normalize($html, $config, $context);
+        $new_html = $this->wrapHTML($new_html, $config, $context);
+        try {
+            $parser = new HTML5($new_html);
+            $doc = $parser->save();
+        } catch (DOMException $e) {
+            // Uh oh, it failed. Punt to DirectLex.
+            $lexer = new HTMLPurifier_Lexer_DirectLex();
+            $context->register('PH5PError', $e); // save the error, so we can detect it
+            return $lexer->tokenizeHTML($html, $config, $context); // use original HTML
+        }
+        $tokens = array();
+        $this->tokenizeDOM(
+            $doc->getElementsByTagName('html')->item(0)-> // <html>
+                  getElementsByTagName('body')->item(0)-> //   <body>
+                  getElementsByTagName('div')->item(0)    //     <div>
+            , $tokens);
+        return $tokens;
+    }
+    
+}
+
+/*
+
+Copyright 2007 Jeroen van der Meer <http://jero.net/> 
+
+Permission is hereby granted, free of charge, to any person obtaining a 
+copy of this software and associated documentation files (the 
+"Software"), to deal in the Software without restriction, including 
+without limitation the rights to use, copy, modify, merge, publish, 
+distribute, sublicense, and/or sell copies of the Software, and to 
+permit persons to whom the Software is furnished to do so, subject to 
+the following conditions: 
+
+The above copyright notice and this permission notice shall be included 
+in all copies or substantial portions of the Software. 
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
+
+*/
+
+class HTML5 {
+    private $data;
+    private $char;
+    private $EOF;
+    private $state;
+    private $tree;
+    private $token;
+    private $content_model;
+    private $escape = false;
+    private $entities = array('AElig;','AElig','AMP;','AMP','Aacute;','Aacute',
+    'Acirc;','Acirc','Agrave;','Agrave','Alpha;','Aring;','Aring','Atilde;',
+    'Atilde','Auml;','Auml','Beta;','COPY;','COPY','Ccedil;','Ccedil','Chi;',
+    'Dagger;','Delta;','ETH;','ETH','Eacute;','Eacute','Ecirc;','Ecirc','Egrave;',
+    'Egrave','Epsilon;','Eta;','Euml;','Euml','GT;','GT','Gamma;','Iacute;',
+    'Iacute','Icirc;','Icirc','Igrave;','Igrave','Iota;','Iuml;','Iuml','Kappa;',
+    'LT;','LT','Lambda;','Mu;','Ntilde;','Ntilde','Nu;','OElig;','Oacute;',
+    'Oacute','Ocirc;','Ocirc','Ograve;','Ograve','Omega;','Omicron;','Oslash;',
+    'Oslash','Otilde;','Otilde','Ouml;','Ouml','Phi;','Pi;','Prime;','Psi;',
+    'QUOT;','QUOT','REG;','REG','Rho;','Scaron;','Sigma;','THORN;','THORN',
+    'TRADE;','Tau;','Theta;','Uacute;','Uacute','Ucirc;','Ucirc','Ugrave;',
+    'Ugrave','Upsilon;','Uuml;','Uuml','Xi;','Yacute;','Yacute','Yuml;','Zeta;',
+    'aacute;','aacute','acirc;','acirc','acute;','acute','aelig;','aelig',
+    'agrave;','agrave','alefsym;','alpha;','amp;','amp','and;','ang;','apos;',
+    'aring;','aring','asymp;','atilde;','atilde','auml;','auml','bdquo;','beta;',
+    'brvbar;','brvbar','bull;','cap;','ccedil;','ccedil','cedil;','cedil',
+    'cent;','cent','chi;','circ;','clubs;','cong;','copy;','copy','crarr;',
+    'cup;','curren;','curren','dArr;','dagger;','darr;','deg;','deg','delta;',
+    'diams;','divide;','divide','eacute;','eacute','ecirc;','ecirc','egrave;',
+    'egrave','empty;','emsp;','ensp;','epsilon;','equiv;','eta;','eth;','eth',
+    'euml;','euml','euro;','exist;','fnof;','forall;','frac12;','frac12',
+    'frac14;','frac14','frac34;','frac34','frasl;','gamma;','ge;','gt;','gt',
+    'hArr;','harr;','hearts;','hellip;','iacute;','iacute','icirc;','icirc',
+    'iexcl;','iexcl','igrave;','igrave','image;','infin;','int;','iota;',
+    'iquest;','iquest','isin;','iuml;','iuml','kappa;','lArr;','lambda;','lang;',
+    'laquo;','laquo','larr;','lceil;','ldquo;','le;','lfloor;','lowast;','loz;',
+    'lrm;','lsaquo;','lsquo;','lt;','lt','macr;','macr','mdash;','micro;','micro',
+    'middot;','middot','minus;','mu;','nabla;','nbsp;','nbsp','ndash;','ne;',
+    'ni;','not;','not','notin;','nsub;','ntilde;','ntilde','nu;','oacute;',
+    'oacute','ocirc;','ocirc','oelig;','ograve;','ograve','oline;','omega;',
+    'omicron;','oplus;','or;','ordf;','ordf','ordm;','ordm','oslash;','oslash',
+    'otilde;','otilde','otimes;','ouml;','ouml','para;','para','part;','permil;',
+    'perp;','phi;','pi;','piv;','plusmn;','plusmn','pound;','pound','prime;',
+    'prod;','prop;','psi;','quot;','quot','rArr;','radic;','rang;','raquo;',
+    'raquo','rarr;','rceil;','rdquo;','real;','reg;','reg','rfloor;','rho;',
+    'rlm;','rsaquo;','rsquo;','sbquo;','scaron;','sdot;','sect;','sect','shy;',
+    'shy','sigma;','sigmaf;','sim;','spades;','sub;','sube;','sum;','sup1;',
+    'sup1','sup2;','sup2','sup3;','sup3','sup;','supe;','szlig;','szlig','tau;',
+    'there4;','theta;','thetasym;','thinsp;','thorn;','thorn','tilde;','times;',
+    'times','trade;','uArr;','uacute;','uacute','uarr;','ucirc;','ucirc',
+    'ugrave;','ugrave','uml;','uml','upsih;','upsilon;','uuml;','uuml','weierp;',
+    'xi;','yacute;','yacute','yen;','yen','yuml;','yuml','zeta;','zwj;','zwnj;');
+
+    const PCDATA    = 0;
+    const RCDATA    = 1;
+    const CDATA     = 2;
+    const PLAINTEXT = 3;
+
+    const DOCTYPE  = 0;
+    const STARTTAG = 1;
+    const ENDTAG   = 2;
+    const COMMENT  = 3;
+    const CHARACTR = 4;
+    const EOF      = 5;
+
+    public function __construct($data) {
+        $data = str_replace("\r\n", "\n", $data);
+        $data = str_replace("\r", null, $data);
+
+        $this->data = $data;
+        $this->char = -1;
+        $this->EOF  = strlen($data);
+        $this->tree = new HTML5TreeConstructer;
+        $this->content_model = self::PCDATA;
+
+        $this->state = 'data';
+
+        while($this->state !== null) {
+            $this->{$this->state.'State'}();
+        }
+    }
+
+    public function save() {
+        return $this->tree->save();
+    }
+
+    private function char() {
+        return ($this->char < $this->EOF)
+            ? $this->data[$this->char]
+            : false;
+    }
+
+    private function character($s, $l = 0) {
+        if($s + $l < $this->EOF) {
+            if($l === 0) {
+                return $this->data[$s];
+            } else {
+                return substr($this->data, $s, $l);
+            }
+        }
+    }
+
+    private function characters($char_class, $start) {
+        return preg_replace('#^(['.$char_class.']+).*#s', '\\1', substr($this->data, $start));
+    }
+
+    private function dataState() {
+        // Consume the next input character
+        $this->char++;
+        $char = $this->char();
+
+        if($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) {
+            /* U+0026 AMPERSAND (&)
+            When the content model flag is set to one of the PCDATA or RCDATA
+            states: switch to the entity data state. Otherwise: treat it as per
+            the "anything else"    entry below. */
+            $this->state = 'entityData';
+
+        } elseif($char === '-') {
+            /* If the content model flag is set to either the RCDATA state or
+            the CDATA state, and the escape flag is false, and there are at
+            least three characters before this one in the input stream, and the
+            last four characters in the input stream, including this one, are
+            U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS,
+            and U+002D HYPHEN-MINUS ("<!--"), then set the escape flag to true. */
+            if(($this->content_model === self::RCDATA || $this->content_model ===
+            self::CDATA) && $this->escape === false &&
+            $this->char >= 3 && $this->character($this->char - 4, 4) === '<!--') {
+                $this->escape = true;
+            }
+
+            /* In any case, emit the input character as a character token. Stay
+            in the data state. */
+            $this->emitToken(array(
+                'type' => self::CHARACTR,
+                'data' => $char
+            ));
+
+        /* U+003C LESS-THAN SIGN (<) */
+        } elseif($char === '<' && ($this->content_model === self::PCDATA ||
+        (($this->content_model === self::RCDATA ||
+        $this->content_model === self::CDATA) && $this->escape === false))) {
+            /* When the content model flag is set to the PCDATA state: switch
+            to the tag open state.
+
+            When the content model flag is set to either the RCDATA state or
+            the CDATA state and the escape flag is false: switch to the tag
+            open state.
+
+            Otherwise: treat it as per the "anything else" entry below. */
+            $this->state = 'tagOpen';
+
+        /* U+003E GREATER-THAN SIGN (>) */
+        } elseif($char === '>') {
+            /* If the content model flag is set to either the RCDATA state or
+            the CDATA state, and the escape flag is true, and the last three
+            characters in the input stream including this one are U+002D
+            HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"),
+            set the escape flag to false. */
+            if(($this->content_model === self::RCDATA ||
+            $this->content_model === self::CDATA) && $this->escape === true &&
+            $this->character($this->char, 3) === '-->') {
+                $this->escape = false;
+            }
+
+            /* In any case, emit the input character as a character token.
+            Stay in the data state. */
+            $this->emitToken(array(
+                'type' => self::CHARACTR,
+                'data' => $char
+            ));
+
+        } elseif($this->char === $this->EOF) {
+            /* EOF
+            Emit an end-of-file token. */
+            $this->EOF();
+
+        } elseif($this->content_model === self::PLAINTEXT) {
+            /* When the content model flag is set to the PLAINTEXT state
+            THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of
+            the text and emit it as a character token. */
+            $this->emitToken(array(
+                'type' => self::CHARACTR,
+                'data' => substr($this->data, $this->char)
+            ));
+
+            $this->EOF();
+
+        } else {
+            /* Anything else
+            THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that
+            otherwise would also be treated as a character token and emit it
+            as a single character token. Stay in the data state. */
+            $len  = strcspn($this->data, '<&', $this->char);
+            $char = substr($this->data, $this->char, $len);
+            $this->char += $len - 1;
+
+            $this->emitToken(array(
+                'type' => self::CHARACTR,
+                'data' => $char
+            ));
+
+            $this->state = 'data';
+        }
+    }
+
+    private function entityDataState() {
+        // Attempt to consume an entity.
+        $entity = $this->entity();
+
+        // If nothing is returned, emit a U+0026 AMPERSAND character token.
+        // Otherwise, emit the character token that was returned.
+        $char = (!$entity) ? '&' : $entity;
+        $this->emitToken(array(
+            'type' => self::CHARACTR,
+            'data' => $char
+        ));
+
+        // Finally, switch to the data state.
+        $this->state = 'data';
+    }
+
+    private function tagOpenState() {
+        switch($this->content_model) {
+            case self::RCDATA:
+            case self::CDATA:
+                /* If the next input character is a U+002F SOLIDUS (/) character,
+                consume it and switch to the close tag open state. If the next
+                input character is not a U+002F SOLIDUS (/) character, emit a
+                U+003C LESS-THAN SIGN character token and switch to the data
+                state to process the next input character. */
+                if($this->character($this->char + 1) === '/') {
+                    $this->char++;
+                    $this->state = 'closeTagOpen';
+
+                } else {
+                    $this->emitToken(array(
+                        'type' => self::CHARACTR,
+                        'data' => '<'
+                    ));
+
+                    $this->state = 'data';
+                }
+            break;
+
+            case self::PCDATA:
+                // If the content model flag is set to the PCDATA state
+                // Consume the next input character:
+                $this->char++;
+                $char = $this->char();
+
+                if($char === '!') {
+                    /* U+0021 EXCLAMATION MARK (!)
+                    Switch to the markup declaration open state. */
+                    $this->state = 'markupDeclarationOpen';
+
+                } elseif($char === '/') {
+                    /* U+002F SOLIDUS (/)
+                    Switch to the close tag open state. */
+                    $this->state = 'closeTagOpen';
+
+                } elseif(preg_match('/^[A-Za-z]$/', $char)) {
+                    /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
+                    Create a new start tag token, set its tag name to the lowercase
+                    version of the input character (add 0x0020 to the character's code
+                    point), then switch to the tag name state. (Don't emit the token
+                    yet; further details will be filled in before it is emitted.) */
+                    $this->token = array(
+                        'name'  => strtolower($char),
+                        'type'  => self::STARTTAG,
+                        'attr'  => array()
+                    );
+
+                    $this->state = 'tagName';
+
+                } elseif($char === '>') {
+                    /* U+003E GREATER-THAN SIGN (>)
+                    Parse error. Emit a U+003C LESS-THAN SIGN character token and a
+                    U+003E GREATER-THAN SIGN character token. Switch to the data state. */
+                    $this->emitToken(array(
+                        'type' => self::CHARACTR,
+                        'data' => '<>'
+                    ));
+
+                    $this->state = 'data';
+
+                } elseif($char === '?') {
+                    /* U+003F QUESTION MARK (?)
+                    Parse error. Switch to the bogus comment state. */
+                    $this->state = 'bogusComment';
+
+                } else {
+                    /* Anything else
+                    Parse error. Emit a U+003C LESS-THAN SIGN character token and
+                    reconsume the current input character in the data state. */
+                    $this->emitToken(array(
+                        'type' => self::CHARACTR,
+                        'data' => '<'
+                    ));
+
+                    $this->char--;
+                    $this->state = 'data';
+                }
+            break;
+        }
+    }
+
+    private function closeTagOpenState() {
+        $next_node = strtolower($this->characters('A-Za-z', $this->char + 1));
+        $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName;
+
+        if(($this->content_model === self::RCDATA || $this->content_model === self::CDATA) &&
+        (!$the_same || ($the_same && (!preg_match('/[\t\n\x0b\x0c >\/]/',
+        $this->character($this->char + 1 + strlen($next_node))) || $this->EOF === $this->char)))) {
+            /* If the content model flag is set to the RCDATA or CDATA states then
+            examine the next few characters. If they do not match the tag name of
+            the last start tag token emitted (case insensitively), or if they do but
+            they are not immediately followed by one of the following characters:
+                * U+0009 CHARACTER TABULATION
+                * U+000A LINE FEED (LF)
+                * U+000B LINE TABULATION
+                * U+000C FORM FEED (FF)
+                * U+0020 SPACE
+                * U+003E GREATER-THAN SIGN (>)
+                * U+002F SOLIDUS (/)
+                * EOF
+            ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character
+            token, a U+002F SOLIDUS character token, and switch to the data state
+            to process the next input character. */
+            $this->emitToken(array(
+                'type' => self::CHARACTR,
+                'data' => '</'
+            ));
+
+            $this->state = 'data';
+
+        } else {
+            /* Otherwise, if the content model flag is set to the PCDATA state,
+            or if the next few characters do match that tag name, consume the
+            next input character: */
+            $this->char++;
+            $char = $this->char();
+
+            if(preg_match('/^[A-Za-z]$/', $char)) {
+                /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
+                Create a new end tag token, set its tag name to the lowercase version
+                of the input character (add 0x0020 to the character's code point), then
+                switch to the tag name state. (Don't emit the token yet; further details
+                will be filled in before it is emitted.) */
+                $this->token = array(
+                    'name'  => strtolower($char),
+                    'type'  => self::ENDTAG
+                );
+
+                $this->state = 'tagName';
+
+            } elseif($char === '>') {
+                /* U+003E GREATER-THAN SIGN (>)
+                Parse error. Switch to the data state. */
+                $this->state = 'data';
+
+            } elseif($this->char === $this->EOF) {
+                /* EOF
+                Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F
+                SOLIDUS character token. Reconsume the EOF character in the data state. */
+                $this->emitToken(array(
+                    'type' => self::CHARACTR,
+                    'data' => '</'
+                ));
+
+                $this->char--;
+                $this->state = 'data';
+
+            } else {
+                /* Parse error. Switch to the bogus comment state. */
+                $this->state = 'bogusComment';
+            }
+        }
+    }
+
+    private function tagNameState() {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Switch to the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the EOF
+            character in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } elseif($char === '/') {
+            /* U+002F SOLIDUS (/)
+            Parse error unless this is a permitted slash. Switch to the before
+            attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current tag token's tag name.
+            Stay in the tag name state. */
+            $this->token['name'] .= strtolower($char);
+            $this->state = 'tagName';
+        }
+    }
+
+    private function beforeAttributeNameState() {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Stay in the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif($char === '/') {
+            /* U+002F SOLIDUS (/)
+            Parse error unless this is a permitted slash. Stay in the before
+            attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the EOF
+            character in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Start a new attribute in the current tag token. Set that attribute's
+            name to the current input character, and its value to the empty string.
+            Switch to the attribute name state. */
+            $this->token['attr'][] = array(
+                'name'  => strtolower($char),
+                'value' => null
+            );
+
+            $this->state = 'attributeName';
+        }
+    }
+
+    private function attributeNameState() {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Stay in the before attribute name state. */
+            $this->state = 'afterAttributeName';
+
+        } elseif($char === '=') {
+            /* U+003D EQUALS SIGN (=)
+            Switch to the before attribute value state. */
+            $this->state = 'beforeAttributeValue';
+
+        } elseif($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif($char === '/' && $this->character($this->char + 1) !== '>') {
+            /* U+002F SOLIDUS (/)
+            Parse error unless this is a permitted slash. Switch to the before
+            attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the EOF
+            character in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's name.
+            Stay in the attribute name state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['name'] .= strtolower($char);
+
+            $this->state = 'attributeName';
+        }
+    }
+
+    private function afterAttributeNameState() {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Stay in the after attribute name state. */
+            $this->state = 'afterAttributeName';
+
+        } elseif($char === '=') {
+            /* U+003D EQUALS SIGN (=)
+            Switch to the before attribute value state. */
+            $this->state = 'beforeAttributeValue';
+
+        } elseif($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif($char === '/' && $this->character($this->char + 1) !== '>') {
+            /* U+002F SOLIDUS (/)
+            Parse error unless this is a permitted slash. Switch to the
+            before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the EOF
+            character in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Start a new attribute in the current tag token. Set that attribute's
+            name to the current input character, and its value to the empty string.
+            Switch to the attribute name state. */
+            $this->token['attr'][] = array(
+                'name'  => strtolower($char),
+                'value' => null
+            );
+
+            $this->state = 'attributeName';
+        }
+    }
+
+    private function beforeAttributeValueState() {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Stay in the before attribute value state. */
+            $this->state = 'beforeAttributeValue';
+
+        } elseif($char === '"') {
+            /* U+0022 QUOTATION MARK (")
+            Switch to the attribute value (double-quoted) state. */
+            $this->state = 'attributeValueDoubleQuoted';
+
+        } elseif($char === '&') {
+            /* U+0026 AMPERSAND (&)
+            Switch to the attribute value (unquoted) state and reconsume
+            this input character. */
+            $this->char--;
+            $this->state = 'attributeValueUnquoted';
+
+        } elseif($char === '\'') {
+            /* U+0027 APOSTROPHE (')
+            Switch to the attribute value (single-quoted) state. */
+            $this->state = 'attributeValueSingleQuoted';
+
+        } elseif($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's value.
+            Switch to the attribute value (unquoted) state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['value'] .= $char;
+
+            $this->state = 'attributeValueUnquoted';
+        }
+    }
+
+    private function attributeValueDoubleQuotedState() {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if($char === '"') {
+            /* U+0022 QUOTATION MARK (")
+            Switch to the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif($char === '&') {
+            /* U+0026 AMPERSAND (&)
+            Switch to the entity in attribute value state. */
+            $this->entityInAttributeValueState('double');
+
+        } elseif($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the character
+            in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's value.
+            Stay in the attribute value (double-quoted) state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['value'] .= $char;
+
+            $this->state = 'attributeValueDoubleQuoted';
+        }
+    }
+
+    private function attributeValueSingleQuotedState() {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if($char === '\'') {
+            /* U+0022 QUOTATION MARK (')
+            Switch to the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif($char === '&') {
+            /* U+0026 AMPERSAND (&)
+            Switch to the entity in attribute value state. */
+            $this->entityInAttributeValueState('single');
+
+        } elseif($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the character
+            in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's value.
+            Stay in the attribute value (single-quoted) state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['value'] .= $char;
+
+            $this->state = 'attributeValueSingleQuoted';
+        }
+    }
+
+    private function attributeValueUnquotedState() {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Switch to the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif($char === '&') {
+            /* U+0026 AMPERSAND (&)
+            Switch to the entity in attribute value state. */
+            $this->entityInAttributeValueState();
+
+        } elseif($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's value.
+            Stay in the attribute value (unquoted) state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['value'] .= $char;
+
+            $this->state = 'attributeValueUnquoted';
+        }
+    }
+
+    private function entityInAttributeValueState() {
+        // Attempt to consume an entity.
+        $entity = $this->entity();
+
+        // If nothing is returned, append a U+0026 AMPERSAND character to the
+        // current attribute's value. Otherwise, emit the character token that
+        // was returned.
+        $char = (!$entity)
+            ? '&'
+            : $entity;
+
+        $last = count($this->token['attr']) - 1;
+        $this->token['attr'][$last]['value'] .= $char;
+    }
+
+    private function bogusCommentState() {
+        /* Consume every character up to the first U+003E GREATER-THAN SIGN
+        character (>) or the end of the file (EOF), whichever comes first. Emit
+        a comment token whose data is the concatenation of all the characters
+        starting from and including the character that caused the state machine
+        to switch into the bogus comment state, up to and including the last
+        consumed character before the U+003E character, if any, or up to the
+        end of the file otherwise. (If the comment was started by the end of
+        the file (EOF), the token is empty.) */
+        $data = $this->characters('^>', $this->char);
+        $this->emitToken(array(
+            'data' => $data,
+            'type' => self::COMMENT
+        ));
+
+        $this->char += strlen($data);
+
+        /* Switch to the data state. */
+        $this->state = 'data';
+
+        /* If the end of the file was reached, reconsume the EOF character. */
+        if($this->char === $this->EOF) {
+            $this->char = $this->EOF - 1;
+        }
+    }
+
+    private function markupDeclarationOpenState() {
+        /* If the next two characters are both U+002D HYPHEN-MINUS (-)
+        characters, consume those two characters, create a comment token whose
+        data is the empty string, and switch to the comment state. */
+        if($this->character($this->char + 1, 2) === '--') {
+            $this->char += 2;
+            $this->state = 'comment';
+            $this->token = array(
+                'data' => null,
+                'type' => self::COMMENT
+            );
+
+        /* Otherwise if the next seven chacacters are a case-insensitive match
+        for the word "DOCTYPE", then consume those characters and switch to the
+        DOCTYPE state. */
+        } elseif(strtolower($this->character($this->char + 1, 7)) === 'doctype') {
+            $this->char += 7;
+            $this->state = 'doctype';
+
+        /* Otherwise, is is a parse error. Switch to the bogus comment state.
+        The next character that is consumed, if any, is the first character
+        that will be in the comment. */
+        } else {
+            $this->char++;
+            $this->state = 'bogusComment';
+        }
+    }
+
+    private function commentState() {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        /* U+002D HYPHEN-MINUS (-) */
+        if($char === '-') {
+            /* Switch to the comment dash state  */
+            $this->state = 'commentDash';
+
+        /* EOF */
+        } elseif($this->char === $this->EOF) {
+            /* Parse error. Emit the comment token. Reconsume the EOF character
+            in the data state. */
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        /* Anything else */
+        } else {
+            /* Append the input character to the comment token's data. Stay in
+            the comment state. */
+            $this->token['data'] .= $char;
+        }
+    }
+
+    private function commentDashState() {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        /* U+002D HYPHEN-MINUS (-) */
+        if($char === '-') {
+            /* Switch to the comment end state  */
+            $this->state = 'commentEnd';
+
+        /* EOF */
+        } elseif($this->char === $this->EOF) {
+            /* Parse error. Emit the comment token. Reconsume the EOF character
+            in the data state. */
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        /* Anything else */
+        } else {
+            /* Append a U+002D HYPHEN-MINUS (-) character and the input
+            character to the comment token's data. Switch to the comment state. */
+            $this->token['data'] .= '-'.$char;
+            $this->state = 'comment';
+        }
+    }
+
+    private function commentEndState() {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if($char === '>') {
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif($char === '-') {
+            $this->token['data'] .= '-';
+
+        } elseif($this->char === $this->EOF) {
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            $this->token['data'] .= '--'.$char;
+            $this->state = 'comment';
+        }
+    }
+
+    private function doctypeState() {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            $this->state = 'beforeDoctypeName';
+
+        } else {
+            $this->char--;
+            $this->state = 'beforeDoctypeName';
+        }
+    }
+
+    private function beforeDoctypeNameState() {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            // Stay in the before DOCTYPE name state.
+
+        } elseif(preg_match('/^[a-z]$/', $char)) {
+            $this->token = array(
+                'name' => strtoupper($char),
+                'type' => self::DOCTYPE,
+                'error' => true
+            );
+
+            $this->state = 'doctypeName';
+
+        } elseif($char === '>') {
+            $this->emitToken(array(
+                'name' => null,
+                'type' => self::DOCTYPE,
+                'error' => true
+            ));
+
+            $this->state = 'data';
+
+        } elseif($this->char === $this->EOF) {
+            $this->emitToken(array(
+                'name' => null,
+                'type' => self::DOCTYPE,
+                'error' => true
+            ));
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            $this->token = array(
+                'name' => $char,
+                'type' => self::DOCTYPE,
+                'error' => true
+            );
+
+            $this->state = 'doctypeName';
+        }
+    }
+
+    private function doctypeNameState() {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            $this->state = 'AfterDoctypeName';
+
+        } elseif($char === '>') {
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif(preg_match('/^[a-z]$/', $char)) {
+            $this->token['name'] .= strtoupper($char);
+
+        } elseif($this->char === $this->EOF) {
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            $this->token['name'] .= $char;
+        }
+
+        $this->token['error'] = ($this->token['name'] === 'HTML')
+            ? false
+            : true;
+    }
+
+    private function afterDoctypeNameState() {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            // Stay in the DOCTYPE name state.
+
+        } elseif($char === '>') {
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif($this->char === $this->EOF) {
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            $this->token['error'] = true;
+            $this->state = 'bogusDoctype';
+        }
+    }
+
+    private function bogusDoctypeState() {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if($char === '>') {
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif($this->char === $this->EOF) {
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            // Stay in the bogus DOCTYPE state.
+        }
+    }
+
+    private function entity() {
+        $start = $this->char;
+
+        // This section defines how to consume an entity. This definition is
+        // used when parsing entities in text and in attributes.
+
+        // The behaviour depends on the identity of the next character (the
+        // one immediately after the U+0026 AMPERSAND character): 
+
+        switch($this->character($this->char + 1)) {
+            // U+0023 NUMBER SIGN (#)
+            case '#':
+
+                // The behaviour further depends on the character after the
+                // U+0023 NUMBER SIGN:
+                switch($this->character($this->char + 1)) {
+                    // U+0078 LATIN SMALL LETTER X
+                    // U+0058 LATIN CAPITAL LETTER X
+                    case 'x':
+                    case 'X':
+                        // Follow the steps below, but using the range of
+                        // characters U+0030 DIGIT ZERO through to U+0039 DIGIT
+                        // NINE, U+0061 LATIN SMALL LETTER A through to U+0066
+                        // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER
+                        // A, through to U+0046 LATIN CAPITAL LETTER F (in other
+                        // words, 0-9, A-F, a-f).
+                        $char = 1;
+                        $char_class = '0-9A-Fa-f';
+                    break;
+
+                    // Anything else
+                    default:
+                        // Follow the steps below, but using the range of
+                        // characters U+0030 DIGIT ZERO through to U+0039 DIGIT
+                        // NINE (i.e. just 0-9).
+                        $char = 0;
+                        $char_class = '0-9';
+                    break;
+                }
+
+                // Consume as many characters as match the range of characters
+                // given above.
+                $this->char++;
+                $e_name = $this->characters($char_class, $this->char + $char + 1);
+                $entity = $this->character($start, $this->char);
+                $cond = strlen($e_name) > 0;
+
+                // The rest of the parsing happens bellow.
+            break;
+
+            // Anything else
+            default:
+                // Consume the maximum number of characters possible, with the
+                // consumed characters case-sensitively matching one of the
+                // identifiers in the first column of the entities table.
+                $e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
+                $len = strlen($e_name);
+
+                for($c = 1; $c <= $len; $c++) {
+                    $id = substr($e_name, 0, $c);
+                    $this->char++;
+
+                    if(in_array($id, $this->entities)) {
+                        if ($e_name[$c-1] !== ';') {
+                            if ($c < $len && $e_name[$c] == ';') {
+                                $this->char++; // consume extra semicolon
+                            }
+                        }
+                        $entity = $id;
+                        break;
+                    }
+                }
+
+                $cond = isset($entity);
+                // The rest of the parsing happens bellow.
+            break;
+        }
+
+        if(!$cond) {
+            // If no match can be made, then this is a parse error. No
+            // characters are consumed, and nothing is returned.
+            $this->char = $start;
+            return false;
+        }
+
+        // Return a character token for the character corresponding to the
+        // entity name (as given by the second column of the entities table).
+        return html_entity_decode('&'.$entity.';', ENT_QUOTES, 'UTF-8');
+    }
+
+    private function emitToken($token) {
+        $emit = $this->tree->emitToken($token);
+
+        if(is_int($emit)) {
+            $this->content_model = $emit;
+
+        } elseif($token['type'] === self::ENDTAG) {
+            $this->content_model = self::PCDATA;
+        }
+    }
+
+    private function EOF() {
+        $this->state = null;
+        $this->tree->emitToken(array(
+            'type' => self::EOF
+        ));
+    }
+}
+
+class HTML5TreeConstructer {
+    public $stack = array();
+
+    private $phase;
+    private $mode;
+    private $dom;
+    private $foster_parent = null;
+    private $a_formatting  = array();
+
+    private $head_pointer = null;
+    private $form_pointer = null;
+
+    private $scoping = array('button','caption','html','marquee','object','table','td','th');
+    private $formatting = array('a','b','big','em','font','i','nobr','s','small','strike','strong','tt','u');
+    private $special = array('address','area','base','basefont','bgsound',
+    'blockquote','body','br','center','col','colgroup','dd','dir','div','dl',
+    'dt','embed','fieldset','form','frame','frameset','h1','h2','h3','h4','h5',
+    'h6','head','hr','iframe','image','img','input','isindex','li','link',
+    'listing','menu','meta','noembed','noframes','noscript','ol','optgroup',
+    'option','p','param','plaintext','pre','script','select','spacer','style',
+    'tbody','textarea','tfoot','thead','title','tr','ul','wbr');
+
+    // The different phases.
+    const INIT_PHASE = 0;
+    const ROOT_PHASE = 1;
+    const MAIN_PHASE = 2;
+    const END_PHASE  = 3;
+
+    // The different insertion modes for the main phase.
+    const BEFOR_HEAD = 0;
+    const IN_HEAD    = 1;
+    const AFTER_HEAD = 2;
+    const IN_BODY    = 3;
+    const IN_TABLE   = 4;
+    const IN_CAPTION = 5;
+    const IN_CGROUP  = 6;
+    const IN_TBODY   = 7;
+    const IN_ROW     = 8;
+    const IN_CELL    = 9;
+    const IN_SELECT  = 10;
+    const AFTER_BODY = 11;
+    const IN_FRAME   = 12;
+    const AFTR_FRAME = 13;
+
+    // The different types of elements.
+    const SPECIAL    = 0;
+    const SCOPING    = 1;
+    const FORMATTING = 2;
+    const PHRASING   = 3;
+
+    const MARKER     = 0;
+
+    public function __construct() {
+        $this->phase = self::INIT_PHASE;
+        $this->mode = self::BEFOR_HEAD;
+        $this->dom = new DOMDocument;
+
+        $this->dom->encoding = 'UTF-8';
+        $this->dom->preserveWhiteSpace = true;
+        $this->dom->substituteEntities = true;
+        $this->dom->strictErrorChecking = false;
+    }
+
+    // Process tag tokens
+    public function emitToken($token) {
+        switch($this->phase) {
+            case self::INIT_PHASE: return $this->initPhase($token); break;
+            case self::ROOT_PHASE: return $this->rootElementPhase($token); break;
+            case self::MAIN_PHASE: return $this->mainPhase($token); break;
+            case self::END_PHASE : return $this->trailingEndPhase($token); break;
+        }
+    }
+
+    private function initPhase($token) {
+        /* Initially, the tree construction stage must handle each token
+        emitted from the tokenisation stage as follows: */
+
+        /* A DOCTYPE token that is marked as being in error
+        A comment token
+        A start tag token
+        An end tag token
+        A character token that is not one of one of U+0009 CHARACTER TABULATION,
+            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+            or U+0020 SPACE
+        An end-of-file token */
+        if((isset($token['error']) && $token['error']) ||
+        $token['type'] === HTML5::COMMENT ||
+        $token['type'] === HTML5::STARTTAG ||
+        $token['type'] === HTML5::ENDTAG ||
+        $token['type'] === HTML5::EOF ||
+        ($token['type'] === HTML5::CHARACTR && isset($token['data']) &&
+        !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))) {
+            /* This specification does not define how to handle this case. In
+            particular, user agents may ignore the entirety of this specification
+            altogether for such documents, and instead invoke special parse modes
+            with a greater emphasis on backwards compatibility. */
+
+            $this->phase = self::ROOT_PHASE;
+            return $this->rootElementPhase($token);
+
+        /* A DOCTYPE token marked as being correct */
+        } elseif(isset($token['error']) && !$token['error']) {
+            /* Append a DocumentType node to the Document  node, with the name
+            attribute set to the name given in the DOCTYPE token (which will be
+            "HTML"), and the other attributes specific to DocumentType objects
+            set to null, empty lists, or the empty string as appropriate. */
+            $doctype = new DOMDocumentType(null, null, 'HTML');
+
+            /* Then, switch to the root element phase of the tree construction
+            stage. */
+            $this->phase = self::ROOT_PHASE;
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        } elseif(isset($token['data']) && preg_match('/^[\t\n\x0b\x0c ]+$/',
+        $token['data'])) {
+            /* Append that character  to the Document node. */
+            $text = $this->dom->createTextNode($token['data']);
+            $this->dom->appendChild($text);
+        }
+    }
+
+    private function rootElementPhase($token) {
+        /* After the initial phase, as each token is emitted from the tokenisation
+        stage, it must be processed as described in this section. */
+
+        /* A DOCTYPE token */
+        if($token['type'] === HTML5::DOCTYPE) {
+            // Parse error. Ignore the token.
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the Document object with the data
+            attribute set to the data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            $this->dom->appendChild($comment);
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        } elseif($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Append that character  to the Document node. */
+            $text = $this->dom->createTextNode($token['data']);
+            $this->dom->appendChild($text);
+
+        /* A character token that is not one of U+0009 CHARACTER TABULATION,
+            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED
+            (FF), or U+0020 SPACE
+        A start tag token
+        An end tag token
+        An end-of-file token */
+        } elseif(($token['type'] === HTML5::CHARACTR &&
+        !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
+        $token['type'] === HTML5::STARTTAG ||
+        $token['type'] === HTML5::ENDTAG ||
+        $token['type'] === HTML5::EOF) {
+            /* Create an HTMLElement node with the tag name html, in the HTML
+            namespace. Append it to the Document object. Switch to the main
+            phase and reprocess the current token. */
+            $html = $this->dom->createElement('html');
+            $this->dom->appendChild($html);
+            $this->stack[] = $html;
+
+            $this->phase = self::MAIN_PHASE;
+            return $this->mainPhase($token);
+        }
+    }
+
+    private function mainPhase($token) {
+        /* Tokens in the main phase must be handled as follows: */
+
+        /* A DOCTYPE token */
+        if($token['type'] === HTML5::DOCTYPE) {
+            // Parse error. Ignore the token.
+
+        /* A start tag token with the tag name "html" */
+        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') {
+            /* If this start tag token was not the first start tag token, then
+            it is a parse error. */
+
+            /* For each attribute on the token, check to see if the attribute
+            is already present on the top element of the stack of open elements.
+            If it is not, add the attribute and its corresponding value to that
+            element. */
+            foreach($token['attr'] as $attr) {
+                if(!$this->stack[0]->hasAttribute($attr['name'])) {
+                    $this->stack[0]->setAttribute($attr['name'], $attr['value']);
+                }
+            }
+
+        /* An end-of-file token */
+        } elseif($token['type'] === HTML5::EOF) {
+            /* Generate implied end tags. */
+            $this->generateImpliedEndTags();
+
+        /* Anything else. */
+        } else {
+            /* Depends on the insertion mode: */
+            switch($this->mode) {
+                case self::BEFOR_HEAD: return $this->beforeHead($token); break;
+                case self::IN_HEAD:    return $this->inHead($token); break;
+                case self::AFTER_HEAD: return $this->afterHead($token); break;
+                case self::IN_BODY:    return $this->inBody($token); break;
+                case self::IN_TABLE:   return $this->inTable($token); break;
+                case self::IN_CAPTION: return $this->inCaption($token); break;
+                case self::IN_CGROUP:  return $this->inColumnGroup($token); break;
+                case self::IN_TBODY:   return $this->inTableBody($token); break;
+                case self::IN_ROW:     return $this->inRow($token); break;
+                case self::IN_CELL:    return $this->inCell($token); break;
+                case self::IN_SELECT:  return $this->inSelect($token); break;
+                case self::AFTER_BODY: return $this->afterBody($token); break;
+                case self::IN_FRAME:   return $this->inFrameset($token); break;
+                case self::AFTR_FRAME: return $this->afterFrameset($token); break;
+                case self::END_PHASE:  return $this->trailingEndPhase($token); break;
+            }
+        }
+    }
+
+    private function beforeHead($token) {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data attribute
+            set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+        /* A start tag token with the tag name "head" */
+        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') {
+            /* Create an element for the token, append the new element to the
+            current node and push it onto the stack of open elements. */
+            $element = $this->insertElement($token);
+
+            /* Set the head element pointer to this new element node. */
+            $this->head_pointer = $element;
+
+            /* Change the insertion mode to "in head". */
+            $this->mode = self::IN_HEAD;
+
+        /* A start tag token whose tag name is one of: "base", "link", "meta",
+        "script", "style", "title". Or an end tag with the tag name "html".
+        Or a character token that is not one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE. Or any other start tag token */
+        } elseif($token['type'] === HTML5::STARTTAG ||
+        ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') ||
+        ($token['type'] === HTML5::CHARACTR && !preg_match('/^[\t\n\x0b\x0c ]$/',
+        $token['data']))) {
+            /* Act as if a start tag token with the tag name "head" and no
+            attributes had been seen, then reprocess the current token. */
+            $this->beforeHead(array(
+                'name' => 'head',
+                'type' => HTML5::STARTTAG,
+                'attr' => array()
+            ));
+
+            return $this->inHead($token);
+
+        /* Any other end tag */
+        } elseif($token['type'] === HTML5::ENDTAG) {
+            /* Parse error. Ignore the token. */
+        }
+    }
+
+    private function inHead($token) {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE.
+
+        THIS DIFFERS FROM THE SPEC: If the current node is either a title, style
+        or script element, append the character to the current node regardless
+        of its content. */
+        if(($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || (
+        $token['type'] === HTML5::CHARACTR && in_array(end($this->stack)->nodeName,
+        array('title', 'style', 'script')))) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data attribute
+            set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+        } elseif($token['type'] === HTML5::ENDTAG &&
+        in_array($token['name'], array('title', 'style', 'script'))) {
+            array_pop($this->stack);
+            return HTML5::PCDATA;
+
+        /* A start tag with the tag name "title" */
+        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') {
+            /* Create an element for the token and append the new element to the
+            node pointed to by the head element pointer, or, if that is null
+            (innerHTML case), to the current node. */
+            if($this->head_pointer !== null) {
+                $element = $this->insertElement($token, false);
+                $this->head_pointer->appendChild($element);
+
+            } else {
+                $element = $this->insertElement($token);
+            }
+
+            /* Switch the tokeniser's content model flag  to the RCDATA state. */
+            return HTML5::RCDATA;
+
+        /* A start tag with the tag name "style" */
+        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') {
+            /* Create an element for the token and append the new element to the
+            node pointed to by the head element pointer, or, if that is null
+            (innerHTML case), to the current node. */
+            if($this->head_pointer !== null) {
+                $element = $this->insertElement($token, false);
+                $this->head_pointer->appendChild($element);
+
+            } else {
+                $this->insertElement($token);
+            }
+
+            /* Switch the tokeniser's content model flag  to the CDATA state. */
+            return HTML5::CDATA;
+
+        /* A start tag with the tag name "script" */
+        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') {
+            /* Create an element for the token. */
+            $element = $this->insertElement($token, false);
+            $this->head_pointer->appendChild($element);
+
+            /* Switch the tokeniser's content model flag  to the CDATA state. */
+            return HTML5::CDATA;
+
+        /* A start tag with the tag name "base", "link", or "meta" */
+        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
+        array('base', 'link', 'meta'))) {
+            /* Create an element for the token and append the new element to the
+            node pointed to by the head element pointer, or, if that is null
+            (innerHTML case), to the current node. */
+            if($this->head_pointer !== null) {
+                $element = $this->insertElement($token, false);
+                $this->head_pointer->appendChild($element);
+                array_pop($this->stack);
+
+            } else {
+                $this->insertElement($token);
+            }
+
+        /* An end tag with the tag name "head" */
+        } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') {
+            /* If the current node is a head element, pop the current node off
+            the stack of open elements. */
+            if($this->head_pointer->isSameNode(end($this->stack))) {
+                array_pop($this->stack);
+
+            /* Otherwise, this is a parse error. */
+            } else {
+                // k
+            }
+
+            /* Change the insertion mode to "after head". */
+            $this->mode = self::AFTER_HEAD;
+
+        /* A start tag with the tag name "head" or an end tag except "html". */
+        } elseif(($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') ||
+        ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')) {
+            // Parse error. Ignore the token.
+
+        /* Anything else */
+        } else {
+            /* If the current node is a head element, act as if an end tag
+            token with the tag name "head" had been seen. */
+            if($this->head_pointer->isSameNode(end($this->stack))) {
+                $this->inHead(array(
+                    'name' => 'head',
+                    'type' => HTML5::ENDTAG
+                ));
+
+            /* Otherwise, change the insertion mode to "after head". */
+            } else {
+                $this->mode = self::AFTER_HEAD;
+            }
+
+            /* Then, reprocess the current token. */
+            return $this->afterHead($token);
+        }
+    }
+
+    private function afterHead($token) {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data attribute
+            set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+        /* A start tag token with the tag name "body" */
+        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') {
+            /* Insert a body element for the token. */
+            $this->insertElement($token);
+
+            /* Change the insertion mode to "in body". */
+            $this->mode = self::IN_BODY;
+
+        /* A start tag token with the tag name "frameset" */
+        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') {
+            /* Insert a frameset element for the token. */
+            $this->insertElement($token);
+
+            /* Change the insertion mode to "in frameset". */
+            $this->mode = self::IN_FRAME;
+
+        /* A start tag token whose tag name is one of: "base", "link", "meta",
+        "script", "style", "title" */
+        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
+        array('base', 'link', 'meta', 'script', 'style', 'title'))) {
+            /* Parse error. Switch the insertion mode back to "in head" and
+            reprocess the token. */
+            $this->mode = self::IN_HEAD;
+            return $this->inHead($token);
+
+        /* Anything else */
+        } else {
+            /* Act as if a start tag token with the tag name "body" and no
+            attributes had been seen, and then reprocess the current token. */
+            $this->afterHead(array(
+                'name' => 'body',
+                'type' => HTML5::STARTTAG,
+                'attr' => array()
+            ));
+
+            return $this->inBody($token);
+        }
+    }
+
+    private function inBody($token) {
+        /* Handle the token as follows: */
+
+        switch($token['type']) {
+            /* A character token */
+            case HTML5::CHARACTR:
+                /* Reconstruct the active formatting elements, if any. */
+                $this->reconstructActiveFormattingElements();
+
+                /* Append the token's character to the current node. */
+                $this->insertText($token['data']);
+            break;
+
+            /* A comment token */
+            case HTML5::COMMENT:
+                /* Append a Comment node to the current node with the data
+                attribute set to the data given in the comment token. */
+                $this->insertComment($token['data']);
+            break;
+
+            case HTML5::STARTTAG:
+            switch($token['name']) {
+                /* A start tag token whose tag name is one of: "script",
+                "style" */
+                case 'script': case 'style':
+                    /* Process the token as if the insertion mode had been "in
+                    head". */
+                    return $this->inHead($token);
+                break;
+
+                /* A start tag token whose tag name is one of: "base", "link",
+                "meta", "title" */
+                case 'base': case 'link': case 'meta': case 'title':
+                    /* Parse error. Process the token as if the insertion mode
+                    had    been "in head". */
+                    return $this->inHead($token);
+                break;
+
+                /* A start tag token with the tag name "body" */
+                case 'body':
+                    /* Parse error. If the second element on the stack of open
+                    elements is not a body element, or, if the stack of open
+                    elements has only one node on it, then ignore the token.
+                    (innerHTML case) */
+                    if(count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') {
+                        // Ignore
+
+                    /* Otherwise, for each attribute on the token, check to see
+                    if the attribute is already present on the body element (the
+                    second element)    on the stack of open elements. If it is not,
+                    add the attribute and its corresponding value to that
+                    element. */
+                    } else {
+                        foreach($token['attr'] as $attr) {
+                            if(!$this->stack[1]->hasAttribute($attr['name'])) {
+                                $this->stack[1]->setAttribute($attr['name'], $attr['value']);
+                            }
+                        }
+                    }
+                break;
+
+                /* A start tag whose tag name is one of: "address",
+                "blockquote", "center", "dir", "div", "dl", "fieldset",
+                "listing", "menu", "ol", "p", "ul" */
+                case 'address': case 'blockquote': case 'center': case 'dir':
+                case 'div': case 'dl': case 'fieldset': case 'listing':
+                case 'menu': case 'ol': case 'p': case 'ul':
+                    /* If the stack of open elements has a p element in scope,
+                    then act as if an end tag with the tag name p had been
+                    seen. */
+                    if($this->elementInScope('p')) {
+                        $this->emitToken(array(
+                            'name' => 'p',
+                            'type' => HTML5::ENDTAG
+                        ));
+                    }
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+                break;
+
+                /* A start tag whose tag name is "form" */
+                case 'form':
+                    /* If the form element pointer is not null, ignore the
+                    token with a parse error. */
+                    if($this->form_pointer !== null) {
+                        // Ignore.
+
+                    /* Otherwise: */
+                    } else {
+                        /* If the stack of open elements has a p element in
+                        scope, then act as if an end tag with the tag name p
+                        had been seen. */
+                        if($this->elementInScope('p')) {
+                            $this->emitToken(array(
+                                'name' => 'p',
+                                'type' => HTML5::ENDTAG
+                            ));
+                        }
+
+                        /* Insert an HTML element for the token, and set the
+                        form element pointer to point to the element created. */
+                        $element = $this->insertElement($token);
+                        $this->form_pointer = $element;
+                    }
+                break;
+
+                /* A start tag whose tag name is "li", "dd" or "dt" */
+                case 'li': case 'dd': case 'dt':
+                    /* If the stack of open elements has a p  element in scope,
+                    then act as if an end tag with the tag name p had been
+                    seen. */
+                    if($this->elementInScope('p')) {
+                        $this->emitToken(array(
+                            'name' => 'p',
+                            'type' => HTML5::ENDTAG
+                        ));
+                    }
+
+                    $stack_length = count($this->stack) - 1;
+
+                    for($n = $stack_length; 0 <= $n; $n--) {
+                        /* 1. Initialise node to be the current node (the
+                        bottommost node of the stack). */
+                        $stop = false;
+                        $node = $this->stack[$n];
+                        $cat  = $this->getElementCategory($node->tagName);
+
+                        /* 2. If node is an li, dd or dt element, then pop all
+                        the    nodes from the current node up to node, including
+                        node, then stop this algorithm. */
+                        if($token['name'] === $node->tagName ||    ($token['name'] !== 'li'
+                        && ($node->tagName === 'dd' || $node->tagName === 'dt'))) {
+                            for($x = $stack_length; $x >= $n ; $x--) {
+                                array_pop($this->stack);
+                            }
+
+                            break;
+                        }
+
+                        /* 3. If node is not in the formatting category, and is
+                        not    in the phrasing category, and is not an address or
+                        div element, then stop this algorithm. */
+                        if($cat !== self::FORMATTING && $cat !== self::PHRASING &&
+                        $node->tagName !== 'address' && $node->tagName !== 'div') {
+                            break;
+                        }
+                    }
+
+                    /* Finally, insert an HTML element with the same tag
+                    name as the    token's. */
+                    $this->insertElement($token);
+                break;
+
+                /* A start tag token whose tag name is "plaintext" */
+                case 'plaintext':
+                    /* If the stack of open elements has a p  element in scope,
+                    then act as if an end tag with the tag name p had been
+                    seen. */
+                    if($this->elementInScope('p')) {
+                        $this->emitToken(array(
+                            'name' => 'p',
+                            'type' => HTML5::ENDTAG
+                        ));
+                    }
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+
+                    return HTML5::PLAINTEXT;
+                break;
+
+                /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4",
+                "h5", "h6" */
+                case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
+                    /* If the stack of open elements has a p  element in scope,
+                    then act as if an end tag with the tag name p had been seen. */
+                    if($this->elementInScope('p')) {
+                        $this->emitToken(array(
+                            'name' => 'p',
+                            'type' => HTML5::ENDTAG
+                        ));
+                    }
+
+                    /* If the stack of open elements has in scope an element whose
+                    tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
+                    this is a parse error; pop elements from the stack until an
+                    element with one of those tag names has been popped from the
+                    stack. */
+                    while($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
+                        array_pop($this->stack);
+                    }
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+                break;
+
+                /* A start tag whose tag name is "a" */
+                case 'a':
+                    /* If the list of active formatting elements contains
+                    an element whose tag name is "a" between the end of the
+                    list and the last marker on the list (or the start of
+                    the list if there is no marker on the list), then this
+                    is a parse error; act as if an end tag with the tag name
+                    "a" had been seen, then remove that element from the list
+                    of active formatting elements and the stack of open
+                    elements if the end tag didn't already remove it (it
+                    might not have if the element is not in table scope). */
+                    $leng = count($this->a_formatting);
+
+                    for($n = $leng - 1; $n >= 0; $n--) {
+                        if($this->a_formatting[$n] === self::MARKER) {
+                            break;
+
+                        } elseif($this->a_formatting[$n]->nodeName === 'a') {
+                            $this->emitToken(array(
+                                'name' => 'a',
+                                'type' => HTML5::ENDTAG
+                            ));
+                            break;
+                        }
+                    }
+
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    /* Insert an HTML element for the token. */
+                    $el = $this->insertElement($token);
+
+                    /* Add that element to the list of active formatting
+                    elements. */
+                    $this->a_formatting[] = $el;
+                break;
+
+                /* A start tag whose tag name is one of: "b", "big", "em", "font",
+                "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
+                case 'b': case 'big': case 'em': case 'font': case 'i':
+                case 'nobr': case 's': case 'small': case 'strike':
+                case 'strong': case 'tt': case 'u':
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    /* Insert an HTML element for the token. */
+                    $el = $this->insertElement($token);
+
+                    /* Add that element to the list of active formatting
+                    elements. */
+                    $this->a_formatting[] = $el;
+                break;
+
+                /* A start tag token whose tag name is "button" */
+                case 'button':
+                    /* If the stack of open elements has a button element in scope,
+                    then this is a parse error; act as if an end tag with the tag
+                    name "button" had been seen, then reprocess the token. (We don't
+                    do that. Unnecessary.) */
+                    if($this->elementInScope('button')) {
+                        $this->inBody(array(
+                            'name' => 'button',
+                            'type' => HTML5::ENDTAG
+                        ));
+                    }
+
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+
+                    /* Insert a marker at the end of the list of active
+                    formatting elements. */
+                    $this->a_formatting[] = self::MARKER;
+                break;
+
+                /* A start tag token whose tag name is one of: "marquee", "object" */
+                case 'marquee': case 'object':
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+
+                    /* Insert a marker at the end of the list of active
+                    formatting elements. */
+                    $this->a_formatting[] = self::MARKER;
+                break;
+
+                /* A start tag token whose tag name is "xmp" */
+                case 'xmp':
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+
+                    /* Switch the content model flag to the CDATA state. */
+                    return HTML5::CDATA;
+                break;
+
+                /* A start tag whose tag name is "table" */
+                case 'table':
+                    /* If the stack of open elements has a p element in scope,
+                    then act as if an end tag with the tag name p had been seen. */
+                    if($this->elementInScope('p')) {
+                        $this->emitToken(array(
+                            'name' => 'p',
+                            'type' => HTML5::ENDTAG
+                        ));
+                    }
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+
+                    /* Change the insertion mode to "in table". */
+                    $this->mode = self::IN_TABLE;
+                break;
+
+                /* A start tag whose tag name is one of: "area", "basefont",
+                "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */
+                case 'area': case 'basefont': case 'bgsound': case 'br':
+                case 'embed': case 'img': case 'param': case 'spacer':
+                case 'wbr':
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+
+                    /* Immediately pop the current node off the stack of open elements. */
+                    array_pop($this->stack);
+                break;
+
+                /* A start tag whose tag name is "hr" */
+                case 'hr':
+                    /* If the stack of open elements has a p element in scope,
+                    then act as if an end tag with the tag name p had been seen. */
+                    if($this->elementInScope('p')) {
+                        $this->emitToken(array(
+                            'name' => 'p',
+                            'type' => HTML5::ENDTAG
+                        ));
+                    }
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+
+                    /* Immediately pop the current node off the stack of open elements. */
+                    array_pop($this->stack);
+                break;
+
+                /* A start tag whose tag name is "image" */
+                case 'image':
+                    /* Parse error. Change the token's tag name to "img" and
+                    reprocess it. (Don't ask.) */
+                    $token['name'] = 'img';
+                    return $this->inBody($token);
+                break;
+
+                /* A start tag whose tag name is "input" */
+                case 'input':
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    /* Insert an input element for the token. */
+                    $element = $this->insertElement($token, false);
+
+                    /* If the form element pointer is not null, then associate the
+                    input element with the form element pointed to by the form
+                    element pointer. */
+                    $this->form_pointer !== null
+                        ? $this->form_pointer->appendChild($element)
+                        : end($this->stack)->appendChild($element);
+
+                    /* Pop that input element off the stack of open elements. */
+                    array_pop($this->stack);
+                break;
+
+                /* A start tag whose tag name is "isindex" */
+                case 'isindex':
+                    /* Parse error. */
+                    // w/e
+
+                    /* If the form element pointer is not null,
+                    then ignore the token. */
+                    if($this->form_pointer === null) {
+                        /* Act as if a start tag token with the tag name "form" had
+                        been seen. */
+                        $this->inBody(array(
+                            'name' => 'body',
+                            'type' => HTML5::STARTTAG,
+                            'attr' => array()
+                        ));
+
+                        /* Act as if a start tag token with the tag name "hr" had
+                        been seen. */
+                        $this->inBody(array(
+                            'name' => 'hr',
+                            'type' => HTML5::STARTTAG,
+                            'attr' => array()
+                        ));
+
+                        /* Act as if a start tag token with the tag name "p" had
+                        been seen. */
+                        $this->inBody(array(
+                            'name' => 'p',
+                            'type' => HTML5::STARTTAG,
+                            'attr' => array()
+                        ));
+
+                        /* Act as if a start tag token with the tag name "label"
+                        had been seen. */
+                        $this->inBody(array(
+                            'name' => 'label',
+                            'type' => HTML5::STARTTAG,
+                            'attr' => array()
+                        ));
+
+                        /* Act as if a stream of character tokens had been seen. */
+                        $this->insertText('This is a searchable index. '.
+                        'Insert your search keywords here: ');
+
+                        /* Act as if a start tag token with the tag name "input"
+                        had been seen, with all the attributes from the "isindex"
+                        token, except with the "name" attribute set to the value
+                        "isindex" (ignoring any explicit "name" attribute). */
+                        $attr = $token['attr'];
+                        $attr[] = array('name' => 'name', 'value' => 'isindex');
+
+                        $this->inBody(array(
+                            'name' => 'input',
+                            'type' => HTML5::STARTTAG,
+                            'attr' => $attr
+                        ));
+
+                        /* Act as if a stream of character tokens had been seen
+                        (see below for what they should say). */
+                        $this->insertText('This is a searchable index. '.
+                        'Insert your search keywords here: ');
+
+                        /* Act as if an end tag token with the tag name "label"
+                        had been seen. */
+                        $this->inBody(array(
+                            'name' => 'label',
+                            'type' => HTML5::ENDTAG
+                        ));
+
+                        /* Act as if an end tag token with the tag name "p" had
+                        been seen. */
+                        $this->inBody(array(
+                            'name' => 'p',
+                            'type' => HTML5::ENDTAG
+                        ));
+
+                        /* Act as if a start tag token with the tag name "hr" had
+                        been seen. */
+                        $this->inBody(array(
+                            'name' => 'hr',
+                            'type' => HTML5::ENDTAG
+                        ));
+
+                        /* Act as if an end tag token with the tag name "form" had
+                        been seen. */
+                        $this->inBody(array(
+                            'name' => 'form',
+                            'type' => HTML5::ENDTAG
+                        ));
+                    }
+                break;
+
+                /* A start tag whose tag name is "textarea" */
+                case 'textarea':
+                    $this->insertElement($token);
+
+                    /* Switch the tokeniser's content model flag to the
+                    RCDATA state. */
+                    return HTML5::RCDATA;
+                break;
+
+                /* A start tag whose tag name is one of: "iframe", "noembed",
+                "noframes" */
+                case 'iframe': case 'noembed': case 'noframes':
+                    $this->insertElement($token);
+
+                    /* Switch the tokeniser's content model flag to the CDATA state. */
+                    return HTML5::CDATA;
+                break;
+
+                /* A start tag whose tag name is "select" */
+                case 'select':
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    /* Insert an HTML element for the token. */
+                    $this->insertElement($token);
+
+                    /* Change the insertion mode to "in select". */
+                    $this->mode = self::IN_SELECT;
+                break;
+
+                /* A start or end tag whose tag name is one of: "caption", "col",
+                "colgroup", "frame", "frameset", "head", "option", "optgroup",
+                "tbody", "td", "tfoot", "th", "thead", "tr". */
+                case 'caption': case 'col': case 'colgroup': case 'frame':
+                case 'frameset': case 'head': case 'option': case 'optgroup':
+                case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead':
+                case 'tr':
+                    // Parse error. Ignore the token.
+                break;
+
+                /* A start or end tag whose tag name is one of: "event-source",
+                "section", "nav", "article", "aside", "header", "footer",
+                "datagrid", "command" */
+                case 'event-source': case 'section': case 'nav': case 'article':
+                case 'aside': case 'header': case 'footer': case 'datagrid':
+                case 'command':
+                    // Work in progress!
+                break;
+
+                /* A start tag token not covered by the previous entries */
+                default:
+                    /* Reconstruct the active formatting elements, if any. */
+                    $this->reconstructActiveFormattingElements();
+
+                    $this->insertElement($token, true, true);
+                break;
+            }
+            break;
+
+            case HTML5::ENDTAG:
+            switch($token['name']) {
+                /* An end tag with the tag name "body" */
+                case 'body':
+                    /* If the second element in the stack of open elements is
+                    not a body element, this is a parse error. Ignore the token.
+                    (innerHTML case) */
+                    if(count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') {
+                        // Ignore.
+
+                    /* If the current node is not the body element, then this
+                    is a parse error. */
+                    } elseif(end($this->stack)->nodeName !== 'body') {
+                        // Parse error.
+                    }
+
+                    /* Change the insertion mode to "after body". */
+                    $this->mode = self::AFTER_BODY;
+                break;
+
+                /* An end tag with the tag name "html" */
+                case 'html':
+                    /* Act as if an end tag with tag name "body" had been seen,
+                    then, if that token wasn't ignored, reprocess the current
+                    token. */
+                    $this->inBody(array(
+                        'name' => 'body',
+                        'type' => HTML5::ENDTAG
+                    ));
+
+                    return $this->afterBody($token);
+                break;
+
+                /* An end tag whose tag name is one of: "address", "blockquote",
+                "center", "dir", "div", "dl", "fieldset", "listing", "menu",
+                "ol", "pre", "ul" */
+                case 'address': case 'blockquote': case 'center': case 'dir':
+                case 'div': case 'dl': case 'fieldset': case 'listing':
+                case 'menu': case 'ol': case 'pre': case 'ul':
+                    /* If the stack of open elements has an element in scope
+                    with the same tag name as that of the token, then generate
+                    implied end tags. */
+                    if($this->elementInScope($token['name'])) {
+                        $this->generateImpliedEndTags();
+
+                        /* Now, if the current node is not an element with
+                        the same tag name as that of the token, then this
+                        is a parse error. */
+                        // w/e
+
+                        /* If the stack of open elements has an element in
+                        scope with the same tag name as that of the token,
+                        then pop elements from this stack until an element
+                        with that tag name has been popped from the stack. */
+                        for($n = count($this->stack) - 1; $n >= 0; $n--) {
+                            if($this->stack[$n]->nodeName === $token['name']) {
+                                $n = -1;
+                            }
+
+                            array_pop($this->stack);
+                        }
+                    }
+                break;
+
+                /* An end tag whose tag name is "form" */
+                case 'form':
+                    /* If the stack of open elements has an element in scope
+                    with the same tag name as that of the token, then generate
+                    implied    end tags. */
+                    if($this->elementInScope($token['name'])) {
+                        $this->generateImpliedEndTags();
+
+                    } 
+
+                    if(end($this->stack)->nodeName !== $token['name']) {
+                        /* Now, if the current node is not an element with the
+                        same tag name as that of the token, then this is a parse
+                        error. */
+                        // w/e
+
+                    } else {
+                        /* Otherwise, if the current node is an element with
+                        the same tag name as that of the token pop that element
+                        from the stack. */
+                        array_pop($this->stack);
+                    }
+
+                    /* In any case, set the form element pointer to null. */
+                    $this->form_pointer = null;
+                break;
+
+                /* An end tag whose tag name is "p" */
+                case 'p':
+                    /* If the stack of open elements has a p element in scope,
+                    then generate implied end tags, except for p elements. */
+                    if($this->elementInScope('p')) {
+                        $this->generateImpliedEndTags(array('p'));
+
+                        /* If the current node is not a p element, then this is
+                        a parse error. */
+                        // k
+
+                        /* If the stack of open elements has a p element in
+                        scope, then pop elements from this stack until the stack
+                        no longer has a p element in scope. */
+                        for($n = count($this->stack) - 1; $n >= 0; $n--) {
+                            if($this->elementInScope('p')) {
+                                array_pop($this->stack);
+
+                            } else {
+                                break;
+                            }
+                        }
+                    }
+                break;
+
+                /* An end tag whose tag name is "dd", "dt", or "li" */
+                case 'dd': case 'dt': case 'li':
+                    /* If the stack of open elements has an element in scope
+                    whose tag name matches the tag name of the token, then
+                    generate implied end tags, except for elements with the
+                    same tag name as the token. */
+                    if($this->elementInScope($token['name'])) {
+                        $this->generateImpliedEndTags(array($token['name']));
+
+                        /* If the current node is not an element with the same
+                        tag name as the token, then this is a parse error. */
+                        // w/e
+
+                        /* If the stack of open elements has an element in scope
+                        whose tag name matches the tag name of the token, then
+                        pop elements from this stack until an element with that
+                        tag name has been popped from the stack. */
+                        for($n = count($this->stack) - 1; $n >= 0; $n--) {
+                            if($this->stack[$n]->nodeName === $token['name']) {
+                                $n = -1;
+                            }
+
+                            array_pop($this->stack);
+                        }
+                    }
+                break;
+
+                /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4",
+                "h5", "h6" */
+                case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
+                    $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');
+
+                    /* If the stack of open elements has in scope an element whose
+                    tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
+                    generate implied end tags. */
+                    if($this->elementInScope($elements)) {
+                        $this->generateImpliedEndTags();
+
+                        /* Now, if the current node is not an element with the same
+                        tag name as that of the token, then this is a parse error. */
+                        // w/e
+
+                        /* If the stack of open elements has in scope an element
+                        whose tag name is one of "h1", "h2", "h3", "h4", "h5", or
+                        "h6", then pop elements from the stack until an element
+                        with one of those tag names has been popped from the stack. */
+                        while($this->elementInScope($elements)) {
+                            array_pop($this->stack);
+                        }
+                    }
+                break;
+
+                /* An end tag whose tag name is one of: "a", "b", "big", "em",
+                "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
+                case 'a': case 'b': case 'big': case 'em': case 'font':
+                case 'i': case 'nobr': case 's': case 'small': case 'strike':
+                case 'strong': case 'tt': case 'u':
+                    /* 1. Let the formatting element be the last element in
+                    the list of active formatting elements that:
+                        * is between the end of the list and the last scope
+                        marker in the list, if any, or the start of the list
+                        otherwise, and
+                        * has the same tag name as the token.
+                    */
+                    while(true) {
+                        for($a = count($this->a_formatting) - 1; $a >= 0; $a--) {
+                            if($this->a_formatting[$a] === self::MARKER) {
+                                break;
+
+                            } elseif($this->a_formatting[$a]->tagName === $token['name']) {
+                                $formatting_element = $this->a_formatting[$a];
+                                $in_stack = in_array($formatting_element, $this->stack, true);
+                                $fe_af_pos = $a;
+                                break;
+                            }
+                        }
+
+                        /* If there is no such node, or, if that node is
+                        also in the stack of open elements but the element
+                        is not in scope, then this is a parse error. Abort
+                        these steps. The token is ignored. */
+                        if(!isset($formatting_element) || ($in_stack &&
+                        !$this->elementInScope($token['name']))) {
+                            break;
+
+                        /* Otherwise, if there is such a node, but that node
+                        is not in the stack of open elements, then this is a
+                        parse error; remove the element from the list, and
+                        abort these steps. */
+                        } elseif(isset($formatting_element) && !$in_stack) {
+                            unset($this->a_formatting[$fe_af_pos]);
+                            $this->a_formatting = array_merge($this->a_formatting);
+                            break;
+                        }
+
+                        /* 2. Let the furthest block be the topmost node in the
+                        stack of open elements that is lower in the stack
+                        than the formatting element, and is not an element in
+                        the phrasing or formatting categories. There might
+                        not be one. */
+                        $fe_s_pos = array_search($formatting_element, $this->stack, true);
+                        $length = count($this->stack);
+
+                        for($s = $fe_s_pos + 1; $s < $length; $s++) {
+                            $category = $this->getElementCategory($this->stack[$s]->nodeName);
+
+                            if($category !== self::PHRASING && $category !== self::FORMATTING) {
+                                $furthest_block = $this->stack[$s];
+                            }
+                        }
+
+                        /* 3. If there is no furthest block, then the UA must
+                        skip the subsequent steps and instead just pop all
+                        the nodes from the bottom of the stack of open
+                        elements, from the current node up to the formatting
+                        element, and remove the formatting element from the
+                        list of active formatting elements. */
+                        if(!isset($furthest_block)) {
+                            for($n = $length - 1; $n >= $fe_s_pos; $n--) {
+                                array_pop($this->stack);
+                            }
+
+                            unset($this->a_formatting[$fe_af_pos]);
+                            $this->a_formatting = array_merge($this->a_formatting);
+                            break;
+                        }
+
+                        /* 4. Let the common ancestor be the element
+                        immediately above the formatting element in the stack
+                        of open elements. */
+                        $common_ancestor = $this->stack[$fe_s_pos - 1];
+
+                        /* 5. If the furthest block has a parent node, then
+                        remove the furthest block from its parent node. */
+                        if($furthest_block->parentNode !== null) {
+                            $furthest_block->parentNode->removeChild($furthest_block);
+                        }
+
+                        /* 6. Let a bookmark note the position of the
+                        formatting element in the list of active formatting
+                        elements relative to the elements on either side
+                        of it in the list. */
+                        $bookmark = $fe_af_pos;
+
+                        /* 7. Let node and last node  be the furthest block.
+                        Follow these steps: */
+                        $node = $furthest_block;
+                        $last_node = $furthest_block;
+
+                        while(true) {
+                            for($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
+                                /* 7.1 Let node be the element immediately
+                                prior to node in the stack of open elements. */
+                                $node = $this->stack[$n];
+
+                                /* 7.2 If node is not in the list of active
+                                formatting elements, then remove node from
+                                the stack of open elements and then go back
+                                to step 1. */
+                                if(!in_array($node, $this->a_formatting, true)) {
+                                    unset($this->stack[$n]);
+                                    $this->stack = array_merge($this->stack);
+
+                                } else {
+                                    break;
+                                }
+                            }
+
+                            /* 7.3 Otherwise, if node is the formatting
+                            element, then go to the next step in the overall
+                            algorithm. */
+                            if($node === $formatting_element) {
+                                break;
+
+                            /* 7.4 Otherwise, if last node is the furthest
+                            block, then move the aforementioned bookmark to
+                            be immediately after the node in the list of
+                            active formatting elements. */
+                            } elseif($last_node === $furthest_block) {
+                                $bookmark = array_search($node, $this->a_formatting, true) + 1;
+                            }
+
+                            /* 7.5 If node has any children, perform a
+                            shallow clone of node, replace the entry for
+                            node in the list of active formatting elements
+                            with an entry for the clone, replace the entry
+                            for node in the stack of open elements with an
+                            entry for the clone, and let node be the clone. */
+                            if($node->hasChildNodes()) {
+                                $clone = $node->cloneNode();
+                                $s_pos = array_search($node, $this->stack, true);
+                                $a_pos = array_search($node, $this->a_formatting, true);
+
+                                $this->stack[$s_pos] = $clone;
+                                $this->a_formatting[$a_pos] = $clone;
+                                $node = $clone;
+                            }
+
+                            /* 7.6 Insert last node into node, first removing
+                            it from its previous parent node if any. */
+                            if($last_node->parentNode !== null) {
+                                $last_node->parentNode->removeChild($last_node);
+                            }
+
+                            $node->appendChild($last_node);
+
+                            /* 7.7 Let last node be node. */
+                            $last_node = $node;
+                        }
+
+                        /* 8. Insert whatever last node ended up being in
+                        the previous step into the common ancestor node,
+                        first removing it from its previous parent node if
+                        any. */
+                        if($last_node->parentNode !== null) {
+                            $last_node->parentNode->removeChild($last_node);
+                        }
+
+                        $common_ancestor->appendChild($last_node);
+
+                        /* 9. Perform a shallow clone of the formatting
+                        element. */
+                        $clone = $formatting_element->cloneNode();
+
+                        /* 10. Take all of the child nodes of the furthest
+                        block and append them to the clone created in the
+                        last step. */
+                        while($furthest_block->hasChildNodes()) {
+                            $child = $furthest_block->firstChild;
+                            $furthest_block->removeChild($child);
+                            $clone->appendChild($child);
+                        }
+
+                        /* 11. Append that clone to the furthest block. */
+                        $furthest_block->appendChild($clone);
+
+                        /* 12. Remove the formatting element from the list
+                        of active formatting elements, and insert the clone
+                        into the list of active formatting elements at the
+                        position of the aforementioned bookmark. */
+                        $fe_af_pos = array_search($formatting_element, $this->a_formatting, true);
+                        unset($this->a_formatting[$fe_af_pos]);
+                        $this->a_formatting = array_merge($this->a_formatting);
+
+                        $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
+                        $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting));
+                        $this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
+
+                        /* 13. Remove the formatting element from the stack
+                        of open elements, and insert the clone into the stack
+                        of open elements immediately after (i.e. in a more
+                        deeply nested position than) the position of the
+                        furthest block in that stack. */
+                        $fe_s_pos = array_search($formatting_element, $this->stack, true);
+                        $fb_s_pos = array_search($furthest_block, $this->stack, true);
+                        unset($this->stack[$fe_s_pos]);
+
+                        $s_part1 = array_slice($this->stack, 0, $fb_s_pos);
+                        $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack));
+                        $this->stack = array_merge($s_part1, array($clone), $s_part2);
+
+                        /* 14. Jump back to step 1 in this series of steps. */
+                        unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
+                    }
+                break;
+
+                /* An end tag token whose tag name is one of: "button",
+                "marquee", "object" */
+                case 'button': case 'marquee': case 'object':
+                    /* If the stack of open elements has an element in scope whose
+                    tag name matches the tag name of the token, then generate implied
+                    tags. */
+                    if($this->elementInScope($token['name'])) {
+                        $this->generateImpliedEndTags();
+
+                        /* Now, if the current node is not an element with the same
+                        tag name as the token, then this is a parse error. */
+                        // k
+
+                        /* Now, if the stack of open elements has an element in scope
+                        whose tag name matches the tag name of the token, then pop
+                        elements from the stack until that element has been popped from
+                        the stack, and clear the list of active formatting elements up
+                        to the last marker. */
+                        for($n = count($this->stack) - 1; $n >= 0; $n--) {
+                            if($this->stack[$n]->nodeName === $token['name']) {
+                                $n = -1;
+                            }
+
+                            array_pop($this->stack);
+                        }
+
+                        $marker = end(array_keys($this->a_formatting, self::MARKER, true));
+
+                        for($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
+                            array_pop($this->a_formatting);
+                        }
+                    }
+                break;
+
+                /* Or an end tag whose tag name is one of: "area", "basefont",
+                "bgsound", "br", "embed", "hr", "iframe", "image", "img",
+                "input", "isindex", "noembed", "noframes", "param", "select",
+                "spacer", "table", "textarea", "wbr" */
+                case 'area': case 'basefont': case 'bgsound': case 'br':
+                case 'embed': case 'hr': case 'iframe': case 'image':
+                case 'img': case 'input': case 'isindex': case 'noembed':
+                case 'noframes': case 'param': case 'select': case 'spacer':
+                case 'table': case 'textarea': case 'wbr':
+                    // Parse error. Ignore the token.
+                break;
+
+                /* An end tag token not covered by the previous entries */
+                default:
+                    for($n = count($this->stack) - 1; $n >= 0; $n--) {
+                        /* Initialise node to be the current node (the bottommost
+                        node of the stack). */
+                        $node = end($this->stack);
+
+                        /* If node has the same tag name as the end tag token,
+                        then: */
+                        if($token['name'] === $node->nodeName) {
+                            /* Generate implied end tags. */
+                            $this->generateImpliedEndTags();
+
+                            /* If the tag name of the end tag token does not
+                            match the tag name of the current node, this is a
+                            parse error. */
+                            // k
+
+                            /* Pop all the nodes from the current node up to
+                            node, including node, then stop this algorithm. */
+                            for($x = count($this->stack) - $n; $x >= $n; $x--) {
+                                array_pop($this->stack);
+                            }
+                                    
+                        } else {
+                            $category = $this->getElementCategory($node);
+
+                            if($category !== self::SPECIAL && $category !== self::SCOPING) {
+                                /* Otherwise, if node is in neither the formatting
+                                category nor the phrasing category, then this is a
+                                parse error. Stop this algorithm. The end tag token
+                                is ignored. */
+                                return false;
+                            }
+                        }
+                    }
+                break;
+            }
+            break;
+        }
+    }
+
+    private function inTable($token) {
+        $clear = array('html', 'table');
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Append the character to the current node. */
+            $text = $this->dom->createTextNode($token['data']);
+            end($this->stack)->appendChild($text);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            end($this->stack)->appendChild($comment);
+
+        /* A start tag whose tag name is "caption" */
+        } elseif($token['type'] === HTML5::STARTTAG &&
+        $token['name'] === 'caption') {
+            /* Clear the stack back to a table context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert a marker at the end of the list of active
+            formatting elements. */
+            $this->a_formatting[] = self::MARKER;
+
+            /* Insert an HTML element for the token, then switch the
+            insertion mode to "in caption". */
+            $this->insertElement($token);
+            $this->mode = self::IN_CAPTION;
+
+        /* A start tag whose tag name is "colgroup" */
+        } elseif($token['type'] === HTML5::STARTTAG &&
+        $token['name'] === 'colgroup') {
+            /* Clear the stack back to a table context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert an HTML element for the token, then switch the
+            insertion mode to "in column group". */
+            $this->insertElement($token);
+            $this->mode = self::IN_CGROUP;
+
+        /* A start tag whose tag name is "col" */
+        } elseif($token['type'] === HTML5::STARTTAG &&
+        $token['name'] === 'col') {
+            $this->inTable(array(
+                'name' => 'colgroup',
+                'type' => HTML5::STARTTAG,
+                'attr' => array()
+            ));
+
+            $this->inColumnGroup($token);
+
+        /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
+        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
+        array('tbody', 'tfoot', 'thead'))) {
+            /* Clear the stack back to a table context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert an HTML element for the token, then switch the insertion
+            mode to "in table body". */
+            $this->insertElement($token);
+            $this->mode = self::IN_TBODY;
+
+        /* A start tag whose tag name is one of: "td", "th", "tr" */
+        } elseif($token['type'] === HTML5::STARTTAG &&
+        in_array($token['name'], array('td', 'th', 'tr'))) {
+            /* Act as if a start tag token with the tag name "tbody" had been
+            seen, then reprocess the current token. */
+            $this->inTable(array(
+                'name' => 'tbody',
+                'type' => HTML5::STARTTAG,
+                'attr' => array()
+            ));
+
+            return $this->inTableBody($token);
+
+        /* A start tag whose tag name is "table" */
+        } elseif($token['type'] === HTML5::STARTTAG &&
+        $token['name'] === 'table') {
+            /* Parse error. Act as if an end tag token with the tag name "table"
+            had been seen, then, if that token wasn't ignored, reprocess the
+            current token. */
+            $this->inTable(array(
+                'name' => 'table',
+                'type' => HTML5::ENDTAG
+            ));
+
+            return $this->mainPhase($token);
+
+        /* An end tag whose tag name is "table" */
+        } elseif($token['type'] === HTML5::ENDTAG &&
+        $token['name'] === 'table') {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. (innerHTML case) */
+            if(!$this->elementInScope($token['name'], true)) {
+                return false;
+
+            /* Otherwise: */
+            } else {
+                /* Generate implied end tags. */
+                $this->generateImpliedEndTags();
+
+                /* Now, if the current node is not a table element, then this
+                is a parse error. */
+                // w/e
+
+                /* Pop elements from this stack until a table element has been
+                popped from the stack. */
+                while(true) {
+                    $current = end($this->stack)->nodeName;
+                    array_pop($this->stack);
+
+                    if($current === 'table') {
+                        break;
+                    }
+                }
+
+                /* Reset the insertion mode appropriately. */
+                $this->resetInsertionMode();
+            }
+
+        /* An end tag whose tag name is one of: "body", "caption", "col",
+        "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
+        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
+        array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td',
+        'tfoot', 'th', 'thead', 'tr'))) {
+            // Parse error. Ignore the token.
+
+        /* Anything else */
+        } else {
+            /* Parse error. Process the token as if the insertion mode was "in
+            body", with the following exception: */
+
+            /* If the current node is a table, tbody, tfoot, thead, or tr
+            element, then, whenever a node would be inserted into the current
+            node, it must instead be inserted into the foster parent element. */
+            if(in_array(end($this->stack)->nodeName,
+            array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
+                /* The foster parent element is the parent element of the last
+                table element in the stack of open elements, if there is a
+                table element and it has such a parent element. If there is no
+                table element in the stack of open elements (innerHTML case),
+                then the foster parent element is the first element in the
+                stack of open elements (the html  element). Otherwise, if there
+                is a table element in the stack of open elements, but the last
+                table element in the stack of open elements has no parent, or
+                its parent node is not an element, then the foster parent
+                element is the element before the last table element in the
+                stack of open elements. */
+                for($n = count($this->stack) - 1; $n >= 0; $n--) {
+                    if($this->stack[$n]->nodeName === 'table') {
+                        $table = $this->stack[$n];
+                        break;
+                    }
+                }
+
+                if(isset($table) && $table->parentNode !== null) {
+                    $this->foster_parent = $table->parentNode;
+
+                } elseif(!isset($table)) {
+                    $this->foster_parent = $this->stack[0];
+
+                } elseif(isset($table) && ($table->parentNode === null ||
+                $table->parentNode->nodeType !== XML_ELEMENT_NODE)) {
+                    $this->foster_parent = $this->stack[$n - 1];
+                }
+            }
+
+            $this->inBody($token);
+        }
+    }
+
+    private function inCaption($token) {
+        /* An end tag whose tag name is "caption" */
+        if($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. (innerHTML case) */
+            if(!$this->elementInScope($token['name'], true)) {
+                // Ignore
+
+            /* Otherwise: */
+            } else {
+                /* Generate implied end tags. */
+                $this->generateImpliedEndTags();
+
+                /* Now, if the current node is not a caption element, then this
+                is a parse error. */
+                // w/e
+
+                /* Pop elements from this stack until a caption element has
+                been popped from the stack. */
+                while(true) {
+                    $node = end($this->stack)->nodeName;
+                    array_pop($this->stack);
+
+                    if($node === 'caption') {
+                        break;
+                    }
+                }
+
+                /* Clear the list of active formatting elements up to the last
+                marker. */
+                $this->clearTheActiveFormattingElementsUpToTheLastMarker();
+
+                /* Switch the insertion mode to "in table". */
+                $this->mode = self::IN_TABLE;
+            }
+
+        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+        "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag
+        name is "table" */
+        } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'],
+        array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
+        'thead', 'tr'))) || ($token['type'] === HTML5::ENDTAG &&
+        $token['name'] === 'table')) {
+            /* Parse error. Act as if an end tag with the tag name "caption"
+            had been seen, then, if that token wasn't ignored, reprocess the
+            current token. */
+            $this->inCaption(array(
+                'name' => 'caption',
+                'type' => HTML5::ENDTAG
+            ));
+
+            return $this->inTable($token);
+
+        /* An end tag whose tag name is one of: "body", "col", "colgroup",
+        "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
+        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
+        array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th',
+        'thead', 'tr'))) {
+            // Parse error. Ignore the token.
+
+        /* Anything else */
+        } else {
+            /* Process the token as if the insertion mode was "in body". */
+            $this->inBody($token);
+        }
+    }
+
+    private function inColumnGroup($token) {
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Append the character to the current node. */
+            $text = $this->dom->createTextNode($token['data']);
+            end($this->stack)->appendChild($text);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            end($this->stack)->appendChild($comment);
+
+        /* A start tag whose tag name is "col" */
+        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') {
+            /* Insert a col element for the token. Immediately pop the current
+            node off the stack of open elements. */
+            $this->insertElement($token);
+            array_pop($this->stack);
+
+        /* An end tag whose tag name is "colgroup" */
+        } elseif($token['type'] === HTML5::ENDTAG &&
+        $token['name'] === 'colgroup') {
+            /* If the current node is the root html element, then this is a
+            parse error, ignore the token. (innerHTML case) */
+            if(end($this->stack)->nodeName === 'html') {
+                // Ignore
+
+            /* Otherwise, pop the current node (which will be a colgroup
+            element) from the stack of open elements. Switch the insertion
+            mode to "in table". */
+            } else {
+                array_pop($this->stack);
+                $this->mode = self::IN_TABLE;
+            }
+
+        /* An end tag whose tag name is "col" */
+        } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') {
+            /* Parse error. Ignore the token. */
+
+        /* Anything else */
+        } else {
+            /* Act as if an end tag with the tag name "colgroup" had been seen,
+            and then, if that token wasn't ignored, reprocess the current token. */
+            $this->inColumnGroup(array(
+                'name' => 'colgroup',
+                'type' => HTML5::ENDTAG
+            ));
+
+            return $this->inTable($token);
+        }
+    }
+
+    private function inTableBody($token) {
+        $clear = array('tbody', 'tfoot', 'thead', 'html');
+
+        /* A start tag whose tag name is "tr" */
+        if($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') {
+            /* Clear the stack back to a table body context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert a tr element for the token, then switch the insertion
+            mode to "in row". */
+            $this->insertElement($token);
+            $this->mode = self::IN_ROW;
+
+        /* A start tag whose tag name is one of: "th", "td" */
+        } elseif($token['type'] === HTML5::STARTTAG &&
+        ($token['name'] === 'th' ||    $token['name'] === 'td')) {
+            /* Parse error. Act as if a start tag with the tag name "tr" had
+            been seen, then reprocess the current token. */
+            $this->inTableBody(array(
+                'name' => 'tr',
+                'type' => HTML5::STARTTAG,
+                'attr' => array()
+            ));
+
+            return $this->inRow($token);
+
+        /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
+        } elseif($token['type'] === HTML5::ENDTAG &&
+        in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. */
+            if(!$this->elementInScope($token['name'], true)) {
+                // Ignore
+
+            /* Otherwise: */
+            } else {
+                /* Clear the stack back to a table body context. */
+                $this->clearStackToTableContext($clear);
+
+                /* Pop the current node from the stack of open elements. Switch
+                the insertion mode to "in table". */
+                array_pop($this->stack);
+                $this->mode = self::IN_TABLE;
+            }
+
+        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+        "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */
+        } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'],
+        array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead'))) ||
+        ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table')) {
+            /* If the stack of open elements does not have a tbody, thead, or
+            tfoot element in table scope, this is a parse error. Ignore the
+            token. (innerHTML case) */
+            if(!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) {
+                // Ignore.
+
+            /* Otherwise: */
+            } else {
+                /* Clear the stack back to a table body context. */
+                $this->clearStackToTableContext($clear);
+
+                /* Act as if an end tag with the same tag name as the current
+                node ("tbody", "tfoot", or "thead") had been seen, then
+                reprocess the current token. */
+                $this->inTableBody(array(
+                    'name' => end($this->stack)->nodeName,
+                    'type' => HTML5::ENDTAG
+                ));
+
+                return $this->mainPhase($token);
+            }
+
+        /* An end tag whose tag name is one of: "body", "caption", "col",
+        "colgroup", "html", "td", "th", "tr" */
+        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
+        array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
+            /* Parse error. Ignore the token. */
+
+        /* Anything else */
+        } else {
+            /* Process the token as if the insertion mode was "in table". */
+            $this->inTable($token);
+        }
+    }
+
+    private function inRow($token) {
+        $clear = array('tr', 'html');
+
+        /* A start tag whose tag name is one of: "th", "td" */
+        if($token['type'] === HTML5::STARTTAG &&
+        ($token['name'] === 'th' || $token['name'] === 'td')) {
+            /* Clear the stack back to a table row context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert an HTML element for the token, then switch the insertion
+            mode to "in cell". */
+            $this->insertElement($token);
+            $this->mode = self::IN_CELL;
+
+            /* Insert a marker at the end of the list of active formatting
+            elements. */
+            $this->a_formatting[] = self::MARKER;
+
+        /* An end tag whose tag name is "tr" */
+        } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. (innerHTML case) */
+            if(!$this->elementInScope($token['name'], true)) {
+                // Ignore.
+
+            /* Otherwise: */
+            } else {
+                /* Clear the stack back to a table row context. */
+                $this->clearStackToTableContext($clear);
+
+                /* Pop the current node (which will be a tr element) from the
+                stack of open elements. Switch the insertion mode to "in table
+                body". */
+                array_pop($this->stack);
+                $this->mode = self::IN_TBODY;
+            }
+
+        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+        "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */
+        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
+        array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) {
+            /* Act as if an end tag with the tag name "tr" had been seen, then,
+            if that token wasn't ignored, reprocess the current token. */
+            $this->inRow(array(
+                'name' => 'tr',
+                'type' => HTML5::ENDTAG
+            ));
+
+            return $this->inCell($token);
+
+        /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
+        } elseif($token['type'] === HTML5::ENDTAG &&
+        in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. */
+            if(!$this->elementInScope($token['name'], true)) {
+                // Ignore.
+
+            /* Otherwise: */
+            } else {
+                /* Otherwise, act as if an end tag with the tag name "tr" had
+                been seen, then reprocess the current token. */
+                $this->inRow(array(
+                    'name' => 'tr',
+                    'type' => HTML5::ENDTAG
+                ));
+
+                return $this->inCell($token);
+            }
+
+        /* An end tag whose tag name is one of: "body", "caption", "col",
+        "colgroup", "html", "td", "th" */
+        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
+        array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
+            /* Parse error. Ignore the token. */
+
+        /* Anything else */
+        } else {
+            /* Process the token as if the insertion mode was "in table". */
+            $this->inTable($token);
+        }
+    }
+
+    private function inCell($token) {
+        /* An end tag whose tag name is one of: "td", "th" */
+        if($token['type'] === HTML5::ENDTAG &&
+        ($token['name'] === 'td' || $token['name'] === 'th')) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as that of the token, then this is a
+            parse error and the token must be ignored. */
+            if(!$this->elementInScope($token['name'], true)) {
+                // Ignore.
+
+            /* Otherwise: */
+            } else {
+                /* Generate implied end tags, except for elements with the same
+                tag name as the token. */
+                $this->generateImpliedEndTags(array($token['name']));
+
+                /* Now, if the current node is not an element with the same tag
+                name as the token, then this is a parse error. */
+                // k
+
+                /* Pop elements from this stack until an element with the same
+                tag name as the token has been popped from the stack. */
+                while(true) {
+                    $node = end($this->stack)->nodeName;
+                    array_pop($this->stack);
+
+                    if($node === $token['name']) {
+                        break;
+                    }
+                }
+
+                /* Clear the list of active formatting elements up to the last
+                marker. */
+                $this->clearTheActiveFormattingElementsUpToTheLastMarker();
+
+                /* Switch the insertion mode to "in row". (The current node
+                will be a tr element at this point.) */
+                $this->mode = self::IN_ROW;
+            }
+
+        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+        "tbody", "td", "tfoot", "th", "thead", "tr" */
+        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
+        array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
+        'thead', 'tr'))) {
+            /* If the stack of open elements does not have a td or th element
+            in table scope, then this is a parse error; ignore the token.
+            (innerHTML case) */
+            if(!$this->elementInScope(array('td', 'th'), true)) {
+                // Ignore.
+
+            /* Otherwise, close the cell (see below) and reprocess the current
+            token. */
+            } else {
+                $this->closeCell();
+                return $this->inRow($token);
+            }
+
+        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+        "tbody", "td", "tfoot", "th", "thead", "tr" */
+        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
+        array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
+        'thead', 'tr'))) {
+            /* If the stack of open elements does not have a td or th element
+            in table scope, then this is a parse error; ignore the token.
+            (innerHTML case) */
+            if(!$this->elementInScope(array('td', 'th'), true)) {
+                // Ignore.
+
+            /* Otherwise, close the cell (see below) and reprocess the current
+            token. */
+            } else {
+                $this->closeCell();
+                return $this->inRow($token);
+            }
+
+        /* An end tag whose tag name is one of: "body", "caption", "col",
+        "colgroup", "html" */
+        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
+        array('body', 'caption', 'col', 'colgroup', 'html'))) {
+            /* Parse error. Ignore the token. */
+
+        /* An end tag whose tag name is one of: "table", "tbody", "tfoot",
+        "thead", "tr" */
+        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
+        array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as that of the token (which can only
+            happen for "tbody", "tfoot" and "thead", or, in the innerHTML case),
+            then this is a parse error and the token must be ignored. */
+            if(!$this->elementInScope($token['name'], true)) {
+                // Ignore.
+
+            /* Otherwise, close the cell (see below) and reprocess the current
+            token. */
+            } else {
+                $this->closeCell();
+                return $this->inRow($token);
+            }
+
+        /* Anything else */
+        } else {
+            /* Process the token as if the insertion mode was "in body". */
+            $this->inBody($token);
+        }
+    }
+
+    private function inSelect($token) {
+        /* Handle the token as follows: */
+
+        /* A character token */
+        if($token['type'] === HTML5::CHARACTR) {
+            /* Append the token's character to the current node. */
+            $this->insertText($token['data']);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+        /* A start tag token whose tag name is "option" */
+        } elseif($token['type'] === HTML5::STARTTAG &&
+        $token['name'] === 'option') {
+            /* If the current node is an option element, act as if an end tag
+            with the tag name "option" had been seen. */
+            if(end($this->stack)->nodeName === 'option') {
+                $this->inSelect(array(
+                    'name' => 'option',
+                    'type' => HTML5::ENDTAG
+                ));
+            }
+
+            /* Insert an HTML element for the token. */
+            $this->insertElement($token);
+
+        /* A start tag token whose tag name is "optgroup" */
+        } elseif($token['type'] === HTML5::STARTTAG &&
+        $token['name'] === 'optgroup') {
+            /* If the current node is an option element, act as if an end tag
+            with the tag name "option" had been seen. */
+            if(end($this->stack)->nodeName === 'option') {
+                $this->inSelect(array(
+                    'name' => 'option',
+                    'type' => HTML5::ENDTAG
+                ));
+            }
+
+            /* If the current node is an optgroup element, act as if an end tag
+            with the tag name "optgroup" had been seen. */
+            if(end($this->stack)->nodeName === 'optgroup') {
+                $this->inSelect(array(
+                    'name' => 'optgroup',
+                    'type' => HTML5::ENDTAG
+                ));
+            }
+
+            /* Insert an HTML element for the token. */
+            $this->insertElement($token);
+
+        /* An end tag token whose tag name is "optgroup" */
+        } elseif($token['type'] === HTML5::ENDTAG &&
+        $token['name'] === 'optgroup') {
+            /* First, if the current node is an option element, and the node
+            immediately before it in the stack of open elements is an optgroup
+            element, then act as if an end tag with the tag name "option" had
+            been seen. */
+            $elements_in_stack = count($this->stack);
+
+            if($this->stack[$elements_in_stack - 1]->nodeName === 'option' &&
+            $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup') {
+                $this->inSelect(array(
+                    'name' => 'option',
+                    'type' => HTML5::ENDTAG
+                ));
+            }
+
+            /* If the current node is an optgroup element, then pop that node
+            from the stack of open elements. Otherwise, this is a parse error,
+            ignore the token. */
+            if($this->stack[$elements_in_stack - 1] === 'optgroup') {
+                array_pop($this->stack);
+            }
+
+        /* An end tag token whose tag name is "option" */
+        } elseif($token['type'] === HTML5::ENDTAG &&
+        $token['name'] === 'option') {
+            /* If the current node is an option element, then pop that node
+            from the stack of open elements. Otherwise, this is a parse error,
+            ignore the token. */
+            if(end($this->stack)->nodeName === 'option') {
+                array_pop($this->stack);
+            }
+
+        /* An end tag whose tag name is "select" */
+        } elseif($token['type'] === HTML5::ENDTAG &&
+        $token['name'] === 'select') {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. (innerHTML case) */
+            if(!$this->elementInScope($token['name'], true)) {
+                // w/e
+
+            /* Otherwise: */
+            } else {
+                /* Pop elements from the stack of open elements until a select
+                element has been popped from the stack. */
+                while(true) {
+                    $current = end($this->stack)->nodeName;
+                    array_pop($this->stack);
+
+                    if($current === 'select') {
+                        break;
+                    }
+                }
+
+                /* Reset the insertion mode appropriately. */
+                $this->resetInsertionMode();
+            }
+
+        /* A start tag whose tag name is "select" */
+        } elseif($token['name'] === 'select' &&
+        $token['type'] === HTML5::STARTTAG) {
+            /* Parse error. Act as if the token had been an end tag with the
+            tag name "select" instead. */
+            $this->inSelect(array(
+                'name' => 'select',
+                'type' => HTML5::ENDTAG
+            ));
+
+        /* An end tag whose tag name is one of: "caption", "table", "tbody",
+        "tfoot", "thead", "tr", "td", "th" */
+        } elseif(in_array($token['name'], array('caption', 'table', 'tbody',
+        'tfoot', 'thead', 'tr', 'td', 'th')) && $token['type'] === HTML5::ENDTAG) {
+            /* Parse error. */
+            // w/e
+
+            /* If the stack of open elements has an element in table scope with
+            the same tag name as that of the token, then act as if an end tag
+            with the tag name "select" had been seen, and reprocess the token.
+            Otherwise, ignore the token. */
+            if($this->elementInScope($token['name'], true)) {
+                $this->inSelect(array(
+                    'name' => 'select',
+                    'type' => HTML5::ENDTAG
+                ));
+
+                $this->mainPhase($token);
+            }
+
+        /* Anything else */
+        } else {
+            /* Parse error. Ignore the token. */
+        }
+    }
+
+    private function afterBody($token) {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Process the token as it would be processed if the insertion mode
+            was "in body". */
+            $this->inBody($token);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the first element in the stack of open
+            elements (the html element), with the data attribute set to the
+            data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            $this->stack[0]->appendChild($comment);
+
+        /* An end tag with the tag name "html" */
+        } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') {
+            /* If the parser was originally created in order to handle the
+            setting of an element's innerHTML attribute, this is a parse error;
+            ignore the token. (The element will be an html element in this
+            case.) (innerHTML case) */
+
+            /* Otherwise, switch to the trailing end phase. */
+            $this->phase = self::END_PHASE;
+
+        /* Anything else */
+        } else {
+            /* Parse error. Set the insertion mode to "in body" and reprocess
+            the token. */
+            $this->mode = self::IN_BODY;
+            return $this->inBody($token);
+        }
+    }
+
+    private function inFrameset($token) {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
+        if($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+        /* A start tag with the tag name "frameset" */
+        } elseif($token['name'] === 'frameset' &&
+        $token['type'] === HTML5::STARTTAG) {
+            $this->insertElement($token);
+
+        /* An end tag with the tag name "frameset" */
+        } elseif($token['name'] === 'frameset' &&
+        $token['type'] === HTML5::ENDTAG) {
+            /* If the current node is the root html element, then this is a
+            parse error; ignore the token. (innerHTML case) */
+            if(end($this->stack)->nodeName === 'html') {
+                // Ignore
+
+            } else {
+                /* Otherwise, pop the current node from the stack of open
+                elements. */
+                array_pop($this->stack);
+
+                /* If the parser was not originally created in order to handle
+                the setting of an element's innerHTML attribute (innerHTML case),
+                and the current node is no longer a frameset element, then change
+                the insertion mode to "after frameset". */
+                $this->mode = self::AFTR_FRAME;
+            }
+
+        /* A start tag with the tag name "frame" */
+        } elseif($token['name'] === 'frame' &&
+        $token['type'] === HTML5::STARTTAG) {
+            /* Insert an HTML element for the token. */
+            $this->insertElement($token);
+
+            /* Immediately pop the current node off the stack of open elements. */
+            array_pop($this->stack);
+
+        /* A start tag with the tag name "noframes" */
+        } elseif($token['name'] === 'noframes' &&
+        $token['type'] === HTML5::STARTTAG) {
+            /* Process the token as if the insertion mode had been "in body". */
+            $this->inBody($token);
+
+        /* Anything else */
+        } else {
+            /* Parse error. Ignore the token. */
+        }
+    }
+
+    private function afterFrameset($token) {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
+        if($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+        /* An end tag with the tag name "html" */
+        } elseif($token['name'] === 'html' &&
+        $token['type'] === HTML5::ENDTAG) {
+            /* Switch to the trailing end phase. */
+            $this->phase = self::END_PHASE;
+
+        /* A start tag with the tag name "noframes" */
+        } elseif($token['name'] === 'noframes' &&
+        $token['type'] === HTML5::STARTTAG) {
+            /* Process the token as if the insertion mode had been "in body". */
+            $this->inBody($token);
+
+        /* Anything else */
+        } else {
+            /* Parse error. Ignore the token. */
+        }
+    }
+
+    private function trailingEndPhase($token) {
+        /* After the main phase, as each token is emitted from the tokenisation
+        stage, it must be processed as described in this section. */
+
+        /* A DOCTYPE token */
+        if($token['type'] === HTML5::DOCTYPE) {
+            // Parse error. Ignore the token.
+
+        /* A comment token */
+        } elseif($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the Document object with the data
+            attribute set to the data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            $this->dom->appendChild($comment);
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        } elseif($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+            /* Process the token as it would be processed in the main phase. */
+            $this->mainPhase($token);
+
+        /* A character token that is not one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE. Or a start tag token. Or an end tag token. */
+        } elseif(($token['type'] === HTML5::CHARACTR &&
+        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
+        $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG) {
+            /* Parse error. Switch back to the main phase and reprocess the
+            token. */
+            $this->phase = self::MAIN_PHASE;
+            return $this->mainPhase($token);
+
+        /* An end-of-file token */
+        } elseif($token['type'] === HTML5::EOF) {
+            /* OMG DONE!! */
+        }
+    }
+
+    private function insertElement($token, $append = true, $check = false) {
+        // Proprietary workaround for libxml2's limitations with tag names
+        if ($check) {
+            // Slightly modified HTML5 tag-name modification,
+            // removing anything that's not an ASCII letter, digit, or hyphen
+            $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']);
+            // Remove leading hyphens and numbers
+            $token['name'] = ltrim($token['name'], '-0..9');
+            // In theory, this should ever be needed, but just in case
+            if ($token['name'] === '') $token['name'] = 'span'; // arbitrary generic choice
+        }
+        
+        $el = $this->dom->createElement($token['name']);
+
+        foreach($token['attr'] as $attr) {
+            if(!$el->hasAttribute($attr['name'])) {
+                $el->setAttribute($attr['name'], $attr['value']);
+            }
+        }
+
+        $this->appendToRealParent($el);
+        $this->stack[] = $el;
+
+        return $el;
+    }
+
+    private function insertText($data) {
+        $text = $this->dom->createTextNode($data);
+        $this->appendToRealParent($text);
+    }
+
+    private function insertComment($data) {
+        $comment = $this->dom->createComment($data);
+        $this->appendToRealParent($comment);
+    }
+
+    private function appendToRealParent($node) {
+        if($this->foster_parent === null) {
+            end($this->stack)->appendChild($node);
+
+        } elseif($this->foster_parent !== null) {
+            /* If the foster parent element is the parent element of the
+            last table element in the stack of open elements, then the new
+            node must be inserted immediately before the last table element
+            in the stack of open elements in the foster parent element;
+            otherwise, the new node must be appended to the foster parent
+            element. */
+            for($n = count($this->stack) - 1; $n >= 0; $n--) {
+                if($this->stack[$n]->nodeName === 'table' &&
+                $this->stack[$n]->parentNode !== null) {
+                    $table = $this->stack[$n];
+                    break;
+                }
+            }
+
+            if(isset($table) && $this->foster_parent->isSameNode($table->parentNode))
+                $this->foster_parent->insertBefore($node, $table);
+            else
+                $this->foster_parent->appendChild($node);
+
+            $this->foster_parent = null;
+        }
+    }
+
+    private function elementInScope($el, $table = false) {
+        if(is_array($el)) {
+            foreach($el as $element) {
+                if($this->elementInScope($element, $table)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        $leng = count($this->stack);
+
+        for($n = 0; $n < $leng; $n++) {
+            /* 1. Initialise node to be the current node (the bottommost node of
+            the stack). */
+            $node = $this->stack[$leng - 1 - $n];
+
+            if($node->tagName === $el) {
+                /* 2. If node is the target node, terminate in a match state. */
+                return true;
+
+            } elseif($node->tagName === 'table') {
+                /* 3. Otherwise, if node is a table element, terminate in a failure
+                state. */
+                return false;
+
+            } elseif($table === true && in_array($node->tagName, array('caption', 'td',
+            'th', 'button', 'marquee', 'object'))) {
+                /* 4. Otherwise, if the algorithm is the "has an element in scope"
+                variant (rather than the "has an element in table scope" variant),
+                and node is one of the following, terminate in a failure state. */
+                return false;
+
+            } elseif($node === $node->ownerDocument->documentElement) {
+                /* 5. Otherwise, if node is an html element (root element), terminate
+                in a failure state. (This can only happen if the node is the topmost
+                node of the    stack of open elements, and prevents the next step from
+                being invoked if there are no more elements in the stack.) */
+                return false;
+            }
+
+            /* Otherwise, set node to the previous entry in the stack of open
+            elements and return to step 2. (This will never fail, since the loop
+            will always terminate in the previous step if the top of the stack
+            is reached.) */
+        }
+    }
+
+    private function reconstructActiveFormattingElements() {
+        /* 1. If there are no entries in the list of active formatting elements,
+        then there is nothing to reconstruct; stop this algorithm. */
+        $formatting_elements = count($this->a_formatting);
+
+        if($formatting_elements === 0) {
+            return false;
+        }
+
+        /* 3. Let entry be the last (most recently added) element in the list
+        of active formatting elements. */
+        $entry = end($this->a_formatting);
+
+        /* 2. If the last (most recently added) entry in the list of active
+        formatting elements is a marker, or if it is an element that is in the
+        stack of open elements, then there is nothing to reconstruct; stop this
+        algorithm. */
+        if($entry === self::MARKER || in_array($entry, $this->stack, true)) {
+            return false;
+        }
+
+        for($a = $formatting_elements - 1; $a >= 0; true) {
+            /* 4. If there are no entries before entry in the list of active
+            formatting elements, then jump to step 8. */
+            if($a === 0) {
+                $step_seven = false;
+                break;
+            }
+
+            /* 5. Let entry be the entry one earlier than entry in the list of
+            active formatting elements. */
+            $a--;
+            $entry = $this->a_formatting[$a];
+
+            /* 6. If entry is neither a marker nor an element that is also in
+            thetack of open elements, go to step 4. */
+            if($entry === self::MARKER || in_array($entry, $this->stack, true)) {
+                break;
+            }
+        }
+
+        while(true) {
+            /* 7. Let entry be the element one later than entry in the list of
+            active formatting elements. */
+            if(isset($step_seven) && $step_seven === true) {
+                $a++;
+                $entry = $this->a_formatting[$a];
+            }
+
+            /* 8. Perform a shallow clone of the element entry to obtain clone. */
+            $clone = $entry->cloneNode();
+
+            /* 9. Append clone to the current node and push it onto the stack
+            of open elements  so that it is the new current node. */
+            end($this->stack)->appendChild($clone);
+            $this->stack[] = $clone;
+
+            /* 10. Replace the entry for entry in the list with an entry for
+            clone. */
+            $this->a_formatting[$a] = $clone;
+
+            /* 11. If the entry for clone in the list of active formatting
+            elements is not the last entry in the list, return to step 7. */
+            if(end($this->a_formatting) !== $clone) {
+                $step_seven = true;
+            } else {
+                break;
+            }
+        }
+    }
+
+    private function clearTheActiveFormattingElementsUpToTheLastMarker() {
+        /* When the steps below require the UA to clear the list of active
+        formatting elements up to the last marker, the UA must perform the
+        following steps: */
+
+        while(true) {
+            /* 1. Let entry be the last (most recently added) entry in the list
+            of active formatting elements. */
+            $entry = end($this->a_formatting);
+
+            /* 2. Remove entry from the list of active formatting elements. */
+            array_pop($this->a_formatting);
+
+            /* 3. If entry was a marker, then stop the algorithm at this point.
+            The list has been cleared up to the last marker. */
+            if($entry === self::MARKER) {
+                break;
+            }
+        }
+    }
+
+    private function generateImpliedEndTags($exclude = array()) {
+        /* When the steps below require the UA to generate implied end tags,
+        then, if the current node is a dd element, a dt element, an li element,
+        a p element, a td element, a th  element, or a tr element, the UA must
+        act as if an end tag with the respective tag name had been seen and
+        then generate implied end tags again. */
+        $node = end($this->stack);
+        $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);
+
+        while(in_array(end($this->stack)->nodeName, $elements)) {
+            array_pop($this->stack);
+        }
+    }
+
+    private function getElementCategory($node) {
+        $name = $node->tagName;
+        if(in_array($name, $this->special))
+            return self::SPECIAL;
+
+        elseif(in_array($name, $this->scoping))
+            return self::SCOPING;
+
+        elseif(in_array($name, $this->formatting))
+            return self::FORMATTING;
+
+        else
+            return self::PHRASING;
+    }
+
+    private function clearStackToTableContext($elements) {
+        /* When the steps above require the UA to clear the stack back to a
+        table context, it means that the UA must, while the current node is not
+        a table element or an html element, pop elements from the stack of open
+        elements. If this causes any elements to be popped from the stack, then
+        this is a parse error. */
+        while(true) {
+            $node = end($this->stack)->nodeName;
+
+            if(in_array($node, $elements)) {
+                break;
+            } else {
+                array_pop($this->stack);
+            }
+        }
+    }
+
+    private function resetInsertionMode() {
+        /* 1. Let last be false. */
+        $last = false;
+        $leng = count($this->stack);
+
+        for($n = $leng - 1; $n >= 0; $n--) {
+            /* 2. Let node be the last node in the stack of open elements. */
+            $node = $this->stack[$n];
+
+            /* 3. If node is the first node in the stack of open elements, then
+            set last to true. If the element whose innerHTML  attribute is being
+            set is neither a td  element nor a th element, then set node to the
+            element whose innerHTML  attribute is being set. (innerHTML  case) */
+            if($this->stack[0]->isSameNode($node)) {
+                $last = true;
+            }
+
+            /* 4. If node is a select element, then switch the insertion mode to
+            "in select" and abort these steps. (innerHTML case) */
+            if($node->nodeName === 'select') {
+                $this->mode = self::IN_SELECT;
+                break;
+
+            /* 5. If node is a td or th element, then switch the insertion mode
+            to "in cell" and abort these steps. */
+            } elseif($node->nodeName === 'td' || $node->nodeName === 'th') {
+                $this->mode = self::IN_CELL;
+                break;
+
+            /* 6. If node is a tr element, then switch the insertion mode to
+            "in    row" and abort these steps. */
+            } elseif($node->nodeName === 'tr') {
+                $this->mode = self::IN_ROW;
+                break;
+
+            /* 7. If node is a tbody, thead, or tfoot element, then switch the
+            insertion mode to "in table body" and abort these steps. */
+            } elseif(in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) {
+                $this->mode = self::IN_TBODY;
+                break;
+
+            /* 8. If node is a caption element, then switch the insertion mode
+            to "in caption" and abort these steps. */
+            } elseif($node->nodeName === 'caption') {
+                $this->mode = self::IN_CAPTION;
+                break;
+
+            /* 9. If node is a colgroup element, then switch the insertion mode
+            to "in column group" and abort these steps. (innerHTML case) */
+            } elseif($node->nodeName === 'colgroup') {
+                $this->mode = self::IN_CGROUP;
+                break;
+
+            /* 10. If node is a table element, then switch the insertion mode
+            to "in table" and abort these steps. */
+            } elseif($node->nodeName === 'table') {
+                $this->mode = self::IN_TABLE;
+                break;
+
+            /* 11. If node is a head element, then switch the insertion mode
+            to "in body" ("in body"! not "in head"!) and abort these steps.
+            (innerHTML case) */
+            } elseif($node->nodeName === 'head') {
+                $this->mode = self::IN_BODY;
+                break;
+
+            /* 12. If node is a body element, then switch the insertion mode to
+            "in body" and abort these steps. */
+            } elseif($node->nodeName === 'body') {
+                $this->mode = self::IN_BODY;
+                break;
+
+            /* 13. If node is a frameset element, then switch the insertion
+            mode to "in frameset" and abort these steps. (innerHTML case) */
+            } elseif($node->nodeName === 'frameset') {
+                $this->mode = self::IN_FRAME;
+                break;
+
+            /* 14. If node is an html element, then: if the head element
+            pointer is null, switch the insertion mode to "before head",
+            otherwise, switch the insertion mode to "after head". In either
+            case, abort these steps. (innerHTML case) */
+            } elseif($node->nodeName === 'html') {
+                $this->mode = ($this->head_pointer === null)
+                    ? self::BEFOR_HEAD
+                    : self::AFTER_HEAD;
+
+                break;
+
+            /* 15. If last is true, then set the insertion mode to "in body"
+            and    abort these steps. (innerHTML case) */
+            } elseif($last) {
+                $this->mode = self::IN_BODY;
+                break;
+            }
+        }
+    }
+
+    private function closeCell() {
+        /* If the stack of open elements has a td or th element in table scope,
+        then act as if an end tag token with that tag name had been seen. */
+        foreach(array('td', 'th') as $cell) {
+            if($this->elementInScope($cell, true)) {
+                $this->inCell(array(
+                    'name' => $cell,
+                    'type' => HTML5::ENDTAG
+                ));
+
+                break;
+            }
+        }
+    }
+
+    public function save() {
+        return $this->dom;
+    }
+}
+?>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/DirectLex.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/DirectLex.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/DirectLex.php (revision 21)
@@ -0,0 +1,468 @@
+<?php
+
+/**
+ * Our in-house implementation of a parser.
+ * 
+ * A pure PHP parser, DirectLex has absolutely no dependencies, making
+ * it a reasonably good default for PHP4.  Written with efficiency in mind,
+ * it can be four times faster than HTMLPurifier_Lexer_PEARSax3, although it
+ * pales in comparison to HTMLPurifier_Lexer_DOMLex.
+ * 
+ * @todo Reread XML spec and document differences.
+ */
+class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
+{
+    
+    /**
+     * Whitespace characters for str(c)spn.
+     */
+    protected $_whitespace = "\x20\x09\x0D\x0A";
+    
+    /**
+     * Callback function for script CDATA fudge
+     * @param $matches, in form of array(opening tag, contents, closing tag)
+     */
+    protected function scriptCallback($matches) {
+        return $matches[1] . htmlspecialchars($matches[2], ENT_COMPAT, 'UTF-8') . $matches[3];
+    }
+    
+    public function tokenizeHTML($html, $config, $context) {
+        
+        // special normalization for script tags without any armor
+        // our "armor" heurstic is a < sign any number of whitespaces after
+        // the first script tag
+        if ($config->get('HTML', 'Trusted')) {
+            $html = preg_replace_callback('#(<script[^>]*>)(\s*[^<].+?)(</script>)#si',
+                array($this, 'scriptCallback'), $html);
+        }
+        
+        $html = $this->normalize($html, $config, $context);
+        
+        $cursor = 0; // our location in the text
+        $inside_tag = false; // whether or not we're parsing the inside of a tag
+        $array = array(); // result array
+        
+        $maintain_line_numbers = $config->get('Core', 'MaintainLineNumbers');
+        
+        if ($maintain_line_numbers === null) {
+            // automatically determine line numbering by checking
+            // if error collection is on
+            $maintain_line_numbers = $config->get('Core', 'CollectErrors');
+        }
+        
+        if ($maintain_line_numbers) $current_line = 1;
+        else $current_line = false;
+        $context->register('CurrentLine', $current_line);
+        $nl = "\n";
+        // how often to manually recalculate. This will ALWAYS be right,
+        // but it's pretty wasteful. Set to 0 to turn off
+        $synchronize_interval = $config->get('Core', 'DirectLexLineNumberSyncInterval'); 
+        
+        $e = false;
+        if ($config->get('Core', 'CollectErrors')) {
+            $e =& $context->get('ErrorCollector');
+        }
+        
+        // for testing synchronization
+        $loops = 0;
+        
+        while(++$loops) {
+            
+            // recalculate lines
+            if (
+                $maintain_line_numbers && // line number tracking is on
+                $synchronize_interval &&  // synchronization is on
+                $cursor > 0 &&            // cursor is further than zero
+                $loops % $synchronize_interval === 0 // time to synchronize!
+            ) {
+                $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor);
+            }
+            
+            $position_next_lt = strpos($html, '<', $cursor);
+            $position_next_gt = strpos($html, '>', $cursor);
+            
+            // triggers on "<b>asdf</b>" but not "asdf <b></b>"
+            // special case to set up context
+            if ($position_next_lt === $cursor) {
+                $inside_tag = true;
+                $cursor++;
+            }
+            
+            if (!$inside_tag && $position_next_lt !== false) {
+                // We are not inside tag and there still is another tag to parse
+                $token = new
+                    HTMLPurifier_Token_Text(
+                        $this->parseData(
+                            substr(
+                                $html, $cursor, $position_next_lt - $cursor
+                            )
+                        )
+                    );
+                if ($maintain_line_numbers) {
+                    $token->line = $current_line;
+                    $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor);
+                }
+                $array[] = $token;
+                $cursor  = $position_next_lt + 1;
+                $inside_tag = true;
+                continue;
+            } elseif (!$inside_tag) {
+                // We are not inside tag but there are no more tags
+                // If we're already at the end, break
+                if ($cursor === strlen($html)) break;
+                // Create Text of rest of string
+                $token = new
+                    HTMLPurifier_Token_Text(
+                        $this->parseData(
+                            substr(
+                                $html, $cursor
+                            )
+                        )
+                    );
+                if ($maintain_line_numbers) $token->line = $current_line;
+                $array[] = $token;
+                break;
+            } elseif ($inside_tag && $position_next_gt !== false) {
+                // We are in tag and it is well formed
+                // Grab the internals of the tag
+                $strlen_segment = $position_next_gt - $cursor;
+                
+                if ($strlen_segment < 1) {
+                    // there's nothing to process!
+                    $token = new HTMLPurifier_Token_Text('<');
+                    $cursor++;
+                    continue;
+                }
+                
+                $segment = substr($html, $cursor, $strlen_segment);
+                
+                if ($segment === false) {
+                    // somehow, we attempted to access beyond the end of
+                    // the string, defense-in-depth, reported by Nate Abele
+                    break;
+                }
+                
+                // Check if it's a comment
+                if (
+                    substr($segment, 0, 3) === '!--'
+                ) {
+                    // re-determine segment length, looking for -->
+                    $position_comment_end = strpos($html, '-->', $cursor);
+                    if ($position_comment_end === false) {
+                        // uh oh, we have a comment that extends to
+                        // infinity. Can't be helped: set comment
+                        // end position to end of string
+                        if ($e) $e->send(E_WARNING, 'Lexer: Unclosed comment');
+                        $position_comment_end = strlen($html);
+                        $end = true;
+                    } else {
+                        $end = false;
+                    }
+                    $strlen_segment = $position_comment_end - $cursor;
+                    $segment = substr($html, $cursor, $strlen_segment);
+                    $token = new
+                        HTMLPurifier_Token_Comment(
+                            substr(
+                                $segment, 3, $strlen_segment - 3
+                            )
+                        );
+                    if ($maintain_line_numbers) {
+                        $token->line = $current_line;
+                        $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment);
+                    }
+                    $array[] = $token;
+                    $cursor = $end ? $position_comment_end : $position_comment_end + 3;
+                    $inside_tag = false;
+                    continue;
+                }
+                
+                // Check if it's an end tag
+                $is_end_tag = (strpos($segment,'/') === 0);
+                if ($is_end_tag) {
+                    $type = substr($segment, 1);
+                    $token = new HTMLPurifier_Token_End($type);
+                    if ($maintain_line_numbers) {
+                        $token->line = $current_line;
+                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
+                    }
+                    $array[] = $token;
+                    $inside_tag = false;
+                    $cursor = $position_next_gt + 1;
+                    continue;
+                }
+                
+                // Check leading character is alnum, if not, we may
+                // have accidently grabbed an emoticon. Translate into
+                // text and go our merry way
+                if (!ctype_alpha($segment[0])) {
+                    // XML:  $segment[0] !== '_' && $segment[0] !== ':'
+                    if ($e) $e->send(E_NOTICE, 'Lexer: Unescaped lt');
+                    $token = new
+                        HTMLPurifier_Token_Text(
+                            '<' .
+                            $this->parseData(
+                                $segment
+                            ) . 
+                            '>'
+                        );
+                    if ($maintain_line_numbers) {
+                        $token->line = $current_line;
+                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
+                    }
+                    $array[] = $token;
+                    $cursor = $position_next_gt + 1;
+                    $inside_tag = false;
+                    continue;
+                }
+                
+                // Check if it is explicitly self closing, if so, remove
+                // trailing slash. Remember, we could have a tag like <br>, so
+                // any later token processing scripts must convert improperly
+                // classified EmptyTags from StartTags.
+                $is_self_closing = (strrpos($segment,'/') === $strlen_segment-1);
+                if ($is_self_closing) {
+                    $strlen_segment--;
+                    $segment = substr($segment, 0, $strlen_segment);
+                }
+                
+                // Check if there are any attributes
+                $position_first_space = strcspn($segment, $this->_whitespace);
+                
+                if ($position_first_space >= $strlen_segment) {
+                    if ($is_self_closing) {
+                        $token = new HTMLPurifier_Token_Empty($segment);
+                    } else {
+                        $token = new HTMLPurifier_Token_Start($segment);
+                    }
+                    if ($maintain_line_numbers) {
+                        $token->line = $current_line;
+                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
+                    }
+                    $array[] = $token;
+                    $inside_tag = false;
+                    $cursor = $position_next_gt + 1;
+                    continue;
+                }
+                
+                // Grab out all the data
+                $type = substr($segment, 0, $position_first_space);
+                $attribute_string =
+                    trim(
+                        substr(
+                            $segment, $position_first_space
+                        )
+                    );
+                if ($attribute_string) {
+                    $attr = $this->parseAttributeString(
+                                    $attribute_string
+                                  , $config, $context
+                              );
+                } else {
+                    $attr = array();
+                }
+                
+                if ($is_self_closing) {
+                    $token = new HTMLPurifier_Token_Empty($type, $attr);
+                } else {
+                    $token = new HTMLPurifier_Token_Start($type, $attr);
+                }
+                if ($maintain_line_numbers) {
+                    $token->line = $current_line;
+                    $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
+                }
+                $array[] = $token;
+                $cursor = $position_next_gt + 1;
+                $inside_tag = false;
+                continue;
+            } else {
+                // inside tag, but there's no ending > sign
+                if ($e) $e->send(E_WARNING, 'Lexer: Missing gt');
+                $token = new
+                    HTMLPurifier_Token_Text(
+                        '<' .
+                        $this->parseData(
+                            substr($html, $cursor)
+                        )
+                    );
+                if ($maintain_line_numbers) $token->line = $current_line;
+                // no cursor scroll? Hmm...
+                $array[] = $token;
+                break;
+            }
+            break;
+        }
+        
+        $context->destroy('CurrentLine');
+        return $array;
+    }
+    
+    /**
+     * PHP 5.0.x compatible substr_count that implements offset and length
+     */
+    protected function substrCount($haystack, $needle, $offset, $length) {
+        static $oldVersion;
+        if ($oldVersion === null) {
+            $oldVersion = version_compare(PHP_VERSION, '5.1', '<');
+        }
+        if ($oldVersion) {
+            $haystack = substr($haystack, $offset, $length);
+            return substr_count($haystack, $needle);
+        } else {
+            return substr_count($haystack, $needle, $offset, $length);
+        }
+    }
+    
+    /**
+     * Takes the inside of an HTML tag and makes an assoc array of attributes.
+     * 
+     * @param $string Inside of tag excluding name.
+     * @returns Assoc array of attributes.
+     */
+    public function parseAttributeString($string, $config, $context) {
+        $string = (string) $string; // quick typecast
+        
+        if ($string == '') return array(); // no attributes
+        
+        $e = false;
+        if ($config->get('Core', 'CollectErrors')) {
+            $e =& $context->get('ErrorCollector');
+        }
+        
+        // let's see if we can abort as quickly as possible
+        // one equal sign, no spaces => one attribute
+        $num_equal = substr_count($string, '=');
+        $has_space = strpos($string, ' ');
+        if ($num_equal === 0 && !$has_space) {
+            // bool attribute
+            return array($string => $string);
+        } elseif ($num_equal === 1 && !$has_space) {
+            // only one attribute
+            list($key, $quoted_value) = explode('=', $string);
+            $quoted_value = trim($quoted_value);
+            if (!$key) {
+                if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key');
+                return array();
+            }
+            if (!$quoted_value) return array($key => '');
+            $first_char = @$quoted_value[0];
+            $last_char  = @$quoted_value[strlen($quoted_value)-1];
+            
+            $same_quote = ($first_char == $last_char);
+            $open_quote = ($first_char == '"' || $first_char == "'");
+            
+            if ( $same_quote && $open_quote) {
+                // well behaved
+                $value = substr($quoted_value, 1, strlen($quoted_value) - 2);
+            } else {
+                // not well behaved
+                if ($open_quote) {
+                    if ($e) $e->send(E_ERROR, 'Lexer: Missing end quote');
+                    $value = substr($quoted_value, 1);
+                } else {
+                    $value = $quoted_value;
+                }
+            }
+            if ($value === false) $value = '';
+            return array($key => $value);
+        }
+        
+        // setup loop environment
+        $array  = array(); // return assoc array of attributes
+        $cursor = 0; // current position in string (moves forward)
+        $size   = strlen($string); // size of the string (stays the same)
+        
+        // if we have unquoted attributes, the parser expects a terminating
+        // space, so let's guarantee that there's always a terminating space.
+        $string .= ' ';
+        
+        while(true) {
+            
+            if ($cursor >= $size) {
+                break;
+            }
+            
+            $cursor += ($value = strspn($string, $this->_whitespace, $cursor));
+            // grab the key
+            
+            $key_begin = $cursor; //we're currently at the start of the key
+            
+            // scroll past all characters that are the key (not whitespace or =)
+            $cursor += strcspn($string, $this->_whitespace . '=', $cursor);
+            
+            $key_end = $cursor; // now at the end of the key
+            
+            $key = substr($string, $key_begin, $key_end - $key_begin);
+            
+            if (!$key) {
+                if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key');
+                $cursor += strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop
+                continue; // empty key
+            }
+            
+            // scroll past all whitespace
+            $cursor += strspn($string, $this->_whitespace, $cursor);
+            
+            if ($cursor >= $size) {
+                $array[$key] = $key;
+                break;
+            }
+            
+            // if the next character is an equal sign, we've got a regular
+            // pair, otherwise, it's a bool attribute
+            $first_char = @$string[$cursor];
+            
+            if ($first_char == '=') {
+                // key="value"
+                
+                $cursor++;
+                $cursor += strspn($string, $this->_whitespace, $cursor);
+                
+                if ($cursor === false) {
+                    $array[$key] = '';
+                    break;
+                }
+                
+                // we might be in front of a quote right now
+                
+                $char = @$string[$cursor];
+                
+                if ($char == '"' || $char == "'") {
+                    // it's quoted, end bound is $char
+                    $cursor++;
+                    $value_begin = $cursor;
+                    $cursor = strpos($string, $char, $cursor);
+                    $value_end = $cursor;
+                } else {
+                    // it's not quoted, end bound is whitespace
+                    $value_begin = $cursor;
+                    $cursor += strcspn($string, $this->_whitespace, $cursor);
+                    $value_end = $cursor;
+                }
+                
+                // we reached a premature end
+                if ($cursor === false) {
+                    $cursor = $size;
+                    $value_end = $cursor;
+                }
+                
+                $value = substr($string, $value_begin, $value_end - $value_begin);
+                if ($value === false) $value = '';
+                $array[$key] = $this->parseData($value);
+                $cursor++;
+                
+            } else {
+                // boolattr
+                if ($key !== '') {
+                    $array[$key] = $key;
+                } else {
+                    // purely theoretical
+                    if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key');
+                }
+                
+            }
+        }
+        return $array;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/PEARSax3.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/PEARSax3.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/PEARSax3.php (revision 21)
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * Proof-of-concept lexer that uses the PEAR package XML_HTMLSax3 to parse HTML.
+ * 
+ * PEAR, not suprisingly, also has a SAX parser for HTML.  I don't know
+ * very much about implementation, but it's fairly well written.  However, that
+ * abstraction comes at a price: performance. You need to have it installed,
+ * and if the API changes, it might break our adapter. Not sure whether or not
+ * it's UTF-8 aware, but it has some entity parsing trouble (in all areas,
+ * text and attributes).
+ * 
+ * Quite personally, I don't recommend using the PEAR class, and the defaults
+ * don't use it. The unit tests do perform the tests on the SAX parser too, but
+ * whatever it does for poorly formed HTML is up to it.
+ * 
+ * @todo Generalize so that XML_HTMLSax is also supported.
+ * 
+ * @warning Entity-resolution inside attributes is broken.
+ */
+
+class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer
+{
+    
+    /**
+     * Internal accumulator array for SAX parsers.
+     */
+    protected $tokens = array();
+    
+    public function tokenizeHTML($string, $config, $context) {
+        
+        $this->tokens = array();
+        
+        $string = $this->normalize($string, $config, $context);
+        
+        $parser = new XML_HTMLSax3();
+        $parser->set_object($this);
+        $parser->set_element_handler('openHandler','closeHandler');
+        $parser->set_data_handler('dataHandler');
+        $parser->set_escape_handler('escapeHandler');
+        
+        // doesn't seem to work correctly for attributes
+        $parser->set_option('XML_OPTION_ENTITIES_PARSED', 1);
+        
+        $parser->parse($string);
+        
+        return $this->tokens;
+        
+    }
+    
+    /**
+     * Open tag event handler, interface is defined by PEAR package.
+     */
+    public function openHandler(&$parser, $name, $attrs, $closed) {
+        // entities are not resolved in attrs
+        foreach ($attrs as $key => $attr) {
+            $attrs[$key] = $this->parseData($attr);
+        }
+        if ($closed) {
+            $this->tokens[] = new HTMLPurifier_Token_Empty($name, $attrs);
+        } else {
+            $this->tokens[] = new HTMLPurifier_Token_Start($name, $attrs);
+        }
+        return true;
+    }
+    
+    /**
+     * Close tag event handler, interface is defined by PEAR package.
+     */
+    public function closeHandler(&$parser, $name) {
+        // HTMLSax3 seems to always send empty tags an extra close tag
+        // check and ignore if you see it:
+        // [TESTME] to make sure it doesn't overreach
+        if ($this->tokens[count($this->tokens)-1] instanceof HTMLPurifier_Token_Empty) {
+            return true;
+        }
+        $this->tokens[] = new HTMLPurifier_Token_End($name);
+        return true;
+    }
+    
+    /**
+     * Data event handler, interface is defined by PEAR package.
+     */
+    public function dataHandler(&$parser, $data) {
+        $this->tokens[] = new HTMLPurifier_Token_Text($data);
+        return true;
+    }
+    
+    /**
+     * Escaped text handler, interface is defined by PEAR package.
+     */
+    public function escapeHandler(&$parser, $data) {
+        if (strpos($data, '--') === 0) {
+            $this->tokens[] = new HTMLPurifier_Token_Comment($data);
+        }
+        // CDATA is handled elsewhere, but if it was handled here:
+        //if (strpos($data, '[CDATA[') === 0) {
+        //    $this->tokens[] = new HTMLPurifier_Token_Text(
+        //        substr($data, 7, strlen($data) - 9) );
+        //}
+        return true;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/DOMLex.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/DOMLex.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer/DOMLex.php (revision 21)
@@ -0,0 +1,209 @@
+<?php
+
+/**
+ * Parser that uses PHP 5's DOM extension (part of the core).
+ * 
+ * In PHP 5, the DOM XML extension was revamped into DOM and added to the core.
+ * It gives us a forgiving HTML parser, which we use to transform the HTML
+ * into a DOM, and then into the tokens.  It is blazingly fast (for large
+ * documents, it performs twenty times faster than
+ * HTMLPurifier_Lexer_DirectLex,and is the default choice for PHP 5. 
+ * 
+ * @note Any empty elements will have empty tokens associated with them, even if
+ * this is prohibited by the spec. This is cannot be fixed until the spec
+ * comes into play.
+ * 
+ * @note PHP's DOM extension does not actually parse any entities, we use
+ *       our own function to do that.
+ * 
+ * @warning DOM tends to drop whitespace, which may wreak havoc on indenting.
+ *          If this is a huge problem, due to the fact that HTML is hand
+ *          edited and you are unable to get a parser cache that caches the
+ *          the output of HTML Purifier while keeping the original HTML lying
+ *          around, you may want to run Tidy on the resulting output or use
+ *          HTMLPurifier_DirectLex
+ */
+
+class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
+{
+    
+    private $factory;
+    
+    public function __construct() {
+        // setup the factory
+        parent::__construct();
+        $this->factory = new HTMLPurifier_TokenFactory();
+    }
+    
+    public function tokenizeHTML($html, $config, $context) {
+        
+        $html = $this->normalize($html, $config, $context);
+        
+        // attempt to armor stray angled brackets that cannot possibly
+        // form tags and thus are probably being used as emoticons
+        if ($config->get('Core', 'AggressivelyFixLt')) {
+            $char = '[^a-z!\/]';
+            $comment = "/<!--(.*?)(-->|\z)/is";
+            $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html);
+            $html = preg_replace("/<($char)/i", '&lt;\\1', $html);
+            $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments
+        }
+        
+        // preprocess html, essential for UTF-8
+        $html = $this->wrapHTML($html, $config, $context);
+        
+        $doc = new DOMDocument();
+        $doc->encoding = 'UTF-8'; // theoretically, the above has this covered
+        
+        set_error_handler(array($this, 'muteErrorHandler'));
+        $doc->loadHTML($html);
+        restore_error_handler();
+        
+        $tokens = array();
+        $this->tokenizeDOM(
+            $doc->getElementsByTagName('html')->item(0)-> // <html>
+                  getElementsByTagName('body')->item(0)-> //   <body>
+                  getElementsByTagName('div')->item(0)    //     <div>
+            , $tokens);
+        return $tokens;
+    }
+    
+    /**
+     * Recursive function that tokenizes a node, putting it into an accumulator.
+     * 
+     * @param $node     DOMNode to be tokenized.
+     * @param $tokens   Array-list of already tokenized tokens.
+     * @param $collect  Says whether or start and close are collected, set to
+     *                  false at first recursion because it's the implicit DIV
+     *                  tag you're dealing with.
+     * @returns Tokens of node appended to previously passed tokens.
+     */
+    protected function tokenizeDOM($node, &$tokens, $collect = false) {
+        
+        // intercept non element nodes. WE MUST catch all of them,
+        // but we're not getting the character reference nodes because
+        // those should have been preprocessed
+        if ($node->nodeType === XML_TEXT_NODE) {
+            $tokens[] = $this->factory->createText($node->data);
+            return;
+        } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) {
+            // undo libxml's special treatment of <script> and <style> tags
+            $last = end($tokens);
+            $data = $node->data;
+            // (note $node->tagname is already normalized)
+            if ($last instanceof HTMLPurifier_Token_Start && ($last->name == 'script' || $last->name == 'style')) {
+                $new_data = trim($data);
+                if (substr($new_data, 0, 4) === '<!--') {
+                    $data = substr($new_data, 4);
+                    if (substr($data, -3) === '-->') {
+                        $data = substr($data, 0, -3);
+                    } else {
+                        // Highly suspicious! Not sure what to do...
+                    }
+                }
+            }
+            $tokens[] = $this->factory->createText($this->parseData($data));
+            return;
+        } elseif ($node->nodeType === XML_COMMENT_NODE) {
+            // this is code is only invoked for comments in script/style in versions
+            // of libxml pre-2.6.28 (regular comments, of course, are still
+            // handled regularly)
+            $tokens[] = $this->factory->createComment($node->data);
+            return;
+        } elseif (
+            // not-well tested: there may be other nodes we have to grab
+            $node->nodeType !== XML_ELEMENT_NODE
+        ) {
+            return;
+        }
+        
+        $attr = $node->hasAttributes() ?
+            $this->transformAttrToAssoc($node->attributes) :
+            array();
+        
+        // We still have to make sure that the element actually IS empty
+        if (!$node->childNodes->length) {
+            if ($collect) {
+                $tokens[] = $this->factory->createEmpty($node->tagName, $attr);
+            }
+        } else {
+            if ($collect) { // don't wrap on first iteration
+                $tokens[] = $this->factory->createStart(
+                    $tag_name = $node->tagName, // somehow, it get's dropped
+                    $attr
+                );
+            }
+            foreach ($node->childNodes as $node) {
+                // remember, it's an accumulator. Otherwise, we'd have
+                // to use array_merge
+                $this->tokenizeDOM($node, $tokens, true);
+            }
+            if ($collect) {
+                $tokens[] = $this->factory->createEnd($tag_name);
+            }
+        }
+        
+    }
+    
+    /**
+     * Converts a DOMNamedNodeMap of DOMAttr objects into an assoc array.
+     * 
+     * @param $attribute_list DOMNamedNodeMap of DOMAttr objects.
+     * @returns Associative array of attributes.
+     */
+    protected function transformAttrToAssoc($node_map) {
+        // NamedNodeMap is documented very well, so we're using undocumented
+        // features, namely, the fact that it implements Iterator and
+        // has a ->length attribute
+        if ($node_map->length === 0) return array();
+        $array = array();
+        foreach ($node_map as $attr) {
+            $array[$attr->name] = $attr->value;
+        }
+        return $array;
+    }
+    
+    /**
+     * An error handler that mutes all errors
+     */
+    public function muteErrorHandler($errno, $errstr) {}
+    
+    /**
+     * Callback function for undoing escaping of stray angled brackets
+     * in comments
+     */
+    public function callbackUndoCommentSubst($matches) {
+        return '<!--' . strtr($matches[1], array('&amp;'=>'&','&lt;'=>'<')) . $matches[2];
+    }
+    
+    /**
+     * Callback function that entity-izes ampersands in comments so that
+     * callbackUndoCommentSubst doesn't clobber them
+     */
+    public function callbackArmorCommentEntities($matches) {
+        return '<!--' . str_replace('&', '&amp;', $matches[1]) . $matches[2];
+    }
+    
+    /**
+     * Wraps an HTML fragment in the necessary HTML
+     */
+    protected function wrapHTML($html, $config, $context) {
+        $def = $config->getDefinition('HTML');
+        $ret = '';
+        
+        if (!empty($def->doctype->dtdPublic) || !empty($def->doctype->dtdSystem)) {
+            $ret .= '<!DOCTYPE html ';
+            if (!empty($def->doctype->dtdPublic)) $ret .= 'PUBLIC "' . $def->doctype->dtdPublic . '" ';
+            if (!empty($def->doctype->dtdSystem)) $ret .= '"' . $def->doctype->dtdSystem . '" ';
+            $ret .= '>';
+        }
+        
+        $ret .= '<html><head>';
+        $ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
+        // No protection if $html contains a stray </div>!
+        $ret .= '</head><body><div>'.$html.'</div></body></html>';
+        return $ret;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Tag.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Tag.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Tag.php (revision 21)
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Abstract class of a tag token (start, end or empty), and its behavior.
+ */
+class HTMLPurifier_Token_Tag extends HTMLPurifier_Token
+{
+    /**
+     * Static bool marker that indicates the class is a tag.
+     * 
+     * This allows us to check objects with <tt>!empty($obj->is_tag)</tt>
+     * without having to use a function call <tt>is_a()</tt>.
+     */
+    public $is_tag = true;
+    
+    /**
+     * The lower-case name of the tag, like 'a', 'b' or 'blockquote'.
+     * 
+     * @note Strictly speaking, XML tags are case sensitive, so we shouldn't
+     * be lower-casing them, but these tokens cater to HTML tags, which are
+     * insensitive.
+     */
+    public $name;
+    
+    /**
+     * Associative array of the tag's attributes.
+     */
+    public $attr = array();
+    
+    /**
+     * Non-overloaded constructor, which lower-cases passed tag name.
+     * 
+     * @param $name String name.
+     * @param $attr Associative array of attributes.
+     */
+    public function __construct($name, $attr = array(), $line = null) {
+        $this->name = ctype_lower($name) ? $name : strtolower($name);
+        foreach ($attr as $key => $value) {
+            // normalization only necessary when key is not lowercase
+            if (!ctype_lower($key)) {
+                $new_key = strtolower($key);
+                if (!isset($attr[$new_key])) {
+                    $attr[$new_key] = $attr[$key];
+                }
+                if ($new_key !== $key) {
+                    unset($attr[$key]);
+                }
+            }
+        }
+        $this->attr = $attr;
+        $this->line = $line;
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Start.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Start.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Start.php (revision 21)
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * Concrete start token class.
+ */
+class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag
+{
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Empty.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Empty.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Empty.php (revision 21)
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * Concrete empty token class.
+ */
+class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag
+{
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Comment.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Comment.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Comment.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Concrete comment token class. Generally will be ignored.
+ */
+class HTMLPurifier_Token_Comment extends HTMLPurifier_Token
+{
+    public $data; /**< Character data within comment. */
+    /**
+     * Transparent constructor.
+     * 
+     * @param $data String comment data.
+     */
+    public function __construct($data, $line = null) {
+        $this->data = $data;
+        $this->line = $line;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Text.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Text.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/Text.php (revision 21)
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Concrete text token class.
+ * 
+ * Text tokens comprise of regular parsed character data (PCDATA) and raw
+ * character data (from the CDATA sections). Internally, their
+ * data is parsed with all entities expanded. Surprisingly, the text token
+ * does have a "tag name" called #PCDATA, which is how the DTD represents it
+ * in permissible child nodes.
+ */
+class HTMLPurifier_Token_Text extends HTMLPurifier_Token
+{
+    
+    public $name = '#PCDATA'; /**< PCDATA tag name compatible with DTD. */
+    public $data; /**< Parsed character data of text. */
+    public $is_whitespace; /**< Bool indicating if node is whitespace. */
+    
+    /**
+     * Constructor, accepts data and determines if it is whitespace.
+     * 
+     * @param $data String parsed character data.
+     */
+    public function __construct($data, $line = null) {
+        $this->data = $data;
+        $this->is_whitespace = ctype_space($data);
+        $this->line = $line;
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/End.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/End.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token/End.php (revision 21)
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * Concrete end token class.
+ * 
+ * @warning This class accepts attributes even though end tags cannot. This
+ * is for optimization reasons, as under normal circumstances, the Lexers
+ * do not pass attributes.
+ */
+class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag
+{
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.php (revision 21)
@@ -0,0 +1,322 @@
+<?php
+
+class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
+{
+    
+    /**
+     * Printers for specific fields
+     */
+    protected $fields = array();
+    
+    /**
+     * Documentation URL, can have fragment tagged on end
+     */
+    protected $docURL;
+    
+    /**
+     * Name of form element to stuff config in
+     */
+    protected $name;
+    
+    /**
+     * Whether or not to compress directive names, clipping them off
+     * after a certain amount of letters. False to disable or integer letters
+     * before clipping.
+     */
+    protected $compress = false;
+    
+    /**
+     * @param $name Form element name for directives to be stuffed into
+     * @param $doc_url String documentation URL, will have fragment tagged on
+     * @param $compress Integer max length before compressing a directive name, set to false to turn off
+     */
+    public function __construct(
+        $name, $doc_url = null, $compress = false
+    ) {
+        parent::__construct();
+        $this->docURL = $doc_url;
+        $this->name   = $name;
+        $this->compress = $compress;
+        // initialize sub-printers
+        $this->fields['default']    = new HTMLPurifier_Printer_ConfigForm_default();
+        $this->fields['bool']       = new HTMLPurifier_Printer_ConfigForm_bool();
+    }
+    
+    /**
+     * Sets default column and row size for textareas in sub-printers
+     * @param $cols Integer columns of textarea, null to use default
+     * @param $rows Integer rows of textarea, null to use default
+     */
+    public function setTextareaDimensions($cols = null, $rows = null) {
+        if ($cols) $this->fields['default']->cols = $cols;
+        if ($rows) $this->fields['default']->rows = $rows;
+    }
+    
+    /**
+     * Retrieves styling, in case it is not accessible by webserver
+     */
+    public static function getCSS() {
+        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
+    }
+    
+    /**
+     * Retrieves JavaScript, in case it is not accessible by webserver
+     */
+    public static function getJavaScript() {
+        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
+    }
+    
+    /**
+     * Returns HTML output for a configuration form
+     * @param $config Configuration object of current form state
+     * @param $allowed Optional namespace(s) and directives to restrict form to.
+     */
+    public function render($config, $allowed = true, $render_controls = true) {
+        $this->config = $config;
+        $this->prepareGenerator($config);
+        
+        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed);
+        $all = array();
+        foreach ($allowed as $key) {
+            list($ns, $directive) = $key;
+            $all[$ns][$directive] = $config->get($ns, $directive);
+        }
+        
+        $ret = '';
+        $ret .= $this->start('table', array('class' => 'hp-config'));
+        $ret .= $this->start('thead');
+        $ret .= $this->start('tr');
+            $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
+            $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
+        $ret .= $this->end('tr');
+        $ret .= $this->end('thead');
+        foreach ($all as $ns => $directives) {
+            $ret .= $this->renderNamespace($ns, $directives);
+        }
+        if ($render_controls) {
+             $ret .= $this->start('tbody');
+             $ret .= $this->start('tr');
+                 $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
+                     $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
+                     $ret .= '[<a href="?">Reset</a>]';
+                 $ret .= $this->end('td');
+             $ret .= $this->end('tr');
+             $ret .= $this->end('tbody');
+        }
+        $ret .= $this->end('table');
+        return $ret;
+    }
+    
+    /**
+     * Renders a single namespace
+     * @param $ns String namespace name
+     * @param $directive Associative array of directives to values
+     */
+    protected function renderNamespace($ns, $directives) {
+        $ret = '';
+        $ret .= $this->start('tbody', array('class' => 'namespace'));
+        $ret .= $this->start('tr');
+            $ret .= $this->element('th', $ns, array('colspan' => 2));
+        $ret .= $this->end('tr');
+        $ret .= $this->end('tbody');
+        $ret .= $this->start('tbody');
+        foreach ($directives as $directive => $value) {
+            $ret .= $this->start('tr');
+            $ret .= $this->start('th');
+            if ($this->docURL) {
+                $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);
+                $ret .= $this->start('a', array('href' => $url));
+            }
+                $attr = array('for' => "{$this->name}:$ns.$directive");
+                
+                // crop directive name if it's too long
+                if (!$this->compress || (strlen($directive) < $this->compress)) {
+                    $directive_disp = $directive;
+                } else {
+                    $directive_disp = substr($directive, 0, $this->compress - 2) . '...';
+                    $attr['title'] = $directive;
+                }
+                
+                $ret .= $this->element(
+                    'label',
+                    $directive_disp,
+                    // component printers must create an element with this id
+                    $attr
+                );
+            if ($this->docURL) $ret .= $this->end('a');
+            $ret .= $this->end('th');
+            
+            $ret .= $this->start('td');
+                $def = $this->config->def->info[$ns][$directive];
+                $type = $def->type;
+                if (!isset($this->fields[$type])) $type = 'default';
+                $type_obj = $this->fields[$type];
+                if ($def->allow_null) {
+                    $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
+                }
+                $ret .= $type_obj->render($ns, $directive, $value, $this->name, $this->config);
+            $ret .= $this->end('td');
+            $ret .= $this->end('tr');
+        }
+        $ret .= $this->end('tbody');
+        return $ret;
+    }
+    
+}
+
+/**
+ * Printer decorator for directives that accept null
+ */
+class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer {
+    /**
+     * Printer being decorated
+     */
+    protected $obj;
+    /**
+     * @param $obj Printer to decorate
+     */
+    public function __construct($obj) {
+        parent::__construct();
+        $this->obj = $obj;
+    }
+    public function render($ns, $directive, $value, $name, $config) {
+        $this->prepareGenerator($config);
+        $ret = '';
+        $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive"));
+        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
+        $ret .= $this->text(' Null/Disabled');
+        $ret .= $this->end('label');
+        $attr = array(
+            'type' => 'checkbox',
+            'value' => '1',
+            'class' => 'null-toggle',
+            'name' => "$name"."[Null_$ns.$directive]",
+            'id' => "$name:Null_$ns.$directive",
+            'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
+        );
+        if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
+            // modify inline javascript slightly
+            $attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)";
+        }
+        if ($value === null) $attr['checked'] = 'checked';
+        $ret .= $this->elementEmpty('input', $attr);
+        $ret .= $this->text(' or ');
+        $ret .= $this->elementEmpty('br');
+        $ret .= $this->obj->render($ns, $directive, $value, $name, $config);
+        return $ret;
+    }
+}
+
+/**
+ * Swiss-army knife configuration form field printer
+ */
+class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
+    public $cols = 18;
+    public $rows = 5;
+    public function render($ns, $directive, $value, $name, $config) {
+        $this->prepareGenerator($config);
+        // this should probably be split up a little
+        $ret = '';
+        $def = $config->def->info[$ns][$directive];
+        if (is_array($value)) {
+            switch ($def->type) {
+                case 'lookup':
+                    $array = $value;
+                    $value = array();
+                    foreach ($array as $val => $b) {
+                        $value[] = $val;
+                    }
+                case 'list':
+                    $value = implode(PHP_EOL, $value);
+                    break;
+                case 'hash':
+                    $nvalue = '';
+                    foreach ($value as $i => $v) {
+                        $nvalue .= "$i:$v" . PHP_EOL;
+                    }
+                    $value = $nvalue;
+                    break;
+                default:
+                    $value = '';
+            }
+        }
+        if ($def->type === 'mixed') {
+            return 'Not supported';
+            $value = serialize($value);
+        }
+        $attr = array(
+            'name' => "$name"."[$ns.$directive]",
+            'id' => "$name:$ns.$directive"
+        );
+        if ($value === null) $attr['disabled'] = 'disabled';
+        if (is_array($def->allowed)) {
+            $ret .= $this->start('select', $attr);
+            foreach ($def->allowed as $val => $b) {
+                $attr = array();
+                if ($value == $val) $attr['selected'] = 'selected';
+                $ret .= $this->element('option', $val, $attr);
+            }
+            $ret .= $this->end('select');
+        } elseif (
+            $def->type == 'text' || $def->type == 'itext' ||
+            $def->type == 'list' || $def->type == 'hash' || $def->type == 'lookup'
+        ) {
+            $attr['cols'] = $this->cols;
+            $attr['rows'] = $this->rows;
+            $ret .= $this->start('textarea', $attr);
+            $ret .= $this->text($value);
+            $ret .= $this->end('textarea');
+        } else {
+            $attr['value'] = $value;
+            $attr['type'] = 'text';
+            $ret .= $this->elementEmpty('input', $attr);
+        }
+        return $ret;
+    }
+}
+
+/**
+ * Bool form field printer
+ */
+class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {
+    public function render($ns, $directive, $value, $name, $config) {
+        $this->prepareGenerator($config);
+        $ret = '';
+        $ret .= $this->start('div', array('id' => "$name:$ns.$directive"));
+        
+        $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive"));
+        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
+        $ret .= $this->text(' Yes');
+        $ret .= $this->end('label');
+        
+        $attr = array(
+            'type' => 'radio',
+            'name' => "$name"."[$ns.$directive]",
+            'id' => "$name:Yes_$ns.$directive",
+            'value' => '1'
+        );
+        if ($value === true) $attr['checked'] = 'checked';
+        if ($value === null) $attr['disabled'] = 'disabled';
+        $ret .= $this->elementEmpty('input', $attr);
+        
+        $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
+        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
+        $ret .= $this->text(' No');
+        $ret .= $this->end('label');
+        
+        $attr = array(
+            'type' => 'radio',
+            'name' => "$name"."[$ns.$directive]",
+            'id' => "$name:No_$ns.$directive",
+            'value' => '0'
+        );
+        if ($value === false) $attr['checked'] = 'checked';
+        if ($value === null) $attr['disabled'] = 'disabled';
+        $ret .= $this->elementEmpty('input', $attr);
+                
+        $ret .= $this->end('div');
+        
+        return $ret;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.css
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.css (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.css (revision 21)
@@ -0,0 +1,8 @@
+
+.hp-config {}
+
+.hp-config tbody th {text-align:right; padding-right:0.5em;}
+.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;}
+.hp-config .namespace th {text-align:center;}
+.hp-config .verbose {display:none;}
+.hp-config .controls {text-align:center;}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/HTMLDefinition.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/HTMLDefinition.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/HTMLDefinition.php (revision 21)
@@ -0,0 +1,271 @@
+<?php
+
+class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
+{
+    
+    /**
+     * Instance of HTMLPurifier_HTMLDefinition, for easy access
+     */
+    protected $def;
+    
+    public function render($config) {
+        $ret = '';
+        $this->config =& $config;
+        
+        $this->def = $config->getHTMLDefinition();
+        
+        $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
+        
+        $ret .= $this->renderDoctype();
+        $ret .= $this->renderEnvironment();
+        $ret .= $this->renderContentSets();
+        $ret .= $this->renderInfo();
+        
+        $ret .= $this->end('div');
+        
+        return $ret;
+    }
+    
+    /**
+     * Renders the Doctype table
+     */
+    protected function renderDoctype() {
+        $doctype = $this->def->doctype;
+        $ret = '';
+        $ret .= $this->start('table');
+        $ret .= $this->element('caption', 'Doctype');
+        $ret .= $this->row('Name', $doctype->name);
+        $ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No');
+        $ret .= $this->row('Default Modules', implode($doctype->modules, ', '));
+        $ret .= $this->row('Default Tidy Modules', implode($doctype->tidyModules, ', '));
+        $ret .= $this->end('table');
+        return $ret;
+    }
+    
+    
+    /**
+     * Renders environment table, which is miscellaneous info
+     */
+    protected function renderEnvironment() {
+        $def = $this->def;
+        
+        $ret = '';
+        
+        $ret .= $this->start('table');
+        $ret .= $this->element('caption', 'Environment');
+        
+        $ret .= $this->row('Parent of fragment', $def->info_parent);
+        $ret .= $this->renderChildren($def->info_parent_def->child);
+        $ret .= $this->row('Block wrap name', $def->info_block_wrapper);
+        
+        $ret .= $this->start('tr');
+            $ret .= $this->element('th', 'Global attributes');
+            $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr),0,0);
+        $ret .= $this->end('tr');
+        
+        $ret .= $this->start('tr');
+            $ret .= $this->element('th', 'Tag transforms');
+            $list = array();
+            foreach ($def->info_tag_transform as $old => $new) {
+                $new = $this->getClass($new, 'TagTransform_');
+                $list[] = "<$old> with $new";
+            }
+            $ret .= $this->element('td', $this->listify($list));
+        $ret .= $this->end('tr');
+        
+        $ret .= $this->start('tr');
+            $ret .= $this->element('th', 'Pre-AttrTransform');
+            $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre));
+        $ret .= $this->end('tr');
+        
+        $ret .= $this->start('tr');
+            $ret .= $this->element('th', 'Post-AttrTransform');
+            $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post));
+        $ret .= $this->end('tr');
+        
+        $ret .= $this->end('table');
+        return $ret;
+    }
+    
+    /**
+     * Renders the Content Sets table
+     */
+    protected function renderContentSets() {
+        $ret = '';
+        $ret .= $this->start('table');
+        $ret .= $this->element('caption', 'Content Sets');
+        foreach ($this->def->info_content_sets as $name => $lookup) {
+            $ret .= $this->heavyHeader($name);
+            $ret .= $this->start('tr');
+            $ret .= $this->element('td', $this->listifyTagLookup($lookup));
+            $ret .= $this->end('tr');
+        }
+        $ret .= $this->end('table');
+        return $ret;
+    }
+    
+    /**
+     * Renders the Elements ($info) table
+     */
+    protected function renderInfo() {
+        $ret = '';
+        $ret .= $this->start('table');
+        $ret .= $this->element('caption', 'Elements ($info)');
+        ksort($this->def->info);
+        $ret .= $this->heavyHeader('Allowed tags', 2);
+        $ret .= $this->start('tr');
+        $ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2));
+        $ret .= $this->end('tr');
+        foreach ($this->def->info as $name => $def) {
+            $ret .= $this->start('tr');
+                $ret .= $this->element('th', "<$name>", array('class'=>'heavy', 'colspan' => 2));
+            $ret .= $this->end('tr');
+            $ret .= $this->start('tr');
+                $ret .= $this->element('th', 'Inline content');
+                $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No');
+            $ret .= $this->end('tr');
+            if (!empty($def->excludes)) {
+                $ret .= $this->start('tr');
+                    $ret .= $this->element('th', 'Excludes');
+                    $ret .= $this->element('td', $this->listifyTagLookup($def->excludes));
+                $ret .= $this->end('tr');
+            }
+            if (!empty($def->attr_transform_pre)) {
+                $ret .= $this->start('tr');
+                    $ret .= $this->element('th', 'Pre-AttrTransform');
+                    $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre));
+                $ret .= $this->end('tr');
+            }
+            if (!empty($def->attr_transform_post)) {
+                $ret .= $this->start('tr');
+                    $ret .= $this->element('th', 'Post-AttrTransform');
+                    $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post));
+                $ret .= $this->end('tr');
+            }
+            if (!empty($def->auto_close)) {
+                $ret .= $this->start('tr');
+                    $ret .= $this->element('th', 'Auto closed by');
+                    $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close));
+                $ret .= $this->end('tr');
+            }
+            $ret .= $this->start('tr');
+                $ret .= $this->element('th', 'Allowed attributes');
+                $ret .= $this->element('td',$this->listifyAttr($def->attr), array(), 0);
+            $ret .= $this->end('tr');
+            
+            if (!empty($def->required_attr)) {
+                $ret .= $this->row('Required attributes', $this->listify($def->required_attr));
+            }
+            
+            $ret .= $this->renderChildren($def->child);
+        }
+        $ret .= $this->end('table');
+        return $ret;
+    }
+    
+    /** 
+     * Renders a row describing the allowed children of an element
+     * @param $def HTMLPurifier_ChildDef of pertinent element
+     */
+    protected function renderChildren($def) {
+        $context = new HTMLPurifier_Context();
+        $ret = '';
+        $ret .= $this->start('tr');
+            $elements = array();
+            $attr = array();
+            if (isset($def->elements)) {
+                if ($def->type == 'strictblockquote') {
+                    $def->validateChildren(array(), $this->config, $context);
+                }
+                $elements = $def->elements;
+            }
+            if ($def->type == 'chameleon') {
+                $attr['rowspan'] = 2;
+            } elseif ($def->type == 'empty') {
+                $elements = array();
+            } elseif ($def->type == 'table') {
+                $elements = array_flip(array('col', 'caption', 'colgroup', 'thead',
+                    'tfoot', 'tbody', 'tr'));
+            }
+            $ret .= $this->element('th', 'Allowed children', $attr);
+            
+            if ($def->type == 'chameleon') {
+                
+                $ret .= $this->element('td',
+                    '<em>Block</em>: ' .
+                    $this->escape($this->listifyTagLookup($def->block->elements)),0,0);
+                $ret .= $this->end('tr');
+                $ret .= $this->start('tr');
+                $ret .= $this->element('td',
+                    '<em>Inline</em>: ' .
+                    $this->escape($this->listifyTagLookup($def->inline->elements)),0,0);
+                
+            } elseif ($def->type == 'custom') {
+                
+                $ret .= $this->element('td', '<em>'.ucfirst($def->type).'</em>: ' .
+                    $def->dtd_regex);
+                
+            } else {
+                $ret .= $this->element('td',
+                    '<em>'.ucfirst($def->type).'</em>: ' .
+                    $this->escape($this->listifyTagLookup($elements)),0,0);
+            }
+        $ret .= $this->end('tr');
+        return $ret;
+    }
+    
+    /** 
+     * Listifies a tag lookup table.
+     * @param $array Tag lookup array in form of array('tagname' => true)
+     */
+    protected function listifyTagLookup($array) {
+        ksort($array);
+        $list = array();
+        foreach ($array as $name => $discard) {
+            if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue;
+            $list[] = $name;
+        }
+        return $this->listify($list);
+    }
+    
+    /**
+     * Listifies a list of objects by retrieving class names and internal state
+     * @param $array List of objects
+     * @todo Also add information about internal state
+     */
+    protected function listifyObjectList($array) {
+        ksort($array);
+        $list = array();
+        foreach ($array as $discard => $obj) {
+            $list[] = $this->getClass($obj, 'AttrTransform_');
+        }
+        return $this->listify($list);
+    }
+    
+    /**
+     * Listifies a hash of attributes to AttrDef classes
+     * @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
+     */
+    protected function listifyAttr($array) {
+        ksort($array);
+        $list = array();
+        foreach ($array as $name => $obj) {
+            if ($obj === false) continue;
+            $list[] = "$name&nbsp;=&nbsp;<i>" . $this->getClass($obj, 'AttrDef_') . '</i>';
+        }
+        return $this->listify($list);
+    }
+    
+    /**
+     * Creates a heavy header row
+     */
+    protected function heavyHeader($text, $num = 1) {
+        $ret = '';
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy'));
+        $ret .= $this->end('tr');
+        return $ret;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.js
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.js (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/ConfigForm.js (revision 21)
@@ -0,0 +1,3 @@
+function toggleWriteability(id_of_patient, checked) {
+    document.getElementById(id_of_patient).disabled = checked;
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/CSSDefinition.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/CSSDefinition.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer/CSSDefinition.php (revision 21)
@@ -0,0 +1,37 @@
+<?php
+
+class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer
+{
+    
+    protected $def;
+    
+    public function render($config) {
+        $this->def = $config->getCSSDefinition();
+        $ret = '';
+        
+        $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
+        $ret .= $this->start('table');
+        
+        $ret .= $this->element('caption', 'Properties ($info)');
+        
+        $ret .= $this->start('thead');
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', 'Property', array('class' => 'heavy'));
+        $ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;'));
+        $ret .= $this->end('tr');
+        $ret .= $this->end('thead');
+        
+        ksort($this->def->info);
+        foreach ($this->def->info as $property => $obj) {
+            $name = $this->getClass($obj, 'AttrDef_');
+            $ret .= $this->row($property, $name);
+        }
+        
+        $ret .= $this->end('table');
+        $ret .= $this->end('div');
+        
+        return $ret;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/Namespace.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/Namespace.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/Namespace.php (revision 21)
@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * Structure object describing of a namespace
+ */
+class HTMLPurifier_ConfigDef_Namespace extends HTMLPurifier_ConfigDef
+{
+    public $class = 'namespace';
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/Directive.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/Directive.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/Directive.php (revision 21)
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Structure object containing definition of a directive.
+ * @note This structure does not contain default values
+ */
+class HTMLPurifier_ConfigDef_Directive extends HTMLPurifier_ConfigDef
+{
+    
+    public $class = 'directive';
+    
+    public function __construct(
+        $type = null,
+        $allow_null = null,
+        $allowed = null,
+        $aliases = null
+    ) {
+        if (       $type !== null)        $this->type = $type;
+        if ( $allow_null !== null)  $this->allow_null = $allow_null;
+        if (    $allowed !== null)     $this->allowed = $allowed;
+        if (    $aliases !== null)     $this->aliases = $aliases;
+    }
+    
+    /**
+     * Allowed type of the directive. Values are:
+     *      - string
+     *      - istring (case insensitive string)
+     *      - int
+     *      - float
+     *      - bool
+     *      - lookup (array of value => true)
+     *      - list (regular numbered index array)
+     *      - hash (array of key => value)
+     *      - mixed (anything goes)
+     */
+    public $type = 'mixed';
+    
+    /**
+     * Is null allowed? Has no effect for mixed type.
+     * @bool
+     */
+    public $allow_null = false;
+    
+    /**
+     * Lookup table of allowed values of the element, bool true if all allowed.
+     */
+    public $allowed = true;
+    
+    /**
+     * Hash of value aliases, i.e. values that are equivalent.
+     */
+    public $aliases = array();
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/DirectiveAlias.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/DirectiveAlias.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef/DirectiveAlias.php (revision 21)
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Structure object describing a directive alias
+ */
+class HTMLPurifier_ConfigDef_DirectiveAlias extends HTMLPurifier_ConfigDef
+{
+    public $class = 'alias';
+    
+    /**
+     * Namespace being aliased to
+     */
+    public $namespace;
+    /**
+     * Directive being aliased to
+     */
+    public $name;
+    
+    public function __construct($namespace, $name) {
+        $this->namespace = $namespace;
+        $this->name = $name;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/ftp.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/ftp.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/ftp.php (revision 21)
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * Validates ftp (File Transfer Protocol) URIs as defined by generic RFC 1738.
+ */
+class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme {
+    
+    public $default_port = 21;
+    public $browsable = true; // usually
+    public $hierarchical = true;
+    
+    public function validate(&$uri, $config, $context) {
+        parent::validate($uri, $config, $context);
+        $uri->query    = null;
+        
+        // typecode check
+        $semicolon_pos = strrpos($uri->path, ';'); // reverse
+        if ($semicolon_pos !== false) {
+            $type = substr($uri->path, $semicolon_pos + 1); // no semicolon
+            $uri->path = substr($uri->path, 0, $semicolon_pos);
+            $type_ret = '';
+            if (strpos($type, '=') !== false) {
+                // figure out whether or not the declaration is correct
+                list($key, $typecode) = explode('=', $type, 2);
+                if ($key !== 'type') {
+                    // invalid key, tack it back on encoded
+                    $uri->path .= '%3B' . $type;
+                } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') {
+                    $type_ret = ";type=$typecode";
+                }
+            } else {
+                $uri->path .= '%3B' . $type;
+            }
+            $uri->path = str_replace(';', '%3B', $uri->path);
+            $uri->path .= $type_ret;
+        }
+        
+        return true;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/news.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/news.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/news.php (revision 21)
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * Validates news (Usenet) as defined by generic RFC 1738
+ */
+class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme {
+    
+    public $browsable = false;
+    
+    public function validate(&$uri, $config, $context) {
+        parent::validate($uri, $config, $context);
+        $uri->userinfo = null;
+        $uri->host     = null;
+        $uri->port     = null;
+        $uri->query    = null;
+        // typecode check needed on path
+        return true;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/http.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/http.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/http.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Validates http (HyperText Transfer Protocol) as defined by RFC 2616
+ */
+class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme {
+    
+    public $default_port = 80;
+    public $browsable = true;
+    public $hierarchical = true;
+    
+    public function validate(&$uri, $config, $context) {
+        parent::validate($uri, $config, $context);
+        $uri->userinfo = null;
+        return true;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/nntp.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/nntp.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/nntp.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Validates nntp (Network News Transfer Protocol) as defined by generic RFC 1738
+ */
+class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme {
+    
+    public $default_port = 119;
+    public $browsable = false;
+    
+    public function validate(&$uri, $config, $context) {
+        parent::validate($uri, $config, $context);
+        $uri->userinfo = null;
+        $uri->query    = null;
+        return true;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/https.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/https.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/https.php (revision 21)
@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * Validates https (Secure HTTP) according to http scheme.
+ */
+class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http {
+    
+    public $default_port = 443;
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/mailto.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/mailto.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme/mailto.php (revision 21)
@@ -0,0 +1,26 @@
+<?php
+
+// VERY RELAXED! Shouldn't cause problems, not even Firefox checks if the
+// email is valid, but be careful!
+
+/**
+ * Validates mailto (for E-mail) according to RFC 2368
+ * @todo Validate the email address
+ * @todo Filter allowed query parameters
+ */
+
+class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme {
+    
+    public $browsable = false;
+    
+    public function validate(&$uri, $config, $context) {
+        parent::validate($uri, $config, $context);
+        $uri->userinfo = null;
+        $uri->host     = null;
+        $uri->port     = null;
+        // we need to validate path against RFC 2368's addr-spec
+        return true;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef.php (revision 21)
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Defines allowed child nodes and validates tokens against it.
+ */
+abstract class HTMLPurifier_ChildDef
+{
+    /**
+     * Type of child definition, usually right-most part of class name lowercase.
+     * Used occasionally in terms of context.
+     */
+    public $type;
+    
+    /**
+     * Bool that indicates whether or not an empty array of children is okay
+     * 
+     * This is necessary for redundant checking when changes affecting
+     * a child node may cause a parent node to now be disallowed.
+     */
+    public $allow_empty;
+    
+    /**
+     * Lookup array of all elements that this definition could possibly allow
+     */
+    public $elements = array();
+    
+    /**
+     * Validates nodes according to definition and returns modification.
+     * 
+     * @param $tokens_of_children Array of HTMLPurifier_Token
+     * @param $config HTMLPurifier_Config object
+     * @param $context HTMLPurifier_Context object
+     * @return bool true to leave nodes as is
+     * @return bool false to remove parent node
+     * @return array of replacement child tokens
+     */
+    abstract public function validateChildren($tokens_of_children, $config, $context);
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy.php (revision 21)
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Supertype for classes that define a strategy for modifying/purifying tokens.
+ * 
+ * While HTMLPurifier's core purpose is fixing HTML into something proper, 
+ * strategies provide plug points for extra configuration or even extra
+ * features, such as custom tags, custom parsing of text, etc.
+ */
+
+ 
+abstract class HTMLPurifier_Strategy
+{
+    
+    /**
+     * Executes the strategy on the tokens.
+     * 
+     * @param $tokens Array of HTMLPurifier_Token objects to be operated on.
+     * @param $config Configuration options
+     * @returns Processed array of token objects.
+     */
+    abstract public function execute($tokens, $config, $context);
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCacheFactory.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCacheFactory.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCacheFactory.php (revision 21)
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * Responsible for creating definition caches.
+ */
+class HTMLPurifier_DefinitionCacheFactory
+{
+    
+    protected $caches = array('Serializer' => array());
+    protected $implementations = array();
+    protected $decorators = array();
+    
+    /**
+     * Initialize default decorators
+     */
+    public function setup() {
+        $this->addDecorator('Cleanup');
+    }
+    
+    /**
+     * Retrieves an instance of global definition cache factory.
+     */
+    public static function instance($prototype = null) {
+        static $instance;
+        if ($prototype !== null) {
+            $instance = $prototype;
+        } elseif ($instance === null || $prototype === true) {
+            $instance = new HTMLPurifier_DefinitionCacheFactory();
+            $instance->setup();
+        }
+        return $instance;
+    }
+    
+    /**
+     * Registers a new definition cache object
+     * @param $short Short name of cache object, for reference
+     * @param $long Full class name of cache object, for construction 
+     */
+    public function register($short, $long) {
+        $this->implementations[$short] = $long;
+    }
+    
+    /**
+     * Factory method that creates a cache object based on configuration
+     * @param $name Name of definitions handled by cache
+     * @param $config Instance of HTMLPurifier_Config
+     */
+    public function create($type, $config) {
+        $method = $config->get('Cache', 'DefinitionImpl');
+        if ($method === null) {
+            return new HTMLPurifier_DefinitionCache_Null($type);
+        }
+        if (!empty($this->caches[$method][$type])) {
+            return $this->caches[$method][$type];
+        }
+        if (
+          isset($this->implementations[$method]) &&
+          class_exists($class = $this->implementations[$method], false)
+        ) {
+            $cache = new $class($type);
+        } else {
+            if ($method != 'Serializer') {
+                trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING);
+            }
+            $cache = new HTMLPurifier_DefinitionCache_Serializer($type);
+        }
+        foreach ($this->decorators as $decorator) {
+            $new_cache = $decorator->decorate($cache);
+            // prevent infinite recursion in PHP 4
+            unset($cache);
+            $cache = $new_cache;
+        }
+        $this->caches[$method][$type] = $cache;
+        return $this->caches[$method][$type];
+    }
+    
+    /**
+     * Registers a decorator to add to all new cache objects
+     * @param 
+     */
+    public function addDecorator($decorator) {
+        if (is_string($decorator)) {
+            $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator";
+            $decorator = new $class;
+        }
+        $this->decorators[$decorator->name] = $decorator;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModuleManager.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModuleManager.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModuleManager.php (revision 21)
@@ -0,0 +1,375 @@
+<?php
+
+class HTMLPurifier_HTMLModuleManager
+{
+    
+    /**
+     * Instance of HTMLPurifier_DoctypeRegistry
+     */
+    public $doctypes;
+    
+    /**
+     * Instance of current doctype
+     */
+    public $doctype;
+    
+    /**
+     * Instance of HTMLPurifier_AttrTypes
+     */
+    public $attrTypes;
+    
+    /**
+     * Active instances of modules for the specified doctype are
+     * indexed, by name, in this array.
+     */
+    public $modules = array();
+    
+    /**
+     * Array of recognized HTMLPurifier_Module instances, indexed by 
+     * module's class name. This array is usually lazy loaded, but a
+     * user can overload a module by pre-emptively registering it.
+     */
+    public $registeredModules = array();
+    
+    /**
+     * List of extra modules that were added by the user using addModule().
+     * These get unconditionally merged into the current doctype, whatever
+     * it may be.
+     */
+    public $userModules = array();
+    
+    /**
+     * Associative array of element name to list of modules that have
+     * definitions for the element; this array is dynamically filled.
+     */
+    public $elementLookup = array();
+    
+    /** List of prefixes we should use for registering small names */
+    public $prefixes = array('HTMLPurifier_HTMLModule_');
+    
+    public $contentSets;     /**< Instance of HTMLPurifier_ContentSets */
+    public $attrCollections; /**< Instance of HTMLPurifier_AttrCollections */
+    
+    /** If set to true, unsafe elements and attributes will be allowed */
+    public $trusted = false;
+    
+    public function __construct() {
+        
+        // editable internal objects
+        $this->attrTypes = new HTMLPurifier_AttrTypes();
+        $this->doctypes  = new HTMLPurifier_DoctypeRegistry();
+        
+        // setup basic modules
+        $common = array(
+            'CommonAttributes', 'Text', 'Hypertext', 'List',
+            'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
+            'StyleAttribute', 'Scripting', 'Object'
+        );
+        $transitional = array('Legacy', 'Target');
+        $xml = array('XMLCommonAttributes');
+        $non_xml = array('NonXMLCommonAttributes');
+        
+        // setup basic doctypes
+        $this->doctypes->register(
+            'HTML 4.01 Transitional', false,
+            array_merge($common, $transitional, $non_xml),
+            array('Tidy_Transitional', 'Tidy_Proprietary'),
+            array(),
+            '-//W3C//DTD HTML 4.01 Transitional//EN',
+            'http://www.w3.org/TR/html4/loose.dtd'
+        );
+        
+        $this->doctypes->register(
+            'HTML 4.01 Strict', false,
+            array_merge($common, $non_xml),
+            array('Tidy_Strict', 'Tidy_Proprietary'),
+            array(),
+            '-//W3C//DTD HTML 4.01//EN',
+            'http://www.w3.org/TR/html4/strict.dtd'
+        );
+        
+        $this->doctypes->register(
+            'XHTML 1.0 Transitional', true,
+            array_merge($common, $transitional, $xml, $non_xml),
+            array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary'),
+            array(),
+            '-//W3C//DTD XHTML 1.0 Transitional//EN',
+            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
+        );
+        
+        $this->doctypes->register(
+            'XHTML 1.0 Strict', true,
+            array_merge($common, $xml, $non_xml),
+            array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary'),
+            array(),
+            '-//W3C//DTD XHTML 1.0 Strict//EN',
+            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
+        );
+        
+        $this->doctypes->register(
+            'XHTML 1.1', true,
+            array_merge($common, $xml, array('Ruby')),
+            array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict'), // Tidy_XHTML1_1
+            array(),
+            '-//W3C//DTD XHTML 1.1//EN',
+            'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
+        );
+        
+    }
+    
+    /**
+     * Registers a module to the recognized module list, useful for
+     * overloading pre-existing modules.
+     * @param $module Mixed: string module name, with or without
+     *                HTMLPurifier_HTMLModule prefix, or instance of
+     *                subclass of HTMLPurifier_HTMLModule.
+     * @param $overload Boolean whether or not to overload previous modules.
+     *                  If this is not set, and you do overload a module,
+     *                  HTML Purifier will complain with a warning.
+     * @note This function will not call autoload, you must instantiate
+     *       (and thus invoke) autoload outside the method.
+     * @note If a string is passed as a module name, different variants
+     *       will be tested in this order:
+     *          - Check for HTMLPurifier_HTMLModule_$name
+     *          - Check all prefixes with $name in order they were added
+     *          - Check for literal object name
+     *          - Throw fatal error
+     *       If your object name collides with an internal class, specify
+     *       your module manually. All modules must have been included
+     *       externally: registerModule will not perform inclusions for you!
+     */
+    public function registerModule($module, $overload = false) {
+        if (is_string($module)) {
+            // attempt to load the module
+            $original_module = $module;
+            $ok = false;
+            foreach ($this->prefixes as $prefix) {
+                $module = $prefix . $original_module;
+                if (class_exists($module)) {
+                    $ok = true;
+                    break;
+                }
+            }
+            if (!$ok) {
+                $module = $original_module;
+                if (!class_exists($module)) {
+                    trigger_error($original_module . ' module does not exist',
+                        E_USER_ERROR);
+                    return;
+                }
+            }
+            $module = new $module();
+        }
+        if (empty($module->name)) {
+            trigger_error('Module instance of ' . get_class($module) . ' must have name');
+            return;
+        }
+        if (!$overload && isset($this->registeredModules[$module->name])) {
+            trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING);
+        }
+        $this->registeredModules[$module->name] = $module;
+    }
+    
+    /**
+     * Adds a module to the current doctype by first registering it,
+     * and then tacking it on to the active doctype
+     */
+    public function addModule($module) {
+        $this->registerModule($module);
+        if (is_object($module)) $module = $module->name;
+        $this->userModules[] = $module;
+    }
+    
+    /**
+     * Adds a class prefix that registerModule() will use to resolve a
+     * string name to a concrete class
+     */
+    public function addPrefix($prefix) {
+        $this->prefixes[] = $prefix;
+    }
+    
+    /**
+     * Performs processing on modules, after being called you may
+     * use getElement() and getElements()
+     * @param $config Instance of HTMLPurifier_Config
+     */
+    public function setup($config) {
+        
+        $this->trusted = $config->get('HTML', 'Trusted');
+        
+        // generate
+        $this->doctype = $this->doctypes->make($config);
+        $modules = $this->doctype->modules;
+        
+        // take out the default modules that aren't allowed
+        $lookup = $config->get('HTML', 'AllowedModules');
+        $special_cases = $config->get('HTML', 'CoreModules');
+        
+        if (is_array($lookup)) {
+            foreach ($modules as $k => $m) {
+                if (isset($special_cases[$m])) continue;
+                if (!isset($lookup[$m])) unset($modules[$k]);
+            }
+        }
+        
+        // merge in custom modules
+        $modules = array_merge($modules, $this->userModules);
+        
+        // add proprietary module (this gets special treatment because
+        // it is completely removed from doctypes, etc.)
+        if ($config->get('HTML', 'Proprietary')) {
+            $modules[] = 'Proprietary';
+        }
+        
+        foreach ($modules as $module) {
+            $this->processModule($module);
+        }
+        
+        foreach ($this->doctype->tidyModules as $module) {
+            $this->processModule($module);
+            if (method_exists($this->modules[$module], 'construct')) {
+                $this->modules[$module]->construct($config);
+            }
+        }
+        
+        // setup lookup table based on all valid modules
+        foreach ($this->modules as $module) {
+            foreach ($module->info as $name => $def) {
+                if (!isset($this->elementLookup[$name])) {
+                    $this->elementLookup[$name] = array();
+                }
+                $this->elementLookup[$name][] = $module->name;
+            }
+        }
+        
+        // note the different choice
+        $this->contentSets = new HTMLPurifier_ContentSets(
+            // content set assembly deals with all possible modules,
+            // not just ones deemed to be "safe"
+            $this->modules
+        );
+        $this->attrCollections = new HTMLPurifier_AttrCollections(
+            $this->attrTypes,
+            // there is no way to directly disable a global attribute,
+            // but using AllowedAttributes or simply not including
+            // the module in your custom doctype should be sufficient
+            $this->modules
+        );
+    }
+    
+    /**
+     * Takes a module and adds it to the active module collection,
+     * registering it if necessary.
+     */
+    public function processModule($module) {
+        if (!isset($this->registeredModules[$module]) || is_object($module)) {
+            $this->registerModule($module);
+        }
+        $this->modules[$module] = $this->registeredModules[$module];
+    }
+    
+    /**
+     * Retrieves merged element definitions.
+     * @return Array of HTMLPurifier_ElementDef
+     */
+    public function getElements() {
+        
+        $elements = array();
+        foreach ($this->modules as $module) {
+            if (!$this->trusted && !$module->safe) continue;
+            foreach ($module->info as $name => $v) {
+                if (isset($elements[$name])) continue;
+                $elements[$name] = $this->getElement($name);
+            }
+        }
+        
+        // remove dud elements, this happens when an element that
+        // appeared to be safe actually wasn't
+        foreach ($elements as $n => $v) {
+            if ($v === false) unset($elements[$n]);
+        }
+        
+        return $elements;
+        
+    }
+    
+    /**
+     * Retrieves a single merged element definition
+     * @param $name Name of element
+     * @param $trusted Boolean trusted overriding parameter: set to true
+     *                 if you want the full version of an element
+     * @return Merged HTMLPurifier_ElementDef
+     * @note You may notice that modules are getting iterated over twice (once
+     *       in getElements() and once here). This
+     *       is because 
+     */
+    public function getElement($name, $trusted = null) {
+        
+        if (!isset($this->elementLookup[$name])) {
+            return false;
+        }
+        
+        // setup global state variables
+        $def = false;
+        if ($trusted === null) $trusted = $this->trusted;
+        
+        // iterate through each module that has registered itself to this
+        // element
+        foreach($this->elementLookup[$name] as $module_name) {
+            
+            $module = $this->modules[$module_name];
+            
+            // refuse to create/merge from a module that is deemed unsafe--
+            // pretend the module doesn't exist--when trusted mode is not on.
+            if (!$trusted && !$module->safe) {
+                continue;
+            }
+            
+            // clone is used because, ideally speaking, the original
+            // definition should not be modified. Usually, this will
+            // make no difference, but for consistency's sake
+            $new_def = clone $module->info[$name];
+            
+            if (!$def && $new_def->standalone) {
+                $def = $new_def;
+            } elseif ($def) {
+                // This will occur even if $new_def is standalone. In practice,
+                // this will usually result in a full replacement.
+                $def->mergeIn($new_def);
+            } else {
+                // :TODO:
+                // non-standalone definitions that don't have a standalone
+                // to merge into could be deferred to the end
+                continue;
+            }
+            
+            // attribute value expansions
+            $this->attrCollections->performInclusions($def->attr);
+            $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes);
+            
+            // descendants_are_inline, for ChildDef_Chameleon
+            if (is_string($def->content_model) &&
+                strpos($def->content_model, 'Inline') !== false) {
+                if ($name != 'del' && $name != 'ins') {
+                    // this is for you, ins/del
+                    $def->descendants_are_inline = true;
+                }
+            }
+            
+            $this->contentSets->generateChildDef($def, $module);
+        }
+            
+        // add information on required attributes
+        foreach ($def->attr as $attr_name => $attr_def) {
+            if ($attr_def->required) {
+                $def->required_attr[] = $attr_name;
+            }
+        }
+        
+        return $def;
+        
+    }
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ElementDef.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ElementDef.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ElementDef.php (revision 21)
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * Structure that stores an HTML element definition. Used by
+ * HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule.
+ * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition.
+ *       Please update that class too.
+ */
+class HTMLPurifier_ElementDef
+{
+    
+    /**
+     * Does the definition work by itself, or is it created solely
+     * for the purpose of merging into another definition?
+     */
+    public $standalone = true;
+    
+    /**
+     * Associative array of attribute name to HTMLPurifier_AttrDef
+     * @note Before being processed by HTMLPurifier_AttrCollections
+     *       when modules are finalized during
+     *       HTMLPurifier_HTMLDefinition->setup(), this array may also
+     *       contain an array at index 0 that indicates which attribute
+     *       collections to load into the full array. It may also
+     *       contain string indentifiers in lieu of HTMLPurifier_AttrDef,
+     *       see HTMLPurifier_AttrTypes on how they are expanded during
+     *       HTMLPurifier_HTMLDefinition->setup() processing.
+     */
+    public $attr = array();
+    
+    /**
+     * Indexed list of tag's HTMLPurifier_AttrTransform to be done before validation
+     */
+    public $attr_transform_pre = array();
+    
+    /**
+     * Indexed list of tag's HTMLPurifier_AttrTransform to be done after validation
+     */
+    public $attr_transform_post = array();
+    
+    /**
+     * HTMLPurifier_ChildDef of this tag.
+     */
+    public $child;
+    
+    /**
+     * Abstract string representation of internal ChildDef rules. See
+     * HTMLPurifier_ContentSets for how this is parsed and then transformed
+     * into an HTMLPurifier_ChildDef.
+     * @warning This is a temporary variable that is not available after
+     *      being processed by HTMLDefinition
+     */
+    public $content_model;
+    
+    /**
+     * Value of $child->type, used to determine which ChildDef to use,
+     * used in combination with $content_model.
+     * @warning This must be lowercase
+     * @warning This is a temporary variable that is not available after
+     *      being processed by HTMLDefinition
+     */
+    public $content_model_type;
+    
+    
+    
+    /**
+     * Does the element have a content model (#PCDATA | Inline)*? This
+     * is important for chameleon ins and del processing in 
+     * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
+     * have to worry about this one.
+     */
+    public $descendants_are_inline = false;
+    
+    /**
+     * List of the names of required attributes this element has. Dynamically
+     * populated by HTMLPurifier_HTMLDefinition::getElement
+     */
+    public $required_attr = array();
+    
+    /**
+     * Lookup table of tags excluded from all descendants of this tag.
+     * @note SGML permits exclusions for all descendants, but this is
+     *       not possible with DTDs or XML Schemas. W3C has elected to
+     *       use complicated compositions of content_models to simulate
+     *       exclusion for children, but we go the simpler, SGML-style
+     *       route of flat-out exclusions, which correctly apply to
+     *       all descendants and not just children. Note that the XHTML
+     *       Modularization Abstract Modules are blithely unaware of such
+     *       distinctions.
+     */
+    public $excludes = array();
+    
+    /**
+     * Low-level factory constructor for creating new standalone element defs
+     */
+    public static function create($content_model, $content_model_type, $attr) {
+        $def = new HTMLPurifier_ElementDef();
+        $def->content_model = $content_model;
+        $def->content_model_type = $content_model_type;
+        $def->attr = $attr;
+        return $def;
+    }
+    
+    /**
+     * Merges the values of another element definition into this one.
+     * Values from the new element def take precedence if a value is
+     * not mergeable.
+     */
+    public function mergeIn($def) {
+        
+        // later keys takes precedence
+        foreach($def->attr as $k => $v) {
+            if ($k === 0) {
+                // merge in the includes
+                // sorry, no way to override an include
+                foreach ($v as $v2) {
+                    $this->attr[0][] = $v2;
+                }
+                continue;
+            }
+            if ($v === false) {
+                if (isset($this->attr[$k])) unset($this->attr[$k]);
+                continue;
+            }
+            $this->attr[$k] = $v;
+        }
+        $this->_mergeAssocArray($this->attr_transform_pre, $def->attr_transform_pre);
+        $this->_mergeAssocArray($this->attr_transform_post, $def->attr_transform_post);
+        $this->_mergeAssocArray($this->excludes, $def->excludes);
+        
+        if(!empty($def->content_model)) {
+            $this->content_model .= ' | ' . $def->content_model;
+            $this->child = false;
+        }
+        if(!empty($def->content_model_type)) {
+            $this->content_model_type = $def->content_model_type;
+            $this->child = false;
+        }
+        if(!is_null($def->child)) $this->child = $def->child;
+        if($def->descendants_are_inline) $this->descendants_are_inline = $def->descendants_are_inline;
+        
+    }
+    
+    /**
+     * Merges one array into another, removes values which equal false
+     * @param $a1 Array by reference that is merged into
+     * @param $a2 Array that merges into $a1
+     */
+    private function _mergeAssocArray(&$a1, $a2) {
+        foreach ($a2 as $k => $v) {
+            if ($v === false) {
+                if (isset($a1[$k])) unset($a1[$k]);
+                continue;
+            }
+            $a1[$k] = $v;
+        }
+    }
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TokenFactory.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TokenFactory.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TokenFactory.php (revision 21)
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Factory for token generation.
+ * 
+ * @note Doing some benchmarking indicates that the new operator is much
+ *       slower than the clone operator (even discounting the cost of the
+ *       constructor).  This class is for that optimization.
+ *       Other then that, there's not much point as we don't
+ *       maintain parallel HTMLPurifier_Token hierarchies (the main reason why
+ *       you'd want to use an abstract factory).
+ * @todo Port DirectLex to use this
+ */
+class HTMLPurifier_TokenFactory
+{
+    
+    /**
+     * Prototypes that will be cloned.
+     * @private
+     */
+    // p stands for prototype
+    private $p_start, $p_end, $p_empty, $p_text, $p_comment;
+    
+    /**
+     * Generates blank prototypes for cloning.
+     */
+    public function __construct() {
+        $this->p_start  = new HTMLPurifier_Token_Start('', array());
+        $this->p_end    = new HTMLPurifier_Token_End('');
+        $this->p_empty  = new HTMLPurifier_Token_Empty('', array());
+        $this->p_text   = new HTMLPurifier_Token_Text('');
+        $this->p_comment= new HTMLPurifier_Token_Comment('');
+    }
+    
+    /**
+     * Creates a HTMLPurifier_Token_Start.
+     * @param $name Tag name
+     * @param $attr Associative array of attributes
+     * @return Generated HTMLPurifier_Token_Start
+     */
+    public function createStart($name, $attr = array()) {
+        $p = clone $this->p_start;
+        $p->__construct($name, $attr);
+        return $p;
+    }
+    
+    /**
+     * Creates a HTMLPurifier_Token_End.
+     * @param $name Tag name
+     * @return Generated HTMLPurifier_Token_End
+     */
+    public function createEnd($name) {
+        $p = clone $this->p_end;
+        $p->__construct($name);
+        return $p;
+    }
+    
+    /**
+     * Creates a HTMLPurifier_Token_Empty.
+     * @param $name Tag name
+     * @param $attr Associative array of attributes
+     * @return Generated HTMLPurifier_Token_Empty
+     */
+    public function createEmpty($name, $attr = array()) {
+        $p = clone $this->p_empty;
+        $p->__construct($name, $attr);
+        return $p;
+    }
+    
+    /**
+     * Creates a HTMLPurifier_Token_Text.
+     * @param $data Data of text token
+     * @return Generated HTMLPurifier_Token_Text
+     */
+    public function createText($data) {
+        $p = clone $this->p_text;
+        $p->__construct($data);
+        return $p;
+    }
+    
+    /**
+     * Creates a HTMLPurifier_Token_Comment.
+     * @param $data Data of comment token
+     * @return Generated HTMLPurifier_Token_Comment
+     */
+    public function createComment($data) {
+        $p = clone $this->p_comment;
+        $p->__construct($data);
+        return $p;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIDefinition.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIDefinition.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIDefinition.php (revision 21)
@@ -0,0 +1,76 @@
+<?php
+
+class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
+{
+    
+    public $type = 'URI';
+    protected $filters = array();
+    protected $registeredFilters = array();
+    
+    /**
+     * HTMLPurifier_URI object of the base specified at %URI.Base
+     */
+    public $base;
+    
+    /**
+     * String host to consider "home" base, derived off of $base
+     */
+    public $host;
+    
+    /**
+     * Name of default scheme based on %URI.DefaultScheme and %URI.Base
+     */
+    public $defaultScheme;
+    
+    public function __construct() {
+        $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternal());
+        $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources());
+        $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist());
+        $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute());
+    }
+    
+    public function registerFilter($filter) {
+        $this->registeredFilters[$filter->name] = $filter;
+    }
+    
+    public function addFilter($filter, $config) {
+        $filter->prepare($config);
+        $this->filters[$filter->name] = $filter;
+    }
+    
+    protected function doSetup($config) {
+        $this->setupMemberVariables($config);
+        $this->setupFilters($config);
+    }
+    
+    protected function setupFilters($config) {
+        foreach ($this->registeredFilters as $name => $filter) {
+            $conf = $config->get('URI', $name);
+            if ($conf !== false && $conf !== null) {
+                $this->addFilter($filter, $config);
+            }
+        }
+        unset($this->registeredFilters);
+    }
+    
+    protected function setupMemberVariables($config) {
+        $this->host = $config->get('URI', 'Host');
+        $base_uri = $config->get('URI', 'Base');
+        if (!is_null($base_uri)) {
+            $parser = new HTMLPurifier_URIParser();
+            $this->base = $parser->parse($base_uri);
+            $this->defaultScheme = $this->base->scheme;
+            if (is_null($this->host)) $this->host = $this->base->host;
+        }
+        if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI', 'DefaultScheme');
+    }
+    
+    public function filter(&$uri, $config, $context) {
+        foreach ($this->filters as $name => $x) {
+            $result = $this->filters[$name]->filter($uri, $config, $context);
+            if (!$result) return false;
+        }
+        return true;
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule.php (revision 21)
@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * Represents an XHTML 1.1 module, with information on elements, tags
+ * and attributes.
+ * @note Even though this is technically XHTML 1.1, it is also used for
+ *       regular HTML parsing. We are using modulization as a convenient
+ *       way to represent the internals of HTMLDefinition, and our
+ *       implementation is by no means conforming and does not directly
+ *       use the normative DTDs or XML schemas.
+ * @note The public variables in a module should almost directly
+ *       correspond to the variables in HTMLPurifier_HTMLDefinition.
+ *       However, the prefix info carries no special meaning in these
+ *       objects (include it anyway if that's the correspondence though).
+ * @todo Consider making some member functions protected
+ */
+
+class HTMLPurifier_HTMLModule
+{
+    
+    // -- Overloadable ----------------------------------------------------
+    
+    /**
+     * Short unique string identifier of the module
+     */
+    public $name;
+    
+    /**
+     * Informally, a list of elements this module changes. Not used in
+     * any significant way.
+     */
+    public $elements = array();
+    
+    /**
+     * Associative array of element names to element definitions.
+     * Some definitions may be incomplete, to be merged in later
+     * with the full definition.
+     */
+    public $info = array();
+    
+    /**
+     * Associative array of content set names to content set additions.
+     * This is commonly used to, say, add an A element to the Inline
+     * content set. This corresponds to an internal variable $content_sets
+     * and NOT info_content_sets member variable of HTMLDefinition.
+     */
+    public $content_sets = array();
+    
+    /**
+     * Associative array of attribute collection names to attribute
+     * collection additions. More rarely used for adding attributes to
+     * the global collections. Example is the StyleAttribute module adding
+     * the style attribute to the Core. Corresponds to HTMLDefinition's
+     * attr_collections->info, since the object's data is only info,
+     * with extra behavior associated with it.
+     */
+    public $attr_collections = array();
+    
+    /**
+     * Associative array of deprecated tag name to HTMLPurifier_TagTransform
+     */
+    public $info_tag_transform = array();
+    
+    /**
+     * List of HTMLPurifier_AttrTransform to be performed before validation.
+     */
+    public $info_attr_transform_pre = array();
+    
+    /**
+     * List of HTMLPurifier_AttrTransform to be performed after validation.
+     */
+    public $info_attr_transform_post = array();
+    
+    /**
+     * Boolean flag that indicates whether or not getChildDef is implemented.
+     * For optimization reasons: may save a call to a function. Be sure
+     * to set it if you do implement getChildDef(), otherwise it will have
+     * no effect!
+     */
+    public $defines_child_def = false;
+    
+    /**
+     * Boolean flag whether or not this module is safe. If it is not safe, all
+     * of its members are unsafe. Modules are safe by default (this might be
+     * slightly dangerous, but it doesn't make much sense to force HTML Purifier,
+     * which is based off of safe HTML, to explicitly say, "This is safe," even
+     * though there are modules which are "unsafe")
+     * 
+     * @note Previously, safety could be applied at an element level granularity.
+     *       We've removed this ability, so in order to add "unsafe" elements
+     *       or attributes, a dedicated module with this property set to false
+     *       must be used.
+     */
+    public $safe = true;
+    
+    /**
+     * Retrieves a proper HTMLPurifier_ChildDef subclass based on 
+     * content_model and content_model_type member variables of
+     * the HTMLPurifier_ElementDef class. There is a similar function
+     * in HTMLPurifier_HTMLDefinition.
+     * @param $def HTMLPurifier_ElementDef instance
+     * @return HTMLPurifier_ChildDef subclass
+     */
+    public function getChildDef($def) {return false;}
+    
+    // -- Convenience -----------------------------------------------------
+    
+    /**
+     * Convenience function that sets up a new element
+     * @param $element Name of element to add
+     * @param $type What content set should element be registered to?
+     *              Set as false to skip this step.
+     * @param $contents Allowed children in form of:
+     *              "$content_model_type: $content_model"
+     * @param $attr_includes What attribute collections to register to
+     *              element?
+     * @param $attr What unique attributes does the element define?
+     * @note See ElementDef for in-depth descriptions of these parameters.
+     * @return Created element definition object, so you 
+     *         can set advanced parameters
+     */
+    public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) {
+        $this->elements[] = $element;
+        // parse content_model
+        list($content_model_type, $content_model) = $this->parseContents($contents);
+        // merge in attribute inclusions
+        $this->mergeInAttrIncludes($attr, $attr_includes);
+        // add element to content sets
+        if ($type) $this->addElementToContentSet($element, $type);
+        // create element
+        $this->info[$element] = HTMLPurifier_ElementDef::create(
+            $content_model, $content_model_type, $attr
+        );
+        // literal object $contents means direct child manipulation
+        if (!is_string($contents)) $this->info[$element]->child = $contents;
+        return $this->info[$element];
+    }
+    
+    /**
+     * Convenience function that creates a totally blank, non-standalone
+     * element.
+     * @param $element Name of element to create
+     * @return Created element
+     */
+    public function addBlankElement($element) {
+        if (!isset($this->info[$element])) {
+            $this->elements[] = $element;
+            $this->info[$element] = new HTMLPurifier_ElementDef();
+            $this->info[$element]->standalone = false;
+        } else {
+            trigger_error("Definition for $element already exists in module, cannot redefine");
+        }
+        return $this->info[$element];
+    }
+    
+    /**
+     * Convenience function that registers an element to a content set
+     * @param Element to register
+     * @param Name content set (warning: case sensitive, usually upper-case
+     *        first letter)
+     */
+    public function addElementToContentSet($element, $type) {
+        if (!isset($this->content_sets[$type])) $this->content_sets[$type] = '';
+        else $this->content_sets[$type] .= ' | ';
+        $this->content_sets[$type] .= $element;
+    }
+    
+    /**
+     * Convenience function that transforms single-string contents
+     * into separate content model and content model type
+     * @param $contents Allowed children in form of:
+     *                  "$content_model_type: $content_model"
+     * @note If contents is an object, an array of two nulls will be
+     *       returned, and the callee needs to take the original $contents
+     *       and use it directly.
+     */
+    public function parseContents($contents) {
+        if (!is_string($contents)) return array(null, null); // defer
+        switch ($contents) {
+            // check for shorthand content model forms
+            case 'Empty':
+                return array('empty', '');
+            case 'Inline':
+                return array('optional', 'Inline | #PCDATA');
+            case 'Flow':
+                return array('optional', 'Flow | #PCDATA');
+        }
+        list($content_model_type, $content_model) = explode(':', $contents);
+        $content_model_type = strtolower(trim($content_model_type));
+        $content_model = trim($content_model);
+        return array($content_model_type, $content_model);
+    }
+    
+    /**
+     * Convenience function that merges a list of attribute includes into
+     * an attribute array.
+     * @param $attr Reference to attr array to modify
+     * @param $attr_includes Array of includes / string include to merge in
+     */
+    public function mergeInAttrIncludes(&$attr, $attr_includes) {
+        if (!is_array($attr_includes)) {
+            if (empty($attr_includes)) $attr_includes = array();
+            else $attr_includes = array($attr_includes);
+        }
+        $attr[0] = $attr_includes;
+    }
+    
+    /**
+     * Convenience function that generates a lookup table with boolean
+     * true as value.
+     * @param $list List of values to turn into a lookup
+     * @note You can also pass an arbitrary number of arguments in
+     *       place of the regular argument
+     * @return Lookup array equivalent of list
+     */
+    public function makeLookup($list) {
+        if (is_string($list)) $list = func_get_args();
+        $ret = array();
+        foreach ($list as $value) {
+            if (is_null($value)) continue;
+            $ret[$value] = true;
+        }
+        return $ret;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector.php (revision 21)
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * Injects tokens into the document while parsing for well-formedness.
+ * This enables "formatter-like" functionality such as auto-paragraphing,
+ * smiley-ification and linkification to take place.
+ * 
+ * @todo Allow injectors to request a re-run on their output. This 
+ *       would help if an operation is recursive.
+ */
+abstract class HTMLPurifier_Injector
+{
+    
+    /**
+     * Advisory name of injector, this is for friendly error messages
+     */
+    public $name;
+    
+    /**
+     * Amount of tokens the injector needs to skip + 1. Because
+     * the decrement is the first thing that happens, this needs to
+     * be one greater than the "real" skip count.
+     */
+    public $skip = 1;
+    
+    /**
+     * Instance of HTMLPurifier_HTMLDefinition
+     */
+    protected $htmlDefinition;
+    
+    /**
+     * Reference to CurrentNesting variable in Context. This is an array
+     * list of tokens that we are currently "inside"
+     */
+    protected $currentNesting;
+    
+    /**
+     * Reference to InputTokens variable in Context. This is an array
+     * list of the input tokens that are being processed.
+     */
+    protected $inputTokens;
+    
+    /**
+     * Reference to InputIndex variable in Context. This is an integer
+     * array index for $this->inputTokens that indicates what token
+     * is currently being processed.
+     */
+    protected $inputIndex;
+    
+    /**
+     * Array of elements and attributes this injector creates and therefore
+     * need to be allowed by the definition. Takes form of
+     * array('element' => array('attr', 'attr2'), 'element2')
+     */
+    public $needed = array();
+    
+    /**
+     * Prepares the injector by giving it the config and context objects:
+     * this allows references to important variables to be made within
+     * the injector. This function also checks if the HTML environment
+     * will work with the Injector: if p tags are not allowed, the
+     * Auto-Paragraphing injector should not be enabled.
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     * @return Boolean false if success, string of missing needed element/attribute if failure
+     */
+    public function prepare($config, $context) {
+        $this->htmlDefinition = $config->getHTMLDefinition();
+        // perform $needed checks
+        foreach ($this->needed as $element => $attributes) {
+            if (is_int($element)) $element = $attributes;
+            if (!isset($this->htmlDefinition->info[$element])) return $element;
+            if (!is_array($attributes)) continue;
+            foreach ($attributes as $name) {
+                if (!isset($this->htmlDefinition->info[$element]->attr[$name])) return "$element.$name";
+            }
+        }
+        $this->currentNesting =& $context->get('CurrentNesting');
+        $this->inputTokens    =& $context->get('InputTokens');
+        $this->inputIndex     =& $context->get('InputIndex');
+        return false;
+    }
+    
+    /**
+     * Tests if the context node allows a certain element
+     * @param $name Name of element to test for
+     * @return True if element is allowed, false if it is not
+     */
+    public function allowsElement($name) {
+        if (!empty($this->currentNesting)) {
+            $parent_token = array_pop($this->currentNesting);
+            $this->currentNesting[] = $parent_token;
+            $parent = $this->htmlDefinition->info[$parent_token->name];
+        } else {
+            $parent = $this->htmlDefinition->info_parent_def;
+        }
+        if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) {
+            return false;
+        }
+        return true;
+    }
+    
+    /**
+     * Handler that is called when a text token is processed
+     */
+    public function handleText(&$token) {}
+    
+    /**
+     * Handler that is called when a start or empty token is processed
+     */
+    public function handleElement(&$token) {}
+    
+    /**
+     * Notifier that is called when an end token is processed
+     * @note This differs from handlers in that the token is read-only
+     */
+    public function notifyEnd($token) {}
+    
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser/Flexible.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser/Flexible.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser/Flexible.php (revision 21)
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * Performs safe variable parsing based on types which can be used by
+ * users. This may not be able to represent all possible data inputs,
+ * however.
+ */
+class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
+{
+    
+    protected function parseImplementation($var, $type, $allow_null) {
+        if ($allow_null && $var === null) return null;
+        switch ($type) {
+            // Note: if code "breaks" from the switch, it triggers a generic
+            // exception to be thrown. Specific errors can be specifically
+            // done here.
+            case 'mixed':
+            case 'istring':
+            case 'string':
+            case 'text':
+            case 'itext':
+                return $var;
+            case 'int':
+                if (is_string($var) && ctype_digit($var)) $var = (int) $var;
+                return $var;
+            case 'float':
+                if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var;
+                return $var;
+            case 'bool':
+                if (is_int($var) && ($var === 0 || $var === 1)) {
+                    $var = (bool) $var;
+                } elseif (is_string($var)) {
+                    if ($var == 'on' || $var == 'true' || $var == '1') {
+                        $var = true;
+                    } elseif ($var == 'off' || $var == 'false' || $var == '0') {
+                        $var = false;
+                    } else {
+                        throw new HTMLPurifier_VarParserException("Unrecognized value '$var' for $type");
+                    }
+                }
+                return $var;
+            case 'list':
+            case 'hash':
+            case 'lookup':
+                if (is_string($var)) {
+                    // special case: technically, this is an array with
+                    // a single empty string item, but having an empty
+                    // array is more intuitive
+                    if ($var == '') return array();
+                    if (strpos($var, "\n") === false && strpos($var, "\r") === false) {
+                        // simplistic string to array method that only works
+                        // for simple lists of tag names or alphanumeric characters
+                        $var = explode(',',$var);
+                    } else {
+                        $var = preg_split('/(,|[\n\r]+)/', $var);
+                    }
+                    // remove spaces
+                    foreach ($var as $i => $j) $var[$i] = trim($j);
+                    if ($type === 'hash') {
+                        // key:value,key2:value2
+                        $nvar = array();
+                        foreach ($var as $keypair) {
+                            $c = explode(':', $keypair, 2);
+                            if (!isset($c[1])) continue;
+                            $nvar[$c[0]] = $c[1];
+                        }
+                        $var = $nvar;
+                    }
+                }
+                if (!is_array($var)) break;
+                $keys = array_keys($var);
+                if ($keys === array_keys($keys)) {
+                    if ($type == 'list') return $var;
+                    elseif ($type == 'lookup') {
+                        $new = array();
+                        foreach ($var as $key) {
+                            $new[$key] = true;
+                        }
+                        return $new;
+                    } else break;
+                }
+                if ($type === 'lookup') {
+                    foreach ($var as $key => $value) {
+                        $var[$key] = true;
+                    }
+                }
+                return $var;
+            default:
+                $this->errorInconsistent(__CLASS__, $type);
+        }
+        $this->errorGeneric($var, $type);
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser/Native.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser/Native.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser/Native.php (revision 21)
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * This variable parser uses PHP's internal code engine. Because it does
+ * this, it can represent all inputs; however, it is dangerous and cannot
+ * be used by users.
+ */
+class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser
+{
+    
+    protected function parseImplementation($var, $type, $allow_null) {
+        return $this->evalExpression($var);
+    }
+    
+    protected function evalExpression($expr) {
+        $var = null;
+        $result = eval("\$var = $expr;");
+        if ($result === false) {
+            throw new HTMLPurifier_VarParserException("Fatal error in evaluated code");
+        }
+        return $var;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Border.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Border.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Border.php (revision 21)
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * Pre-transform that changes deprecated border attribute to CSS.
+ */
+class HTMLPurifier_AttrTransform_Border extends HTMLPurifier_AttrTransform {
+
+    public function transform($attr, $config, $context) {
+        if (!isset($attr['border'])) return $attr;
+        $border_width = $this->confiscateAttr($attr, 'border');
+        // some validation should happen here
+        $this->prependCSS($attr, "border:{$border_width}px solid;");
+        return $attr;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ImgRequired.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ImgRequired.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ImgRequired.php (revision 21)
@@ -0,0 +1,36 @@
+<?php
+
+// must be called POST validation
+
+/**
+ * Transform that supplies default values for the src and alt attributes
+ * in img tags, as well as prevents the img tag from being removed
+ * because of a missing alt tag. This needs to be registered as both
+ * a pre and post attribute transform.
+ */
+class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
+{
+    
+    public function transform($attr, $config, $context) {
+        
+        $src = true;
+        if (!isset($attr['src'])) {
+            if ($config->get('Core', 'RemoveInvalidImg')) return $attr;
+            $attr['src'] = $config->get('Attr', 'DefaultInvalidImage');
+            $src = false;
+        }
+        
+        if (!isset($attr['alt'])) {
+            if ($src) {
+                $attr['alt'] = basename($attr['src']);
+            } else {
+                $attr['alt'] = $config->get('Attr', 'DefaultInvalidImageAlt');
+            }
+        }
+        
+        return $attr;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/EnumToCSS.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/EnumToCSS.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/EnumToCSS.php (revision 21)
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * Generic pre-transform that converts an attribute with a fixed number of
+ * values (enumerated) to CSS.
+ */
+class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform {
+    
+    /**
+     * Name of attribute to transform from
+     */
+    protected $attr;
+    
+    /**
+     * Lookup array of attribute values to CSS
+     */
+    protected $enumToCSS = array();
+    
+    /**
+     * Case sensitivity of the matching
+     * @warning Currently can only be guaranteed to work with ASCII
+     *          values.
+     */
+    protected $caseSensitive = false;
+    
+    /**
+     * @param $attr String attribute name to transform from
+     * @param $enumToCSS Lookup array of attribute values to CSS
+     * @param $case_sensitive Boolean case sensitivity indicator, default false
+     */
+    public function __construct($attr, $enum_to_css, $case_sensitive = false) {
+        $this->attr = $attr;
+        $this->enumToCSS = $enum_to_css;
+        $this->caseSensitive = (bool) $case_sensitive;
+    }
+    
+    public function transform($attr, $config, $context) {
+        
+        if (!isset($attr[$this->attr])) return $attr;
+        
+        $value = trim($attr[$this->attr]);
+        unset($attr[$this->attr]);
+        
+        if (!$this->caseSensitive) $value = strtolower($value);
+        
+        if (!isset($this->enumToCSS[$value])) {
+            return $attr;
+        }
+        
+        $this->prependCSS($attr, $this->enumToCSS[$value]);
+        
+        return $attr;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Name.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Name.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Name.php (revision 21)
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Pre-transform that changes deprecated name attribute to ID if necessary
+ */
+class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform
+{
+    
+    public function transform($attr, $config, $context) {
+        if (!isset($attr['name'])) return $attr;
+        $id = $this->confiscateAttr($attr, 'name');
+        if ( isset($attr['id']))   return $attr;
+        $attr['id'] = $id;
+        return $attr;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Lang.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Lang.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Lang.php (revision 21)
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * Post-transform that copies lang's value to xml:lang (and vice-versa)
+ * @note Theoretically speaking, this could be a pre-transform, but putting
+ *       post is more efficient.
+ */
+class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
+{
+    
+    public function transform($attr, $config, $context) {
+        
+        $lang     = isset($attr['lang']) ? $attr['lang'] : false;
+        $xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
+        
+        if ($lang !== false && $xml_lang === false) {
+            $attr['xml:lang'] = $lang;
+        } elseif ($xml_lang !== false) {
+            $attr['lang'] = $xml_lang;
+        }
+        
+        return $attr;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Length.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Length.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/Length.php (revision 21)
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Class for handling width/height length attribute transformations to CSS
+ */
+class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform
+{
+    
+    protected $name;
+    protected $cssName;
+    
+    public function __construct($name, $css_name = null) {
+        $this->name = $name;
+        $this->cssName = $css_name ? $css_name : $name;
+    }
+    
+    public function transform($attr, $config, $context) {
+        if (!isset($attr[$this->name])) return $attr;
+        $length = $this->confiscateAttr($attr, $this->name);
+        if(ctype_digit($length)) $length .= 'px';
+        $this->prependCSS($attr, $this->cssName . ":$length;");
+        return $attr;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BdoDir.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BdoDir.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BdoDir.php (revision 21)
@@ -0,0 +1,18 @@
+<?php
+
+// this MUST be placed in post, as it assumes that any value in dir is valid
+
+/**
+ * Post-trasnform that ensures that bdo tags have the dir attribute set.
+ */
+class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
+{
+    
+    public function transform($attr, $config, $context) {
+        if (isset($attr['dir'])) return $attr;
+        $attr['dir'] = $config->get('Attr', 'DefaultTextDir');
+        return $attr;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ScriptRequired.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ScriptRequired.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ScriptRequired.php (revision 21)
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * Implements required attribute stipulation for <script>
+ */
+class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform
+{
+    public function transform($attr, $config, $context) {
+        if (!isset($attr['type'])) {
+            $attr['type'] = 'text/javascript';
+        }
+        return $attr;
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BgColor.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BgColor.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BgColor.php (revision 21)
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * Pre-transform that changes deprecated bgcolor attribute to CSS.
+ */
+class HTMLPurifier_AttrTransform_BgColor extends HTMLPurifier_AttrTransform {
+
+    public function transform($attr, $config, $context) {
+        
+        if (!isset($attr['bgcolor'])) return $attr;
+        
+        $bgcolor = $this->confiscateAttr($attr, 'bgcolor');
+        // some validation should happen here
+        
+        $this->prependCSS($attr, "background-color:$bgcolor;");
+        
+        return $attr;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BoolToCSS.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BoolToCSS.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/BoolToCSS.php (revision 21)
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * Pre-transform that changes converts a boolean attribute to fixed CSS
+ */
+class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform {
+    
+    /**
+     * Name of boolean attribute that is trigger
+     */
+    protected $attr;
+    
+    /**
+     * CSS declarations to add to style, needs trailing semicolon
+     */
+    protected $css;
+    
+    /**
+     * @param $attr string attribute name to convert from
+     * @param $css string CSS declarations to add to style (needs semicolon)
+     */
+    public function __construct($attr, $css) {
+        $this->attr = $attr;
+        $this->css  = $css;
+    }
+    
+    public function transform($attr, $config, $context) {
+        if (!isset($attr[$this->attr])) return $attr;
+        unset($attr[$this->attr]);
+        $this->prependCSS($attr, $this->css);
+        return $attr;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ImgSpace.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ImgSpace.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform/ImgSpace.php (revision 21)
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * Pre-transform that changes deprecated hspace and vspace attributes to CSS
+ */
+class HTMLPurifier_AttrTransform_ImgSpace extends HTMLPurifier_AttrTransform {
+    
+    protected $attr;
+    protected $css = array(
+        'hspace' => array('left', 'right'),
+        'vspace' => array('top', 'bottom')
+    );
+    
+    public function __construct($attr) {
+        $this->attr = $attr;
+        if (!isset($this->css[$attr])) {
+            trigger_error(htmlspecialchars($attr) . ' is not valid space attribute');
+        }
+    }
+    
+    public function transform($attr, $config, $context) {
+        
+        if (!isset($attr[$this->attr])) return $attr;
+        
+        $width = $this->confiscateAttr($attr, $this->attr);
+        // some validation could happen here
+        
+        if (!isset($this->css[$this->attr])) return $attr;
+        
+        $style = '';
+        foreach ($this->css[$this->attr] as $suffix) {
+            $property = "margin-$suffix";
+            $style .= "$property:{$width}px;";
+        }
+        
+        $this->prependCSS($attr, $style);
+        
+        return $attr;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityLookup/entities.ser
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityLookup/entities.ser (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityLookup/entities.ser (revision 21)
@@ -0,0 +1,6 @@
+a:246:{s:4:"nbsp";s:2:"Â ";s:5:"iexcl";s:2:"Â¡";s:4:"cent";s:2:"Â¢";s:5:"pound";s:2:"Â£";s:6:"curren";s:2:"Â€";s:3:"yen";s:2:"Â¥";s:6:"brvbar";s:2:"ÂŠ";s:4:"sect";s:2:"Â§";s:3:"uml";s:2:"Âš";s:4:"copy";s:2:"Â©";s:4:"ordf";s:2:"Âª";s:5:"laquo";s:2:"Â«";s:3:"not";s:2:"Â¬";s:3:"shy";s:2:"Â­";s:3:"reg";s:2:"Â®";s:4:"macr";s:2:"Â¯";s:3:"deg";s:2:"Â°";s:6:"plusmn";s:2:"Â±";s:5:"acute";s:2:"ÂŽ";s:5:"micro";s:2:"Âµ";s:4:"para";s:2:"Â¶";s:6:"middot";s:2:"Â·";s:5:"cedil";s:2:"Âž";s:4:"ordm";s:2:"Âº";s:5:"raquo";s:2:"Â»";s:6:"iquest";s:2:"Â¿";s:6:"Agrave";s:2:"Ã";s:6:"Aacute";s:2:"Ã";s:5:"Acirc";s:2:"Ã";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ã";s:5:"Aring";s:2:"Ã
+";s:5:"AElig";s:2:"Ã";s:6:"Ccedil";s:2:"Ã";s:6:"Egrave";s:2:"Ã";s:6:"Eacute";s:2:"Ã";s:5:"Ecirc";s:2:"Ã";s:4:"Euml";s:2:"Ã";s:6:"Igrave";s:2:"Ã";s:6:"Iacute";s:2:"Ã";s:5:"Icirc";s:2:"Ã";s:4:"Iuml";s:2:"Ã";s:3:"ETH";s:2:"Ã";s:6:"Ntilde";s:2:"Ã";s:6:"Ograve";s:2:"Ã";s:6:"Oacute";s:2:"Ã";s:5:"Ocirc";s:2:"Ã";s:6:"Otilde";s:2:"Ã";s:4:"Ouml";s:2:"Ã";s:5:"times";s:2:"Ã";s:6:"Oslash";s:2:"Ã";s:6:"Ugrave";s:2:"Ã";s:6:"Uacute";s:2:"Ã";s:5:"Ucirc";s:2:"Ã";s:4:"Uuml";s:2:"Ã";s:6:"Yacute";s:2:"Ã";s:5:"THORN";s:2:"Ã";s:5:"szlig";s:2:"Ã";s:6:"agrave";s:2:"Ã ";s:6:"aacute";s:2:"Ã¡";s:5:"acirc";s:2:"Ã¢";s:6:"atilde";s:2:"Ã£";s:4:"auml";s:2:"Ã€";s:5:"aring";s:2:"Ã¥";s:5:"aelig";s:2:"ÃŠ";s:6:"ccedil";s:2:"Ã§";s:6:"egrave";s:2:"Ãš";s:6:"eacute";s:2:"Ã©";s:5:"ecirc";s:2:"Ãª";s:4:"euml";s:2:"Ã«";s:6:"igrave";s:2:"Ã¬";s:6:"iacute";s:2:"Ã­";s:5:"icirc";s:2:"Ã®";s:4:"iuml";s:2:"Ã¯";s:3:"eth";s:2:"Ã°";s:6:"ntilde";s:2:"Ã±";s:6:"ograve";s:2:"Ã²";s:6:"oacute";s:2:"Ã³";s:5:"ocirc";s:2:"ÃŽ";s:6:"otilde";s:2:"Ãµ";s:4:"ouml";s:2:"Ã¶";s:6:"divide";s:2:"Ã·";s:6:"oslash";s:2:"Ãž";s:6:"ugrave";s:2:"Ã¹";s:6:"uacute";s:2:"Ãº";s:5:"ucirc";s:2:"Ã»";s:4:"uuml";s:2:"ÃŒ";s:6:"yacute";s:2:"Ãœ";s:5:"thorn";s:2:"ÃŸ";s:4:"yuml";s:2:"Ã¿";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Å";s:5:"oelig";s:2:"Å";s:6:"Scaron";s:2:"Å ";s:6:"scaron";s:2:"Å¡";s:4:"Yuml";s:2:"Åž";s:4:"circ";s:2:"Ë";s:5:"tilde";s:2:"Ë";s:4:"ensp";s:3:"â";s:4:"emsp";s:3:"â";s:6:"thinsp";s:3:"â";s:4:"zwnj";s:3:"â";s:3:"zwj";s:3:"â";s:3:"lrm";s:3:"â";s:3:"rlm";s:3:"â";s:5:"ndash";s:3:"â";s:5:"mdash";s:3:"â";s:5:"lsquo";s:3:"â";s:5:"rsquo";s:3:"â";s:5:"sbquo";s:3:"â";s:5:"ldquo";s:3:"â";s:5:"rdquo";s:3:"â";s:5:"bdquo";s:3:"â";s:6:"dagger";s:3:"â ";s:6:"Dagger";s:3:"â¡";s:6:"permil";s:3:"â°";s:6:"lsaquo";s:3:"â¹";s:6:"rsaquo";s:3:"âº";s:4:"euro";s:3:"â¬";s:4:"fnof";s:2:"Æ";s:5:"Alpha";s:2:"Î";s:4:"Beta";s:2:"Î";s:5:"Gamma";s:2:"Î";s:5:"Delta";s:2:"Î";s:7:"Epsilon";s:2:"Î";s:4:"Zeta";s:2:"Î";s:3:"Eta";s:2:"Î";s:5:"Theta";s:2:"Î";s:4:"Iota";s:2:"Î";s:5:"Kappa";s:2:"Î";s:6:"Lambda";s:2:"Î";s:2:"Mu";s:2:"Î";s:2:"Nu";s:2:"Î";s:2:"Xi";s:2:"Î";s:7:"Omicron";s:2:"Î";s:2:"Pi";s:2:"Î ";s:3:"Rho";s:2:"Î¡";s:5:"Sigma";s:2:"Î£";s:3:"Tau";s:2:"Î€";s:7:"Upsilon";s:2:"Î¥";s:3:"Phi";s:2:"ÎŠ";s:3:"Chi";s:2:"Î§";s:3:"Psi";s:2:"Îš";s:5:"Omega";s:2:"Î©";s:5:"alpha";s:2:"Î±";s:4:"beta";s:2:"Î²";s:5:"gamma";s:2:"Î³";s:5:"delta";s:2:"ÎŽ";s:7:"epsilon";s:2:"Îµ";s:4:"zeta";s:2:"Î¶";s:3:"eta";s:2:"Î·";s:5:"theta";s:2:"Îž";s:4:"iota";s:2:"Î¹";s:5:"kappa";s:2:"Îº";s:6:"lambda";s:2:"Î»";s:2:"mu";s:2:"ÎŒ";s:2:"nu";s:2:"Îœ";s:2:"xi";s:2:"ÎŸ";s:7:"omicron";s:2:"Î¿";s:2:"pi";s:2:"Ï";s:3:"rho";s:2:"Ï";s:6:"sigmaf";s:2:"Ï";s:5:"sigma";s:2:"Ï";s:3:"tau";s:2:"Ï";s:7:"upsilon";s:2:"Ï
+";s:3:"phi";s:2:"Ï";s:3:"chi";s:2:"Ï";s:3:"psi";s:2:"Ï";s:5:"omega";s:2:"Ï";s:8:"thetasym";s:2:"Ï";s:5:"upsih";s:2:"Ï";s:3:"piv";s:2:"Ï";s:4:"bull";s:3:"â¢";s:6:"hellip";s:3:"âŠ";s:5:"prime";s:3:"â²";s:5:"Prime";s:3:"â³";s:5:"oline";s:3:"âŸ";s:5:"frasl";s:3:"â";s:6:"weierp";s:3:"â";s:5:"image";s:3:"â";s:4:"real";s:3:"â";s:5:"trade";s:3:"â¢";s:7:"alefsym";s:3:"âµ";s:4:"larr";s:3:"â";s:4:"uarr";s:3:"â";s:4:"rarr";s:3:"â";s:4:"darr";s:3:"â";s:4:"harr";s:3:"â";s:5:"crarr";s:3:"âµ";s:4:"lArr";s:3:"â";s:4:"uArr";s:3:"â";s:4:"rArr";s:3:"â";s:4:"dArr";s:3:"â";s:4:"hArr";s:3:"â";s:6:"forall";s:3:"â";s:4:"part";s:3:"â";s:5:"exist";s:3:"â";s:5:"empty";s:3:"â
+";s:5:"nabla";s:3:"â";s:4:"isin";s:3:"â";s:5:"notin";s:3:"â";s:2:"ni";s:3:"â";s:4:"prod";s:3:"â";s:3:"sum";s:3:"â";s:5:"minus";s:3:"â";s:6:"lowast";s:3:"â";s:5:"radic";s:3:"â";s:4:"prop";s:3:"â";s:5:"infin";s:3:"â";s:3:"ang";s:3:"â ";s:3:"and";s:3:"â§";s:2:"or";s:3:"âš";s:3:"cap";s:3:"â©";s:3:"cup";s:3:"âª";s:3:"int";s:3:"â«";s:3:"sim";s:3:"âŒ";s:4:"cong";s:3:"â
+";s:5:"asymp";s:3:"â";s:2:"ne";s:3:"â ";s:5:"equiv";s:3:"â¡";s:2:"le";s:3:"â€";s:2:"ge";s:3:"â¥";s:3:"sub";s:3:"â";s:3:"sup";s:3:"â";s:4:"nsub";s:3:"â";s:4:"sube";s:3:"â";s:4:"supe";s:3:"â";s:5:"oplus";s:3:"â";s:6:"otimes";s:3:"â";s:4:"perp";s:3:"â¥";s:4:"sdot";s:3:"â
+";s:5:"lceil";s:3:"â";s:5:"rceil";s:3:"â";s:6:"lfloor";s:3:"â";s:6:"rfloor";s:3:"â";s:4:"lang";s:3:"â©";s:4:"rang";s:3:"âª";s:3:"loz";s:3:"â";s:6:"spades";s:3:"â ";s:5:"clubs";s:3:"â£";s:6:"hearts";s:3:"â¥";s:5:"diams";s:3:"âŠ";}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Generator.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Generator.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Generator.php (revision 21)
@@ -0,0 +1,176 @@
+<?php
+
+/**
+ * Generates HTML from tokens.
+ * @todo Refactor interface so that configuration/context is determined
+ *       upon instantiation, no need for messy generateFromTokens() calls
+ * @todo Make some of the more internal functions protected, and have
+ *       unit tests work around that
+ */
+class HTMLPurifier_Generator
+{
+    
+    /**
+     * Whether or not generator should produce XML output
+     */
+    private $_xhtml = true;
+    
+    /**
+     * :HACK: Whether or not generator should comment the insides of <script> tags
+     */
+    private $_scriptFix = false;
+    
+    /**
+     * Cache of HTMLDefinition during HTML output to determine whether or
+     * not attributes should be minimized.
+     */
+    private $_def;
+    
+    /**
+     * Configuration for the generator
+     */
+    protected $config;
+    
+    /**
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     */
+    public function __construct($config = null, $context = null) {
+        if (!$config) $config = HTMLPurifier_Config::createDefault();
+        $this->config = $config;
+        $this->_scriptFix = $config->get('Output', 'CommentScriptContents');
+        $this->_def = $config->getHTMLDefinition();
+        $this->_xhtml = $this->_def->doctype->xml;
+    }
+    
+    /**
+     * Generates HTML from an array of tokens.
+     * @param $tokens Array of HTMLPurifier_Token
+     * @param $config HTMLPurifier_Config object
+     * @return Generated HTML
+     */
+    public function generateFromTokens($tokens) {
+        if (!$tokens) return '';
+        
+        // Basic algorithm
+        $html = '';
+        for ($i = 0, $size = count($tokens); $i < $size; $i++) {
+            if ($this->_scriptFix && $tokens[$i]->name === 'script'
+                && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) {
+                // script special case
+                // the contents of the script block must be ONE token
+                // for this to work.
+                $html .= $this->generateFromToken($tokens[$i++]);
+                $html .= $this->generateScriptFromToken($tokens[$i++]);
+            }
+            $html .= $this->generateFromToken($tokens[$i]);
+        }
+        
+        // Tidy cleanup
+        if (extension_loaded('tidy') && $this->config->get('Output', 'TidyFormat')) {
+            $tidy = new Tidy;
+            $tidy->parseString($html, array(
+               'indent'=> true,
+               'output-xhtml' => $this->_xhtml,
+               'show-body-only' => true,
+               'indent-spaces' => 2,
+               'wrap' => 68,
+            ), 'utf8');
+            $tidy->cleanRepair();
+            $html = (string) $tidy; // explicit cast necessary
+        }
+        
+        // Normalize newlines to system defined value
+        $nl = $this->config->get('Output', 'Newline');
+        if ($nl === null) $nl = PHP_EOL;
+        if ($nl !== "\n") $html = str_replace("\n", $nl, $html);
+        return $html;
+    }
+    
+    /**
+     * Generates HTML from a single token.
+     * @param $token HTMLPurifier_Token object.
+     * @return Generated HTML
+     */
+    public function generateFromToken($token) {
+        if (!$token instanceof HTMLPurifier_Token) {
+            trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
+            return '';
+            
+        } elseif ($token instanceof HTMLPurifier_Token_Start) {
+            $attr = $this->generateAttributes($token->attr, $token->name);
+            return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
+            
+        } elseif ($token instanceof HTMLPurifier_Token_End) {
+            return '</' . $token->name . '>';
+            
+        } elseif ($token instanceof HTMLPurifier_Token_Empty) {
+            $attr = $this->generateAttributes($token->attr, $token->name);
+             return '<' . $token->name . ($attr ? ' ' : '') . $attr .
+                ( $this->_xhtml ? ' /': '' ) // <br /> v. <br>
+                . '>';
+            
+        } elseif ($token instanceof HTMLPurifier_Token_Text) {
+            return $this->escape($token->data, ENT_NOQUOTES);
+            
+        } elseif ($token instanceof HTMLPurifier_Token_Comment) {
+            return '<!--' . $token->data . '-->';
+        } else {
+            return '';
+            
+        }
+    }
+    
+    /**
+     * Special case processor for the contents of script tags
+     * @warning This runs into problems if there's already a literal
+     *          --> somewhere inside the script contents.
+     */
+    public function generateScriptFromToken($token) {
+        if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token);
+        // Thanks <http://lachy.id.au/log/2005/05/script-comments>
+        $data = preg_replace('#//\s*$#', '', $token->data);
+        return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>';
+    }
+    
+    /**
+     * Generates attribute declarations from attribute array.
+     * @note This does not include the leading or trailing space.
+     * @param $assoc_array_of_attributes Attribute array
+     * @param $element Name of element attributes are for, used to check
+     *        attribute minimization.
+     * @return Generate HTML fragment for insertion.
+     */
+    public function generateAttributes($assoc_array_of_attributes, $element = false) {
+        $html = '';
+        foreach ($assoc_array_of_attributes as $key => $value) {
+            if (!$this->_xhtml) {
+                // Remove namespaced attributes
+                if (strpos($key, ':') !== false) continue;
+                // Check if we should minimize the attribute: val="val" -> val
+                if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
+                    $html .= $key . ' ';
+                    continue;
+                }
+            }
+            $html .= $key.'="'.$this->escape($value).'" ';
+        }
+        return rtrim($html);
+    }
+    
+    /**
+     * Escapes raw text data.
+     * @todo This really ought to be protected, but until we have a facility
+     *       for properly generating HTML here w/o using tokens, it stays
+     *       public.
+     * @param $string String data to escape for HTML.
+     * @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is
+     *               permissible for non-attribute output.
+     * @return String escaped data.
+     */
+    public function escape($string, $quote = ENT_COMPAT) {
+        return htmlspecialchars($string, $quote, 'UTF-8');
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Definition.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Definition.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Definition.php (revision 21)
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Super-class for definition datatype objects, implements serialization
+ * functions for the class.
+ */
+abstract class HTMLPurifier_Definition
+{
+    
+    /**
+     * Has setup() been called yet?
+     */
+    public $setup = false;
+    
+    /**
+     * What type of definition is it?
+     */
+    public $type;
+    
+    /**
+     * Sets up the definition object into the final form, something
+     * not done by the constructor
+     * @param $config HTMLPurifier_Config instance
+     */
+    abstract protected function doSetup($config);
+    
+    /**
+     * Setup function that aborts if already setup
+     * @param $config HTMLPurifier_Config instance
+     */
+    public function setup($config) {
+        if ($this->setup) return;
+        $this->setup = true;
+        $this->doSetup($config);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en-x-testmini.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en-x-testmini.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en-x-testmini.php (revision 21)
@@ -0,0 +1,11 @@
+<?php
+
+// private language message file for unit testing purposes
+// this language file has no class associated with it
+
+$fallback = 'en';
+
+$messages = array(
+    'HTMLPurifier' => 'HTML Purifier XNone'
+);
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en.php (revision 21)
@@ -0,0 +1,57 @@
+<?php
+
+$fallback = false;
+
+$messages = array(
+
+'HTMLPurifier' => 'HTML Purifier',
+
+// for unit testing purposes
+'LanguageFactoryTest: Pizza' => 'Pizza',
+'LanguageTest: List' => '$1',
+'LanguageTest: Hash' => '$1.Keys; $1.Values',
+
+'Item separator' => ', ',
+'Item separator last' => ' and ', // non-Harvard style
+
+'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.',
+'ErrorCollector: At line' => ' at line $line',
+
+'Lexer: Unclosed comment'      => 'Unclosed comment',
+'Lexer: Unescaped lt'          => 'Unescaped less-than sign (<) should be &lt;',
+'Lexer: Missing gt'            => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped',
+'Lexer: Missing attribute key' => 'Attribute declaration has no key',
+'Lexer: Missing end quote'     => 'Attribute declaration has no end quote',
+
+'Strategy_RemoveForeignElements: Tag transform'              => '<$1> element transformed into $CurrentToken.Serialized',
+'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1',
+'Strategy_RemoveForeignElements: Foreign element to text'    => 'Unrecognized $CurrentToken.Serialized tag converted to text',
+'Strategy_RemoveForeignElements: Foreign element removed'    => 'Unrecognized $CurrentToken.Serialized tag removed',
+'Strategy_RemoveForeignElements: Comment removed'            => 'Comment containing "$CurrentToken.Data" removed',
+'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed',
+'Strategy_RemoveForeignElements: Token removed to end'       => 'Tags and text starting from $1 element where removed to end',
+
+'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed',
+'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text',
+'Strategy_MakeWellFormed: Tag auto closed'             => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact',
+'Strategy_MakeWellFormed: Stray end tag removed'       => 'Stray $CurrentToken.Serialized tag removed',
+'Strategy_MakeWellFormed: Stray end tag to text'       => 'Stray $CurrentToken.Serialized tag converted to text',
+'Strategy_MakeWellFormed: Tag closed by element end'   => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized',
+'Strategy_MakeWellFormed: Tag closed by document end'  => '$1.Compact tag started on line $1.Line closed by end of document',
+
+'Strategy_FixNesting: Node removed'          => '$CurrentToken.Compact node removed',
+'Strategy_FixNesting: Node excluded'         => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element',
+'Strategy_FixNesting: Node reorganized'      => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model',
+'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed',
+
+'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys',
+'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed',
+
+);
+
+$errorNames = array(
+    E_ERROR => 'Error',
+    E_WARNING => 'Warning',
+    E_NOTICE => 'Notice'
+);
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en-x-test.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en-x-test.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/messages/en-x-test.php (revision 21)
@@ -0,0 +1,10 @@
+<?php
+
+// private language message file for unit testing purposes
+
+$fallback = 'en';
+
+$messages = array(
+    'HTMLPurifier' => 'HTML Purifier X'
+);
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/classes/en-x-test.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/classes/en-x-test.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language/classes/en-x-test.php (revision 21)
@@ -0,0 +1,11 @@
+<?php
+
+// private class for unit testing
+
+class HTMLPurifier_Language_en_x_test extends HTMLPurifier_Language
+{
+    
+    
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URI.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URI.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URI.php (revision 21)
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * HTML Purifier's internal representation of a URI.
+ * @note
+ *      Internal data-structures are completely escaped. If the data needs
+ *      to be used in a non-URI context (which is very unlikely), be sure
+ *      to decode it first. The URI may not necessarily be well-formed until
+ *      validate() is called.
+ */
+class HTMLPurifier_URI
+{
+    
+    public $scheme, $userinfo, $host, $port, $path, $query, $fragment;
+    
+    /**
+     * @note Automatically normalizes scheme and port
+     */
+    public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment) {
+        $this->scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme);
+        $this->userinfo = $userinfo;
+        $this->host = $host;
+        $this->port = is_null($port) ? $port : (int) $port;
+        $this->path = $path;
+        $this->query = $query;
+        $this->fragment = $fragment;
+    }
+    
+    /**
+     * Retrieves a scheme object corresponding to the URI's scheme/default
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     * @return Scheme object appropriate for validating this URI
+     */
+    public function getSchemeObj($config, $context) {
+        $registry = HTMLPurifier_URISchemeRegistry::instance();
+        if ($this->scheme !== null) {
+            $scheme_obj = $registry->getScheme($this->scheme, $config, $context);
+            if (!$scheme_obj) return false; // invalid scheme, clean it out
+        } else {
+            // no scheme: retrieve the default one
+            $def = $config->getDefinition('URI');
+            $scheme_obj = $registry->getScheme($def->defaultScheme, $config, $context);
+            if (!$scheme_obj) {
+                // something funky happened to the default scheme object
+                trigger_error(
+                    'Default scheme object "' . $def->defaultScheme . '" was not readable',
+                    E_USER_WARNING
+                );
+                return false;
+            }
+        }
+        return $scheme_obj;
+    }
+    
+    /**
+     * Generic validation method applicable for all schemes. May modify
+     * this URI in order to get it into a compliant form.
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     * @return True if validation/filtering succeeds, false if failure
+     */
+    public function validate($config, $context) {
+        
+        // ABNF definitions from RFC 3986
+        $chars_sub_delims = '!$&\'()*+,;=';
+        $chars_gen_delims = ':/?#[]@';
+        $chars_pchar = $chars_sub_delims . ':@';
+        
+        // validate scheme (MUST BE FIRST!)
+        if (!is_null($this->scheme) && is_null($this->host)) {
+            $def = $config->getDefinition('URI');
+            if ($def->defaultScheme === $this->scheme) {
+                $this->scheme = null;
+            }
+        }
+        
+        // validate host
+        if (!is_null($this->host)) {
+            $host_def = new HTMLPurifier_AttrDef_URI_Host();
+            $this->host = $host_def->validate($this->host, $config, $context);
+            if ($this->host === false) $this->host = null;
+        }
+        
+        // validate username
+        if (!is_null($this->userinfo)) {
+            $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');
+            $this->userinfo = $encoder->encode($this->userinfo);
+        }
+        
+        // validate port
+        if (!is_null($this->port)) {
+            if ($this->port < 1 || $this->port > 65535) $this->port = null;
+        }
+        
+        // validate path
+        $path_parts = array();
+        $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
+        if (!is_null($this->host)) {
+            // path-abempty (hier and relative)
+            $this->path = $segments_encoder->encode($this->path);
+        } elseif ($this->path !== '' && $this->path[0] === '/') {
+            // path-absolute (hier and relative)
+            if (strlen($this->path) >= 2 && $this->path[1] === '/') {
+                // This shouldn't ever happen!
+                $this->path = '';
+            } else {
+                $this->path = $segments_encoder->encode($this->path);
+            }
+        } elseif (!is_null($this->scheme) && $this->path !== '') {
+            // path-rootless (hier)
+            // Short circuit evaluation means we don't need to check nz
+            $this->path = $segments_encoder->encode($this->path);
+        } elseif (is_null($this->scheme) && $this->path !== '') {
+            // path-noscheme (relative)
+            // (once again, not checking nz)
+            $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');
+            $c = strpos($this->path, '/');
+            if ($c !== false) {
+                $this->path = 
+                    $segment_nc_encoder->encode(substr($this->path, 0, $c)) .
+                    $segments_encoder->encode(substr($this->path, $c));
+            } else {
+                $this->path = $segment_nc_encoder->encode($this->path);
+            }
+        } else {
+            // path-empty (hier and relative)
+            $this->path = ''; // just to be safe
+        }
+        
+        return true;
+        
+    }
+    
+    /**
+     * Convert URI back to string
+     * @return String URI appropriate for output
+     */
+    public function toString() {
+        // reconstruct authority
+        $authority = null;
+        if (!is_null($this->host)) {
+            $authority = '';
+            if(!is_null($this->userinfo)) $authority .= $this->userinfo . '@';
+            $authority .= $this->host;
+            if(!is_null($this->port))     $authority .= ':' . $this->port;
+        }
+        
+        // reconstruct the result
+        $result = '';
+        if (!is_null($this->scheme))    $result .= $this->scheme . ':';
+        if (!is_null($authority))       $result .=  '//' . $authority;
+        $result .= $this->path;
+        if (!is_null($this->query))     $result .= '?' . $this->query;
+        if (!is_null($this->fragment))  $result .= '#' . $this->fragment;
+        
+        return $result;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTypes.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTypes.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTypes.php (revision 21)
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Provides lookup array of attribute types to HTMLPurifier_AttrDef objects
+ */
+class HTMLPurifier_AttrTypes
+{
+    /**
+     * Lookup array of attribute string identifiers to concrete implementations
+     */
+    protected $info = array();
+    
+    /**
+     * Constructs the info array, supplying default implementations for attribute
+     * types.
+     */
+    public function __construct() {
+        // pseudo-types, must be instantiated via shorthand
+        $this->info['Enum']    = new HTMLPurifier_AttrDef_Enum();
+        $this->info['Bool']    = new HTMLPurifier_AttrDef_HTML_Bool();
+        
+        $this->info['CDATA']    = new HTMLPurifier_AttrDef_Text();
+        $this->info['ID']       = new HTMLPurifier_AttrDef_HTML_ID();
+        $this->info['Length']   = new HTMLPurifier_AttrDef_HTML_Length();
+        $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength();
+        $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens();
+        $this->info['Pixels']   = new HTMLPurifier_AttrDef_HTML_Pixels();
+        $this->info['Text']     = new HTMLPurifier_AttrDef_Text();
+        $this->info['URI']      = new HTMLPurifier_AttrDef_URI();
+        $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
+        $this->info['Color']    = new HTMLPurifier_AttrDef_HTML_Color();
+        
+        // unimplemented aliases
+        $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
+        
+        // number is really a positive integer (one or more digits)
+        // FIXME: ^^ not always, see start and value of list items
+        $this->info['Number']   = new HTMLPurifier_AttrDef_Integer(false, false, true);
+    }
+    
+    /**
+     * Retrieves a type
+     * @param $type String type name
+     * @return Object AttrDef for type
+     */
+    public function get($type) {
+        
+        // determine if there is any extra info tacked on
+        if (strpos($type, '#') !== false) list($type, $string) = explode('#', $type, 2);
+        else $string = '';
+        
+        if (!isset($this->info[$type])) {
+            trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR);
+            return;
+        }
+        
+        return $this->info[$type]->make($string);
+        
+    }
+    
+    /**
+     * Sets a new implementation for a type
+     * @param $type String type name
+     * @param $impl Object AttrDef for type
+     */
+    public function set($type, $impl) {
+        $this->info[$type] = $impl;
+    }
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/PercentEncoder.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/PercentEncoder.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/PercentEncoder.php (revision 21)
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * Class that handles operations involving percent-encoding in URIs.
+ *
+ * @warning
+ *      Be careful when reusing instances of PercentEncoder. The object
+ *      you use for normalize() SHOULD NOT be used for encode(), or
+ *      vice-versa.
+ */
+class HTMLPurifier_PercentEncoder
+{
+    
+    /**
+     * Reserved characters to preserve when using encode().
+     */
+    protected $preserve = array();
+    
+    /**
+     * String of characters that should be preserved while using encode().
+     */
+    public function __construct($preserve = false) {
+        // unreserved letters, ought to const-ify
+        for ($i = 48; $i <= 57;  $i++) $this->preserve[$i] = true; // digits
+        for ($i = 65; $i <= 90;  $i++) $this->preserve[$i] = true; // upper-case
+        for ($i = 97; $i <= 122; $i++) $this->preserve[$i] = true; // lower-case
+        $this->preserve[45] = true; // Dash         -
+        $this->preserve[46] = true; // Period       .
+        $this->preserve[95] = true; // Underscore   _
+        $this->preserve[126]= true; // Tilde        ~
+        
+        // extra letters not to escape
+        if ($preserve !== false) {
+            for ($i = 0, $c = strlen($preserve); $i < $c; $i++) {
+                $this->preserve[ord($preserve[$i])] = true;
+            }
+        }
+    }
+    
+    /**
+     * Our replacement for urlencode, it encodes all non-reserved characters,
+     * as well as any extra characters that were instructed to be preserved.
+     * @note
+     *      Assumes that the string has already been normalized, making any
+     *      and all percent escape sequences valid. Percents will not be
+     *      re-escaped, regardless of their status in $preserve
+     * @param $string String to be encoded
+     * @return Encoded string.
+     */
+    public function encode($string) {
+        $ret = '';
+        for ($i = 0, $c = strlen($string); $i < $c; $i++) {
+            if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])]) ) {
+                $ret .= '%' . sprintf('%02X', $int);
+            } else {
+                $ret .= $string[$i];
+            }
+        }
+        return $ret;
+    }
+    
+    /**
+     * Fix up percent-encoding by decoding unreserved characters and normalizing.
+     * @warning This function is affected by $preserve, even though the
+     *          usual desired behavior is for this not to preserve those
+     *          characters. Be careful when reusing instances of PercentEncoder!
+     * @param $string String to normalize
+     */
+    public function normalize($string) {
+        if ($string == '') return '';
+        $parts = explode('%', $string);
+        $ret = array_shift($parts);
+        foreach ($parts as $part) {
+            $length = strlen($part);
+            if ($length < 2) {
+                $ret .= '%25' . $part;
+                continue;
+            }
+            $encoding = substr($part, 0, 2);
+            $text     = substr($part, 2);
+            if (!ctype_xdigit($encoding)) {
+                $ret .= '%25' . $part;
+                continue;
+            }
+            $int = hexdec($encoding);
+            if (isset($this->preserve[$int])) {
+                $ret .= chr($int) . $text;
+                continue;
+            }
+            $encoding = strtoupper($encoding);
+            $ret .= '%' . $encoding . $text;
+        }
+        return $ret;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt (revision 21)
@@ -0,0 +1,9 @@
+Attr.DefaultTextDir
+TYPE: string
+DEFAULT: 'ltr'
+--DESCRIPTION--
+Defines the default text direction (ltr or rtl) of the document being
+parsed.  This generally is the same as the value of the dir attribute in
+HTML, or ltr if that is not specified.
+--ALLOWED--
+'ltr', 'rtl'
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt (revision 21)
@@ -0,0 +1,13 @@
+Core.AggressivelyFixLt
+TYPE: bool
+VERSION: 2.1.0
+DEFAULT: false
+--DESCRIPTION--
+
+This directive enables aggressive pre-filter fixes HTML Purifier can
+perform in order to ensure that open angled-brackets do not get killed
+during parsing stage. Enabling this will result in two preg_replace_callback
+calls and one preg_replace call for every bit of HTML passed through here.
+It is not necessary and will have no effect for PHP 4.
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt (revision 21)
@@ -0,0 +1,8 @@
+URI.HostBlacklist
+TYPE: list
+VERSION: 1.3.0
+DEFAULT: array()
+--DESCRIPTION--
+List of strings that are forbidden in the host of any URI. Use it to kill
+domain names of spam, etc. Note that it will catch anything in the domain,
+so <tt>moo.com</tt> will catch <tt>moo.com.example.com</tt>. 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt (revision 21)
@@ -0,0 +1,10 @@
+URI.DefaultScheme
+TYPE: string
+DEFAULT: 'http'
+--DESCRIPTION--
+
+<p>
+    Defines through what scheme the output will be served, in order to 
+    select the proper object validator when no scheme information is present.
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt (revision 21)
@@ -0,0 +1,6 @@
+Test.ForceNoIconv
+TYPE: bool
+DEFAULT: false
+--DESCRIPTION--
+When set to true, HTMLPurifier_Encoder will act as if iconv does not exist
+and use only pure PHP implementations.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt (revision 21)
@@ -0,0 +1,11 @@
+Core.CollectErrors
+TYPE: bool
+VERSION: 2.0.0
+DEFAULT: false
+--DESCRIPTION--
+
+Whether or not to collect errors found while filtering the document. This
+is a useful way to give feedback to your users. <strong>Warning:</strong>
+Currently this feature is very patchy and experimental, with lots of
+possible error messages not yet implemented. It will not cause any
+problems, but it may not help your users either. 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt (revision 21)
@@ -0,0 +1,10 @@
+HTML.Doctype
+TYPE: string/null
+DEFAULT: NULL
+--DESCRIPTION--
+Doctype to use during filtering. Technically speaking this is not actually
+a doctype (as it does not identify a corresponding DTD), but we are using
+this name for sake of simplicity. When non-blank, this will override any
+older directives like %HTML.XHTML or %HTML.Strict.
+--ALLOWED--
+'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1'
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt (revision 21)
@@ -0,0 +1,12 @@
+Core.EscapeNonASCIICharacters
+TYPE: bool
+VERSION: 1.4.0
+DEFAULT: false
+--DESCRIPTION--
+This directive overcomes a deficiency in %Core.Encoding by blindly
+converting all non-ASCII characters into decimal numeric entities before
+converting it to its native encoding. This means that even characters that
+can be expressed in the non-UTF-8 encoding will be entity-ized, which can
+be a real downer for encodings like Big5. It also assumes that the ASCII
+repetoire is available, although this is the case for almost all encodings.
+Anyway, use UTF-8! 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt (revision 21)
@@ -0,0 +1,13 @@
+Cache.DefinitionImpl
+TYPE: string/null
+VERSION: 2.0.0
+DEFAULT: 'Serializer'
+--DESCRIPTION--
+
+This directive defines which method to use when caching definitions,
+the complex data-type that makes HTML Purifier tick. Set to null
+to disable caching (not recommended, as you will see a definite
+performance degradation). 
+
+--ALIASES--
+Core.DefinitionCache
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt (revision 21)
@@ -0,0 +1,15 @@
+Attr.EnableID
+TYPE: bool
+DEFAULT: false
+VERSION: 1.2.0
+--DESCRIPTION--
+Allows the ID attribute in HTML.  This is disabled by default due to the
+fact that without proper configuration user input can easily break the
+validation of a webpage by specifying an ID that is already on the
+surrounding HTML.  If you don't mind throwing caution to the wind, enable
+this directive, but I strongly recommend you also consider blacklisting IDs
+you use (%Attr.IDBlacklist) or prefixing all user supplied IDs
+(%Attr.IDPrefix).  When set to true HTML Purifier reverts to the behavior of
+pre-1.2.0 versions.
+--ALIASES--
+HTML.EnableAttrID
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt (revision 21)
@@ -0,0 +1,37 @@
+Filter.ExtractStyleBlocks
+TYPE: bool
+VERSION: 3.1.0
+DEFAULT: false
+EXTERNAL: CSSTidy
+--DESCRIPTION--
+<p>
+  This directive turns on the style block extraction filter, which removes
+  <code>style</code> blocks from input HTML, cleans them up with CSSTidy,
+  and places them in the <code>StyleBlocks</code> context variable, for further
+  use by you, usually to be placed in an external stylesheet, or a
+  <code>style</code> block in the <code>head</code> of your document.
+</p>
+<p>
+  Sample usage:
+</p>
+<pre><![CDATA[$config = HTMLPurifier_Config::createDefault();
+$config->set('Filter', 'ExtractStyleBlocks', true);
+$purifier = new HTMLPurifier($config);
+$styles = $purifier->context->get('StyleBlocks');
+foreach ($styles as $style) {
+    echo '<style type="text/css">' . $style . "</style>\n";
+}]]></pre>
+<p>
+  <strong>Warning:</strong> It is possible for a user to mount an
+  imagecrash attack using this CSS. Counter-measures are difficult;
+  it is not simply enough to limit the range of CSS lengths (using
+  relative lengths with many nesting levels allows for large values
+  to be attained without actually specifying them in the stylesheet),
+  and the flexible nature of selectors makes it difficult to selectively
+  disable lengths on image tags (HTML Purifier, however, does disable
+  CSS width and height in inline styling). There are probably two effective
+  counter measures: an explicit width and height set to auto in all
+  images in your document (unlikely) or the disabling of width and
+  height (somewhat reasonable). Whether or not these measures should be
+  used is left to the reader.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Test.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Test.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Test.txt (revision 21)
@@ -0,0 +1,2 @@
+Test
+DESCRIPTION: Developer testing configuration for our unit tests.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt (revision 21)
@@ -0,0 +1,19 @@
+Core.HiddenElements
+TYPE: lookup
+--DEFAULT--
+array (
+  'script' => true,
+  'style' => true,
+)
+--DESCRIPTION--
+
+<p>
+  This directive is a lookup array of elements which should have their
+  contents removed when they are not allowed by the HTML definition.
+  For example, the contents of a <code>script</code> tag are not 
+  normally shown in a document, so if script tags are to be removed,
+  their contents should be removed to. This is opposed to a <code>b</code>
+  tag, which defines some presentational changes but does not hide its
+  contents.
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt (revision 21)
@@ -0,0 +1,24 @@
+Output.TidyFormat
+TYPE: bool
+VERSION: 1.1.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Determines whether or not to run Tidy on the final output for pretty 
+    formatting reasons, such as indentation and wrap.
+</p>
+<p>
+    This can greatly improve readability for editors who are hand-editing
+    the HTML, but is by no means necessary as HTML Purifier has already
+    fixed all major errors the HTML may have had. Tidy is a non-default
+    extension, and this directive will silently fail if Tidy is not
+    available.
+</p>
+<p>
+    If you are looking to make the overall look of your page's source
+    better, I recommend running Tidy on the entire page rather than just
+    user-content (after all, the indentation relative to the containing
+    blocks will be incorrect).
+</p>
+--ALIASES--
+Core.TidyFormat
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.txt (revision 21)
@@ -0,0 +1,2 @@
+AutoFormat
+DESCRIPTION: Configuration for activating auto-formatting functionality (also known as <code>Injector</code>s)
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt (revision 21)
@@ -0,0 +1,11 @@
+HTML.Proprietary
+TYPE: bool
+VERSION: 3.1.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Whether or not to allow proprietary elements and attributes in your
+    documents, as per <code>HTMLPurifier_HTMLModule_Proprietary</code>.
+    <strong>Warning:</strong> This can cause your documents to stop
+    validating!
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt (revision 21)
@@ -0,0 +1,23 @@
+HTML.CoreModules
+TYPE: lookup
+VERSION: 2.0.0
+--DEFAULT--
+array (
+  'Structure' => true,
+  'Text' => true,
+  'Hypertext' => true,
+  'List' => true,
+  'NonXMLCommonAttributes' => true,
+  'XMLCommonAttributes' => true,
+  'CommonAttributes' => true,
+)
+--DESCRIPTION--
+
+<p>
+    Certain modularized doctypes (XHTML, namely), have certain modules
+    that must be included for the doctype to be an conforming document
+    type: put those modules here. By default, XHTML's core modules
+    are used. You can set this to a blank array to disable core module
+    protection, but this is not recommended. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.txt (revision 21)
@@ -0,0 +1,2 @@
+Filter
+DESCRIPTION: Directives for turning filters on and off, or specifying custom filters.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt (revision 21)
@@ -0,0 +1,12 @@
+AutoFormat.Linkify
+TYPE: bool
+VERSION: 2.0.1
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+  This directive turns on linkification, auto-linking http, ftp and
+  https URLs. <code>a</code> tags with the <code>href</code> attribute
+  must be allowed. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt (revision 21)
@@ -0,0 +1,22 @@
+HTML.Allowed
+TYPE: itext/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    This is a convenience directive that rolls the functionality of
+    %HTML.AllowedElements and %HTML.AllowedAttributes into one directive.
+    Specify elements and attributes that are allowed using:
+    <code>element1[attr1|attr2],element2...</code>. You can also use
+    newlines instead of commas to separate elements.
+</p>
+<p>
+    <strong>Warning</strong>:
+    All of the constraints on the component directives are still enforced.
+    The syntax is a <em>subset</em> of TinyMCE's <code>valid_elements</code>
+    whitelist: directly copy-pasting it here will probably result in
+    broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes
+    are set, this directive has no effect.
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt (revision 21)
@@ -0,0 +1,8 @@
+HTML.TidyRemove
+TYPE: lookup
+VERSION: 2.0.0
+DEFAULT: array()
+--DESCRIPTION--
+
+Fixes to remove from the default set of Tidy fixes as per your level. 
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt (revision 21)
@@ -0,0 +1,11 @@
+CSS.DefinitionRev
+TYPE: int
+VERSION: 2.0.0
+DEFAULT: 1
+--DESCRIPTION--
+
+<p>
+    Revision identifier for your custom definition. See
+    %HTML.DefinitionRev for details. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt (revision 21)
@@ -0,0 +1,29 @@
+Core.ColorKeywords
+TYPE: hash
+VERSION: 2.0.0
+--DEFAULT--
+array (
+  'maroon' => '#800000',
+  'red' => '#FF0000',
+  'orange' => '#FFA500',
+  'yellow' => '#FFFF00',
+  'olive' => '#808000',
+  'purple' => '#800080',
+  'fuchsia' => '#FF00FF',
+  'white' => '#FFFFFF',
+  'lime' => '#00FF00',
+  'green' => '#008000',
+  'navy' => '#000080',
+  'blue' => '#0000FF',
+  'aqua' => '#00FFFF',
+  'teal' => '#008080',
+  'black' => '#000000',
+  'silver' => '#C0C0C0',
+  'gray' => '#808080',
+)
+--DESCRIPTION--
+
+Lookup array of color names to six digit hexadecimal number corresponding
+to color, with preceding hash mark. Used when parsing colors.
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt (revision 21)
@@ -0,0 +1,14 @@
+URI.AllowedSchemes
+TYPE: lookup
+--DEFAULT--
+array (
+  'http' => true,
+  'https' => true,
+  'mailto' => true,
+  'ftp' => true,
+  'nntp' => true,
+  'news' => true,
+)
+--DESCRIPTION--
+Whitelist that defines the schemes that a URI is allowed to have.  This
+prevents XSS attacks from using pseudo-schemes like javascript or mocha.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt (revision 21)
@@ -0,0 +1,8 @@
+URI.OverrideAllowedSchemes
+TYPE: bool
+DEFAULT: true
+--DESCRIPTION--
+If this is set to true (which it is by default), you can override
+%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the
+registry.  If false, you will also have to update that directive in order
+to add more schemes.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt (revision 21)
@@ -0,0 +1,18 @@
+HTML.BlockWrapper
+TYPE: string
+VERSION: 1.3.0
+DEFAULT: 'p'
+--DESCRIPTION--
+
+<p>
+    String name of element to wrap inline elements that are inside a block
+    context.  This only occurs in the children of blockquote in strict mode.
+</p>
+<p>
+    Example: by default value,
+    <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> would become
+    <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>.
+    The <code>&lt;p&gt;</code> tags can be replaced with whatever you desire,
+    as long as it is a block level element. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt (revision 21)
@@ -0,0 +1,13 @@
+Attr.IDPrefixLocal
+TYPE: string
+VERSION: 1.2.0
+DEFAULT: ''
+--DESCRIPTION--
+Temporary prefix for IDs used in conjunction with %Attr.IDPrefix.  If you
+need to allow multiple sets of user content on web page, you may need to
+have a seperate prefix that changes with each iteration.  This way,
+seperately submitted user content displayed on the same page doesn't
+clobber each other. Ideal values are unique identifiers for the content it
+represents (i.e. the id of the row in the database). Be sure to add a
+seperator (like an underscore) at the end.  Warning: this directive will
+not work unless %Attr.IDPrefix is set to a non-empty value! 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt (revision 21)
@@ -0,0 +1,7 @@
+CSS.AllowImportant
+TYPE: bool
+DEFAULT: false
+VERSION: 3.1.0
+--DESCRIPTION--
+This parameter determines whether or not !important cascade modifiers should
+be allowed in user CSS. If false, !important will stripped.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.txt (revision 21)
@@ -0,0 +1,2 @@
+HTML
+DESCRIPTION: Configuration regarding allowed HTML.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt (revision 21)
@@ -0,0 +1,9 @@
+Core.EscapeInvalidChildren
+TYPE: bool
+DEFAULT: false
+--DESCRIPTION--
+When true, a child is found that is not allowed in the context of the
+parent element will be transformed into text as if it were ASCII. When
+false, that element and all internal tags will be dropped, though text will
+be preserved.  There is no option for dropping the element but preserving
+child nodes.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt (revision 21)
@@ -0,0 +1,7 @@
+Attr.DefaultInvalidImageAlt
+TYPE: string
+DEFAULT: 'Invalid image'
+--DESCRIPTION--
+This is the content of the alt tag of an invalid image if the user had not
+previously specified an alt attribute.  It has no effect when the image is
+valid but there was no alt attribute present.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt (revision 21)
@@ -0,0 +1,8 @@
+Attr.IDBlacklistRegexp
+TYPE: string/null
+VERSION: 1.6.0
+DEFAULT: NULL
+--DESCRIPTION--
+PCRE regular expression to be matched against all IDs. If the expression is
+matches, the ID is rejected. Use this with care: may cause significant
+degradation. ID matching is done after all other validation. 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt (revision 21)
@@ -0,0 +1,12 @@
+URI.MakeAbsolute
+TYPE: bool
+VERSION: 2.1.0
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+    Converts all URIs into absolute forms. This is useful when the HTML
+    being filtered assumes a specific base path, but will actually be
+    viewed in a different context (and setting an alternate base URI is
+    not possible). %URI.Base must be set for this directive to work.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksTidyImpl.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksTidyImpl.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksTidyImpl.txt (revision 21)
@@ -0,0 +1,14 @@
+FilterParam.ExtractStyleBlocksTidyImpl
+TYPE: mixed/null
+VERSION: 3.1.0
+DEFAULT: NULL
+--DESCRIPTION--
+<p>
+  If left NULL, HTML Purifier will attempt to instantiate a <code>csstidy</code>
+  class to use for internal cleaning. This will usually be good enough.
+</p>
+<p>
+  However, for trusted user input, you can set this to <code>false</code> to
+  disable cleaning. In addition, you can supply your own concrete implementation
+  of Tidy's interface to use, although I don't know why you'd want to do that.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Host.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Host.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Host.txt (revision 21)
@@ -0,0 +1,19 @@
+URI.Host
+TYPE: string/null
+VERSION: 1.2.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Defines the domain name of the server, so we can determine whether or 
+    an absolute URI is from your website or not.  Not strictly necessary, 
+    as users should be using relative URIs to reference resources on your 
+    website.  It will, however, let you use absolute URIs to link to 
+    subdomains of the domain you post here: i.e. example.com will allow 
+    sub.example.com.  However, higher up domains will still be excluded: 
+    if you set %URI.Host to sub.example.com, example.com will be blocked. 
+    <strong>Note:</strong> This directive overrides %URI.Base because
+    a given page may be on a sub-domain, but you wish HTML Purifier to be
+    more relaxed and allow some of the parent domains too.
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt (revision 21)
@@ -0,0 +1,14 @@
+Core.Encoding
+TYPE: istring
+DEFAULT: 'utf-8'
+--DESCRIPTION--
+If for some reason you are unable to convert all webpages to UTF-8, you can
+use this directive as a stop-gap compatibility change to let HTML Purifier
+deal with non UTF-8 input.  This technique has notable deficiencies:
+absolutely no characters outside of the selected character encoding will be
+preserved, not even the ones that have been ampersand escaped (this is due
+to a UTF-8 specific <em>feature</em> that automatically resolves all
+entities), making it pretty useless for anything except the most I18N-blind
+applications, although %Core.EscapeNonASCIICharacters offers fixes this
+trouble with another tradeoff. This directive only accepts ISO-8859-1 if
+iconv is not enabled.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt (revision 21)
@@ -0,0 +1,11 @@
+Attr.IDPrefix
+TYPE: string
+VERSION: 1.2.0
+DEFAULT: ''
+--DESCRIPTION--
+String to prefix to IDs.  If you have no idea what IDs your pages may use,
+you may opt to simply add a prefix to all user-submitted ID attributes so
+that they are still usable, but will not conflict with core page IDs.
+Example: setting the directive to 'user_' will result in a user submitted
+'foo' to become 'user_foo'  Be sure to set %HTML.EnableAttrID to true
+before using this.  
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt (revision 21)
@@ -0,0 +1,20 @@
+HTML.AllowedModules
+TYPE: lookup/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    A doctype comes with a set of usual modules to use. Without having
+    to mucking about with the doctypes, you can quickly activate or
+    disable these modules by specifying which modules you wish to allow
+    with this directive. This is most useful for unit testing specific
+    modules, although end users may find it useful for their own ends.
+</p>
+<p>
+    If you specify a module that does not exist, the manager will silently
+    fail to use it, so be careful! User-defined modules are not affected
+    by this directive. Modules defined in %HTML.CoreModules are not
+    affected by this directive. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt (revision 21)
@@ -0,0 +1,18 @@
+HTML.AllowedElements
+TYPE: lookup/null
+VERSION: 1.3.0
+DEFAULT: NULL
+--DESCRIPTION--
+<p>
+    If HTML Purifier's tag set is unsatisfactory for your needs, you 
+    can overload it with your own list of tags to allow.  Note that this 
+    method is subtractive: it does its job by taking away from HTML Purifier 
+    usual feature set, so you cannot add a tag that HTML Purifier never 
+    supported in the first place (like embed, form or head).  If you 
+    change this, you probably also want to change %HTML.AllowedAttributes.
+</p>
+<p>
+    <strong>Warning:</strong> If another directive conflicts with the 
+    elements here, <em>that</em> directive will win and override. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt (revision 21)
@@ -0,0 +1,12 @@
+Core.RemoveInvalidImg
+TYPE: bool
+DEFAULT: true
+VERSION: 1.3.0
+--DESCRIPTION--
+
+<p>
+  This directive enables pre-emptive URI checking in <code>img</code> 
+  tags, as the attribute validation strategy is not authorized to 
+  remove elements from the document. Revert to pre-1.3.0 behavior by setting to false.
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt (revision 21)
@@ -0,0 +1,12 @@
+AutoFormat.PurifierLinkify
+TYPE: bool
+VERSION: 2.0.1
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+  Internal auto-formatter that converts configuration directives in
+  syntax <a>%Namespace.Directive</a> to links. <code>a</code> tags
+  with the <code>href</code> attribute must be allowed.
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.txt (revision 21)
@@ -0,0 +1,2 @@
+URI
+DESCRIPTION: Features regarding Uniform Resource Identifiers.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt (revision 21)
@@ -0,0 +1,13 @@
+Output.Newline
+TYPE: string/null
+VERSION: 2.0.1
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Newline string to format final output with. If left null, HTML Purifier
+    will auto-detect the default newline type of the system and use that;
+    you can manually override it here. Remember, \r\n is Windows, \r
+    is Mac, and \n is Unix. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt (revision 21)
@@ -0,0 +1,19 @@
+HTML.AllowedAttributes
+TYPE: lookup/null
+VERSION: 1.3.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    If HTML Purifier's attribute set is unsatisfactory, overload it! 
+    The syntax is "tag.attr" or "*.attr" for the global attributes 
+    (style, id, class, dir, lang, xml:lang).
+</p>
+<p>
+    <strong>Warning:</strong> If another directive conflicts with the 
+    elements here, <em>that</em> directive will win and override. For 
+    example, %HTML.EnableAttrID will take precedence over *.id in this 
+    directive.  You must set that directive to true before you can use 
+    IDs at all. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormatParam.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormatParam.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormatParam.txt (revision 21)
@@ -0,0 +1,2 @@
+AutoFormatParam
+DESCRIPTION: Configuration for customizing auto-formatting functionality
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt (revision 21)
@@ -0,0 +1,11 @@
+URI.DefinitionRev
+TYPE: int
+VERSION: 2.1.0
+DEFAULT: 1
+--DESCRIPTION--
+
+<p>
+    Revision identifier for your custom definition. See
+    %HTML.DefinitionRev for details. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt (revision 21)
@@ -0,0 +1,11 @@
+URI.DefinitionID
+TYPE: string/null
+VERSION: 2.1.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Unique identifier for a custom-built URI definition. If you  want
+    to add custom URIFilters, you must specify this value.
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.txt (revision 21)
@@ -0,0 +1,2 @@
+Cache
+DESCRIPTION: Configuration for DefinitionCache and related subclasses.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt (revision 21)
@@ -0,0 +1,9 @@
+Output.CommentScriptContents
+TYPE: bool
+VERSION: 2.0.0
+DEFAULT: true
+--DESCRIPTION--
+Determines whether or not HTML Purifier should attempt to fix up the
+contents of script tags for legacy browsers with comments. 
+--ALIASES--
+Core.CommentScriptContents
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt (revision 21)
@@ -0,0 +1,11 @@
+Attr.AllowedFrameTargets
+TYPE: lookup
+DEFAULT: array()
+--DESCRIPTION--
+Lookup table of all allowed link frame targets.  Some commonly used link
+targets include _blank, _self, _parent and _top. Values should be
+lowercase, as validation will be done in a case-sensitive manner despite
+W3C's recommendation. XHTML 1.0 Strict does not permit the target attribute
+so this directive will have no effect in that doctype. XHTML 1.1 does not
+enable the Target module by default, you will have to manually enable it
+(see the module documentation for more details.)
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt (revision 21)
@@ -0,0 +1,10 @@
+HTML.CustomDoctype
+TYPE: string/null
+VERSION: 2.0.1
+DEFAULT: NULL
+--DESCRIPTION--
+
+A custom doctype for power-users who defined there own document
+type. This directive only applies when %HTML.Doctype is blank.
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.txt (revision 21)
@@ -0,0 +1,2 @@
+FilterParam
+DESCRIPTION: Configuration for filters.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt (revision 21)
@@ -0,0 +1,20 @@
+HTML.ForbiddenAttributes
+TYPE: lookup
+VERSION: 3.1.0
+DEFAULT: array()
+--DESCRIPTION--
+<p>
+    While this directive is similar to %HTML.AllowedAttributes, for
+    forwards-compatibility with XML, this attribute has a different syntax. Instead of
+    <code>tag.attr</code>, use <code>tag@attr</code>. To disallow <code>href</code>
+    attributes in <code>a</code> tags, set this directive to
+    <code>a@href</code>. You can also disallow an attribute globally with
+    <code>attr</code> or <code>*@attr</code> (either syntax is fine; the latter
+    is provided for consistency with %HTML.AllowedAttributes).
+</p>
+<p>
+    <strong>Warning:</strong> This directive complements %HTML.ForbiddenElements,
+    accordingly, check
+    out that directive for a discussion of why you
+    should think twice before using this directive.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt (revision 21)
@@ -0,0 +1,10 @@
+CSS.Proprietary
+TYPE: bool
+VERSION: 3.0.0
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+    Whether or not to allow safe, proprietary CSS values. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt (revision 21)
@@ -0,0 +1,8 @@
+Attr.DefaultInvalidImage
+TYPE: string
+DEFAULT: ''
+--DESCRIPTION--
+This is the default image an img tag will be pointed to if it does not have
+a valid src attribute.  In future versions, we may allow the image tag to
+be removed completely, but due to design issues, this is not possible right
+now.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Base.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Base.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Base.txt (revision 21)
@@ -0,0 +1,17 @@
+URI.Base
+TYPE: string/null
+VERSION: 2.1.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    The base URI is the URI of the document this purified HTML will be
+    inserted into.  This information is important if HTML Purifier needs
+    to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute
+    is on.  You may use a non-absolute URI for this value, but behavior
+    may vary (%URI.MakeAbsolute deals nicely with both absolute and 
+    relative paths, but forwards-compatibility is not guaranteed).
+    <strong>Warning:</strong> If set, the scheme on this URI
+    overrides the one specified by %URI.DefaultScheme. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksEscaping.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksEscaping.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksEscaping.txt (revision 21)
@@ -0,0 +1,14 @@
+FilterParam.ExtractStyleBlocksEscaping
+TYPE: bool
+VERSION: 3.0.0
+DEFAULT: true
+ALIASES: Filter.ExtractStyleBlocksEscaping
+--DESCRIPTION--
+
+<p>
+  Whether or not to escape the dangerous characters &lt;, &gt; and &amp;
+  as \3C, \3E and \26, respectively. This is can be safely set to false
+  if the contents of StyleBlocks will be placed in an external stylesheet,
+  where there is no risk of it being interpreted as HTML. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt (revision 21)
@@ -0,0 +1,33 @@
+HTML.DefinitionID
+TYPE: string/null
+DEFAULT: NULL
+VERSION: 2.0.0
+--DESCRIPTION--
+
+<p>
+    Unique identifier for a custom-built HTML definition. If you edit
+    the raw version of the HTMLDefinition, introducing changes that the
+    configuration object does not reflect, you must specify this variable.
+    If you change your custom edits, you should change this directive, or
+    clear your cache. Example:
+</p>
+<pre>
+$config = HTMLPurifier_Config::createDefault();
+$config->set('HTML', 'DefinitionID', '1');
+$def = $config->getHTMLDefinition();
+$def->addAttribute('a', 'tabindex', 'Number');
+</pre>
+<p>
+    In the above example, the configuration is still at the defaults, but
+    using the advanced API, an extra attribute has been added. The
+    configuration object normally has no way of knowing that this change
+    has taken place, so it needs an extra directive: %HTML.DefinitionID.
+    If someone else attempts to use the default configuration, these two
+    pieces of code will not clobber each other in the cache, since one has
+    an extra directive attached to it.
+</p>
+<p>
+    You <em>must</em> specify a value to this directive to use the
+    advanced API features.
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.txt (revision 21)
@@ -0,0 +1,2 @@
+Attr
+DESCRIPTION: Features regarding attribute validation.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt (revision 21)
@@ -0,0 +1,6 @@
+Core.EscapeInvalidTags
+TYPE: bool
+DEFAULT: false
+--DESCRIPTION--
+When true, invalid tags will be written back to the document as plain text.
+Otherwise, they are silently dropped.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormatParam.PurifierLinkifyDocURL.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormatParam.PurifierLinkifyDocURL.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormatParam.PurifierLinkifyDocURL.txt (revision 21)
@@ -0,0 +1,12 @@
+AutoFormatParam.PurifierLinkifyDocURL
+TYPE: string
+VERSION: 2.0.1
+DEFAULT: '#%s'
+--DESCRIPTION--
+
+<p>
+  Location of configuration documentation to link to, let %s substitute
+  into the configuration's namespace and directive names sans the percent
+  sign. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.txt (revision 21)
@@ -0,0 +1,2 @@
+CSS
+DESCRIPTION: Configuration regarding allowed CSS.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt (revision 21)
@@ -0,0 +1,33 @@
+Core.LexerImpl
+TYPE: mixed/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+  This parameter determines what lexer implementation can be used. The
+  valid values are:
+</p>
+<dl>
+  <dt><em>null</em></dt>
+  <dd>
+    Recommended, the lexer implementation will be auto-detected based on
+    your PHP-version and configuration.
+  </dd>
+  <dt><em>string</em> lexer identifier</dt>
+  <dd>
+    This is a slim way of manually overridding the implementation.
+    Currently recognized values are: DOMLex (the default PHP5
+implementation)
+    and DirectLex (the default PHP4 implementation). Only use this if
+    you know what you are doing: usually, the auto-detection will
+    manage things for cases you aren't even aware of.
+  </dd>
+  <dt><em>object</em> lexer instance</dt>
+  <dd>
+    Super-advanced: you can specify your own, custom, implementation that
+    implements the interface defined by <code>HTMLPurifier_Lexer</code>.
+    I may remove this option simply because I don't expect anyone
+    to use it.
+  </dd>
+</dl>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksScope.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksScope.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksScope.txt (revision 21)
@@ -0,0 +1,28 @@
+FilterParam.ExtractStyleBlocksScope
+TYPE: string/null
+VERSION: 3.0.0
+DEFAULT: NULL
+ALIASES: Filter.ExtractStyleBlocksScope
+--DESCRIPTION--
+
+<p>
+  If you would like users to be able to define external stylesheets, but
+  only allow them to specify CSS declarations for a specific node and
+  prevent them from fiddling with other elements, use this directive.
+  It accepts any valid CSS selector, and will prepend this to any
+  CSS declaration extracted from the document. For example, if this
+  directive is set to <code>#user-content</code> and a user uses the
+  selector <code>a:hover</code>, the final selector will be
+  <code>#user-content a:hover</code>.
+</p>
+<p>
+  The comma shorthand may be used; consider the above example, with
+  <code>#user-content, #user-content2</code>, the final selector will
+  be <code>#user-content a:hover, #user-content2 a:hover</code>.
+</p>
+<p>
+  <strong>Warning:</strong> It is possible for users to bypass this measure
+  using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML
+  Purifier, and I am working to get it fixed. Until then, HTML Purifier
+  performs a basic check to prevent this.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt (revision 21)
@@ -0,0 +1,13 @@
+Cache.SerializerPath
+TYPE: string/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Absolute path with no trailing slash to store serialized definitions in.
+    Default is within the
+    HTML Purifier library inside DefinitionCache/Serializer. This
+    path must be writable by the webserver. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt (revision 21)
@@ -0,0 +1,12 @@
+AutoFormat.Custom
+TYPE: list
+VERSION: 2.0.1
+DEFAULT: array()
+--DESCRIPTION--
+
+<p>
+  This directive can be used to add custom auto-format injectors.
+  Specify an array of injector names (class name minus the prefix)
+  or concrete implementations. Injector class must exist. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt (revision 21)
@@ -0,0 +1,16 @@
+Core.MaintainLineNumbers
+TYPE: bool/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+  If true, HTML Purifier will add line number information to all tokens.
+  This is useful when error reporting is turned on, but can result in
+  significant performance degradation and should not be used when
+  unnecessary. This directive must be used with the DirectLex lexer,
+  as the DOMLex lexer does not (yet) support this functionality. 
+  If the value is null, an appropriate value will be selected based
+  on other configuration. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt (revision 21)
@@ -0,0 +1,10 @@
+CSS.AllowTricky
+TYPE: bool
+DEFAULT: false
+VERSION: 3.1.0
+--DESCRIPTION--
+This parameter determines whether or not to allow "tricky" CSS properties and
+values. Tricky CSS properties/values can drastically modify page layout or
+be used for deceptive practices but do not directly constitute a security risk.
+For example, <code>display:none;</code> is considered a tricky property that
+will only be allowed if this directive is set to true.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt (revision 21)
@@ -0,0 +1,17 @@
+CSS.AllowedProperties
+TYPE: lookup/null
+VERSION: 3.1.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    If HTML Purifier's style attributes set is unsatisfactory for your needs, 
+    you can overload it with your own list of tags to allow.  Note that this 
+    method is subtractive: it does its job by taking away from HTML Purifier 
+    usual feature set, so you cannot add an attribute that HTML Purifier never 
+    supported in the first place.
+</p>
+<p>
+    <strong>Warning:</strong> If another directive conflicts with the 
+    elements here, <em>that</em> directive will win and override. 
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt (revision 21)
@@ -0,0 +1,12 @@
+HTML.Parent
+TYPE: string
+VERSION: 1.3.0
+DEFAULT: 'div'
+--DESCRIPTION--
+
+<p>
+    String name of element that HTML fragment passed to library will be 
+    inserted in.  An interesting variation would be using span as the 
+    parent element, meaning that only inline tags would be allowed. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt (revision 21)
@@ -0,0 +1,13 @@
+Core.ConvertDocumentToFragment
+TYPE: bool
+DEFAULT: true
+--DESCRIPTION--
+
+This parameter determines whether or not the filter should convert
+input that is a full document with html and body tags to a fragment
+of just the contents of a body tag. This parameter is simply something
+HTML Purifier can do during an edge-case: for most inputs, this
+processing is not necessary.
+
+--ALIASES--
+Core.AcceptFullDocuments
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt (revision 21)
@@ -0,0 +1,10 @@
+Filter.Custom
+TYPE: list
+VERSION: 3.1.0
+DEFAULT: array()
+--DESCRIPTION--
+<p>
+  This directive can be used to add custom filters; it is nearly the
+  equivalent of the now deprecated <code>HTMLPurifier-&gt;addFilter()</code>
+  method. Specify an array of concrete implementations.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt (revision 21)
@@ -0,0 +1,4 @@
+Attr.IDBlacklist
+TYPE: list
+DEFAULT: array()
+DESCRIPTION: Array of IDs not allowed in the document.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt (revision 21)
@@ -0,0 +1,12 @@
+URI.DisableExternalResources
+TYPE: bool
+VERSION: 1.3.0
+DEFAULT: false
+--DESCRIPTION--
+Disables the embedding of external resources, preventing users from
+embedding things like images from other hosts. This prevents access
+tracking (good for email viewers), bandwidth leeching, cross-site request
+forging, goatse.cx posting, and other nasties, but also results in a loss
+of end-user functionality (they can't directly post a pic they posted from
+Flickr anymore). Use it if you don't have a robust user-content moderation
+team. 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Output.txt (revision 21)
@@ -0,0 +1,2 @@
+Output
+DESCRIPTION: Configuration relating to the generation of (X)HTML.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt (revision 21)
@@ -0,0 +1,13 @@
+URI.Disable
+TYPE: bool
+VERSION: 1.3.0
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+    Disables all URIs in all forms. Not sure why you'd want to do that 
+    (after all, the Internet's founded on the notion of a hyperlink). 
+</p>
+
+--ALIASES--
+Attr.DisableURI
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt (revision 21)
@@ -0,0 +1,8 @@
+Attr.AllowedRel
+TYPE: lookup
+VERSION: 1.6.0
+DEFAULT: array()
+--DESCRIPTION--
+List of allowed forward document relationships in the rel attribute. Common
+values may be nofollow or print. By default, this is empty, meaning that no
+document relationships are allowed. 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt (revision 21)
@@ -0,0 +1,23 @@
+HTML.TidyLevel
+TYPE: string
+VERSION: 2.0.0
+DEFAULT: 'medium'
+--DESCRIPTION--
+
+<p>General level of cleanliness the Tidy module should enforce.
+There are four allowed values:</p>
+<dl>
+    <dt>none</dt>
+    <dd>No extra tidying should be done</dd>
+    <dt>light</dt>
+    <dd>Only fix elements that would be discarded otherwise due to
+    lack of support in doctype</dd>
+    <dt>medium</dt>
+    <dd>Enforce best practices</dd>
+    <dt>heavy</dt>
+    <dd>Transform all deprecated elements and attributes to standards
+    compliant equivalents</dd>
+</dl>
+
+--ALLOWED--
+'none', 'light', 'medium', 'heavy'
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt (revision 21)
@@ -0,0 +1,10 @@
+URI.DisableExternal
+TYPE: bool
+VERSION: 1.2.0
+DEFAULT: false
+--DESCRIPTION--
+Disables links to external websites.  This is a highly effective anti-spam
+and anti-pagerank-leech measure, but comes at a hefty price: nolinks or
+images outside of your domain will be allowed.  Non-linkified URIs will
+still be preserved.  If you want to be able to link to subdomains or use
+absolute URIs, specify %URI.Host for your website. 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt (revision 21)
@@ -0,0 +1,16 @@
+HTML.DefinitionRev
+TYPE: int
+VERSION: 2.0.0
+DEFAULT: 1
+--DESCRIPTION--
+
+<p>
+    Revision identifier for your custom definition specified in
+    %HTML.DefinitionID.  This serves the same purpose: uniquely identifying
+    your custom definition, but this one does so in a chronological
+    context: revision 3 is more up-to-date then revision 2.  Thus, when
+    this gets incremented, the cache handling is smart enough to clean
+    up any older revisions of your definition as well as flush the
+    cache.  
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt (revision 21)
@@ -0,0 +1,31 @@
+URI.Munge
+TYPE: string/null
+VERSION: 1.3.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Munges all browsable (usually http, https and ftp)
+    absolute URI's into another URI, usually a URI redirection service.
+    This directive accepts a URI, formatted with a <code>%s</code> where 
+    the url-encoded original URI should be inserted (sample: 
+    <code>http://www.google.com/url?q=%s</code>).
+</p>
+<p>
+    Uses for this directive:
+</p>
+<ul>
+    <li>
+        Prevent PageRank leaks, while being fairly transparent 
+        to users (you may also want to add some client side JavaScript to 
+        override the text in the statusbar). <strong>Notice</strong>:
+        Many security experts believe that this form of protection does
+not deter spam-bots. 
+    </li>
+    <li>
+        Redirect users to a splash page telling them they are leaving your
+        website. While this is poor usability practice, it is often
+mandated
+        in corporate environments.
+    </li>
+</ul>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.Language.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.Language.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.Language.txt (revision 21)
@@ -0,0 +1,11 @@
+Core.Language
+TYPE: string
+VERSION: 2.0.0
+DEFAULT: 'en'
+--DESCRIPTION--
+
+ISO 639 language code for localizable things in HTML Purifier to use,
+which is mainly error reporting. There is currently only an English (en)
+translation, so this directive is currently useless.
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt (revision 21)
@@ -0,0 +1,8 @@
+HTML.Strict
+TYPE: bool
+VERSION: 1.3.0
+DEFAULT: false
+DEPRECATED-VERSION: 1.7.0
+DEPRECATED-USE: HTML.Doctype
+--DESCRIPTION--
+Determines whether or not to use Transitional (loose) or Strict rulesets.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt (revision 21)
@@ -0,0 +1,8 @@
+Attr.AllowedRev
+TYPE: lookup
+VERSION: 1.6.0
+DEFAULT: array()
+--DESCRIPTION--
+List of allowed reverse document relationships in the rev attribute. This
+attribute is a bit of an edge-case; if you don't know what it is for, stay
+away. 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt (revision 21)
@@ -0,0 +1,30 @@
+AutoFormat.AutoParagraph
+TYPE: bool
+VERSION: 2.0.1
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+  This directive turns on auto-paragraphing, where double newlines are
+  converted in to paragraphs whenever possible. Auto-paragraphing:
+</p>
+<ul>
+  <li>Always applies to inline elements or text in the root node,</li>
+  <li>Applies to inline elements or text with double newlines in nodes
+      that allow paragraph tags,</li>
+  <li>Applies to double newlines in paragraph tags</li>
+</ul>
+<p>
+  <code>p</code> tags must be allowed for this directive to take effect.
+  We do not use <code>br</code> tags for paragraphing, as that is
+  semantically incorrect.
+</p>
+<p>
+  To prevent auto-paragraphing as a content-producer, refrain from using
+  double-newlines except to specify a new paragraph or in contexts where
+  it has special meaning (whitespace usually has no meaning except in
+  tags like <code>pre</code>, so this should not be difficult.) To prevent
+  the paragraphing of inline text adjacent to block elements, wrap them
+  in <code>div</code> tags (the behavior is slightly different outside of
+  the root node.)
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt (revision 21)
@@ -0,0 +1,10 @@
+Filter.YouTube
+TYPE: bool
+VERSION: 3.1.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+  This directive enables YouTube video embedding in HTML Purifier. Check
+  <a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document
+  on embedding videos</a> for more information on what this filter does.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt (revision 21)
@@ -0,0 +1,8 @@
+HTML.TidyAdd
+TYPE: lookup
+VERSION: 2.0.0
+DEFAULT: array()
+--DESCRIPTION--
+
+Fixes to add to the default set of Tidy fixes as per your level. 
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt (revision 21)
@@ -0,0 +1,11 @@
+Core.RemoveScriptContents
+TYPE: bool/null
+DEFAULT: NULL
+VERSION: 2.0.0
+DEPRECATED-VERSION: 2.1.0
+DEPRECATED-USE: Core.HiddenElements
+--DESCRIPTION--
+<p>
+  This directive enables HTML Purifier to remove not only script tags
+  but all of their contents.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.txt (revision 21)
@@ -0,0 +1,2 @@
+Core
+DESCRIPTION: Core features that are always available.
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/info.ini
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/info.ini (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/info.ini (revision 21)
@@ -0,0 +1,1 @@
+name = "HTML Purifier"
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt (revision 21)
@@ -0,0 +1,19 @@
+HTML.ForbiddenElements
+TYPE: lookup
+VERSION: 3.1.0
+DEFAULT: array()
+--DESCRIPTION--
+<p>
+    This was, perhaps, the most requested feature ever in HTML
+    Purifier. Please don't abuse it! This is the logical inverse of
+    %HTML.AllowedElements, and it will override that directive, or any
+    other directive.
+</p>
+<p>
+    If possible, %HTML.Allowed is recommended over this directive, because it
+    can sometimes be difficult to tell whether or not you've forbidden all of
+    the behavior you would like to disallow. If you forbid <code>img</code>
+    with the expectation of preventing images on your site, you'll be in for
+    a nasty surprise when people start using the <code>background-image</code>
+    CSS property.
+</p>
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt (revision 21)
@@ -0,0 +1,12 @@
+URI.DisableResources
+TYPE: bool
+VERSION: 1.3.0
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+    Disables embedding resources, essentially meaning no pictures. You can
+    still link to them though. See %URI.DisableExternalResources for why 
+    this might be a good idea. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt (revision 21)
@@ -0,0 +1,17 @@
+Core.DirectLexLineNumberSyncInterval
+TYPE: int
+VERSION: 2.0.0
+DEFAULT: 0
+--DESCRIPTION--
+
+<p>
+  Specifies the number of tokens the DirectLex line number tracking
+  implementations should process before attempting to resyncronize the
+  current line count by manually counting all previous new-lines. When
+  at 0, this functionality is disabled. Lower values will decrease
+  performance, and this is only strictly necessary if the counting
+  algorithm is buggy (in which case you should report it as a bug).
+  This has no effect when %Core.MaintainLineNumbers is disabled or DirectLex is
+  not being used. 
+</p>
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt (revision 21)
@@ -0,0 +1,7 @@
+HTML.Trusted
+TYPE: bool
+VERSION: 2.0.0
+DEFAULT: false
+--DESCRIPTION--
+Indicates whether or not the user input is trusted or not. If the input is
+trusted, a more expansive set of allowed tags and attributes will be used. 
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt (revision 21)
@@ -0,0 +1,10 @@
+HTML.XHTML
+TYPE: bool
+DEFAULT: true
+VERSION: 1.1.0
+DEPRECATED-VERSION: 1.7.0
+DEPRECATED-USE: HTML.Doctype
+--DESCRIPTION--
+Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor.
+--ALIASES--
+Core.XHTML
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Exception.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Exception.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Exception.php (revision 21)
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * Exceptions related to configuration schema
+ */
+class HTMLPurifier_ConfigSchema_Exception extends HTMLPurifier_Exception
+{
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/InterchangeBuilder.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/InterchangeBuilder.php (revision 21)
@@ -0,0 +1,175 @@
+<?php
+
+class HTMLPurifier_ConfigSchema_InterchangeBuilder
+{
+    
+    /**
+     * Used for processing DEFAULT, nothing else.
+     */
+    protected $varParser;
+    
+    public function __construct($varParser = null) {
+        $this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
+    }
+    
+    public static function buildFromDirectory($dir = null) {
+        $parser      = new HTMLPurifier_StringHashParser();
+        $builder     = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
+        $interchange = new HTMLPurifier_ConfigSchema_Interchange();
+        
+        if (!$dir) $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema/';
+        $info = parse_ini_file($dir . 'info.ini');
+        $interchange->name = $info['name'];
+        
+        $files = array();
+        $dh = opendir($dir);
+        while (false !== ($file = readdir($dh))) {
+            if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
+                continue;
+            }
+            $files[] = $file;
+        }
+        closedir($dh);
+        
+        sort($files);
+        foreach ($files as $file) {
+            $builder->build(
+                $interchange,
+                new HTMLPurifier_StringHash( $parser->parseFile($dir . $file) )
+            );
+        }
+        
+        return $interchange;
+    }
+    
+    /**
+     * Builds an interchange object based on a hash.
+     * @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build
+     * @param $hash HTMLPurifier_ConfigSchema_StringHash source data
+     */
+    public function build($interchange, $hash) {
+        if (!$hash instanceof HTMLPurifier_StringHash) {
+            $hash = new HTMLPurifier_StringHash($hash);
+        }
+        if (!isset($hash['ID'])) {
+            throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
+        }
+        if (strpos($hash['ID'], '.') === false) {
+            $this->buildNamespace($interchange, $hash);
+        } else {
+            $this->buildDirective($interchange, $hash);
+        }
+        $this->_findUnused($hash);
+    }
+    
+    public function buildNamespace($interchange, $hash) {
+        $namespace = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
+        $namespace->namespace   = $hash->offsetGet('ID');
+        if (isset($hash['DESCRIPTION'])) {
+            $namespace->description = $hash->offsetGet('DESCRIPTION');
+        }
+        $interchange->addNamespace($namespace);
+    }
+    
+    public function buildDirective($interchange, $hash) {
+        $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
+        
+        // These are required elements:
+        $directive->id = $this->id($hash->offsetGet('ID'));
+        $id = $directive->id->toString(); // convenience
+        
+        if (isset($hash['TYPE'])) {
+            $type = explode('/', $hash->offsetGet('TYPE'));
+            if (isset($type[1])) $directive->typeAllowsNull = true;
+            $directive->type = $type[0];
+        } else {
+            throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined");
+        }
+        
+        if (isset($hash['DEFAULT'])) {
+            try {
+                $directive->default = $this->varParser->parse($hash->offsetGet('DEFAULT'), $directive->type, $directive->typeAllowsNull);
+            } catch (HTMLPurifier_VarParserException $e) {
+                throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'");
+            }
+        }
+        
+        if (isset($hash['DESCRIPTION'])) {
+            $directive->description = $hash->offsetGet('DESCRIPTION');
+        }
+        
+        if (isset($hash['ALLOWED'])) {
+            $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED')));
+        }
+        
+        if (isset($hash['VALUE-ALIASES'])) {
+            $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES'));
+        }
+        
+        if (isset($hash['ALIASES'])) {
+            $raw_aliases = trim($hash->offsetGet('ALIASES'));
+            $aliases = preg_split('/\s*,\s*/', $raw_aliases);
+            foreach ($aliases as $alias) {
+                $directive->aliases[] = $this->id($alias);
+            }
+        }
+        
+        if (isset($hash['VERSION'])) {
+            $directive->version = $hash->offsetGet('VERSION');
+        }
+        
+        if (isset($hash['DEPRECATED-USE'])) {
+            $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE'));
+        }
+        
+        if (isset($hash['DEPRECATED-VERSION'])) {
+            $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION');
+        }
+        
+        if (isset($hash['EXTERNAL'])) {
+            $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL')));
+        }
+        
+        $interchange->addDirective($directive);
+    }
+    
+    /**
+     * Evaluates an array PHP code string without array() wrapper
+     */
+    protected function evalArray($contents) {
+        return eval('return array('. $contents .');');
+    }
+    
+    /**
+     * Converts an array list into a lookup array.
+     */
+    protected function lookup($array) {
+        $ret = array();
+        foreach ($array as $val) $ret[$val] = true;
+        return $ret;
+    }
+    
+    /**
+     * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id
+     * object based on a string Id.
+     */
+    protected function id($id) {
+        return HTMLPurifier_ConfigSchema_Interchange_Id::make($id);
+    }
+    
+    /**
+     * Triggers errors for any unused keys passed in the hash; such keys
+     * may indicate typos, missing values, etc.
+     * @param $hash Instance of ConfigSchema_StringHash to check.
+     */
+    protected function _findUnused($hash) {
+        $accessed = $hash->getAccessed();
+        foreach ($hash as $k => $v) {
+            if (!isset($accessed[$k])) {
+                trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE);
+            }
+        }
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Builder/Xml.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Builder/Xml.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Builder/Xml.php (revision 21)
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * Converts HTMLPurifier_ConfigSchema_Interchange to an XML format,
+ * which can be further processed to generate documentation.
+ */
+class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
+{
+    
+    protected $interchange;
+    
+    protected function writeHTMLDiv($html) {
+        $this->startElement('div');
+        
+        $purifier = HTMLPurifier::getInstance();
+        $html = $purifier->purify($html);
+        $this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
+        $this->writeRaw($html);
+        
+        $this->endElement(); // div
+    }
+    
+    protected function export($var) {
+        if ($var === array()) return 'array()';
+        return var_export($var, true);
+    }
+    
+    public function build($interchange) {
+        // global access, only use as last resort
+        $this->interchange = $interchange;
+        
+        $this->setIndent(true);
+        $this->startDocument('1.0', 'UTF-8');
+        $this->startElement('configdoc');
+        $this->writeElement('title', $interchange->name);
+        
+        foreach ($interchange->namespaces as $namespace) {
+            $this->buildNamespace($namespace);
+        }
+        
+        $this->endElement(); // configdoc
+        $this->flush();
+    }
+    
+    public function buildNamespace($namespace) {
+        $this->startElement('namespace');
+        $this->writeAttribute('id', $namespace->namespace);
+        
+        $this->writeElement('name', $namespace->namespace);
+        $this->startElement('description');
+            $this->writeHTMLDiv($namespace->description);
+        $this->endElement(); // description
+        
+        foreach ($this->interchange->directives as $directive) {
+            if ($directive->id->namespace !== $namespace->namespace) continue;
+            $this->buildDirective($directive);
+        }
+        
+        $this->endElement(); // namespace
+    }
+    
+    public function buildDirective($directive) {
+        $this->startElement('directive');
+        $this->writeAttribute('id', $directive->id->toString());
+        
+        $this->writeElement('name', $directive->id->directive);
+        
+        $this->startElement('aliases');
+            foreach ($directive->aliases as $alias) $this->writeElement('alias', $alias->toString());
+        $this->endElement(); // aliases
+        
+        $this->startElement('constraints');
+            if ($directive->version) $this->writeElement('version', $directive->version);
+            $this->startElement('type');
+                if ($directive->typeAllowsNull) $this->writeAttribute('allow-null', 'yes');
+                $this->text($directive->type);
+            $this->endElement(); // type
+            if ($directive->allowed) {
+                $this->startElement('allowed');
+                    foreach ($directive->allowed as $value => $x) $this->writeElement('value', $value);
+                $this->endElement(); // allowed
+            }
+            $this->writeElement('default', $this->export($directive->default));
+            $this->writeAttribute('xml:space', 'preserve');
+            if ($directive->external) {
+                $this->startElement('external');
+                    foreach ($directive->external as $project) $this->writeElement('project', $project);
+                $this->endElement();
+            }
+        $this->endElement(); // constraints
+        
+        if ($directive->deprecatedVersion) {
+            $this->startElement('deprecated');
+                $this->writeElement('version', $directive->deprecatedVersion);
+                $this->writeElement('use', $directive->deprecatedUse->toString());
+            $this->endElement(); // deprecated
+        }
+        
+        $this->startElement('description');
+            $this->writeHTMLDiv($directive->description);
+        $this->endElement(); // description
+        
+        $this->endElement(); // directive
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php (revision 21)
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Converts HTMLPurifier_ConfigSchema_Interchange to our runtime
+ * representation used to perform checks on user configuration.
+ */
+class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
+{
+    
+    public function build($interchange) {
+        $schema = new HTMLPurifier_ConfigSchema();
+        foreach ($interchange->namespaces as $n) {
+            $schema->addNamespace($n->namespace);
+        }
+        foreach ($interchange->directives as $d) {
+            $schema->add(
+                $d->id->namespace,
+                $d->id->directive,
+                $d->default,
+                $d->type,
+                $d->typeAllowsNull
+            );
+            if ($d->allowed !== null) {
+                $schema->addAllowedValues(
+                    $d->id->namespace,
+                    $d->id->directive,
+                    $d->allowed
+                );
+            }
+            foreach ($d->aliases as $alias) {
+                $schema->addAlias(
+                    $alias->namespace,
+                    $alias->directive,
+                    $d->id->namespace,
+                    $d->id->directive
+                );
+            }
+            if ($d->valueAliases !== null) {
+                $schema->addValueAliases(
+                    $d->id->namespace,
+                    $d->id->directive,
+                    $d->valueAliases
+                );
+            }
+        }
+        return $schema;
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Id.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Id.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Id.php (revision 21)
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * Represents a directive ID in the interchange format.
+ */
+class HTMLPurifier_ConfigSchema_Interchange_Id
+{
+    
+    public $namespace, $directive;
+    
+    public function __construct($namespace, $directive) {
+        $this->namespace = $namespace;
+        $this->directive = $directive;
+    }
+    
+    /**
+     * @warning This is NOT magic, to ensure that people don't abuse SPL and
+     *          cause problems for PHP 5.0 support.
+     */
+    public function toString() {
+        return $this->namespace . '.' . $this->directive;
+    }
+    
+    public static function make($id) {
+        list($namespace, $directive) = explode('.', $id);
+        return new HTMLPurifier_ConfigSchema_Interchange_Id($namespace, $directive);
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Namespace.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Namespace.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Namespace.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Interchange component class describing namespaces.
+ */
+class HTMLPurifier_ConfigSchema_Interchange_Namespace
+{
+    
+    /**
+     * Name of namespace defined.
+     */
+    public $namespace;
+    
+    /**
+     * HTML description.
+     */
+    public $description;
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Directive.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Directive.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange/Directive.php (revision 21)
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Interchange component class describing configuration directives.
+ */
+class HTMLPurifier_ConfigSchema_Interchange_Directive
+{
+    
+    /**
+     * ID of directive, instance of HTMLPurifier_ConfigSchema_Interchange_Id.
+     */
+    public $id;
+    
+    /**
+     * String type, e.g. 'integer' or 'istring'.
+     */
+    public $type;
+    
+    /**
+     * Default value, e.g. 3 or 'DefaultVal'.
+     */
+    public $default;
+    
+    /**
+     * HTML description.
+     */
+    public $description;
+    
+    /**
+     * Boolean whether or not null is allowed as a value.
+     */
+    public $typeAllowsNull = false;
+    
+    /**
+     * Lookup table of allowed scalar values, e.g. array('allowed' => true).
+     * Null if all values are allowed.
+     */
+    public $allowed;
+    
+    /**
+     * List of aliases for the directive,
+     * e.g. array(new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir'))).
+     */
+    public $aliases = array();
+    
+    /**
+     * Hash of value aliases, e.g. array('alt' => 'real'). Null if value
+     * aliasing is disabled (necessary for non-scalar types).
+     */
+    public $valueAliases;
+    
+    /**
+     * Version of HTML Purifier the directive was introduced, e.g. '1.3.1'.
+     * Null if the directive has always existed.
+     */
+    public $version;
+    
+    /**
+     * ID of directive that supercedes this old directive, is an instance
+     * of HTMLPurifier_ConfigSchema_Interchange_Id. Null if not deprecated.
+     */
+    public $deprecatedUse;
+    
+    /**
+     * Version of HTML Purifier this directive was deprecated. Null if not
+     * deprecated.
+     */
+    public $deprecatedVersion;
+    
+    /**
+     * List of external projects this directive depends on, e.g. array('CSSTidy').
+     */
+    public $external = array();
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema.ser
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema.ser (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/schema.ser (revision 21)
@@ -0,0 +1,1 @@
+O:25:"HTMLPurifier_ConfigSchema":3:{s:8:"defaults";a:12:{s:4:"Attr";a:11:{s:19:"AllowedFrameTargets";a:0:{}s:10:"AllowedRel";a:0:{}s:10:"AllowedRev";a:0:{}s:19:"DefaultInvalidImage";s:0:"";s:22:"DefaultInvalidImageAlt";s:13:"Invalid image";s:14:"DefaultTextDir";s:3:"ltr";s:8:"EnableID";b:0;s:11:"IDBlacklist";a:0:{}s:17:"IDBlacklistRegexp";N;s:8:"IDPrefix";s:0:"";s:13:"IDPrefixLocal";s:0:"";}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";b:0;s:6:"Custom";a:0:{}s:7:"Linkify";b:0;s:15:"PurifierLinkify";b:0;}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";s:3:"#%s";}s:3:"CSS";a:5:{s:14:"AllowImportant";b:0;s:11:"AllowTricky";b:0;s:17:"AllowedProperties";N;s:13:"DefinitionRev";i:1;s:11:"Proprietary";b:0;}s:5:"Cache";a:2:{s:14:"DefinitionImpl";s:10:"Serializer";s:14:"SerializerPath";N;}s:4:"Core";a:15:{s:17:"AggressivelyFixLt";b:0;s:13:"CollectErrors";b:0;s:13:"ColorKeywords";a:17:{s:6:"maroon";s:7:"#800000";s:3:"red";s:7:"#FF0000";s:6:"orange";s:7:"#FFA500";s:6:"yellow";s:7:"#FFFF00";s:5:"olive";s:7:"#808000";s:6:"purple";s:7:"#800080";s:7:"fuchsia";s:7:"#FF00FF";s:5:"white";s:7:"#FFFFFF";s:4:"lime";s:7:"#00FF00";s:5:"green";s:7:"#008000";s:4:"navy";s:7:"#000080";s:4:"blue";s:7:"#0000FF";s:4:"aqua";s:7:"#00FFFF";s:4:"teal";s:7:"#008080";s:5:"black";s:7:"#000000";s:6:"silver";s:7:"#C0C0C0";s:4:"gray";s:7:"#808080";}s:25:"ConvertDocumentToFragment";b:1;s:31:"DirectLexLineNumberSyncInterval";i:0;s:8:"Encoding";s:5:"utf-8";s:21:"EscapeInvalidChildren";b:0;s:17:"EscapeInvalidTags";b:0;s:24:"EscapeNonASCIICharacters";b:0;s:14:"HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:8:"Language";s:2:"en";s:9:"LexerImpl";N;s:19:"MaintainLineNumbers";N;s:16:"RemoveInvalidImg";b:1;s:20:"RemoveScriptContents";N;}s:6:"Filter";a:3:{s:6:"Custom";a:0:{}s:18:"ExtractStyleBlocks";b:0;s:7:"YouTube";b:0;}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";b:1;s:23:"ExtractStyleBlocksScope";N;s:26:"ExtractStyleBlocksTidyImpl";N;}s:4:"HTML";a:20:{s:7:"Allowed";N;s:17:"AllowedAttributes";N;s:15:"AllowedElements";N;s:14:"AllowedModules";N;s:12:"BlockWrapper";s:1:"p";s:11:"CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:13:"CustomDoctype";N;s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Doctype";N;s:19:"ForbiddenAttributes";a:0:{}s:17:"ForbiddenElements";a:0:{}s:6:"Parent";s:3:"div";s:11:"Proprietary";b:0;s:6:"Strict";b:0;s:7:"TidyAdd";a:0:{}s:9:"TidyLevel";s:6:"medium";s:10:"TidyRemove";a:0:{}s:7:"Trusted";b:0;s:5:"XHTML";b:1;}s:6:"Output";a:3:{s:21:"CommentScriptContents";b:1;s:7:"Newline";N;s:10:"TidyFormat";b:0;}s:4:"Test";a:1:{s:12:"ForceNoIconv";b:0;}s:3:"URI";a:14:{s:14:"AllowedSchemes";a:6:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;}s:4:"Base";N;s:13:"DefaultScheme";s:4:"http";s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Disable";b:0;s:15:"DisableExternal";b:0;s:24:"DisableExternalResources";b:0;s:16:"DisableResources";b:0;s:4:"Host";N;s:13:"HostBlacklist";a:0:{}s:12:"MakeAbsolute";b:0;s:5:"Munge";N;s:22:"OverrideAllowedSchemes";b:1;}}s:4:"info";a:12:{s:4:"Attr";a:12:{s:19:"AllowedFrameTargets";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:10:"AllowedRel";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:10:"AllowedRev";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:19:"DefaultInvalidImage";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:22:"DefaultInvalidImageAlt";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:14:"DefaultTextDir";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";a:2:{s:3:"ltr";b:1;s:3:"rtl";b:1;}s:7:"aliases";a:0:{}}s:8:"EnableID";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"IDBlacklist";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"list";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"IDBlacklistRegexp";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:8:"IDPrefix";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"IDPrefixLocal";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:10:"DisableURI";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:3:"URI";s:4:"name";s:7:"Disable";}}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:6:"Custom";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"list";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Linkify";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:15:"PurifierLinkify";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:3:"CSS";a:5:{s:14:"AllowImportant";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"AllowTricky";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"AllowedProperties";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"DefinitionRev";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:3:"int";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"Proprietary";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:5:"Cache";a:2:{s:14:"DefinitionImpl";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:14:"SerializerPath";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:4:"Core";a:20:{s:15:"DefinitionCache";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:5:"Cache";s:4:"name";s:14:"DefinitionImpl";}s:17:"AggressivelyFixLt";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"CollectErrors";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"ColorKeywords";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"hash";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:25:"ConvertDocumentToFragment";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:19:"AcceptFullDocuments";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:4:"Core";s:4:"name";s:25:"ConvertDocumentToFragment";}s:31:"DirectLexLineNumberSyncInterval";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:3:"int";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:8:"Encoding";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:7:"istring";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:21:"EscapeInvalidChildren";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"EscapeInvalidTags";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:24:"EscapeNonASCIICharacters";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:14:"HiddenElements";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:8:"Language";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:9:"LexerImpl";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:5:"mixed";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:19:"MaintainLineNumbers";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:16:"RemoveInvalidImg";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:20:"RemoveScriptContents";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:5:"XHTML";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:4:"HTML";s:4:"name";s:5:"XHTML";}s:21:"CommentScriptContents";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:6:"Output";s:4:"name";s:21:"CommentScriptContents";}s:10:"TidyFormat";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:6:"Output";s:4:"name";s:10:"TidyFormat";}}s:6:"Filter";a:5:{s:6:"Custom";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"list";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:18:"ExtractStyleBlocks";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"YouTube";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:26:"ExtractStyleBlocksEscaping";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:11:"FilterParam";s:4:"name";s:26:"ExtractStyleBlocksEscaping";}s:23:"ExtractStyleBlocksScope";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:11:"FilterParam";s:4:"name";s:23:"ExtractStyleBlocksScope";}}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:23:"ExtractStyleBlocksScope";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:26:"ExtractStyleBlocksTidyImpl";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:5:"mixed";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:4:"HTML";a:21:{s:12:"EnableAttrID";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:4:"Attr";s:4:"name";s:8:"EnableID";}s:7:"Allowed";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:5:"itext";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"AllowedAttributes";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:15:"AllowedElements";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:14:"AllowedModules";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:12:"BlockWrapper";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"CoreModules";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"CustomDoctype";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:12:"DefinitionID";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"DefinitionRev";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:3:"int";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Doctype";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";a:5:{s:22:"HTML 4.01 Transitional";b:1;s:16:"HTML 4.01 Strict";b:1;s:22:"XHTML 1.0 Transitional";b:1;s:16:"XHTML 1.0 Strict";b:1;s:9:"XHTML 1.1";b:1;}s:7:"aliases";a:0:{}}s:19:"ForbiddenAttributes";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"ForbiddenElements";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:6:"Parent";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"Proprietary";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:6:"Strict";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"TidyAdd";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:9:"TidyLevel";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";a:4:{s:4:"none";b:1;s:5:"light";b:1;s:6:"medium";b:1;s:5:"heavy";b:1;}s:7:"aliases";a:0:{}}s:10:"TidyRemove";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Trusted";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:5:"XHTML";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:6:"Output";a:3:{s:21:"CommentScriptContents";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Newline";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:10:"TidyFormat";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:4:"Test";a:1:{s:12:"ForceNoIconv";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:3:"URI";a:14:{s:14:"AllowedSchemes";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:4:"Base";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"DefaultScheme";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:12:"DefinitionID";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"DefinitionRev";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:3:"int";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Disable";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:15:"DisableExternal";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:24:"DisableExternalResources";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:16:"DisableResources";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:4:"Host";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"HostBlacklist";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"list";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:12:"MakeAbsolute";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:5:"Munge";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:22:"OverrideAllowedSchemes";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}}s:9:" * parser";O:31:"HTMLPurifier_VarParser_Flexible":0:{}}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Validator.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Validator.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Validator.php (revision 21)
@@ -0,0 +1,222 @@
+<?php
+
+/**
+ * Performs validations on HTMLPurifier_ConfigSchema_Interchange
+ *
+ * @note If you see '// handled by InterchangeBuilder', that means a
+ *       design decision in that class would prevent this validation from
+ *       ever being necessary. We have them anyway, however, for
+ *       redundancy.
+ */
+class HTMLPurifier_ConfigSchema_Validator
+{
+    
+    /**
+     * Easy to access global objects.
+     */
+    protected $interchange, $aliases;
+    
+    /**
+     * Context-stack to provide easy to read error messages.
+     */
+    protected $context = array();
+    
+    /**
+     * HTMLPurifier_VarParser to test default's type.
+     */
+    protected $parser;
+    
+    public function __construct() {
+        $this->parser = new HTMLPurifier_VarParser();
+    }
+    
+    /**
+     * Validates a fully-formed interchange object. Throws an
+     * HTMLPurifier_ConfigSchema_Exception if there's a problem.
+     */
+    public function validate($interchange) {
+        $this->interchange = $interchange;
+        $this->aliases = array();
+        // PHP is a bit lax with integer <=> string conversions in
+        // arrays, so we don't use the identical !== comparison
+        foreach ($interchange->namespaces as $i => $namespace) {
+            if ($i != $namespace->namespace) $this->error(false, "Integrity violation: key '$i' does not match internal id '{$namespace->namespace}'");
+            $this->validateNamespace($namespace);
+        }
+        foreach ($interchange->directives as $i => $directive) {
+            $id = $directive->id->toString();
+            if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
+            $this->validateDirective($directive);
+        }
+        return true;
+    }
+    
+    /**
+     * Validates a HTMLPurifier_ConfigSchema_Interchange_Namespace object.
+     */
+    public function validateNamespace($n) {
+        $this->context[] = "namespace '{$n->namespace}'";
+        $this->with($n, 'namespace')
+            ->assertNotEmpty()
+            ->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
+        $this->with($n, 'description')
+            ->assertNotEmpty()
+            ->assertIsString(); // handled by InterchangeBuilder
+        array_pop($this->context);
+    }
+    
+    /**
+     * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object.
+     */
+    public function validateId($id) {
+        $id_string = $id->toString();
+        $this->context[] = "id '$id_string'";
+        if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
+            // handled by InterchangeBuilder
+            $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
+        }
+        if (!isset($this->interchange->namespaces[$id->namespace])) {
+            $this->error('namespace', 'does not exist'); // assumes that the namespace was validated already
+        }
+        $this->with($id, 'directive')
+            ->assertNotEmpty()
+            ->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
+        array_pop($this->context);
+    }
+    
+    /**
+     * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object.
+     */
+    public function validateDirective($d) {
+        $id = $d->id->toString();
+        $this->context[] = "directive '$id'";
+        $this->validateId($d->id);
+        
+        $this->with($d, 'description')
+            ->assertNotEmpty();
+        
+        // BEGIN - handled by InterchangeBuilder
+        $this->with($d, 'type')
+            ->assertNotEmpty();
+        $this->with($d, 'typeAllowsNull')
+            ->assertIsBool();
+        try {
+            // This also tests validity of $d->type
+            $this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
+        } catch (HTMLPurifier_VarParserException $e) {
+            $this->error('default', 'had error: ' . $e->getMessage());
+        }
+        // END - handled by InterchangeBuilder
+        
+        if (!is_null($d->allowed) || !empty($d->valueAliases)) {
+            // allowed and valueAliases require that we be dealing with
+            // strings, so check for that early.
+            if (!isset(HTMLPurifier_VarParser::$stringTypes[$d->type])) {
+                $this->error('type', 'must be a string type when used with allowed or value aliases');
+            }
+        }
+        
+        $this->validateDirectiveAllowed($d);
+        $this->validateDirectiveValueAliases($d);
+        $this->validateDirectiveAliases($d);
+        
+        array_pop($this->context);
+    }
+    
+    /**
+     * Extra validation if $allowed member variable of
+     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
+     */
+    public function validateDirectiveAllowed($d) {
+        if (is_null($d->allowed)) return;
+        $this->with($d, 'allowed')
+            ->assertNotEmpty()
+            ->assertIsLookup(); // handled by InterchangeBuilder
+        if (is_string($d->default) && !isset($d->allowed[$d->default])) {
+            $this->error('default', 'must be an allowed value');
+        }
+        $this->context[] = 'allowed';
+        foreach ($d->allowed as $val => $x) {
+            if (!is_string($val)) $this->error("value $val", 'must be a string');
+        }
+        array_pop($this->context);
+    }
+    
+    /**
+     * Extra validation if $valueAliases member variable of
+     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
+     */
+    public function validateDirectiveValueAliases($d) {
+        if (is_null($d->valueAliases)) return;
+        $this->with($d, 'valueAliases')
+            ->assertIsArray(); // handled by InterchangeBuilder
+        $this->context[] = 'valueAliases';
+        foreach ($d->valueAliases as $alias => $real) {
+            if (!is_string($alias)) $this->error("alias $alias", 'must be a string');
+            if (!is_string($real))  $this->error("alias target $real from alias '$alias'",  'must be a string');
+            if ($alias === $real) {
+                $this->error("alias '$alias'", "must not be an alias to itself");
+            }
+        }
+        if (!is_null($d->allowed)) {
+            foreach ($d->valueAliases as $alias => $real) {
+                if (isset($d->allowed[$alias])) {
+                    $this->error("alias '$alias'", 'must not be an allowed value');
+                } elseif (!isset($d->allowed[$real])) {
+                    $this->error("alias '$alias'", 'must be an alias to an allowed value');
+                }
+            }
+        }
+        array_pop($this->context);
+    }
+    
+    /**
+     * Extra validation if $aliases member variable of
+     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
+     */
+    public function validateDirectiveAliases($d) {
+        $this->with($d, 'aliases')
+            ->assertIsArray(); // handled by InterchangeBuilder
+        $this->context[] = 'aliases';
+        foreach ($d->aliases as $alias) {
+            $this->validateId($alias);
+            $s = $alias->toString();
+            if (isset($this->interchange->directives[$s])) {
+                $this->error("alias '$s'", 'collides with another directive');
+            }
+            if (isset($this->aliases[$s])) {
+                $other_directive = $this->aliases[$s];
+                $this->error("alias '$s'", "collides with alias for directive '$other_directive'");
+            }
+            $this->aliases[$s] = $d->id->toString();
+        }
+        array_pop($this->context);
+    }
+    
+    // protected helper functions
+    
+    /**
+     * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom
+     * for validating simple member variables of objects.
+     */
+    protected function with($obj, $member) {
+        return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
+    }
+    
+    /**
+     * Emits an error, providing helpful context.
+     */
+    protected function error($target, $msg) {
+        if ($target !== false) $prefix = ucfirst($target) . ' in ' .  $this->getFormattedContext();
+        else $prefix = ucfirst($this->getFormattedContext());
+        throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
+    }
+    
+    /**
+     * Returns a formatted context string.
+     */
+    protected function getFormattedContext() {
+        return implode(' in ', array_reverse($this->context));
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/ValidatorAtom.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/ValidatorAtom.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/ValidatorAtom.php (revision 21)
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * Fluent interface for validating the contents of member variables.
+ * This should be immutable. See HTMLPurifier_ConfigSchema_Validator for
+ * use-cases. We name this an 'atom' because it's ONLY for validations that
+ * are independent and usually scalar.
+ */
+class HTMLPurifier_ConfigSchema_ValidatorAtom
+{
+    
+    protected $context, $obj, $member, $contents;
+    
+    public function __construct($context, $obj, $member) {
+        $this->context     = $context;
+        $this->obj         = $obj;
+        $this->member      = $member;
+        $this->contents    =& $obj->$member;
+    }
+    
+    public function assertIsString() {
+        if (!is_string($this->contents)) $this->error('must be a string');
+        return $this;
+    }
+    
+    public function assertIsBool() {
+        if (!is_bool($this->contents)) $this->error('must be a boolean');
+        return $this;
+    }
+    
+    public function assertIsArray() {
+        if (!is_array($this->contents)) $this->error('must be an array');
+        return $this;
+    }
+    
+    public function assertNotNull() {
+        if ($this->contents === null) $this->error('must not be null');
+        return $this;
+    }
+    
+    public function assertAlnum() {
+        $this->assertIsString();
+        if (!ctype_alnum($this->contents)) $this->error('must be alphanumeric');
+        return $this;
+    }
+    
+    public function assertNotEmpty() {
+        if (empty($this->contents)) $this->error('must not be empty');
+        return $this;
+    }
+    
+    public function assertIsLookup() {
+        $this->assertIsArray();
+        foreach ($this->contents as $v) {
+            if ($v !== true) $this->error('must be a lookup array');
+        }
+        return $this;
+    }
+    
+    protected function error($msg) {
+        throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg);
+    }
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema/Interchange.php (revision 21)
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Generic schema interchange format that can be converted to a runtime
+ * representation (HTMLPurifier_ConfigSchema) or HTML documentation. Members
+ * are completely validated.
+ */
+class HTMLPurifier_ConfigSchema_Interchange
+{
+    
+    /**
+     * Name of the application this schema is describing.
+     */
+    public $name;
+    
+    /**
+     * Array of Namespace ID => array(namespace info)
+     */
+    public $namespaces = array();
+    
+    /**
+     * Array of Directive ID => array(directive info)
+     */
+    public $directives = array();
+    
+    /**
+     * Adds a namespace array to $namespaces
+     */
+    public function addNamespace($namespace) {
+        if (isset($this->namespaces[$i = $namespace->namespace])) {
+            throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine namespace '$i'");
+        }
+        $this->namespaces[$i] = $namespace;
+    }
+    
+    /**
+     * Adds a directive array to $directives
+     */
+    public function addDirective($directive) {
+        if (isset($this->directives[$i = $directive->id->toString()])) {
+            throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'");
+        }
+        $this->directives[$i] = $directive;
+    }
+    
+    /**
+     * Convenience function to perform standard validation. Throws exception
+     * on failed validation.
+     */
+    public function validate() {
+        $validator = new HTMLPurifier_ConfigSchema_Validator();
+        return $validator->validate($this);
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform.php (revision 21)
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * Defines a mutation of an obsolete tag into a valid tag.
+ */
+abstract class HTMLPurifier_TagTransform
+{
+    
+    /**
+     * Tag name to transform the tag to.
+     */
+    public $transform_to;
+    
+    /**
+     * Transforms the obsolete tag into the valid tag.
+     * @param $tag Tag to be transformed.
+     * @param $config Mandatory HTMLPurifier_Config object
+     * @param $context Mandatory HTMLPurifier_Context object
+     */
+    abstract public function transform($tag, $config, $context);
+    
+    /**
+     * Prepends CSS properties to the style attribute, creating the
+     * attribute if it doesn't exist.
+     * @warning Copied over from AttrTransform, be sure to keep in sync
+     * @param $attr Attribute array to process (passed by reference)
+     * @param $css CSS to prepend
+     */
+    protected function prependCSS(&$attr, $css) {
+        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
+        $attr['style'] = $css . $attr['style'];
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Doctype.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Doctype.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Doctype.php (revision 21)
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * Represents a document type, contains information on which modules
+ * need to be loaded.
+ * @note This class is inspected by Printer_HTMLDefinition->renderDoctype.
+ *       If structure changes, please update that function.
+ */
+class HTMLPurifier_Doctype
+{
+    /**
+     * Full name of doctype
+     */
+    public $name;
+    
+    /**
+     * List of standard modules (string identifiers or literal objects)
+     * that this doctype uses
+     */
+    public $modules = array();
+    
+    /**
+     * List of modules to use for tidying up code
+     */
+    public $tidyModules = array();
+    
+    /**
+     * Is the language derived from XML (i.e. XHTML)?
+     */
+    public $xml = true;
+    
+    /**
+     * List of aliases for this doctype
+     */
+    public $aliases = array();
+    
+    /**
+     * Public DTD identifier
+     */
+    public $dtdPublic;
+    
+    /**
+     * System DTD identifier
+     */
+    public $dtdSystem;
+    
+    public function __construct($name = null, $xml = true, $modules = array(),
+        $tidyModules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null
+    ) {
+        $this->name         = $name;
+        $this->xml          = $xml;
+        $this->modules      = $modules;
+        $this->tidyModules  = $tidyModules;
+        $this->aliases      = $aliases;
+        $this->dtdPublic    = $dtd_public;
+        $this->dtdSystem    = $dtd_system;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ContentSets.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ContentSets.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ContentSets.php (revision 21)
@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * @todo Unit test
+ */
+class HTMLPurifier_ContentSets
+{
+    
+    /**
+     * List of content set strings (pipe seperators) indexed by name.
+     */
+    public $info = array();
+    
+    /**
+     * List of content set lookups (element => true) indexed by name.
+     * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
+     */
+    public $lookup = array();
+    
+    /**
+     * Synchronized list of defined content sets (keys of info)
+     */
+    protected $keys = array();
+    /**
+     * Synchronized list of defined content values (values of info)
+     */
+    protected $values = array();
+    
+    /**
+     * Merges in module's content sets, expands identifiers in the content
+     * sets and populates the keys, values and lookup member variables.
+     * @param $modules List of HTMLPurifier_HTMLModule
+     */
+    public function __construct($modules) {
+        if (!is_array($modules)) $modules = array($modules);
+        // populate content_sets based on module hints
+        // sorry, no way of overloading
+        foreach ($modules as $module_i => $module) {
+            foreach ($module->content_sets as $key => $value) {
+                if (isset($this->info[$key])) {
+                    // add it into the existing content set
+                    $this->info[$key] = $this->info[$key] . ' | ' . $value;
+                } else {
+                    $this->info[$key] = $value;
+                }
+            }
+        }
+        // perform content_set expansions
+        $this->keys = array_keys($this->info);
+        foreach ($this->info as $i => $set) {
+            // only performed once, so infinite recursion is not
+            // a problem
+            $this->info[$i] =
+                str_replace(
+                    $this->keys,
+                    // must be recalculated each time due to
+                    // changing substitutions
+                    array_values($this->info),
+                $set);
+        }
+        $this->values = array_values($this->info);
+        
+        // generate lookup tables
+        foreach ($this->info as $name => $set) {
+            $this->lookup[$name] = $this->convertToLookup($set);
+        }
+    }
+    
+    /**
+     * Accepts a definition; generates and assigns a ChildDef for it
+     * @param $def HTMLPurifier_ElementDef reference
+     * @param $module Module that defined the ElementDef
+     */
+    public function generateChildDef(&$def, $module) {
+        if (!empty($def->child)) return; // already done!
+        $content_model = $def->content_model;
+        if (is_string($content_model)) {
+            $def->content_model = str_replace(
+                $this->keys, $this->values, $content_model);
+        }
+        $def->child = $this->getChildDef($def, $module);
+    }
+    
+    /**
+     * Instantiates a ChildDef based on content_model and content_model_type
+     * member variables in HTMLPurifier_ElementDef
+     * @note This will also defer to modules for custom HTMLPurifier_ChildDef
+     *       subclasses that need content set expansion
+     * @param $def HTMLPurifier_ElementDef to have ChildDef extracted
+     * @return HTMLPurifier_ChildDef corresponding to ElementDef
+     */
+    public function getChildDef($def, $module) {
+        $value = $def->content_model;
+        if (is_object($value)) {
+            trigger_error(
+                'Literal object child definitions should be stored in '.
+                'ElementDef->child not ElementDef->content_model',
+                E_USER_NOTICE
+            );
+            return $value;
+        }
+        switch ($def->content_model_type) {
+            case 'required':
+                return new HTMLPurifier_ChildDef_Required($value);
+            case 'optional':
+                return new HTMLPurifier_ChildDef_Optional($value);
+            case 'empty':
+                return new HTMLPurifier_ChildDef_Empty();
+            case 'custom':
+                return new HTMLPurifier_ChildDef_Custom($value);
+        }
+        // defer to its module
+        $return = false;
+        if ($module->defines_child_def) { // save a func call
+            $return = $module->getChildDef($def);
+        }
+        if ($return !== false) return $return;
+        // error-out
+        trigger_error(
+            'Could not determine which ChildDef class to instantiate',
+            E_USER_ERROR
+        );
+        return false;
+    }
+    
+    /**
+     * Converts a string list of elements separated by pipes into
+     * a lookup array.
+     * @param $string List of elements
+     * @return Lookup array of elements
+     */
+    protected function convertToLookup($string) {
+        $array = explode('|', str_replace(' ', '', $string));
+        $ret = array();
+        foreach ($array as $i => $k) {
+            $ret[$k] = true;
+        }
+        return $ret;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIParser.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIParser.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIParser.php (revision 21)
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * Parses a URI into the components and fragment identifier as specified
+ * by RFC 3986.
+ */
+class HTMLPurifier_URIParser
+{
+    
+    /**
+     * Instance of HTMLPurifier_PercentEncoder to do normalization with.
+     */
+    protected $percentEncoder;
+    
+    public function __construct() {
+        $this->percentEncoder = new HTMLPurifier_PercentEncoder();
+    }
+    
+    /**
+     * Parses a URI.
+     * @param $uri string URI to parse
+     * @return HTMLPurifier_URI representation of URI. This representation has
+     *         not been validated yet and may not conform to RFC.
+     */
+    public function parse($uri) {
+        
+        $uri = $this->percentEncoder->normalize($uri);
+        
+        // Regexp is as per Appendix B.
+        // Note that ["<>] are an addition to the RFC's recommended 
+        // characters, because they represent external delimeters.
+        $r_URI = '!'.
+            '(([^:/?#"<>]+):)?'. // 2. Scheme
+            '(//([^/?#"<>]*))?'. // 4. Authority
+            '([^?#"<>]*)'.       // 5. Path
+            '(\?([^#"<>]*))?'.   // 7. Query
+            '(#([^"<>]*))?'.     // 8. Fragment
+            '!';
+        
+        $matches = array();
+        $result = preg_match($r_URI, $uri, $matches);
+        
+        if (!$result) return false; // *really* invalid URI
+        
+        // seperate out parts
+        $scheme     = !empty($matches[1]) ? $matches[2] : null;
+        $authority  = !empty($matches[3]) ? $matches[4] : null;
+        $path       = $matches[5]; // always present, can be empty
+        $query      = !empty($matches[6]) ? $matches[7] : null;
+        $fragment   = !empty($matches[8]) ? $matches[9] : null;
+        
+        // further parse authority
+        if ($authority !== null) {
+            $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/";
+            $matches = array();
+            preg_match($r_authority, $authority, $matches);
+            $userinfo   = !empty($matches[1]) ? $matches[2] : null;
+            $host       = !empty($matches[3]) ? $matches[3] : '';
+            $port       = !empty($matches[4]) ? (int) $matches[5] : null;
+        } else {
+            $port = $host = $userinfo = null;
+        }
+        
+        return new HTMLPurifier_URI(
+            $scheme, $userinfo, $host, $port, $path, $query, $fragment);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache.php (revision 21)
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * Abstract class representing Definition cache managers that implements
+ * useful common methods and is a factory.
+ * @todo Create a separate maintenance file advanced users can use to
+ *       cache their custom HTMLDefinition, which can be loaded
+ *       via a configuration directive
+ * @todo Implement memcached
+ */
+abstract class HTMLPurifier_DefinitionCache
+{
+    
+    public $type;
+    
+    /**
+     * @param $name Type of definition objects this instance of the
+     *      cache will handle.
+     */
+    public function __construct($type) {
+        $this->type = $type;
+    }
+    
+    /**
+     * Generates a unique identifier for a particular configuration
+     * @param Instance of HTMLPurifier_Config
+     */
+    public function generateKey($config) {
+        return $config->version . ',' . // possibly replace with function calls
+               $config->getBatchSerial($this->type) . ',' .
+               $config->get($this->type, 'DefinitionRev');
+    }
+    
+    /**
+     * Tests whether or not a key is old with respect to the configuration's
+     * version and revision number.
+     * @param $key Key to test
+     * @param $config Instance of HTMLPurifier_Config to test against
+     */
+    public function isOld($key, $config) {
+        if (substr_count($key, ',') < 2) return true;
+        list($version, $hash, $revision) = explode(',', $key, 3);
+        $compare = version_compare($version, $config->version);
+        // version mismatch, is always old
+        if ($compare != 0) return true;
+        // versions match, ids match, check revision number
+        if (
+            $hash == $config->getBatchSerial($this->type) &&
+            $revision < $config->get($this->type, 'DefinitionRev')
+        ) return true;
+        return false;
+    }
+    
+    /**
+     * Checks if a definition's type jives with the cache's type
+     * @note Throws an error on failure
+     * @param $def Definition object to check
+     * @return Boolean true if good, false if not
+     */
+    public function checkDefType($def) {
+        if ($def->type !== $this->type) {
+            trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}");
+            return false;
+        }
+        return true;
+    }
+    
+    /**
+     * Adds a definition object to the cache
+     */
+    abstract public function add($def, $config);
+    
+    /**
+     * Unconditionally saves a definition object to the cache
+     */
+    abstract public function set($def, $config);
+    
+    /**
+     * Replace an object in the cache
+     */
+    abstract public function replace($def, $config);
+    
+    /**
+     * Retrieves a definition object from the cache
+     */
+    abstract public function get($config);
+    
+    /**
+     * Removes a definition object to the cache
+     */
+    abstract public function remove($config);
+    
+    /**
+     * Clears all objects from cache
+     */
+    abstract public function flush($config);
+    
+    /**
+     * Clears all expired (older version or revision) objects from cache
+     * @note Be carefuly implementing this method as flush. Flush must
+     *       not interfere with other Definition types, and cleanup()
+     *       should not be repeatedly called by userland code.
+     */
+    abstract public function cleanup($config);
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLDefinition.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLDefinition.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLDefinition.php (revision 21)
@@ -0,0 +1,403 @@
+<?php
+
+/**
+ * Definition of the purified HTML that describes allowed children,
+ * attributes, and many other things.
+ * 
+ * Conventions:
+ * 
+ * All member variables that are prefixed with info
+ * (including the main $info array) are used by HTML Purifier internals
+ * and should not be directly edited when customizing the HTMLDefinition.
+ * They can usually be set via configuration directives or custom
+ * modules.
+ * 
+ * On the other hand, member variables without the info prefix are used
+ * internally by the HTMLDefinition and MUST NOT be used by other HTML
+ * Purifier internals. Many of them, however, are public, and may be
+ * edited by userspace code to tweak the behavior of HTMLDefinition.
+ * 
+ * @note This class is inspected by Printer_HTMLDefinition; please
+ *       update that class if things here change.
+ *
+ * @warning Directives that change this object's structure must be in
+ *          the HTML or Attr namespace!
+ */
+class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
+{
+    
+    // FULLY-PUBLIC VARIABLES ---------------------------------------------
+    
+    /**
+     * Associative array of element names to HTMLPurifier_ElementDef
+     */
+    public $info = array();
+    
+    /**
+     * Associative array of global attribute name to attribute definition.
+     */
+    public $info_global_attr = array();
+    
+    /**
+     * String name of parent element HTML will be going into.
+     */
+    public $info_parent = 'div';
+    
+    /**
+     * Definition for parent element, allows parent element to be a
+     * tag that's not allowed inside the HTML fragment.
+     */
+    public $info_parent_def;
+    
+    /**
+     * String name of element used to wrap inline elements in block context
+     * @note This is rarely used except for BLOCKQUOTEs in strict mode
+     */
+    public $info_block_wrapper = 'p';
+    
+    /**
+     * Associative array of deprecated tag name to HTMLPurifier_TagTransform
+     */
+    public $info_tag_transform = array();
+    
+    /**
+     * Indexed list of HTMLPurifier_AttrTransform to be performed before validation.
+     */
+    public $info_attr_transform_pre = array();
+    
+    /**
+     * Indexed list of HTMLPurifier_AttrTransform to be performed after validation.
+     */
+    public $info_attr_transform_post = array();
+    
+    /**
+     * Nested lookup array of content set name (Block, Inline) to
+     * element name to whether or not it belongs in that content set.
+     */
+    public $info_content_sets = array();
+    
+    /**
+     * Doctype object
+     */
+    public $doctype;
+    
+    
+    
+    // RAW CUSTOMIZATION STUFF --------------------------------------------
+    
+    /**
+     * Adds a custom attribute to a pre-existing element
+     * @note This is strictly convenience, and does not have a corresponding
+     *       method in HTMLPurifier_HTMLModule
+     * @param $element_name String element name to add attribute to
+     * @param $attr_name String name of attribute
+     * @param $def Attribute definition, can be string or object, see
+     *             HTMLPurifier_AttrTypes for details
+     */
+    public function addAttribute($element_name, $attr_name, $def) {
+        $module = $this->getAnonymousModule();
+        if (!isset($module->info[$element_name])) {
+            $element = $module->addBlankElement($element_name);
+        } else {
+            $element = $module->info[$element_name];
+        }
+        $element->attr[$attr_name] = $def;
+    }
+    
+    /**
+     * Adds a custom element to your HTML definition
+     * @note See HTMLPurifier_HTMLModule::addElement for detailed 
+     *       parameter and return value descriptions.
+     */
+    public function addElement($element_name, $type, $contents, $attr_collections, $attributes) {
+        $module = $this->getAnonymousModule();
+        // assume that if the user is calling this, the element
+        // is safe. This may not be a good idea
+        $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes);
+        return $element;
+    }
+    
+    /**
+     * Adds a blank element to your HTML definition, for overriding
+     * existing behavior
+     * @note See HTMLPurifier_HTMLModule::addBlankElement for detailed
+     *       parameter and return value descriptions.
+     */
+    public function addBlankElement($element_name) {
+        $module  = $this->getAnonymousModule();
+        $element = $module->addBlankElement($element_name);
+        return $element;
+    }
+    
+    /**
+     * Retrieves a reference to the anonymous module, so you can
+     * bust out advanced features without having to make your own
+     * module.
+     */
+    public function getAnonymousModule() {
+        if (!$this->_anonModule) {
+            $this->_anonModule = new HTMLPurifier_HTMLModule();
+            $this->_anonModule->name = 'Anonymous';
+        }
+        return $this->_anonModule;
+    }
+    
+    private $_anonModule;
+    
+    
+    // PUBLIC BUT INTERNAL VARIABLES --------------------------------------
+    
+    public $type = 'HTML';
+    public $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */
+    
+    /**
+     * Performs low-cost, preliminary initialization.
+     */
+    public function __construct() {
+        $this->manager = new HTMLPurifier_HTMLModuleManager();
+    }
+    
+    protected function doSetup($config) {
+        $this->processModules($config);
+        $this->setupConfigStuff($config);
+        unset($this->manager);
+        
+        // cleanup some of the element definitions
+        foreach ($this->info as $k => $v) {
+            unset($this->info[$k]->content_model);
+            unset($this->info[$k]->content_model_type);
+        }
+    }
+    
+    /**
+     * Extract out the information from the manager
+     */
+    protected function processModules($config) {
+        
+        if ($this->_anonModule) {
+            // for user specific changes
+            // this is late-loaded so we don't have to deal with PHP4
+            // reference wonky-ness
+            $this->manager->addModule($this->_anonModule);
+            unset($this->_anonModule);
+        }
+        
+        $this->manager->setup($config);
+        $this->doctype = $this->manager->doctype;
+        
+        foreach ($this->manager->modules as $module) {
+            foreach($module->info_tag_transform         as $k => $v) {
+                if ($v === false) unset($this->info_tag_transform[$k]);
+                else $this->info_tag_transform[$k] = $v;
+            }
+            foreach($module->info_attr_transform_pre    as $k => $v) {
+                if ($v === false) unset($this->info_attr_transform_pre[$k]);
+                else $this->info_attr_transform_pre[$k] = $v;
+            }
+            foreach($module->info_attr_transform_post   as $k => $v) {
+                if ($v === false) unset($this->info_attr_transform_post[$k]);
+                else $this->info_attr_transform_post[$k] = $v;
+            }
+        }
+        
+        $this->info = $this->manager->getElements();
+        $this->info_content_sets = $this->manager->contentSets->lookup;
+        
+    }
+    
+    /**
+     * Sets up stuff based on config. We need a better way of doing this.
+     */
+    protected function setupConfigStuff($config) {
+        
+        $block_wrapper = $config->get('HTML', 'BlockWrapper');
+        if (isset($this->info_content_sets['Block'][$block_wrapper])) {
+            $this->info_block_wrapper = $block_wrapper;
+        } else {
+            trigger_error('Cannot use non-block element as block wrapper',
+                E_USER_ERROR);
+        }
+        
+        $parent = $config->get('HTML', 'Parent');
+        $def = $this->manager->getElement($parent, true);
+        if ($def) {
+            $this->info_parent = $parent;
+            $this->info_parent_def = $def;
+        } else {
+            trigger_error('Cannot use unrecognized element as parent',
+                E_USER_ERROR);
+            $this->info_parent_def = $this->manager->getElement($this->info_parent, true);
+        }
+        
+        // support template text
+        $support = "(for information on implementing this, see the ".
+                   "support forums) ";
+        
+        // setup allowed elements -----------------------------------------
+        
+        $allowed_elements = $config->get('HTML', 'AllowedElements');
+        $allowed_attributes = $config->get('HTML', 'AllowedAttributes'); // retrieve early
+        
+        if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
+            $allowed = $config->get('HTML', 'Allowed');
+            if (is_string($allowed)) {
+                list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed);
+            }
+        }
+        
+        if (is_array($allowed_elements)) {
+            foreach ($this->info as $name => $d) {
+                if(!isset($allowed_elements[$name])) unset($this->info[$name]);
+                unset($allowed_elements[$name]);
+            }
+            // emit errors
+            foreach ($allowed_elements as $element => $d) {
+                $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful!
+                trigger_error("Element '$element' is not supported $support", E_USER_WARNING);
+            }
+        }
+        
+        // setup allowed attributes ---------------------------------------
+        
+        $allowed_attributes_mutable = $allowed_attributes; // by copy!
+        if (is_array($allowed_attributes)) {
+            
+            // This actually doesn't do anything, since we went away from
+            // global attributes. It's possible that userland code uses
+            // it, but HTMLModuleManager doesn't!
+            foreach ($this->info_global_attr as $attr => $x) {
+                $keys = array($attr, "*@$attr", "*.$attr");
+                $delete = true;
+                foreach ($keys as $key) {
+                    if ($delete && isset($allowed_attributes[$key])) {
+                        $delete = false;
+                    }
+                    if (isset($allowed_attributes_mutable[$key])) {
+                        unset($allowed_attributes_mutable[$key]);
+                    }
+                }
+                if ($delete) unset($this->info_global_attr[$attr]);
+            }
+            
+            foreach ($this->info as $tag => $info) {
+                foreach ($info->attr as $attr => $x) {
+                    $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr");
+                    $delete = true;
+                    foreach ($keys as $key) {
+                        if ($delete && isset($allowed_attributes[$key])) {
+                            $delete = false;
+                        }
+                        if (isset($allowed_attributes_mutable[$key])) {
+                            unset($allowed_attributes_mutable[$key]);
+                        }
+                    }
+                    if ($delete) unset($this->info[$tag]->attr[$attr]);
+                }
+            }
+            // emit errors
+            foreach ($allowed_attributes_mutable as $elattr => $d) {
+                $bits = preg_split('/[.@]/', $elattr, 2);
+                $c = count($bits);
+                switch ($c) {
+                    case 2:
+                        if ($bits[0] !== '*') {
+                            $element = htmlspecialchars($bits[0]);
+                            $attribute = htmlspecialchars($bits[1]);
+                            if (!isset($this->info[$element])) {
+                                trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support");
+                            } else {
+                                trigger_error("Attribute '$attribute' in element '$element' not supported $support",
+                                    E_USER_WARNING);
+                            }
+                            break;
+                        }
+                        // otherwise fall through
+                    case 1:
+                        $attribute = htmlspecialchars($bits[0]);
+                        trigger_error("Global attribute '$attribute' is not ".
+                            "supported in any elements $support",
+                            E_USER_WARNING);
+                        break;
+                }
+            }
+            
+        }
+        
+        // setup forbidden elements ---------------------------------------
+        
+        $forbidden_elements   = $config->get('HTML', 'ForbiddenElements');
+        $forbidden_attributes = $config->get('HTML', 'ForbiddenAttributes');
+        
+        foreach ($this->info as $tag => $info) {
+            if (isset($forbidden_elements[$tag])) {
+                unset($this->info[$tag]);
+                continue;
+            }
+            foreach ($info->attr as $attr => $x) {
+                if (
+                    isset($forbidden_attributes["$tag@$attr"]) ||
+                    isset($forbidden_attributes["*@$attr"]) ||
+                    isset($forbidden_attributes[$attr])
+                ) {
+                    unset($this->info[$tag]->attr[$attr]);
+                    continue;
+                } // this segment might get removed eventually
+                elseif (isset($forbidden_attributes["$tag.$attr"])) {
+                    // $tag.$attr are not user supplied, so no worries!
+                    trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING);
+                }
+            }
+        }
+        foreach ($forbidden_attributes as $key => $v) {
+            if (strlen($key) < 2) continue;
+            if ($key[0] != '*') continue;
+            if ($key[1] == '.') {
+                trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING);
+            }
+        }
+        
+    }
+    
+    /**
+     * Parses a TinyMCE-flavored Allowed Elements and Attributes list into
+     * separate lists for processing. Format is element[attr1|attr2],element2...
+     * @warning Although it's largely drawn from TinyMCE's implementation,
+     *      it is different, and you'll probably have to modify your lists
+     * @param $list String list to parse
+     * @param array($allowed_elements, $allowed_attributes)
+     * @todo Give this its own class, probably static interface
+     */
+    public function parseTinyMCEAllowedList($list) {
+        
+        $list = str_replace(array(' ', "\t"), '', $list);
+        
+        $elements = array();
+        $attributes = array();
+        
+        $chunks = preg_split('/(,|[\n\r]+)/', $list);
+        foreach ($chunks as $chunk) {
+            if (empty($chunk)) continue;
+            // remove TinyMCE element control characters
+            if (!strpos($chunk, '[')) {
+                $element = $chunk;
+                $attr = false;
+            } else {
+                list($element, $attr) = explode('[', $chunk);
+            }
+            if ($element !== '*') $elements[$element] = true;
+            if (!$attr) continue;
+            $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ]
+            $attr = explode('|', $attr);
+            foreach ($attr as $key) {
+                $attributes["$element.$key"] = true;
+            }
+        }
+        
+        return array($elements, $attributes);
+        
+    }
+    
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Encoder.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Encoder.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Encoder.php (revision 21)
@@ -0,0 +1,373 @@
+<?php
+
+/**
+ * A UTF-8 specific character encoder that handles cleaning and transforming.
+ * @note All functions in this class should be static.
+ */
+class HTMLPurifier_Encoder
+{
+    
+    /**
+     * Constructor throws fatal error if you attempt to instantiate class
+     */
+    private function __construct() {
+        trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
+    }
+    
+    /**
+     * Error-handler that mutes errors, alternative to shut-up operator.
+     */
+    private static function muteErrorHandler() {}
+    
+    /**
+     * Cleans a UTF-8 string for well-formedness and SGML validity
+     * 
+     * It will parse according to UTF-8 and return a valid UTF8 string, with
+     * non-SGML codepoints excluded.
+     * 
+     * @note Just for reference, the non-SGML code points are 0 to 31 and
+     *       127 to 159, inclusive.  However, we allow code points 9, 10
+     *       and 13, which are the tab, line feed and carriage return
+     *       respectively. 128 and above the code points map to multibyte
+     *       UTF-8 representations.
+     * 
+     * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and
+     *       hsivonen@iki.fi at <http://iki.fi/hsivonen/php-utf8/> under the
+     *       LGPL license.  Notes on what changed are inside, but in general,
+     *       the original code transformed UTF-8 text into an array of integer
+     *       Unicode codepoints. Understandably, transforming that back to
+     *       a string would be somewhat expensive, so the function was modded to
+     *       directly operate on the string.  However, this discourages code
+     *       reuse, and the logic enumerated here would be useful for any
+     *       function that needs to be able to understand UTF-8 characters.
+     *       As of right now, only smart lossless character encoding converters
+     *       would need that, and I'm probably not going to implement them.
+     *       Once again, PHP 6 should solve all our problems.
+     */
+    public static function cleanUTF8($str, $force_php = false) {
+        
+        static $non_sgml_chars = array();
+        if (empty($non_sgml_chars)) {
+            for ($i = 0; $i <= 31; $i++) {
+                // non-SGML ASCII chars
+                // save \r, \t and \n
+                if ($i == 9 || $i == 13 || $i == 10) continue;
+                $non_sgml_chars[chr($i)] = '';
+            }
+            for ($i = 127; $i <= 159; $i++) {
+                $non_sgml_chars[HTMLPurifier_Encoder::unichr($i)] = '';
+            }
+        }
+        
+        static $iconv = null;
+        if ($iconv === null) $iconv = function_exists('iconv');
+        
+        // UTF-8 validity is checked since PHP 4.3.5
+        // This is an optimization: if the string is already valid UTF-8, no
+        // need to do iconv/php stuff. 99% of the time, this will be the case.
+        if (preg_match('/^.{1}/us', $str)) {
+            return strtr($str, $non_sgml_chars);
+        }
+        
+        if ($iconv && !$force_php) {
+            // do the shortcut way
+            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
+            $str = iconv('UTF-8', 'UTF-8//IGNORE', $str);
+            restore_error_handler();
+            return strtr($str, $non_sgml_chars);
+        }
+        
+        $mState = 0; // cached expected number of octets after the current octet
+                     // until the beginning of the next UTF8 character sequence
+        $mUcs4  = 0; // cached Unicode character
+        $mBytes = 1; // cached expected number of octets in the current sequence
+        
+        // original code involved an $out that was an array of Unicode
+        // codepoints.  Instead of having to convert back into UTF-8, we've
+        // decided to directly append valid UTF-8 characters onto a string
+        // $out once they're done.  $char accumulates raw bytes, while $mUcs4
+        // turns into the Unicode code point, so there's some redundancy.
+        
+        $out = '';
+        $char = '';
+        
+        $len = strlen($str);
+        for($i = 0; $i < $len; $i++) {
+            $in = ord($str{$i});
+            $char .= $str[$i]; // append byte to char
+            if (0 == $mState) {
+                // When mState is zero we expect either a US-ASCII character 
+                // or a multi-octet sequence.
+                if (0 == (0x80 & ($in))) {
+                    // US-ASCII, pass straight through.
+                    if (($in <= 31 || $in == 127) && 
+                        !($in == 9 || $in == 13 || $in == 10) // save \r\t\n
+                    ) {
+                        // control characters, remove
+                    } else {
+                        $out .= $char;
+                    }
+                    // reset
+                    $char = '';
+                    $mBytes = 1;
+                } elseif (0xC0 == (0xE0 & ($in))) {
+                    // First octet of 2 octet sequence
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 0x1F) << 6;
+                    $mState = 1;
+                    $mBytes = 2;
+                } elseif (0xE0 == (0xF0 & ($in))) {
+                    // First octet of 3 octet sequence
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 0x0F) << 12;
+                    $mState = 2;
+                    $mBytes = 3;
+                } elseif (0xF0 == (0xF8 & ($in))) {
+                    // First octet of 4 octet sequence
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 0x07) << 18;
+                    $mState = 3;
+                    $mBytes = 4;
+                } elseif (0xF8 == (0xFC & ($in))) {
+                    // First octet of 5 octet sequence.
+                    // 
+                    // This is illegal because the encoded codepoint must be 
+                    // either:
+                    // (a) not the shortest form or
+                    // (b) outside the Unicode range of 0-0x10FFFF.
+                    // Rather than trying to resynchronize, we will carry on 
+                    // until the end of the sequence and let the later error
+                    // handling code catch it.
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 0x03) << 24;
+                    $mState = 4;
+                    $mBytes = 5;
+                } elseif (0xFC == (0xFE & ($in))) {
+                    // First octet of 6 octet sequence, see comments for 5
+                    // octet sequence.
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 1) << 30;
+                    $mState = 5;
+                    $mBytes = 6;
+                } else {
+                    // Current octet is neither in the US-ASCII range nor a 
+                    // legal first octet of a multi-octet sequence.
+                    $mState = 0;
+                    $mUcs4  = 0;
+                    $mBytes = 1;
+                    $char = '';
+                }
+            } else {
+                // When mState is non-zero, we expect a continuation of the
+                // multi-octet sequence
+                if (0x80 == (0xC0 & ($in))) {
+                    // Legal continuation.
+                    $shift = ($mState - 1) * 6;
+                    $tmp = $in;
+                    $tmp = ($tmp & 0x0000003F) << $shift;
+                    $mUcs4 |= $tmp;
+                    
+                    if (0 == --$mState) {
+                        // End of the multi-octet sequence. mUcs4 now contains
+                        // the final Unicode codepoint to be output
+                        
+                        // Check for illegal sequences and codepoints.
+                        
+                        // From Unicode 3.1, non-shortest form is illegal
+                        if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
+                            ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+                            ((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
+                            (4 < $mBytes) ||
+                            // From Unicode 3.2, surrogate characters = illegal
+                            (($mUcs4 & 0xFFFFF800) == 0xD800) ||
+                            // Codepoints outside the Unicode range are illegal
+                            ($mUcs4 > 0x10FFFF)
+                        ) {
+                            
+                        } elseif (0xFEFF != $mUcs4 && // omit BOM
+                            !($mUcs4 >= 128 && $mUcs4 <= 159) // omit non-SGML
+                        ) {
+                            $out .= $char;
+                        }
+                        // initialize UTF8 cache (reset)
+                        $mState = 0;
+                        $mUcs4  = 0;
+                        $mBytes = 1;
+                        $char = '';
+                    }
+                } else {
+                    // ((0xC0 & (*in) != 0x80) && (mState != 0))
+                    // Incomplete multi-octet sequence.
+                    // used to result in complete fail, but we'll reset
+                    $mState = 0;
+                    $mUcs4  = 0;
+                    $mBytes = 1;
+                    $char ='';
+                }
+            }
+        }
+        return $out;
+    }
+    
+    /**
+     * Translates a Unicode codepoint into its corresponding UTF-8 character.
+     * @note Based on Feyd's function at
+     *       <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>,
+     *       which is in public domain.
+     * @note While we're going to do code point parsing anyway, a good
+     *       optimization would be to refuse to translate code points that
+     *       are non-SGML characters.  However, this could lead to duplication.
+     * @note This is very similar to the unichr function in
+     *       maintenance/generate-entity-file.php (although this is superior,
+     *       due to its sanity checks).
+     */
+    
+    // +----------+----------+----------+----------+
+    // | 33222222 | 22221111 | 111111   |          |
+    // | 10987654 | 32109876 | 54321098 | 76543210 | bit
+    // +----------+----------+----------+----------+
+    // |          |          |          | 0xxxxxxx | 1 byte 0x00000000..0x0000007F
+    // |          |          | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF
+    // |          | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF
+    // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF
+    // +----------+----------+----------+----------+
+    // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
+    // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
+    // +----------+----------+----------+----------+ 
+    
+    public static function unichr($code) {
+        if($code > 1114111 or $code < 0 or
+          ($code >= 55296 and $code <= 57343) ) {
+            // bits are set outside the "valid" range as defined
+            // by UNICODE 4.1.0 
+            return '';
+        }
+        
+        $x = $y = $z = $w = 0; 
+        if ($code < 128) {
+            // regular ASCII character
+            $x = $code;
+        } else {
+            // set up bits for UTF-8
+            $x = ($code & 63) | 128;
+            if ($code < 2048) {
+                $y = (($code & 2047) >> 6) | 192;
+            } else {
+                $y = (($code & 4032) >> 6) | 128;
+                if($code < 65536) {
+                    $z = (($code >> 12) & 15) | 224;
+                } else {
+                    $z = (($code >> 12) & 63) | 128;
+                    $w = (($code >> 18) & 7)  | 240;
+                }
+            } 
+        }
+        // set up the actual character
+        $ret = '';
+        if($w) $ret .= chr($w);
+        if($z) $ret .= chr($z);
+        if($y) $ret .= chr($y);
+        $ret .= chr($x); 
+        
+        return $ret;
+    }
+    
+    /**
+     * Converts a string to UTF-8 based on configuration.
+     */
+    public static function convertToUTF8($str, $config, $context) {
+        static $iconv = null;
+        if ($iconv === null) $iconv = function_exists('iconv');
+        $encoding = $config->get('Core', 'Encoding');
+        if ($encoding === 'utf-8') return $str;
+        if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
+            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
+            $str = iconv($encoding, 'utf-8//IGNORE', $str);
+            restore_error_handler();
+            return $str;
+        } elseif ($encoding === 'iso-8859-1') {
+            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
+            $str = utf8_encode($str);
+            restore_error_handler();
+            return $str;
+        }
+        trigger_error('Encoding not supported', E_USER_ERROR);
+    }
+    
+    /**
+     * Converts a string from UTF-8 based on configuration.
+     * @note Currently, this is a lossy conversion, with unexpressable
+     *       characters being omitted.
+     */
+    public static function convertFromUTF8($str, $config, $context) {
+        static $iconv = null;
+        if ($iconv === null) $iconv = function_exists('iconv');
+        $encoding = $config->get('Core', 'Encoding');
+        if ($encoding === 'utf-8') return $str;
+        if ($config->get('Core', 'EscapeNonASCIICharacters')) {
+            $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
+        }
+        if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
+            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
+            $str = iconv('utf-8', $encoding . '//IGNORE', $str);
+            restore_error_handler();
+            return $str;
+        } elseif ($encoding === 'iso-8859-1') {
+            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
+            $str = utf8_decode($str);
+            restore_error_handler();
+            return $str;
+        }
+        trigger_error('Encoding not supported', E_USER_ERROR);
+    }
+    
+    /**
+     * Lossless (character-wise) conversion of HTML to ASCII
+     * @param $str UTF-8 string to be converted to ASCII
+     * @returns ASCII encoded string with non-ASCII character entity-ized
+     * @warning Adapted from MediaWiki, claiming fair use: this is a common
+     *       algorithm. If you disagree with this license fudgery,
+     *       implement it yourself.
+     * @note Uses decimal numeric entities since they are best supported.
+     * @note This is a DUMB function: it has no concept of keeping
+     *       character entities that the projected character encoding
+     *       can allow. We could possibly implement a smart version
+     *       but that would require it to also know which Unicode
+     *       codepoints the charset supported (not an easy task).
+     * @note Sort of with cleanUTF8() but it assumes that $str is
+     *       well-formed UTF-8
+     */
+    public static function convertToASCIIDumbLossless($str) {
+        $bytesleft = 0;
+        $result = '';
+        $working = 0;
+        $len = strlen($str);
+        for( $i = 0; $i < $len; $i++ ) {
+            $bytevalue = ord( $str[$i] );
+            if( $bytevalue <= 0x7F ) { //0xxx xxxx
+                $result .= chr( $bytevalue );
+                $bytesleft = 0;
+            } elseif( $bytevalue <= 0xBF ) { //10xx xxxx
+                $working = $working << 6;
+                $working += ($bytevalue & 0x3F);
+                $bytesleft--;
+                if( $bytesleft <= 0 ) {
+                    $result .= "&#" . $working . ";";
+                }
+            } elseif( $bytevalue <= 0xDF ) { //110x xxxx
+                $working = $bytevalue & 0x1F;
+                $bytesleft = 1;
+            } elseif( $bytevalue <= 0xEF ) { //1110 xxxx
+                $working = $bytevalue & 0x0F;
+                $bytesleft = 2;
+            } else { //1111 0xxx
+                $working = $bytevalue & 0x07;
+                $bytesleft = 3;
+            }
+        }
+        return $result;
+    }
+    
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Lexer.php (revision 21)
@@ -0,0 +1,272 @@
+<?php
+
+/**
+ * Forgivingly lexes HTML (SGML-style) markup into tokens.
+ * 
+ * A lexer parses a string of SGML-style markup and converts them into
+ * corresponding tokens.  It doesn't check for well-formedness, although its
+ * internal mechanism may make this automatic (such as the case of
+ * HTMLPurifier_Lexer_DOMLex).  There are several implementations to choose
+ * from.
+ * 
+ * A lexer is HTML-oriented: it might work with XML, but it's not
+ * recommended, as we adhere to a subset of the specification for optimization
+ * reasons. This might change in the future. Also, most tokenizers are not
+ * expected to handle DTDs or PIs.
+ * 
+ * This class should not be directly instantiated, but you may use create() to
+ * retrieve a default copy of the lexer.  Being a supertype, this class
+ * does not actually define any implementation, but offers commonly used
+ * convenience functions for subclasses.
+ * 
+ * @note The unit tests will instantiate this class for testing purposes, as
+ *       many of the utility functions require a class to be instantiated.
+ *       This means that, even though this class is not runnable, it will
+ *       not be declared abstract.
+ * 
+ * @par
+ * 
+ * @note
+ * We use tokens rather than create a DOM representation because DOM would:
+ * 
+ * @par
+ *  -# Require more processing and memory to create,
+ *  -# Is not streamable, and
+ *  -# Has the entire document structure (html and body not needed).
+ * 
+ * @par
+ * However, DOM is helpful in that it makes it easy to move around nodes
+ * without a lot of lookaheads to see when a tag is closed. This is a
+ * limitation of the token system and some workarounds would be nice.
+ */
+class HTMLPurifier_Lexer
+{
+    
+    // -- STATIC ----------------------------------------------------------
+    
+    /**
+     * Retrieves or sets the default Lexer as a Prototype Factory.
+     * 
+     * By default HTMLPurifier_Lexer_DOMLex will be returned. There are
+     * a few exceptions involving special features that only DirectLex
+     * implements.
+     * 
+     * @note The behavior of this class has changed, rather than accepting
+     *       a prototype object, it now accepts a configuration object.
+     *       To specify your own prototype, set %Core.LexerImpl to it.
+     *       This change in behavior de-singletonizes the lexer object.
+     * 
+     * @param $config Instance of HTMLPurifier_Config
+     * @return Concrete lexer.
+     */
+    public static function create($config) {
+        
+        if (!($config instanceof HTMLPurifier_Config)) {
+            $lexer = $config;
+            trigger_error("Passing a prototype to 
+              HTMLPurifier_Lexer::create() is deprecated, please instead
+              use %Core.LexerImpl", E_USER_WARNING);
+        } else {
+            $lexer = $config->get('Core', 'LexerImpl');
+        }
+        
+        if (is_object($lexer)) {
+            return $lexer;
+        }
+        
+        if (is_null($lexer)) { do {
+            // auto-detection algorithm
+            
+            // once PHP DOM implements native line numbers, or we
+            // hack out something using XSLT, remove this stipulation
+            $line_numbers = $config->get('Core', 'MaintainLineNumbers');
+            if (
+                $line_numbers === true ||
+                ($line_numbers === null && $config->get('Core', 'CollectErrors'))
+            ) {
+                $lexer = 'DirectLex';
+                break;
+            }
+            
+            if (class_exists('DOMDocument')) {
+                // check for DOM support, because, surprisingly enough,
+                // it's *not* part of the core!
+                $lexer = 'DOMLex';
+            } else {
+                $lexer = 'DirectLex';
+            }
+            
+        } while(0); } // do..while so we can break
+        
+        // instantiate recognized string names
+        switch ($lexer) {
+            case 'DOMLex':
+                return new HTMLPurifier_Lexer_DOMLex();
+            case 'DirectLex':
+                return new HTMLPurifier_Lexer_DirectLex();
+            case 'PH5P':
+                return new HTMLPurifier_Lexer_PH5P();
+            default:
+                trigger_error("Cannot instantiate unrecognized Lexer type " . htmlspecialchars($lexer), E_USER_ERROR);
+        }
+        
+    }
+    
+    // -- CONVENIENCE MEMBERS ---------------------------------------------
+    
+    public function __construct() {
+        $this->_entity_parser = new HTMLPurifier_EntityParser();
+    }
+    
+    /**
+     * Most common entity to raw value conversion table for special entities.
+     */
+    protected $_special_entity2str =
+            array(
+                    '&quot;' => '"',
+                    '&amp;'  => '&',
+                    '&lt;'   => '<',
+                    '&gt;'   => '>',
+                    '&#39;'  => "'",
+                    '&#039;' => "'",
+                    '&#x27;' => "'"
+            );
+    
+    /**
+     * Parses special entities into the proper characters.
+     * 
+     * This string will translate escaped versions of the special characters
+     * into the correct ones.
+     * 
+     * @warning
+     * You should be able to treat the output of this function as
+     * completely parsed, but that's only because all other entities should
+     * have been handled previously in substituteNonSpecialEntities()
+     * 
+     * @param $string String character data to be parsed.
+     * @returns Parsed character data.
+     */
+    public function parseData($string) {
+        
+        // following functions require at least one character
+        if ($string === '') return '';
+        
+        // subtracts amps that cannot possibly be escaped
+        $num_amp = substr_count($string, '&') - substr_count($string, '& ') -
+            ($string[strlen($string)-1] === '&' ? 1 : 0);
+        
+        if (!$num_amp) return $string; // abort if no entities
+        $num_esc_amp = substr_count($string, '&amp;');
+        $string = strtr($string, $this->_special_entity2str);
+        
+        // code duplication for sake of optimization, see above
+        $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') -
+            ($string[strlen($string)-1] === '&' ? 1 : 0);
+        
+        if ($num_amp_2 <= $num_esc_amp) return $string;
+        
+        // hmm... now we have some uncommon entities. Use the callback.
+        $string = $this->_entity_parser->substituteSpecialEntities($string);
+        return $string;
+    }
+    
+    /**
+     * Lexes an HTML string into tokens.
+     * 
+     * @param $string String HTML.
+     * @return HTMLPurifier_Token array representation of HTML.
+     */
+    public function tokenizeHTML($string, $config, $context) {
+        trigger_error('Call to abstract class', E_USER_ERROR);
+    }
+    
+    /**
+     * Translates CDATA sections into regular sections (through escaping).
+     * 
+     * @param $string HTML string to process.
+     * @returns HTML with CDATA sections escaped.
+     */
+    protected static function escapeCDATA($string) {
+        return preg_replace_callback(
+            '/<!\[CDATA\[(.+?)\]\]>/s',
+            array('HTMLPurifier_Lexer', 'CDATACallback'),
+            $string
+        );
+    }
+    
+    /**
+     * Special CDATA case that is especially convoluted for <script>
+     */
+    protected static function escapeCommentedCDATA($string) {
+        return preg_replace_callback(
+            '#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s',
+            array('HTMLPurifier_Lexer', 'CDATACallback'),
+            $string
+        );
+    }
+    
+    /**
+     * Callback function for escapeCDATA() that does the work.
+     * 
+     * @warning Though this is public in order to let the callback happen,
+     *          calling it directly is not recommended.
+     * @params $matches PCRE matches array, with index 0 the entire match
+     *                  and 1 the inside of the CDATA section.
+     * @returns Escaped internals of the CDATA section.
+     */
+    protected static function CDATACallback($matches) {
+        // not exactly sure why the character set is needed, but whatever
+        return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
+    }
+    
+    /**
+     * Takes a piece of HTML and normalizes it by converting entities, fixing
+     * encoding, extracting bits, and other good stuff.
+     * @todo Consider making protected
+     */
+    public function normalize($html, $config, $context) {
+        
+        // extract body from document if applicable
+        if ($config->get('Core', 'ConvertDocumentToFragment')) {
+            $html = $this->extractBody($html);
+        }
+        
+        // normalize newlines to \n
+        $html = str_replace("\r\n", "\n", $html);
+        $html = str_replace("\r", "\n", $html);
+        
+        if ($config->get('HTML', 'Trusted')) {
+            // escape convoluted CDATA
+            $html = $this->escapeCommentedCDATA($html);
+        }
+        
+        // escape CDATA
+        $html = $this->escapeCDATA($html);
+        
+        // expand entities that aren't the big five
+        $html = $this->_entity_parser->substituteNonSpecialEntities($html);
+        
+        // clean into wellformed UTF-8 string for an SGML context: this has
+        // to be done after entity expansion because the entities sometimes
+        // represent non-SGML characters (horror, horror!)
+        $html = HTMLPurifier_Encoder::cleanUTF8($html);
+        
+        return $html;
+    }
+    
+    /**
+     * Takes a string of HTML (fragment or document) and returns the content
+     * @todo Consider making protected
+     */
+    public function extractBody($html) {
+        $matches = array();
+        $result = preg_match('!<body[^>]*>(.+?)</body>!is', $html, $matches);
+        if ($result) {
+            return $matches[1];
+        } else {
+            return $html;
+        }
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/MakeAbsolute.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/MakeAbsolute.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/MakeAbsolute.php (revision 21)
@@ -0,0 +1,105 @@
+<?php
+
+// does not support network paths
+
+class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
+{
+    public $name = 'MakeAbsolute';
+    protected $base;
+    protected $basePathStack = array();
+    public function prepare($config) {
+        $def = $config->getDefinition('URI');
+        $this->base = $def->base;
+        if (is_null($this->base)) {
+            trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_ERROR);
+            return;
+        }
+        $this->base->fragment = null; // fragment is invalid for base URI
+        $stack = explode('/', $this->base->path);
+        array_pop($stack); // discard last segment
+        $stack = $this->_collapseStack($stack); // do pre-parsing
+        $this->basePathStack = $stack;
+    }
+    public function filter(&$uri, $config, $context) {
+        if (is_null($this->base)) return true; // abort early
+        if (
+            $uri->path === '' && is_null($uri->scheme) &&
+            is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)
+        ) {
+            // reference to current document
+            $uri = clone $this->base;
+            return true;
+        }
+        if (!is_null($uri->scheme)) {
+            // absolute URI already: don't change
+            if (!is_null($uri->host)) return true;
+            $scheme_obj = $uri->getSchemeObj($config, $context);
+            if (!$scheme_obj) {
+                // scheme not recognized
+                return false;
+            }
+            if (!$scheme_obj->hierarchical) {
+                // non-hierarchal URI with explicit scheme, don't change
+                return true;
+            }
+            // special case: had a scheme but always is hierarchical and had no authority
+        }
+        if (!is_null($uri->host)) {
+            // network path, don't bother
+            return true;
+        }
+        if ($uri->path === '') {
+            $uri->path = $this->base->path;
+        }elseif ($uri->path[0] !== '/') {
+            // relative path, needs more complicated processing
+            $stack = explode('/', $uri->path);
+            $new_stack = array_merge($this->basePathStack, $stack);
+            $new_stack = $this->_collapseStack($new_stack);
+            $uri->path = implode('/', $new_stack);
+        }
+        // re-combine
+        $uri->scheme = $this->base->scheme;
+        if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo;
+        if (is_null($uri->host))     $uri->host     = $this->base->host;
+        if (is_null($uri->port))     $uri->port     = $this->base->port;
+        return true;
+    }
+    
+    /**
+     * Resolve dots and double-dots in a path stack
+     */
+    private function _collapseStack($stack) {
+        $result = array();
+        for ($i = 0; isset($stack[$i]); $i++) {
+            $is_folder = false;
+            // absorb an internally duplicated slash
+            if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue;
+            if ($stack[$i] == '..') {
+                if (!empty($result)) {
+                    $segment = array_pop($result);
+                    if ($segment === '' && empty($result)) {
+                        // error case: attempted to back out too far:
+                        // restore the leading slash
+                        $result[] = '';
+                    } elseif ($segment === '..') {
+                        $result[] = '..'; // cannot remove .. with ..
+                    }
+                } else {
+                    // relative path, preserve the double-dots
+                    $result[] = '..';
+                }
+                $is_folder = true;
+                continue;
+            }
+            if ($stack[$i] == '.') {
+                // silently absorb
+                $is_folder = true;
+                continue;
+            }
+            $result[] = $stack[$i];
+        }
+        if ($is_folder) $result[] = '';
+        return $result;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/DisableExternalResources.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/DisableExternalResources.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/DisableExternalResources.php (revision 21)
@@ -0,0 +1,11 @@
+<?php
+
+class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal
+{
+    public $name = 'DisableExternalResources';
+    public function filter(&$uri, $config, $context) {
+        if (!$context->get('EmbeddedURI', true)) return true;
+        return parent::filter($uri, $config, $context);
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/DisableExternal.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/DisableExternal.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/DisableExternal.php (revision 21)
@@ -0,0 +1,22 @@
+<?php
+
+class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter
+{
+    public $name = 'DisableExternal';
+    protected $ourHostParts = false;
+    public function prepare($config) {
+        $our_host = $config->get('URI', 'Host');
+        if ($our_host !== null) $this->ourHostParts = array_reverse(explode('.', $our_host));
+    }
+    public function filter(&$uri, $config, $context) {
+        if (is_null($uri->host)) return true;
+        if ($this->ourHostParts === false) return false;
+        $host_parts = array_reverse(explode('.', $uri->host));
+        foreach ($this->ourHostParts as $i => $x) {
+            if (!isset($host_parts[$i])) return false;
+            if ($host_parts[$i] != $this->ourHostParts[$i]) return false;
+        }
+        return true;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/HostBlacklist.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/HostBlacklist.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter/HostBlacklist.php (revision 21)
@@ -0,0 +1,18 @@
+<?php
+
+class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
+{
+    public $name = 'HostBlacklist';
+    protected $blacklist = array();
+    public function prepare($config) {
+        $this->blacklist = $config->get('URI', 'HostBlacklist');
+    }
+    public function filter(&$uri, $config, $context) {
+        foreach($this->blacklist as $blacklisted_host_fragment) {
+            if (strpos($uri->host, $blacklisted_host_fragment) !== false) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Token.php (revision 21)
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Abstract base token class that all others inherit from.
+ */
+class HTMLPurifier_Token {
+    public $type; /**< Type of node to bypass <tt>is_a()</tt>. */
+    public $line; /**< Line number node was on in source document. Null if unknown. */
+    
+    /**
+     * Lookup array of processing that this token is exempt from.
+     * Currently, valid values are "ValidateAttributes" and
+     * "MakeWellFormed_TagClosedError"
+     */
+    public $armor = array();
+    
+    public function __get($n) {
+      if ($n === 'type') {
+        trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
+        switch (get_class($this)) {
+          case 'HTMLPurifier_Token_Start': return 'start';
+          case 'HTMLPurifier_Token_Empty': return 'empty';
+          case 'HTMLPurifier_Token_End': return 'end';
+          case 'HTMLPurifier_Token_Text': return 'text';
+          case 'HTMLPurifier_Token_Comment': return 'comment';
+          default: return null;
+        }
+      }
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Printer.php (revision 21)
@@ -0,0 +1,170 @@
+<?php
+
+// OUT OF DATE, NEEDS UPDATING!
+// USE XMLWRITER!
+
+class HTMLPurifier_Printer
+{
+    
+    /**
+     * Instance of HTMLPurifier_Generator for HTML generation convenience funcs
+     */
+    protected $generator;
+    
+    /**
+     * Instance of HTMLPurifier_Config, for easy access
+     */
+    protected $config;
+    
+    /**
+     * Initialize $generator.
+     */
+    public function __construct() {
+        $this->generator = new HTMLPurifier_Generator();
+    }
+    
+    /**
+     * Give generator necessary configuration if possible
+     */
+    public function prepareGenerator($config) {
+        // hack for smoketests/configForm.php
+        $all = $config->getAll();
+        if (empty($all['HTML'])) return;
+        $context = new HTMLPurifier_Context();
+        $this->generator->generateFromTokens(array(), $config, $context);
+    }
+    
+    /**
+     * Main function that renders object or aspect of that object
+     * @note Parameters vary depending on printer
+     */
+    // function render() {}
+    
+    /**
+     * Returns a start tag
+     * @param $tag Tag name
+     * @param $attr Attribute array
+     */
+    protected function start($tag, $attr = array()) {
+        return $this->generator->generateFromToken(
+                    new HTMLPurifier_Token_Start($tag, $attr ? $attr : array())
+               );
+    }
+    
+    /**
+     * Returns an end teg
+     * @param $tag Tag name
+     */
+    protected function end($tag) {
+        return $this->generator->generateFromToken(
+                    new HTMLPurifier_Token_End($tag)
+               );
+    }
+    
+    /**
+     * Prints a complete element with content inside
+     * @param $tag Tag name
+     * @param $contents Element contents
+     * @param $attr Tag attributes
+     * @param $escape Bool whether or not to escape contents
+     */
+    protected function element($tag, $contents, $attr = array(), $escape = true) {
+        return $this->start($tag, $attr) .
+               ($escape ? $this->escape($contents) : $contents) .
+               $this->end($tag);
+    }
+    
+    protected function elementEmpty($tag, $attr = array()) {
+        return $this->generator->generateFromToken(
+            new HTMLPurifier_Token_Empty($tag, $attr)
+        );
+    }
+    
+    protected function text($text) {
+        return $this->generator->generateFromToken(
+            new HTMLPurifier_Token_Text($text)
+        );
+    }
+    
+    /**
+     * Prints a simple key/value row in a table.
+     * @param $name Key
+     * @param $value Value
+     */
+    protected function row($name, $value) {
+        if (is_bool($value)) $value = $value ? 'On' : 'Off';
+        return
+            $this->start('tr') . "\n" .
+                $this->element('th', $name) . "\n" .
+                $this->element('td', $value) . "\n" .
+            $this->end('tr')
+        ;
+    }
+    
+    /**
+     * Escapes a string for HTML output.
+     * @param $string String to escape
+     */
+    protected function escape($string) {
+        $string = HTMLPurifier_Encoder::cleanUTF8($string);
+        $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
+        return $string;
+    }
+    
+    /**
+     * Takes a list of strings and turns them into a single list
+     * @param $array List of strings
+     * @param $polite Bool whether or not to add an end before the last
+     */
+    protected function listify($array, $polite = false) {
+        if (empty($array)) return 'None';
+        $ret = '';
+        $i = count($array);
+        foreach ($array as $value) {
+            $i--;
+            $ret .= $value;
+            if ($i > 0 && !($polite && $i == 1)) $ret .= ', ';
+            if ($polite && $i == 1) $ret .= 'and ';
+        }
+        return $ret;
+    }
+    
+    /**
+     * Retrieves the class of an object without prefixes, as well as metadata
+     * @param $obj Object to determine class of
+     * @param $prefix Further prefix to remove
+     */
+    protected function getClass($obj, $sec_prefix = '') {
+        static $five = null;
+        if ($five === null) $five = version_compare(PHP_VERSION, '5', '>=');
+        $prefix = 'HTMLPurifier_' . $sec_prefix;
+        if (!$five) $prefix = strtolower($prefix);
+        $class = str_replace($prefix, '', get_class($obj));
+        $lclass = strtolower($class);
+        $class .= '(';
+        switch ($lclass) {
+            case 'enum':
+                $values = array();
+                foreach ($obj->valid_values as $value => $bool) {
+                    $values[] = $value;
+                }
+                $class .= implode(', ', $values);
+                break;
+            case 'css_composite':
+                $values = array();
+                foreach ($obj->defs as $def) {
+                    $values[] = $this->getClass($def, $sec_prefix);
+                }
+                $class .= implode(', ', $values);
+                break;
+            case 'css_multiple':
+                $class .= $this->getClass($obj->single, $sec_prefix) . ', ';
+                $class .= $obj->max;
+                break;
+        }
+        $class .= ')';
+        return $class;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParserException.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParserException.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParserException.php (revision 21)
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * Exception type for HTMLPurifier_VarParser
+ */
+class HTMLPurifier_VarParserException extends HTMLPurifier_Exception
+{
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIScheme.php (revision 21)
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * Validator for the components of a URI for a specific scheme
+ */
+class HTMLPurifier_URIScheme
+{
+    
+    /**
+     * Scheme's default port (integer)
+     */
+    public $default_port = null;
+    
+    /**
+     * Whether or not URIs of this schem are locatable by a browser
+     * http and ftp are accessible, while mailto and news are not.
+     */
+    public $browsable = false;
+    
+    /**
+     * Whether or not the URI always uses <hier_part>, resolves edge cases
+     * with making relative URIs absolute
+     */
+    public $hierarchical = false;
+    
+    /**
+     * Validates the components of a URI
+     * @note This implementation should be called by children if they define
+     *       a default port, as it does port processing.
+     * @param $uri Instance of HTMLPurifier_URI
+     * @param $config HTMLPurifier_Config object
+     * @param $context HTMLPurifier_Context object
+     * @return Bool success or failure
+     */
+    public function validate(&$uri, $config, $context) {
+        if ($this->default_port == $uri->port) $uri->port = null;
+        return true;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigDef.php (revision 21)
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * Base class for configuration entity
+ */
+abstract class HTMLPurifier_ConfigDef {
+    public $class = false;
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/StringHashParser.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/StringHashParser.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/StringHashParser.php (revision 21)
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * Parses string hash files. File format is as such:
+ * 
+ *      DefaultKeyValue
+ *      KEY: Value
+ *      KEY2: Value2
+ *      --MULTILINE-KEY--
+ *      Multiline
+ *      value.
+ *
+ * Which would output something similar to:
+ *
+ *      array(
+ *          'ID' => 'DefaultKeyValue',
+ *          'KEY' => 'Value',
+ *          'KEY2' => 'Value2',
+ *          'MULTILINE-KEY' => "Multiline\nvalue.\n",
+ *      )
+ *
+ * We use this as an easy to use file-format for configuration schema
+ * files, but the class itself is usage agnostic.
+ *
+ * You can use ---- to forcibly terminate parsing of a single string-hash;
+ * this marker is used in multi string-hashes to delimit boundaries.
+ */
+class HTMLPurifier_StringHashParser
+{
+    
+    public $default = 'ID';
+    
+    /**
+     * Parses a file that contains a single string-hash.
+     */
+    public function parseFile($file) {
+        if (!file_exists($file)) return false;
+        $fh = fopen($file, 'r');
+        if (!$fh) return false;
+        $ret = $this->parseHandle($fh);
+        fclose($fh);
+        return $ret;
+    }
+    
+    /**
+     * Parses a file that contains multiple string-hashes delimited by '----'
+     */
+    public function parseMultiFile($file) {
+        if (!file_exists($file)) return false;
+        $ret = array();
+        $fh = fopen($file, 'r');
+        if (!$fh) return false;
+        while (!feof($fh)) {
+            $ret[] = $this->parseHandle($fh);
+        }
+        fclose($fh);
+        return $ret;
+    }
+    
+    /**
+     * Internal parser that acepts a file handle.
+     * @note While it's possible to simulate in-memory parsing by using
+     *       custom stream wrappers, if such a use-case arises we should
+     *       factor out the file handle into its own class.
+     * @param $fh File handle with pointer at start of valid string-hash
+     *            block.
+     */
+    protected function parseHandle($fh) {
+        $state   = false;
+        $single  = false;
+        $ret     = array();
+        do {
+            $line = fgets($fh);
+            if ($line === false) break;
+            $line = rtrim($line, "\n\r");
+            if (!$state && $line === '') continue;
+            if ($line === '----') break;
+            if (strncmp('--', $line, 2) === 0) {
+                // Multiline declaration
+                $state = trim($line, '- ');
+                continue;
+            } elseif (!$state) {
+                $single = true;
+                if (strpos($line, ':') !== false) {
+                    // Single-line declaration
+                    list($state, $line) = explode(': ', $line, 2);
+                } else {
+                    // Use default declaration
+                    $state  = $this->default;
+                }
+            }
+            if ($single) {
+                $ret[$state] = $line;
+                $single = false;
+                $state  = false;
+            } else {
+                if (!isset($ret[$state])) $ret[$state] = '';
+                $ret[$state] .= "$line\n";
+            }
+        } while (!feof($fh));
+        return $ret;
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter/ExtractStyleBlocks.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter/ExtractStyleBlocks.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter/ExtractStyleBlocks.php (revision 21)
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * This filter extracts <style> blocks from input HTML, cleans them up
+ * using CSSTidy, and then places them in $purifier->context->get('StyleBlocks')
+ * so they can be used elsewhere in the document.
+ * 
+ * @note
+ *      See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for
+ *      sample usage.
+ * 
+ * @note
+ *      This filter can also be used on stylesheets not included in the
+ *      document--something purists would probably prefer. Just directly
+ *      call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS()
+ */
+class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
+{
+    
+    public $name = 'ExtractStyleBlocks';
+    private $_styleMatches = array();
+    private $_tidy;
+    
+    public function __construct() {
+        $this->_tidy = new csstidy();
+    }
+    
+    /**
+     * Save the contents of CSS blocks to style matches
+     * @param $matches preg_replace style $matches array
+     */
+    protected function styleCallback($matches) {
+        $this->_styleMatches[] = $matches[1];
+    }
+    
+    /**
+     * Removes inline <style> tags from HTML, saves them for later use
+     * @todo Extend to indicate non-text/css style blocks
+     */
+    public function preFilter($html, $config, $context) {
+        $tidy = $config->get('FilterParam', 'ExtractStyleBlocksTidyImpl');
+        if ($tidy !== null) $this->_tidy = $tidy;
+        $html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
+        $style_blocks = $this->_styleMatches;
+        $this->_styleMatches = array(); // reset
+        $context->register('StyleBlocks', $style_blocks); // $context must not be reused
+        if ($this->_tidy) {
+            foreach ($style_blocks as &$style) {
+                $style = $this->cleanCSS($style, $config, $context);
+            }
+        }
+        return $html;
+    }
+    
+    /**
+     * Takes CSS (the stuff found in <style>) and cleans it.
+     * @warning Requires CSSTidy <http://csstidy.sourceforge.net/>
+     * @param $css     CSS styling to clean
+     * @param $config  Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     * @return Cleaned CSS
+     */
+    public function cleanCSS($css, $config, $context) {
+        // prepare scope
+        $scope = $config->get('FilterParam', 'ExtractStyleBlocksScope');
+        if ($scope !== null) {
+            $scopes = array_map('trim', explode(',', $scope));
+        } else {
+            $scopes = array();
+        }
+        // remove comments from CSS
+        $css = trim($css);
+        if (strncmp('<!--', $css, 4) === 0) {
+            $css = substr($css, 4);
+        }
+        if (strlen($css) > 3 && substr($css, -3) == '-->') {
+            $css = substr($css, 0, -3);
+        }
+        $css = trim($css);
+        $this->_tidy->parse($css);
+        $css_definition = $config->getDefinition('CSS');
+        foreach ($this->_tidy->css as $k => $decls) {
+            // $decls are all CSS declarations inside an @ selector
+            $new_decls = array();
+            foreach ($decls as $selector => $style) {
+                $selector = trim($selector);
+                if ($selector === '') continue; // should not happen
+                if ($selector[0] === '+') {
+                    if ($selector !== '' && $selector[0] === '+') continue;
+                }
+                if (!empty($scopes)) {
+                    $new_selector = array(); // because multiple ones are possible
+                    $selectors = array_map('trim', explode(',', $selector));
+                    foreach ($scopes as $s1) {
+                        foreach ($selectors as $s2) {
+                            $new_selector[] = "$s1 $s2";
+                        }
+                    }
+                    $selector = implode(', ', $new_selector); // now it's a string
+                }
+                foreach ($style as $name => $value) {
+                    if (!isset($css_definition->info[$name])) {
+                        unset($style[$name]);
+                        continue;
+                    }
+                    $def = $css_definition->info[$name];
+                    $ret = $def->validate($value, $config, $context);
+                    if ($ret === false) unset($style[$name]);
+                    else $style[$name] = $ret;
+                }
+                $new_decls[$selector] = $style;
+            }
+            $this->_tidy->css[$k] = $new_decls;
+        }
+        // remove stuff that shouldn't be used, could be reenabled
+        // after security risks are analyzed
+        $this->_tidy->import = array();
+        $this->_tidy->charset = null;
+        $this->_tidy->namespace = null;
+        $css = $this->_tidy->print->plain();
+        // we are going to escape any special characters <>& to ensure
+        // that no funny business occurs (i.e. </style> in a font-family prop).
+        if ($config->get('FilterParam', 'ExtractStyleBlocksEscaping')) {
+            $css = str_replace(
+                array('<',    '>',    '&'),
+                array('\3C ', '\3E ', '\26 '),
+                $css
+            );
+        }
+        return $css;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter/YouTube.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter/YouTube.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter/YouTube.php (revision 21)
@@ -0,0 +1,31 @@
+<?php
+
+class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
+{
+    
+    public $name = 'YouTube';
+    
+    public function preFilter($html, $config, $context) {
+        $pre_regex = '#<object[^>]+>.+?'.
+            'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?</object>#s';
+        $pre_replace = '<span class="youtube-embed">\1</span>';
+        return preg_replace($pre_regex, $pre_replace, $html);
+    }
+    
+    public function postFilter($html, $config, $context) {
+        $post_regex = '#<span class="youtube-embed">([A-Za-z0-9\-_]+)</span>#';
+        $post_replace = '<object width="425" height="350" '.
+            'data="http://www.youtube.com/v/\1">'.
+            '<param name="movie" value="http://www.youtube.com/v/\1"></param>'.
+            '<param name="wmode" value="transparent"></param>'.
+            '<!--[if IE]>'.
+            '<embed src="http://www.youtube.com/v/\1"'.
+            'type="application/x-shockwave-flash"'.
+            'wmode="transparent" width="425" height="350" />'.
+            '<![endif]-->'.
+            '</object>';
+        return preg_replace($post_regex, $post_replace, $html);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DoctypeRegistry.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DoctypeRegistry.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DoctypeRegistry.php (revision 21)
@@ -0,0 +1,102 @@
+<?php
+
+class HTMLPurifier_DoctypeRegistry
+{
+    
+    /**
+     * Hash of doctype names to doctype objects
+     */
+    protected $doctypes;
+    
+    /**
+     * Lookup table of aliases to real doctype names
+     */
+    protected $aliases;
+    
+    /**
+     * Registers a doctype to the registry
+     * @note Accepts a fully-formed doctype object, or the
+     *       parameters for constructing a doctype object
+     * @param $doctype Name of doctype or literal doctype object
+     * @param $modules Modules doctype will load
+     * @param $modules_for_modes Modules doctype will load for certain modes
+     * @param $aliases Alias names for doctype
+     * @return Editable registered doctype
+     */
+    public function register($doctype, $xml = true, $modules = array(),
+        $tidy_modules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null
+    ) {
+        if (!is_array($modules)) $modules = array($modules);
+        if (!is_array($tidy_modules)) $tidy_modules = array($tidy_modules);
+        if (!is_array($aliases)) $aliases = array($aliases);
+        if (!is_object($doctype)) {
+            $doctype = new HTMLPurifier_Doctype(
+                $doctype, $xml, $modules, $tidy_modules, $aliases, $dtd_public, $dtd_system
+            );
+        }
+        $this->doctypes[$doctype->name] = $doctype;
+        $name = $doctype->name;
+        // hookup aliases
+        foreach ($doctype->aliases as $alias) {
+            if (isset($this->doctypes[$alias])) continue;
+            $this->aliases[$alias] = $name;
+        }
+        // remove old aliases
+        if (isset($this->aliases[$name])) unset($this->aliases[$name]);
+        return $doctype;
+    }
+    
+    /**
+     * Retrieves reference to a doctype of a certain name
+     * @note This function resolves aliases
+     * @note When possible, use the more fully-featured make()
+     * @param $doctype Name of doctype
+     * @return Editable doctype object
+     */
+    public function get($doctype) {
+        if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype];
+        if (!isset($this->doctypes[$doctype])) {
+            trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR);
+            $anon = new HTMLPurifier_Doctype($doctype);
+            return $anon;
+        }
+        return $this->doctypes[$doctype];
+    }
+    
+    /**
+     * Creates a doctype based on a configuration object,
+     * will perform initialization on the doctype
+     * @note Use this function to get a copy of doctype that config
+     *       can hold on to (this is necessary in order to tell
+     *       Generator whether or not the current document is XML
+     *       based or not).
+     */
+    public function make($config) {
+        return clone $this->get($this->getDoctypeFromConfig($config));
+    }
+    
+    /**
+     * Retrieves the doctype from the configuration object
+     */
+    public function getDoctypeFromConfig($config) {
+        // recommended test
+        $doctype = $config->get('HTML', 'Doctype');
+        if (!empty($doctype)) return $doctype;
+        $doctype = $config->get('HTML', 'CustomDoctype');
+        if (!empty($doctype)) return $doctype;
+        // backwards-compatibility
+        if ($config->get('HTML', 'XHTML')) {
+            $doctype = 'XHTML 1.0';
+        } else {
+            $doctype = 'HTML 4.01';
+        }
+        if ($config->get('HTML', 'Strict')) {
+            $doctype .= ' Strict';
+        } else {
+            $doctype .= ' Transitional';
+        }
+        return $doctype;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Host.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Host.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Host.php (revision 21)
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
+ */
+class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Instance of HTMLPurifier_AttrDef_URI_IPv4 sub-validator
+     */
+    protected $ipv4;
+    
+    /**
+     * Instance of HTMLPurifier_AttrDef_URI_IPv6 sub-validator
+     */
+    protected $ipv6;
+    
+    public function __construct() {
+        $this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
+        $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
+    }
+    
+    public function validate($string, $config, $context) {
+        $length = strlen($string);
+        if ($string === '') return '';
+        if ($length > 1 && $string[0] === '[' && $string[$length-1] === ']') {
+            //IPv6
+            $ip = substr($string, 1, $length - 2);
+            $valid = $this->ipv6->validate($ip, $config, $context);
+            if ($valid === false) return false;
+            return '['. $valid . ']';
+        }
+        
+        // need to do checks on unusual encodings too
+        $ipv4 = $this->ipv4->validate($string, $config, $context);
+        if ($ipv4 !== false) return $ipv4;
+        
+        // A regular domain name.
+        
+        // This breaks I18N domain names, but we don't have proper IRI support,
+        // so force users to insert Punycode. If there's complaining we'll 
+        // try to fix things into an international friendly form.
+        
+        // The productions describing this are:
+        $a   = '[a-z]';     // alpha
+        $an  = '[a-z0-9]';  // alphanum
+        $and = '[a-z0-9-]'; // alphanum | "-"
+        // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+        $domainlabel   = "$an($and*$an)?";
+        // toplabel    = alpha | alpha *( alphanum | "-" ) alphanum
+        $toplabel      = "$a($and*$an)?";
+        // hostname    = *( domainlabel "." ) toplabel [ "." ]
+        $match = preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string);
+        if (!$match) return false;
+        
+        return $string;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php (revision 21)
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * Primitive email validation class based on the regexp found at 
+ * http://www.regular-expressions.info/email.html
+ */
+class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email
+{
+    
+    public function validate($string, $config, $context) {
+        // no support for named mailboxes i.e. "Bob <bob@example.com>"
+        // that needs more percent encoding to be done
+        if ($string == '') return false;
+        $string = trim($string);
+        $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
+        return $result ? $string : false;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/IPv4.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/IPv4.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/IPv4.php (revision 21)
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Validates an IPv4 address
+ * @author Feyd @ forums.devnetwork.net (public domain)
+ */
+class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * IPv4 regex, protected so that IPv6 can reuse it
+     */
+    protected $ip4;
+    
+    public function validate($aIP, $config, $context) {
+        
+        if (!$this->ip4) $this->_loadRegex();
+        
+        if (preg_match('#^' . $this->ip4 . '$#s', $aIP))
+        {
+                return $aIP;
+        }
+        
+        return false;
+        
+    }
+    
+    /**
+     * Lazy load function to prevent regex from being stuffed in
+     * cache.
+     */
+    protected function _loadRegex() {
+        $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255
+        $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})";
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/IPv6.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/IPv6.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/IPv6.php (revision 21)
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * Validates an IPv6 address.
+ * @author Feyd @ forums.devnetwork.net (public domain)
+ * @note This function requires brackets to have been removed from address
+ *       in URI.
+ */
+class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4
+{
+    
+    public function validate($aIP, $config, $context) {
+        
+        if (!$this->ip4) $this->_loadRegex();
+        
+        $original = $aIP;
+        
+        $hex = '[0-9a-fA-F]';
+        $blk = '(?:' . $hex . '{1,4})';
+        $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))';   // /0 - /128
+        
+        //      prefix check
+        if (strpos($aIP, '/') !== false)
+        {
+                if (preg_match('#' . $pre . '$#s', $aIP, $find))
+                {
+                        $aIP = substr($aIP, 0, 0-strlen($find[0]));
+                        unset($find);
+                }
+                else
+                {
+                        return false;
+                }
+        }
+        
+        //      IPv4-compatiblity check       
+        if (preg_match('#(?<=:'.')' . $this->ip4 . '$#s', $aIP, $find))
+        {
+                $aIP = substr($aIP, 0, 0-strlen($find[0]));
+                $ip = explode('.', $find[0]);
+                $ip = array_map('dechex', $ip);
+                $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3];
+                unset($find, $ip);
+        }
+        
+        //      compression check
+        $aIP = explode('::', $aIP);
+        $c = count($aIP);
+        if ($c > 2)
+        {
+                return false;
+        }
+        elseif ($c == 2)
+        {
+                list($first, $second) = $aIP;
+                $first = explode(':', $first);
+                $second = explode(':', $second);
+               
+                if (count($first) + count($second) > 8)
+                {
+                        return false;
+                }
+               
+                while(count($first) < 8)
+                {
+                        array_push($first, '0');
+                }
+
+                array_splice($first, 8 - count($second), 8, $second);
+                $aIP = $first;
+                unset($first,$second);
+        }
+        else
+        {
+                $aIP = explode(':', $aIP[0]);
+        }
+        $c = count($aIP);
+        
+        if ($c != 8)
+        {
+                return false;
+        }
+       
+        //      All the pieces should be 16-bit hex strings. Are they?
+        foreach ($aIP as $piece)
+        {
+                if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece)))
+                {
+                        return false;
+                }
+        }
+        
+        return $original;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Email.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Email.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI/Email.php (revision 21)
@@ -0,0 +1,15 @@
+<?php
+
+abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Unpacks a mailbox into its display-name and address
+     */
+    function unpack($string) {
+        // needs to be implemented
+    }
+    
+}
+
+// sub-implementations
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Integer.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Integer.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Integer.php (revision 21)
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Validates an integer.
+ * @note While this class was modeled off the CSS definition, no currently
+ *       allowed CSS uses this type.  The properties that do are: widows,
+ *       orphans, z-index, counter-increment, counter-reset.  Some of the
+ *       HTML attributes, however, find use for a non-negative version of this.
+ */
+class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Bool indicating whether or not negative values are allowed
+     */
+    protected $negative = true;
+    
+    /**
+     * Bool indicating whether or not zero is allowed
+     */
+    protected $zero = true;
+    
+    /**
+     * Bool indicating whether or not positive values are allowed
+     */
+    protected $positive = true;
+    
+    /**
+     * @param $negative Bool indicating whether or not negative values are allowed
+     * @param $zero Bool indicating whether or not zero is allowed
+     * @param $positive Bool indicating whether or not positive values are allowed
+     */
+    public function __construct(
+        $negative = true, $zero = true, $positive = true
+    ) {
+        $this->negative = $negative;
+        $this->zero     = $zero;
+        $this->positive = $positive;
+    }
+    
+    public function validate($integer, $config, $context) {
+        
+        $integer = $this->parseCDATA($integer);
+        if ($integer === '') return false;
+        
+        // we could possibly simply typecast it to integer, but there are
+        // certain fringe cases that must not return an integer.
+        
+        // clip leading sign
+        if ( $this->negative && $integer[0] === '-' ) {
+            $digits = substr($integer, 1);
+            if ($digits === '0') $integer = '0'; // rm minus sign for zero
+        } elseif( $this->positive && $integer[0] === '+' ) {
+            $digits = $integer = substr($integer, 1); // rm unnecessary plus
+        } else {
+            $digits = $integer;
+        }
+        
+        // test if it's numeric
+        if (!ctype_digit($digits)) return false;
+        
+        // perform scope tests
+        if (!$this->zero     && $integer == 0) return false;
+        if (!$this->positive && $integer > 0) return false;
+        if (!$this->negative && $integer < 0) return false;
+        
+        return $integer;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Bool.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Bool.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Bool.php (revision 21)
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * Validates a boolean attribute
+ */
+class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
+{
+    
+    protected $name;
+    public $minimized = true;
+    
+    public function __construct($name = false) {$this->name = $name;}
+    
+    public function validate($string, $config, $context) {
+        if (empty($string)) return false;
+        return $this->name;
+    }
+    
+    /**
+     * @param $string Name of attribute
+     */
+    public function make($string) {
+        return new HTMLPurifier_AttrDef_HTML_Bool($string);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/ID.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/ID.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/ID.php (revision 21)
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * Validates the HTML attribute ID.
+ * @warning Even though this is the id processor, it
+ *          will ignore the directive Attr:IDBlacklist, since it will only
+ *          go according to the ID accumulator. Since the accumulator is
+ *          automatically generated, it will have already absorbed the
+ *          blacklist. If you're hacking around, make sure you use load()!
+ */
+
+class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
+{
+    
+    // ref functionality disabled, since we also have to verify
+    // whether or not the ID it refers to exists
+    
+    public function validate($id, $config, $context) {
+        
+        if (!$config->get('Attr', 'EnableID')) return false;
+        
+        $id = trim($id); // trim it first
+        
+        if ($id === '') return false;
+        
+        $prefix = $config->get('Attr', 'IDPrefix');
+        if ($prefix !== '') {
+            $prefix .= $config->get('Attr', 'IDPrefixLocal');
+            // prevent re-appending the prefix
+            if (strpos($id, $prefix) !== 0) $id = $prefix . $id;
+        } elseif ($config->get('Attr', 'IDPrefixLocal') !== '') {
+            trigger_error('%Attr.IDPrefixLocal cannot be used unless '.
+                '%Attr.IDPrefix is set', E_USER_WARNING);
+        }
+        
+        //if (!$this->ref) {
+            $id_accumulator =& $context->get('IDAccumulator');
+            if (isset($id_accumulator->ids[$id])) return false;
+        //}
+        
+        // we purposely avoid using regex, hopefully this is faster
+        
+        if (ctype_alpha($id)) {
+            $result = true;
+        } else {
+            if (!ctype_alpha(@$id[0])) return false;
+            $trim = trim( // primitive style of regexps, I suppose
+                $id,
+                'A..Za..z0..9:-._'
+              );
+            $result = ($trim === '');
+        }
+        
+        $regexp = $config->get('Attr', 'IDBlacklistRegexp');
+        if ($regexp && preg_match($regexp, $id)) {
+            return false;
+        }
+        
+        if (/*!$this->ref && */$result) $id_accumulator->add($id);
+        
+        // if no change was made to the ID, return the result
+        // else, return the new id if stripping whitespace made it
+        //     valid, or return false.
+        return $result ? $id : false;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/MultiLength.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/MultiLength.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/MultiLength.php (revision 21)
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Validates a MultiLength as defined by the HTML spec.
+ * 
+ * A multilength is either a integer (pixel count), a percentage, or
+ * a relative number.
+ */
+class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length
+{
+    
+    public function validate($string, $config, $context) {
+        
+        $string = trim($string);
+        if ($string === '') return false;
+        
+        $parent_result = parent::validate($string, $config, $context);
+        if ($parent_result !== false) return $parent_result;
+        
+        $length = strlen($string);
+        $last_char = $string[$length - 1];
+        
+        if ($last_char !== '*') return false;
+        
+        $int = substr($string, 0, $length - 1);
+        
+        if ($int == '') return '*';
+        if (!is_numeric($int)) return false;
+        
+        $int = (int) $int;
+        
+        if ($int < 0) return false;
+        if ($int == 0) return '0';
+        if ($int == 1) return '*';
+        return ((string) $int) . '*';
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Color.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Color.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Color.php (revision 21)
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Validates a color according to the HTML spec.
+ */
+class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($string, $config, $context) {
+        
+        static $colors = null;
+        if ($colors === null) $colors = $config->get('Core', 'ColorKeywords');
+        
+        $string = trim($string);
+        
+        if (empty($string)) return false;
+        if (isset($colors[$string])) return $colors[$string];
+        if ($string[0] === '#') $hex = substr($string, 1);
+        else $hex = $string;
+        
+        $length = strlen($hex);
+        if ($length !== 3 && $length !== 6) return false;
+        if (!ctype_xdigit($hex)) return false;
+        if ($length === 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
+        
+        return "#$hex";
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Nmtokens.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Nmtokens.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Nmtokens.php (revision 21)
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Validates contents based on NMTOKENS attribute type.
+ * @note The only current use for this is the class attribute in HTML
+ * @note Could have some functionality factored out into Nmtoken class
+ * @warning We cannot assume this class will be used only for 'class'
+ *          attributes. Not sure how to hook in magic behavior, then.
+ */
+class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($string, $config, $context) {
+        
+        $string = trim($string);
+        
+        // early abort: '' and '0' (strings that convert to false) are invalid
+        if (!$string) return false;
+        
+        // OPTIMIZABLE!
+        // do the preg_match, capture all subpatterns for reformulation
+        
+        // we don't support U+00A1 and up codepoints or
+        // escaping because I don't know how to do that with regexps
+        // and plus it would complicate optimization efforts (you never
+        // see that anyway).
+        $matches = array();
+        $pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start
+                   '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'.
+                   '(?:(?=\s)|\z)/'; // look ahead for space or string end
+        preg_match_all($pattern, $string, $matches);
+        
+        if (empty($matches[1])) return false;
+        
+        // reconstruct string
+        $new_string = '';
+        foreach ($matches[1] as $token) {
+            $new_string .= $token . ' ';
+        }
+        $new_string = rtrim($new_string);
+        
+        return $new_string;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/FrameTarget.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/FrameTarget.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/FrameTarget.php (revision 21)
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * Special-case enum attribute definition that lazy loads allowed frame targets
+ */
+class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum
+{
+    
+    public $valid_values = false; // uninitialized value
+    protected $case_sensitive = false;
+    
+    public function __construct() {}
+    
+    public function validate($string, $config, $context) {
+        if ($this->valid_values === false) $this->valid_values = $config->get('Attr', 'AllowedFrameTargets');
+        return parent::validate($string, $config, $context);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Length.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Length.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Length.php (revision 21)
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Validates the HTML type length (not to be confused with CSS's length).
+ * 
+ * This accepts integer pixels or percentages as lengths for certain
+ * HTML attributes.
+ */
+
+class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels
+{
+    
+    public function validate($string, $config, $context) {
+        
+        $string = trim($string);
+        if ($string === '') return false;
+        
+        $parent_result = parent::validate($string, $config, $context);
+        if ($parent_result !== false) return $parent_result;
+        
+        $length = strlen($string);
+        $last_char = $string[$length - 1];
+        
+        if ($last_char !== '%') return false;
+        
+        $points = substr($string, 0, $length - 1);
+        
+        if (!is_numeric($points)) return false;
+        
+        $points = (int) $points;
+        
+        if ($points < 0) return '0%';
+        if ($points > 100) return '100%';
+        
+        return ((string) $points) . '%';
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/LinkTypes.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/LinkTypes.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/LinkTypes.php (revision 21)
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Validates a rel/rev link attribute against a directive of allowed values
+ * @note We cannot use Enum because link types allow multiple
+ *       values.
+ * @note Assumes link types are ASCII text
+ */
+class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef
+{
+    
+    /** Name config attribute to pull. */
+    protected $name;
+    
+    public function __construct($name) {
+        $configLookup = array(
+            'rel' => 'AllowedRel',
+            'rev' => 'AllowedRev'
+        );
+        if (!isset($configLookup[$name])) {
+            trigger_error('Unrecognized attribute name for link '.
+                'relationship.', E_USER_ERROR);
+            return;
+        }
+        $this->name = $configLookup[$name];
+    }
+    
+    public function validate($string, $config, $context) {
+        
+        $allowed = $config->get('Attr', $this->name);
+        if (empty($allowed)) return false;
+        
+        $string = $this->parseCDATA($string);
+        $parts = explode(' ', $string);
+        
+        // lookup to prevent duplicates
+        $ret_lookup = array();
+        foreach ($parts as $part) {
+            $part = strtolower(trim($part));
+            if (!isset($allowed[$part])) continue;
+            $ret_lookup[$part] = true;
+        }
+        
+        if (empty($ret_lookup)) return false;
+        
+        $ret_array = array();
+        foreach ($ret_lookup as $part => $bool) $ret_array[] = $part;
+        $string = implode(' ', $ret_array);
+        
+        return $string;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Pixels.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Pixels.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/HTML/Pixels.php (revision 21)
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Validates an integer representation of pixels according to the HTML spec.
+ */
+class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($string, $config, $context) {
+        
+        $string = trim($string);
+        if ($string === '0') return $string;
+        if ($string === '')  return false;
+        $length = strlen($string);
+        if (substr($string, $length - 2) == 'px') {
+            $string = substr($string, 0, $length - 2);
+        }
+        if (!is_numeric($string)) return false;
+        $int = (int) $string;
+        
+        if ($int < 0) return '0';
+        
+        // upper-bound value, extremely high values can
+        // crash operating systems, see <http://ha.ckers.org/imagecrash.html>
+        // WARNING, above link WILL crash you if you're using Windows
+        
+        if ($int > 1200) return '1200';
+        
+        return (string) $int;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/URI.php (revision 21)
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * Validates a URI as defined by RFC 3986.
+ * @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
+ */
+class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
+{
+    
+    protected $parser;
+    protected $embedsResource;
+    
+    /**
+     * @param $embeds_resource_resource Does the URI here result in an extra HTTP request?
+     */
+    public function __construct($embeds_resource = false) {
+        $this->parser = new HTMLPurifier_URIParser();
+        $this->embedsResource = (bool) $embeds_resource;
+    }
+    
+    public function validate($uri, $config, $context) {
+        
+        if ($config->get('URI', 'Disable')) return false;
+        
+        $uri = $this->parseCDATA($uri);
+        
+        // parse the URI
+        $uri = $this->parser->parse($uri);
+        if ($uri === false) return false;
+        
+        // add embedded flag to context for validators
+        $context->register('EmbeddedURI', $this->embedsResource); 
+        
+        $ok = false;
+        do {
+            
+            // generic validation
+            $result = $uri->validate($config, $context);
+            if (!$result) break;
+            
+            // chained filtering
+            $uri_def = $config->getDefinition('URI');
+            $result = $uri_def->filter($uri, $config, $context);
+            if (!$result) break;
+            
+            // scheme-specific validation 
+            $scheme_obj = $uri->getSchemeObj($config, $context);
+            if (!$scheme_obj) break;
+            if ($this->embedsResource && !$scheme_obj->browsable) break;
+            $result = $scheme_obj->validate($uri, $config, $context);
+            if (!$result) break;
+            
+            // survived gauntlet
+            $ok = true;
+            
+        } while (false);
+        
+        $context->destroy('EmbeddedURI');
+        if (!$ok) return false;
+        
+        // back to string
+        $result = $uri->toString();
+        
+        // munge entire URI if necessary
+        if (
+            !is_null($uri->host) && // indicator for authority
+            !empty($scheme_obj->browsable) &&
+            !is_null($munge = $config->get('URI', 'Munge'))
+        ) {
+            $result = str_replace('%s', rawurlencode($result), $munge);
+        }
+        
+        return $result;
+        
+    }
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Lang.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Lang.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Lang.php (revision 21)
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Validates the HTML attribute lang, effectively a language code.
+ * @note Built according to RFC 3066, which obsoleted RFC 1766
+ */
+class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($string, $config, $context) {
+        
+        $string = trim($string);
+        if (!$string) return false;
+        
+        $subtags = explode('-', $string);
+        $num_subtags = count($subtags);
+        
+        if ($num_subtags == 0) return false; // sanity check
+        
+        // process primary subtag : $subtags[0]
+        $length = strlen($subtags[0]);
+        switch ($length) {
+            case 0:
+                return false;
+            case 1:
+                if (! ($subtags[0] == 'x' || $subtags[0] == 'i') ) {
+                    return false;
+                }
+                break;
+            case 2:
+            case 3:
+                if (! ctype_alpha($subtags[0]) ) {
+                    return false;
+                } elseif (! ctype_lower($subtags[0]) ) {
+                    $subtags[0] = strtolower($subtags[0]);
+                }
+                break;
+            default:
+                return false;
+        }
+        
+        $new_string = $subtags[0];
+        if ($num_subtags == 1) return $new_string;
+        
+        // process second subtag : $subtags[1]
+        $length = strlen($subtags[1]);
+        if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) {
+            return $new_string;
+        }
+        if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]);
+        
+        $new_string .= '-' . $subtags[1];
+        if ($num_subtags == 2) return $new_string;
+        
+        // process all other subtags, index 2 and up
+        for ($i = 2; $i < $num_subtags; $i++) {
+            $length = strlen($subtags[$i]);
+            if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) {
+                return $new_string;
+            }
+            if (!ctype_lower($subtags[$i])) {
+                $subtags[$i] = strtolower($subtags[$i]);
+            }
+            $new_string .= '-' . $subtags[$i];
+        }
+        
+        return $new_string;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Multiple.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Multiple.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Multiple.php (revision 21)
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * Framework class for strings that involve multiple values.
+ * 
+ * Certain CSS properties such as border-width and margin allow multiple
+ * lengths to be specified.  This class can take a vanilla border-width
+ * definition and multiply it, usually into a max of four.
+ * 
+ * @note Even though the CSS specification isn't clear about it, inherit
+ *       can only be used alone: it will never manifest as part of a multi
+ *       shorthand declaration.  Thus, this class does not allow inherit.
+ */
+class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Instance of component definition to defer validation to.
+     * @todo Make protected
+     */
+    public $single;
+    
+    /**
+     * Max number of values allowed.
+     * @todo Make protected
+     */
+    public $max;
+    
+    /**
+     * @param $single HTMLPurifier_AttrDef to multiply
+     * @param $max Max number of values allowed (usually four)
+     */
+    public function __construct($single, $max = 4) {
+        $this->single = $single;
+        $this->max = $max;
+    }
+    
+    public function validate($string, $config, $context) {
+        $string = $this->parseCDATA($string);
+        if ($string === '') return false;
+        $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n
+        $length = count($parts);
+        $final = '';
+        for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) {
+            if (ctype_space($parts[$i])) continue;
+            $result = $this->single->validate($parts[$i], $config, $context);
+            if ($result !== false) {
+                $final .= $result . ' ';
+                $num++;
+            }
+        }
+        if ($final === '') return false;
+        return rtrim($final);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/TextDecoration.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/TextDecoration.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/TextDecoration.php (revision 21)
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Validates the value for the CSS property text-decoration
+ * @note This class could be generalized into a version that acts sort of
+ *       like Enum except you can compound the allowed values.
+ */
+class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($string, $config, $context) {
+        
+        static $allowed_values = array(
+            'line-through' => true,
+            'overline' => true,
+            'underline' => true
+        );
+        
+        $string = strtolower($this->parseCDATA($string));
+        $parts = explode(' ', $string);
+        $final = '';
+        foreach ($parts as $part) {
+            if (isset($allowed_values[$part])) {
+                $final .= $part . ' ';
+            }
+        }
+        $final = rtrim($final);
+        if ($final === '') return false;
+        return $final;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/ListStyle.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/ListStyle.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/ListStyle.php (revision 21)
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * Validates shorthand CSS property list-style.
+ * @warning Does not support url tokens that have internal spaces.
+ */
+class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Local copy of component validators.
+     * @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl.
+     */
+    protected $info;
+    
+    public function __construct($config) {
+        $def = $config->getCSSDefinition();
+        $this->info['list-style-type']     = $def->info['list-style-type'];
+        $this->info['list-style-position'] = $def->info['list-style-position'];
+        $this->info['list-style-image'] = $def->info['list-style-image'];
+    }
+    
+    public function validate($string, $config, $context) {
+        
+        // regular pre-processing
+        $string = $this->parseCDATA($string);
+        if ($string === '') return false;
+        
+        // assumes URI doesn't have spaces in it
+        $bits = explode(' ', strtolower($string)); // bits to process
+        
+        $caught = array();
+        $caught['type']     = false;
+        $caught['position'] = false;
+        $caught['image']    = false;
+        
+        $i = 0; // number of catches
+        $none = false;
+        
+        foreach ($bits as $bit) {
+            if ($i >= 3) return; // optimization bit
+            if ($bit === '') continue;
+            foreach ($caught as $key => $status) {
+                if ($status !== false) continue;
+                $r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
+                if ($r === false) continue;
+                if ($r === 'none') {
+                    if ($none) continue;
+                    else $none = true;
+                    if ($key == 'image') continue;
+                }
+                $caught[$key] = $r;
+                $i++;
+                break;
+            }
+        }
+        
+        if (!$i) return false;
+        
+        $ret = array();
+        
+        // construct type
+        if ($caught['type']) $ret[] = $caught['type'];
+        
+        // construct image
+        if ($caught['image']) $ret[] = $caught['image'];
+        
+        // construct position
+        if ($caught['position']) $ret[] = $caught['position'];
+        
+        if (empty($ret)) return false;
+        return implode(' ', $ret);
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/URI.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/URI.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/URI.php (revision 21)
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Validates a URI in CSS syntax, which uses url('http://example.com')
+ * @note While theoretically speaking a URI in a CSS document could
+ *       be non-embedded, as of CSS2 there is no such usage so we're
+ *       generalizing it. This may need to be changed in the future.
+ * @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as
+ *          the separator, you cannot put a literal semicolon in
+ *          in the URI. Try percent encoding it, in that case.
+ */
+class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
+{
+    
+    public function __construct() {
+        parent::__construct(true); // always embedded
+    }
+    
+    public function validate($uri_string, $config, $context) {
+        // parse the URI out of the string and then pass it onto
+        // the parent object
+        
+        $uri_string = $this->parseCDATA($uri_string);
+        if (strpos($uri_string, 'url(') !== 0) return false;
+        $uri_string = substr($uri_string, 4);
+        $new_length = strlen($uri_string) - 1;
+        if ($uri_string[$new_length] != ')') return false;
+        $uri = trim(substr($uri_string, 0, $new_length));
+        
+        if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) {
+            $quote = $uri[0];
+            $new_length = strlen($uri) - 1;
+            if ($uri[$new_length] !== $quote) return false;
+            $uri = substr($uri, 1, $new_length - 1);
+        }
+        
+        $keys   = array(  '(',   ')',   ',',   ' ',   '"',   "'");
+        $values = array('\\(', '\\)', '\\,', '\\ ', '\\"', "\\'");
+        $uri = str_replace($values, $keys, $uri);
+        
+        $result = parent::validate($uri, $config, $context);
+        
+        if ($result === false) return false;
+        
+        // escape necessary characters according to CSS spec
+        // except for the comma, none of these should appear in the
+        // URI at all
+        $result = str_replace($keys, $values, $result);
+        
+        return "url($result)";
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Composite.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Composite.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Composite.php (revision 21)
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * Allows multiple validators to attempt to validate attribute.
+ * 
+ * Composite is just what it sounds like: a composite of many validators.
+ * This means that multiple HTMLPurifier_AttrDef objects will have a whack
+ * at the string.  If one of them passes, that's what is returned.  This is
+ * especially useful for CSS values, which often are a choice between
+ * an enumerated set of predefined values or a flexible data type.
+ */
+class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * List of HTMLPurifier_AttrDef objects that may process strings
+     * @todo Make protected
+     */
+    public $defs;
+    
+    /**
+     * @param $defs List of HTMLPurifier_AttrDef objects
+     */
+    public function __construct($defs) {
+        $this->defs = $defs;
+    }
+    
+    public function validate($string, $config, $context) {
+        foreach ($this->defs as $i => $def) {
+            $result = $this->defs[$i]->validate($string, $config, $context);
+            if ($result !== false) return $result;
+        }
+        return false;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php (revision 21)
@@ -0,0 +1,125 @@
+<?php
+
+/* W3C says:
+    [ // adjective and number must be in correct order, even if
+      // you could switch them without introducing ambiguity.
+      // some browsers support that syntax
+        [
+            <percentage> | <length> | left | center | right
+        ]
+        [ 
+            <percentage> | <length> | top | center | bottom
+        ]?
+    ] |
+    [ // this signifies that the vertical and horizontal adjectives
+      // can be arbitrarily ordered, however, there can only be two,
+      // one of each, or none at all
+        [
+            left | center | right
+        ] ||
+        [
+            top | center | bottom
+        ]
+    ]
+    top, left = 0%
+    center, (none) = 50%
+    bottom, right = 100%
+*/
+
+/* QuirksMode says:
+    keyword + length/percentage must be ordered correctly, as per W3C
+    
+    Internet Explorer and Opera, however, support arbitrary ordering. We
+    should fix it up.
+    
+    Minor issue though, not strictly necessary.
+*/
+
+// control freaks may appreciate the ability to convert these to
+// percentages or something, but it's not necessary
+
+/**
+ * Validates the value of background-position.
+ */
+class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
+{
+    
+    protected $length;
+    protected $percentage;
+    
+    public function __construct() {
+        $this->length     = new HTMLPurifier_AttrDef_CSS_Length();
+        $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage();
+    }
+    
+    public function validate($string, $config, $context) {
+        $string = $this->parseCDATA($string);
+        $bits = explode(' ', $string);
+        
+        $keywords = array();
+        $keywords['h'] = false; // left, right
+        $keywords['v'] = false; // top, bottom
+        $keywords['c'] = false; // center
+        $measures = array();
+        
+        $i = 0;
+        
+        $lookup = array(
+            'top' => 'v',
+            'bottom' => 'v',
+            'left' => 'h',
+            'right' => 'h',
+            'center' => 'c'
+        );
+        
+        foreach ($bits as $bit) {
+            if ($bit === '') continue;
+            
+            // test for keyword
+            $lbit = ctype_lower($bit) ? $bit : strtolower($bit);
+            if (isset($lookup[$lbit])) {
+                $status = $lookup[$lbit];
+                $keywords[$status] = $lbit;
+                $i++;
+            }
+            
+            // test for length
+            $r = $this->length->validate($bit, $config, $context);
+            if ($r !== false) {
+                $measures[] = $r;
+                $i++;
+            }
+            
+            // test for percentage
+            $r = $this->percentage->validate($bit, $config, $context);
+            if ($r !== false) {
+                $measures[] = $r;
+                $i++;
+            }
+            
+        }
+        
+        if (!$i) return false; // no valid values were caught
+        
+        
+        $ret = array();
+        
+        // first keyword
+        if     ($keywords['h'])     $ret[] = $keywords['h'];
+        elseif (count($measures))   $ret[] = array_shift($measures);
+        elseif ($keywords['c']) {
+            $ret[] = $keywords['c'];
+            $keywords['c'] = false; // prevent re-use: center = center center
+        }
+        
+        if     ($keywords['v'])     $ret[] = $keywords['v'];
+        elseif (count($measures))   $ret[] = array_shift($measures);
+        elseif ($keywords['c'])     $ret[] = $keywords['c'];
+        
+        if (empty($ret)) return false;
+        return implode(' ', $ret);
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Font.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Font.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Font.php (revision 21)
@@ -0,0 +1,148 @@
+<?php
+
+/**
+ * Validates shorthand CSS property font.
+ */
+class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Local copy of component validators.
+     * 
+     * @note If we moved specific CSS property definitions to their own
+     *       classes instead of having them be assembled at run time by
+     *       CSSDefinition, this wouldn't be necessary.  We'd instantiate
+     *       our own copies.
+     */
+    protected $info = array();
+    
+    public function __construct($config) {
+        $def = $config->getCSSDefinition();
+        $this->info['font-style']   = $def->info['font-style'];
+        $this->info['font-variant'] = $def->info['font-variant'];
+        $this->info['font-weight']  = $def->info['font-weight'];
+        $this->info['font-size']    = $def->info['font-size'];
+        $this->info['line-height']  = $def->info['line-height'];
+        $this->info['font-family']  = $def->info['font-family'];
+    }
+    
+    public function validate($string, $config, $context) {
+        
+        static $system_fonts = array(
+            'caption' => true,
+            'icon' => true,
+            'menu' => true,
+            'message-box' => true,
+            'small-caption' => true,
+            'status-bar' => true
+        );
+        
+        // regular pre-processing
+        $string = $this->parseCDATA($string);
+        if ($string === '') return false;
+        
+        // check if it's one of the keywords
+        $lowercase_string = strtolower($string);
+        if (isset($system_fonts[$lowercase_string])) {
+            return $lowercase_string;
+        }
+        
+        $bits = explode(' ', $string); // bits to process
+        $stage = 0; // this indicates what we're looking for
+        $caught = array(); // which stage 0 properties have we caught?
+        $stage_1 = array('font-style', 'font-variant', 'font-weight');
+        $final = ''; // output
+        
+        for ($i = 0, $size = count($bits); $i < $size; $i++) {
+            if ($bits[$i] === '') continue;
+            switch ($stage) {
+                
+                // attempting to catch font-style, font-variant or font-weight
+                case 0:
+                    foreach ($stage_1 as $validator_name) {
+                        if (isset($caught[$validator_name])) continue;
+                        $r = $this->info[$validator_name]->validate(
+                                                $bits[$i], $config, $context);
+                        if ($r !== false) {
+                            $final .= $r . ' ';
+                            $caught[$validator_name] = true;
+                            break;
+                        }
+                    }
+                    // all three caught, continue on
+                    if (count($caught) >= 3) $stage = 1;
+                    if ($r !== false) break;
+                
+                // attempting to catch font-size and perhaps line-height
+                case 1:
+                    $found_slash = false;
+                    if (strpos($bits[$i], '/') !== false) {
+                        list($font_size, $line_height) =
+                                                    explode('/', $bits[$i]);
+                        if ($line_height === '') {
+                            // ooh, there's a space after the slash!
+                            $line_height = false;
+                            $found_slash = true;
+                        }
+                    } else {
+                        $font_size = $bits[$i];
+                        $line_height = false;
+                    }
+                    $r = $this->info['font-size']->validate(
+                                              $font_size, $config, $context);
+                    if ($r !== false) {
+                        $final .= $r;
+                        // attempt to catch line-height
+                        if ($line_height === false) {
+                            // we need to scroll forward
+                            for ($j = $i + 1; $j < $size; $j++) {
+                                if ($bits[$j] === '') continue;
+                                if ($bits[$j] === '/') {
+                                    if ($found_slash) {
+                                        return false;
+                                    } else {
+                                        $found_slash = true;
+                                        continue;
+                                    }
+                                }
+                                $line_height = $bits[$j];
+                                break;
+                            }
+                        } else {
+                            // slash already found
+                            $found_slash = true;
+                            $j = $i;
+                        }
+                        if ($found_slash) {
+                            $i = $j;
+                            $r = $this->info['line-height']->validate(
+                                              $line_height, $config, $context);
+                            if ($r !== false) {
+                                $final .= '/' . $r;
+                            }
+                        }
+                        $final .= ' ';
+                        $stage = 2;
+                        break;
+                    }
+                    return false;
+                
+                // attempting to catch font-family
+                case 2:
+                    $font_family =
+                        implode(' ', array_slice($bits, $i, $size - $i));
+                    $r = $this->info['font-family']->validate(
+                                              $font_family, $config, $context);
+                    if ($r !== false) {
+                        $final .= $r . ' ';
+                        // processing completed successfully
+                        return rtrim($final);
+                    }
+                    return false;
+            }
+        }
+        return false;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/FontFamily.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/FontFamily.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/FontFamily.php (revision 21)
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * Validates a font family list according to CSS spec
+ * @todo whitelisting allowed fonts would be nice
+ */
+class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($string, $config, $context) {
+        static $generic_names = array(
+            'serif' => true,
+            'sans-serif' => true,
+            'monospace' => true,
+            'fantasy' => true,
+            'cursive' => true
+        );
+        
+        $string = $this->parseCDATA($string);
+        // assume that no font names contain commas in them
+        $fonts = explode(',', $string);
+        $final = '';
+        foreach($fonts as $font) {
+            $font = trim($font);
+            if ($font === '') continue;
+            // match a generic name
+            if (isset($generic_names[$font])) {
+                $final .= $font . ', ';
+                continue;
+            }
+            // match a quoted name
+            if ($font[0] === '"' || $font[0] === "'") {
+                $length = strlen($font);
+                if ($length <= 2) continue;
+                $quote = $font[0];
+                if ($font[$length - 1] !== $quote) continue;
+                $font = substr($font, 1, $length - 2);
+                // double-backslash processing is buggy
+                $font = str_replace("\\$quote", $quote, $font); // de-escape quote
+                $font = str_replace("\\\n", "\n", $font);       // de-escape newlines
+            }
+            // $font is a pure representation of the font name
+            
+            if (ctype_alnum($font)) {
+                // very simple font, allow it in unharmed
+                $final .= $font . ', ';
+                continue;
+            }
+            
+            // complicated font, requires quoting
+            
+            // armor single quotes and new lines
+            $font = str_replace("'", "\\'", $font);
+            $font = str_replace("\n", "\\\n", $font);
+            $final .= "'$font', ";
+        }
+        $final = rtrim($final, ', ');
+        if ($final === '') return false;
+        return $final;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php (revision 21)
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Decorator which enables CSS properties to be disabled for specific elements.
+ */
+class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef
+{
+    protected $def, $element;
+    
+    /**
+     * @param $def Definition to wrap
+     * @param $element Element to deny
+     */
+    public function __construct($def, $element) {
+        $this->def = $def;
+        $this->element = $element;
+    }
+    /**
+     * Checks if CurrentToken is set and equal to $this->element
+     */
+    public function validate($string, $config, $context) {
+        $token = $context->get('CurrentToken', true);
+        if ($token && $token->name == $this->element) return false;
+        return $this->def->validate($string, $config, $context);
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Border.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Border.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Border.php (revision 21)
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * Validates the border property as defined by CSS.
+ */
+class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Local copy of properties this property is shorthand for.
+     */
+    protected $info = array();
+    
+    public function __construct($config) {
+        $def = $config->getCSSDefinition();
+        $this->info['border-width'] = $def->info['border-width'];
+        $this->info['border-style'] = $def->info['border-style'];
+        $this->info['border-top-color'] = $def->info['border-top-color'];
+    }
+    
+    public function validate($string, $config, $context) {
+        $string = $this->parseCDATA($string);
+        $string = $this->mungeRgb($string);
+        $bits = explode(' ', $string);
+        $done = array(); // segments we've finished
+        $ret = ''; // return value
+        foreach ($bits as $bit) {
+            foreach ($this->info as $propname => $validator) {
+                if (isset($done[$propname])) continue;
+                $r = $validator->validate($bit, $config, $context);
+                if ($r !== false) {
+                    $ret .= $r . ' ';
+                    $done[$propname] = true;
+                    break;
+                }
+            }
+        }
+        return rtrim($ret);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Percentage.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Percentage.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Percentage.php (revision 21)
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * Validates a Percentage as defined by the CSS spec.
+ */
+class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Instance of HTMLPurifier_AttrDef_CSS_Number to defer number validation
+     */
+    protected $number_def;
+    
+    /**
+     * @param Bool indicating whether to forbid negative values
+     */
+    public function __construct($non_negative = false) {
+        $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
+    }
+    
+    public function validate($string, $config, $context) {
+        
+        $string = $this->parseCDATA($string);
+        
+        if ($string === '') return false;
+        $length = strlen($string);
+        if ($length === 1) return false;
+        if ($string[$length - 1] !== '%') return false;
+        
+        $number = substr($string, 0, $length - 1);
+        $number = $this->number_def->validate($number, $config, $context);
+        
+        if ($number === false) return false;
+        return "$number%";
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Color.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Color.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Color.php (revision 21)
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * Validates Color as defined by CSS.
+ */
+class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($color, $config, $context) {
+        
+        static $colors = null;
+        if ($colors === null) $colors = $config->get('Core', 'ColorKeywords');
+        
+        $color = trim($color);
+        if ($color === '') return false;
+        
+        $lower = strtolower($color);
+        if (isset($colors[$lower])) return $colors[$lower];
+        
+        if (strpos($color, 'rgb(') !== false) {
+            // rgb literal handling
+            $length = strlen($color);
+            if (strpos($color, ')') !== $length - 1) return false;
+            $triad = substr($color, 4, $length - 4 - 1);
+            $parts = explode(',', $triad);
+            if (count($parts) !== 3) return false;
+            $type = false; // to ensure that they're all the same type
+            $new_parts = array();
+            foreach ($parts as $part) {
+                $part = trim($part);
+                if ($part === '') return false;
+                $length = strlen($part);
+                if ($part[$length - 1] === '%') {
+                    // handle percents
+                    if (!$type) {
+                        $type = 'percentage';
+                    } elseif ($type !== 'percentage') {
+                        return false;
+                    }
+                    $num = (float) substr($part, 0, $length - 1);
+                    if ($num < 0) $num = 0;
+                    if ($num > 100) $num = 100;
+                    $new_parts[] = "$num%";
+                } else {
+                    // handle integers
+                    if (!$type) {
+                        $type = 'integer';
+                    } elseif ($type !== 'integer') {
+                        return false;
+                    }
+                    $num = (int) $part;
+                    if ($num < 0) $num = 0;
+                    if ($num > 255) $num = 255;
+                    $new_parts[] = (string) $num;
+                }
+            }
+            $new_triad = implode(',', $new_parts);
+            $color = "rgb($new_triad)";
+        } else {
+            // hexadecimal handling
+            if ($color[0] === '#') {
+                $hex = substr($color, 1);
+            } else {
+                $hex = $color;
+                $color = '#' . $color;
+            }
+            $length = strlen($hex);
+            if ($length !== 3 && $length !== 6) return false;
+            if (!ctype_xdigit($hex)) return false;
+        }
+        
+        return $color;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Background.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Background.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Background.php (revision 21)
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * Validates shorthand CSS property background.
+ * @warning Does not support url tokens that have internal spaces.
+ */
+class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Local copy of component validators.
+     * @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
+     */
+    protected $info;
+    
+    public function __construct($config) {
+        $def = $config->getCSSDefinition();
+        $this->info['background-color'] = $def->info['background-color'];
+        $this->info['background-image'] = $def->info['background-image'];
+        $this->info['background-repeat'] = $def->info['background-repeat'];
+        $this->info['background-attachment'] = $def->info['background-attachment'];
+        $this->info['background-position'] = $def->info['background-position'];
+    }
+    
+    public function validate($string, $config, $context) {
+        
+        // regular pre-processing
+        $string = $this->parseCDATA($string);
+        if ($string === '') return false;
+        
+        // munge rgb() decl if necessary
+        $string = $this->mungeRgb($string);
+        
+        // assumes URI doesn't have spaces in it
+        $bits = explode(' ', strtolower($string)); // bits to process
+        
+        $caught = array();
+        $caught['color']    = false;
+        $caught['image']    = false;
+        $caught['repeat']   = false;
+        $caught['attachment'] = false;
+        $caught['position'] = false;
+        
+        $i = 0; // number of catches
+        $none = false;
+        
+        foreach ($bits as $bit) {
+            if ($bit === '') continue;
+            foreach ($caught as $key => $status) {
+                if ($key != 'position') {
+                    if ($status !== false) continue;
+                    $r = $this->info['background-' . $key]->validate($bit, $config, $context);
+                } else {
+                    $r = $bit;
+                }
+                if ($r === false) continue;
+                if ($key == 'position') {
+                    if ($caught[$key] === false) $caught[$key] = '';
+                    $caught[$key] .= $r . ' ';
+                } else {
+                    $caught[$key] = $r;
+                }
+                $i++;
+                break;
+            }
+        }
+        
+        if (!$i) return false;
+        if ($caught['position'] !== false) {
+            $caught['position'] = $this->info['background-position']->
+                validate($caught['position'], $config, $context);
+        }
+        
+        $ret = array();
+        foreach ($caught as $value) {
+            if ($value === false) continue;
+            $ret[] = $value;
+        }
+        
+        if (empty($ret)) return false;
+        return implode(' ', $ret);
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php (revision 21)
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Decorator which enables !important to be used in CSS values.
+ */
+class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef
+{
+    protected $def, $allow;
+    
+    /**
+     * @param $def Definition to wrap
+     * @param $allow Whether or not to allow !important
+     */
+    public function __construct($def, $allow = false) {
+        $this->def = $def;
+        $this->allow = $allow;
+    }
+    /**
+     * Intercepts and removes !important if necessary
+     */
+    public function validate($string, $config, $context) {
+        // test for ! and important tokens
+        $string = trim($string);
+        $is_important = false;
+        // :TODO: optimization: test directly for !important and ! important
+        if (strlen($string) >= 9 && substr($string, -9) === 'important') {
+            $temp = rtrim(substr($string, 0, -9));
+            // use a temp, because we might want to restore important
+            if (strlen($temp) >= 1 && substr($temp, -1) === '!') {
+                $string = rtrim(substr($temp, 0, -1));
+                $is_important = true;
+            }
+        }
+        $string = $this->def->validate($string, $config, $context);
+        if ($this->allow && $is_important) $string .= ' !important';
+        return $string;
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Length.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Length.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Length.php (revision 21)
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Represents a Length as defined by CSS.
+ */
+class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Valid unit lookup table.
+     * @warning The code assumes all units are two characters long.  Be careful
+     *          if we have to change this behavior!
+     */
+    protected $units = array('em' => true, 'ex' => true, 'px' => true, 'in' => true,
+         'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true);
+    /**
+     * Instance of HTMLPurifier_AttrDef_Number to defer number validation to
+     */
+    protected $number_def;
+    
+    /**
+     * @param $non_negative Bool indication whether or not negative values are
+     *                      allowed.
+     */
+    public function __construct($non_negative = false) {
+        $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
+    }
+    
+    public function validate($length, $config, $context) {
+        
+        $length = $this->parseCDATA($length);
+        if ($length === '') return false;
+        if ($length === '0') return '0';
+        $strlen = strlen($length);
+        if ($strlen === 1) return false; // impossible!
+        
+        // we assume all units are two characters
+        $unit = substr($length, $strlen - 2);
+        if (!ctype_lower($unit)) $unit = strtolower($unit);
+        $number = substr($length, 0, $strlen - 2);
+        
+        if (!isset($this->units[$unit])) return false;
+        
+        $number = $this->number_def->validate($number, $config, $context);
+        if ($number === false) return false;
+        
+        return $number . $unit;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/AlphaValue.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/AlphaValue.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/AlphaValue.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number
+{
+    
+    public function __construct() {
+        parent::__construct(false); // opacity is non-negative, but we will clamp it
+    }
+    
+    public function validate($number, $config, $context) {
+        $result = parent::validate($number, $config, $context);
+        if ($result === false) return $result;
+        $float = (float) $result;
+        if ($float < 0.0) $result = '0';
+        if ($float > 1.0) $result = '1';
+        return $result;
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Filter.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Filter.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Filter.php (revision 21)
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Microsoft's proprietary filter: CSS property
+ * @note Currently supports the alpha filter. In the future, this will
+ *       probably need an extensible framework
+ */
+class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef
+{
+    
+    protected $intValidator;
+    
+    public function __construct() {
+        $this->intValidator = new HTMLPurifier_AttrDef_Integer();
+    }
+    
+    public function validate($value, $config, $context) {
+        $value = $this->parseCDATA($value);
+        if ($value === 'none') return $value;
+        // if we looped this we could support multiple filters
+        $function_length = strcspn($value, '(');
+        $function = trim(substr($value, 0, $function_length));
+        if ($function !== 'alpha' &&
+            $function !== 'Alpha' &&
+            $function !== 'progid:DXImageTransform.Microsoft.Alpha'
+            ) return false;
+        $cursor = $function_length + 1;
+        $parameters_length = strcspn($value, ')', $cursor);
+        $parameters = substr($value, $cursor, $parameters_length);
+        $params = explode(',', $parameters);
+        $ret_params = array();
+        $lookup = array();
+        foreach ($params as $param) {
+            list($key, $value) = explode('=', $param);
+            $key   = trim($key);
+            $value = trim($value);
+            if (isset($lookup[$key])) continue;
+            if ($key !== 'opacity') continue;
+            $value = $this->intValidator->validate($value, $config, $context);
+            if ($value === false) continue;
+            $int = (int) $value;
+            if ($int > 100) $value = '100';
+            if ($int < 0) $value = '0';
+            $ret_params[] = "$key=$value";
+            $lookup[$key] = true;
+        }
+        $ret_parameters = implode(',', $ret_params);
+        $ret_function = "$function($ret_parameters)";
+        return $ret_function;
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Number.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Number.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS/Number.php (revision 21)
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * Validates a number as defined by the CSS spec.
+ */
+class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Bool indicating whether or not only positive values allowed.
+     */
+    protected $non_negative = false;
+    
+    /**
+     * @param $non_negative Bool indicating whether negatives are forbidden
+     */
+    public function __construct($non_negative = false) {
+        $this->non_negative = $non_negative;
+    }
+    
+    public function validate($number, $config, $context) {
+        
+        $number = $this->parseCDATA($number);
+        
+        if ($number === '') return false;
+        if ($number === '0') return '0';
+        
+        $sign = '';
+        switch ($number[0]) {
+            case '-':
+                if ($this->non_negative) return false;
+                $sign = '-';
+            case '+':
+                $number = substr($number, 1);
+        }
+        
+        if (ctype_digit($number)) {
+            $number = ltrim($number, '0');
+            return $number ? $sign . $number : '0';
+        }
+        
+        // Period is the only non-numeric character allowed
+        if (strpos($number, '.') === false) return false;
+        
+        list($left, $right) = explode('.', $number, 2);
+        
+        if ($left === '' && $right === '') return false;
+        if ($left !== '' && !ctype_digit($left)) return false;
+        
+        $left  = ltrim($left,  '0');
+        $right = rtrim($right, '0');
+        
+        if ($right === '') {
+            return $left ? $sign . $left : '0';
+        } elseif (!ctype_digit($right)) {
+            return false;
+        }
+        
+        return $sign . $left . '.' . $right;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Enum.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Enum.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Enum.php (revision 21)
@@ -0,0 +1,64 @@
+<?php
+
+// Enum = Enumerated
+/**
+ * Validates a keyword against a list of valid values.
+ * @warning The case-insensitive compare of this function uses PHP's
+ *          built-in strtolower and ctype_lower functions, which may
+ *          cause problems with international comparisons
+ */
+class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef
+{
+    
+    /**
+     * Lookup table of valid values.
+     * @todo Make protected
+     */
+    public $valid_values   = array();
+    
+    /**
+     * Bool indicating whether or not enumeration is case sensitive.
+     * @note In general this is always case insensitive.
+     */
+    protected $case_sensitive = false; // values according to W3C spec
+    
+    /**
+     * @param $valid_values List of valid values
+     * @param $case_sensitive Bool indicating whether or not case sensitive
+     */
+    public function __construct(
+        $valid_values = array(), $case_sensitive = false
+    ) {
+        $this->valid_values = array_flip($valid_values);
+        $this->case_sensitive = $case_sensitive;
+    }
+    
+    public function validate($string, $config, $context) {
+        $string = trim($string);
+        if (!$this->case_sensitive) {
+            // we may want to do full case-insensitive libraries
+            $string = ctype_lower($string) ? $string : strtolower($string);
+        }
+        $result = isset($this->valid_values[$string]);
+        
+        return $result ? $string : false;
+    }
+    
+    /**
+     * @param $string In form of comma-delimited list of case-insensitive
+     *      valid values. Example: "foo,bar,baz". Prepend "s:" to make
+     *      case sensitive
+     */
+    public function make($string) {
+        if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') {
+            $string = substr($string, 2);
+            $sensitive = true;
+        } else {
+            $sensitive = false;
+        }
+        $values = explode(',', $string);
+        return new HTMLPurifier_AttrDef_Enum($values, $sensitive);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Text.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Text.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/Text.php (revision 21)
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * Validates arbitrary text according to the HTML spec.
+ */
+class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($string, $config, $context) {
+        return $this->parseCDATA($string);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrDef/CSS.php (revision 21)
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * Validates the HTML attribute style, otherwise known as CSS.
+ * @note We don't implement the whole CSS specification, so it might be
+ *       difficult to reuse this component in the context of validating
+ *       actual stylesheet declarations.
+ * @note If we were really serious about validating the CSS, we would
+ *       tokenize the styles and then parse the tokens. Obviously, we
+ *       are not doing that. Doing that could seriously harm performance,
+ *       but would make these components a lot more viable for a CSS
+ *       filtering solution.
+ */
+class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
+{
+    
+    public function validate($css, $config, $context) {
+        
+        $css = $this->parseCDATA($css);
+        
+        $definition = $config->getCSSDefinition();
+        
+        // we're going to break the spec and explode by semicolons.
+        // This is because semicolon rarely appears in escaped form
+        // Doing this is generally flaky but fast
+        // IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
+        // for details
+        
+        $declarations = explode(';', $css);
+        $propvalues = array();
+        
+        foreach ($declarations as $declaration) {
+            if (!$declaration) continue;
+            if (!strpos($declaration, ':')) continue;
+            list($property, $value) = explode(':', $declaration, 2);
+            $property = trim($property);
+            $value    = trim($value);
+            $ok = false;
+            do {
+                if (isset($definition->info[$property])) {
+                    $ok = true;
+                    break;
+                }
+                if (ctype_lower($property)) break;
+                $property = strtolower($property);
+                if (isset($definition->info[$property])) {
+                    $ok = true;
+                    break;
+                }
+            } while(0);
+            if (!$ok) continue;
+            // inefficient call, since the validator will do this again
+            if (strtolower(trim($value)) !== 'inherit') {
+                // inherit works for everything (but only on the base property)
+                $result = $definition->info[$property]->validate(
+                    $value, $config, $context );
+            } else {
+                $result = 'inherit';
+            }
+            if ($result === false) continue;
+            $propvalues[$property] = $result;
+        }
+        
+        // procedure does not write the new CSS simultaneously, so it's
+        // slightly inefficient, but it's the only way of getting rid of
+        // duplicates. Perhaps config to optimize it, but not now.
+        
+        $new_declarations = '';
+        foreach ($propvalues as $prop => $value) {
+            $new_declarations .= "$prop:$value;";
+        }
+        
+        return $new_declarations ? $new_declarations : false;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/VarParser.php (revision 21)
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * Parses string representations into their corresponding native PHP
+ * variable type. The base implementation does a simple type-check.
+ */
+class HTMLPurifier_VarParser
+{
+    
+    /**
+     * Lookup table of allowed types.
+     */
+    static public $types = array(
+        'string'    => true,
+        'istring'   => true,
+        'text'      => true,
+        'itext'     => true,
+        'int'       => true,
+        'float'     => true,
+        'bool'      => true,
+        'lookup'    => true,
+        'list'      => true,
+        'hash'      => true,
+        'mixed'     => true
+    );
+    
+    /**
+     * Lookup table of types that are string, and can have aliases or
+     * allowed value lists.
+     */
+    static public $stringTypes = array(
+        'string'    => true,
+        'istring'   => true,
+        'text'      => true,
+        'itext'     => true,
+    );
+    
+    /**
+     * Validate a variable according to type. Throws
+     * HTMLPurifier_VarParserException if invalid.
+     * It may return NULL as a valid type if $allow_null is true.
+     *
+     * @param $var Variable to validate
+     * @param $type Type of variable, see HTMLPurifier_VarParser->types
+     * @param $allow_null Whether or not to permit null as a value
+     * @return Validated and type-coerced variable
+     */
+    final public function parse($var, $type, $allow_null = false) {
+        if (!isset(HTMLPurifier_VarParser::$types[$type])) {
+            throw new HTMLPurifier_VarParserException("Invalid type '$type'");
+        }
+        $var = $this->parseImplementation($var, $type, $allow_null);
+        if ($allow_null && $var === null) return null;
+        // These are basic checks, to make sure nothing horribly wrong
+        // happened in our implementations.
+        switch ($type) {
+            case 'string':
+            case 'istring':
+            case 'text':
+            case 'itext':
+                if (!is_string($var)) break;
+                if ($type[0] == 'i') $var = strtolower($var);
+                return $var;
+            case 'int':
+                if (!is_int($var)) break;
+                return $var;
+            case 'float':
+                if (!is_float($var)) break;
+                return $var;
+            case 'bool':
+                if (!is_bool($var)) break;
+                return $var;
+            case 'lookup':
+            case 'list':
+            case 'hash':
+                if (!is_array($var)) break;
+                if ($type === 'lookup') {
+                    foreach ($var as $k) if ($k !== true) $this->error('Lookup table contains value other than true');
+                } elseif ($type === 'list') {
+                    $keys = array_keys($var);
+                    if (array_keys($keys) !== $keys) $this->error('Indices for list are not uniform');
+                }
+                return $var;
+            case 'mixed':
+                return $var;
+            default:
+                $this->errorInconsistent(get_class($this), $type);
+        }
+        $this->errorGeneric($var, $type);
+    }
+    
+    /**
+     * Actually implements the parsing. Base implementation is to not
+     * do anything to $var. Subclasses should overload this!
+     */
+    protected function parseImplementation($var, $type, $allow_null) {
+        return $var;
+    }
+    
+    /**
+     * Throws an exception.
+     */
+    protected function error($msg) {
+        throw new HTMLPurifier_VarParserException($msg);
+    }
+    
+    /**
+     * Throws an inconsistency exception.
+     * @note This should not ever be called. It would be called if we
+     *       extend the allowed values of HTMLPurifier_VarParser without
+     *       updating subclasses.
+     */
+    protected function errorInconsistent($class, $type) {
+        throw new HTMLPurifier_Exception("Inconsistency in $class: $type not implemented");
+    }
+    
+    /**
+     * Generic error for if a type didn't work.
+     */
+    protected function errorGeneric($var, $type) {
+        $vtype = gettype($var);
+        $this->error("Expected type $type, got $vtype");
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Config.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Config.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Config.php (revision 21)
@@ -0,0 +1,484 @@
+<?php
+
+/**
+ * Configuration object that triggers customizable behavior.
+ *
+ * @warning This class is strongly defined: that means that the class
+ *          will fail if an undefined directive is retrieved or set.
+ * 
+ * @note Many classes that could (although many times don't) use the
+ *       configuration object make it a mandatory parameter.  This is
+ *       because a configuration object should always be forwarded,
+ *       otherwise, you run the risk of missing a parameter and then
+ *       being stumped when a configuration directive doesn't work.
+ * 
+ * @todo Reconsider some of the public member variables
+ */
+class HTMLPurifier_Config
+{
+    
+    /**
+     * HTML Purifier's version
+     */
+    public $version = '3.1.0';
+    
+    /**
+     * Bool indicator whether or not to automatically finalize 
+     * the object if a read operation is done
+     */
+    public $autoFinalize = true;
+    
+    // protected member variables
+    
+    /**
+     * Namespace indexed array of serials for specific namespaces (see
+     * getSerial() for more info).
+     */
+    protected $serials = array();
+    
+    /**
+     * Serial for entire configuration object
+     */
+    protected $serial;
+    
+    /**
+     * Two-level associative array of configuration directives
+     */
+    protected $conf;
+    
+    /**
+     * Parser for variables
+     */
+    protected $parser;
+    
+    /**
+     * Reference HTMLPurifier_ConfigSchema for value checking
+     * @note This is public for introspective purposes. Please don't
+     *       abuse!
+     */
+    public $def;
+    
+    /**
+     * Indexed array of definitions
+     */
+    protected $definitions;
+    
+    /**
+     * Bool indicator whether or not config is finalized
+     */
+    protected $finalized = false;
+    
+    /**
+     * @param $definition HTMLPurifier_ConfigSchema that defines what directives
+     *                    are allowed.
+     */
+    public function __construct($definition) {
+        $this->conf = $definition->defaults; // set up, copy in defaults
+        $this->def  = $definition; // keep a copy around for checking
+        $this->parser = new HTMLPurifier_VarParser_Flexible();
+    }
+    
+    /**
+     * Convenience constructor that creates a config object based on a mixed var
+     * @param mixed $config Variable that defines the state of the config
+     *                      object. Can be: a HTMLPurifier_Config() object,
+     *                      an array of directives based on loadArray(),
+     *                      or a string filename of an ini file.
+     * @param HTMLPurifier_ConfigSchema Schema object
+     * @return Configured HTMLPurifier_Config object
+     */
+    public static function create($config, $schema = null) {
+        if ($config instanceof HTMLPurifier_Config) {
+            // pass-through
+            return $config;
+        }
+        if (!$schema) {
+            $ret = HTMLPurifier_Config::createDefault();
+        } else {
+            $ret = new HTMLPurifier_Config($schema);
+        }
+        if (is_string($config)) $ret->loadIni($config);
+        elseif (is_array($config)) $ret->loadArray($config);
+        return $ret;
+    }
+    
+    /**
+     * Convenience constructor that creates a default configuration object.
+     * @return Default HTMLPurifier_Config object.
+     */
+    public static function createDefault() {
+        $definition = HTMLPurifier_ConfigSchema::instance();
+        $config = new HTMLPurifier_Config($definition);
+        return $config;
+    }
+    
+    /**
+     * Retreives a value from the configuration.
+     * @param $namespace String namespace
+     * @param $key String key
+     */
+    public function get($namespace, $key) {
+        if (!$this->finalized && $this->autoFinalize) $this->finalize();
+        if (!isset($this->def->info[$namespace][$key])) {
+            // can't add % due to SimpleTest bug
+            trigger_error('Cannot retrieve value of undefined directive ' . htmlspecialchars("$namespace.$key"),
+                E_USER_WARNING);
+            return;
+        }
+        if ($this->def->info[$namespace][$key]->class == 'alias') {
+            $d = $this->def->info[$namespace][$key];
+            trigger_error('Cannot get value from aliased directive, use real name ' . $d->namespace . '.' . $d->name,
+                E_USER_ERROR);
+            return;
+        }
+        return $this->conf[$namespace][$key];
+    }
+    
+    /**
+     * Retreives an array of directives to values from a given namespace
+     * @param $namespace String namespace
+     */
+    public function getBatch($namespace) {
+        if (!$this->finalized && $this->autoFinalize) $this->finalize();
+        if (!isset($this->def->info[$namespace])) {
+            trigger_error('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace),
+                E_USER_WARNING);
+            return;
+        }
+        return $this->conf[$namespace];
+    }
+    
+    /**
+     * Returns a md5 signature of a segment of the configuration object
+     * that uniquely identifies that particular configuration
+     * @note Revision is handled specially and is removed from the batch
+     *       before processing!
+     * @param $namespace Namespace to get serial for
+     */
+    public function getBatchSerial($namespace) {
+        if (empty($this->serials[$namespace])) {
+            $batch = $this->getBatch($namespace);
+            unset($batch['DefinitionRev']);
+            $this->serials[$namespace] = md5(serialize($batch));
+        }
+        return $this->serials[$namespace];
+    }
+    
+    /**
+     * Returns a md5 signature for the entire configuration object
+     * that uniquely identifies that particular configuration
+     */
+    public function getSerial() {
+        if (empty($this->serial)) {
+            $this->serial = md5(serialize($this->getAll()));
+        }
+        return $this->serial;
+    }
+    
+    /**
+     * Retrieves all directives, organized by namespace
+     */
+    public function getAll() {
+        if (!$this->finalized && $this->autoFinalize) $this->finalize();
+        return $this->conf;
+    }
+    
+    /**
+     * Sets a value to configuration.
+     * @param $namespace String namespace
+     * @param $key String key
+     * @param $value Mixed value
+     */
+    public function set($namespace, $key, $value, $from_alias = false) {
+        if ($this->isFinalized('Cannot set directive after finalization')) return;
+        if (!isset($this->def->info[$namespace][$key])) {
+            trigger_error('Cannot set undefined directive ' . htmlspecialchars("$namespace.$key") . ' to value',
+                E_USER_WARNING);
+            return;
+        }
+        if ($this->def->info[$namespace][$key]->class == 'alias') {
+            if ($from_alias) {
+                trigger_error('Double-aliases not allowed, please fix '.
+                    'ConfigSchema bug with' . "$namespace.$key", E_USER_ERROR);
+                return;
+            }
+            $this->set($new_ns = $this->def->info[$namespace][$key]->namespace,
+                       $new_dir = $this->def->info[$namespace][$key]->name,
+                       $value, true);
+            trigger_error("$namespace.$key is an alias, preferred directive name is $new_ns.$new_dir", E_USER_NOTICE);
+            return;
+        }
+        try {
+            $value = $this->parser->parse(
+                        $value,
+                        $type = $this->def->info[$namespace][$key]->type,
+                        $this->def->info[$namespace][$key]->allow_null
+                     );
+        } catch (HTMLPurifier_VarParserException $e) {
+            trigger_error('Value for ' . "$namespace.$key" . ' is of invalid type, should be ' . $type, E_USER_WARNING);
+            return;
+        }
+        if (is_string($value)) {
+            // resolve value alias if defined
+            if (isset($this->def->info[$namespace][$key]->aliases[$value])) {
+                $value = $this->def->info[$namespace][$key]->aliases[$value];
+            }
+            if ($this->def->info[$namespace][$key]->allowed !== true) {
+                // check to see if the value is allowed
+                if (!isset($this->def->info[$namespace][$key]->allowed[$value])) {
+                    trigger_error('Value not supported, valid values are: ' .
+                        $this->_listify($this->def->info[$namespace][$key]->allowed), E_USER_WARNING);
+                    return;
+                }
+            }
+        }
+        $this->conf[$namespace][$key] = $value;
+        
+        // reset definitions if the directives they depend on changed
+        // this is a very costly process, so it's discouraged 
+        // with finalization
+        if ($namespace == 'HTML' || $namespace == 'CSS') {
+            $this->definitions[$namespace] = null;
+        }
+        
+        $this->serials[$namespace] = false;
+    }
+    
+    /**
+     * Convenience function for error reporting
+     */
+    private function _listify($lookup) {
+        $list = array();
+        foreach ($lookup as $name => $b) $list[] = $name;
+        return implode(', ', $list);
+    }
+    
+    /**
+     * Retrieves object reference to the HTML definition.
+     * @param $raw Return a copy that has not been setup yet. Must be
+     *             called before it's been setup, otherwise won't work.
+     */
+    public function getHTMLDefinition($raw = false) {
+        return $this->getDefinition('HTML', $raw);
+    }
+    
+    /**
+     * Retrieves object reference to the CSS definition
+     * @param $raw Return a copy that has not been setup yet. Must be
+     *             called before it's been setup, otherwise won't work.
+     */
+    public function getCSSDefinition($raw = false) {
+        return $this->getDefinition('CSS', $raw);
+    }
+    
+    /**
+     * Retrieves a definition
+     * @param $type Type of definition: HTML, CSS, etc
+     * @param $raw  Whether or not definition should be returned raw
+     */
+    public function getDefinition($type, $raw = false) {
+        if (!$this->finalized && $this->autoFinalize) $this->finalize();
+        $factory = HTMLPurifier_DefinitionCacheFactory::instance();
+        $cache = $factory->create($type, $this);
+        if (!$raw) {
+            // see if we can quickly supply a definition
+            if (!empty($this->definitions[$type])) {
+                if (!$this->definitions[$type]->setup) {
+                    $this->definitions[$type]->setup($this);
+                    $cache->set($this->definitions[$type], $this);
+                }
+                return $this->definitions[$type];
+            }
+            // memory check missed, try cache
+            $this->definitions[$type] = $cache->get($this);
+            if ($this->definitions[$type]) {
+                // definition in cache, return it
+                return $this->definitions[$type];
+            }
+        } elseif (
+            !empty($this->definitions[$type]) &&
+            !$this->definitions[$type]->setup
+        ) {
+            // raw requested, raw in memory, quick return
+            return $this->definitions[$type];
+        }
+        // quick checks failed, let's create the object
+        if ($type == 'HTML') {
+            $this->definitions[$type] = new HTMLPurifier_HTMLDefinition();
+        } elseif ($type == 'CSS') {
+            $this->definitions[$type] = new HTMLPurifier_CSSDefinition();
+        } elseif ($type == 'URI') {
+            $this->definitions[$type] = new HTMLPurifier_URIDefinition();
+        } else {
+            throw new HTMLPurifier_Exception("Definition of $type type not supported");
+        }
+        // quick abort if raw
+        if ($raw) {
+            if (is_null($this->get($type, 'DefinitionID'))) {
+                // fatally error out if definition ID not set
+                throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID");
+            }
+            return $this->definitions[$type];
+        }
+        // set it up
+        $this->definitions[$type]->setup($this);
+        // save in cache
+        $cache->set($this->definitions[$type], $this);
+        return $this->definitions[$type];
+    }
+    
+    /**
+     * Loads configuration values from an array with the following structure:
+     * Namespace.Directive => Value
+     * @param $config_array Configuration associative array
+     */
+    public function loadArray($config_array) {
+        if ($this->isFinalized('Cannot load directives after finalization')) return;
+        foreach ($config_array as $key => $value) {
+            $key = str_replace('_', '.', $key);
+            if (strpos($key, '.') !== false) {
+                // condensed form
+                list($namespace, $directive) = explode('.', $key);
+                $this->set($namespace, $directive, $value);
+            } else {
+                $namespace = $key;
+                $namespace_values = $value;
+                foreach ($namespace_values as $directive => $value) {
+                    $this->set($namespace, $directive, $value);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Returns a list of array(namespace, directive) for all directives
+     * that are allowed in a web-form context as per an allowed
+     * namespaces/directives list.
+     * @param $allowed List of allowed namespaces/directives
+     */
+    public static function getAllowedDirectivesForForm($allowed, $schema = null) {
+        if (!$schema) {
+            $schema = HTMLPurifier_ConfigSchema::instance();
+        }
+        if ($allowed !== true) {
+             if (is_string($allowed)) $allowed = array($allowed);
+             $allowed_ns = array();
+             $allowed_directives = array();
+             $blacklisted_directives = array();
+             foreach ($allowed as $ns_or_directive) {
+                 if (strpos($ns_or_directive, '.') !== false) {
+                     // directive
+                     if ($ns_or_directive[0] == '-') {
+                         $blacklisted_directives[substr($ns_or_directive, 1)] = true;
+                     } else {
+                         $allowed_directives[$ns_or_directive] = true;
+                     }
+                 } else {
+                     // namespace
+                     $allowed_ns[$ns_or_directive] = true;
+                 }
+             }
+        }
+        $ret = array();
+        foreach ($schema->info as $ns => $keypairs) {
+            foreach ($keypairs as $directive => $def) {
+                if ($allowed !== true) {
+                    if (isset($blacklisted_directives["$ns.$directive"])) continue;
+                    if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue;
+                }
+                if ($def->class == 'alias') continue;
+                if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
+                $ret[] = array($ns, $directive);
+            }
+        }
+        return $ret;
+    }
+    
+    /**
+     * Loads configuration values from $_GET/$_POST that were posted
+     * via ConfigForm
+     * @param $array $_GET or $_POST array to import
+     * @param $index Index/name that the config variables are in
+     * @param $allowed List of allowed namespaces/directives 
+     * @param $mq_fix Boolean whether or not to enable magic quotes fix
+     * @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy
+     */
+    public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
+        $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
+        $config = HTMLPurifier_Config::create($ret, $schema);
+        return $config;
+    }
+    
+    /**
+     * Merges in configuration values from $_GET/$_POST to object. NOT STATIC.
+     * @note Same parameters as loadArrayFromForm
+     */
+    public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) {
+         $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
+         $this->loadArray($ret);
+    }
+    
+    /**
+     * Prepares an array from a form into something usable for the more
+     * strict parts of HTMLPurifier_Config
+     */
+    public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
+        if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
+        $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
+        
+        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);
+        $ret = array();
+        foreach ($allowed as $key) {
+            list($ns, $directive) = $key;
+            $skey = "$ns.$directive";
+            if (!empty($array["Null_$skey"])) {
+                $ret[$ns][$directive] = null;
+                continue;
+            }
+            if (!isset($array[$skey])) continue;
+            $value = $mq ? stripslashes($array[$skey]) : $array[$skey];
+            $ret[$ns][$directive] = $value;
+        }
+        return $ret;
+    }
+    
+    /**
+     * Loads configuration values from an ini file
+     * @param $filename Name of ini file
+     */
+    public function loadIni($filename) {
+        if ($this->isFinalized('Cannot load directives after finalization')) return;
+        $array = parse_ini_file($filename, true);
+        $this->loadArray($array);
+    }
+    
+    /**
+     * Checks whether or not the configuration object is finalized.
+     * @param $error String error message, or false for no error
+     */
+    public function isFinalized($error = false) {
+        if ($this->finalized && $error) {
+            trigger_error($error, E_USER_ERROR);
+        }
+        return $this->finalized;
+    }
+    
+    /**
+     * Finalizes configuration only if auto finalize is on and not
+     * already finalized
+     */
+    public function autoFinalize() {
+        if (!$this->finalized && $this->autoFinalize) $this->finalize();
+    }
+    
+    /**
+     * Finalizes a configuration object, prohibiting further change
+     */
+    public function finalize() {
+        $this->finalized = true;
+    }
+    
+}
+
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityLookup.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityLookup.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityLookup.php (revision 21)
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * Object that provides entity lookup table from entity name to character
+ */
+class HTMLPurifier_EntityLookup {
+    
+    /**
+     * Assoc array of entity name to character represented.
+     */
+    public $table;
+    
+    /**
+     * Sets up the entity lookup table from the serialized file contents.
+     * @note The serialized contents are versioned, but were generated
+     *       using the maintenance script generate_entity_file.php
+     * @warning This is not in constructor to help enforce the Singleton
+     */
+    public function setup($file = false) {
+        if (!$file) {
+            $file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/EntityLookup/entities.ser';
+        }
+        $this->table = unserialize(file_get_contents($file));
+    }
+    
+    /**
+     * Retrieves sole instance of the object.
+     * @param Optional prototype of custom lookup table to overload with.
+     */
+    public static function instance($prototype = false) {
+        // no references, since PHP doesn't copy unless modified
+        static $instance = null;
+        if ($prototype) {
+            $instance = $prototype;
+        } elseif (!$instance) {
+            $instance = new HTMLPurifier_EntityLookup();
+            $instance->setup();
+        }
+        return $instance;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrTransform.php (revision 21)
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Processes an entire attribute array for corrections needing multiple values.
+ * 
+ * Occasionally, a certain attribute will need to be removed and popped onto
+ * another value.  Instead of creating a complex return syntax for
+ * HTMLPurifier_AttrDef, we just pass the whole attribute array to a
+ * specialized object and have that do the special work.  That is the
+ * family of HTMLPurifier_AttrTransform.
+ * 
+ * An attribute transformation can be assigned to run before or after
+ * HTMLPurifier_AttrDef validation.  See HTMLPurifier_HTMLDefinition for
+ * more details.
+ */
+
+abstract class HTMLPurifier_AttrTransform
+{
+    
+    /**
+     * Abstract: makes changes to the attributes dependent on multiple values.
+     * 
+     * @param $attr Assoc array of attributes, usually from
+     *              HTMLPurifier_Token_Tag::$attr
+     * @param $config Mandatory HTMLPurifier_Config object.
+     * @param $context Mandatory HTMLPurifier_Context object
+     * @returns Processed attribute array.
+     */
+    abstract public function transform($attr, $config, $context);
+    
+    /**
+     * Prepends CSS properties to the style attribute, creating the
+     * attribute if it doesn't exist.
+     * @param $attr Attribute array to process (passed by reference)
+     * @param $css CSS to prepend
+     */
+    public function prependCSS(&$attr, $css) {
+        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
+        $attr['style'] = $css . $attr['style'];
+    }
+    
+    /**
+     * Retrieves and removes an attribute
+     * @param $attr Attribute array to process (passed by reference)
+     * @param $key Key of attribute to confiscate
+     */
+    public function confiscateAttr(&$attr, $key) {
+        if (!isset($attr[$key])) return null;
+        $value = $attr[$key];
+        unset($attr[$key]);
+        return $value;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/LanguageFactory.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/LanguageFactory.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/LanguageFactory.php (revision 21)
@@ -0,0 +1,197 @@
+<?php
+
+/**
+ * Class responsible for generating HTMLPurifier_Language objects, managing
+ * caching and fallbacks.
+ * @note Thanks to MediaWiki for the general logic, although this version
+ *       has been entirely rewritten
+ * @todo Serialized cache for languages
+ */
+class HTMLPurifier_LanguageFactory
+{
+    
+    /**
+     * Cache of language code information used to load HTMLPurifier_Language objects
+     * Structure is: $factory->cache[$language_code][$key] = $value
+     * @value array map
+     */
+    public $cache;
+    
+    /**
+     * Valid keys in the HTMLPurifier_Language object. Designates which
+     * variables to slurp out of a message file.
+     * @value array list
+     */
+    public $keys = array('fallback', 'messages', 'errorNames');
+    
+    /**
+     * Instance of HTMLPurifier_AttrDef_Lang to validate language codes
+     * @value object HTMLPurifier_AttrDef_Lang
+     */
+    protected $validator;
+    
+    /**
+     * Cached copy of dirname(__FILE__), directory of current file without
+     * trailing slash
+     * @value string filename
+     */
+    protected $dir;
+    
+    /**
+     * Keys whose contents are a hash map and can be merged
+     * @value array lookup
+     */
+    protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true);
+    
+    /**
+     * Keys whose contents are a list and can be merged
+     * @value array lookup
+     */
+    protected $mergeable_keys_list = array();
+    
+    /**
+     * Retrieve sole instance of the factory.
+     * @param $prototype Optional prototype to overload sole instance with,
+     *                   or bool true to reset to default factory.
+     */
+    public static function instance($prototype = null) {
+        static $instance = null;
+        if ($prototype !== null) {
+            $instance = $prototype;
+        } elseif ($instance === null || $prototype == true) {
+            $instance = new HTMLPurifier_LanguageFactory();
+            $instance->setup();
+        }
+        return $instance;
+    }
+    
+    /**
+     * Sets up the singleton, much like a constructor
+     * @note Prevents people from getting this outside of the singleton
+     */
+    public function setup() {
+        $this->validator = new HTMLPurifier_AttrDef_Lang();
+        $this->dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier';
+    }
+    
+    /**
+     * Creates a language object, handles class fallbacks
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     * @param $code Code to override configuration with. Private parameter.
+     */
+    public function create($config, $context, $code = false) {
+        
+        // validate language code
+        if ($code === false) {
+            $code = $this->validator->validate(
+              $config->get('Core', 'Language'), $config, $context
+            );
+        } else {
+            $code = $this->validator->validate($code, $config, $context);
+        }
+        if ($code === false) $code = 'en'; // malformed code becomes English
+        
+        $pcode = str_replace('-', '_', $code); // make valid PHP classname
+        static $depth = 0; // recursion protection
+        
+        if ($code == 'en') {
+            $lang = new HTMLPurifier_Language($config, $context);
+        } else {
+            $class = 'HTMLPurifier_Language_' . $pcode;
+            $file  = $this->dir . '/Language/classes/' . $code . '.php';
+            if (file_exists($file) || class_exists($class, false)) {
+                $lang = new $class($config, $context);
+            } else {
+                // Go fallback
+                $raw_fallback = $this->getFallbackFor($code);
+                $fallback = $raw_fallback ? $raw_fallback : 'en';
+                $depth++;
+                $lang = $this->create($config, $context, $fallback);
+                if (!$raw_fallback) {
+                    $lang->error = true;
+                }
+                $depth--;
+            }
+        }
+        
+        $lang->code = $code;
+        
+        return $lang;
+        
+    }
+    
+    /**
+     * Returns the fallback language for language
+     * @note Loads the original language into cache
+     * @param $code string language code
+     */
+    public function getFallbackFor($code) {
+        $this->loadLanguage($code);
+        return $this->cache[$code]['fallback'];
+    }
+    
+    /**
+     * Loads language into the cache, handles message file and fallbacks
+     * @param $code string language code
+     */
+    public function loadLanguage($code) {
+        static $languages_seen = array(); // recursion guard
+        
+        // abort if we've already loaded it
+        if (isset($this->cache[$code])) return;
+        
+        // generate filename
+        $filename = $this->dir . '/Language/messages/' . $code . '.php';
+        
+        // default fallback : may be overwritten by the ensuing include
+        $fallback = ($code != 'en') ? 'en' : false;
+        
+        // load primary localisation
+        if (!file_exists($filename)) {
+            // skip the include: will rely solely on fallback
+            $filename = $this->dir . '/Language/messages/en.php';
+            $cache = array();
+        } else {
+            include $filename;
+            $cache = compact($this->keys);
+        }
+        
+        // load fallback localisation
+        if (!empty($fallback)) {
+            
+            // infinite recursion guard
+            if (isset($languages_seen[$code])) {
+                trigger_error('Circular fallback reference in language ' .
+                    $code, E_USER_ERROR);
+                $fallback = 'en';
+            }
+            $language_seen[$code] = true;
+            
+            // load the fallback recursively
+            $this->loadLanguage($fallback);
+            $fallback_cache = $this->cache[$fallback];
+            
+            // merge fallback with current language
+            foreach ( $this->keys as $key ) {
+                if (isset($cache[$key]) && isset($fallback_cache[$key])) {
+                    if (isset($this->mergeable_keys_map[$key])) {
+                        $cache[$key] = $cache[$key] + $fallback_cache[$key];
+                    } elseif (isset($this->mergeable_keys_list[$key])) {
+                        $cache[$key] = array_merge( $fallback_cache[$key], $cache[$key] );
+                    }
+                } else {
+                    $cache[$key] = $fallback_cache[$key];
+                }
+            }
+            
+        }
+        
+        // save to cache for later retrieval
+        $this->cache[$code] = $cache;
+        
+        return;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Custom.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Custom.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Custom.php (revision 21)
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * Custom validation class, accepts DTD child definitions
+ * 
+ * @warning Currently this class is an all or nothing proposition, that is,
+ *          it will only give a bool return value.
+ * @note This class is currently not used by any code, although it is unit
+ *       tested.
+ */
+class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
+{
+    public $type = 'custom';
+    public $allow_empty = false;
+    /**
+     * Allowed child pattern as defined by the DTD
+     */
+    public $dtd_regex;
+    /**
+     * PCRE regex derived from $dtd_regex
+     * @private
+     */
+    private $_pcre_regex;
+    /**
+     * @param $dtd_regex Allowed child pattern from the DTD
+     */
+    public function __construct($dtd_regex) {
+        $this->dtd_regex = $dtd_regex;
+        $this->_compileRegex();
+    }
+    /**
+     * Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex)
+     */
+    protected function _compileRegex() {
+        $raw = str_replace(' ', '', $this->dtd_regex);
+        if ($raw{0} != '(') {
+            $raw = "($raw)";
+        }
+        $el = '[#a-zA-Z0-9_.-]+';
+        $reg = $raw;
+        
+        // COMPLICATED! AND MIGHT BE BUGGY! I HAVE NO CLUE WHAT I'M
+        // DOING! Seriously: if there's problems, please report them.
+        
+        // collect all elements into the $elements array
+        preg_match_all("/$el/", $reg, $matches);
+        foreach ($matches[0] as $match) {
+            $this->elements[$match] = true;
+        }
+        
+        // setup all elements as parentheticals with leading commas
+        $reg = preg_replace("/$el/", '(,\\0)', $reg);
+        
+        // remove commas when they were not solicited
+        $reg = preg_replace("/([^,(|]\(+),/", '\\1', $reg);
+        
+        // remove all non-paranthetical commas: they are handled by first regex
+        $reg = preg_replace("/,\(/", '(', $reg);
+        
+        $this->_pcre_regex = $reg;
+    }
+    public function validateChildren($tokens_of_children, $config, $context) {
+        $list_of_children = '';
+        $nesting = 0; // depth into the nest
+        foreach ($tokens_of_children as $token) {
+            if (!empty($token->is_whitespace)) continue;
+            
+            $is_child = ($nesting == 0); // direct
+            
+            if ($token instanceof HTMLPurifier_Token_Start) {
+                $nesting++;
+            } elseif ($token instanceof HTMLPurifier_Token_End) {
+                $nesting--;
+            }
+            
+            if ($is_child) {
+                $list_of_children .= $token->name . ',';
+            }
+        }
+        // add leading comma to deal with stray comma declarations
+        $list_of_children = ',' . rtrim($list_of_children, ',');
+        $okay =
+            preg_match(
+                '/^,?'.$this->_pcre_regex.'$/',
+                $list_of_children
+            );
+        
+        return (bool) $okay;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Chameleon.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Chameleon.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Chameleon.php (revision 21)
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Definition that uses different definitions depending on context.
+ * 
+ * The del and ins tags are notable because they allow different types of
+ * elements depending on whether or not they're in a block or inline context.
+ * Chameleon allows this behavior to happen by using two different
+ * definitions depending on context.  While this somewhat generalized,
+ * it is specifically intended for those two tags.
+ */
+class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
+{
+    
+    /**
+     * Instance of the definition object to use when inline. Usually stricter.
+     */
+    public $inline;
+    
+    /**
+     * Instance of the definition object to use when block.
+     */
+    public $block;
+    
+    public $type = 'chameleon';
+    
+    /**
+     * @param $inline List of elements to allow when inline.
+     * @param $block List of elements to allow when block.
+     */
+    public function __construct($inline, $block) {
+        $this->inline = new HTMLPurifier_ChildDef_Optional($inline);
+        $this->block  = new HTMLPurifier_ChildDef_Optional($block);
+        $this->elements = $this->block->elements;
+    }
+    
+    public function validateChildren($tokens_of_children, $config, $context) {
+        if ($context->get('IsInline') === false) {
+            return $this->block->validateChildren(
+                $tokens_of_children, $config, $context);
+        } else {
+            return $this->inline->validateChildren(
+                $tokens_of_children, $config, $context);
+        }
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Empty.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Empty.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Empty.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Definition that disallows all elements.
+ * @warning validateChildren() in this class is actually never called, because
+ *          empty elements are corrected in HTMLPurifier_Strategy_MakeWellFormed
+ *          before child definitions are parsed in earnest by
+ *          HTMLPurifier_Strategy_FixNesting.
+ */
+class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef
+{
+    public $allow_empty = true;
+    public $type = 'empty';
+    public function __construct() {}
+    public function validateChildren($tokens_of_children, $config, $context) {
+        return array();
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Required.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Required.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Required.php (revision 21)
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * Definition that allows a set of elements, but disallows empty children.
+ */
+class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
+{
+    /**
+     * Lookup table of allowed elements.
+     * @public
+     */
+    public $elements = array();
+    /**
+     * @param $elements List of allowed element names (lowercase).
+     */
+    public function __construct($elements) {
+        if (is_string($elements)) {
+            $elements = str_replace(' ', '', $elements);
+            $elements = explode('|', $elements);
+        }
+        $keys = array_keys($elements);
+        if ($keys == array_keys($keys)) {
+            $elements = array_flip($elements);
+            foreach ($elements as $i => $x) {
+                $elements[$i] = true;
+                if (empty($i)) unset($elements[$i]); // remove blank
+            }
+        }
+        $this->elements = $elements;
+    }
+    public $allow_empty = false;
+    public $type = 'required';
+    public function validateChildren($tokens_of_children, $config, $context) {
+        // if there are no tokens, delete parent node
+        if (empty($tokens_of_children)) return false;
+        
+        // the new set of children
+        $result = array();
+        
+        // current depth into the nest
+        $nesting = 0;
+        
+        // whether or not we're deleting a node
+        $is_deleting = false;
+        
+        // whether or not parsed character data is allowed
+        // this controls whether or not we silently drop a tag
+        // or generate escaped HTML from it
+        $pcdata_allowed = isset($this->elements['#PCDATA']);
+        
+        // a little sanity check to make sure it's not ALL whitespace
+        $all_whitespace = true;
+        
+        // some configuration
+        $escape_invalid_children = $config->get('Core', 'EscapeInvalidChildren');
+        
+        // generator
+        static $gen = null;
+        if ($gen === null) {
+            $gen = new HTMLPurifier_Generator();
+        }
+        
+        foreach ($tokens_of_children as $token) {
+            if (!empty($token->is_whitespace)) {
+                $result[] = $token;
+                continue;
+            }
+            $all_whitespace = false; // phew, we're not talking about whitespace
+            
+            $is_child = ($nesting == 0);
+            
+            if ($token instanceof HTMLPurifier_Token_Start) {
+                $nesting++;
+            } elseif ($token instanceof HTMLPurifier_Token_End) {
+                $nesting--;
+            }
+            
+            if ($is_child) {
+                $is_deleting = false;
+                if (!isset($this->elements[$token->name])) {
+                    $is_deleting = true;
+                    if ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text) {
+                        $result[] = $token;
+                    } elseif ($pcdata_allowed && $escape_invalid_children) {
+                        $result[] = new HTMLPurifier_Token_Text(
+                            $gen->generateFromToken($token, $config)
+                        );
+                    }
+                    continue;
+                }
+            }
+            if (!$is_deleting || ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text)) {
+                $result[] = $token;
+            } elseif ($pcdata_allowed && $escape_invalid_children) {
+                $result[] =
+                    new HTMLPurifier_Token_Text(
+                        $gen->generateFromToken( $token, $config )
+                    );
+            } else {
+                // drop silently
+            }
+        }
+        if (empty($result)) return false;
+        if ($all_whitespace) return false;
+        if ($tokens_of_children == $result) return true;
+        return $result;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/StrictBlockquote.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/StrictBlockquote.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/StrictBlockquote.php (revision 21)
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Takes the contents of blockquote when in strict and reformats for validation.
+ */
+class HTMLPurifier_ChildDef_StrictBlockquote extends HTMLPurifier_ChildDef_Required
+{
+    protected $real_elements;
+    protected $fake_elements;
+    public $allow_empty = true;
+    public $type = 'strictblockquote';
+    protected $init = false;
+    public function validateChildren($tokens_of_children, $config, $context) {
+        
+        $def = $config->getHTMLDefinition();
+        if (!$this->init) {
+            // allow all inline elements
+            $this->real_elements = $this->elements;
+            $this->fake_elements = $def->info_content_sets['Flow'];
+            $this->fake_elements['#PCDATA'] = true;
+            $this->init = true;
+        }
+        
+        // trick the parent class into thinking it allows more
+        $this->elements = $this->fake_elements;
+        $result = parent::validateChildren($tokens_of_children, $config, $context);
+        $this->elements = $this->real_elements;
+        
+        if ($result === false) return array();
+        if ($result === true) $result = $tokens_of_children;
+        
+        $block_wrap_start = new HTMLPurifier_Token_Start($def->info_block_wrapper);
+        $block_wrap_end   = new HTMLPurifier_Token_End(  $def->info_block_wrapper);
+        $is_inline = false;
+        $depth = 0;
+        $ret = array();
+        
+        // assuming that there are no comment tokens
+        foreach ($result as $i => $token) {
+            $token = $result[$i];
+            // ifs are nested for readability
+            if (!$is_inline) {
+                if (!$depth) {
+                     if (
+                        ($token instanceof HTMLPurifier_Token_Text && !$token->is_whitespace) ||
+                        (!$token instanceof HTMLPurifier_Token_Text && !isset($this->elements[$token->name]))
+                     ) {
+                        $is_inline = true;
+                        $ret[] = $block_wrap_start;
+                     }
+                }
+            } else {
+                if (!$depth) {
+                    // starting tokens have been inline text / empty
+                    if ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) {
+                        if (isset($this->elements[$token->name])) {
+                            // ended
+                            $ret[] = $block_wrap_end;
+                            $is_inline = false;
+                        }
+                    }
+                }
+            }
+            $ret[] = $token;
+            if ($token instanceof HTMLPurifier_Token_Start) $depth++;
+            if ($token instanceof HTMLPurifier_Token_End)   $depth--;
+        }
+        if ($is_inline) $ret[] = $block_wrap_end;
+        return $ret;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Optional.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Optional.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Optional.php (revision 21)
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * Definition that allows a set of elements, and allows no children.
+ * @note This is a hack to reuse code from HTMLPurifier_ChildDef_Required,
+ *       really, one shouldn't inherit from the other.  Only altered behavior
+ *       is to overload a returned false with an array.  Thus, it will never
+ *       return false.
+ */
+class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required
+{
+    public $allow_empty = true;
+    public $type = 'optional';
+    public function validateChildren($tokens_of_children, $config, $context) {
+        $result = parent::validateChildren($tokens_of_children, $config, $context);
+        if ($result === false) {
+            if (empty($tokens_of_children)) return true;
+            else return array();
+        }
+        return $result;
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Table.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Table.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ChildDef/Table.php (revision 21)
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * Definition for tables
+ */
+class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
+{
+    public $allow_empty = false;
+    public $type = 'table';
+    public $elements = array('tr' => true, 'tbody' => true, 'thead' => true,
+        'tfoot' => true, 'caption' => true, 'colgroup' => true, 'col' => true);
+    public function __construct() {}
+    public function validateChildren($tokens_of_children, $config, $context) {
+        if (empty($tokens_of_children)) return false;
+        
+        // this ensures that the loop gets run one last time before closing
+        // up. It's a little bit of a hack, but it works! Just make sure you
+        // get rid of the token later.
+        $tokens_of_children[] = false;
+        
+        // only one of these elements is allowed in a table
+        $caption = false;
+        $thead   = false;
+        $tfoot   = false;
+        
+        // as many of these as you want
+        $cols    = array();
+        $content = array();
+        
+        $nesting = 0; // current depth so we can determine nodes
+        $is_collecting = false; // are we globbing together tokens to package
+                                // into one of the collectors?
+        $collection = array(); // collected nodes
+        $tag_index = 0; // the first node might be whitespace,
+                            // so this tells us where the start tag is
+        
+        foreach ($tokens_of_children as $token) {
+            $is_child = ($nesting == 0);
+            
+            if ($token === false) {
+                // terminating sequence started
+            } elseif ($token instanceof HTMLPurifier_Token_Start) {
+                $nesting++;
+            } elseif ($token instanceof HTMLPurifier_Token_End) {
+                $nesting--;
+            }
+            
+            // handle node collection
+            if ($is_collecting) {
+                if ($is_child) {
+                    // okay, let's stash the tokens away
+                    // first token tells us the type of the collection
+                    switch ($collection[$tag_index]->name) {
+                        case 'tr':
+                        case 'tbody':
+                            $content[] = $collection;
+                            break;
+                        case 'caption':
+                            if ($caption !== false) break;
+                            $caption = $collection;
+                            break;
+                        case 'thead':
+                        case 'tfoot':
+                            // access the appropriate variable, $thead or $tfoot
+                            $var = $collection[$tag_index]->name;
+                            if ($$var === false) {
+                                $$var = $collection;
+                            } else {
+                                // transmutate the first and less entries into
+                                // tbody tags, and then put into content
+                                $collection[$tag_index]->name = 'tbody';
+                                $collection[count($collection)-1]->name = 'tbody';
+                                $content[] = $collection;
+                            }
+                            break;
+                         case 'colgroup':
+                            $cols[] = $collection;
+                            break;
+                    }
+                    $collection = array();
+                    $is_collecting = false;
+                    $tag_index = 0;
+                } else {
+                    // add the node to the collection
+                    $collection[] = $token;
+                }
+            }
+            
+            // terminate
+            if ($token === false) break;
+            
+            if ($is_child) {
+                // determine what we're dealing with
+                if ($token->name == 'col') {
+                    // the only empty tag in the possie, we can handle it
+                    // immediately
+                    $cols[] = array_merge($collection, array($token));
+                    $collection = array();
+                    $tag_index = 0;
+                    continue;
+                }
+                switch($token->name) {
+                    case 'caption':
+                    case 'colgroup':
+                    case 'thead':
+                    case 'tfoot':
+                    case 'tbody':
+                    case 'tr':
+                        $is_collecting = true;
+                        $collection[] = $token;
+                        continue;
+                    default:
+                        if ($token instanceof HTMLPurifier_Token_Text && $token->is_whitespace) {
+                            $collection[] = $token;
+                            $tag_index++;
+                        }
+                        continue;
+                }
+            }
+        }
+        
+        if (empty($content)) return false;
+        
+        $ret = array();
+        if ($caption !== false) $ret = array_merge($ret, $caption);
+        if ($cols !== false)    foreach ($cols as $token_array) $ret = array_merge($ret, $token_array);
+        if ($thead !== false)   $ret = array_merge($ret, $thead);
+        if ($tfoot !== false)   $ret = array_merge($ret, $tfoot);
+        foreach ($content as $token_array) $ret = array_merge($ret, $token_array);
+        if (!empty($collection) && $is_collecting == false){
+            // grab the trailing space
+            $ret = array_merge($ret, $collection);
+        }
+        
+        array_pop($tokens_of_children); // remove phantom token
+        
+        return ($ret === $tokens_of_children) ? true : $ret;
+        
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/FixNesting.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/FixNesting.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/FixNesting.php (revision 21)
@@ -0,0 +1,328 @@
+<?php
+
+/**
+ * Takes a well formed list of tokens and fixes their nesting.
+ * 
+ * HTML elements dictate which elements are allowed to be their children,
+ * for example, you can't have a p tag in a span tag.  Other elements have
+ * much more rigorous definitions: tables, for instance, require a specific
+ * order for their elements.  There are also constraints not expressible by
+ * document type definitions, such as the chameleon nature of ins/del
+ * tags and global child exclusions.
+ * 
+ * The first major objective of this strategy is to iterate through all the
+ * nodes (not tokens) of the list of tokens and determine whether or not
+ * their children conform to the element's definition.  If they do not, the
+ * child definition may optionally supply an amended list of elements that
+ * is valid or require that the entire node be deleted (and the previous
+ * node rescanned).
+ * 
+ * The second objective is to ensure that explicitly excluded elements of
+ * an element do not appear in its children.  Code that accomplishes this
+ * task is pervasive through the strategy, though the two are distinct tasks
+ * and could, theoretically, be seperated (although it's not recommended).
+ * 
+ * @note Whether or not unrecognized children are silently dropped or
+ *       translated into text depends on the child definitions.
+ * 
+ * @todo Enable nodes to be bubbled out of the structure.
+ */
+
+class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
+{
+    
+    public function execute($tokens, $config, $context) {
+        //####################################################################//
+        // Pre-processing
+        
+        // get a copy of the HTML definition
+        $definition = $config->getHTMLDefinition();
+        
+        // insert implicit "parent" node, will be removed at end.
+        // DEFINITION CALL
+        $parent_name = $definition->info_parent;
+        array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name));
+        $tokens[] = new HTMLPurifier_Token_End($parent_name);
+        
+        // setup the context variable 'IsInline', for chameleon processing
+        // is 'false' when we are not inline, 'true' when it must always
+        // be inline, and an integer when it is inline for a certain
+        // branch of the document tree
+        $is_inline = $definition->info_parent_def->descendants_are_inline;
+        $context->register('IsInline', $is_inline);
+        
+        // setup error collector
+        $e =& $context->get('ErrorCollector', true);
+        
+        //####################################################################//
+        // Loop initialization
+        
+        // stack that contains the indexes of all parents,
+        // $stack[count($stack)-1] being the current parent
+        $stack = array();
+        
+        // stack that contains all elements that are excluded
+        // it is organized by parent elements, similar to $stack, 
+        // but it is only populated when an element with exclusions is
+        // processed, i.e. there won't be empty exclusions.
+        $exclude_stack = array();
+        
+        // variable that contains the start token while we are processing
+        // nodes. This enables error reporting to do its job
+        $start_token = false;
+        $context->register('CurrentToken', $start_token);
+        
+        //####################################################################//
+        // Loop
+        
+        // iterate through all start nodes. Determining the start node
+        // is complicated so it has been omitted from the loop construct
+        for ($i = 0, $size = count($tokens) ; $i < $size; ) {
+            
+            //################################################################//
+            // Gather information on children
+            
+            // child token accumulator
+            $child_tokens = array();
+            
+            // scroll to the end of this node, report number, and collect
+            // all children
+            for ($j = $i, $depth = 0; ; $j++) {
+                if ($tokens[$j] instanceof HTMLPurifier_Token_Start) {
+                    $depth++;
+                    // skip token assignment on first iteration, this is the
+                    // token we currently are on
+                    if ($depth == 1) continue;
+                } elseif ($tokens[$j] instanceof HTMLPurifier_Token_End) {
+                    $depth--;
+                    // skip token assignment on last iteration, this is the
+                    // end token of the token we're currently on
+                    if ($depth == 0) break;
+                }
+                $child_tokens[] = $tokens[$j];
+            }
+            
+            // $i is index of start token
+            // $j is index of end token
+            
+            $start_token = $tokens[$i]; // to make token available via CurrentToken
+            
+            //################################################################//
+            // Gather information on parent
+            
+            // calculate parent information
+            if ($count = count($stack)) {
+                $parent_index = $stack[$count-1];
+                $parent_name  = $tokens[$parent_index]->name;
+                if ($parent_index == 0) {
+                    $parent_def   = $definition->info_parent_def;
+                } else {
+                    $parent_def   = $definition->info[$parent_name];
+                }
+            } else {
+                // processing as if the parent were the "root" node
+                // unknown info, it won't be used anyway, in the future,
+                // we may want to enforce one element only (this is 
+                // necessary for HTML Purifier to clean entire documents
+                $parent_index = $parent_name = $parent_def = null;
+            }
+            
+            // calculate context
+            if ($is_inline === false) {
+                // check if conditions make it inline
+                if (!empty($parent_def) && $parent_def->descendants_are_inline) {
+                    $is_inline = $count - 1;
+                }
+            } else {
+                // check if we're out of inline
+                if ($count === $is_inline) {
+                    $is_inline = false;
+                }
+            }
+            
+            //################################################################//
+            // Determine whether element is explicitly excluded SGML-style
+            
+            // determine whether or not element is excluded by checking all
+            // parent exclusions. The array should not be very large, two
+            // elements at most.
+            $excluded = false;
+            if (!empty($exclude_stack)) {
+                foreach ($exclude_stack as $lookup) {
+                    if (isset($lookup[$tokens[$i]->name])) {
+                        $excluded = true;
+                        // no need to continue processing
+                        break;
+                    }
+                }
+            }
+            
+            //################################################################//
+            // Perform child validation
+            
+            if ($excluded) {
+                // there is an exclusion, remove the entire node
+                $result = false;
+                $excludes = array(); // not used, but good to initialize anyway
+            } else {
+                // DEFINITION CALL
+                if ($i === 0) {
+                    // special processing for the first node
+                    $def = $definition->info_parent_def;
+                } else {
+                    $def = $definition->info[$tokens[$i]->name];
+                    
+                }
+                
+                if (!empty($def->child)) {
+                    // have DTD child def validate children
+                    $result = $def->child->validateChildren(
+                        $child_tokens, $config, $context);
+                } else {
+                    // weird, no child definition, get rid of everything
+                    $result = false;
+                }
+                
+                // determine whether or not this element has any exclusions
+                $excludes = $def->excludes;
+            }
+            
+            // $result is now a bool or array
+            
+            //################################################################//
+            // Process result by interpreting $result
+            
+            if ($result === true || $child_tokens === $result) {
+                // leave the node as is
+                
+                // register start token as a parental node start
+                $stack[] = $i;
+                
+                // register exclusions if there are any
+                if (!empty($excludes)) $exclude_stack[] = $excludes;
+                
+                // move cursor to next possible start node
+                $i++;
+                
+            } elseif($result === false) {
+                // remove entire node
+                
+                if ($e) {
+                    if ($excluded) {
+                        $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded');
+                    } else {
+                        $e->send(E_ERROR, 'Strategy_FixNesting: Node removed');
+                    }
+                }
+                
+                // calculate length of inner tokens and current tokens
+                $length = $j - $i + 1;
+                
+                // perform removal
+                array_splice($tokens, $i, $length);
+                
+                // update size
+                $size -= $length;
+                
+                // there is no start token to register,
+                // current node is now the next possible start node
+                // unless it turns out that we need to do a double-check
+                
+                // this is a rought heuristic that covers 100% of HTML's
+                // cases and 99% of all other cases. A child definition
+                // that would be tricked by this would be something like:
+                // ( | a b c) where it's all or nothing. Fortunately,
+                // our current implementation claims that that case would
+                // not allow empty, even if it did
+                if (!$parent_def->child->allow_empty) {
+                    // we need to do a double-check
+                    $i = $parent_index;
+                    array_pop($stack);
+                }
+                
+                // PROJECTED OPTIMIZATION: Process all children elements before
+                // reprocessing parent node.
+                
+            } else {
+                // replace node with $result
+                
+                // calculate length of inner tokens
+                $length = $j - $i - 1;
+                
+                if ($e) {
+                    if (empty($result) && $length) {
+                        $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed');
+                    } else {
+                        $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized');
+                    }
+                }
+                
+                // perform replacement
+                array_splice($tokens, $i + 1, $length, $result);
+                
+                // update size
+                $size -= $length;
+                $size += count($result);
+                
+                // register start token as a parental node start
+                $stack[] = $i;
+                
+                // register exclusions if there are any
+                if (!empty($excludes)) $exclude_stack[] = $excludes;
+                
+                // move cursor to next possible start node
+                $i++;
+                
+            }
+            
+            //################################################################//
+            // Scroll to next start node
+            
+            // We assume, at this point, that $i is the index of the token
+            // that is the first possible new start point for a node.
+            
+            // Test if the token indeed is a start tag, if not, move forward
+            // and test again.
+            $size = count($tokens);
+            while ($i < $size and !$tokens[$i] instanceof HTMLPurifier_Token_Start) {
+                if ($tokens[$i] instanceof HTMLPurifier_Token_End) {
+                    // pop a token index off the stack if we ended a node
+                    array_pop($stack);
+                    // pop an exclusion lookup off exclusion stack if
+                    // we ended node and that node had exclusions
+                    if ($i == 0 || $i == $size - 1) {
+                        // use specialized var if it's the super-parent
+                        $s_excludes = $definition->info_parent_def->excludes;
+                    } else {
+                        $s_excludes = $definition->info[$tokens[$i]->name]->excludes;
+                    }
+                    if ($s_excludes) {
+                        array_pop($exclude_stack);
+                    }
+                }
+                $i++;
+            }
+            
+        }
+        
+        //####################################################################//
+        // Post-processing
+        
+        // remove implicit parent tokens at the beginning and end
+        array_shift($tokens);
+        array_pop($tokens);
+        
+        // remove context variables
+        $context->destroy('IsInline');
+        $context->destroy('CurrentToken');
+        
+        //####################################################################//
+        // Return
+        
+        return $tokens;
+        
+    }
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/MakeWellFormed.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/MakeWellFormed.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/MakeWellFormed.php (revision 21)
@@ -0,0 +1,297 @@
+<?php
+
+/**
+ * Takes tokens makes them well-formed (balance end tags, etc.)
+ */
+class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
+{
+    
+    /**
+     * Locally shared variable references
+     */
+    protected $inputTokens, $inputIndex, $outputTokens, $currentNesting,
+        $currentInjector, $injectors;
+    
+    public function execute($tokens, $config, $context) {
+        
+        $definition = $config->getHTMLDefinition();
+        
+        // local variables
+        $result = array();
+        $generator = new HTMLPurifier_Generator();
+        $escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags');
+        $e = $context->get('ErrorCollector', true);
+        
+        // member variables
+        $this->currentNesting = array();
+        $this->inputIndex     = false;
+        $this->inputTokens    =& $tokens;
+        $this->outputTokens   =& $result;
+        
+        // context variables
+        $context->register('CurrentNesting', $this->currentNesting);
+        $context->register('InputIndex',     $this->inputIndex);
+        $context->register('InputTokens',    $tokens);
+        
+        // -- begin INJECTOR --
+        
+        $this->injectors = array();
+        
+        $injectors = $config->getBatch('AutoFormat');
+        $custom_injectors = $injectors['Custom'];
+        unset($injectors['Custom']); // special case
+        foreach ($injectors as $injector => $b) {
+            $injector = "HTMLPurifier_Injector_$injector";
+            if (!$b) continue;
+            $this->injectors[] = new $injector;
+        }
+        foreach ($custom_injectors as $injector) {
+            if (is_string($injector)) {
+                $injector = "HTMLPurifier_Injector_$injector";
+                $injector = new $injector;
+            }
+            $this->injectors[] = $injector;
+        }
+        
+        // array index of the injector that resulted in an array
+        // substitution. This enables processTokens() to know which
+        // injectors are affected by the added tokens and which are
+        // not (namely, the ones after the current injector are not
+        // affected)
+        $this->currentInjector = false;
+        
+        // give the injectors references to the definition and context
+        // variables for performance reasons
+        foreach ($this->injectors as $i => $injector) {
+            $error = $injector->prepare($config, $context);
+            if (!$error) continue;
+            array_splice($this->injectors, $i, 1); // rm the injector
+            trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING);
+        }
+        
+        // warning: most foreach loops follow the convention $i => $injector.
+        // Don't define these as loop-wide variables, please!
+        
+        // -- end INJECTOR --
+        
+        $token = false;
+        $context->register('CurrentToken', $token);
+        
+        // isset is in loop because $tokens size changes during loop exec
+        for ($this->inputIndex = 0; isset($tokens[$this->inputIndex]); $this->inputIndex++) {
+            
+            // if all goes well, this token will be passed through unharmed
+            $token = $tokens[$this->inputIndex];
+            
+            //printTokens($tokens, $this->inputIndex);
+            
+            foreach ($this->injectors as $injector) {
+                if ($injector->skip > 0) $injector->skip--;
+            }
+            
+            // quick-check: if it's not a tag, no need to process
+            if (empty( $token->is_tag )) {
+                if ($token instanceof HTMLPurifier_Token_Text) {
+                     // injector handler code; duplicated for performance reasons
+                     foreach ($this->injectors as $i => $injector) {
+                         if (!$injector->skip) $injector->handleText($token);
+                         if (is_array($token)) {
+                             $this->currentInjector = $i;
+                             break;
+                         }
+                     }
+                }
+                $this->processToken($token, $config, $context);
+                continue;
+            }
+            
+            $info = $definition->info[$token->name]->child;
+            
+            // quick tag checks: anything that's *not* an end tag
+            $ok = false;
+            if ($info->type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {
+                // test if it claims to be a start tag but is empty
+                $token = new HTMLPurifier_Token_Empty($token->name, $token->attr);
+                $ok = true;
+            } elseif ($info->type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {
+                // claims to be empty but really is a start tag
+                $token = array(
+                    new HTMLPurifier_Token_Start($token->name, $token->attr),
+                    new HTMLPurifier_Token_End($token->name)
+                );
+                $ok = true;
+            } elseif ($token instanceof HTMLPurifier_Token_Empty) {
+                // real empty token
+                $ok = true;
+            } elseif ($token instanceof HTMLPurifier_Token_Start) {
+                // start tag
+                
+                // ...unless they also have to close their parent
+                if (!empty($this->currentNesting)) {
+                    
+                    $parent = array_pop($this->currentNesting);
+                    $parent_info = $definition->info[$parent->name];
+                    
+                    // this can be replaced with a more general algorithm:
+                    // if the token is not allowed by the parent, auto-close
+                    // the parent
+                    if (!isset($parent_info->child->elements[$token->name])) {
+                        if ($e) $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent);
+                        // close the parent, then re-loop to reprocess token
+                        $result[] = new HTMLPurifier_Token_End($parent->name);
+                        $this->inputIndex--;
+                        continue;
+                    }
+                    
+                    $this->currentNesting[] = $parent; // undo the pop
+                }
+                $ok = true;
+            }
+            
+            // injector handler code; duplicated for performance reasons
+            if ($ok) {
+                foreach ($this->injectors as $i => $injector) {
+                    if (!$injector->skip) $injector->handleElement($token);
+                    if (is_array($token)) {
+                        $this->currentInjector = $i;
+                        break;
+                    }
+                }
+                $this->processToken($token, $config, $context);
+                continue;
+            }
+            
+            // sanity check: we should be dealing with a closing tag
+            if (!$token instanceof HTMLPurifier_Token_End) continue;
+            
+            // make sure that we have something open
+            if (empty($this->currentNesting)) {
+                if ($escape_invalid_tags) {
+                    if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
+                    $result[] = new HTMLPurifier_Token_Text(
+                        $generator->generateFromToken($token, $config, $context)
+                    );
+                } elseif ($e) {
+                    $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
+                }
+                continue;
+            }
+            
+            // first, check for the simplest case: everything closes neatly
+            $current_parent = array_pop($this->currentNesting);
+            if ($current_parent->name == $token->name) {
+                $result[] = $token;
+                foreach ($this->injectors as $i => $injector) {
+                    $injector->notifyEnd($token);
+                }
+                continue;
+            }
+            
+            // okay, so we're trying to close the wrong tag
+            
+            // undo the pop previous pop
+            $this->currentNesting[] = $current_parent;
+            
+            // scroll back the entire nest, trying to find our tag.
+            // (feature could be to specify how far you'd like to go)
+            $size = count($this->currentNesting);
+            // -2 because -1 is the last element, but we already checked that
+            $skipped_tags = false;
+            for ($i = $size - 2; $i >= 0; $i--) {
+                if ($this->currentNesting[$i]->name == $token->name) {
+                    // current nesting is modified
+                    $skipped_tags = array_splice($this->currentNesting, $i);
+                    break;
+                }
+            }
+            
+            // we still didn't find the tag, so remove
+            if ($skipped_tags === false) {
+                if ($escape_invalid_tags) {
+                    $result[] = new HTMLPurifier_Token_Text(
+                        $generator->generateFromToken($token, $config, $context)
+                    );
+                    if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
+                } elseif ($e) {
+                    $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
+                }
+                continue;
+            }
+            
+            // okay, we found it, close all the skipped tags
+            // note that skipped tags contains the element we need closed
+            for ($i = count($skipped_tags) - 1; $i >= 0; $i--) {
+                // please don't redefine $i!
+                if ($i && $e && !isset($skipped_tags[$i]->armor['MakeWellFormed_TagClosedError'])) {
+                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$i]);
+                }
+                $result[] = $new_token = new HTMLPurifier_Token_End($skipped_tags[$i]->name);
+                foreach ($this->injectors as $injector) {
+                    $injector->notifyEnd($new_token);
+                }
+            }
+            
+        }
+        
+        $context->destroy('CurrentNesting');
+        $context->destroy('InputTokens');
+        $context->destroy('InputIndex');
+        $context->destroy('CurrentToken');
+        
+        // we're at the end now, fix all still unclosed tags (this is
+        // duplicated from the end of the loop with some slight modifications)
+        // not using $skipped_tags since it would invariably be all of them
+        if (!empty($this->currentNesting)) {
+            for ($i = count($this->currentNesting) - 1; $i >= 0; $i--) {
+                // please don't redefine $i!
+                if ($e && !isset($this->currentNesting[$i]->armor['MakeWellFormed_TagClosedError'])) {
+                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $this->currentNesting[$i]);
+                }
+                $result[] = $new_token = new HTMLPurifier_Token_End($this->currentNesting[$i]->name);
+                foreach ($this->injectors as $injector) {
+                    $injector->notifyEnd($new_token);
+                }
+            }
+        }
+        
+        unset($this->outputTokens, $this->injectors, $this->currentInjector,
+          $this->currentNesting, $this->inputTokens, $this->inputIndex);
+        
+        return $result;
+    }
+    
+    function processToken($token, $config, $context) {
+        if (is_array($token)) {
+            // the original token was overloaded by an injector, time
+            // to some fancy acrobatics
+            
+            // $this->inputIndex is decremented so that the entire set gets
+            // re-processed
+            array_splice($this->inputTokens, $this->inputIndex--, 1, $token);
+            
+            // adjust the injector skips based on the array substitution
+            if ($this->injectors) {
+                $offset = count($token);
+                for ($i = 0; $i <= $this->currentInjector; $i++) {
+                    // because of the skip back, we need to add one more
+                    // for uninitialized injectors. I'm not exactly
+                    // sure why this is the case, but I think it has to
+                    // do with the fact that we're decrementing skips
+                    // before re-checking text
+                    if (!$this->injectors[$i]->skip) $this->injectors[$i]->skip++;
+                    $this->injectors[$i]->skip += $offset;
+                }
+            }
+        } elseif ($token) {
+            // regular case
+            $this->outputTokens[] = $token;
+            if ($token instanceof HTMLPurifier_Token_Start) {
+                $this->currentNesting[] = $token;
+            } elseif ($token instanceof HTMLPurifier_Token_End) {
+                array_pop($this->currentNesting); // not actually used
+            }
+        }
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/ValidateAttributes.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/ValidateAttributes.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/ValidateAttributes.php (revision 21)
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Validate all attributes in the tokens.
+ */
+
+class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
+{
+    
+    public function execute($tokens, $config, $context) {
+        
+        // setup validator
+        $validator = new HTMLPurifier_AttrValidator();
+        
+        $token = false;
+        $context->register('CurrentToken', $token);
+        
+        foreach ($tokens as $key => $token) {
+            
+            // only process tokens that have attributes,
+            //   namely start and empty tags
+            if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) continue;
+            
+            // skip tokens that are armored
+            if (!empty($token->armor['ValidateAttributes'])) continue;
+            
+            // note that we have no facilities here for removing tokens
+            $validator->validateToken($token, $config, $context);
+            
+            $tokens[$key] = $token; // for PHP 4
+        }
+        $context->destroy('CurrentToken');
+        
+        return $tokens;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/Composite.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/Composite.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/Composite.php (revision 21)
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Composite strategy that runs multiple strategies on tokens.
+ */
+abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy
+{
+    
+    /**
+     * List of strategies to run tokens through.
+     */
+    protected $strategies = array();
+    
+    abstract public function __construct();
+    
+    public function execute($tokens, $config, $context) {
+        foreach ($this->strategies as $strategy) {
+            $tokens = $strategy->execute($tokens, $config, $context);
+        }
+        return $tokens;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/RemoveForeignElements.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/RemoveForeignElements.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/RemoveForeignElements.php (revision 21)
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * Removes all unrecognized tags from the list of tokens.
+ * 
+ * This strategy iterates through all the tokens and removes unrecognized
+ * tokens. If a token is not recognized but a TagTransform is defined for
+ * that element, the element will be transformed accordingly.
+ */
+
+class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
+{
+    
+    public function execute($tokens, $config, $context) {
+        $definition = $config->getHTMLDefinition();
+        $generator = new HTMLPurifier_Generator();
+        $result = array();
+        
+        $escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags');
+        $remove_invalid_img  = $config->get('Core', 'RemoveInvalidImg');
+        
+        $remove_script_contents = $config->get('Core', 'RemoveScriptContents');
+        $hidden_elements     = $config->get('Core', 'HiddenElements');
+        
+        // remove script contents compatibility
+        if ($remove_script_contents === true) {
+            $hidden_elements['script'] = true;
+        } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) {
+            unset($hidden_elements['script']);
+        }
+        
+        $attr_validator = new HTMLPurifier_AttrValidator();
+        
+        // removes tokens until it reaches a closing tag with its value
+        $remove_until = false;
+        
+        // converts comments into text tokens when this is equal to a tag name
+        $textify_comments = false;
+        
+        $token = false;
+        $context->register('CurrentToken', $token);
+        
+        $e = false;
+        if ($config->get('Core', 'CollectErrors')) {
+            $e =& $context->get('ErrorCollector');
+        }
+        
+        foreach($tokens as $token) {
+            if ($remove_until) {
+                if (empty($token->is_tag) || $token->name !== $remove_until) {
+                    continue;
+                }
+            }
+            if (!empty( $token->is_tag )) {
+                // DEFINITION CALL
+                
+                // before any processing, try to transform the element
+                if (
+                    isset($definition->info_tag_transform[$token->name])
+                ) {
+                    $original_name = $token->name;
+                    // there is a transformation for this tag
+                    // DEFINITION CALL
+                    $token = $definition->
+                                info_tag_transform[$token->name]->
+                                    transform($token, $config, $context);
+                    if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name);
+                }
+                
+                if (isset($definition->info[$token->name])) {
+                    
+                    // mostly everything's good, but
+                    // we need to make sure required attributes are in order
+                    if (
+                        ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) &&
+                        $definition->info[$token->name]->required_attr &&
+                        ($token->name != 'img' || $remove_invalid_img) // ensure config option still works
+                    ) {
+                        $attr_validator->validateToken($token, $config, $context);
+                        $ok = true;
+                        foreach ($definition->info[$token->name]->required_attr as $name) {
+                            if (!isset($token->attr[$name])) {
+                                $ok = false;
+                                break;
+                            }
+                        }
+                        if (!$ok) {
+                            if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', $name);
+                            continue;
+                        }
+                        $token->armor['ValidateAttributes'] = true;
+                    }
+                    
+                    if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) {
+                        $textify_comments = $token->name;
+                    } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) {
+                        $textify_comments = false;
+                    }
+                    
+                } elseif ($escape_invalid_tags) {
+                    // invalid tag, generate HTML representation and insert in
+                    if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
+                    $token = new HTMLPurifier_Token_Text(
+                        $generator->generateFromToken($token, $config, $context)
+                    );
+                } else {
+                    // check if we need to destroy all of the tag's children
+                    // CAN BE GENERICIZED
+                    if (isset($hidden_elements[$token->name])) {
+                        if ($token instanceof HTMLPurifier_Token_Start) {
+                            $remove_until = $token->name;
+                        } elseif ($token instanceof HTMLPurifier_Token_Empty) {
+                            // do nothing: we're still looking
+                        } else {
+                            $remove_until = false;
+                        }
+                        if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed');
+                    } else {
+                        if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
+                    }
+                    continue;
+                }
+            } elseif ($token instanceof HTMLPurifier_Token_Comment) {
+                // textify comments in script tags when they are allowed
+                if ($textify_comments !== false) {
+                    $data = $token->data;
+                    $token = new HTMLPurifier_Token_Text($data);
+                } else {
+                    // strip comments
+                    if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
+                    continue;
+                }
+            } elseif ($token instanceof HTMLPurifier_Token_Text) {
+            } else {
+                continue;
+            }
+            $result[] = $token;
+        }
+        if ($remove_until && $e) {
+            // we removed tokens until the end, throw error
+            $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until);
+        }
+        
+        $context->destroy('CurrentToken');
+        
+        return $result;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/Core.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/Core.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Strategy/Core.php (revision 21)
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * Core strategy composed of the big four strategies.
+ */
+class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite
+{
+    
+    public function __construct() {
+        $this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements();
+        $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed();
+        $this->strategies[] = new HTMLPurifier_Strategy_FixNesting();
+        $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes();
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URISchemeRegistry.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URISchemeRegistry.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URISchemeRegistry.php (revision 21)
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * Registry for retrieving specific URI scheme validator objects.
+ */
+class HTMLPurifier_URISchemeRegistry
+{
+    
+    /**
+     * Retrieve sole instance of the registry.
+     * @param $prototype Optional prototype to overload sole instance with,
+     *                   or bool true to reset to default registry.
+     * @note Pass a registry object $prototype with a compatible interface and
+     *       the function will copy it and return it all further times.
+     */
+    public static function instance($prototype = null) {
+        static $instance = null;
+        if ($prototype !== null) {
+            $instance = $prototype;
+        } elseif ($instance === null || $prototype == true) {
+            $instance = new HTMLPurifier_URISchemeRegistry();
+        }
+        return $instance;
+    }
+    
+    /**
+     * Cache of retrieved schemes.
+     */
+    protected $schemes = array();
+    
+    /**
+     * Retrieves a scheme validator object
+     * @param $scheme String scheme name like http or mailto
+     * @param $config HTMLPurifier_Config object
+     * @param $config HTMLPurifier_Context object
+     */
+    public function getScheme($scheme, $config, $context) {
+        if (!$config) $config = HTMLPurifier_Config::createDefault();
+        $null = null; // for the sake of passing by reference
+        
+        // important, otherwise attacker could include arbitrary file
+        $allowed_schemes = $config->get('URI', 'AllowedSchemes');
+        if (!$config->get('URI', 'OverrideAllowedSchemes') &&
+            !isset($allowed_schemes[$scheme])
+        ) {
+            return $null;
+        }
+        
+        if (isset($this->schemes[$scheme])) return $this->schemes[$scheme];
+        if (!isset($allowed_schemes[$scheme])) return $null;
+        
+        $class = 'HTMLPurifier_URIScheme_' . $scheme;
+        if (!class_exists($class)) return $null;
+        $this->schemes[$scheme] = new $class();
+        return $this->schemes[$scheme];
+    }
+    
+    /**
+     * Registers a custom scheme to the cache, bypassing reflection.
+     * @param $scheme Scheme name
+     * @param $scheme_obj HTMLPurifier_URIScheme object
+     */
+    public function register($scheme, $scheme_obj) {
+        $this->schemes[$scheme] = $scheme_obj;
+    }
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Language.php (revision 21)
@@ -0,0 +1,162 @@
+<?php
+
+/**
+ * Represents a language and defines localizable string formatting and
+ * other functions, as well as the localized messages for HTML Purifier.
+ */
+class HTMLPurifier_Language
+{
+    
+    /**
+     * ISO 639 language code of language. Prefers shortest possible version
+     */
+    public $code = 'en';
+    
+    /**
+     * Fallback language code
+     */
+    public $fallback = false;
+    
+    /**
+     * Array of localizable messages
+     */
+    public $messages = array();
+    
+    /**
+     * Array of localizable error codes
+     */
+    public $errorNames = array();
+    
+    /**
+     * True if no message file was found for this language, so English
+     * is being used instead. Check this if you'd like to notify the
+     * user that they've used a non-supported language.
+     */
+    public $error = false;
+    
+    /**
+     * Has the language object been loaded yet?
+     * @todo Make it private, fix usage in HTMLPurifier_LanguageTest
+     */
+    public $_loaded = false;
+    
+    /**
+     * Instances of HTMLPurifier_Config and HTMLPurifier_Context
+     */
+    protected $config, $context;
+    
+    public function __construct($config, $context) {
+        $this->config  = $config;
+        $this->context = $context;
+    }
+    
+    /**
+     * Loads language object with necessary info from factory cache
+     * @note This is a lazy loader
+     */
+    public function load() {
+        if ($this->_loaded) return;
+        $factory = HTMLPurifier_LanguageFactory::instance();
+        $factory->loadLanguage($this->code);
+        foreach ($factory->keys as $key) {
+            $this->$key = $factory->cache[$this->code][$key];
+        }
+        $this->_loaded = true;
+    }
+    
+    /**
+     * Retrieves a localised message.
+     * @param $key string identifier of message
+     * @return string localised message
+     */
+    public function getMessage($key) {
+        if (!$this->_loaded) $this->load();
+        if (!isset($this->messages[$key])) return "[$key]";
+        return $this->messages[$key];
+    }
+    
+    /**
+     * Retrieves a localised error name.
+     * @param $int integer error number, corresponding to PHP's error
+     *             reporting
+     * @return string localised message
+     */
+    public function getErrorName($int) {
+        if (!$this->_loaded) $this->load();
+        if (!isset($this->errorNames[$int])) return "[Error: $int]";
+        return $this->errorNames[$int];
+    }
+    
+    /**
+     * Converts an array list into a string readable representation
+     */
+    public function listify($array) {
+        $sep      = $this->getMessage('Item separator');
+        $sep_last = $this->getMessage('Item separator last');
+        $ret = '';
+        for ($i = 0, $c = count($array); $i < $c; $i++) {
+            if ($i == 0) {
+            } elseif ($i + 1 < $c) {
+                $ret .= $sep;
+            } else {
+                $ret .= $sep_last;
+            }
+            $ret .= $array[$i];
+        }
+        return $ret;
+    }
+    
+    /**
+     * Formats a localised message with passed parameters
+     * @param $key string identifier of message
+     * @param $args Parameters to substitute in
+     * @return string localised message
+     * @todo Implement conditionals? Right now, some messages make
+     *     reference to line numbers, but those aren't always available
+     */
+    public function formatMessage($key, $args = array()) {
+        if (!$this->_loaded) $this->load();
+        if (!isset($this->messages[$key])) return "[$key]";
+        $raw = $this->messages[$key];
+        $subst = array();
+        $generator = false;
+        foreach ($args as $i => $value) {
+            if (is_object($value)) {
+                if ($value instanceof HTMLPurifier_Token) {
+                    // factor this out some time
+                    if (!$generator) $generator = $this->context->get('Generator');
+                    if (isset($value->name)) $subst['$'.$i.'.Name'] = $value->name;
+                    if (isset($value->data)) $subst['$'.$i.'.Data'] = $value->data;
+                    $subst['$'.$i.'.Compact'] = 
+                    $subst['$'.$i.'.Serialized'] = $generator->generateFromToken($value);
+                    // a more complex algorithm for compact representation
+                    // could be introduced for all types of tokens. This
+                    // may need to be factored out into a dedicated class
+                    if (!empty($value->attr)) {
+                        $stripped_token = clone $value;
+                        $stripped_token->attr = array();
+                        $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token);
+                    }
+                    $subst['$'.$i.'.Line'] = $value->line ? $value->line : 'unknown';
+                }
+                continue;
+            } elseif (is_array($value)) {
+                $keys = array_keys($value);
+                if (array_keys($keys) === $keys) {
+                    // list
+                    $subst['$'.$i] = $this->listify($value);
+                } else {
+                    // associative array
+                    // no $i implementation yet, sorry
+                    $subst['$'.$i.'.Keys'] = $this->listify($keys);
+                    $subst['$'.$i.'.Values'] = $this->listify(array_values($value));
+                }
+                continue;
+            }
+            $subst['$' . $i] = $value;
+        }
+        return strtr($raw, $subst);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php (revision 21)
@@ -0,0 +1,13 @@
+<?php
+
+class HTMLPurifier_HTMLModule_NonXMLCommonAttributes extends HTMLPurifier_HTMLModule
+{
+    public $name = 'NonXMLCommonAttributes';
+    
+    public $attr_collections = array(
+        'Lang' => array(
+            'lang' => 'LanguageCode',
+        )
+    );
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Hypertext.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Hypertext.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Hypertext.php (revision 21)
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * XHTML 1.1 Hypertext Module, defines hypertext links. Core Module.
+ */
+class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Hypertext';
+    
+    public function __construct() {
+        $a = $this->addElement(
+            'a', 'Inline', 'Inline', 'Common',
+            array(
+                // 'accesskey' => 'Character',
+                // 'charset' => 'Charset',
+                'href' => 'URI',
+                // 'hreflang' => 'LanguageCode',
+                'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'),
+                'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'),
+                // 'tabindex' => 'Number',
+                // 'type' => 'ContentType',
+            )
+        );
+        $a->excludes = array('a' => true);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/CommonAttributes.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/CommonAttributes.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/CommonAttributes.php (revision 21)
@@ -0,0 +1,24 @@
+<?php
+
+class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
+{
+    public $name = 'CommonAttributes';
+    
+    public $attr_collections = array(
+        'Core' => array(
+            0 => array('Style'),
+            // 'xml:space' => false,
+            'class' => 'NMTOKENS',
+            'id' => 'ID',
+            'title' => 'CDATA',
+        ),
+        'Lang' => array(),
+        'I18N' => array(
+            0 => array('Lang'), // proprietary, for xml:lang/lang
+        ),
+        'Common' => array(
+            0 => array('Core', 'I18N')
+        )
+    );
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Scripting.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Scripting.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Scripting.php (revision 21)
@@ -0,0 +1,53 @@
+<?php
+
+/*
+
+WARNING: THIS MODULE IS EXTREMELY DANGEROUS AS IT ENABLES INLINE SCRIPTING
+INSIDE HTML PURIFIER DOCUMENTS. USE ONLY WITH TRUSTED USER INPUT!!!
+
+*/
+
+/**
+ * XHTML 1.1 Scripting module, defines elements that are used to contain
+ * information pertaining to executable scripts or the lack of support
+ * for executable scripts.
+ * @note This module does not contain inline scripting elements
+ */
+class HTMLPurifier_HTMLModule_Scripting extends HTMLPurifier_HTMLModule
+{
+    public $name = 'Scripting';
+    public $elements = array('script', 'noscript');
+    public $content_sets = array('Block' => 'script | noscript', 'Inline' => 'script | noscript');
+    public $safe = false;
+    
+    public function __construct() {
+        // TODO: create custom child-definition for noscript that
+        // auto-wraps stray #PCDATA in a similar manner to 
+        // blockquote's custom definition (we would use it but
+        // blockquote's contents are optional while noscript's contents
+        // are required)
+        
+        // TODO: convert this to new syntax, main problem is getting
+        // both content sets working
+        
+        // In theory, this could be safe, but I don't see any reason to
+        // allow it.
+        $this->info['noscript'] = new HTMLPurifier_ElementDef();
+        $this->info['noscript']->attr = array( 0 => array('Common') );
+        $this->info['noscript']->content_model = 'Heading | List | Block';
+        $this->info['noscript']->content_model_type = 'required';
+        
+        $this->info['script'] = new HTMLPurifier_ElementDef();
+        $this->info['script']->attr = array(
+            'defer' => new HTMLPurifier_AttrDef_Enum(array('defer')),
+            'src'   => new HTMLPurifier_AttrDef_URI(true),
+            'type'  => new HTMLPurifier_AttrDef_Enum(array('text/javascript'))
+        );
+        $this->info['script']->content_model = '#PCDATA';
+        $this->info['script']->content_model_type = 'optional';
+        $this->info['script']->attr_transform_pre['type'] =
+        $this->info['script']->attr_transform_post['type'] =
+            new HTMLPurifier_AttrTransform_ScriptRequired();
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Bdo.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Bdo.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Bdo.php (revision 21)
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * XHTML 1.1 Bi-directional Text Module, defines elements that
+ * declare directionality of content. Text Extension Module.
+ */
+class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Bdo';
+    public $attr_collections = array(
+        'I18N' => array('dir' => false)
+    );
+    
+    public function __construct() {
+        $bdo = $this->addElement(
+            'bdo', 'Inline', 'Inline', array('Core', 'Lang'),
+            array(
+                'dir' => 'Enum#ltr,rtl', // required
+                // The Abstract Module specification has the attribute
+                // inclusions wrong for bdo: bdo allows Lang
+            )
+        );
+        $bdo->attr_transform_post['required-dir'] = new HTMLPurifier_AttrTransform_BdoDir();
+        
+        $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl';
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Legacy.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Legacy.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Legacy.php (revision 21)
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * XHTML 1.1 Legacy module defines elements that were previously 
+ * deprecated.
+ * 
+ * @note Not all legacy elements have been implemented yet, which
+ *       is a bit of a reverse problem as compared to browsers! In
+ *       addition, this legacy module may implement a bit more than
+ *       mandated by XHTML 1.1.
+ * 
+ * This module can be used in combination with TransformToStrict in order
+ * to transform as many deprecated elements as possible, but retain
+ * questionably deprecated elements that do not have good alternatives
+ * as well as transform elements that don't have an implementation.
+ * See docs/ref-strictness.txt for more details.
+ */
+
+class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Legacy';
+    
+    public function __construct() {
+        
+        $this->addElement('basefont', 'Inline', 'Empty', false, array(
+            'color' => 'Color',
+            'face' => 'Text', // extremely broad, we should
+            'size' => 'Text', // tighten it
+            'id' => 'ID'
+        ));
+        $this->addElement('center', 'Block', 'Flow', 'Common');
+        $this->addElement('dir', 'Block', 'Required: li', 'Common', array(
+            'compact' => 'Bool#compact'
+        ));
+        $this->addElement('font', 'Inline', 'Inline', array('Core', 'I18N'), array(
+            'color' => 'Color',
+            'face' => 'Text', // extremely broad, we should
+            'size' => 'Text', // tighten it
+        ));
+        $this->addElement('menu', 'Block', 'Required: li', 'Common', array(
+            'compact' => 'Bool#compact'
+        ));
+        $this->addElement('s', 'Inline', 'Inline', 'Common');
+        $this->addElement('strike', 'Inline', 'Inline', 'Common');
+        $this->addElement('u', 'Inline', 'Inline', 'Common');
+        
+        // setup modifications to old elements
+        
+        $align = 'Enum#left,right,center,justify';
+        
+        $address = $this->addBlankElement('address');
+        $address->content_model = 'Inline | #PCDATA | p';
+        $address->content_model_type = 'optional';
+        $address->child = false;
+        
+        $blockquote = $this->addBlankElement('blockquote');
+        $blockquote->content_model = 'Flow | #PCDATA';
+        $blockquote->content_model_type = 'optional';
+        $blockquote->child = false;
+        
+        $br = $this->addBlankElement('br');
+        $br->attr['clear'] = 'Enum#left,all,right,none';
+        
+        $caption = $this->addBlankElement('caption');
+        $caption->attr['align'] = 'Enum#top,bottom,left,right';
+        
+        $div = $this->addBlankElement('div');
+        $div->attr['align'] = $align;
+        
+        $dl = $this->addBlankElement('dl');
+        $dl->attr['compact'] = 'Bool#compact';
+        
+        for ($i = 1; $i <= 6; $i++) {
+            $h = $this->addBlankElement("h$i");
+            $h->attr['align'] = $align;
+        }
+        
+        $hr = $this->addBlankElement('hr');
+        $hr->attr['align'] = $align;
+        $hr->attr['noshade'] = 'Bool#noshade';
+        $hr->attr['size'] = 'Pixels';
+        $hr->attr['width'] = 'Length';
+        
+        $img = $this->addBlankElement('img');
+        $img->attr['align'] = 'Enum#top,middle,bottom,left,right';
+        $img->attr['border'] = 'Pixels';
+        $img->attr['hspace'] = 'Pixels';
+        $img->attr['vspace'] = 'Pixels';
+        
+        // figure out this integer business
+        
+        $li = $this->addBlankElement('li');
+        $li->attr['value'] = new HTMLPurifier_AttrDef_Integer();
+        $li->attr['type']  = 'Enum#s:1,i,I,a,A,disc,square,circle';
+        
+        $ol = $this->addBlankElement('ol');
+        $ol->attr['compact'] = 'Bool#compact';
+        $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer();
+        $ol->attr['type'] = 'Enum#s:1,i,I,a,A';
+        
+        $p = $this->addBlankElement('p');
+        $p->attr['align'] = $align;
+        
+        $pre = $this->addBlankElement('pre');
+        $pre->attr['width'] = 'Number';
+        
+        // script omitted
+        
+        $table = $this->addBlankElement('table');
+        $table->attr['align'] = 'Enum#left,center,right';
+        $table->attr['bgcolor'] = 'Color';
+        
+        $tr = $this->addBlankElement('tr');
+        $tr->attr['bgcolor'] = 'Color';
+        
+        $th = $this->addBlankElement('th');
+        $th->attr['bgcolor'] = 'Color';
+        $th->attr['height'] = 'Length';
+        $th->attr['nowrap'] = 'Bool#nowrap';
+        $th->attr['width'] = 'Length';
+        
+        $td = $this->addBlankElement('td');
+        $td->attr['bgcolor'] = 'Color';
+        $td->attr['height'] = 'Length';
+        $td->attr['nowrap'] = 'Bool#nowrap';
+        $td->attr['width'] = 'Length';
+        
+        $ul = $this->addBlankElement('ul');
+        $ul->attr['compact'] = 'Bool#compact';
+        $ul->attr['type'] = 'Enum#square,disc,circle';
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Object.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Object.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Object.php (revision 21)
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * XHTML 1.1 Object Module, defines elements for generic object inclusion
+ * @warning Users will commonly use <embed> to cater to legacy browsers: this
+ *      module does not allow this sort of behavior
+ */
+class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Object';
+    public $safe = false;
+    
+    public function __construct() {
+        
+        $this->addElement('object', 'Inline', 'Optional: #PCDATA | Flow | param', 'Common', 
+            array(
+                'archive' => 'URI',
+                'classid' => 'URI',
+                'codebase' => 'URI',
+                'codetype' => 'Text',
+                'data' => 'URI',
+                'declare' => 'Bool#declare',
+                'height' => 'Length',
+                'name' => 'CDATA',
+                'standby' => 'Text',
+                'tabindex' => 'Number',
+                'type' => 'ContentType',
+                'width' => 'Length'
+            )
+        );
+
+        $this->addElement('param', false, 'Empty', false,
+            array(
+                'id' => 'ID',
+                'name*' => 'Text',
+                'type' => 'Text',
+                'value' => 'Text',
+                'valuetype' => 'Enum#data,ref,object'
+           )
+        );
+    
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy.php (revision 21)
@@ -0,0 +1,207 @@
+<?php
+
+/**
+ * Abstract class for a set of proprietary modules that clean up (tidy)
+ * poorly written HTML.
+ * @todo Figure out how to protect some of these methods/properties
+ */
+class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule
+{
+    
+    /**
+     * List of supported levels. Index zero is a special case "no fixes"
+     * level.
+     */
+    public $levels = array(0 => 'none', 'light', 'medium', 'heavy');
+    
+    /**
+     * Default level to place all fixes in. Disabled by default
+     */
+    public $defaultLevel = null;
+    
+    /**
+     * Lists of fixes used by getFixesForLevel(). Format is:
+     *      HTMLModule_Tidy->fixesForLevel[$level] = array('fix-1', 'fix-2');
+     */
+    public $fixesForLevel = array(
+        'light'  => array(),
+        'medium' => array(),
+        'heavy'  => array()
+    );
+    
+    /**
+     * Lazy load constructs the module by determining the necessary
+     * fixes to create and then delegating to the populate() function.
+     * @todo Wildcard matching and error reporting when an added or
+     *       subtracted fix has no effect.
+     */
+    public function construct($config) {
+        
+        // create fixes, initialize fixesForLevel
+        $fixes = $this->makeFixes();
+        $this->makeFixesForLevel($fixes);
+        
+        // figure out which fixes to use
+        $level = $config->get('HTML', 'TidyLevel');
+        $fixes_lookup = $this->getFixesForLevel($level);
+        
+        // get custom fix declarations: these need namespace processing
+        $add_fixes    = $config->get('HTML', 'TidyAdd');
+        $remove_fixes = $config->get('HTML', 'TidyRemove');
+        
+        foreach ($fixes as $name => $fix) {
+            // needs to be refactored a little to implement globbing
+            if (
+                isset($remove_fixes[$name]) ||
+                (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name]))
+            ) {
+                unset($fixes[$name]);
+            }
+        }
+        
+        // populate this module with necessary fixes
+        $this->populate($fixes);
+        
+    }
+    
+    /**
+     * Retrieves all fixes per a level, returning fixes for that specific
+     * level as well as all levels below it.
+     * @param $level String level identifier, see $levels for valid values
+     * @return Lookup up table of fixes
+     */
+    public function getFixesForLevel($level) {
+        if ($level == $this->levels[0]) {
+            return array();
+        }
+        $activated_levels = array();
+        for ($i = 1, $c = count($this->levels); $i < $c; $i++) {
+            $activated_levels[] = $this->levels[$i];
+            if ($this->levels[$i] == $level) break;
+        }
+        if ($i == $c) {
+            trigger_error(
+                'Tidy level ' . htmlspecialchars($level) . ' not recognized',
+                E_USER_WARNING
+            );
+            return array();
+        }
+        $ret = array();
+        foreach ($activated_levels as $level) {
+            foreach ($this->fixesForLevel[$level] as $fix) {
+                $ret[$fix] = true;
+            }
+        }
+        return $ret;
+    }
+    
+    /**
+     * Dynamically populates the $fixesForLevel member variable using
+     * the fixes array. It may be custom overloaded, used in conjunction
+     * with $defaultLevel, or not used at all.
+     */
+    public function makeFixesForLevel($fixes) {
+        if (!isset($this->defaultLevel)) return;
+        if (!isset($this->fixesForLevel[$this->defaultLevel])) {
+            trigger_error(
+                'Default level ' . $this->defaultLevel . ' does not exist',
+                E_USER_ERROR
+            );
+            return;
+        }
+        $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes);
+    }
+    
+    /**
+     * Populates the module with transforms and other special-case code
+     * based on a list of fixes passed to it
+     * @param $lookup Lookup table of fixes to activate
+     */
+    public function populate($fixes) {
+        foreach ($fixes as $name => $fix) {
+            // determine what the fix is for
+            list($type, $params) = $this->getFixType($name);
+            switch ($type) {
+                case 'attr_transform_pre':
+                case 'attr_transform_post':
+                    $attr = $params['attr'];
+                    if (isset($params['element'])) {
+                        $element = $params['element'];
+                        if (empty($this->info[$element])) {
+                            $e = $this->addBlankElement($element);
+                        } else {
+                            $e = $this->info[$element];
+                        }
+                    } else {
+                        $type = "info_$type";
+                        $e = $this;
+                    }
+                    // PHP does some weird parsing when I do
+                    // $e->$type[$attr], so I have to assign a ref.
+                    $f =& $e->$type;
+                    $f[$attr] = $fix;
+                    break;
+                case 'tag_transform':
+                    $this->info_tag_transform[$params['element']] = $fix;
+                    break;
+                case 'child':
+                case 'content_model_type':
+                    $element = $params['element'];
+                    if (empty($this->info[$element])) {
+                        $e = $this->addBlankElement($element);
+                    } else {
+                        $e = $this->info[$element];
+                    }
+                    $e->$type = $fix;
+                    break;
+                default:
+                    trigger_error("Fix type $type not supported", E_USER_ERROR);
+                    break;
+            }
+        }
+    }
+    
+    /**
+     * Parses a fix name and determines what kind of fix it is, as well
+     * as other information defined by the fix
+     * @param $name String name of fix
+     * @return array(string $fix_type, array $fix_parameters)
+     * @note $fix_parameters is type dependant, see populate() for usage
+     *       of these parameters
+     */
+    public function getFixType($name) {
+        // parse it
+        $property = $attr = null;
+        if (strpos($name, '#') !== false) list($name, $property) = explode('#', $name);
+        if (strpos($name, '@') !== false) list($name, $attr)     = explode('@', $name);
+        
+        // figure out the parameters
+        $params = array();
+        if ($name !== '')    $params['element'] = $name;
+        if (!is_null($attr)) $params['attr'] = $attr;
+        
+        // special case: attribute transform
+        if (!is_null($attr)) {
+            if (is_null($property)) $property = 'pre';
+            $type = 'attr_transform_' . $property;
+            return array($type, $params);
+        }
+        
+        // special case: tag transform
+        if (is_null($property)) {
+            return array('tag_transform', $params);
+        }
+        
+        return array($property, $params);
+        
+    }
+    
+    /**
+     * Defines all fixes the module will perform in a compact
+     * associative array of fix name to fix implementation.
+     */
+    public function makeFixes() {}
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tables.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tables.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tables.php (revision 21)
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * XHTML 1.1 Tables Module, fully defines accessible table elements.
+ */
+class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Tables';
+    
+    public function __construct() {
+        
+        $this->addElement('caption', false, 'Inline', 'Common');
+        
+        $this->addElement('table', 'Block', 
+            new HTMLPurifier_ChildDef_Table(),  'Common', 
+            array(
+                'border' => 'Pixels',
+                'cellpadding' => 'Length',
+                'cellspacing' => 'Length',
+                'frame' => 'Enum#void,above,below,hsides,lhs,rhs,vsides,box,border',
+                'rules' => 'Enum#none,groups,rows,cols,all',
+                'summary' => 'Text',
+                'width' => 'Length'
+            )
+        );
+        
+        // common attributes
+        $cell_align = array(
+            'align' => 'Enum#left,center,right,justify,char',
+            'charoff' => 'Length',
+            'valign' => 'Enum#top,middle,bottom,baseline',
+        );
+        
+        $cell_t = array_merge(
+            array(
+                'abbr'    => 'Text',
+                'colspan' => 'Number',
+                'rowspan' => 'Number',
+            ),
+            $cell_align
+        );
+        $this->addElement('td', false, 'Flow', 'Common', $cell_t);
+        $this->addElement('th', false, 'Flow', 'Common', $cell_t);
+        
+        $this->addElement('tr', false, 'Required: td | th', 'Common', $cell_align);
+        
+        $cell_col = array_merge(
+            array(
+                'span'  => 'Number',
+                'width' => 'MultiLength',
+            ),
+            $cell_align
+        );
+        $this->addElement('col',      false, 'Empty',         'Common', $cell_col);
+        $this->addElement('colgroup', false, 'Optional: col', 'Common', $cell_col);
+        
+        $this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align);
+        $this->addElement('thead', false, 'Required: tr', 'Common', $cell_align);
+        $this->addElement('tfoot', false, 'Required: tr', 'Common', $cell_align);
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/List.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/List.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/List.php (revision 21)
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * XHTML 1.1 List Module, defines list-oriented elements. Core Module.
+ */
+class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'List';
+    
+    // According to the abstract schema, the List content set is a fully formed
+    // one or more expr, but it invariably occurs in an optional declaration
+    // so we're not going to do that subtlety. It might cause trouble
+    // if a user defines "List" and expects that multiple lists are
+    // allowed to be specified, but then again, that's not very intuitive.
+    // Furthermore, the actual XML Schema may disagree. Regardless,
+    // we don't have support for such nested expressions without using
+    // the incredibly inefficient and draconic Custom ChildDef.
+    
+    public $content_sets = array('Flow' => 'List');
+    
+    public function __construct() {
+        $this->addElement('ol', 'List', 'Required: li', 'Common');
+        $this->addElement('ul', 'List', 'Required: li', 'Common');
+        $this->addElement('dl', 'List', 'Required: dt | dd', 'Common');
+        
+        $this->addElement('li', false, 'Flow', 'Common');
+        
+        $this->addElement('dd', false, 'Flow', 'Common');
+        $this->addElement('dt', false, 'Inline', 'Common');
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Presentation.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Presentation.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Presentation.php (revision 21)
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * XHTML 1.1 Presentation Module, defines simple presentation-related
+ * markup. Text Extension Module.
+ * @note The official XML Schema and DTD specs further divide this into
+ *       two modules:
+ *          - Block Presentation (hr)
+ *          - Inline Presentation (b, big, i, small, sub, sup, tt)
+ *       We have chosen not to heed this distinction, as content_sets
+ *       provides satisfactory disambiguation.
+ */
+class HTMLPurifier_HTMLModule_Presentation extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Presentation';
+    
+    public function __construct() {
+        $this->addElement('b',      'Inline', 'Inline', 'Common');
+        $this->addElement('big',    'Inline', 'Inline', 'Common');
+        $this->addElement('hr',     'Block',  'Empty',  'Common');
+        $this->addElement('i',      'Inline', 'Inline', 'Common');
+        $this->addElement('small',  'Inline', 'Inline', 'Common');
+        $this->addElement('sub',    'Inline', 'Inline', 'Common');
+        $this->addElement('sup',    'Inline', 'Inline', 'Common');
+        $this->addElement('tt',     'Inline', 'Inline', 'Common');
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/XMLCommonAttributes.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/XMLCommonAttributes.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/XMLCommonAttributes.php (revision 21)
@@ -0,0 +1,13 @@
+<?php
+
+class HTMLPurifier_HTMLModule_XMLCommonAttributes extends HTMLPurifier_HTMLModule
+{
+    public $name = 'XMLCommonAttributes';
+    
+    public $attr_collections = array(
+        'Lang' => array(
+            'xml:lang' => 'LanguageCode',
+        )
+    );
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Proprietary.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Proprietary.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Proprietary.php (revision 21)
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Module defines proprietary tags and attributes in HTML.
+ * @warning If this module is enabled, standards-compliance is off!
+ */
+class HTMLPurifier_HTMLModule_Proprietary extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Proprietary';
+    
+    public function __construct() {
+        
+        $this->addElement('marquee', 'Inline', 'Flow', 'Common', 
+            array(
+                'direction' => 'Enum#left,right,up,down',
+                'behavior' => 'Enum#alternate',
+                'width' => 'Length',
+                'height' => 'Length',
+                'scrolldelay' => 'Number',
+                'scrollamount' => 'Number',
+                'loop' => 'Number',
+                'bgcolor' => 'Color',
+                'hspace' => 'Pixels',
+                'vspace' => 'Pixels',
+            )
+        );
+    
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Ruby.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Ruby.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Ruby.php (revision 21)
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * XHTML 1.1 Ruby Annotation Module, defines elements that indicate
+ * short runs of text alongside base text for annotation or pronounciation.
+ */
+class HTMLPurifier_HTMLModule_Ruby extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Ruby';
+    
+    public function __construct() {
+        $this->addElement('ruby', 'Inline',
+            'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))',
+            'Common');
+        $this->addElement('rbc', false, 'Required: rb', 'Common');
+        $this->addElement('rtc', false, 'Required: rt', 'Common');
+        $rb = $this->addElement('rb', false, 'Inline', 'Common');
+        $rb->excludes = array('ruby' => true);
+        $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number'));
+        $rt->excludes = array('ruby' => true);
+        $this->addElement('rp', false, 'Optional: #PCDATA', 'Common');
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Image.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Image.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Image.php (revision 21)
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * XHTML 1.1 Image Module provides basic image embedding.
+ * @note There is specialized code for removing empty images in
+ *       HTMLPurifier_Strategy_RemoveForeignElements
+ */
+class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Image';
+    
+    public function __construct() {
+        $img = $this->addElement(
+            'img', 'Inline', 'Empty', 'Common',
+            array(
+                'alt*' => 'Text',
+                'height' => 'Length',
+                'longdesc' => 'URI', 
+                'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded
+                'width' => 'Length'
+            )
+        );
+        // kind of strange, but splitting things up would be inefficient
+        $img->attr_transform_pre[] =
+        $img->attr_transform_post[] =
+            new HTMLPurifier_AttrTransform_ImgRequired();
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/XHTML.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/XHTML.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/XHTML.php (revision 21)
@@ -0,0 +1,16 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_XHTML extends HTMLPurifier_HTMLModule_Tidy
+{
+    
+    public $name = 'Tidy_XHTML';
+    public $defaultLevel = 'medium';
+    
+    public function makeFixes() {
+        $r = array();
+        $r['@lang'] = new HTMLPurifier_AttrTransform_Lang();
+        return $r;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Proprietary.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Proprietary.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Proprietary.php (revision 21)
@@ -0,0 +1,14 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_Proprietary extends HTMLPurifier_HTMLModule_Tidy
+{
+    
+    public $name = 'Tidy_Proprietary';
+    public $defaultLevel = 'light';
+    
+    public function makeFixes() {
+        return array();
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Transitional.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Transitional.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Transitional.php (revision 21)
@@ -0,0 +1,8 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_Transitional extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
+{
+    public $name = 'Tidy_Transitional';
+    public $defaultLevel = 'heavy';
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Strict.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Strict.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/Strict.php (revision 21)
@@ -0,0 +1,19 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_Strict extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
+{
+    public $name = 'Tidy_Strict';
+    public $defaultLevel = 'light';
+    
+    public function makeFixes() {
+        $r = parent::makeFixes();
+        $r['blockquote#content_model_type'] = 'strictblockquote';
+        return $r;
+    }
+    
+    public $defines_child_def = true;
+    public function getChildDef($def) {
+        if ($def->content_model_type != 'strictblockquote') return parent::getChildDef($def);
+        return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model);
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php (revision 21)
@@ -0,0 +1,164 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 extends HTMLPurifier_HTMLModule_Tidy
+{
+    
+    public function makeFixes() {
+        
+        $r = array();
+        
+        // == deprecated tag transforms ===================================
+        
+        $r['font']   = new HTMLPurifier_TagTransform_Font();
+        $r['menu']   = new HTMLPurifier_TagTransform_Simple('ul');
+        $r['dir']    = new HTMLPurifier_TagTransform_Simple('ul');
+        $r['center'] = new HTMLPurifier_TagTransform_Simple('div',  'text-align:center;');
+        $r['u']      = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:underline;');
+        $r['s']      = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
+        $r['strike'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
+        
+        // == deprecated attribute transforms =============================
+        
+        $r['caption@align'] = 
+            new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
+                // we're following IE's behavior, not Firefox's, due
+                // to the fact that no one supports caption-side:right,
+                // W3C included (with CSS 2.1). This is a slightly
+                // unreasonable attribute!
+                'left'   => 'text-align:left;',
+                'right'  => 'text-align:right;',
+                'top'    => 'caption-side:top;',
+                'bottom' => 'caption-side:bottom;' // not supported by IE
+            ));
+        
+        // @align for img -------------------------------------------------
+        $r['img@align'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
+                'left'   => 'float:left;',
+                'right'  => 'float:right;',
+                'top'    => 'vertical-align:top;',
+                'middle' => 'vertical-align:middle;',
+                'bottom' => 'vertical-align:baseline;',
+            ));
+        
+        // @align for table -----------------------------------------------
+        $r['table@align'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
+                'left'   => 'float:left;',
+                'center' => 'margin-left:auto;margin-right:auto;',
+                'right'  => 'float:right;'
+            ));
+        
+        // @align for hr -----------------------------------------------
+        $r['hr@align'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
+                // we use both text-align and margin because these work
+                // for different browsers (IE and Firefox, respectively)
+                // and the melange makes for a pretty cross-compatible
+                // solution
+                'left'   => 'margin-left:0;margin-right:auto;text-align:left;',
+                'center' => 'margin-left:auto;margin-right:auto;text-align:center;',
+                'right'  => 'margin-left:auto;margin-right:0;text-align:right;'
+            ));
+        
+        // @align for h1, h2, h3, h4, h5, h6, p, div ----------------------
+        // {{{
+            $align_lookup = array();
+            $align_values = array('left', 'right', 'center', 'justify');
+            foreach ($align_values as $v) $align_lookup[$v] = "text-align:$v;";
+        // }}}
+        $r['h1@align'] =
+        $r['h2@align'] =
+        $r['h3@align'] =
+        $r['h4@align'] =
+        $r['h5@align'] =
+        $r['h6@align'] =
+        $r['p@align']  =
+        $r['div@align'] = 
+            new HTMLPurifier_AttrTransform_EnumToCSS('align', $align_lookup);
+        
+        // @bgcolor for table, tr, td, th ---------------------------------
+        $r['table@bgcolor'] =
+        $r['td@bgcolor'] =
+        $r['th@bgcolor'] =
+            new HTMLPurifier_AttrTransform_BgColor();
+        
+        // @border for img ------------------------------------------------
+        $r['img@border'] = new HTMLPurifier_AttrTransform_Border();
+        
+        // @clear for br --------------------------------------------------
+        $r['br@clear'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS('clear', array(
+                'left'  => 'clear:left;',
+                'right' => 'clear:right;',
+                'all'   => 'clear:both;',
+                'none'  => 'clear:none;',
+            ));
+        
+        // @height for td, th ---------------------------------------------
+        $r['td@height'] = 
+        $r['th@height'] =
+            new HTMLPurifier_AttrTransform_Length('height');
+        
+        // @hspace for img ------------------------------------------------
+        $r['img@hspace'] = new HTMLPurifier_AttrTransform_ImgSpace('hspace');
+        
+        // @name for img, a -----------------------------------------------
+        $r['img@name'] = 
+        $r['a@name'] = new HTMLPurifier_AttrTransform_Name();
+        
+        // @noshade for hr ------------------------------------------------
+        // this transformation is not precise but often good enough.
+        // different browsers use different styles to designate noshade
+        $r['hr@noshade'] =
+            new HTMLPurifier_AttrTransform_BoolToCSS(
+                'noshade',
+                'color:#808080;background-color:#808080;border:0;'
+            );
+        
+        // @nowrap for td, th ---------------------------------------------
+        $r['td@nowrap'] = 
+        $r['th@nowrap'] =
+            new HTMLPurifier_AttrTransform_BoolToCSS(
+                'nowrap',
+                'white-space:nowrap;'
+            );
+        
+        // @size for hr  --------------------------------------------------
+        $r['hr@size'] = new HTMLPurifier_AttrTransform_Length('size', 'height');
+        
+        // @type for li, ol, ul -------------------------------------------
+        // {{{
+            $ul_types = array(
+                'disc'   => 'list-style-type:disc;',
+                'square' => 'list-style-type:square;',
+                'circle' => 'list-style-type:circle;'
+            );
+            $ol_types = array(
+                '1'   => 'list-style-type:decimal;',
+                'i'   => 'list-style-type:lower-roman;',
+                'I'   => 'list-style-type:upper-roman;',
+                'a'   => 'list-style-type:lower-alpha;',
+                'A'   => 'list-style-type:upper-alpha;'
+            );
+            $li_types = $ul_types + $ol_types;
+        // }}}
+        
+        $r['ul@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ul_types);
+        $r['ol@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ol_types, true);
+        $r['li@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $li_types, true);
+        
+        // @vspace for img ------------------------------------------------
+        $r['img@vspace'] = new HTMLPurifier_AttrTransform_ImgSpace('vspace');
+        
+        // @width for hr, td, th ------------------------------------------
+        $r['td@width'] =
+        $r['th@width'] = 
+        $r['hr@width'] = new HTMLPurifier_AttrTransform_Length('width');
+        
+        return $r;
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/StyleAttribute.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/StyleAttribute.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/StyleAttribute.php (revision 21)
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
+ * Module.
+ */
+class HTMLPurifier_HTMLModule_StyleAttribute extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'StyleAttribute';
+    public $attr_collections = array(
+        // The inclusion routine differs from the Abstract Modules but
+        // is in line with the DTD and XML Schemas.
+        'Style' => array('style' => false), // see constructor
+        'Core' => array(0 => array('Style'))
+    );
+    
+    public function __construct() {
+        $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS();
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Text.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Text.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Text.php (revision 21)
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * XHTML 1.1 Text Module, defines basic text containers. Core Module.
+ * @note In the normative XML Schema specification, this module
+ *       is further abstracted into the following modules:
+ *          - Block Phrasal (address, blockquote, pre, h1, h2, h3, h4, h5, h6)
+ *          - Block Structural (div, p)
+ *          - Inline Phrasal (abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var)
+ *          - Inline Structural (br, span)
+ *       This module, functionally, does not distinguish between these
+ *       sub-modules, but the code is internally structured to reflect
+ *       these distinctions.
+ */
+class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Text';
+    public $content_sets = array(
+        'Flow' => 'Heading | Block | Inline'
+    );
+    
+    public function __construct() {
+        
+        // Inline Phrasal -------------------------------------------------
+        $this->addElement('abbr',    'Inline', 'Inline', 'Common');
+        $this->addElement('acronym', 'Inline', 'Inline', 'Common');
+        $this->addElement('cite',    'Inline', 'Inline', 'Common');
+        $this->addElement('code',    'Inline', 'Inline', 'Common');
+        $this->addElement('dfn',     'Inline', 'Inline', 'Common');
+        $this->addElement('em',      'Inline', 'Inline', 'Common');
+        $this->addElement('kbd',     'Inline', 'Inline', 'Common');
+        $this->addElement('q',       'Inline', 'Inline', 'Common', array('cite' => 'URI'));
+        $this->addElement('samp',    'Inline', 'Inline', 'Common');
+        $this->addElement('strong',  'Inline', 'Inline', 'Common');
+        $this->addElement('var',     'Inline', 'Inline', 'Common');
+        
+        // Inline Structural ----------------------------------------------
+        $this->addElement('span', 'Inline', 'Inline', 'Common');
+        $this->addElement('br',   'Inline', 'Empty',  'Core');
+        
+        // Block Phrasal --------------------------------------------------
+        $this->addElement('address',     'Block', 'Inline', 'Common');
+        $this->addElement('blockquote',  'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI') );
+        $pre = $this->addElement('pre', 'Block', 'Inline', 'Common');
+        $pre->excludes = $this->makeLookup(
+            'img', 'big', 'small', 'object', 'applet', 'font', 'basefont' );
+        $this->addElement('h1', 'Heading', 'Inline', 'Common');
+        $this->addElement('h2', 'Heading', 'Inline', 'Common');
+        $this->addElement('h3', 'Heading', 'Inline', 'Common');
+        $this->addElement('h4', 'Heading', 'Inline', 'Common');
+        $this->addElement('h5', 'Heading', 'Inline', 'Common');
+        $this->addElement('h6', 'Heading', 'Inline', 'Common');
+        
+        // Block Structural -----------------------------------------------
+        $this->addElement('p', 'Block', 'Inline', 'Common');
+        $this->addElement('div', 'Block', 'Flow', 'Common');
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Edit.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Edit.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Edit.php (revision 21)
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
+ * Module.
+ */
+class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Edit';
+    
+    public function __construct() {
+        $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow';
+        $attr = array(
+            'cite' => 'URI',
+            // 'datetime' => 'Datetime', // not implemented
+        );
+        $this->addElement('del', 'Inline', $contents, 'Common', $attr);
+        $this->addElement('ins', 'Inline', $contents, 'Common', $attr);
+    }
+    
+    // HTML 4.01 specifies that ins/del must not contain block
+    // elements when used in an inline context, chameleon is
+    // a complicated workaround to acheive this effect
+    
+    // Inline context ! Block context (exclamation mark is
+    // separator, see getChildDef for parsing)
+    
+    public $defines_child_def = true;
+    public function getChildDef($def) {
+        if ($def->content_model_type != 'chameleon') return false;
+        $value = explode('!', $def->content_model);
+        return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Target.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Target.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/HTMLModule/Target.php (revision 21)
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * XHTML 1.1 Target Module, defines target attribute in link elements.
+ */
+class HTMLPurifier_HTMLModule_Target extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'Target';
+    
+    public function __construct() {
+        $elements = array('a');
+        foreach ($elements as $name) {
+            $e = $this->addBlankElement($name);
+            $e->attr = array(
+                'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget()
+            );
+        }
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ConfigSchema.php (revision 21)
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * Configuration definition, defines directives and their defaults.
+ */
+class HTMLPurifier_ConfigSchema {
+    
+    /**
+     * Defaults of the directives and namespaces.
+     * @note This shares the exact same structure as HTMLPurifier_Config::$conf
+     */
+    public $defaults = array();
+    
+    /**
+     * Definition of the directives.
+     */
+    public $info = array();
+    
+    /**
+     * Application-wide singleton
+     */
+    static protected $singleton;
+    
+    /**
+     * Variable parser.
+     */
+    protected $parser;
+    
+    public function __construct() {
+        $this->parser = new HTMLPurifier_VarParser_Flexible();
+    }
+    
+    /**
+     * Unserializes the default ConfigSchema.
+     */
+    public static function makeFromSerial() {
+        return unserialize(file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser'));
+    }
+    
+    /**
+     * Retrieves an instance of the application-wide configuration definition.
+     */
+    public static function instance($prototype = null) {
+        if ($prototype !== null) {
+            HTMLPurifier_ConfigSchema::$singleton = $prototype;
+        } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) {
+            HTMLPurifier_ConfigSchema::$singleton = HTMLPurifier_ConfigSchema::makeFromSerial();
+        }
+        return HTMLPurifier_ConfigSchema::$singleton;
+    }
+    
+    /**
+     * Defines a directive for configuration
+     * @warning Will fail of directive's namespace is defined.
+     * @warning This method's signature is slightly different from the legacy
+     *          define() static method! Beware!
+     * @param $namespace Namespace the directive is in
+     * @param $name Key of directive
+     * @param $default Default value of directive
+     * @param $type Allowed type of the directive. See
+     *      HTMLPurifier_DirectiveDef::$type for allowed values
+     * @param $allow_null Whether or not to allow null values
+     */
+    public function add($namespace, $name, $default, $type, $allow_null) {
+        $default = $this->parser->parse($default, $type, $allow_null);
+        $this->info[$namespace][$name] = new HTMLPurifier_ConfigDef_Directive();
+        $this->info[$namespace][$name]->type = $type;
+        $this->info[$namespace][$name]->allow_null = $allow_null;
+        $this->defaults[$namespace][$name]   = $default;
+    }
+    
+    /**
+     * Defines a namespace for directives to be put into.
+     * @warning This is slightly different from the corresponding static
+     *          method.
+     * @param $namespace Namespace's name
+     */
+    public function addNamespace($namespace) {
+        $this->info[$namespace] = array();
+        $this->defaults[$namespace] = array();
+    }
+    
+    /**
+     * Defines a directive value alias.
+     * 
+     * Directive value aliases are convenient for developers because it lets
+     * them set a directive to several values and get the same result.
+     * @param $namespace Directive's namespace
+     * @param $name Name of Directive
+     * @param $aliases Hash of aliased values to the real alias
+     */
+    public function addValueAliases($namespace, $name, $aliases) {
+        foreach ($aliases as $alias => $real) {
+            $this->info[$namespace][$name]->aliases[$alias] = $real;
+        }
+    }
+    
+    /**
+     * Defines a set of allowed values for a directive.
+     * @warning This is slightly different from the corresponding static
+     *          method definition.
+     * @param $namespace Namespace of directive
+     * @param $name Name of directive
+     * @param $allowed Lookup array of allowed values
+     */
+    public function addAllowedValues($namespace, $name, $allowed) {
+        $type = $this->info[$namespace][$name]->type;
+        $this->info[$namespace][$name]->allowed = $allowed;
+    }
+    
+    /**
+     * Defines a directive alias for backwards compatibility
+     * @param $namespace
+     * @param $name Directive that will be aliased
+     * @param $new_namespace
+     * @param $new_name Directive that the alias will be to
+     */
+    public function addAlias($namespace, $name, $new_namespace, $new_name) {
+        $this->info[$namespace][$name] = new HTMLPurifier_ConfigDef_DirectiveAlias($new_namespace, $new_name);
+    }
+    
+    // DEPRECATED METHODS
+    
+    /** @see HTMLPurifier_ConfigSchema->set() */
+    public static function define($namespace, $name, $default, $type, $description) {
+        HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
+        // process modifiers (OPTIMIZE!)
+        $type_values = explode('/', $type, 2);
+        $type = $type_values[0];
+        $modifier = isset($type_values[1]) ? $type_values[1] : false;
+        $allow_null = ($modifier === 'null');
+        $def = HTMLPurifier_ConfigSchema::instance();
+        $def->add($namespace, $name, $default, $type, $allow_null);
+    }
+    
+    /** @see HTMLPurifier_ConfigSchema->addNamespace() */
+    public static function defineNamespace($namespace, $description) {
+        HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
+        $def = HTMLPurifier_ConfigSchema::instance();
+        $def->addNamespace($namespace);
+    }
+    
+    /** @see HTMLPurifier_ConfigSchema->addValueAliases() */
+    public static function defineValueAliases($namespace, $name, $aliases) {
+        HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
+        $def = HTMLPurifier_ConfigSchema::instance();
+        $def->addValueAliases($namespace, $name, $aliases);
+    }
+    
+    /** @see HTMLPurifier_ConfigSchema->addAllowedValues() */
+    public static function defineAllowedValues($namespace, $name, $allowed_values) {
+        HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
+        $allowed = array();
+        foreach ($allowed_values as $value) {
+            $allowed[$value] = true;
+        }
+        $def = HTMLPurifier_ConfigSchema::instance();
+        $def->addAllowedValues($namespace, $name, $allowed);
+    }
+    
+    /** @see HTMLPurifier_ConfigSchema->addAlias() */
+    public static function defineAlias($namespace, $name, $new_namespace, $new_name) {
+        HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
+        $def = HTMLPurifier_ConfigSchema::instance();
+        $def->addAlias($namespace, $name, $new_namespace, $new_name);
+    }
+    
+    /** @deprecated, use HTMLPurifier_VarParser->parse() */
+    public function validate($a, $b, $c = false) {
+        trigger_error("HTMLPurifier_ConfigSchema->validate deprecated, use HTMLPurifier_VarParser->parse instead", E_USER_NOTICE);
+        return $this->parser->parse($a, $b, $c);
+    }
+    
+    /**
+     * Throws an E_USER_NOTICE stating that a method is deprecated.
+     */
+    private static function deprecated($method) {
+        trigger_error("Static HTMLPurifier_ConfigSchema::$method deprecated, use add*() method instead", E_USER_NOTICE);
+    }
+    
+}
+
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/PurifierLinkify.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/PurifierLinkify.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/PurifierLinkify.php (revision 21)
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Injector that converts configuration directive syntax %Namespace.Directive
+ * to links
+ */
+class HTMLPurifier_Injector_PurifierLinkify extends HTMLPurifier_Injector
+{
+    
+    public $name = 'PurifierLinkify';
+    public $docURL;
+    public $needed = array('a' => array('href'));
+    
+    public function prepare($config, $context) {
+        $this->docURL = $config->get('AutoFormatParam', 'PurifierLinkifyDocURL');
+        return parent::prepare($config, $context);
+    }
+    
+    public function handleText(&$token) {
+        if (!$this->allowsElement('a')) return;
+        if (strpos($token->data, '%') === false) return;
+        
+        $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
+        $token = array();
+        
+        // $i = index
+        // $c = count
+        // $l = is link
+        for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
+            if (!$l) {
+                if ($bits[$i] === '') continue;
+                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
+            } else {
+                $token[] = new HTMLPurifier_Token_Start('a',
+                    array('href' => str_replace('%s', $bits[$i], $this->docURL)));
+                $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]);
+                $token[] = new HTMLPurifier_Token_End('a');
+            }
+        }
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/AutoParagraph.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/AutoParagraph.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/AutoParagraph.php (revision 21)
@@ -0,0 +1,248 @@
+<?php
+
+/**
+ * Injector that auto paragraphs text in the root node based on
+ * double-spacing.
+ */
+class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector
+{
+    
+    public $name = 'AutoParagraph';
+    public $needed = array('p');
+    
+    private function _pStart() {
+        $par = new HTMLPurifier_Token_Start('p');
+        $par->armor['MakeWellFormed_TagClosedError'] = true;
+        return $par;
+    }
+    
+    public function handleText(&$token) {
+        $text = $token->data;
+        if (empty($this->currentNesting)) {
+            if (!$this->allowsElement('p')) return;
+            // case 1: we're in root node (and it allows paragraphs)
+            $token = array($this->_pStart());
+            $this->_splitText($text, $token);
+        } elseif ($this->currentNesting[count($this->currentNesting)-1]->name == 'p') {
+            // case 2: we're in a paragraph
+            $token = array();
+            $this->_splitText($text, $token);
+        } elseif ($this->allowsElement('p')) {
+            // case 3: we're in an element that allows paragraphs
+            if (strpos($text, "\n\n") !== false) {
+                // case 3.1: this text node has a double-newline
+                $token = array($this->_pStart());
+                $this->_splitText($text, $token);
+            } else {
+                $ok = false;
+                // test if up-coming tokens are either block or have
+                // a double newline in them
+                $nesting = 0;
+                for ($i = $this->inputIndex + 1; isset($this->inputTokens[$i]); $i++) {
+                    if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_Start){
+                        if (!$this->_isInline($this->inputTokens[$i])) {
+                            // we haven't found a double-newline, and
+                            // we've hit a block element, so don't paragraph
+                            $ok = false;
+                            break;
+                        }
+                        $nesting++;
+                    }
+                    if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_End) {
+                        if ($nesting <= 0) break;
+                        $nesting--;
+                    }
+                    if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_Text) {
+                        // found it!
+                        if (strpos($this->inputTokens[$i]->data, "\n\n") !== false) {
+                            $ok = true;
+                            break;
+                        }
+                    }
+                }
+                if ($ok) {
+                    // case 3.2: this text node is next to another node
+                    // that will start a paragraph
+                    $token = array($this->_pStart(), $token);
+                }
+            }
+        }
+        
+    }
+    
+    public function handleElement(&$token) {
+        // check if we're inside a tag already
+        if (!empty($this->currentNesting)) {
+            if ($this->allowsElement('p')) {
+                // special case: we're in an element that allows paragraphs
+                
+                // this token is already paragraph, abort
+                if ($token->name == 'p') return;
+                
+                // this token is a block level, abort
+                if (!$this->_isInline($token)) return;
+                
+                // check if this token is adjacent to the parent token
+                $prev = $this->inputTokens[$this->inputIndex - 1];
+                if (!$prev instanceof HTMLPurifier_Token_Start) {
+                    // not adjacent, we can abort early
+                    // add lead paragraph tag if our token is inline
+                    // and the previous tag was an end paragraph
+                    if (
+                        $prev->name == 'p' && $prev instanceof HTMLPurifier_Token_End &&
+                        $this->_isInline($token)
+                    ) {
+                        $token = array($this->_pStart(), $token);
+                    }
+                    return;
+                }
+                
+                // this token is the first child of the element that allows
+                // paragraph. We have to peek ahead and see whether or not
+                // there is anything inside that suggests that a paragraph
+                // will be needed
+                $ok = false;
+                // maintain a mini-nesting counter, this lets us bail out
+                // early if possible
+                $j = 1; // current nesting, one is due to parent (we recalculate current token)
+                for ($i = $this->inputIndex; isset($this->inputTokens[$i]); $i++) {
+                    if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_Start) $j++;
+                    if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_End) $j--;
+                    if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_Text) {
+                        if (strpos($this->inputTokens[$i]->data, "\n\n") !== false) {
+                            $ok = true;
+                            break;
+                        }
+                    }
+                    if ($j <= 0) break;
+                }
+                if ($ok) {
+                    $token = array($this->_pStart(), $token);
+                }
+            }
+            return;
+        }
+        
+        // check if the start tag counts as a "block" element
+        if (!$this->_isInline($token)) return;
+        
+        // append a paragraph tag before the token
+        $token = array($this->_pStart(), $token);
+    }
+    
+    /**
+     * Splits up a text in paragraph tokens and appends them
+     * to the result stream that will replace the original
+     * @param $data String text data that will be processed
+     *    into paragraphs
+     * @param $result Reference to array of tokens that the
+     *    tags will be appended onto
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     */
+    private function _splitText($data, &$result) {
+        $raw_paragraphs = explode("\n\n", $data);
+        
+        // remove empty paragraphs
+        $paragraphs = array();
+        $needs_start = false;
+        $needs_end   = false;
+        
+        $c = count($raw_paragraphs);
+        if ($c == 1) {
+            // there were no double-newlines, abort quickly
+            $result[] = new HTMLPurifier_Token_Text($data);
+            return;
+        }
+        
+        for ($i = 0; $i < $c; $i++) {
+            $par = $raw_paragraphs[$i];
+            if (trim($par) !== '') {
+                $paragraphs[] = $par;
+                continue;
+            }
+            if ($i == 0 && empty($result)) {
+                // The empty result indicates that the AutoParagraph
+                // injector did not add any start paragraph tokens.
+                // The fact that the first paragraph is empty indicates
+                // that there was a double-newline at the start of the
+                // data.
+                // Combined together, this means that we are in a paragraph,
+                // and the newline means we should start a new one.
+                $result[] = new HTMLPurifier_Token_End('p');
+                // However, the start token should only be added if 
+                // there is more processing to be done (i.e. there are
+                // real paragraphs in here). If there are none, the
+                // next start paragraph tag will be handled by the
+                // next run-around the injector
+                $needs_start = true;
+            } elseif ($i + 1 == $c) {
+                // a double-paragraph at the end indicates that
+                // there is an overriding need to start a new paragraph
+                // for the next section. This has no effect until
+                // we've processed all of the other paragraphs though
+                $needs_end = true;
+            }
+        }
+        
+        // check if there are no "real" paragraphs to be processed
+        if (empty($paragraphs)) {
+            return;
+        }
+        
+        // add a start tag if an end tag was added while processing
+        // the raw paragraphs (that happens if there's a leading double
+        // newline)
+        if ($needs_start) $result[] = $this->_pStart();
+        
+        // append the paragraphs onto the result
+        foreach ($paragraphs as $par) {
+            $result[] = new HTMLPurifier_Token_Text($par);
+            $result[] = new HTMLPurifier_Token_End('p');
+            $result[] = $this->_pStart();
+        }
+        
+        // remove trailing start token, if one is needed, it will
+        // be handled the next time this injector is called
+        array_pop($result);
+        
+        // check the outside to determine whether or not the
+        // end paragraph tag should be removed. It should be removed
+        // unless the next non-whitespace token is a paragraph
+        // or a block element.
+        $remove_paragraph_end = true;
+        
+        if (!$needs_end) {
+            // Start of the checks one after the current token's index
+            for ($i = $this->inputIndex + 1; isset($this->inputTokens[$i]); $i++) {
+                if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_Start || $this->inputTokens[$i] instanceof HTMLPurifier_Token_Empty) {
+                    $remove_paragraph_end = $this->_isInline($this->inputTokens[$i]);
+                }
+                // check if we can abort early (whitespace means we carry-on!)
+                if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_Text && !$this->inputTokens[$i]->is_whitespace) break;
+                // end tags will automatically be handled by MakeWellFormed,
+                // so we don't have to worry about them
+                if ($this->inputTokens[$i] instanceof HTMLPurifier_Token_End) break;
+            }
+        } else {
+            $remove_paragraph_end = false;
+        }
+        
+        // check the outside to determine whether or not the
+        // end paragraph tag should be removed
+        if ($remove_paragraph_end) {
+            array_pop($result);
+        }
+        
+    }
+    
+    /**
+     * Returns true if passed token is inline (and, ergo, allowed in
+     * paragraph tags)
+     */
+    private function _isInline($token) {
+        return isset($this->htmlDefinition->info['p']->child->elements[$token->name]);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/Linkify.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/Linkify.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Injector/Linkify.php (revision 21)
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Injector that converts http, https and ftp text URLs to actual links.
+ */
+class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector
+{
+    
+    public $name = 'Linkify';
+    public $needed = array('a' => array('href'));
+    
+    public function handleText(&$token) {
+        if (!$this->allowsElement('a')) return;
+        
+        if (strpos($token->data, '://') === false) {
+            // our really quick heuristic failed, abort
+            // this may not work so well if we want to match things like
+            // "google.com", but then again, most people don't
+            return;
+        }
+        
+        // there is/are URL(s). Let's split the string:
+        // Note: this regex is extremely permissive
+        $bits = preg_split('#((?:https?|ftp)://[^\s\'"<>()]+)#S', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
+        
+        $token = array();
+        
+        // $i = index
+        // $c = count
+        // $l = is link
+        for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
+            if (!$l) {
+                if ($bits[$i] === '') continue;
+                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
+            } else {
+                $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i]));
+                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
+                $token[] = new HTMLPurifier_Token_End('a');
+            }
+        }
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityParser.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityParser.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/EntityParser.php (revision 21)
@@ -0,0 +1,143 @@
+<?php
+
+// if want to implement error collecting here, we'll need to use some sort
+// of global data (probably trigger_error) because it's impossible to pass
+// $config or $context to the callback functions.
+
+/**
+ * Handles referencing and derefencing character entities
+ */
+class HTMLPurifier_EntityParser
+{
+    
+    /**
+     * Reference to entity lookup table.
+     */
+    protected $_entity_lookup;
+    
+    /**
+     * Callback regex string for parsing entities.
+     */                             
+    protected $_substituteEntitiesRegex =
+'/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/';
+//     1. hex             2. dec      3. string (XML style)
+    
+    
+    /**
+     * Decimal to parsed string conversion table for special entities.
+     */
+    protected $_special_dec2str =
+            array(
+                    34 => '"',
+                    38 => '&',
+                    39 => "'",
+                    60 => '<',
+                    62 => '>'
+            );
+    
+    /**
+     * Stripped entity names to decimal conversion table for special entities.
+     */
+    protected $_special_ent2dec =
+            array(
+                    'quot' => 34,
+                    'amp'  => 38,
+                    'lt'   => 60,
+                    'gt'   => 62
+            );
+    
+    /**
+     * Substitutes non-special entities with their parsed equivalents. Since
+     * running this whenever you have parsed character is t3h 5uck, we run
+     * it before everything else.
+     * 
+     * @param $string String to have non-special entities parsed.
+     * @returns Parsed string.
+     */
+    public function substituteNonSpecialEntities($string) {
+        // it will try to detect missing semicolons, but don't rely on it
+        return preg_replace_callback(
+            $this->_substituteEntitiesRegex,
+            array($this, 'nonSpecialEntityCallback'),
+            $string
+            );
+    }
+    
+    /**
+     * Callback function for substituteNonSpecialEntities() that does the work.
+     * 
+     * @param $matches  PCRE matches array, with 0 the entire match, and
+     *                  either index 1, 2 or 3 set with a hex value, dec value,
+     *                  or string (respectively).
+     * @returns Replacement string.
+     */
+    
+    protected function nonSpecialEntityCallback($matches) {
+        // replaces all but big five
+        $entity = $matches[0];
+        $is_num = (@$matches[0][1] === '#');
+        if ($is_num) {
+            $is_hex = (@$entity[2] === 'x');
+            $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
+            
+            // abort for special characters
+            if (isset($this->_special_dec2str[$code]))  return $entity;
+            
+            return HTMLPurifier_Encoder::unichr($code);
+        } else {
+            if (isset($this->_special_ent2dec[$matches[3]])) return $entity;
+            if (!$this->_entity_lookup) {
+                $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
+            }
+            if (isset($this->_entity_lookup->table[$matches[3]])) {
+                return $this->_entity_lookup->table[$matches[3]];
+            } else {
+                return $entity;
+            }
+        }
+    }
+    
+    /**
+     * Substitutes only special entities with their parsed equivalents.
+     * 
+     * @notice We try to avoid calling this function because otherwise, it
+     * would have to be called a lot (for every parsed section).
+     * 
+     * @param $string String to have non-special entities parsed.
+     * @returns Parsed string.
+     */
+    public function substituteSpecialEntities($string) {
+        return preg_replace_callback(
+            $this->_substituteEntitiesRegex,
+            array($this, 'specialEntityCallback'),
+            $string);
+    }
+    
+    /**
+     * Callback function for substituteSpecialEntities() that does the work.
+     * 
+     * This callback has same syntax as nonSpecialEntityCallback().
+     * 
+     * @param $matches  PCRE-style matches array, with 0 the entire match, and
+     *                  either index 1, 2 or 3 set with a hex value, dec value,
+     *                  or string (respectively).
+     * @returns Replacement string.
+     */
+    protected function specialEntityCallback($matches) {
+        $entity = $matches[0];
+        $is_num = (@$matches[0][1] === '#');
+        if ($is_num) {
+            $is_hex = (@$entity[2] === 'x');
+            $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
+            return isset($this->_special_dec2str[$int]) ?
+                $this->_special_dec2str[$int] :
+                $entity;
+        } else {
+            return isset($this->_special_ent2dec[$matches[3]]) ?
+                $this->_special_ent2dec[$matches[3]] :
+                $entity;
+        }
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/IDAccumulator.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/IDAccumulator.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/IDAccumulator.php (revision 21)
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Component of HTMLPurifier_AttrContext that accumulates IDs to prevent dupes
+ * @note In Slashdot-speak, dupe means duplicate.
+ * @note The default constructor does not accept $config or $context objects:
+ *       use must use the static build() factory method to perform initialization.
+ */
+class HTMLPurifier_IDAccumulator
+{
+    
+    /**
+     * Lookup table of IDs we've accumulated.
+     * @public
+     */
+    public $ids = array();
+    
+    /**
+     * Builds an IDAccumulator, also initializing the default blacklist
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     * @return Fully initialized HTMLPurifier_IDAccumulator
+     */
+    public static function build($config, $context) {
+        $id_accumulator = new HTMLPurifier_IDAccumulator();
+        $id_accumulator->load($config->get('Attr', 'IDBlacklist'));
+        return $id_accumulator;
+    }
+    
+    /**
+     * Add an ID to the lookup table.
+     * @param $id ID to be added.
+     * @return Bool status, true if success, false if there's a dupe
+     */
+    public function add($id) {
+        if (isset($this->ids[$id])) return false;
+        return $this->ids[$id] = true;
+    }
+    
+    /**
+     * Load a list of IDs into the lookup table
+     * @param $array_of_ids Array of IDs to load
+     * @note This function doesn't care about duplicates
+     */
+    public function load($array_of_ids) {
+        foreach ($array_of_ids as $id) {
+            $this->ids[$id] = true;
+        }
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Exception.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Exception.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Exception.php (revision 21)
@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * Global exception class for HTML Purifier; any exceptions we throw
+ * are from here.
+ */
+class HTMLPurifier_Exception extends Exception
+{
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ErrorCollector.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ErrorCollector.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/ErrorCollector.php (revision 21)
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * Error collection class that enables HTML Purifier to report HTML
+ * problems back to the user
+ */
+class HTMLPurifier_ErrorCollector
+{
+    
+    protected $errors = array();
+    protected $locale;
+    protected $generator;
+    protected $context;
+    
+    public function __construct($context) {
+        $this->locale    =& $context->get('Locale');
+        $this->generator =& $context->get('Generator');
+        $this->context   = $context;
+    }
+    
+    /**
+     * Sends an error message to the collector for later use
+     * @param $line Integer line number, or HTMLPurifier_Token that caused error
+     * @param $severity int Error severity, PHP error style (don't use E_USER_)
+     * @param $msg string Error message text
+     */
+    public function send($severity, $msg) {
+        
+        $args = array();
+        if (func_num_args() > 2) {
+            $args = func_get_args();
+            array_shift($args);
+            unset($args[0]);
+        }
+        
+        $token = $this->context->get('CurrentToken', true);
+        $line  = $token ? $token->line : $this->context->get('CurrentLine', true);
+        $attr  = $this->context->get('CurrentAttr', true);
+        
+        // perform special substitutions, also add custom parameters
+        $subst = array();
+        if (!is_null($token)) {
+            $args['CurrentToken'] = $token;
+        }
+        if (!is_null($attr)) {
+            $subst['$CurrentAttr.Name'] = $attr;
+            if (isset($token->attr[$attr])) $subst['$CurrentAttr.Value'] = $token->attr[$attr];
+        }
+        
+        if (empty($args)) {
+            $msg = $this->locale->getMessage($msg);
+        } else {
+            $msg = $this->locale->formatMessage($msg, $args);
+        }
+        
+        if (!empty($subst)) $msg = strtr($msg, $subst);
+        
+        $this->errors[] = array($line, $severity, $msg);
+    }
+    
+    /**
+     * Retrieves raw error data for custom formatter to use
+     * @param List of arrays in format of array(Error message text,
+     *        token that caused error, tokens surrounding token)
+     */
+    public function getRaw() {
+        return $this->errors;
+    }
+    
+    /**
+     * Default HTML formatting implementation for error messages
+     * @param $config Configuration array, vital for HTML output nature
+     */
+    public function getHTMLFormatted($config) {
+        $ret = array();
+        
+        $errors = $this->errors;
+        
+        // sort error array by line
+        // line numbers are enabled if they aren't explicitly disabled
+        if ($config->get('Core', 'MaintainLineNumbers') !== false) {
+            $has_line       = array();
+            $lines          = array();
+            $original_order = array();
+            foreach ($errors as $i => $error) {
+                $has_line[] = (int) (bool) $error[0];
+                $lines[] = $error[0];
+                $original_order[] = $i;
+            }
+            array_multisort($has_line, SORT_DESC, $lines, SORT_ASC, $original_order, SORT_ASC, $errors);
+        }
+        
+        foreach ($errors as $error) {
+            list($line, $severity, $msg) = $error;
+            $string = '';
+            $string .= '<strong>' . $this->locale->getErrorName($severity) . '</strong>: ';
+            $string .= $this->generator->escape($msg); 
+            if ($line) {
+                // have javascript link generation that causes 
+                // textarea to skip to the specified line
+                $string .= $this->locale->formatMessage(
+                    'ErrorCollector: At line', array('line' => $line));
+            }
+            $ret[] = $string;
+        }
+        
+        if (empty($errors)) {
+            return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>';
+        } else {
+            return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>';
+        }
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/CSSDefinition.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/CSSDefinition.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/CSSDefinition.php (revision 21)
@@ -0,0 +1,278 @@
+<?php
+
+/**
+ * Defines allowed CSS attributes and what their values are.
+ * @see HTMLPurifier_HTMLDefinition
+ */
+class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
+{
+    
+    public $type = 'CSS';
+    
+    /**
+     * Assoc array of attribute name to definition object.
+     */
+    public $info = array();
+    
+    /**
+     * Constructs the info array.  The meat of this class.
+     */
+    protected function doSetup($config) {
+        
+        $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
+            array('left', 'right', 'center', 'justify'), false);
+        
+        $border_style =
+        $this->info['border-bottom-style'] = 
+        $this->info['border-right-style'] = 
+        $this->info['border-left-style'] = 
+        $this->info['border-top-style'] =  new HTMLPurifier_AttrDef_Enum(
+            array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
+            'groove', 'ridge', 'inset', 'outset'), false);
+        
+        $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
+        
+        $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
+            array('none', 'left', 'right', 'both'), false);
+        $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
+            array('none', 'left', 'right'), false);
+        $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
+            array('normal', 'italic', 'oblique'), false);
+        $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
+            array('normal', 'small-caps'), false);
+        
+        $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_Enum(array('none')),
+                new HTMLPurifier_AttrDef_CSS_URI()
+            )
+        );
+        
+        $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
+            array('inside', 'outside'), false);
+        $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
+            array('disc', 'circle', 'square', 'decimal', 'lower-roman',
+            'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
+        $this->info['list-style-image'] = $uri_or_none;
+        
+        $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
+        
+        $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
+            array('capitalize', 'uppercase', 'lowercase', 'none'), false);
+        $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
+        
+        $this->info['background-image'] = $uri_or_none;
+        $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
+            array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
+        );
+        $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
+            array('scroll', 'fixed')
+        );
+        $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
+        
+        $border_color = 
+        $this->info['border-top-color'] = 
+        $this->info['border-bottom-color'] = 
+        $this->info['border-left-color'] = 
+        $this->info['border-right-color'] = 
+        $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_Enum(array('transparent')),
+            new HTMLPurifier_AttrDef_CSS_Color()
+        ));
+        
+        $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
+        
+        $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
+        
+        $border_width = 
+        $this->info['border-top-width'] = 
+        $this->info['border-bottom-width'] = 
+        $this->info['border-left-width'] = 
+        $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
+            new HTMLPurifier_AttrDef_CSS_Length(true) //disallow negative
+        ));
+        
+        $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
+        
+        $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_Enum(array('normal')),
+            new HTMLPurifier_AttrDef_CSS_Length()
+        ));
+        
+        $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_Enum(array('normal')),
+            new HTMLPurifier_AttrDef_CSS_Length()
+        ));
+        
+        $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small',
+                'small', 'medium', 'large', 'x-large', 'xx-large',
+                'larger', 'smaller')),
+            new HTMLPurifier_AttrDef_CSS_Percentage(),
+            new HTMLPurifier_AttrDef_CSS_Length()
+        ));
+        
+        $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_Enum(array('normal')),
+            new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
+            new HTMLPurifier_AttrDef_CSS_Length(true),
+            new HTMLPurifier_AttrDef_CSS_Percentage(true)
+        ));
+        
+        $margin =
+        $this->info['margin-top'] = 
+        $this->info['margin-bottom'] = 
+        $this->info['margin-left'] = 
+        $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_CSS_Length(),
+            new HTMLPurifier_AttrDef_CSS_Percentage(),
+            new HTMLPurifier_AttrDef_Enum(array('auto'))
+        ));
+        
+        $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
+        
+        // non-negative
+        $padding =
+        $this->info['padding-top'] = 
+        $this->info['padding-bottom'] = 
+        $this->info['padding-left'] = 
+        $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_CSS_Length(true),
+            new HTMLPurifier_AttrDef_CSS_Percentage(true)
+        ));
+        
+        $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
+        
+        $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_CSS_Length(),
+            new HTMLPurifier_AttrDef_CSS_Percentage()
+        ));
+        
+        $this->info['width'] =
+        $this->info['height'] =
+        new HTMLPurifier_AttrDef_CSS_DenyElementDecorator(
+        new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_CSS_Length(true),
+            new HTMLPurifier_AttrDef_CSS_Percentage(true),
+            new HTMLPurifier_AttrDef_Enum(array('auto'))
+        )), 'img');
+        
+        $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
+        
+        $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
+        
+        // this could use specialized code
+        $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
+            array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300',
+            '400', '500', '600', '700', '800', '900'), false);
+        
+        // MUST be called after other font properties, as it references
+        // a CSSDefinition object
+        $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
+        
+        // same here
+        $this->info['border'] =
+        $this->info['border-bottom'] = 
+        $this->info['border-top'] = 
+        $this->info['border-left'] = 
+        $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
+        
+        $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
+            'collapse', 'separate'));
+        
+        $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array(
+            'top', 'bottom'));
+        
+        $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
+            'auto', 'fixed'));
+        
+        $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
+                'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
+            new HTMLPurifier_AttrDef_CSS_Length(),
+            new HTMLPurifier_AttrDef_CSS_Percentage()
+        ));
+        
+        $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
+        
+        // partial support
+        $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
+        
+        if ($config->get('CSS', 'Proprietary')) {
+            $this->doSetupProprietary($config);
+        }
+        
+        if ($config->get('CSS', 'AllowTricky')) {
+            $this->doSetupTricky($config);
+        }
+        
+        $allow_important = $config->get('CSS', 'AllowImportant');
+        // wrap all attr-defs with decorator that handles !important
+        foreach ($this->info as $k => $v) {
+            $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
+        }
+        
+        $this->setupConfigStuff($config);
+    }
+    
+    protected function doSetupProprietary($config) {
+        // Internet Explorer only scrollbar colors
+        $this->info['scrollbar-arrow-color']        = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-base-color']         = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-darkshadow-color']   = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-face-color']         = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-highlight-color']    = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-shadow-color']       = new HTMLPurifier_AttrDef_CSS_Color();
+        
+        // technically not proprietary, but CSS3, and no one supports it
+        $this->info['opacity']          = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+        $this->info['-moz-opacity']     = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+        $this->info['-khtml-opacity']   = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+        
+        // only opacity, for now
+        $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
+        
+    }
+    
+    protected function doSetupTricky($config) {
+        $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array(
+            'inline', 'block', 'list-item', 'run-in', 'compact',
+            'marker', 'table', 'inline-table', 'table-row-group',
+            'table-header-group', 'table-footer-group', 'table-row',
+            'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none'
+        ));
+        $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array(
+            'visible', 'hidden', 'collapse'
+        ));
+    }
+    
+    
+    /**
+     * Performs extra config-based processing. Based off of
+     * HTMLPurifier_HTMLDefinition.
+     * @todo Refactor duplicate elements into common class (probably using
+     *       composition, not inheritance).
+     */
+    protected function setupConfigStuff($config) {
+        
+        // setup allowed elements
+        $support = "(for information on implementing this, see the ".
+                   "support forums) ";
+        $allowed_attributes = $config->get('CSS', 'AllowedProperties');
+        if ($allowed_attributes !== null) {
+            foreach ($this->info as $name => $d) {
+                if(!isset($allowed_attributes[$name])) unset($this->info[$name]);
+                unset($allowed_attributes[$name]);
+            }
+            // emit errors
+            foreach ($allowed_attributes as $name => $d) {
+                // :TODO: Is this htmlspecialchars() call really necessary?
+                $name = htmlspecialchars($name);
+                trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
+            }
+        }
+        
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/URIFilter.php (revision 21)
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Chainable filters for custom URI processing.
+ * 
+ * These filters can perform custom actions on a URI filter object,
+ * including transformation or blacklisting.
+ * 
+ * @warning This filter is called before scheme object validation occurs.
+ *          Make sure, if you require a specific scheme object, you
+ *          you check that it exists. This allows filters to convert
+ *          proprietary URI schemes into regular ones.
+ */
+abstract class HTMLPurifier_URIFilter
+{
+    
+    /**
+     * Unique identifier of filter
+     */
+    public $name;
+    
+    /**
+     * Performs initialization for the filter
+     */
+    public function prepare($config) {}
+    
+    /**
+     * Filter a URI object
+     * @param $uri Reference to URI object variable
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     * @return bool Whether or not to continue processing: false indicates
+     *         URL is no good, true indicates continue processing. Note that
+     *         all changes are committed directly on the URI object
+     */
+    abstract public function filter(&$uri, $config, $context);
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrCollections.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrCollections.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrCollections.php (revision 21)
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * Defines common attribute collections that modules reference
+ */
+
+class HTMLPurifier_AttrCollections
+{
+    
+    /**
+     * Associative array of attribute collections, indexed by name
+     */
+    public $info = array();
+    
+    /**
+     * Performs all expansions on internal data for use by other inclusions
+     * It also collects all attribute collection extensions from
+     * modules
+     * @param $attr_types HTMLPurifier_AttrTypes instance
+     * @param $modules Hash array of HTMLPurifier_HTMLModule members
+     */
+    public function __construct($attr_types, $modules) {
+        // load extensions from the modules
+        foreach ($modules as $module) {
+            foreach ($module->attr_collections as $coll_i => $coll) {
+                if (!isset($this->info[$coll_i])) {
+                    $this->info[$coll_i] = array();
+                }
+                foreach ($coll as $attr_i => $attr) {
+                    if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
+                        // merge in includes
+                        $this->info[$coll_i][$attr_i] = array_merge(
+                            $this->info[$coll_i][$attr_i], $attr);
+                        continue;
+                    }
+                    $this->info[$coll_i][$attr_i] = $attr;
+                }
+            }
+        }
+        // perform internal expansions and inclusions
+        foreach ($this->info as $name => $attr) {
+            // merge attribute collections that include others
+            $this->performInclusions($this->info[$name]);
+            // replace string identifiers with actual attribute objects
+            $this->expandIdentifiers($this->info[$name], $attr_types);
+        }
+    }
+    
+    /**
+     * Takes a reference to an attribute associative array and performs
+     * all inclusions specified by the zero index.
+     * @param &$attr Reference to attribute array
+     */
+    public function performInclusions(&$attr) {
+        if (!isset($attr[0])) return;
+        $merge = $attr[0];
+        $seen  = array(); // recursion guard
+        // loop through all the inclusions
+        for ($i = 0; isset($merge[$i]); $i++) {
+            if (isset($seen[$merge[$i]])) continue;
+            $seen[$merge[$i]] = true;
+            // foreach attribute of the inclusion, copy it over
+            if (!isset($this->info[$merge[$i]])) continue;
+            foreach ($this->info[$merge[$i]] as $key => $value) {
+                if (isset($attr[$key])) continue; // also catches more inclusions
+                $attr[$key] = $value;
+            }
+            if (isset($this->info[$merge[$i]][0])) {
+                // recursion
+                $merge = array_merge($merge, $this->info[$merge[$i]][0]);
+            }
+        }
+        unset($attr[0]);
+    }
+    
+    /**
+     * Expands all string identifiers in an attribute array by replacing
+     * them with the appropriate values inside HTMLPurifier_AttrTypes
+     * @param &$attr Reference to attribute array
+     * @param $attr_types HTMLPurifier_AttrTypes instance
+     */
+    public function expandIdentifiers(&$attr, $attr_types) {
+        
+        // because foreach will process new elements we add, make sure we
+        // skip duplicates
+        $processed = array();
+        
+        foreach ($attr as $def_i => $def) {
+            // skip inclusions
+            if ($def_i === 0) continue;
+            
+            if (isset($processed[$def_i])) continue;
+            
+            // determine whether or not attribute is required
+            if ($required = (strpos($def_i, '*') !== false)) {
+                // rename the definition
+                unset($attr[$def_i]);
+                $def_i = trim($def_i, '*');
+                $attr[$def_i] = $def;
+            }
+            
+            $processed[$def_i] = true;
+            
+            // if we've already got a literal object, move on
+            if (is_object($def)) {
+                // preserve previous required
+                $attr[$def_i]->required = ($required || $attr[$def_i]->required);
+                continue;
+            }
+            
+            if ($def === false) {
+                unset($attr[$def_i]);
+                continue;
+            }
+            
+            if ($t = $attr_types->get($def)) {
+                $attr[$def_i] = $t;
+                $attr[$def_i]->required = $required;
+            } else {
+                unset($attr[$def_i]);
+            }
+        }
+        
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/StringHash.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/StringHash.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/StringHash.php (revision 21)
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * This is in almost every respect equivalent to an array except
+ * that it keeps track of which keys were accessed.
+ *
+ * @warning For the sake of backwards compatibility with early versions
+ *     of PHP 5, you must not use the $hash[$key] syntax; if you do
+ *     our version of offsetGet is never called.
+ */
+class HTMLPurifier_StringHash extends ArrayObject
+{
+    protected $accessed = array();
+    
+    /**
+     * Retrieves a value, and logs the access.
+     */
+    public function offsetGet($index) {
+        $this->accessed[$index] = true;
+        return parent::offsetGet($index);
+    }
+    
+    /**
+     * Returns a lookup array of all array indexes that have been accessed.
+     * @return Array in form array($index => true).
+     */
+    public function getAccessed() {
+        return $this->accessed;
+    }
+    
+    /**
+     * Resets the access array.
+     */
+    public function resetAccessed() {
+        $this->accessed = array();
+    }
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Bootstrap.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Bootstrap.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Bootstrap.php (revision 21)
@@ -0,0 +1,96 @@
+<?php
+
+// constants are slow, so we use as few as possible
+if (!defined('HTMLPURIFIER_PREFIX')) {
+    define('HTMLPURIFIER_PREFIX', realpath(dirname(__FILE__) . '/..'));
+}
+
+// accomodations for versions earlier than 5.0.2
+// borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
+if (!defined('PHP_EOL')) {
+    switch (strtoupper(substr(PHP_OS, 0, 3))) {
+        case 'WIN':
+            define('PHP_EOL', "\r\n");
+            break;
+        case 'DAR':
+            define('PHP_EOL', "\r");
+            break;
+        default:
+            define('PHP_EOL', "\n");
+    }
+}
+
+/**
+ * Bootstrap class that contains meta-functionality for HTML Purifier such as
+ * the autoload function.
+ *
+ * @note
+ *      This class may be used without any other files from HTML Purifier.
+ */
+class HTMLPurifier_Bootstrap
+{
+    
+    /**
+     * Autoload function for HTML Purifier
+     * @param $class Class to load
+     */
+    public static function autoload($class) {
+        $file = HTMLPurifier_Bootstrap::getPath($class);
+        if (!$file) return false;
+        require HTMLPURIFIER_PREFIX . '/' . $file;
+        return true;
+    }
+    
+    /**
+     * Returns the path for a specific class.
+     */
+    public static function getPath($class) {
+        if (strncmp('HTMLPurifier', $class, 12) !== 0) return false;
+        // Custom implementations
+        if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
+            $code = str_replace('_', '-', substr($class, 22));
+            $file = 'HTMLPurifier/Language/classes/' . $code . '.php';
+        } else {
+            $file = str_replace('_', '/', $class) . '.php';
+        }
+        if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false;
+        return $file;
+    }
+    
+    /**
+     * "Pre-registers" our autoloader on the SPL stack.
+     */
+    public static function registerAutoload() {
+        $autoload = array('HTMLPurifier_Bootstrap', 'autoload');
+        if ( ($funcs = spl_autoload_functions()) === false ) {
+            spl_autoload_register($autoload);
+        } elseif (function_exists('spl_autoload_unregister')) {
+            $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
+                      version_compare(PHP_VERSION, '5.1.0', '>=');
+            foreach ($funcs as $func) {
+                if (is_array($func)) {
+                    // :TRICKY: There are some compatibility issues and some
+                    // places where we need to error out
+                    $reflector = new ReflectionMethod($func[0], $func[1]);
+                    if (!$reflector->isStatic()) {
+                        throw new Exception('
+                            HTML Purifier autoloader registrar is not compatible
+                            with non-static object methods due to PHP Bug #44144;
+                            Please do not use HTMLPurifier.autoload.php (or any
+                            file that includes this file); instead, place the code:
+                            spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
+                            after your own autoloaders.
+                        ');
+                    }
+                    // Suprisingly, spl_autoload_register supports the
+                    // Class::staticMethod callback format, although call_user_func doesn't
+                    if ($compat) $func = implode('::', $func);
+                }
+                spl_autoload_unregister($func);
+            }
+            spl_autoload_register($autoload);
+            foreach ($funcs as $func) spl_autoload_register($func);
+        }
+    }
+    
+}
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrValidator.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrValidator.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/AttrValidator.php (revision 21)
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * Validates the attributes of a token. Doesn't manage required attributes
+ * very well. The only reason we factored this out was because RemoveForeignElements
+ * also needed it besides ValidateAttributes.
+ */
+class HTMLPurifier_AttrValidator
+{
+    
+    /**
+     * Validates the attributes of a token, returning a modified token
+     * that has valid tokens
+     * @param $token Reference to token to validate. We require a reference
+     *     because the operation this class performs on the token are
+     *     not atomic, so the context CurrentToken to be updated
+     *     throughout
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     */
+    public function validateToken(&$token, &$config, $context) {
+            
+        $definition = $config->getHTMLDefinition();
+        $e =& $context->get('ErrorCollector', true);
+        
+        // initialize IDAccumulator if necessary
+        $ok =& $context->get('IDAccumulator', true);
+        if (!$ok) {
+            $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
+            $context->register('IDAccumulator', $id_accumulator);
+        }
+        
+        // initialize CurrentToken if necessary
+        $current_token =& $context->get('CurrentToken', true);
+        if (!$current_token) $context->register('CurrentToken', $token);
+        
+        if (
+          !$token instanceof HTMLPurifier_Token_Start &&
+          !$token instanceof HTMLPurifier_Token_Empty
+        ) return $token;
+        
+        // create alias to global definition array, see also $defs
+        // DEFINITION CALL
+        $d_defs = $definition->info_global_attr;
+        
+        // reference attributes for easy manipulation
+        $attr =& $token->attr;
+        
+        // do global transformations (pre)
+        // nothing currently utilizes this
+        foreach ($definition->info_attr_transform_pre as $transform) {
+            $attr = $transform->transform($o = $attr, $config, $context);
+            if ($e && ($attr != $o)) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
+        }
+        
+        // do local transformations only applicable to this element (pre)
+        // ex. <p align="right"> to <p style="text-align:right;">
+        foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
+            $attr = $transform->transform($o = $attr, $config, $context);
+            if ($e && ($attr != $o)) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
+        }
+        
+        // create alias to this element's attribute definition array, see
+        // also $d_defs (global attribute definition array)
+        // DEFINITION CALL
+        $defs = $definition->info[$token->name]->attr;
+        
+        $attr_key = false;
+        $context->register('CurrentAttr', $attr_key);
+        
+        // iterate through all the attribute keypairs
+        // Watch out for name collisions: $key has previously been used
+        foreach ($attr as $attr_key => $value) {
+            
+            // call the definition
+            if ( isset($defs[$attr_key]) ) {
+                // there is a local definition defined
+                if ($defs[$attr_key] === false) {
+                    // We've explicitly been told not to allow this element.
+                    // This is usually when there's a global definition
+                    // that must be overridden.
+                    // Theoretically speaking, we could have a
+                    // AttrDef_DenyAll, but this is faster!
+                    $result = false;
+                } else {
+                    // validate according to the element's definition
+                    $result = $defs[$attr_key]->validate(
+                                    $value, $config, $context
+                               );
+                }
+            } elseif ( isset($d_defs[$attr_key]) ) {
+                // there is a global definition defined, validate according
+                // to the global definition
+                $result = $d_defs[$attr_key]->validate(
+                                $value, $config, $context
+                           );
+            } else {
+                // system never heard of the attribute? DELETE!
+                $result = false;
+            }
+            
+            // put the results into effect
+            if ($result === false || $result === null) {
+                // this is a generic error message that should replaced
+                // with more specific ones when possible
+                if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed');
+                
+                // remove the attribute
+                unset($attr[$attr_key]);
+            } elseif (is_string($result)) {
+                // generally, if a substitution is happening, there
+                // was some sort of implicit correction going on. We'll
+                // delegate it to the attribute classes to say exactly what.
+                
+                // simple substitution
+                $attr[$attr_key] = $result;
+            }
+            
+            // we'd also want slightly more complicated substitution
+            // involving an array as the return value,
+            // although we're not sure how colliding attributes would
+            // resolve (certain ones would be completely overriden,
+            // others would prepend themselves).
+        }
+        
+        $context->destroy('CurrentAttr');
+        
+        // post transforms
+        
+        // global (error reporting untested)
+        foreach ($definition->info_attr_transform_post as $transform) {
+            $attr = $transform->transform($o = $attr, $config, $context);
+            if ($e && ($attr != $o)) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
+        }
+        
+        // local (error reporting untested)
+        foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
+            $attr = $transform->transform($o = $attr, $config, $context);
+            if ($e && ($attr != $o)) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
+        }
+        
+        // destroy CurrentToken if we made it ourselves
+        if (!$current_token) $context->destroy('CurrentToken');
+        
+    }
+    
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform/Simple.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform/Simple.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform/Simple.php (revision 21)
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Simple transformation, just change tag name to something else,
+ * and possibly add some styling. This will cover most of the deprecated
+ * tag cases.
+ */
+class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
+{
+    
+    protected $style;
+    
+    /**
+     * @param $transform_to Tag name to transform to.
+     * @param $style CSS style to add to the tag
+     */
+    public function __construct($transform_to, $style = null) {
+        $this->transform_to = $transform_to;
+        $this->style = $style;
+    }
+    
+    public function transform($tag, $config, $context) {
+        $new_tag = clone $tag;
+        $new_tag->name = $this->transform_to;
+        if (!is_null($this->style) &&
+            ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty)
+        ) {
+            $this->prependCSS($new_tag->attr, $this->style);
+        }
+        return $new_tag;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform/Font.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform/Font.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/TagTransform/Font.php (revision 21)
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * Transforms FONT tags to the proper form (SPAN with CSS styling)
+ * 
+ * This transformation takes the three proprietary attributes of FONT and
+ * transforms them into their corresponding CSS attributes.  These are color,
+ * face, and size.
+ * 
+ * @note Size is an interesting case because it doesn't map cleanly to CSS.
+ *       Thanks to
+ *       http://style.cleverchimp.com/font_size_intervals/altintervals.html
+ *       for reasonable mappings.
+ */
+class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
+{
+    
+    public $transform_to = 'span';
+    
+    protected $_size_lookup = array(
+        '0' => 'xx-small',
+        '1' => 'xx-small',
+        '2' => 'small',
+        '3' => 'medium',
+        '4' => 'large',
+        '5' => 'x-large',
+        '6' => 'xx-large',
+        '7' => '300%',
+        '-1' => 'smaller',
+        '-2' => '60%',
+        '+1' => 'larger',
+        '+2' => '150%',
+        '+3' => '200%',
+        '+4' => '300%'
+    );
+    
+    public function transform($tag, $config, $context) {
+        
+        if ($tag instanceof HTMLPurifier_Token_End) {
+            $new_tag = clone $tag;
+            $new_tag->name = $this->transform_to;
+            return $new_tag;
+        }
+        
+        $attr = $tag->attr;
+        $prepend_style = '';
+        
+        // handle color transform
+        if (isset($attr['color'])) {
+            $prepend_style .= 'color:' . $attr['color'] . ';';
+            unset($attr['color']);
+        }
+        
+        // handle face transform
+        if (isset($attr['face'])) {
+            $prepend_style .= 'font-family:' . $attr['face'] . ';';
+            unset($attr['face']);
+        }
+        
+        // handle size transform
+        if (isset($attr['size'])) {
+            // normalize large numbers
+            if ($attr['size']{0} == '+' || $attr['size']{0} == '-') {
+                $size = (int) $attr['size'];
+                if ($size < -2) $attr['size'] = '-2';
+                if ($size > 4)  $attr['size'] = '+4';
+            } else {
+                $size = (int) $attr['size'];
+                if ($size > 7) $attr['size'] = '7';
+            }
+            if (isset($this->_size_lookup[$attr['size']])) {
+                $prepend_style .= 'font-size:' .
+                  $this->_size_lookup[$attr['size']] . ';';
+            }
+            unset($attr['size']);
+        }
+        
+        if ($prepend_style) {
+            $attr['style'] = isset($attr['style']) ?
+                $prepend_style . $attr['style'] :
+                $prepend_style;
+        }
+        
+        $new_tag = clone $tag;
+        $new_tag->name = $this->transform_to;
+        $new_tag->attr = $attr;
+        
+        return $new_tag;
+        
+    }
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Context.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Context.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Context.php (revision 21)
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * Registry object that contains information about the current context.
+ * @warning Is a bit buggy when variables are set to null: it thinks
+ *          they don't exist! So use false instead, please.
+ * @note Since the variables Context deals with may not be objects,
+ *       references are very important here! Do not remove!
+ */
+class HTMLPurifier_Context
+{
+    
+    /**
+     * Private array that stores the references.
+     */
+    private $_storage = array();
+    
+    /**
+     * Registers a variable into the context.
+     * @param $name String name
+     * @param $ref Reference to variable to be registered
+     */
+    public function register($name, &$ref) {
+        if (isset($this->_storage[$name])) {
+            trigger_error("Name $name produces collision, cannot re-register",
+                          E_USER_ERROR);
+            return;
+        }
+        $this->_storage[$name] =& $ref;
+    }
+    
+    /**
+     * Retrieves a variable reference from the context.
+     * @param $name String name
+     * @param $ignore_error Boolean whether or not to ignore error
+     */
+    public function &get($name, $ignore_error = false) {
+        if (!isset($this->_storage[$name])) {
+            if (!$ignore_error) {
+                trigger_error("Attempted to retrieve non-existent variable $name",
+                              E_USER_ERROR);
+            }
+            $var = null; // so we can return by reference
+            return $var;
+        }
+        return $this->_storage[$name];
+    }
+    
+    /**
+     * Destorys a variable in the context.
+     * @param $name String name
+     */
+    public function destroy($name) {
+        if (!isset($this->_storage[$name])) {
+            trigger_error("Attempted to destroy non-existent variable $name",
+                          E_USER_ERROR);
+            return;
+        }
+        unset($this->_storage[$name]);
+    }
+    
+    /**
+     * Checks whether or not the variable exists.
+     * @param $name String name
+     */
+    public function exists($name) {
+        return isset($this->_storage[$name]);
+    }
+    
+    /**
+     * Loads a series of variables from an associative array
+     * @param $context_array Assoc array of variables to load
+     */
+    public function loadArray($context_array) {
+        foreach ($context_array as $key => $discard) {
+            $this->register($key, $context_array[$key]);
+        }
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/Filter.php (revision 21)
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Represents a pre or post processing filter on HTML Purifier's output
+ * 
+ * Sometimes, a little ad-hoc fixing of HTML has to be done before
+ * it gets sent through HTML Purifier: you can use filters to acheive
+ * this effect. For instance, YouTube videos can be preserved using
+ * this manner. You could have used a decorator for this task, but
+ * PHP's support for them is not terribly robust, so we're going
+ * to just loop through the filters.
+ * 
+ * Filters should be exited first in, last out. If there are three filters,
+ * named 1, 2 and 3, the order of execution should go 1->preFilter,
+ * 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter,
+ * 1->postFilter.
+ * 
+ * @note Methods are not declared abstract as it is perfectly legitimate
+ *       for an implementation not to want anything to happen on a step
+ */
+
+class HTMLPurifier_Filter
+{
+    
+    /**
+     * Name of the filter for identification purposes
+     */
+    public $name;
+    
+    /**
+     * Pre-processor function, handles HTML before HTML Purifier 
+     */
+    public function preFilter($html, $config, $context) {
+        return $html;
+    }
+    
+    /**
+     * Post-processor function, handles HTML after HTML Purifier
+     */
+    public function postFilter($html, $config, $context) {
+        return $html;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Serializer.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Serializer.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Serializer.php (revision 21)
@@ -0,0 +1,171 @@
+<?php
+
+class HTMLPurifier_DefinitionCache_Serializer extends
+      HTMLPurifier_DefinitionCache
+{
+    
+    public function add($def, $config) {
+        if (!$this->checkDefType($def)) return;
+        $file = $this->generateFilePath($config);
+        if (file_exists($file)) return false;
+        if (!$this->_prepareDir($config)) return false;
+        return $this->_write($file, serialize($def));
+    }
+    
+    public function set($def, $config) {
+        if (!$this->checkDefType($def)) return;
+        $file = $this->generateFilePath($config);
+        if (!$this->_prepareDir($config)) return false;
+        return $this->_write($file, serialize($def));
+    }
+    
+    public function replace($def, $config) {
+        if (!$this->checkDefType($def)) return;
+        $file = $this->generateFilePath($config);
+        if (!file_exists($file)) return false;
+        if (!$this->_prepareDir($config)) return false;
+        return $this->_write($file, serialize($def));
+    }
+    
+    public function get($config) {
+        $file = $this->generateFilePath($config);
+        if (!file_exists($file)) return false;
+        return unserialize(file_get_contents($file));
+    }
+    
+    public function remove($config) {
+        $file = $this->generateFilePath($config);
+        if (!file_exists($file)) return false;
+        return unlink($file);
+    }
+    
+    public function flush($config) {
+        if (!$this->_prepareDir($config)) return false;
+        $dir = $this->generateDirectoryPath($config);
+        $dh  = opendir($dir);
+        while (false !== ($filename = readdir($dh))) {
+            if (empty($filename)) continue;
+            if ($filename[0] === '.') continue;
+            unlink($dir . '/' . $filename);
+        }
+    }
+    
+    public function cleanup($config) {
+        if (!$this->_prepareDir($config)) return false;
+        $dir = $this->generateDirectoryPath($config);
+        $dh  = opendir($dir);
+        while (false !== ($filename = readdir($dh))) {
+            if (empty($filename)) continue;
+            if ($filename[0] === '.') continue;
+            $key = substr($filename, 0, strlen($filename) - 4);
+            if ($this->isOld($key, $config)) unlink($dir . '/' . $filename);
+        }
+    }
+    
+    /**
+     * Generates the file path to the serial file corresponding to
+     * the configuration and definition name
+     * @todo Make protected
+     */
+    public function generateFilePath($config) {
+        $key = $this->generateKey($config);
+        return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
+    }
+    
+    /**
+     * Generates the path to the directory contain this cache's serial files
+     * @note No trailing slash
+     * @todo Make protected
+     */
+    public function generateDirectoryPath($config) {
+        $base = $this->generateBaseDirectoryPath($config);
+        return $base . '/' . $this->type;
+    }
+    
+    /**
+     * Generates path to base directory that contains all definition type
+     * serials
+     * @todo Make protected
+     */
+    public function generateBaseDirectoryPath($config) {
+        $base = $config->get('Cache', 'SerializerPath');
+        $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
+        return $base;
+    }
+    
+    /**
+     * Convenience wrapper function for file_put_contents
+     * @param $file File name to write to
+     * @param $data Data to write into file
+     * @return Number of bytes written if success, or false if failure.
+     */
+    private function _write($file, $data) {
+        return file_put_contents($file, $data);
+    }
+    
+    /**
+     * Prepares the directory that this type stores the serials in
+     * @return True if successful
+     */
+    private function _prepareDir($config) {
+        $directory = $this->generateDirectoryPath($config);
+        if (!is_dir($directory)) {
+            $base = $this->generateBaseDirectoryPath($config);
+            if (!is_dir($base)) {
+                trigger_error('Base directory '.$base.' does not exist,
+                    please create or change using %Cache.SerializerPath',
+                    E_USER_ERROR);
+                return false;
+            } elseif (!$this->_testPermissions($base)) {
+                return false;
+            }
+            $old = umask(0022); // disable group and world writes
+            mkdir($directory);
+            umask($old);
+        } elseif (!$this->_testPermissions($directory)) {
+            return false;
+        }
+        return true;
+    }
+    
+    /**
+     * Tests permissions on a directory and throws out friendly
+     * error messages and attempts to chmod it itself if possible
+     */
+    private function _testPermissions($dir) {
+        // early abort, if it is writable, everything is hunky-dory
+        if (is_writable($dir)) return true;
+        if (!is_dir($dir)) {
+            // generally, you'll want to handle this beforehand
+            // so a more specific error message can be given
+            trigger_error('Directory '.$dir.' does not exist',
+                E_USER_ERROR);
+            return false;
+        }
+        if (function_exists('posix_getuid')) {
+            // POSIX system, we can give more specific advice
+            if (fileowner($dir) === posix_getuid()) {
+                // we can chmod it ourselves
+                chmod($dir, 0755);
+                return true;
+            } elseif (filegroup($dir) === posix_getgid()) {
+                $chmod = '775';
+            } else {
+                // PHP's probably running as nobody, so we'll
+                // need to give global permissions
+                $chmod = '777';
+            }
+            trigger_error('Directory '.$dir.' not writable, '.
+                'please chmod to ' . $chmod,
+                E_USER_ERROR);
+        } else {
+            // generic error message
+            trigger_error('Directory '.$dir.' not writable, '.
+                'please alter file permissions',
+                E_USER_ERROR);
+        }
+        return false;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Null.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Null.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Null.php (revision 21)
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Null cache object to use when no caching is on.
+ */
+class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache
+{
+    
+    public function add($def, $config) {
+        return false;
+    }
+    
+    public function set($def, $config) {
+        return false;
+    }
+    
+    public function replace($def, $config) {
+        return false;
+    }
+    
+    public function remove($config) {
+        return false;
+    }
+    
+    public function get($config) {
+        return false;
+    }
+    
+    public function flush($config) {
+        return false;
+    }
+    
+    public function cleanup($config) {
+        return false;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Template.php.in
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Template.php.in (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Template.php.in (revision 21)
@@ -0,0 +1,46 @@
+<?php
+
+require_once 'HTMLPurifier/DefinitionCache/Decorator.php';
+
+/**
+ * Definition cache decorator template.
+ */
+class HTMLPurifier_DefinitionCache_Decorator_Template extends
+      HTMLPurifier_DefinitionCache_Decorator
+{
+    
+    var $name = 'Template'; // replace this
+    
+    function copy() {
+        // replace class name with yours
+        return new HTMLPurifier_DefinitionCache_Decorator_Template();
+    }
+    
+    // remove methods you don't need
+    
+    function add($def, $config) {
+        return parent::add($def, $config);
+    }
+    
+    function set($def, $config) {
+        return parent::set($def, $config);
+    }
+    
+    function replace($def, $config) {
+        return parent::replace($def, $config);
+    }
+    
+    function get($config) {
+        return parent::get($config);
+    }
+    
+    function flush() {
+        return parent::flush();
+    }
+    
+    function cleanup($config) {
+        return parent::cleanup($config);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php (revision 21)
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * Definition cache decorator class that cleans up the cache
+ * whenever there is a cache miss.
+ */
+class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends
+      HTMLPurifier_DefinitionCache_Decorator
+{
+    
+    public $name = 'Cleanup';
+    
+    public function copy() {
+        return new HTMLPurifier_DefinitionCache_Decorator_Cleanup();
+    }
+    
+    public function add($def, $config) {
+        $status = parent::add($def, $config);
+        if (!$status) parent::cleanup($config);
+        return $status;
+    }
+    
+    public function set($def, $config) {
+        $status = parent::set($def, $config);
+        if (!$status) parent::cleanup($config);
+        return $status;
+    }
+    
+    public function replace($def, $config) {
+        $status = parent::replace($def, $config);
+        if (!$status) parent::cleanup($config);
+        return $status;
+    }
+    
+    public function get($config) {
+        $ret = parent::get($config);
+        if (!$ret) parent::cleanup($config);
+        return $ret;
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Memory.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Memory.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator/Memory.php (revision 21)
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Definition cache decorator class that saves all cache retrievals
+ * to PHP's memory; good for unit tests or circumstances where 
+ * there are lots of configuration objects floating around.
+ */
+class HTMLPurifier_DefinitionCache_Decorator_Memory extends
+      HTMLPurifier_DefinitionCache_Decorator
+{
+    
+    protected $definitions;
+    public $name = 'Memory';
+    
+    public function copy() {
+        return new HTMLPurifier_DefinitionCache_Decorator_Memory();
+    }
+    
+    public function add($def, $config) {
+        $status = parent::add($def, $config);
+        if ($status) $this->definitions[$this->generateKey($config)] = $def;
+        return $status;
+    }
+    
+    public function set($def, $config) {
+        $status = parent::set($def, $config);
+        if ($status) $this->definitions[$this->generateKey($config)] = $def;
+        return $status;
+    }
+    
+    public function replace($def, $config) {
+        $status = parent::replace($def, $config);
+        if ($status) $this->definitions[$this->generateKey($config)] = $def;
+        return $status;
+    }
+    
+    public function get($config) {
+        $key = $this->generateKey($config);
+        if (isset($this->definitions[$key])) return $this->definitions[$key];
+        $this->definitions[$key] = parent::get($config);
+        return $this->definitions[$key];
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier/DefinitionCache/Decorator.php (revision 21)
@@ -0,0 +1,61 @@
+<?php
+
+class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache
+{
+    
+    /**
+     * Cache object we are decorating
+     */
+    public $cache;
+    
+    public function __construct() {}
+    
+    /**
+     * Lazy decorator function
+     * @param $cache Reference to cache object to decorate
+     */
+    public function decorate(&$cache) {
+        $decorator = $this->copy();
+        // reference is necessary for mocks in PHP 4
+        $decorator->cache =& $cache;
+        $decorator->type  = $cache->type;
+        return $decorator;
+    }
+    
+    /**
+     * Cross-compatible clone substitute
+     */
+    public function copy() {
+        return new HTMLPurifier_DefinitionCache_Decorator();
+    }
+    
+    public function add($def, $config) {
+        return $this->cache->add($def, $config);
+    }
+    
+    public function set($def, $config) {
+        return $this->cache->set($def, $config);
+    }
+    
+    public function replace($def, $config) {
+        return $this->cache->replace($def, $config);
+    }
+    
+    public function get($config) {
+        return $this->cache->get($config);
+    }
+    
+    public function remove($config) {
+        return $this->cache->remove($config);
+    }
+    
+    public function flush($config) {
+        return $this->cache->flush($config);
+    }
+    
+    public function cleanup($config) {
+        return $this->cache->cleanup($config);
+    }
+    
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.func.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.func.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.func.php (revision 21)
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * @file
+ * Defines a function wrapper for HTML Purifier for quick use.
+ * @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
+ */
+
+/**
+ * Purify HTML.
+ * @param $html String HTML to purify
+ * @param $config Configuration to use, can be any value accepted by
+ *        HTMLPurifier_Config::create()
+ */
+function HTMLPurifier($html, $config = null) {
+    static $purifier = false;
+    if (!$purifier) {
+        $purifier = new HTMLPurifier();
+    }
+    return $purifier->purify($html, $config);
+}
+
Index: /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.path.php
===================================================================
--- /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.path.php (revision 21)
+++ /afridex/plugins/Flutter/purifier_lib/HTMLPurifier.path.php (revision 21)
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * @file
+ * Convenience stub file that adds HTML Purifier's library file to the path
+ * without any other side-effects.
+ */
+
+set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
Index: /afridex/plugins/Flutter/RCCWP_CustomGroup.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CustomGroup.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CustomGroup.php (revision 21)
@@ -0,0 +1,141 @@
+<?php
+/**
+ * @package FlutterDatabaseObjects
+ */
+
+/**
+ * Create/Edit/Delete groups. Groups are just a collection of fields.
+ * @package FlutterDatabaseObjects
+ */
+
+class RCCWP_CustomGroup
+{
+	
+	/**
+	 * Create a new group in a write panel
+	 *
+	 * @param unknown_type $customWritePanelId
+	 * @param unknown_type $name group name
+	 * @param unknown_type $duplicate a boolean indicating whether the group can be duplicated
+	 * @param unknown_type $at_right a boolean indicating whether the group should be placed at right side.
+	 * @return the id of the new group
+	 */
+	function Create($customWritePanelId, $name, $duplicate, $at_right)
+	{
+		require_once('RC_Format.php');
+		global $wpdb;
+		$sql = sprintf(
+			"INSERT INTO " . RC_CWP_TABLE_PANEL_GROUPS .
+			" (panel_id, name, duplicate, at_right) values (%d, %s, %d, %d)",
+			$customWritePanelId,
+			RC_Format::TextToSql($name),
+			$duplicate,
+			$at_right
+			);
+		$wpdb->query($sql);
+		
+		$customGroupId = $wpdb->insert_id;
+		return $customGroupId;
+	}
+	
+	/**
+	 * Delete a group given id
+	 *
+	 * @param integer $customGroupId
+	 */
+	function Delete($customGroupId = null)
+	{
+		include_once ('RCCWP_CustomField.php');
+		if (isset($customGroupId))
+		{
+			global $wpdb;
+			
+			$customFields = RCCWP_CustomGroup::GetCustomFields($customGroupId);
+			foreach ($customFields as $field) 
+			{
+				RCCWP_CustomField::Delete($field->id);
+  			}
+		  	
+  			$sql = sprintf(
+				"DELETE FROM " . RC_CWP_TABLE_PANEL_GROUPS .
+				" WHERE id = %d",
+				$customGroupId
+				);
+			$wpdb->query($sql);
+		}
+	}
+	
+	/**
+	 * Get group properties
+	 *
+	 * @param integer $groupId
+	 * @return an object representing the group
+	 */
+	
+	function Get($groupId)
+	{
+		global $wpdb;
+	
+		$sql = "SELECT * FROM " . RC_CWP_TABLE_PANEL_GROUPS;
+		$sql .=	" WHERE id = " . (int)$groupId;
+		$results = $wpdb->get_row($sql);
+		return $results;
+	}
+	
+	/**
+	 * Get a list of the custom fields of a group
+	 *
+	 * @param integer $customGroupId the group id
+	 * @return an array of objects containing information about fields. Each object contains 
+	 * 			3 objects: properties, options and default_value   
+	 */
+	function GetCustomFields($customGroupId)
+	{
+		global $wpdb;
+		$sql = "SELECT cf.id, cf.name, tt.name AS type, cf.description, cf.display_order, cf.required_field,cf.css, co.options, co.default_option AS default_value, tt.has_options, cp.properties, tt.has_properties, tt.allow_multiple_values, cf.duplicate FROM " . RC_CWP_TABLE_GROUP_FIELDS .
+			" cf LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS . " co ON cf.id = co.custom_field_id" .
+			" LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " cp ON cf.id = cp.custom_field_id" .
+			" JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " tt ON cf.type = tt.id" . 
+			" WHERE group_id = " . $customGroupId .
+			" ORDER BY cf.display_order";
+		$results =$wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		
+		for ($i = 0; $i < $wpdb->num_rows; ++$i)
+		{
+			$results[$i]->options = unserialize($results[$i]->options);
+			$results[$i]->properties = unserialize($results[$i]->properties);
+			$results[$i]->default_value = unserialize($results[$i]->default_value);
+		}
+		
+		return $results;
+	}
+	
+	/**
+	 * Update the group
+	 *
+	 * @param unknown_type $customWritePanelId
+	 * @param unknown_type $name group name
+	 * @param unknown_type $duplicate a boolean indicating whether the group can be duplicated
+	 * @param unknown_type $at_right a boolean indicating whether the group should be placed at right side. 
+	 */	
+	function Update($customGroupId, $name, $duplicate, $at_right)
+	{
+		require_once('RC_Format.php');
+		global $wpdb;
+		//$capabilityName = RCCWP_CustomWriteModule::GetCapabilityName($name);
+	
+		$sql = sprintf(
+			"UPDATE " . RC_CWP_TABLE_PANEL_GROUPS .
+			" SET name = %s , duplicate = %d, at_right = %d".
+			" where id = %d",
+			RC_Format::TextToSql($name),
+			$duplicate,
+			$at_right,
+			$customGroupId );
+		$wpdb->query($sql);
+		
+	}
+}
+?>
Index: /afridex/plugins/Flutter/phpThumb.php
===================================================================
--- /afridex/plugins/Flutter/phpThumb.php (revision 21)
+++ /afridex/plugins/Flutter/phpThumb.php (revision 21)
@@ -0,0 +1,592 @@
+<?php
+
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// See: phpthumb.changelog.txt for recent changes           //
+// See: phpthumb.readme.txt for usage instructions          //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+error_reporting(E_ALL);
+ini_set('display_errors', '1');
+if (!@ini_get('safe_mode')) {
+	set_time_limit(60);  // shouldn't take nearly this long in most cases, but with many filter and/or a slow server...
+}
+ini_set('magic_quotes_runtime', '0');
+if (@ini_get('magic_quotes_runtime')) {
+	die('"magic_quotes_runtime" is set in php.ini, cannot run phpThumb with this enabled');
+}
+$starttime = array_sum(explode(' ', microtime()));
+
+// this script relies on the superglobal arrays, fake it here for old PHP versions
+if (phpversion() < '4.1.0') {
+	$_SERVER = $HTTP_SERVER_VARS;
+	$_GET    = $HTTP_GET_VARS;
+}
+
+// instantiate a new phpThumb() object
+ob_start();
+if (!include_once(dirname(__FILE__).'/phpthumb.class.php')) {
+	ob_end_flush();
+	die('failed to include_once("'.realpath(dirname(__FILE__).'/phpthumb.class.php').'")');
+}
+ob_end_clean();
+$phpThumb = new phpThumb();
+$phpThumb->DebugTimingMessage('phpThumb.php start', __FILE__, __LINE__, $starttime);
+$phpThumb->SetParameter('config_error_die_on_error', true);
+
+// phpThumbDebug[0] used to be here, but may reveal too much
+// info when high_security_mode should be enabled (not set yet)
+
+if (file_exists(dirname(__FILE__).'/phpThumb.config.php')) {
+	ob_start();
+	if (include_once(dirname(__FILE__).'/phpThumb.config.php')) {
+		// great
+	} else {
+		ob_end_flush();
+		$phpThumb->ErrorImage('failed to include_once('.dirname(__FILE__).'/phpThumb.config.php) - realpath="'.realpath(dirname(__FILE__).'/phpThumb.config.php').'"');
+	}
+	ob_end_clean();
+} elseif (file_exists(dirname(__FILE__).'/phpThumb.config.php.default')) {
+	$phpThumb->ErrorImage('Please rename "phpThumb.config.php.default" to "phpThumb.config.php"');
+} else {
+	$phpThumb->ErrorImage('failed to include_once('.dirname(__FILE__).'/phpThumb.config.php) - realpath="'.realpath(dirname(__FILE__).'/phpThumb.config.php').'"');
+}
+
+if (!@$PHPTHUMB_CONFIG['disable_pathinfo_parsing'] && (empty($_GET) || isset($_GET['phpThumbDebug'])) && !empty($_SERVER['PATH_INFO'])) {
+	$_SERVER['PHP_SELF'] = str_replace($_SERVER['PATH_INFO'], '', @$_SERVER['PHP_SELF']);
+
+	$args = explode(';', substr($_SERVER['PATH_INFO'], 1));
+	$phpThumb->DebugMessage('PATH_INFO.$args set to ('.implode(')(', $args).')', __FILE__, __LINE__);
+	if (!empty($args)) {
+		$_GET['src'] = @$args[count($args) - 1];
+		if (eregi('^new\=([a-z0-9]+)', $_GET['src'], $matches)) {
+			unset($_GET['src']);
+			$_GET['new'] = $matches[1];
+		}
+	}
+	if (eregi('^([0-9]*)x?([0-9]*)$', @$args[count($args) - 2], $matches)) {
+		$_GET['w'] = $matches[1];
+		$_GET['h'] = $matches[2];
+		$phpThumb->DebugMessage('PATH_INFO."w"x"h" set to "'.$_GET['w'].'"x"'.$_GET['h'].'"', __FILE__, __LINE__);
+	}
+	for ($i = 0; $i < count($args) - 2; $i++) {
+		@list($key, $value) = explode('=', @$args[$i]);
+		if (substr($key, -2) == '[]') {
+			$array_key_name = substr($key, 0, -2);
+			$_GET[$array_key_name][] = $value;
+			$phpThumb->DebugMessage('PATH_INFO."'.$array_key_name.'[]" = "'.$value.'"', __FILE__, __LINE__);
+		} else {
+			$_GET[$key] = $value;
+			$phpThumb->DebugMessage('PATH_INFO."'.$key.'" = "'.$value.'"', __FILE__, __LINE__);
+		}
+	}
+}
+
+if (@$PHPTHUMB_CONFIG['high_security_enabled']) {
+	if (!@$_GET['hash']) {
+		$phpThumb->ErrorImage('ERROR: missing hash');
+	} elseif (strlen($PHPTHUMB_CONFIG['high_security_password']) < 5) {
+		$phpThumb->ErrorImage('ERROR: strlen($PHPTHUMB_CONFIG[high_security_password]) < 5');
+	} elseif ($_GET['hash'] != md5(str_replace('&hash='.$_GET['hash'], '', $_SERVER['QUERY_STRING']).$PHPTHUMB_CONFIG['high_security_password'])) {
+		$phpThumb->ErrorImage('ERROR: invalid hash');
+	}
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[0]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '0') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+// returned the fixed string if the evil "magic_quotes_gpc" setting is on
+if (get_magic_quotes_gpc()) {
+	$RequestVarsToStripSlashes = array('src', 'wmf', 'file', 'err', 'goto', 'down');
+	foreach ($RequestVarsToStripSlashes as $key) {
+		if (isset($_GET[$key])) {
+			$_GET[$key] = stripslashes($_GET[$key]);
+		}
+	}
+}
+
+if (!@$_SERVER['PATH_INFO'] && !@$_SERVER['QUERY_STRING']) {
+	$phpThumb->ErrorImage('phpThumb() v'.$phpThumb->phpthumb_version.'<br><a href="http://phpthumb.sourceforge.net">http://phpthumb.sourceforge.net</a><br><br>ERROR: no parameters specified');
+}
+
+if (@$_GET['src'] && isset($_GET['md5s']) && empty($_GET['md5s'])) {
+	if (eregi('^(f|ht)tps?://', $_GET['src'])) {
+		if ($rawImageData = phpthumb_functions::SafeURLread($_GET['src'], $error, $phpThumb->config_http_fopen_timeout, $phpThumb->config_http_follow_redirect)) {
+			$md5s = md5($rawImageData);
+		}
+	} else {
+		$SourceFilename = $phpThumb->ResolveFilenameToAbsolute($_GET['src']);
+		if (is_readable($SourceFilename)) {
+			$md5s = phpthumb_functions::md5_file_safe($SourceFilename);
+		} else {
+			$phpThumb->ErrorImage('ERROR: "'.$SourceFilename.'" cannot be read');
+		}
+	}
+	if (@$_SERVER['HTTP_REFERER']) {
+		$phpThumb->ErrorImage('&md5s='.$md5s);
+	} else {
+		die('&md5s='.$md5s);
+	}
+}
+
+if (!empty($PHPTHUMB_CONFIG)) {
+	foreach ($PHPTHUMB_CONFIG as $key => $value) {
+		$keyname = 'config_'.$key;
+		$phpThumb->setParameter($keyname, $value);
+		if (!eregi('password', $key)) {
+			$phpThumb->DebugMessage('setParameter('.$keyname.', '.$phpThumb->phpThumbDebugVarDump($value).')', __FILE__, __LINE__);
+		}
+	}
+} else {
+	$phpThumb->DebugMessage('$PHPTHUMB_CONFIG is empty', __FILE__, __LINE__);
+}
+
+if (@$_GET['src'] && !@$PHPTHUMB_CONFIG['allow_local_http_src'] && eregi('^http://'.@$_SERVER['HTTP_HOST'].'(.+)', @$_GET['src'], $matches)) {
+	$phpThumb->ErrorImage('It is MUCH better to specify the "src" parameter as "'.$matches[1].'" instead of "'.$matches[0].'".'."\n\n".'If you really must do it this way, enable "allow_local_http_src" in phpThumb.config.php');
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[1]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '1') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+$parsed_url_referer = parse_url(@$_SERVER['HTTP_REFERER']);
+if ($phpThumb->config_nooffsitelink_require_refer && !in_array(@$parsed_url_referer['host'], $phpThumb->config_nohotlink_valid_domains)) {
+	$phpThumb->ErrorImage('config_nooffsitelink_require_refer enabled and '.(@$parsed_url_referer['host'] ? '"'.$parsed_url_referer['host'].'" is not an allowed referer' : 'no HTTP_REFERER exists'));
+}
+$parsed_url_src = parse_url(@$_GET['src']);
+if ($phpThumb->config_nohotlink_enabled && $phpThumb->config_nohotlink_erase_image && eregi('^(f|ht)tps?://', @$_GET['src']) && !in_array(@$parsed_url_src['host'], $phpThumb->config_nohotlink_valid_domains)) {
+	$phpThumb->ErrorImage($phpThumb->config_nohotlink_text_message);
+}
+
+if ($phpThumb->config_mysql_query) {
+	if ($cid = @mysql_connect($phpThumb->config_mysql_hostname, $phpThumb->config_mysql_username, $phpThumb->config_mysql_password)) {
+		if (@mysql_select_db($phpThumb->config_mysql_database, $cid)) {
+			if ($result = @mysql_query($phpThumb->config_mysql_query, $cid)) {
+				if ($row = @mysql_fetch_array($result)) {
+
+					mysql_free_result($result);
+					mysql_close($cid);
+					$phpThumb->setSourceData($row[0]);
+					unset($row);
+
+				} else {
+					mysql_free_result($result);
+					mysql_close($cid);
+					$phpThumb->ErrorImage('no matching data in database.');
+				}
+			} else {
+				mysql_close($cid);
+				$phpThumb->ErrorImage('Error in MySQL query: "'.mysql_error($cid).'"');
+			}
+		} else {
+			mysql_close($cid);
+			$phpThumb->ErrorImage('cannot select MySQL database: "'.mysql_error($cid).'"');
+		}
+	} else {
+		$phpThumb->ErrorImage('cannot connect to MySQL server');
+	}
+	unset($_GET['id']);
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[2]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '2') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+if (@$PHPTHUMB_CONFIG['cache_default_only_suffix'] && (strpos($PHPTHUMB_CONFIG['cache_default_only_suffix'], '*') !== false)) {
+	$PHPTHUMB_DEFAULTS_DISABLEGETPARAMS = true;
+}
+// deprecated: 'err', 'file', 'goto',
+$allowedGETparameters = array('src', 'new', 'w', 'h', 'wp', 'hp', 'wl', 'hl', 'ws', 'hs', 'f', 'q', 'sx', 'sy', 'sw', 'sh', 'zc', 'bc', 'bg', 'bgt', 'fltr', 'xto', 'ra', 'ar', 'aoe', 'far', 'iar', 'maxb', 'down', 'phpThumbDebug', 'hash', 'md5s', 'sfn', 'dpi', 'sia');
+if (!empty($PHPTHUMB_DEFAULTS) && is_array($PHPTHUMB_DEFAULTS)) {
+	$phpThumb->DebugMessage('setting $PHPTHUMB_DEFAULTS['.implode(';', array_keys($PHPTHUMB_DEFAULTS)).']', __FILE__, __LINE__);
+	foreach ($PHPTHUMB_DEFAULTS as $key => $value) {
+		if ($PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE || !isset($_GET[$key])) {
+			$_GET[$key] = $value;
+			$phpThumb->DebugMessage('PHPTHUMB_DEFAULTS assigning ('.$value.') to $_GET['.$key.']', __FILE__, __LINE__);
+			//$phpThumb->DebugMessage('PHPTHUMB_DEFAULTS.setParameter('.$key.', '.$phpThumb->phpThumbDebugVarDump($value).')', __FILE__, __LINE__);
+			//$phpThumb->setParameter($key, $value);
+		}
+	}
+}
+foreach ($_GET as $key => $value) {
+	if (@$PHPTHUMB_DEFAULTS_DISABLEGETPARAMS && ($key != 'src')) {
+		// disabled, do not set parameter
+		$phpThumb->DebugMessage('ignoring $_GET['.$key.'] because of $PHPTHUMB_DEFAULTS_DISABLEGETPARAMS', __FILE__, __LINE__);
+	} elseif (in_array($key, $allowedGETparameters)) {
+		$phpThumb->DebugMessage('setParameter('.$key.', '.$phpThumb->phpThumbDebugVarDump($value).')', __FILE__, __LINE__);
+		$phpThumb->setParameter($key, $value);
+	} else {
+		$phpThumb->ErrorImage('Forbidden parameter: '.$key);
+	}
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[3]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '3') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+//if (!@$_GET['phpThumbDebug'] && !$phpThumb->sourceFilename && !function_exists('ImageJPEG') && !function_exists('ImagePNG') && !function_exists('ImageGIF')) {
+if (!@$_GET['phpThumbDebug'] && !is_file($phpThumb->sourceFilename) && !phpthumb_functions::gd_version()) {
+	if (!headers_sent()) {
+		// base64-encoded error image in GIF format
+		$ERROR_NOGD = 'R0lGODlhIAAgALMAAAAAABQUFCQkJDY2NkZGRldXV2ZmZnJycoaGhpSUlKWlpbe3t8XFxdXV1eTk5P7+/iwAAAAAIAAgAAAE/vDJSau9WILtTAACUinDNijZtAHfCojS4W5H+qxD8xibIDE9h0OwWaRWDIljJSkUJYsN4bihMB8th3IToAKs1VtYM75cyV8sZ8vygtOE5yMKmGbO4jRdICQCjHdlZzwzNW4qZSQmKDaNjhUMBX4BBAlmMywFSRWEmAI6b5gAlhNxokGhooAIK5o/pi9vEw4Lfj4OLTAUpj6IabMtCwlSFw0DCKBoFqwAB04AjI54PyZ+yY3TD0ss2YcVmN/gvpcu4TOyFivWqYJlbAHPpOntvxNAACcmGHjZzAZqzSzcq5fNjxFmAFw9iFRunD1epU6tsIPmFCAJnWYE0FURk7wJDA0MTKpEzoWAAskiAAA7';
+		header('Content-Type: image/gif');
+		echo base64_decode($ERROR_NOGD);
+	} else {
+		echo '*** ERROR: No PHP-GD support available ***';
+	}
+	exit;
+}
+
+// check to see if file can be output from source with no processing or caching
+$CanPassThroughDirectly = true;
+if ($phpThumb->rawImageData) {
+	// data from SQL, should be fine
+} elseif (eregi('^(f|ht)tp\://', $phpThumb->src)) {
+	$phpThumb->DebugMessage('$CanPassThroughDirectly=false because eregi("^(f|ht)tp\://", '.$phpThumb->src.')', __FILE__, __LINE__);
+	$CanPassThroughDirectly = false;
+} elseif (!@is_file($phpThumb->sourceFilename)) {
+	$phpThumb->DebugMessage('$CanPassThroughDirectly=false because !@is_file('.$phpThumb->sourceFilename.')', __FILE__, __LINE__);
+	$CanPassThroughDirectly = false;
+} elseif (!@is_readable($phpThumb->sourceFilename)) {
+	$phpThumb->DebugMessage('$CanPassThroughDirectly=false because !@is_readable('.$phpThumb->sourceFilename.')', __FILE__, __LINE__);
+	$CanPassThroughDirectly = false;
+}
+foreach ($_GET as $key => $value) {
+	switch ($key) {
+		case 'src':
+			// allowed
+			break;
+
+		case 'w':
+		case 'h':
+			// might be OK if exactly matches original
+			break;
+
+		case 'phpThumbDebug':
+			// handled in direct-passthru code
+			break;
+
+		default:
+			// all other parameters will cause some processing,
+			// therefore cannot pass through original image unmodified
+			$CanPassThroughDirectly = false;
+			$UnAllowedGET[] = $key;
+			break;
+	}
+}
+if (!empty($UnAllowedGET)) {
+	$phpThumb->DebugMessage('$CanPassThroughDirectly=false because $_GET['.implode(';', array_unique($UnAllowedGET)).'] are set', __FILE__, __LINE__);
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[4]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '4') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+function SendSaveAsFileHeaderIfNeeded() {
+	if (headers_sent()) {
+		return false;
+	}
+	global $phpThumb;
+	$downloadfilename = phpthumb_functions::SanitizeFilename(@$_GET['sia'] ? $_GET['sia'] : (@$_GET['down'] ? $_GET['down'] : 'phpThumb_generated_thumbnail'.(@$_GET['f'] ? $_GET['f'] : 'jpg')));
+	if (@$downloadfilename) {
+		$phpThumb->DebugMessage('SendSaveAsFileHeaderIfNeeded() sending header: Content-Disposition: '.(@$_GET['down'] ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"', __FILE__, __LINE__);
+		header('Content-Disposition: '.(@$_GET['down'] ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"');
+	}
+	return true;
+}
+
+$phpThumb->DebugMessage('$CanPassThroughDirectly="'.intval($CanPassThroughDirectly).'" && $phpThumb->src="'.$phpThumb->src.'"', __FILE__, __LINE__);
+while ($CanPassThroughDirectly && $phpThumb->src) {
+	// no parameters set, passthru
+	$SourceFilename = $phpThumb->ResolveFilenameToAbsolute($phpThumb->src);
+
+	// security and size checks
+	if ($phpThumb->getimagesizeinfo = @GetImageSize($SourceFilename)) {
+		$phpThumb->DebugMessage('Direct passthru GetImageSize() returned [w='.$phpThumb->getimagesizeinfo[0].';h='.$phpThumb->getimagesizeinfo[1].';t='.$phpThumb->getimagesizeinfo[2].']', __FILE__, __LINE__);
+
+		if (!@$_GET['w'] && !@$_GET['wp'] && !@$_GET['wl'] && !@$_GET['ws'] && !@$_GET['h'] && !@$_GET['hp'] && !@$_GET['hl'] && !@$_GET['hs']) {
+			// no resizing needed
+			$phpThumb->DebugMessage('Passing "'.$SourceFilename.'" through directly, no resizing required ("'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'")', __FILE__, __LINE__);
+		} elseif (($phpThumb->getimagesizeinfo[0] <= @$_GET['w']) && ($phpThumb->getimagesizeinfo[1] <= @$_GET['h']) && ((@$_GET['w'] == $phpThumb->getimagesizeinfo[0]) || (@$_GET['h'] == $phpThumb->getimagesizeinfo[1]))) {
+			// image fits into 'w'x'h' box, and at least one dimension matches exactly, therefore no resizing needed
+			$phpThumb->DebugMessage('Passing "'.$SourceFilename.'" through directly, no resizing required ("'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'" fits inside "'.@$_GET['w'].'"x"'.@$_GET['h'].'")', __FILE__, __LINE__);
+		} else {
+			$phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because resizing required (from "'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'" to "'.@$_GET['w'].'"x"'.@$_GET['h'].'")', __FILE__, __LINE__);
+			break;
+		}
+		switch ($phpThumb->getimagesizeinfo[2]) {
+			case 1: // GIF
+			case 2: // JPG
+			case 3: // PNG
+				// great, let it through
+				break;
+			default:
+				// browser probably can't handle format, remangle it to JPEG/PNG/GIF
+				$phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because $phpThumb->getimagesizeinfo[2] = "'.$phpThumb->getimagesizeinfo[2].'"', __FILE__, __LINE__);
+				break 2;
+		}
+
+		$ImageCreateFunctions = array(1=>'ImageCreateFromGIF', 2=>'ImageCreateFromJPEG', 3=>'ImageCreateFromPNG');
+		$theImageCreateFunction = @$ImageCreateFunctions[$phpThumb->getimagesizeinfo[2]];
+		if ($phpThumb->config_disable_onlycreateable_passthru || (function_exists($theImageCreateFunction) && ($dummyImage = @$theImageCreateFunction($SourceFilename)))) {
+
+			// great
+			if (@is_resource($dummyImage)) {
+				unset($dummyImage);
+			}
+
+			if (headers_sent()) {
+				$phpThumb->ErrorImage('Headers already sent ('.basename(__FILE__).' line '.__LINE__.')');
+				exit;
+			}
+			if (@$_GET['phpThumbDebug']) {
+				$phpThumb->DebugTimingMessage('skipped direct $SourceFilename passthru', __FILE__, __LINE__);
+				$phpThumb->DebugMessage('Would have passed "'.$SourceFilename.'" through directly, but skipping due to phpThumbDebug', __FILE__, __LINE__);
+				break;
+			}
+
+			SendSaveAsFileHeaderIfNeeded();
+			header('Last-Modified: '.gmdate('D, d M Y H:i:s', @filemtime($SourceFilename)).' GMT');
+			if ($contentType = phpthumb_functions::ImageTypeToMIMEtype(@$phpThumb->getimagesizeinfo[2])) {
+				header('Content-Type: '.$contentType);
+			}
+			@readfile($SourceFilename);
+			exit;
+
+		} else {
+			$phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because ($phpThumb->config_disable_onlycreateable_passthru = "'.$phpThumb->config_disable_onlycreateable_passthru.'") and '.$theImageCreateFunction.'() failed', __FILE__, __LINE__);
+			break;
+		}
+
+	} else {
+		$phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because GetImageSize() failed', __FILE__, __LINE__);
+		break;
+	}
+	break;
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[5]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '5') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+function RedirectToCachedFile() {
+	global $phpThumb, $PHPTHUMB_CONFIG;
+
+	$nice_cachefile = str_replace(DIRECTORY_SEPARATOR, '/', $phpThumb->cache_filename);
+	$nice_docroot   = str_replace(DIRECTORY_SEPARATOR, '/', rtrim($PHPTHUMB_CONFIG['document_root'], '/\\'));
+
+	$parsed_url = @parse_url(@$_SERVER['HTTP_REFERER']);
+
+	$nModified  = filemtime($phpThumb->cache_filename);
+
+	if ($phpThumb->config_nooffsitelink_enabled && @$_SERVER['HTTP_REFERER'] && !in_array(@$parsed_url['host'], $phpThumb->config_nooffsitelink_valid_domains)) {
+
+		$phpThumb->DebugMessage('Would have used cached (image/'.$phpThumb->thumbnailFormat.') file "'.$phpThumb->cache_filename.'" (Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT), but skipping because $_SERVER[HTTP_REFERER] ('.@$_SERVER['HTTP_REFERER'].') is not in $phpThumb->config_nooffsitelink_valid_domains ('.implode(';', $phpThumb->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__);
+
+	} elseif ($phpThumb->phpThumbDebug) {
+
+		$phpThumb->DebugTimingMessage('skipped using cached image', __FILE__, __LINE__);
+		$phpThumb->DebugMessage('Would have used cached file, but skipping due to phpThumbDebug', __FILE__, __LINE__);
+		$phpThumb->DebugMessage('* Would have sent headers (1): Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT', __FILE__, __LINE__);
+		if ($getimagesize = @GetImageSize($phpThumb->cache_filename)) {
+			$phpThumb->DebugMessage('* Would have sent headers (2): Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($getimagesize[2]), __FILE__, __LINE__);
+		}
+		if (ereg('^'.preg_quote($nice_docroot).'(.*)$', $nice_cachefile, $matches)) {
+			$phpThumb->DebugMessage('* Would have sent headers (3): Location: '.dirname($matches[1]).'/'.urlencode(basename($matches[1])), __FILE__, __LINE__);
+		} else {
+			$phpThumb->DebugMessage('* Would have sent data: readfile('.$phpThumb->cache_filename.')', __FILE__, __LINE__);
+		}
+
+	} else {
+
+		if (headers_sent()) {
+			$phpThumb->ErrorImage('Headers already sent ('.basename(__FILE__).' line '.__LINE__.')');
+			exit;
+		}
+		SendSaveAsFileHeaderIfNeeded();
+
+		header('Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT');
+		if (@$_SERVER['HTTP_IF_MODIFIED_SINCE'] && ($nModified == strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) && @$_SERVER['SERVER_PROTOCOL']) {
+			header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
+			exit;
+		}
+
+		if ($getimagesize = @GetImageSize($phpThumb->cache_filename)) {
+			header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($getimagesize[2]));
+		} elseif (eregi('\.ico$', $phpThumb->cache_filename)) {
+			header('Content-Type: image/x-icon');
+		}
+		if (!@$PHPTHUMB_CONFIG['cache_force_passthru'] && ereg('^'.preg_quote($nice_docroot).'(.*)$', $nice_cachefile, $matches)) {
+			header('Location: '.dirname($matches[1]).'/'.urlencode(basename($matches[1])));
+		} else {
+			@readfile($phpThumb->cache_filename);
+		}
+		exit;
+
+	}
+	return true;
+}
+
+// check to see if file already exists in cache, and output it with no processing if it does
+$phpThumb->SetCacheFilename();
+if (@is_file($phpThumb->cache_filename)) {
+	RedirectToCachedFile();
+} else {
+	$phpThumb->DebugMessage('Cached file "'.$phpThumb->cache_filename.'" does not exist, processing as normal', __FILE__, __LINE__);
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[6]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '6') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+if ($phpThumb->rawImageData) {
+
+	// great
+
+} elseif (@$_GET['new']) {
+
+	// generate a blank image resource of the specified size/background color/opacity
+	if (($phpThumb->w <= 0) || ($phpThumb->h <= 0)) {
+		$phpThumb->ErrorImage('"w" and "h" parameters required for "new"');
+	}
+	@list($bghexcolor, $opacity) = explode('|', $_GET['new']);
+	if (!phpthumb_functions::IsHexColor($bghexcolor)) {
+		$phpThumb->ErrorImage('BGcolor parameter for "new" is not valid');
+	}
+	$opacity = (strlen($opacity) ? $opacity : 100);
+	if ($phpThumb->gdimg_source = phpthumb_functions::ImageCreateFunction($phpThumb->w, $phpThumb->h)) {
+		$alpha = (100 - min(100, max(0, $opacity))) * 1.27;
+		if ($alpha) {
+			$phpThumb->setParameter('is_alpha', true);
+			ImageAlphaBlending($phpThumb->gdimg_source, false);
+			ImageSaveAlpha($phpThumb->gdimg_source, true);
+		}
+		$new_background_color = phpthumb_functions::ImageHexColorAllocate($phpThumb->gdimg_source, $bghexcolor, false, $alpha);
+		ImageFilledRectangle($phpThumb->gdimg_source, 0, 0, $phpThumb->w, $phpThumb->h, $new_background_color);
+	} else {
+		$phpThumb->ErrorImage('failed to create "new" image ('.$phpThumb->w.'x'.$phpThumb->h.')');
+	}
+
+} elseif (!$phpThumb->src) {
+
+	$phpThumb->ErrorImage('Usage: '.$_SERVER['PHP_SELF'].'?src=/path/and/filename.jpg'."\n".'read Usage comments for details');
+
+} elseif (eregi('^(f|ht)tp\://', $phpThumb->src)) {
+
+	if ($phpThumb->config_http_user_agent) {
+		ini_set('user_agent', $phpThumb->config_http_user_agent);
+	}
+
+	if ($rawImageData = phpthumb_functions::SafeURLread(phpthumb_functions::CleanUpURLencoding($phpThumb->src), $error, $phpThumb->config_http_fopen_timeout, $phpThumb->config_http_follow_redirect)) {
+		$phpThumb->setSourceData($rawImageData, urlencode($phpThumb->src));
+	} else {
+		$phpThumb->ErrorImage($error);
+	}
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[7]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '7') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+$phpThumb->GenerateThumbnail();
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[8]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '8') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+if ($phpThumb->config_allow_parameter_file && $phpThumb->file) {
+
+	$phpThumb->RenderToFile($phpThumb->ResolveFilenameToAbsolute($phpThumb->file));
+	if ($phpThumb->config_allow_parameter_goto && $phpThumb->goto && eregi('^(f|ht)tps?://', $phpThumb->goto)) {
+		// redirect to another URL after image has been rendered to file
+		header('Location: '.$phpThumb->goto);
+		exit;
+	}
+
+} elseif (@$PHPTHUMB_CONFIG['high_security_enabled'] && @$_GET['nocache']) {
+
+	// cache disabled, don't write cachefile
+
+} else {
+
+	phpthumb_functions::EnsureDirectoryExists(dirname($phpThumb->cache_filename));
+	if ((file_exists($phpThumb->cache_filename) && is_writable($phpThumb->cache_filename)) || is_writable(dirname($phpThumb->cache_filename))) {
+
+		$phpThumb->CleanUpCacheDirectory();
+		if ($phpThumb->RenderToFile($phpThumb->cache_filename) && is_readable($phpThumb->cache_filename)) {
+			chmod($phpThumb->cache_filename, 0644);
+			RedirectToCachedFile();
+		} else {
+			$phpThumb->DebugMessage('Failed: RenderToFile('.$phpThumb->cache_filename.')', __FILE__, __LINE__);
+		}
+
+	} else {
+
+		$phpThumb->DebugMessage('Cannot write to $phpThumb->cache_filename ('.$phpThumb->cache_filename.') because that directory ('.dirname($phpThumb->cache_filename).') is not writable', __FILE__, __LINE__);
+
+	}
+
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[9]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '9') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+if (!$phpThumb->OutputThumbnail()) {
+	$phpThumb->ErrorImage('Error in OutputThumbnail():'."\n".$phpThumb->debugmessages[(count($phpThumb->debugmessages) - 1)]);
+}
+
+////////////////////////////////////////////////////////////////
+// Debug output, to try and help me diagnose problems
+$phpThumb->DebugTimingMessage('phpThumbDebug[10]', __FILE__, __LINE__);
+if (@$_GET['phpThumbDebug'] == '10') {
+	$phpThumb->phpThumbDebug();
+}
+////////////////////////////////////////////////////////////////
+
+?>
Index: /afridex/plugins/Flutter/stylesheet.css
===================================================================
--- /afridex/plugins/Flutter/stylesheet.css (revision 21)
+++ /afridex/plugins/Flutter/stylesheet.css (revision 21)
@@ -0,0 +1,816 @@
+/* WP Mods */
+
+body {
+	padding-bottom: 100px;
+	background-color: #fff;
+	}
+
+a, a:hover, a:visited {
+	text-decoration: none;
+	border: 0px;
+	}
+
+#user_info {
+	opacity: .2;
+	filter: alpha(opacity=20);
+	}
+
+#user_info:hover {
+	opacity: 1.0;
+	filter: alpha(opacity=100);
+	}
+
+#wordpress-logo {
+	display: none;
+	}
+
+#footer {
+	visibility: hidden;
+	}
+
+#toggle-header {
+	height: 5px;
+	padding: 0;
+	padding-top: 1px;
+	}
+
+#toggle-header h2{
+	margin-left:15px;
+	}
+
+span#show_content, span#hide_content {
+	display: block;
+	width: 100%;
+	}
+
+#toggle-header a {
+	border: 0;
+	padding: 5px;
+	color: #a4a4a4;
+	font-size: 9px;
+	}
+
+
+/* Canvas CSS */
+
+#header-top {
+	width: 100%;
+	background: #fff url('images/canvas_logo-white.gif') center top no-repeat;
+	text-align: center;
+	height: 45px;
+	}
+
+#header-bottom {
+	width: 100%;
+	background: #fff url('images/white_gradient.gif') top left repeat-x;
+	text-align: center;
+	height: 8px;
+	border-bottom: 1px solid #a4a4a4;
+	}
+
+#canvas {
+	font-size: 11px;
+	font-family: 'Arial';
+	clear: left;
+	}
+
+.content_wrap {
+	border: none;
+	width: 96%;
+	margin: 0 auto;
+	overflow: visible;
+	vertical-align: top;
+	float:left;
+	margin-left:15px;
+	}
+
+.content_wrap small {
+	font-size: 9px;
+	}
+
+#instructions {
+	padding: 10px;
+	background-color: #DFE6FF;
+	border: 1px solid #ABB7DF;
+	width: 99%;
+	}
+
+#instructions small {
+	padding-left: 10px;
+	}
+
+#publish_image, #export_image, #import_image, #restore_image {
+	border: 1px solid #FFF;
+	}
+
+#export_image, #import_image, #restore_image {
+	padding-left: 3px;
+	}
+
+#publish_image:hover, #export_image:hover, #import_image:hover, #restore_image:hover {
+	border: 1px solid #BBB;
+	}
+
+.shelf_column {
+
+border:1px solid #CCCCCC;
+background-color: #EFEFEF;
+float:left;
+
+margin-bottom:9px;
+margin-left:15px;
+margin-right:-2px;
+min-height:100px;
+height:auto !important;
+height:150px;
+overflow:visible;
+padding:0pt 5px 0pt 0pt;
+position:relative;
+top:0px;
+vertical-align:top;
+width:96%;
+z-index:10;
+    /*position: fixed;*/
+
+	}
+
+#inkadmin .shelf_column {
+	height: 200px;
+
+	}
+
+.spaceholder {
+	width: 10px;
+	height: 3px;
+	display: block;
+	}
+
+#shelf {
+    height: 90%;
+	clear: both;
+	margin: 0 5px;
+	overflow-y: auto;
+  }
+
+#shelf .container, #shelf .container-plugin {
+    width: 80px;
+    height: 42px;
+    overflow: hidden;
+    float: left;
+    margin-top: 3px;
+    padding-left: 4px;
+    padding-right: 4px;
+	background-color: white;
+	border-color: #CCCCCC;
+    }
+
+
+#shelf .container:hover, #shelf .container-plugin:hover {
+	background-color: #DFE6FF;
+	}
+
+#shelf .container-plugin img, #shelf .container-plugin a {
+	display: none;
+	}
+    
+#shelf div.content {
+    display: none;
+    }
+
+#shelf h6.handle {
+	margin-bottom: 0px;
+	background-color: transparent;
+	font-weight: normal;
+	padding: 2px 0;
+	color: #777;
+	font-size: 13px;
+	/*height: 100%;*/	
+}
+
+#shelf h6.handle:hover {
+	color: #A4A4A4;
+	background-color: transparent;
+	}
+
+.shelf_column h5.title {
+	color: #333333;
+	background: none;
+	float: left;
+	height: 15px;
+	padding-top: 5px;
+	padding-right: 10px;
+	border: 0;
+	}
+
+.shelf_column h5.title span {
+	visibility: hidden;
+	}
+
+.shelf_column .titlebar img div{
+	float: right;
+	display: inline;
+	position: relative;
+	margin: 3px 5px 0 0;
+	}
+
+.shelf_column h5.title:hover span {
+	visibility: visible;
+	color: #777;
+	padding-left: 5px;
+	}
+
+.spaceholder {
+	width: 10px;
+	height: 3px;
+	display: block;
+	}
+
+.canvas_droppable_zone {
+	border: 1px solid #cfcfcf;
+	font-family: arial;
+	margin: 2px;
+	height: auto;
+	min-height: 50px;
+	height:auto !important;
+  	height:50px	
+	}
+
+.canvas_droppable_zone:first-child div {
+	}
+
+.shelf_column .canvas_droppable_zone:first-child div {
+	}
+
+.shelf_column .canvas_droppable_zone {
+	border: 0;
+	}
+
+div.blockline {
+	display: block;
+	border-top: 2px solid #545454;
+	clear: both;
+	margin: 2px;
+	}
+
+ul {
+	list-style-type: none;
+	margin: 0px;
+	padding: 0px;
+	}
+
+.container, .container-plugin {
+	border: 1px solid #999999;
+	background-color: #DFE6FF;
+	padding: 0px;
+	margin: 2px;
+	position: relative;
+	font-size: 10px;
+	}
+
+.container-plugin {
+
+	}
+
+div.container-plugin div.content {
+	padding-right: 15px;
+	font-size: 12px;
+	}
+
+.lbLink {
+	right: 18px;
+    position: absolute;
+    display: inline;
+    margin-top: 3px;
+    padding: 5px;
+    }
+
+.lbLinkInfo {
+	right: 0px;
+    position: absolute;
+    display: inline;
+    margin-top: 0px;
+    padding: 5px;
+    }
+
+
+h5.title {
+	font-size: 11px;
+	padding: 3px;
+	padding-bottom: 1px;
+	margin: 0px;
+	}
+
+div.column1 div.full {
+	border: 0px;
+	}
+
+h6 {
+	font-size: 12px;
+	font-weight: bold;
+	padding: 0px;
+	margin: 0px;
+	overflow: auto;
+	}
+
+h6.handle {
+	cursor: move;
+	color: #FFFFFF;
+	background: #333333;
+	padding: 2px 2px 3px 3px;
+	margin-bottom: 5px;
+	overflow: hidden;
+	}
+
+div.container-plugin h6.handle {
+	border-bottom: 1px solid #607695;
+	}
+
+div.container h6.handle {
+	border-bottom: 1px solid #7891B3;
+	}
+
+h6 span {
+	display: inline;
+	overflow: hidden;
+	/*float: left;*/
+	margin-left: 4px;
+	height: 17px;
+	width: 100px;
+	}
+
+h6 a {
+	border: 0;
+	}
+
+p {
+	padding: 2px 5px;
+	}
+
+#publish {
+	float: right;
+	margin-top: 10px;
+	padding-left: 20px;
+	background: #FFF url('') center left no-repeat;
+	background-repeat: no-repeat;
+	}
+
+#save_button {
+	float: left;
+	clear: left;
+	margin-top: 30px;
+	padding-right: 20px;
+	background: #FFF url('') center right no-repeat;
+	background-repeat: no-repeat;
+	}
+
+.publish_nobutton {
+	width: 16px;
+	height: 20px;
+	padding: 0;
+	}
+
+#export_message {
+	height: 40px;
+	padding: 25px 0 0 30px;
+	}
+
+.exporting, .importing {
+	background: #FFF url('images/spinner.gif') center left no-repeat;
+	background-repeat: no-repeat;
+	}
+
+.exporting:after {
+	content: "Exporting your layout...";
+	}
+
+.importing:after {
+	content: "Importing the layout...";
+	}
+
+.canvas_zone_options {
+	padding: 1px;
+	padding-top: 0;
+	margin-top: 0;
+	margin-bottom: 2px;
+	height: 20px;
+	display: block;
+	}
+
+.canvas_zone_options strong {
+	float: right;
+	padding: 0;
+	margin: 0;
+	opacity: 0.5;
+	filter: alpha(opacity=50);
+	}
+
+.canvas_zone_options select {
+	display: none;
+	}
+
+.canvas_zone_options img {
+	float: right;
+	cursor: pointer;
+	}
+
+.canvas_droppable_zone > h8 {
+	display: block;
+	width: 100%;
+	height: 3px;
+	cursor: pointer;
+	}
+
+.canvas_droppable_zone > h8:hover {
+	background-color: #cfcfcf;
+	}
+
+/* Menu */
+
+#canvas_dropdown {
+color:black;
+float:left;
+font-family:'Arial';
+font-size:9px;
+margin:10px auto 15px 15px;
+	}
+	
+#canvas_dropdown a {
+color:black;
+font-size: 11px;}
+
+ul.page_dropdown {
+	width: 150px;
+	background: #FFF url('images/page_dropdown.gif') top left no-repeat;
+	margin-top: 1px;
+	margin-right: 25px;
+	color: #5B5E62;
+	float: left;
+	}
+
+ul.page_dropdown:hover div.dropdown_menu {
+	display: block;
+	}
+
+ul.page_dropdown a, .canvas_menu_item:link, .canvas_menu_item:visited {
+	display: block;
+	color: #5B5E62;
+	width: 140px;
+	padding: 4px 0 3px 10px;
+	}
+
+.canvas_menu_item:link, .canvas_menu_item:visited {
+	background: #FFF url('images/menu_item.gif') top left no-repeat;
+	float: left;
+	margin-top: 1px;
+	margin-right: 25px;
+	padding: 5px 0 4px 10px;
+	}
+
+ul.page_dropdown a:hover, .canvas_menu_item:hover {
+	color: #2F3236;
+	}
+
+div.dropdown_menu {
+	display: none;
+	position: absolute;
+	z-index: 9999;
+	background: url('images/menu_trans.png') top left;
+	border: 1px solid #999;
+	border-top: 0;
+	border-bottom-color: #838282;
+	width: 140px;
+	margin-left: 4px;
+	}
+
+div.dropdown_menu ul li {
+	margin: 0;
+	}
+
+div.dropdown_menu ul li.selected {
+	background-color: #C2C8CF;
+	}
+
+div.dropdown_menu a {
+	padding-top: 2px;
+	padding-bottom: 3px;
+	width: 130px;
+	}
+
+div.dropdown_menu a:hover {
+	color: #000;
+	background-color: #B8BCC2;
+	}
+
+/* Plugin Management */
+
+#canvas ul.manage {
+	float: left;
+	margin-right: 20px;
+	}
+
+#canvas ul.manage li {
+	font-size: 10px;
+	width: 500px;
+	padding: 10px 20px;
+	background: #C9D6ED;
+	border: 1px solid #999;
+	border-bottom: 1px solid #777;
+	border-top: 1px solid #777;
+	}
+
+#canvas ul.manage li.plugin {
+	background-color: #7996BF;
+	border: 1px solid #777;
+	border-bottom: 1px solid #444;
+	border-top: 1px solid #444;
+	}
+
+#canvas ul.manage span {
+	display: block;
+	}
+
+#canvas ul.manage h3 {
+	font-size: 10px;
+	margin: 2px 0;
+	font-weight: normal;
+	}
+
+#canvas ul.manage h3 span {
+	display: inline;
+	font-size: 11px;
+	font-weight: bold;
+	}
+
+#canvas ul.manage h3 span.manageSaving {
+	color: #2C3B1B;
+	}
+
+#canvas ul.manage h3 span:hover {
+	background-image: url('images/edit.png');
+	background-position: center left;
+	background-repeat: no-repeat;
+	padding-left: 12px;
+	margin-left: -12px;
+	}
+
+#canvas .info {
+	background-color: #FFF;
+	border: 1px solid #999;
+	}
+
+#canvas .plugin .info {
+	border: 1px solid #777;
+	}
+
+#canvas .info div {
+	font-size: 9px;
+	padding: 5px;
+	}
+
+.duplicate_block, .delete_block, .block_info {
+	float: right;
+	display: inline;
+	margin-left: 10px;
+	}
+
+#help {
+	display: inline;
+	width: 280px;
+	float: right;
+	}
+
+#help p {
+	color: #101010;
+	}
+
+#help h3, form.options h4 {
+	border-bottom: 1px solid #CCC;
+	padding-bottom: 5px;
+	margin-top: 0;
+	}
+
+#help #message p {
+	background-color: #FFD0DC;
+	border: 1px solid #BE6269;
+	font-weight: bold;
+	font-size: 11px;
+	padding: 5px;
+	padding-bottom: 8px;
+	margin: 0;
+	margin-bottom: 20px;
+	}
+	
+form.options h4 {
+	margin-top: 20px;
+	}
+
+form.options label {
+	display: block;
+	margin: 5px;
+	}
+
+
+/* Tips */
+
+ol.tips li {
+	font-weight: bold;
+	border-top: 1px solid #CCC;
+	padding-top: 10px;
+	}
+
+ol.tips li p {
+	font-weight: normal;
+	color: #101010;
+	}
+
+
+/* Lightbox */
+
+ 
+.lbContent_inside{
+	padding: 20px;
+	padding-top: 15px;
+	height: 360px;
+	overflow: auto;
+	overflow-x: hidden;
+	overflow-y: auto;
+	float:right;
+	width:290px;
+	border-left: 1px solid #DDD;
+	}
+
+.lbContent a:link, .lbContent a:visited {
+	color: #18364F;
+	}
+
+.lbContent a:hover {
+	color: #091E2F;
+	}
+
+.lbContent h3 {
+	position: fixed;
+	font-size: 14px;
+	background: #7996BF;
+	width: 130px;
+	height: 375px;
+	float: left;
+	padding: 15px 10px 10px 10px;
+	margin: 0;
+	border-right: 1px solid #607695;
+	}
+
+.lbContent h3 span {
+	display: block;
+	clear: left;
+	font-size: 11px;
+	font-weight: normal;
+	}
+
+.lbContent h3 p {
+	font-weight: normal;
+	font-size: 10px;
+	margin: 20px 0 0 0;
+	padding: 0;
+	}
+
+.lbContent a#confirm, .lbContent a#cancel {
+	display: block;
+	background: #7996BF url('images/lbsave.png') top left no-repeat;
+	width: 128px;
+	height: 15px;
+	padding-top: 3px;
+	font-weight: normal;
+	text-align: center;
+	position: absolute;
+	bottom: 35px;
+	font-size: 10px;
+	}
+
+.lbContent a#cancel {
+	bottom: 10px;
+	}
+
+.lbContent form {
+	padding: 10px;
+	background: #F6F3FE;
+	border: 1px solid #DDD;
+	font-size: 10px;
+	}
+
+.lbContent form > span {
+	display: block;
+	clear: left;
+	padding-bottom: 5px;
+	margin-bottom: 10px;
+	border-bottom: 1px solid #DDD;
+	}
+
+.lbContent form span p {
+	padding-left: 0;
+	}
+
+.lbContent form label.textbox {
+	margin: 5px 0;
+	display: block;
+	clear: left;
+	}
+
+.lbContent form label.textbox input {
+	margin: 2px 0;
+	}
+
+.lbContent form span label {
+	display: inline;
+	padding-left: 10px;
+	}
+
+.lbContent label small {
+	font-size: 9px;
+	color: #222;
+	}
+
+.lbContent form label.boolean {
+	display: block;
+	clear: left;
+	padding: 5px;
+	}
+
+.lbContent form input, .lbContent form textbox {
+	background: #FFF;
+	padding: 0 1px;
+	}
+
+.lbContent form input.text {
+	width: 100%;
+	}
+
+.lbContent form span.list {
+	padding-bottom: 5px;
+	}
+
+.lbContent form span.list input.text {
+	width: 230px;
+	margin-bottom: 3px;
+	}
+
+.lbContent form span.list a img {
+	padding-left: 5px;
+	padding-top: 1px;
+	}
+
+.lbContent form textarea {
+	clear: left;
+	padding: 0 1px;
+	width: 100%;
+	}
+
+.lbContent form select {
+	width: 100%;
+	padding: 2px;
+	background-color: #FFF;
+	font-family: inherit;
+	font-size: inherit;
+	}
+
+#lbLoadMessage p {
+	text-align: center;
+	width: 18px;
+	margin: 0 auto;
+	}
+
+#lbLoadMessage img {
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	margin: auto;
+	height: 16px;
+	width: 16px;
+	}
+
+.lbContent form div.gallery {
+	height: 200px;
+	clear: left;
+	margin: 0;
+	padding: 0;
+	border: 1px solid #DDD;
+	overflow: scroll;
+	overflow-x: hidden;
+	overflow-y: scroll;
+	}
+
+.lbContent form div.gallery a img {
+	position: relative;
+	margin: 1px;
+	padding: 1px;
+	border: 1px solid #FFF;
+	}
+
+.lbContent form div.gallery a img:hover {
+	border: 1px solid #AAA;
+	background-color: #DDD;
+	}
+
+.lbContent form div.gallery a img.selected_image {
+	border: 1px solid #000;
+	background-color: #DDD;
+	}
Index: /afridex/plugins/Flutter/RCCWP_WritePostPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_WritePostPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_WritePostPage.php (revision 21)
@@ -0,0 +1,1196 @@
+<?php
+class RCCWP_WritePostPage
+{
+	function ApplyCustomWritePanelAssignedCategories($content)
+	{
+		global $CUSTOM_WRITE_PANEL;
+		
+		$assignedCategoryIds = RCCWP_CustomWritePanel::GetAssignedCategoryIds($CUSTOM_WRITE_PANEL->id);
+		foreach ($assignedCategoryIds as $categoryId)
+		{
+			$toReplace = 'id="in-category-' . $categoryId . '"';
+			$replacement = $toReplace . ' checked="checked"';
+			$content = str_replace($toReplace, $replacement, $content);
+		}
+		
+		return $content;
+	}
+	
+	function FormError(){
+		if (RCCWP_Application::InWritePostPanel()){
+			echo "<div id='flutter-publish-error-message' class='error' style='display:none;'><p><strong>".__("Post was not published - ")."</strong> ".__("You have errors in some fields, please check the fields below.")."</p></div>";		
+		}
+	}
+	
+	function ApplyCustomWritePanelHeader()
+	{
+		global $CUSTOM_WRITE_PANEL;
+
+		// Validate capability
+		require_once ('RCCWP_Options.php');
+		$assignToRole = RCCWP_Options::Get('assign-to-role');
+		$requiredPostsCap = 'edit_posts';
+		$requiredPagesCap = 'edit_pages';
+
+		if ($assignToRole == 1){
+			$requiredPostsCap = $CUSTOM_WRITE_PANEL->capability_name;
+			$requiredPagesCap = $CUSTOM_WRITE_PANEL->capability_name;
+		}
+
+		if ($CUSTOM_WRITE_PANEL->type == "post")
+			$requiredCap = $requiredPostsCap;
+		else
+			$requiredCap = $requiredPagesCap;
+		
+		if (!current_user_can($requiredCap)) wp_die( __('You do not have sufficient permissions to access this custom write panel.') );
+
+		// --- Apply Flutter CSS and javascript
+		?>
+
+		<link rel='stylesheet' href='<?php echo CANVASURI?>css/epoch_styles.css' type='text/css' />
+		
+		<style type="text/css">
+			
+			.tr_inside{
+				background-color:transparent !important;
+			}
+			
+			.freshout{
+				display: block;
+    			margin-left: auto;
+    			margin-right: auto ;
+			}
+				
+			.photo_edit_link{
+				clear:both;
+				margin: 0px 0px 0px 0px;
+				width:150px;
+				text-align:center;
+			}
+			
+			.error_msg_txt{
+				font-weight: bold;
+				overflow: auto;
+			}
+			
+			.duplicate_button{
+				text-decoration:none; 
+				font-weight:bold;
+				float:right
+			}
+			
+			.duplicate_image{
+				vertical-align:middle;
+				padding-right:3px;
+			}
+					
+		</style>
+		
+		<link href="<?php echo CANVASURI?>js/greybox/gb_styles.css" rel="stylesheet" type="text/css" media="all" />
+		
+		<!--<script type="text/javascript">
+			
+			tinyMCE.init({
+			theme : "advanced",
+			theme_advanced_toolbar_location : "top",
+			theme_advanced_toolbar_align : "left",
+			theme_advanced_buttons1 : "bold, italic, |, underline, |, bullist, numlist, outdent, indent, |, justifyleft, justifycenter, justifyright, justifyfull, |, link, unlink, spellchecker",
+				theme_advanced_buttons2 : "",
+				theme_advanced_buttons3 : "",
+			mode : "textareas",
+			skin : "wp_theme",
+			tab_focus : "next",
+			spellchecker_languages : "english",
+			convert_urls : false,
+			gecko_spellcheck : true,
+			convert_newlines_to_brs : false,
+			force_br_newlines : true,
+			force_p_newlines : false,
+			//mode : "exact",
+			//elements : "editorContent",
+			editor_selector : "mceEditor",
+			width : "100%",
+			height : "200"
+			});
+			
+		</script>-->
+
+		<script language="JavaScript" type="text/javascript" src="<?php echo CANVASURI; ?>js/prototype.js"></script>
+		
+		<!-- Calendar Control -->
+		<script type="text/javascript" src="<?php echo CANVASURI?>js/epoch_classes.js"></script> <!--Epoch's Code-->
+		<!-- Calendar Control -->
+		
+		
+		<script type="text/javascript">
+			var GB_ROOT_DIR = "<?php echo CANVASURI?>js/greybox/";
+			var flutter_path = "<?php echo FLUTTER_URI ?>" ;
+			var swf_authentication = "<?php if ( function_exists('is_ssl') && is_ssl() ) echo $_COOKIE[SECURE_AUTH_COOKIE]; else echo $_COOKIE[AUTH_COOKIE]; ?>" ;
+			var swf_nonce = "<?php echo wp_create_nonce('media-form'); ?>" ;
+		</script>
+		<script type="text/javascript" src="<?php echo CANVASURI?>js/greybox/AJS.js"></script>
+		<script type="text/javascript" src="<?php echo CANVASURI?>js/greybox/AJS_fx.js"></script>
+		<script type="text/javascript" src="<?php echo CANVASURI?>js/greybox/gb_scripts.js"></script>
+		<script type="text/javascript" src="<?php echo FLUTTER_URI; ?>js/swfupload/swfcallbacks.js" ></script>
+		<script type="text/javascript" src="<?php echo FLUTTER_URI; ?>js/swfupload/swfupload.js"></script>
+
+		<script type="text/javascript">
+				function isset(  ) {
+					// http://kevin.vanzonneveld.net
+					// +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+					// +   improved by: FremyCompany
+					// *     example 1: isset( undefined, true);
+					// *     returns 1: false
+					// *     example 2: isset( 'Kevin van Zonneveld' );
+					// *     returns 2: true
+					
+					var a=arguments; var l=a.length; var i=0;
+					
+					while ( i!=l ) {
+						if (typeof(a[i])=='undefined') { 
+						return false; 
+						} else { 
+						i++; 
+						}
+					}
+					
+					return true;
+				}
+				
+			function checkForm(event){
+				var stopPublish = false;
+				$$('input.field_required','textarea.field_required').each(
+						function(inputField){
+							if ($F(inputField) == "" &&
+								!(Object.isElement($(inputField.id+"_last")) && $F(inputField.id+"_last") != "")	){
+								stopPublish = true;
+
+								// Update row color
+								if (isset($("row_"+inputField.id).style))
+									$("row_"+inputField.id).style.backgroundColor = "#FFEBE8";
+
+								// Update iframe color if it exists
+								if (Object.isElement($("upload_internal_iframe_"+inputField.id))){
+								  	if ($("upload_internal_iframe_"+inputField.id).contentDocument) {
+								    	// For FF
+								    	$("upload_internal_iframe_"+inputField.id).contentDocument.body.style.backgroundColor = "#FFEBE8"; 
+								  	} else if ($("upload_internal_iframe_"+inputField.id).contentWindow) {
+									    // For IE5.5 and IE6
+									    $("upload_internal_iframe_"+inputField.id).contentWindow.document.body.style.backgroundColor = "#FFEBE8";
+								    }
+								}
+									
+								$("fieldcellerror_"+inputField.id).style.display = "";
+								$("fieldcellerror_"+inputField.id).innerHTML = "ERROR: Field can not be empty";
+							}
+							else{
+								$("fieldcellerror_"+inputField.id).style.display = "none";
+								if (isset($("row_"+inputField.id).style))
+									$("row_"+inputField.id).style.backgroundColor = "";
+									
+								// Update iframe color if it exists
+								if (Object.isElement($("upload_internal_iframe_"+inputField.id))){
+								  	if ($("upload_internal_iframe_"+inputField.id).contentDocument) {
+								    	// For FF
+								    	$("upload_internal_iframe_"+inputField.id).contentDocument.body.style.backgroundColor = "#EAF3FA"; 
+								  	} else if ($("upload_internal_iframe_"+inputField.id).contentWindow) {
+									    // For IE5.5 and IE6
+									    $("upload_internal_iframe_"+inputField.id).contentWindow.document.body.style.backgroundColor = "#EAF3FA";
+								    }
+								}
+									
+							}
+						}
+					);
+				if (stopPublish){
+					$("flutter-publish-error-message").style.display = "";
+					Event.stop(event);
+					return false;
+				}
+				
+				return true;
+			}
+
+			Event.observe(window, 'load', function() {
+				Event.observe('post', 'submit', checkForm);
+			});
+			
+			// -------------
+			// Edit Photo functions
+			
+			function setCookie(c_name,value,expiredays)
+			{
+				var exdate=new Date();
+				exdate.setDate(exdate.getDate()+expiredays);
+				document.cookie=c_name+ "=" +escape(value)+
+				((expiredays==null) ? "" : ";expires="+exdate.toGMTString());
+			}
+			function prepareUpdatePhoto(inputName){	
+				document.getElementById(inputName+'_dorename').value = '1';
+				return true;
+			}	
+			function exchangeValues(e, id)
+			{
+				//document.getElementById(document.getElementById('parent_text_'+id.substring(10)).value).value = e;
+				//document.getElementById(document.getElementById('hidImgValue'+id.substring(10)).value).value = e;
+			}
+			
+			// -------------
+			// Date Functions
+
+			var dp_cal = new Array(); // declare the calendars as global variables
+			
+			function pickDate(inputName){
+				if (dp_cal[inputName] && dp_cal[inputName].selectedDates[0]) document.getElementById('date_field_'+inputName).value = dp_cal[inputName].selectedDates[0].dateFormat('Y-m-d');
+			}
+			
+			function InitializeDateObject(inputName, dateFormat, currentValue){
+				if (!Object.isElement($('display_date_field_' + inputName))) return;
+				
+				dp_cal[inputName]  = new Epoch('dp_cal_'+inputName,'popup',document.getElementById('display_date_field_'+inputName), false, 'pickDate', inputName, dateFormat);
+				
+				var d = new Date();
+				
+				if (currentValue.length > 0){
+					d.setYear(parseInt(currentValue.substr(0,4),10));
+					d.setMonth(parseInt(currentValue.substr(5,2),10)-1);
+					d.setDate(parseInt(currentValue.substr(8,2),10));
+				}
+				d.selected = true;
+				d.canSelect = true;
+				var tmpDatesArray = new Array(d);
+				dp_cal[inputName].selectDates(tmpDatesArray, true, true, true);
+				if (dp_cal[inputName] && dp_cal[inputName].selectedDates[0]) 
+					document.getElementById('display_date_field_'+inputName).value = dp_cal[inputName].selectedDates[0].dateFormat(dateFormat);
+			}	
+
+			function today_date(inputName, dateFormat){
+				var d = new Date();
+				var tmpDatesArray = new Array(d);
+				dp_cal[inputName].selectDates(tmpDatesArray, true, true, true);
+				if (dp_cal[inputName] && dp_cal[inputName].selectedDates[0]){ 
+					document.getElementById('display_date_field_'+inputName).value = dp_cal[inputName].selectedDates[0].dateFormat(dateFormat);
+					document.getElementById('date_field_' + inputName).value = dp_cal[inputName].selectedDates[0].dateFormat('Y-m-d');
+				}
+			}
+			
+			// -------------
+			// Duplicates functions
+
+			function getDuplicate(fId,fcounter,div,gcounter)
+			{
+				$(div).setStyle({display: ""});
+				new Ajax.Request('<?php echo FLUTTER_URI; ?>RCCWP_GetDuplicate.php',
+				{
+					method:'post',
+					onSuccess: function(transport){
+						$(div).insert( {before: transport.responseText} );
+						$(div).setStyle({display: "none"});
+					},
+					parameters: "customFieldId="+fId+"&fieldCounter="+fcounter+"&groupCounter="+gcounter
+					});
+			}
+
+			function deleteDuplicate(fieldinputName)
+			{
+				var field = document.getElementById( fieldinputName ) ;
+ 				field.parentNode.removeChild(field) ;
+			}
+			
+			function GetGroupDuplicate(div, customGroupID)
+			{
+				customGroupCounter = $F('g'+customGroupID+'counter');
+				customGroupCounter ++;
+				$('g'+customGroupID+'counter').value = customGroupCounter;
+				$(div).setStyle({display: ""});
+				new Ajax.Request('<?php echo FLUTTER_URI; ?>RCCWP_GetDuplicate.php',
+				{
+					method:'post',
+					onSuccess: function(transport){
+					$(div).insert( {before: transport.responseText} );
+					$(div).setStyle({display: "none"});
+					},
+					parameters: "flag=group&groupId="+customGroupID+"&groupCounter="+customGroupCounter
+				});
+			}
+						
+			function deleteGroupDuplicate(groupDiv)
+			{
+				var group = document.getElementById( groupDiv ) ;
+				group.parentNode.removeChild( group ) ;
+			}
+			function typeHandler(inputName,customFieldId,groupCounter)
+			{
+				oldval = $F("c"+inputName+"Counter");
+				newval = parseInt(oldval) + 1;
+				$("c"+inputName+"Counter").value = newval ;
+				getDuplicate(customFieldId, $F("c"+inputName+"Counter"), "c"+inputName+"Duplicate",$(groupCounter)) ;
+			}
+
+		</script>
+
+		<?php
+		
+		// Show/Hide Panel fields
+	 
+		global $STANDARD_FIELDS;
+		
+		$standardFields = RCCWP_CustomWritePanel::GetStandardFields($CUSTOM_WRITE_PANEL->id);
+		$hideCssIds = array();
+		
+		foreach($STANDARD_FIELDS as $standardField){
+			if (!in_array($standardField->id, $standardFields)){
+				foreach($standardField->cssId as $cssID)
+					array_push($hideCssIds, $cssID);
+			}
+		}
+		
+		if (empty($hideCssIds))
+			return;
+		
+		array_walk($hideCssIds, create_function('&$item1, $key', '$item1 = "#" . $item1;'));
+		$hideCssIdString = implode(', ', $hideCssIds);
+		?>
+		
+		<style type="text/css">
+			<?php echo $hideCssIdString?> {display: none !important;}
+		</style>
+		
+		<?php
+		
+		/*<script type="text/javascript">
+			Event.observe(window, 'load', function() {
+				$$('h2').each(
+					function(element) {
+						if ($(element).innerHTML= "Advanced Options")
+							$(element).style.display="none";
+					});			
+			});
+		</script>*/
+		
+	}
+	
+	
+	function CustomFieldCollectionInterfaceRight(){
+		RCCWP_WritePostPage::CustomFieldCollectionInterface(true);
+	}
+	
+	function CustomFieldCollectionInterface($rightOnly = false)
+	{
+		global $CUSTOM_WRITE_PANEL;
+		
+		if (!isset($CUSTOM_WRITE_PANEL))
+			return;
+		
+		$customGroups = RCCWP_CustomWritePanel::GetCustomGroups($CUSTOM_WRITE_PANEL->id);
+		
+		foreach ($customGroups as $customGroup)
+		{
+			if (($rightOnly && !$customGroup->at_right) ||
+				(!$rightOnly && $customGroup->at_right))
+			{
+				continue;	
+			}
+			
+			// Render a block for each group in the module
+			?>
+			
+			<?php
+				$customFields = RCCWP_CustomGroup::GetCustomFields($customGroup->id);
+				if( isset( $_REQUEST['post'] ) && count($customFields) > 0)
+				{
+					$firstFieldName = $customFields[0]->name;
+					$gc = RCCWP_CustomField::GetFieldGroupDuplicates($_REQUEST['post'], $firstFieldName) ;
+					if ($gc < 1) $gc = 1;//for backward compatability
+					for($i=1;$i<=$gc;$i++)
+					{
+						RCCWP_WritePostPage::GroupDuplicate($customGroup,$i,false) ;
+					}
+				}
+				else
+				{
+					RCCWP_WritePostPage::GroupDuplicate($customGroup,1,false) ;
+					$gc = 1 ;
+				}
+			?>
+			 <div id="<?php echo $customGroup->id."Duplicate";?>" style="display:none">
+			 	<img class="duplicate_image"  src="<?php echo FLUTTER_URI; ?>images/spinner.gif" alt=""/> Loading ...					
+			 </div>
+			 <input type='hidden' name='g<?php echo $customGroup->id?>counter' id='g<?php echo $customGroup->id?>counter' value='<?php echo $gc?>' />
+		<?php	
+		}
+		?>
+			
+		<input type="hidden" name="rc-custom-write-panel-verify-key" id="rc-custom-write-panel-verify-key" value="<?php echo wp_create_nonce('rc-custom-write-panel')?>" />
+		<input type="hidden" name="rc-cwp-custom-write-panel-id" value="<?php echo $CUSTOM_WRITE_PANEL->id?>" />
+
+		<!-- rc_cwp_submit_buttons -->
+		<?php
+	}
+
+	function GroupDuplicate($customGroup, $groupCounter, $fromAjax=true)
+	{
+		$customFields = RCCWP_CustomGroup::GetCustomFields($customGroup->id);
+		if (count($customFields) == 0) return;
+		if( $customGroup->duplicate == 0 & $groupCounter != 1 ) return ;
+		require_once("RC_Format.php");
+		
+		if ( $customGroup->name == "__default"){	
+// 			$customGroup->name = "Module general fields";
+			$customGroup->name = "" ;
+		}
+		?>
+		
+		<?php if ($groupCounter==1){ ?>
+			<div id="freshpostdiv_group_<?php echo $customGroup->id.'_'.$groupCounter;?>" class="<?php if ($customGroup->name != "") echo "postbox ".postbox_classes('freshpostdiv_group_'.$customGroup->id, 'post');?>">
+			
+				<?php if ($customGroup->name != ""){ ?>
+					<h3> 
+						<?php echo  $customGroup->name ?>
+					</h3>
+				<?php } ?>
+		<?php } else { ?>
+			<div id="freshpostdiv_group_<?php echo $customGroup->id.'_'.$groupCounter;?>" class="postbox">
+				<?php if ($fromAjax){ ?>
+				<h3 onclick="jQuery(jQuery(this).parent().get(0)).toggleClass('closed');"> <a class="togbox">+</a>
+				<?php }else{ ?>
+				<h3>
+				<?php } ?>
+				<?php echo  $customGroup->name." ($groupCounter)" ?> 
+				</h3>
+		<?php }?>
+		<div class="inside">
+			<table class="form-table" style="width: 100%;" cellspacing="2" cellpadding="5">
+			<?php	
+			foreach ($customFields as $field)
+			{
+			// Render a row for each field in the group
+				$customField = RCCWP_CustomField::Get($field->id);
+				$customFieldName = RC_Format::GetInputName(attribute_escape($field->name));
+				$customFieldTitle = attribute_escape($customField->description);
+				$inputName = $field->id."_".$groupCounter."_1_".$customFieldName	
+
+			?>
+			
+				<?php
+					if( isset( $_REQUEST['post'] ) )
+					{
+						$fc = RCCWP_CustomField::GetFieldDuplicates($_REQUEST['post'],$field->name,$groupCounter) ;
+						if ($fc < 1) $fc = 1;//for backward compatability
+						for($i=1;$i<=$fc;$i++)
+						{
+							//if ($i>1) echo "<div id='field".$field->id."_$i"."_$groupCounter'>" ;
+							RCCWP_WritePostPage::CustomFieldInterface($field->id,$groupCounter,$i) ;
+							//if ($i>1) echo "</div>" ;
+						}
+					}
+					else
+					{
+						RCCWP_WritePostPage::CustomFieldInterface($field->id,$groupCounter,1);
+						$fc = 1 ;
+					}
+					
+					
+				?>
+				
+				<tr style="display:none" id="<?php echo "c".$inputName."Duplicate"?>">
+					<th valign="top" scope="row">
+					</th>
+					<td>
+						<img class="duplicate_image"  src="<?php echo FLUTTER_URI; ?>images/spinner.gif" alt=""/> Loading ... 
+						<input type="hidden" name="c<?php echo $inputName ?>Counter" id="c<?php echo $inputName ?>Counter" value='<?php echo $fc ?>' /> 
+					</td>
+				</tr>
+					
+				
+			<?php }
+			?>
+				
+		</table>
+		</div>
+		<br />
+		<?php
+		if( $customGroup->duplicate != 0 ){
+			if( $groupCounter == 1 ){
+			?>
+				<a class ="duplicate_button" href="javascript:GetGroupDuplicate('<?php echo $customGroup->id."Duplicate";?>', <?php echo $customGroup->id ?>)"> 
+					<img class="duplicate_image" src="<?php echo FLUTTER_URI; ?>images/duplicate.png" alt="Add group duplicate"/> Duplicate Group 
+				</a>
+				<br style="height:2px"/>
+			<?php
+			}
+			else {
+			?>
+				<a class ="duplicate_button" href="javascript:deleteGroupDuplicate('freshpostdiv_group_<?php echo $customGroup->id.'_'.$groupCounter ?>')" > 
+					<img class="duplicate_image"  src="<?php echo FLUTTER_URI; ?>images/delete.png" alt="Remove field duplicate"/>Remove Group 
+				</a>
+				<br style="height:2px"/>
+			<?php }
+		}
+		?>
+		</div>
+		
+		<?php
+	}
+
+	function CustomFieldInterface($customFieldId, $groupCounter=1, $fieldCounter=1)
+	{
+		require_once("RC_Format.php");
+		$customField = RCCWP_CustomField::Get($customFieldId);
+		$customFieldName = RC_Format::GetInputName(attribute_escape($customField->name));
+		$customFieldTitle = attribute_escape($customField->description);
+		$inputName = $customFieldId."_".$groupCounter."_".$fieldCounter."_".$customFieldName; // Create input tag name
+ 		if( $fieldCounter > 1 && $customField->duplicate == 0 ) return ;
+ 		if( $fieldCounter > 1) $titleCounter = " ($fieldCounter)";
+ 		
+ 		$field_group = RCCWP_CustomGroup::Get($customField->group_id);
+
+		?>
+		<tr class="form-field" id="row_<?php echo $inputName?>">
+			<?php
+				// If the field is at right, put the header over the field
+				if ($field_group->at_right){
+			?>
+			<td>
+				<label style="font-weight:bold" for="<?php echo $inputName?>"><?php echo $customFieldTitle.$titleCounter?></label>
+				<br />
+			<?php
+				} else {
+			?>
+			<th valign="top" scope="row">
+				<label for="<?php echo $inputName?>"><?php echo $customFieldTitle.$titleCounter?></label>
+			</th>
+			<td>
+			<?php
+				}
+			?>
+				
+				<p class="error_msg_txt" id="fieldcellerror_<?php echo $inputName?>" style="display:none"></p>
+				<?php		
+					switch ($customField->type)
+					{
+						case 'Textbox' :
+							RCCWP_WritePostPage::TextboxInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Multiline Textbox' :
+							RCCWP_WritePostPage::MultilineTextboxInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Checkbox' :
+							RCCWP_WritePostPage::CheckboxInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Checkbox List' :
+							RCCWP_WritePostPage::CheckboxListInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Radiobutton List' :
+							RCCWP_WritePostPage::RadiobuttonListInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Dropdown List' :
+							RCCWP_WritePostPage::DropdownListInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Listbox' :
+							RCCWP_WritePostPage::ListboxInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'File' :
+							RCCWP_WritePostPage::FileInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Image' :
+							RCCWP_WritePostPage::PhotoInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Date' :
+							RCCWP_WritePostPage::DateInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						case 'Audio' :
+							RCCWP_WritePostPage::AudioInterface($customField, $inputName, $groupCounter, $fieldCounter);
+							break;
+						default:
+							;
+					}
+
+
+				if($fieldCounter == 1)
+				{
+					?>
+					<?php if($customField->duplicate != 0 ){ ?>
+					<br />
+					
+					<a class ="duplicate_button" href="javascript:typeHandler('<?php echo $inputName ?>',<?php echo $customFieldId ?>,<?php echo $groupCounter ?>)" > 
+						<img class="duplicate_image"  src="<?php echo FLUTTER_URI; ?>images/duplicate.png" alt="Add field duplicate"/> Duplicate 
+					</a>
+					<?php } ?>
+					 
+					<?php
+				}
+				else
+				{	
+				?>
+					<br />
+					
+					<a class ="duplicate_button" href="javascript:deleteDuplicate('row_<?php echo $inputName?>')" > 
+						<img class="duplicate_image"  src="<?php echo FLUTTER_URI; ?>images/delete.png" alt="Remove field duplicate"/> Remove 
+					</a>
+				<?php
+				}
+				?>
+				<input type="hidden" name="rc_cwp_meta_keys[]" value="<?php echo $inputName?>" />
+			</td>
+		</tr>
+	<?php
+	}
+	
+	function CheckboxInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter);
+			$checked = $value == 'true' ? 'checked="checked"' : '';
+		}
+		?>
+		
+		<input type="hidden" name="<?php echo $inputName?>" value="false" />
+		<input tabindex="3" class="checkbox" name="<?php echo $inputName?>" value="true" id="<?php echo $inputName?>" type="checkbox" <?php echo $checked?> />
+		
+		<?php
+	}
+	
+	function CheckboxListInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		$values = array();
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$values = (array) RCCWP_CustomField::GetCustomFieldValues(false, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter);
+		}
+		else
+		{
+			$values = $customField->default_value;
+		}
+		?>
+		
+		
+		<?php
+		foreach ($customField->options as $option) :
+			$checked = in_array($option, (array)$values) ? 'checked="checked"' : '';
+			$option = attribute_escape(trim($option));
+		?>
+		
+			<label for="" class="selectit">
+				<input tabindex="3" id="<?php echo $option?>" name="<?php echo $inputName?>[]" value="<?php echo $option?>" type="checkbox" <?php echo $checked?>/>
+				<?php echo attribute_escape($option)?>
+			</label><br />
+		
+		<?php
+		endforeach;
+		?>
+			
+		
+		<?php
+	}
+	
+	function DropdownListInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter));
+		}
+		else
+		{
+			$value = $customField->default_value[0];
+		}
+		
+		if ($customField->required_field) $requiredClass = "field_required";
+		?>
+		
+		<select tabindex="3"  class="<?php echo $requiredClass;?>"  name="<?php echo $inputName?>">
+			<option value="">--Select--</option>
+		
+		<?php
+		foreach ($customField->options as $option) :
+			$selected = $option == $value ? 'selected="selected"' : '';
+			$option = attribute_escape(trim($option));
+		?>
+		
+			<option value="<?php echo $option?>" <?php echo $selected?>><?php echo $option?></option>
+		
+		<?php
+		endforeach;
+		?>
+		
+		</select>	
+		
+		
+		<?php
+	}
+	
+	function ListboxInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$values = (array) RCCWP_CustomField::GetCustomFieldValues(false, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter);
+		}
+		else
+		{
+			$values = $customField->default_value;
+		}
+		
+		$inputSize = (int)$customField->properties['size'];
+		if ($customField->required_field) $requiredClass = "field_required";
+		?>
+		
+		<select  class="<?php echo $requiredClass;?>"  tabindex="3" id="<?php echo $inputName?>" name="<?php echo $inputName?>[]" multiple size="<?php echo $inputSize?>">
+		
+		<?php
+		foreach ($customField->options as $option) :
+			$selected = in_array($option, (array)$values) ? 'selected="selected"' : '';
+			$option = attribute_escape(trim($option));
+		?>
+			
+			<option value="<?php echo $option?>" <?php echo $selected?>><?php echo $option?></option>
+			
+		<?php
+		endforeach;
+		?>
+		
+		</select>
+		
+		
+		<?php
+	}
+	
+	function MultilineTextboxInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter));
+		}
+		
+		$inputHeight = (int)$customField->properties['height'];
+		$inputWidth = (int)$customField->properties['width'];
+		if ($customField->required_field) $requiredClass = "field_required";
+		
+		?>
+		<script type="text/javascript">
+			//Event.observe(window, 'load', function() {
+				tinyMCE.execCommand('mceAddControl', false, "<?php echo $inputName?>");
+			//});
+			</script>
+		
+		<textarea  class="<?php echo $requiredClass;?>" tabindex="3"  id="<?php echo $inputName?>" name="<?php echo $inputName?>" rows="<?php echo $inputHeight?>" cols="<?php echo $inputWidth?>"><?php echo $value?></textarea>
+		
+		
+		<?php
+	}
+	
+	function TextboxInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter));
+		}
+		
+		
+		$inputSize = (int)$customField->properties['size'];
+		if ($customField->required_field) $requiredClass = "field_required";
+		
+		// If the field is at right, set a constant width to the text box
+		$field_group = RCCWP_CustomGroup::Get($customField->group_id);
+		if ($field_group->at_right){
+			if ($inputSize>14) $inputSize = 14;
+		}
+		?>
+		
+		<input class="<?php echo $requiredClass;?>" tabindex="3" id="<?php echo $inputName?>" name="<?php echo $inputName?>" value="<?php echo $value?>" type="text" size="<?php echo $inputSize?>" />
+		
+		<?php
+	}
+	
+
+	function FileInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		$freshPageFolderName = (dirname(plugin_basename(__FILE__)));
+		if ($customField->required_field) $requiredClass = "field_required";
+
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter));
+			$path = CANVASURI.'files_flutter/';
+			$valueRelative = $value;
+			$value = $path.$value;
+		}
+		
+		// If the field is at right, set a constant width to the text box
+		$field_group = RCCWP_CustomGroup::Get($customField->group_id);
+		$urlInputSize = false;
+		$is_canvas = 0;
+		if ($field_group->at_right){
+			$urlInputSize = 5;
+			$is_canvas = 1;
+		}
+
+		?>
+		
+		<p class="error_msg_txt" id="upload_progress_<?php echo $inputName?>" style="visibility:hidden;height:0px"></p>
+		
+		<?php if( $valueRelative ){ echo '(<a href="' . $value . '" target="_blank">View Current</a>)'; } ?>
+			
+		<input tabindex="3" 
+			id="<?php echo $inputName?>" 
+			name="<?php echo $inputName?>" 
+			type="hidden"
+			class="<?php echo $requiredClass;?>" 
+			size="46"
+			value="<?php echo $valueRelative?>"
+			/>
+		
+		<?php
+		include_once( "RCCWP_SWFUpload.php" ) ;
+		RCCWP_SWFUpload::Body($inputName, 0, $is_canvas, $urlInputSize) ;
+	}
+
+
+	function PhotoInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId 	= '';
+		$filepath 		= $inputName . '_filepath';
+		$noimage 		= "";
+		$freshPageFolderName = (dirname(plugin_basename(__FILE__)));
+		if ($customField->required_field) $requiredClass = "field_required";
+
+		//global $countImageThumbID;
+		$imageThumbID = "";
+		$imageThumbID = "img_thumb_".$inputName; //.++$countImageThumbID;
+
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter);
+
+			$path = CANVASURI.'phpThumb.php?src='.CANVASURI.'files_flutter/';
+			$valueRelative = $value;
+			$value = $path.$value;
+			if(!(strpos($value, 'http') === FALSE))
+				$hidValue = str_replace('"', "'", $valueRelative);
+			$value = stripslashes(trim("\<img src=\'".$value."\' class=\"freshout\" \/\>"));
+		}
+		else
+		{
+			$noimage = "<img src='".CANVASURI."images/noimage.jpg' id='".$imageThumbID."'/>";
+		}
+		if($valueRelative == '')
+		{
+			$noimage = "<img src='".CANVASURI."images/noimage.jpg' id='".$imageThumbID."'/>";
+		}
+
+		include_once('RCCWP_Options.php');
+		$useSnipshot = RCCWP_Options::Get('use-snipshot');
+
+		// If the field is at right, set a constant width to the text box
+		$field_group = RCCWP_CustomGroup::Get($customField->group_id);
+		$urlInputSize = false;
+		$is_canvas = 0;
+		if ($field_group->at_right){
+			$urlInputSize = 5;
+			$is_canvas = 1;
+		}
+	
+		?>
+		
+		<p class="error_msg_txt" id="upload_progress_<?php echo $inputName?>" style="visibility:hidden;height:0px"></p>
+		 
+		<div id="image_photo" style="width:150px;">
+		
+			<?php
+				if($valueRelative != "")
+				{ 
+					if(!(strpos($value, '<img src') === FALSE))
+					{
+						$valueLinkArr = explode("'", $value);
+						$valueLink = $valueLinkArr[1];
+						//$valueLink = $value;
+
+						if(!(strpos($value, '&sw') === FALSE))
+						{
+							// Calculating Image Width/Height
+							$arrSize = explode("=",$value);
+							$arrSize1 = explode("&",$arrSize[3]);
+							$arrSize2 = explode("&",$arrSize[4]);
+
+							$imageWidth = $arrSize1[0];
+							$imageHeight = $arrSize2[0];
+							// END
+
+							$valueArr = explode("&sw", $value);
+							$valueArr = explode("'", $valueArr[1]);
+							$value = str_replace("&sw".$valueArr[0]."'", "&sw".$valueArr[0]."&w=150&h=120' align='center' id='".$imageThumbID."'", $value);
+						}
+						else if(!(strpos($value, '&w') === FALSE))
+						{
+							// Calculating Image Width/Height
+							$arrSize = explode("=",$value);
+							$arrSize1 = explode("&",$arrSize[3]);
+							$arrSize2 = explode("'",$arrSize[4]);
+
+							$imageWidth = $arrSize1[0];
+							$imageHeight = $arrSize2[0];
+							// END
+
+							$valueArr = explode("&", $value);
+							$valueArr = explode("'", $valueArr[2]);
+							$value = str_replace($valueArr[0], "&w=150&h=120' align='left' id='".$imageThumbID."'", $value);
+						}
+						else
+						{
+							// Calculating Image Width/Height
+							$arrSize = explode("&",$params);
+							$arrSize1 = explode("=",$arrSize[1]);
+							$arrSize2 = explode("=",$arrSize[2]);
+
+							$imageWidth = $arrSize1[1];
+							$imageHeight = $arrSize2[1];
+							// END
+
+							$valueArr = explode("'", $value);
+							$value = str_replace($valueArr[1], $valueArr[1]."&w=150' id='".$imageThumbID."' align='", $value);
+						}
+						if(!empty($imageWidth))
+						{
+						?>
+						<script language="javascript">
+							//document.getElementById('spanImageSize').innerHTML = " &nbsp; (<?php echo $imageWidth; echo ' x '; echo $imageHeight; ?>)";
+						</script>
+
+						<?php
+						}
+							echo '<a style="display: block;margin-left: auto;margin-right: auto " href="' . $valueLink . '" target="_blank">' . $value .'</a>';
+						}
+					}
+					echo $noimage;
+					$arrSize = explode("phpThumb.php?src=",$valueLink);
+					$fileLink = $arrSize[1];
+					$andPos = strpos($arrSize[1],"?");
+					if ($andPos === FALSE)	 $andPos = strpos($arrSize[1],"&");
+				
+					// Remove & parameters from file path
+					if ($andPos>0)	$fileLink = substr($arrSize[1], 0, $andPos);
+				
+					$ext = substr($fileLink, -3, 3);	
+					
+					
+					/*$arrSizeext = explode("&",$arrSize[1]);
+					if (sizeof($arrSizeext) > 1){
+					$ext = substr($arrSizeext[0],-4, -1);
+					$fileLink = substr($arrSizeext[0],0, -1);
+					}
+					else	{
+					$ext = substr($arrSizeext[0],-3);
+					$fileLink = $arrSizeext[0];
+					}*/
+			?>	
+		
+		<div id="photo_edit_link_<?php echo $inputName ?>" class="photo_edit_link"> 
+			
+				<?php
+				if(isset($_REQUEST['post']) && $hidValue != '')
+				{
+					if ($useSnipshot){ 
+						echo "<a href='".RCCWP_WritePostPage::snipshot_anchor($fileLink)."' class='thickbox' tittle='Flutter'<strong onclick=prepareUpdatePhoto('$inputName')>Edit</strong> </a>" ;
+					}else{
+						$cropperLink = CANVASURI."cropper.php?input_name=".urlencode($inputName)."&id=".urlencode($hidValue)."&url=".urlencode($_SERVER['REQUEST_URI'])."&imageThumbId=$imageThumbID";
+				?>
+						<a  rel="gb_page_fs[]" href="<?php echo $cropperLink ?>" title="Flutter" class="greybox" id="lnkCropper"> <strong>Crop</strong> </a>
+				<?php 	
+					} 
+				}
+				?>
+			
+		</div>
+		</div>
+		<br />
+		<div id="image_input">
+					
+			<input tabindex="3" 
+				id="<?php echo $inputName?>" 
+				name="<?php echo $inputName?>" 
+				type="hidden" 
+				class="<?php echo $requiredClass;?>"
+				size="46"
+				value="<?php echo $hidValue?>"
+				/>
+			
+			<?php
+			include_once( "RCCWP_SWFUpload.php" ) ;
+			RCCWP_SWFUpload::Body($inputName, 1, $is_canvas, $urlInputSize) ;
+			?>
+
+		</div>
+		
+		<input type="hidden" name="rc_cwp_meta_photos[]" value="<?php echo $inputName?>" 	/>
+		<input type="hidden" name="<?php echo $inputName?>_dorename" id="<?php echo $inputName?>_dorename" value="0" />
+		
+
+		<!-- Used to store name of URL Field -->
+		<!--<input type="hidden" name="parent_text_<?php echo $countImageThumbID; ?>" id="parent_text_<?php echo $countImageThumbID; ?>" value="<?php echo $filepath; ?>"/>
+		<input type="hidden" name="hidImgValue<?php echo $countImageThumbID; ?>" id="hidImgValue<?php echo $countImageThumbID; ?>" value="<?php echo $inputName; ?>_last" />-->
+
+		<?php
+	}
+
+	function snipshot_anchor($fileLink)
+	{
+/*
+		return '<a rel="gb_page_fs[]" href="http://services.snipshot.com/?snipshot_input='. urlencode($fileLink).'&snipshot_callback='.urlencode(CANVASURI."RCCWP_SnipshotCallback.php").'&snipshot_output=file&snipshot_callback_agent=user&test=hello&snipshot_output_options='.urlencode("{\"filetype\":\"$ext\"}").' title="Flutter" class="greybox1" id="lnkCropper">' ;
+
+		return '<a href="http://services.snipshot.com/?snipshot_input='. urlencode($fileLink).'&snipshot_callback='.urlencode(CANVASURI."RCCWP_SnipshotCallback.php").'&snipshot_output=file&snipshot_callback_agent=user&test=hello&snipshot_output_options='.urlencode("{\"filetype\":\"$ext\"}").'&KeepThis=true&TB_iframe=true&height=400&width=600" class="thickbox" title="flutter" >' ;
+*/
+		return 'http://services.snipshot.com/?snipshot_input='. urlencode($fileLink).'&snipshot_callback='.urlencode(CANVASURI."RCCWP_SnipshotCallback.php").'&snipshot_output=file&snipshot_callback_agent=user&test=hello&snipshot_output_options='.urlencode("{\"filetype\":\"$ext\"}").'&KeepThis=true&TB_iframe=true&height=400&width=600' ;
+	}
+	
+	function RadiobuttonListInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		
+		if (isset($_REQUEST['post']))
+		{
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter));
+		}
+		else
+		{
+			$value = $customField->default_value[0];
+		}
+		?>
+		
+		<?php
+		foreach ($customField->options as $option) :
+			$checked = $option == $value ? 'checked="checked"' : '';
+			$option = attribute_escape(trim($option));
+		?>
+			<label for="" class="selectit">
+				<input tabindex="3" id="<?php echo $option?>" name="<?php echo $inputName?>" value="<?php echo $option?>" type="radio" <?php echo $checked?>/>
+				<?php echo $option?>
+			</label><br />
+		<?php
+		endforeach;
+		?>
+		
+		<?php
+	}
+
+	function DateInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		global $wpdb;
+		$customFieldId = '';
+		
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$value = attribute_escape(RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter));
+		}
+		else
+			$value = strftime("%Y-%m-%d");
+		
+	
+
+		$sQuery = "SELECT * FROM " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " WHERE custom_field_id='".$customField->id."'";
+		$result = $wpdb->get_results($sQuery);
+
+		$arrDateFormat = explode('"', $result[0]->properties);
+		$dateFormat = $arrDateFormat[3];
+		
+		// If the field is at right, set a constant width to the text box
+		$field_group = RCCWP_CustomGroup::Get($customField->group_id);
+		$inputSize = 25;
+		if ($field_group->at_right){
+			$inputSize = 15;
+		}
+		
+
+?>
+		<script type="text/javascript">
+			
+			addEventHandler(window, 'load',function () {
+				InitializeDateObject('<?php echo $inputName?>', '<?php echo $dateFormat?>', '<?php echo $value?>');
+			}); 
+			
+			InitializeDateObject('<?php echo $inputName?>', '<?php echo $dateFormat?>', '<?php echo $value?>');
+			
+			
+		</script>	
+
+		
+		<input tabindex="3" id="display_date_field_<?php echo $inputName?>" value="<?php echo $value?>" type="text" size="<?php echo $inputSize?>" READONLY />
+		<input tabindex="3" id="date_field_<?php echo $inputName?>" name="<?php echo $inputName?>" value="<?php echo $value?>" type="hidden" />
+		<input type="button" value="Pick..." onclick="dp_cal['<?php echo $inputName?>'].toggle();" />
+		<input type="button" value="Today" onclick="today_date('<?php echo $inputName?>', '<?php echo $dateFormat?>');" />
+
+		<input type="hidden" name="rc_cwp_meta_date[]" value="<?php echo $inputName?>" 	/>
+
+		
+		<?php
+	}
+
+	function AudioInterface($customField, $inputName, $groupCounter, $fieldCounter)
+	{
+		$customFieldId = '';
+		$freshPageFolderName = (dirname(plugin_basename(__FILE__)));
+		if ($customField->required_field) $requiredClass = "field_required";
+		
+
+		if (isset($_REQUEST['post']))
+		{
+			$customFieldId = $customField->id;
+			$valueOriginal = RCCWP_CustomField::GetCustomFieldValues(true, $_REQUEST['post'], $customField->name, $groupCounter, $fieldCounter);
+			$path = CANVASURI . 'files_flutter/';
+			$$valueOriginalRelative = $valueOriginal;
+			$valueOriginal = $path.$valueOriginal;
+			if (!empty($valueOriginal))
+				$value = stripslashes(trim("\<div style=\'width:260px;padding-top:3px;\'\>\<object classid=\'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\' codebase='\http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0\' width=\'95%\' height=\'20\' wmode=\'transparent\' \>\<param name=\'movie\' value=\'".CANVASURI."js/singlemp3player.swf?file=".urlencode($valueOriginal)."\' wmode=\'transparent\' /\>\<param name=\'quality\' value=\'high\' wmode=\'transparent\' /\>\<embed src=\'".CANVASURI."js/singlemp3player.swf?file=".urlencode($valueOriginal)."' width=\'100\%\' height=\'20\' quality=\'high\' pluginspage=\'http://www.macromedia.com/go/getflashplayer\' type=\'application/x-shockwave-flash\' wmode=\'transparent\' \>\</embed\>\</object\>\</div\><br />"));			
+		}
+		
+		// If the field is at right, set a constant width to the text box
+		$field_group = RCCWP_CustomGroup::Get($customField->group_id);
+		$urlInputSize = false;
+		$is_canvas = 0;
+		if ($field_group->at_right){
+			$urlInputSize = 5;
+			$is_canvas = 1;
+		}
+
+		?>
+		<p class="error_msg_txt" id="upload_progress_<?php echo $inputName?>" style="visibility:hidden;height:0px"></p>
+		 
+		
+		<?php if( $$valueOriginalRelative ){ echo $value; } ?>
+		
+		
+		<input tabindex="3" 
+			id="<?php echo $inputName?>" 
+			name="<?php echo $inputName?>" 
+			type="hidden" 
+			class="<?php echo $requiredClass;?>"
+			size="46"
+			value="<?php echo $$valueOriginalRelative?>"			
+			/>
+		
+
+		<?php
+		include_once( "RCCWP_SWFUpload.php" ) ;
+		RCCWP_SWFUpload::Body($inputName, 2, $is_canvas, $urlInputSize) ;
+		
+	}
+
+}
+?>
Index: /afridex/plugins/Flutter/phpthumb.functions.php
===================================================================
--- /afridex/plugins/Flutter/phpthumb.functions.php (revision 21)
+++ /afridex/plugins/Flutter/phpthumb.functions.php (revision 21)
@@ -0,0 +1,963 @@
+<?php
+//////////////////////////////////////////////////////////////
+///  phpThumb() by James Heinrich <info@silisoftware.com>   //
+//        available at http://phpthumb.sourceforge.net     ///
+//////////////////////////////////////////////////////////////
+///                                                         //
+// phpthumb.functions.php - general support functions       //
+//                                                         ///
+//////////////////////////////////////////////////////////////
+
+class phpthumb_functions {
+
+	function user_function_exists($functionname) {
+		if (function_exists('get_defined_functions')) {
+			static $get_defined_functions = array();
+			if (empty($get_defined_functions)) {
+				$get_defined_functions = get_defined_functions();
+			}
+			return in_array(strtolower($functionname), $get_defined_functions['user']);
+		}
+		return function_exists($functionname);
+	}
+
+
+	function builtin_function_exists($functionname) {
+		if (function_exists('get_defined_functions')) {
+			static $get_defined_functions = array();
+			if (empty($get_defined_functions)) {
+				$get_defined_functions = get_defined_functions();
+			}
+			return in_array(strtolower($functionname), $get_defined_functions['internal']);
+		}
+		return function_exists($functionname);
+	}
+
+
+	function version_compare_replacement_sub($version1, $version2, $operator='') {
+		// If you specify the third optional operator argument, you can test for a particular relationship.
+		// The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively.
+		// Using this argument, the function will return 1 if the relationship is the one specified by the operator, 0 otherwise.
+
+		// If a part contains special version strings these are handled in the following order: dev < (alpha = a) < (beta = b) < RC < pl
+		static $versiontype_lookup = array();
+		if (empty($versiontype_lookup)) {
+			$versiontype_lookup['dev']   = 10001;
+			$versiontype_lookup['a']     = 10002;
+			$versiontype_lookup['alpha'] = 10002;
+			$versiontype_lookup['b']     = 10003;
+			$versiontype_lookup['beta']  = 10003;
+			$versiontype_lookup['RC']    = 10004;
+			$versiontype_lookup['pl']    = 10005;
+		}
+		if (isset($versiontype_lookup[$version1])) {
+			$version1 = $versiontype_lookup[$version1];
+		}
+		if (isset($versiontype_lookup[$version2])) {
+			$version2 = $versiontype_lookup[$version2];
+		}
+
+		switch ($operator) {
+			case '<':
+			case 'lt':
+				return intval($version1 < $version2);
+				break;
+			case '<=':
+			case 'le':
+				return intval($version1 <= $version2);
+				break;
+			case '>':
+			case 'gt':
+				return intval($version1 > $version2);
+				break;
+			case '>=':
+			case 'ge':
+				return intval($version1 >= $version2);
+				break;
+			case '==':
+			case '=':
+			case 'eq':
+				return intval($version1 == $version2);
+				break;
+			case '!=':
+			case '<>':
+			case 'ne':
+				return intval($version1 != $version2);
+				break;
+		}
+		if ($version1 == $version2) {
+			return 0;
+		} elseif ($version1 < $version2) {
+			return -1;
+		}
+		return 1;
+	}
+
+
+	function version_compare_replacement($version1, $version2, $operator='') {
+		if (function_exists('version_compare')) {
+			// built into PHP v4.1.0+
+			return version_compare($version1, $version2, $operator);
+		}
+
+		// The function first replaces _, - and + with a dot . in the version strings
+		$version1 = strtr($version1, '_-+', '...');
+		$version2 = strtr($version2, '_-+', '...');
+
+		// and also inserts dots . before and after any non number so that for example '4.3.2RC1' becomes '4.3.2.RC.1'.
+		// Then it splits the results like if you were using explode('.',$ver). Then it compares the parts starting from left to right.
+		$version1 = eregi_replace('([0-9]+)([A-Z]+)([0-9]+)', '\\1.\\2.\\3', $version1);
+		$version2 = eregi_replace('([0-9]+)([A-Z]+)([0-9]+)', '\\1.\\2.\\3', $version2);
+
+		$parts1 = explode('.', $version1);
+		$parts2 = explode('.', $version1);
+		$parts_count = max(count($parts1), count($parts2));
+		for ($i = 0; $i < $parts_count; $i++) {
+			$comparison = phpthumb_functions::version_compare_replacement_sub($version1, $version2, $operator);
+			if ($comparison != 0) {
+				return $comparison;
+			}
+		}
+		return 0;
+	}
+
+
+	function phpinfo_array() {
+		static $phpinfo_array = array();
+		if (empty($phpinfo_array)) {
+			ob_start();
+			phpinfo();
+			$phpinfo = ob_get_contents();
+			ob_end_clean();
+			$phpinfo_array = explode("\n", $phpinfo);
+		}
+		return $phpinfo_array;
+	}
+
+
+	function exif_info() {
+		static $exif_info = array();
+		if (empty($exif_info)) {
+			// based on code by johnschaefer at gmx dot de
+			// from PHP help on gd_info()
+			$exif_info = array(
+				'EXIF Support'           => '',
+				'EXIF Version'           => '',
+				'Supported EXIF Version' => '',
+				'Supported filetypes'    => ''
+			);
+			$phpinfo_array = phpthumb_functions::phpinfo_array();
+			foreach ($phpinfo_array as $line) {
+				$line = trim(strip_tags($line));
+				foreach ($exif_info as $key => $value) {
+					if (strpos($line, $key) === 0) {
+						$newvalue = trim(str_replace($key, '', $line));
+						$exif_info[$key] = $newvalue;
+					}
+				}
+			}
+		}
+		return $exif_info;
+	}
+
+
+	function ImageTypeToMIMEtype($imagetype) {
+		if (function_exists('image_type_to_mime_type') && ($imagetype >= 1) && ($imagetype <= 16)) {
+			// PHP v4.3.0+
+			return image_type_to_mime_type($imagetype);
+		}
+		static $image_type_to_mime_type = array(
+			1  => 'image/gif',                     // IMAGETYPE_GIF
+			2  => 'image/jpeg',                    // IMAGETYPE_JPEG
+			3  => 'image/png',                     // IMAGETYPE_PNG
+			4  => 'application/x-shockwave-flash', // IMAGETYPE_SWF
+			5  => 'image/psd',                     // IMAGETYPE_PSD
+			6  => 'image/bmp',                     // IMAGETYPE_BMP
+			7  => 'image/tiff',                    // IMAGETYPE_TIFF_II (intel byte order)
+			8  => 'image/tiff',                    // IMAGETYPE_TIFF_MM (motorola byte order)
+			9  => 'application/octet-stream',      // IMAGETYPE_JPC
+			10 => 'image/jp2',                     // IMAGETYPE_JP2
+			11 => 'application/octet-stream',      // IMAGETYPE_JPX
+			12 => 'application/octet-stream',      // IMAGETYPE_JB2
+			13 => 'application/x-shockwave-flash', // IMAGETYPE_SWC
+			14 => 'image/iff',                     // IMAGETYPE_IFF
+			15 => 'image/vnd.wap.wbmp',            // IMAGETYPE_WBMP
+			16 => 'image/xbm',                     // IMAGETYPE_XBM
+
+			'gif'  => 'image/gif',                 // IMAGETYPE_GIF
+			'jpg'  => 'image/jpeg',                // IMAGETYPE_JPEG
+			'jpeg' => 'image/jpeg',                // IMAGETYPE_JPEG
+			'png'  => 'image/png',                 // IMAGETYPE_PNG
+			'bmp'  => 'image/bmp',                 // IMAGETYPE_BMP
+			'ico'  => 'image/x-icon',
+		);
+
+		return (isset($image_type_to_mime_type[$imagetype]) ? $image_type_to_mime_type[$imagetype] : false);
+	}
+
+
+	function HexCharDisplay($string) {
+		$len = strlen($string);
+		$output = '';
+		for ($i = 0; $i < $len; $i++) {
+			$output .= ' 0x'.str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
+		}
+		return $output;
+	}
+
+
+	function IsHexColor($HexColorString) {
+		return eregi('^[0-9A-F]{6}$', $HexColorString);
+	}
+
+
+	function ImageColorAllocateAlphaSafe(&$gdimg_hexcolorallocate, $R, $G, $B, $alpha=false) {
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.2', '>=') && ($alpha !== false)) {
+			return ImageColorAllocateAlpha($gdimg_hexcolorallocate, $R, $G, $B, intval($alpha));
+		} else {
+			return ImageColorAllocate($gdimg_hexcolorallocate, $R, $G, $B);
+		}
+	}
+
+	function ImageHexColorAllocate(&$gdimg_hexcolorallocate, $HexColorString, $dieOnInvalid=false, $alpha=false) {
+		if (!is_resource($gdimg_hexcolorallocate)) {
+			die('$gdimg_hexcolorallocate is not a GD resource in ImageHexColorAllocate()');
+		}
+		if (phpthumb_functions::IsHexColor($HexColorString)) {
+			$R = hexdec(substr($HexColorString, 0, 2));
+			$G = hexdec(substr($HexColorString, 2, 2));
+			$B = hexdec(substr($HexColorString, 4, 2));
+			return phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_hexcolorallocate, $R, $G, $B, $alpha);
+		}
+		if ($dieOnInvalid) {
+			die('Invalid hex color string: "'.$HexColorString.'"');
+		}
+		return ImageColorAllocate($gdimg_hexcolorallocate, 0x00, 0x00, 0x00);
+	}
+
+
+	function HexColorXOR($hexcolor) {
+		return strtoupper(str_pad(dechex(~hexdec($hexcolor) & 0xFFFFFF), 6, '0', STR_PAD_LEFT));
+	}
+
+
+	function GetPixelColor(&$img, $x, $y) {
+		if (!is_resource($img)) {
+			return false;
+		}
+		return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y));
+	}
+
+
+	function GrayscaleValue($r, $g, $b) {
+		return round(($r * 0.30) + ($g * 0.59) + ($b * 0.11));
+	}
+
+
+	function GrayscalePixel($OriginalPixel) {
+		$gray = phpthumb_functions::GrayscaleValue($OriginalPixel['red'], $OriginalPixel['green'], $OriginalPixel['blue']);
+		return array('red'=>$gray, 'green'=>$gray, 'blue'=>$gray);
+	}
+
+
+	function GrayscalePixelRGB($rgb) {
+		$r = ($rgb >> 16) & 0xFF;
+		$g = ($rgb >>  8) & 0xFF;
+		$b =  $rgb        & 0xFF;
+		return ($r * 0.299) + ($g * 0.587) + ($b * 0.114);
+	}
+
+
+	function ImageCopyResampleBicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) {
+		// ron at korving dot demon dot nl
+		// http://www.php.net/imagecopyresampled
+
+		$scaleX = ($src_w - 1) / $dst_w;
+		$scaleY = ($src_h - 1) / $dst_h;
+
+		$scaleX2 = $scaleX / 2.0;
+		$scaleY2 = $scaleY / 2.0;
+
+		$isTrueColor = ImageIsTrueColor($src_img);
+
+		for ($y = $src_y; $y < $src_y + $dst_h; $y++) {
+			$sY   = $y * $scaleY;
+			$siY  = (int) $sY;
+			$siY2 = (int) $sY + $scaleY2;
+
+			for ($x = $src_x; $x < $src_x + $dst_w; $x++) {
+				$sX   = $x * $scaleX;
+				$siX  = (int) $sX;
+				$siX2 = (int) $sX + $scaleX2;
+
+				if ($isTrueColor) {
+
+					$c1 = ImageColorAt($src_img, $siX, $siY2);
+					$c2 = ImageColorAt($src_img, $siX, $siY);
+					$c3 = ImageColorAt($src_img, $siX2, $siY2);
+					$c4 = ImageColorAt($src_img, $siX2, $siY);
+
+					$r = (( $c1             +  $c2             +  $c3             +  $c4            ) >> 2) & 0xFF0000;
+					$g = ((($c1 & 0x00FF00) + ($c2 & 0x00FF00) + ($c3 & 0x00FF00) + ($c4 & 0x00FF00)) >> 2) & 0x00FF00;
+					$b = ((($c1 & 0x0000FF) + ($c2 & 0x0000FF) + ($c3 & 0x0000FF) + ($c4 & 0x0000FF)) >> 2);
+
+				} else {
+
+					$c1 = ImageColorsForIndex($src_img, ImageColorAt($src_img, $siX, $siY2));
+					$c2 = ImageColorsForIndex($src_img, ImageColorAt($src_img, $siX, $siY));
+					$c3 = ImageColorsForIndex($src_img, ImageColorAt($src_img, $siX2, $siY2));
+					$c4 = ImageColorsForIndex($src_img, ImageColorAt($src_img, $siX2, $siY));
+
+					$r = ($c1['red']   + $c2['red']   + $c3['red']   + $c4['red'] )  << 14;
+					$g = ($c1['green'] + $c2['green'] + $c3['green'] + $c4['green']) <<  6;
+					$b = ($c1['blue']  + $c2['blue']  + $c3['blue']  + $c4['blue'] ) >>  2;
+
+				}
+				ImageSetPixel($dst_img, $dst_x + $x - $src_x, $dst_y + $y - $src_y, $r+$g+$b);
+			}
+		}
+		return true;
+	}
+
+
+	function ImageCreateFunction($x_size, $y_size) {
+		$ImageCreateFunction = 'ImageCreate';
+		if (phpthumb_functions::gd_version() >= 2.0) {
+			$ImageCreateFunction = 'ImageCreateTrueColor';
+		}
+		if (!function_exists($ImageCreateFunction)) {
+			return phpthumb::ErrorImage($ImageCreateFunction.'() does not exist - no GD support?');
+		}
+		if (($x_size <= 0) || ($y_size <= 0)) {
+			return phpthumb::ErrorImage('Invalid image dimensions: '.$ImageCreateFunction.'('.$x_size.', '.$y_size.')');
+		}
+		return $ImageCreateFunction($x_size, $y_size);
+	}
+
+
+	function ImageCopyRespectAlpha(&$dst_im, &$src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct=100) {
+		for ($x = $src_x; $x < $src_w; $x++) {
+			for ($y = $src_y; $y < $src_h; $y++) {
+				$RealPixel    = phpthumb_functions::GetPixelColor($dst_im, $dst_x + $x, $dst_y + $y);
+				$OverlayPixel = phpthumb_functions::GetPixelColor($src_im, $x, $y);
+				$alphapct = $OverlayPixel['alpha'] / 127;
+				$opacipct = $pct / 100;
+				$overlaypct = (1 - $alphapct) * $opacipct;
+
+				$newcolor = phpthumb_functions::ImageColorAllocateAlphaSafe(
+					$dst_im,
+					round($RealPixel['red']   * (1 - $overlaypct)) + ($OverlayPixel['red']   * $overlaypct),
+					round($RealPixel['green'] * (1 - $overlaypct)) + ($OverlayPixel['green'] * $overlaypct),
+					round($RealPixel['blue']  * (1 - $overlaypct)) + ($OverlayPixel['blue']  * $overlaypct),
+					//$RealPixel['alpha']);
+					0);
+
+				ImageSetPixel($dst_im, $dst_x + $x, $dst_y + $y, $newcolor);
+			}
+		}
+		return true;
+	}
+
+
+	function ProportionalResize($old_width, $old_height, $new_width=false, $new_height=false) {
+		$old_aspect_ratio = $old_width / $old_height;
+		if (($new_width === false) && ($new_height === false)) {
+			return false;
+		} elseif ($new_width === false) {
+			$new_width = $new_height * $old_aspect_ratio;
+		} elseif ($new_height === false) {
+			$new_height = $new_width / $old_aspect_ratio;
+		}
+		$new_aspect_ratio = $new_width / $new_height;
+		if ($new_aspect_ratio == $old_aspect_ratio) {
+			// great, done
+		} elseif ($new_aspect_ratio < $old_aspect_ratio) {
+			// limited by width
+			$new_height = $new_width / $old_aspect_ratio;
+		} elseif ($new_aspect_ratio > $old_aspect_ratio) {
+			// limited by height
+			$new_width = $new_height * $old_aspect_ratio;
+		}
+		return array(round($new_width), round($new_height));
+	}
+
+
+	function FunctionIsDisabled($function) {
+		static $DisabledFunctions = null;
+		if (is_null($DisabledFunctions)) {
+			$disable_functions_local  = explode(',',     @ini_get('disable_functions'));
+			$disable_functions_global = explode(',', @get_cfg_var('disable_functions'));
+			foreach ($disable_functions_local as $key => $value) {
+				$DisabledFunctions[$value] = 'local';
+			}
+			foreach ($disable_functions_global as $key => $value) {
+				$DisabledFunctions[$value] = 'global';
+			}
+			if (@ini_get('safe_mode')) {
+				$DisabledFunctions['shell_exec'] = 'local';
+			}
+		}
+		return isset($DisabledFunctions[$function]);
+	}
+
+
+	function SafeExec($command) {
+		static $AllowedExecFunctions = array();
+		if (empty($AllowedExecFunctions)) {
+			$AllowedExecFunctions = array('shell_exec'=>true, 'passthru'=>true, 'system'=>true, 'exec'=>true);
+			foreach ($AllowedExecFunctions as $key => $value) {
+				$AllowedExecFunctions[$key] = !phpthumb_functions::FunctionIsDisabled($key);
+			}
+		}
+		foreach ($AllowedExecFunctions as $execfunction => $is_allowed) {
+			if (!$is_allowed) {
+				continue;
+			}
+			switch ($execfunction) {
+				case 'passthru':
+					ob_start();
+					$execfunction($command);
+					$returnvalue = ob_get_contents();
+					ob_end_clean();
+					break;
+
+				case 'shell_exec':
+				case 'system':
+				case 'exec':
+				default:
+					ob_start();
+					$returnvalue = $execfunction($command);
+					ob_end_clean();
+					break;
+			}
+			return $returnvalue;
+		}
+		return false;
+	}
+
+
+	function ApacheLookupURIarray($filename) {
+		// apache_lookup_uri() only works when PHP is installed as an Apache module.
+		if (php_sapi_name() == 'apache') {
+			$keys = array('status', 'the_request', 'status_line', 'method', 'content_type', 'handler', 'uri', 'filename', 'path_info', 'args', 'boundary', 'no_cache', 'no_local_copy', 'allowed', 'send_bodyct', 'bytes_sent', 'byterange', 'clength', 'unparsed_uri', 'mtime', 'request_time');
+			if ($apacheLookupURIobject = @apache_lookup_uri($filename)) {
+				$apacheLookupURIarray = array();
+				foreach ($keys as $key) {
+					$apacheLookupURIarray[$key] = @$apacheLookupURIobject->$key;
+				}
+				return $apacheLookupURIarray;
+			}
+		}
+		return false;
+	}
+
+
+	function gd_is_bundled() {
+		static $isbundled = null;
+		if (is_null($isbundled)) {
+			$gd_info = gd_info();
+			$isbundled = (strpos($gd_info['GD Version'], 'bundled') !== false);
+		}
+		return $isbundled;
+	}
+
+
+	function gd_version($fullstring=false) {
+		static $cache_gd_version = array();
+		if (empty($cache_gd_version)) {
+			$gd_info = gd_info();
+			if (eregi('bundled \((.+)\)$', $gd_info['GD Version'], $matches)) {
+				$cache_gd_version[1] = $gd_info['GD Version'];  // e.g. "bundled (2.0.15 compatible)"
+				$cache_gd_version[0] = (float) $matches[1];     // e.g. "2.0" (not "bundled (2.0.15 compatible)")
+			} else {
+				$cache_gd_version[1] = $gd_info['GD Version'];                       // e.g. "1.6.2 or higher"
+				$cache_gd_version[0] = (float) substr($gd_info['GD Version'], 0, 3); // e.g. "1.6" (not "1.6.2 or higher")
+			}
+		}
+		return $cache_gd_version[intval($fullstring)];
+	}
+
+
+	function filesize_remote($remotefile, $timeout=10) {
+		$size = false;
+		$url = parse_url($remotefile);
+		if ($fp = @fsockopen($url['host'], ($url['port'] ? $url['port'] : 80), $errno, $errstr, $timeout)) {
+			fwrite($fp, 'HEAD '.@$url['path'].@$url['query'].' HTTP/1.0'."\r\n".'Host: '.@$url['host']."\r\n\r\n");
+			if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=')) {
+				stream_set_timeout($fp, $timeout);
+			}
+			while (!feof($fp)) {
+				$headerline = fgets($fp, 4096);
+				if (eregi('^Content-Length: (.*)', $headerline, $matches)) {
+					$size = intval($matches[1]);
+					break;
+				}
+			}
+			fclose ($fp);
+		}
+		return $size;
+	}
+
+
+	function filedate_remote($remotefile, $timeout=10) {
+		$date = false;
+		$url = parse_url($remotefile);
+		if ($fp = @fsockopen($url['host'], ($url['port'] ? $url['port'] : 80), $errno, $errstr, $timeout)) {
+			fwrite($fp, 'HEAD '.@$url['path'].@$url['query'].' HTTP/1.0'."\r\n".'Host: '.@$url['host']."\r\n\r\n");
+			if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=')) {
+				stream_set_timeout($fp, $timeout);
+			}
+			while (!feof($fp)) {
+				$headerline = fgets($fp, 4096);
+				if (eregi('^Last-Modified: (.*)', $headerline, $matches)) {
+					$date = strtotime($matches[1]) - date('Z');
+					break;
+				}
+			}
+			fclose ($fp);
+		}
+		return $date;
+	}
+
+
+	function md5_file_safe($filename) {
+		// md5_file() doesn't exist in PHP < 4.2.0
+		if (function_exists('md5_file')) {
+			return md5_file($filename);
+		}
+		if ($fp = @fopen($filename, 'rb')) {
+			$rawData = '';
+			do {
+				$buffer = fread($fp, 8192);
+				$rawData .= $buffer;
+			} while (strlen($buffer) > 0);
+			fclose($fp);
+			return md5($rawData);
+		}
+		return false;
+	}
+
+
+	function nonempty_min() {
+		$arg_list = func_get_args();
+		$acceptable = array();
+		foreach ($arg_list as $arg) {
+			if ($arg) {
+				$acceptable[] = $arg;
+			}
+		}
+		return min($acceptable);
+	}
+
+
+	function LittleEndian2String($number, $minbytes=1) {
+		$intstring = '';
+		while ($number > 0) {
+			$intstring = $intstring.chr($number & 255);
+			$number >>= 8;
+		}
+		return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
+	}
+
+	function OneOfThese() {
+		// return the first useful (non-empty/non-zero/non-false) value from those passed
+		$arg_list = func_get_args();
+		foreach ($arg_list as $key => $value) {
+			if ($value) {
+				return $value;
+			}
+		}
+		return false;
+	}
+
+	function CaseInsensitiveInArray($needle, $haystack) {
+		$needle = strtolower($needle);
+		foreach ($haystack as $key => $value) {
+			if (is_array($value)) {
+				// skip?
+			} elseif ($needle == strtolower($value)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function URLreadFsock($host, $file, &$errstr, $successonly=true, $port=80, $timeout=10) {
+		if (!function_exists('fsockopen') || phpthumb_functions::FunctionIsDisabled('fsockopen')) {
+			$errstr = 'fsockopen() unavailable';
+			return false;
+		}
+		if ($fp = @fsockopen($host, 80, $errno, $errstr, $timeout)) {
+			$out  = 'GET '.$file.' HTTP/1.0'."\r\n";
+			$out .= 'Host: '.$host."\r\n";
+			$out .= 'Connection: Close'."\r\n\r\n";
+			fwrite($fp, $out);
+
+			$isHeader = true;
+			$Data_header = '';
+			$Data_body   = '';
+			$header_newlocation = '';
+			while (!feof($fp)) {
+				$line = fgets($fp, 1024);
+				if ($isHeader) {
+					$Data_header .= $line;
+				} else {
+					$Data_body .= $line;
+				}
+				if (eregi('^HTTP/[\\.0-9]+ ([0-9]+) (.+)$', rtrim($line), $matches)) {
+					list($dummy, $errno, $errstr) = $matches;
+					$errno = intval($errno);
+				} elseif (eregi('^Location: (.*)$', rtrim($line), $matches)) {
+					$header_newlocation = $matches[1];
+				}
+				if ($isHeader && ($line == "\r\n")) {
+					$isHeader = false;
+					if ($successonly) {
+						switch ($errno) {
+							case 200:
+								// great, continue
+								break;
+
+							default:
+								$errstr = $errno.' '.$errstr.($header_newlocation ? '; Location: '.$header_newlocation : '');
+								fclose($fp);
+								return false;
+								break;
+						}
+					}
+				}
+			}
+			fclose($fp);
+			return $Data_body;
+		}
+		return null;
+	}
+
+	function CleanUpURLencoding($url, $queryseperator='&') {
+		if (!eregi('^http', $url)) {
+			return $url;
+		}
+		$parse_url = @parse_url($url);
+		$pathelements = explode('/', $parse_url['path']);
+		$CleanPathElements = array();
+		$TranslationMatrix = array(' '=>'%20');
+		foreach ($pathelements as $key => $pathelement) {
+			$CleanPathElements[] = strtr($pathelement, $TranslationMatrix);
+		}
+		foreach ($CleanPathElements as $key => $value) {
+			if (!$value) {
+				unset($CleanPathElements[$key]);
+			}
+		}
+
+		$queries = explode($queryseperator, @$parse_url['query']);
+		$CleanQueries = array();
+		foreach ($queries as $key => $query) {
+			@list($param, $value) = explode('=', $query);
+			$CleanQueries[] = strtr($param, $TranslationMatrix).($value ? '='.strtr($value, $TranslationMatrix) : '');
+		}
+		foreach ($CleanQueries as $key => $value) {
+			if (!$value) {
+				unset($CleanQueries[$key]);
+			}
+		}
+
+		$cleaned_url  = $parse_url['scheme'].'://';
+		$cleaned_url .= (@$parse_url['username'] ? $parse_url['host'].(@$parse_url['password'] ? ':'.$parse_url['password'] : '').'@' : '');
+		$cleaned_url .= $parse_url['host'];
+		$cleaned_url .= '/'.implode('/', $CleanPathElements);
+		$cleaned_url .= (@$CleanQueries ? '?'.implode($queryseperator, $CleanQueries) : '');
+		return $cleaned_url;
+	}
+
+	function SafeURLread($url, &$error, $timeout=10, $followredirects=true) {
+		$error = '';
+
+		$parsed_url = @parse_url($url);
+		$alreadyLookedAtURLs[trim($url)] = true;
+		do {
+			$rawData = phpthumb_functions::URLreadFsock(@$parsed_url['host'], @$parsed_url['path'].'?'.@$parsed_url['query'], $errstr, true, (@$parsed_url['port'] ? @$parsed_url['port'] : 80), $timeout);
+			if (eregi('302 Found; Location\\: (http.*)', $errstr, $matches)) {
+				$matches[1] = trim(@$matches[1]);
+				if ($alreadyLookedAtURLs[$matches[1]]) {
+					break;
+				}
+				$alreadyLookedAtURLs[$matches[1]] = true;
+				$parsed_url = @parse_url($matches[1]);
+			} else {
+				break;
+			}
+		} while (true);
+		$error .= 'Error opening "'.$url.'":'."\n\n".$errstr;
+		if ($rawData === false) {
+			return false;
+		} elseif ($rawData === null) {
+			// fall through
+		} else {
+			return $rawData;
+		}
+
+		if (function_exists('curl_version') && !phpthumb_functions::FunctionIsDisabled('curl_exec')) {
+			$ch = curl_init();
+			curl_setopt($ch, CURLOPT_URL, $url);
+			curl_setopt($ch, CURLOPT_HEADER, false);
+			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+			curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
+			curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+			$rawData = curl_exec($ch);
+			curl_close($ch);
+			if (strlen($rawData) > 0) {
+				return $rawData;
+			}
+			$error .= 'CURL available but returned no data; ';
+		} else {
+			$error .= 'CURL unavailable; ';
+		}
+
+		$BrokenURLfopenPHPversions = array('4.4.2');
+		if (in_array(phpversion(), $BrokenURLfopenPHPversions)) {
+			$error .= 'fopen(URL) broken in PHP v'.phpversion().'; ';
+		} elseif (@ini_get('allow_url_fopen')) {
+			$rawData = '';
+			ob_start();
+			if ($fp = fopen($url, 'rb')) {
+				do {
+					$buffer = fread($fp, 8192);
+					$rawData .= $buffer;
+				} while (strlen($buffer) > 0);
+				fclose($fp);
+			} else {
+				$error .= trim(strip_tags(ob_get_contents()));
+			}
+			ob_end_clean();
+			if (!$error) {
+				return $rawData;
+			}
+			$error .= '; "allow_url_fopen" enabled but returned no data; ';
+		} else {
+			$error .= '"allow_url_fopen" disabled; ';
+		}
+
+		return false;
+	}
+
+	function EnsureDirectoryExists($dirname) {
+		$directory_elements = explode(DIRECTORY_SEPARATOR, $dirname);
+		$startoffset = (!$directory_elements[0] ? 2 : 1);  // unix with leading "/" then start with 2nd element; Windows with leading "c:\" then start with 1st element
+		$open_basedirs = explode(':', ini_get('open_basedir'));
+		foreach ($open_basedirs as $key => $open_basedir) {
+			if (ereg('^'.preg_quote($open_basedir), $dirname) && (strlen($dirname) > strlen($open_basedir))) {
+				$startoffset = count(explode(DIRECTORY_SEPARATOR, $open_basedir));
+				break;
+			}
+		}
+		$i = $startoffset;
+		$endoffset = count($directory_elements);
+		for ($i = $startoffset; $i <= $endoffset; $i++) {
+			$test_directory = implode(DIRECTORY_SEPARATOR, array_slice($directory_elements, 0, $i));
+			if (!$test_directory) {
+				continue;
+			}
+			if (!@is_dir($test_directory)) {
+				if (@file_exists($test_directory)) {
+					// directory name already exists as a file
+					return false;
+				}
+				@mkdir($test_directory, 0755);
+				@chmod($test_directory, 0755);
+				if (!@is_dir($test_directory) || !@is_writeable($test_directory)) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+
+	function GetAllFilesInSubfolders($dirname) {
+		$AllFiles = array();
+		$dirname = rtrim(realpath($dirname), '/\\');
+		if ($dirhandle = @opendir($dirname)) {
+			while ($file = readdir($dirhandle)) {
+				$fullfilename = $dirname.DIRECTORY_SEPARATOR.$file;
+				if (is_file($fullfilename)) {
+					$AllFiles[] = $fullfilename;
+				} elseif (is_dir($fullfilename)) {
+					if (($file == '.') || ($file == '..')) {
+						continue;
+					}
+					$subfiles = phpthumb_functions::GetAllFilesInSubfolders($fullfilename);
+					foreach ($subfiles as $filename) {
+						$AllFiles[] = $filename;
+					}
+				} else {
+					// ignore?
+				}
+			}
+			closedir($dirhandle);
+		}
+		sort($AllFiles);
+		return array_unique($AllFiles);
+	}
+
+
+	function SanitizeFilename($filename) {
+		$filename = ereg_replace('[^'.preg_quote(' !#$%^()+,-.;<>=@[]_{}').'a-zA-Z0-9]', '_', $filename);
+		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.1.0', '>=')) {
+			$filename = trim($filename, '.');
+		}
+		return $filename;
+	}
+
+}
+
+
+////////////// END: class phpthumb_functions //////////////
+
+
+if (!function_exists('gd_info')) {
+	// built into PHP v4.3.0+ (with bundled GD2 library)
+	function gd_info() {
+		static $gd_info = array();
+		if (empty($gd_info)) {
+			// based on code by johnschaefer at gmx dot de
+			// from PHP help on gd_info()
+			$gd_info = array(
+				'GD Version'         => '',
+				'FreeType Support'   => false,
+				'FreeType Linkage'   => '',
+				'T1Lib Support'      => false,
+				'GIF Read Support'   => false,
+				'GIF Create Support' => false,
+				'JPG Support'        => false,
+				'PNG Support'        => false,
+				'WBMP Support'       => false,
+				'XBM Support'        => false
+			);
+			$phpinfo_array = phpthumb_functions::phpinfo_array();
+			foreach ($phpinfo_array as $line) {
+				$line = trim(strip_tags($line));
+				foreach ($gd_info as $key => $value) {
+					//if (strpos($line, $key) !== false) {
+					if (strpos($line, $key) === 0) {
+						$newvalue = trim(str_replace($key, '', $line));
+						$gd_info[$key] = $newvalue;
+					}
+				}
+			}
+			if (empty($gd_info['GD Version'])) {
+				// probable cause: "phpinfo() disabled for security reasons"
+				if (function_exists('ImageTypes')) {
+					$imagetypes = ImageTypes();
+					if ($imagetypes & IMG_PNG) {
+						$gd_info['PNG Support'] = true;
+					}
+					if ($imagetypes & IMG_GIF) {
+						$gd_info['GIF Create Support'] = true;
+					}
+					if ($imagetypes & IMG_JPG) {
+						$gd_info['JPG Support'] = true;
+					}
+					if ($imagetypes & IMG_WBMP) {
+						$gd_info['WBMP Support'] = true;
+					}
+				}
+				// to determine capability of GIF creation, try to use ImageCreateFromGIF on a 1px GIF
+				if (function_exists('ImageCreateFromGIF')) {
+					if ($tempfilename = phpthumb::phpThumb_tempnam()) {
+						if ($fp_tempfile = @fopen($tempfilename, 'wb')) {
+							fwrite($fp_tempfile, base64_decode('R0lGODlhAQABAIAAAH//AP///ywAAAAAAQABAAACAUQAOw==')); // very simple 1px GIF file base64-encoded as string
+							fclose($fp_tempfile);
+
+							// if we can convert the GIF file to a GD image then GIF create support must be enabled, otherwise it's not
+							$gd_info['GIF Read Support'] = (bool) @ImageCreateFromGIF($tempfilename);
+						}
+						unlink($tempfilename);
+					}
+				}
+				if (function_exists('ImageCreateTrueColor') && @ImageCreateTrueColor(1, 1)) {
+					$gd_info['GD Version'] = '2.0.1 or higher (assumed)';
+				} elseif (function_exists('ImageCreate') && @ImageCreate(1, 1)) {
+					$gd_info['GD Version'] = '1.6.0 or higher (assumed)';
+				}
+			}
+		}
+		return $gd_info;
+	}
+}
+
+
+if (!function_exists('is_executable')) {
+	// in PHP v3+, but v5.0+ for Windows
+	function is_executable($filename) {
+		// poor substitute, but better than nothing
+		return file_exists($filename);
+	}
+}
+
+
+if (!function_exists('preg_quote')) {
+	// included in PHP v3.0.9+, but may be unavailable if not compiled in
+	function preg_quote($string, $delimiter='\\') {
+		static $preg_quote_array = array();
+		if (empty($preg_quote_array)) {
+			$escapeables = '.\\+*?[^]$(){}=!<>|:';
+			for ($i = 0; $i < strlen($escapeables); $i++) {
+				$strtr_preg_quote[$escapeables{$i}] = $delimiter.$escapeables{$i};
+			}
+		}
+		return strtr($string, $strtr_preg_quote);
+	}
+}
+
+if (!function_exists('file_get_contents')) {
+	// included in PHP v4.3.0+
+	function file_get_contents($filename) {
+		if (eregi('^(f|ht)tp\://', $filename)) {
+			return SafeURLread($filename, $error);
+		}
+		if ($fp = @fopen($filename, 'rb')) {
+			$rawData = '';
+			do {
+				$buffer = fread($fp, 8192);
+				$rawData .= $buffer;
+			} while (strlen($buffer) > 0);
+			fclose($fp);
+			return $rawData;
+		}
+		return false;
+	}
+}
+
+
+if (!function_exists('file_put_contents')) {
+	// included in PHP v5.0.0+
+	function file_put_contents($filename, $filedata) {
+		if ($fp = @fopen($filename, 'wb')) {
+			fwrite($fp, $filedata);
+			fclose($fp);
+			return true;
+		}
+		return false;
+	}
+}
+
+if (!function_exists('imagealphablending')) {
+	// built-in function requires PHP v4.0.6+ *and* GD v2.0.1+
+	function imagealphablending(&$img, $blendmode=true) {
+		// do nothing, this function is declared here just to
+		// prevent runtime errors if GD2 is not available
+		return true;
+	}
+}
+
+if (!function_exists('imagesavealpha')) {
+	// built-in function requires PHP v4.3.2+ *and* GD v2.0.1+
+	function imagesavealpha(&$img, $blendmode=true) {
+		// do nothing, this function is declared here just to
+		// prevent runtime errors if GD2 is not available
+		return true;
+	}
+}
+
+?>
Index: /afridex/plugins/Flutter/RCCWP_Application.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_Application.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_Application.php (revision 21)
@@ -0,0 +1,614 @@
+<?php
+class RCCWP_Application
+{
+	
+	
+	function GetWpCategories()
+	{
+		global $wpdb;
+		//$sql = "SELECT cat_ID, cat_name FROM $wpdb->categories ORDER BY cat_name";
+
+		if( $wpdb->terms != '' )
+		{
+			$sql = "SELECT t.term_id AS cat_ID, t.name AS cat_name FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id WHERE tt.taxonomy = 'category' ORDER BY t.name";
+		}
+		else
+		{
+			$sql = "SELECT cat_ID, cat_name FROM $wpdb->categories ORDER BY cat_name";
+		}
+
+		$results = $wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		return $results;
+	}
+
+	
+
+
+	/**
+	* Check whether this is wordpress mu and the logged in user is the top admin and it is the main blog
+	*/
+	function is_mu_top_admin(){
+		global $wpdb;
+
+		if ($wpdb->prefix != $wpdb->base_prefix.'1_') {
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	* Import modules in default_modules folder
+	*/
+	function import_default_modules(){
+
+		$modsFolder = dirname(__FILE__)."/default_modules/without_panel";
+		if ($handle = opendir($modsFolder)) {
+			while (false !== ($file = readdir($handle))) {
+				if (is_file($modsFolder.'/'.$file))
+					RCCWP_CustomWriteModule::Import($modsFolder.'/'.$file); 
+			}
+		}
+
+		$modsFolder = dirname(__FILE__)."/default_modules/with_panel";
+		if ($handle = opendir($modsFolder)) {
+			while (false !== ($file = readdir($handle))) {
+				if (is_file($modsFolder.'/'.$file))
+					RCCWP_CustomWriteModule::Import($modsFolder.'/'.$file, false, true);
+			}
+		}
+
+	}
+
+	function ContinueInstallation(){
+		//if (RCCWP_Application::IsWordpressMu()) {
+			RCCWP_Application::SetCaps();
+		//}
+	}
+
+	function SetCaps(){
+
+		// Create capabilities if they are not installed
+		if (!current_user_can(FLUTTER_CAPABILITY_PANELS)){
+			$role = get_role('administrator');
+			if (!(RCCWP_Application::IsWordpressMu()) || is_site_admin()){
+				$role->add_cap(FLUTTER_CAPABILITY_PANELS);
+				$role->add_cap(FLUTTER_CAPABILITY_MODULES);
+			}
+			$role->add_cap(FLUTTER_CAPABILITY_LAYOUT);
+			$role->add_cap(FLUTTER_CAPABILITY_STYLE);
+		}
+	}
+
+	function Install()
+	{
+		
+		include_once('RCCWP_Options.php');
+		global $wpdb;
+	
+		// First time installation
+		if (get_option(RC_CWP_OPTION_KEY) === false){
+	
+			// Giving full rights to folders. 
+			@chmod(FLUTTER_UPLOAD_FILES_DIR, 777);
+			@chmod(FLUTTER_IMAGES_CACHE_DIR, 777);
+			@chmod(FLUTTER_MODULES_DIR, 777);
+			@chmod(FLUTTER_PURIFIER_CACHE_DIR, 777);
+			
+			//Initialize options
+			$options['hide-write-post'] = 0;
+			$options['hide-write-page'] = 0;
+			$options['prompt-editing-post'] = 0;
+			$options['assign-to-role'] = 0;
+			$options['use-snipshot'] = 1;
+			$options['enable-editnplace'] = 1;
+			$options['enable-swfupload'] = 1 ;
+			$options['default-custom-write-panel'] = "";
+			if (version_compare(PHP_VERSION, '5.0.0') === 1)
+				$options['enable-HTMLPurifier'] = 1;
+			else
+				$options['enable-HTMLPurifier'] = 0;
+			$options['tidy-level'] = "medium";
+			$options['canvas_show_instructions'] = 1;
+			$options['canvas_show_zone_name'] = 0;
+			$options['canvas_show'] = 1;
+			$options['ink_show'] = 0;
+
+			RCCWP_Options::Update($options);
+			
+		}
+		
+		
+
+		// Check blog database
+		if (get_option("RC_CWP_BLOG_DB_VERSION") == '') update_option("RC_CWP_BLOG_DB_VERSION", 0);
+		
+		if (get_option("RC_CWP_BLOG_DB_VERSION") < RC_CWP_DB_VERSION) 
+			$BLOG_DBChanged = true;
+		else
+			$BLOG_DBChanged = false;
+				
+			
+		// Install blog tables
+		if (!$wpdb->get_var("SHOW TABLES LIKE '".RC_CWP_TABLE_POST_META."'") == RC_CWP_TABLE_POST_META ||
+				$BLOG_DBChanged){	
+			$blog_tables[] = "CREATE TABLE " . RC_CWP_TABLE_POST_META . " (
+				id integer NOT NULL,
+				group_count integer NOT NULL,
+				field_count integer NOT NULL,
+				post_id integer NOT NULL,
+				field_name text NOT NULL,
+				PRIMARY KEY (id) )" ;
+
+			// try to get around
+			// these includes like http://trac.mu.wordpress.org/ticket/384 
+			// and http://www.quirm.net/punbb/viewtopic.php?pid=832#p832
+			if (file_exists(ABSPATH . 'wp-includes/pluggable.php')) {
+				require_once(ABSPATH . 'wp-includes/pluggable.php');
+			} else {
+				require_once(ABSPATH . 'wp-includes/pluggable-functions.php');
+			}
+			require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
+			
+			foreach($blog_tables as $blog_table)
+				dbDelta($blog_table);
+		}
+		canvas_install($BLOG_DBChanged);
+		
+		// Upgrade Blog
+		if ($BLOG_DBChanged)	RCCWP_Application::UpgradeBlog();
+
+				
+		if (RCCWP_Application::IsWordpressMu()){	
+			if (get_site_option("RC_CWP_DB_VERSION") == '') update_site_option("RC_CWP_DB_VERSION", 0);
+			if (get_site_option("RC_CWP_DB_VERSION") < RC_CWP_DB_VERSION) 
+				$DBChanged = true;
+			else
+				$DBChanged = false;
+		}
+		else{
+			if (get_option("RC_CWP_DB_VERSION") == '') update_option("RC_CWP_DB_VERSION", 0);
+			if (get_option("RC_CWP_DB_VERSION") < RC_CWP_DB_VERSION) 
+				$DBChanged = true;
+			else
+				$DBChanged = false;
+		}
+		
+		
+		// -- Create Tables if they don't exist or the database changed
+		if(!$wpdb->get_var("SHOW TABLES LIKE '".RC_CWP_TABLE_PANELS."'") == RC_CWP_TABLE_PANELS) 	$not_installed = true;
+
+		if( $not_installed ||
+			$DBChanged){ 
+
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_PANELS . " (
+				id int(11) NOT NULL auto_increment,
+				name varchar(50) NOT NULL,
+				description varchar(255),
+				display_order tinyint,
+				capability_name varchar(50) NOT NULL,
+				type varchar(50) NOT NULL,
+				PRIMARY KEY (id) )";
+			
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " (
+				id tinyint(11) NOT NULL auto_increment,
+				name varchar(50) NOT NULL,
+				description varchar(100),
+				has_options enum('true', 'false') NOT NULL,
+				has_properties enum('true', 'false') NOT NULL,
+				allow_multiple_values enum('true', 'false') NOT NULL,
+				PRIMARY KEY (id) )";
+				
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_GROUP_FIELDS . " (
+				id int(11) NOT NULL auto_increment,
+				group_id int(11) NOT NULL,
+				name varchar(50) NOT NULL,
+				description varchar(255),
+				display_order tinyint,
+				display_name enum('true', 'false') NOT NULL,
+				display_description enum('true', 'false') NOT NULL,
+				type tinyint NOT NULL,
+				CSS varchar(100),
+				required_field tinyint,
+				duplicate boolean NOT NULL,
+				PRIMARY KEY (id) )";
+				
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS . " (
+				custom_field_id int(11) NOT NULL,
+				options text,
+				default_option text,
+				PRIMARY KEY (custom_field_id) )";
+			
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_PANEL_CATEGORY . " (
+				panel_id int(11) NOT NULL,
+				cat_id int(11) NOT NULL,
+				PRIMARY KEY (panel_id, cat_id) )";
+				
+			/*$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_STANDARD_FIELDS . " (
+				id int(11) NOT NULL AUTO_INCREMENT,
+				name VARCHAR(50) NOT NULL,
+				css_id VARCHAR(50) NOT NULL,
+				default_inclusion ENUM('true', 'false') NOT NULL,
+				advanced_field ENUM('true', 'false') NOT NULL,
+				exclude_version float(11) NOT NULL,
+				for_post ENUM('true', 'false') NOT NULL,
+				PRIMARY KEY  (id)
+				);";*/
+			
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_PANEL_STANDARD_FIELD . " (
+				panel_id int(11) NOT NULL,
+				standard_field_id int(11) NOT NULL,
+				PRIMARY KEY (panel_id, standard_field_id) )";
+			
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " (
+				custom_field_id int(11) NOT NULL AUTO_INCREMENT,
+				properties TEXT,
+				PRIMARY KEY (custom_field_id)
+				);";
+			
+			/*$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD . " (
+				panel_id int(11) NOT NULL,
+				css_id varchar(50) NOT NULL,
+				PRIMARY KEY (panel_id, css_id) )";*/
+	
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_MODULES . " (
+				id int(11) NOT NULL auto_increment,
+				name varchar(50) NOT NULL,
+				description text,
+				PRIMARY KEY (id) )";
+	
+			$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_PANEL_GROUPS . " (
+				id int(11) NOT NULL auto_increment,
+				panel_id int(11) NOT NULL,
+				name varchar(50) NOT NULL,
+				duplicate boolean NOT NULL,
+				at_right boolean NOT NULL,
+				PRIMARY KEY (id) )";
+	
+			/*$qst_tables[] = "CREATE TABLE " . RC_CWP_TABLE_PANEL_MODULES . " (
+				id int(11) NOT NULL auto_increment,
+				panel_id int(11) NOT NULL,
+				module_id int(11) NOT NULL,
+				PRIMARY KEY (id) )";*/
+
+			// try to get around
+			// these includes like http://trac.mu.wordpress.org/ticket/384 
+			// and http://www.quirm.net/punbb/viewtopic.php?pid=832#p832
+			if (file_exists(ABSPATH . 'wp-includes/pluggable.php')) {
+				require_once(ABSPATH . 'wp-includes/pluggable.php');
+			} else {
+				require_once(ABSPATH . 'wp-includes/pluggable-functions.php');
+			}
+			require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
+			
+			foreach($qst_tables as $qst_table)
+				dbDelta($qst_table);
+
+			if (RCCWP_Application::IsWordpressMu()) {
+					update_site_option('RC_CWP_DB_VERSION', RC_CWP_DB_VERSION);
+			}
+			else{
+					update_option('RC_CWP_DB_VERSION', RC_CWP_DB_VERSION);
+			}
+		
+		}
+
+		// Insert standard fields definition
+		if($not_installed){
+		
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (1, 'Textbox', NULL, 'false', 'true', 'false')";
+			$wpdb->query($sql6);
+			
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (2, 'Multiline Textbox', NULL, 'false', 'true', 'false')";
+			$wpdb->query($sql6);
+			
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (3, 'Checkbox', NULL, 'false', 'false', 'false')";
+			$wpdb->query($sql6);
+			
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (4, 'Checkbox List', NULL, 'true', 'false', 'true')";
+			$wpdb->query($sql6);
+			
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (5, 'Radiobutton List', NULL, 'true', 'false', 'false')";
+			$wpdb->query($sql6);
+			
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (6, 'Dropdown List', NULL, 'true', 'false', 'false')";
+			$wpdb->query($sql6);
+			
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (7, 'Listbox', NULL, 'true', 'true', 'true')";
+			$wpdb->query($sql6);
+			
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (8, 'File', NULL, 'false', 'false', 'false')";
+			$wpdb->query($sql6);
+			
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (9, 'Image', NULL, 'false', 'true', 'false')";
+			$wpdb->query($sql6);
+	
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (10, 'Date', NULL, 'false', 'true', 'false')";
+			$wpdb->query($sql6);
+	
+			$sql6 = "INSERT IGNORE INTO " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " VALUES (11, 'Audio', NULL, 'false', 'false', 'false')";
+			$wpdb->query($sql6);
+			
+			
+			// -- Panel standard fields
+			
+			//$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (1, 'Title', 'titlediv', 'true', 'false', 1000)";
+			//$wpdb->query($sql9);
+			
+			/*$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (12, 'Post/Page', 'postdivrich', 'true', 'false', 1000)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (2, 'Categories', 'categorydiv', 'false', 'false', 1000)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (3, 'Discussion', 'commentstatusdiv', 'true', 'true', 1000)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (4, 'Post Password', 'passworddiv', 'true', 'true', 1000)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (5, 'Post Slug', 'slugdiv', 'false', 'false', 2.5)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (6, 'Post Status', 'poststatusdiv', 'false', 'false', 2.5)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (7, 'Post Timestamp', 'posttimestampdiv', 'false', 'false', 2.5)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (8, 'Upload', 'uploading', 'false', 'false', 2.5)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (9, 'Optional Excerpt', 'postexcerpt', 'true', 'true', 1000)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (10, 'Trackbacks', 'trackbacksdiv', 'true', 'true', 1000)";
+			$wpdb->query($sql9);
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (11, 'Custom Fields', 'postcustom', 'true', 'true', 1000)";
+			$wpdb->query($sql9);
+			
+			
+			
+			$sql9 = "INSERT IGNORE INTO " . RC_CWP_TABLE_STANDARD_FIELDS . " VALUES (13, 'Post Author', 'authordiv', 'false', 'false', 2.5)";
+			$wpdb->query($sql9);*/
+		}
+		
+		// Upgrade Blog site
+		if ($DBChanged) RCCWP_Application::UpgradeBlogSite();
+		
+		
+		
+		//Import Default modules 
+		if (RCCWP_Application::IsWordpressMu()){
+			if (get_site_option('FLUTTER_fist_time') == ''){
+				RCCWP_Application::import_default_modules();
+				update_site_option('FLUTTER_fist_time', '1');
+			}
+		}
+		else{
+			if (get_option('FLUTTER_fist_time') == ''){
+				RCCWP_Application::import_default_modules();
+				update_option('FLUTTER_fist_time', '1');
+			}
+		}
+		
+	}
+	
+	function UpgradeBlog(){
+		global $wpdb;
+		if (RC_CWP_DB_VERSION == 26){
+			// Migrate database from previous versions after introducing models/panels
+			// separation concept
+			// The following code adds the field name to RC_CWP_TABLE_POST_META.
+			
+			$fieldMetaIDs = $wpdb->get_results("SELECT id FROM " . RC_CWP_TABLE_POST_META);
+			foreach($fieldMetaIDs as $fieldMetaID){
+				$metakey = $wpdb->get_var( "SELECT meta_key FROM $wpdb->postmeta WHERE meta_id = '{$fieldMetaID->id}'" );
+				$wpdb->query("UPDATE ". RC_CWP_TABLE_POST_META .
+							 " SET field_name = '$metakey'".
+							 " WHERE id = '{$fieldMetaID->id}'");
+					
+			}
+		
+		}
+		
+	}
+
+	function UpgradeBlogSite(){
+		global $wpdb;
+		if (RC_CWP_DB_VERSION == 26){
+			
+			// Migrate database from previous versions after introducing models/panels
+			// separation concept
+			// The following code transfers fields/groups fomr modules to panels.
+			
+			require_once("RCCWP_CustomWritePanel.php");
+			if (RCCWP_Application::IsWordpressMu()) {
+				$RC_CWP_TABLE_PANEL_MODULES = $wpdb->base_prefix . 'rc_cwp_panel_modules';
+			}
+			else{
+				$RC_CWP_TABLE_PANEL_MODULES = $wpdb->prefix . 'rc_cwp_panel_modules';
+			}
+			
+			$writePanels = RCCWP_CustomWritePanel::GetCustomWritePanels();
+			
+			foreach($writePanels as $writePanel){
+				$sql = "SELECT module_id FROM " . $RC_CWP_TABLE_PANEL_MODULES .
+					" WHERE panel_id = " . $writePanel->id;
+				$panelModules =$wpdb->get_results($sql);
+				
+				foreach($panelModules as $panelModule){
+					$wpdb->query("UPDATE ". RC_CWP_TABLE_PANEL_GROUPS .
+							 " SET panel_id = '{$writePanel->id}'".
+							 " WHERE module_id = '{$panelModule->module_id}'");
+				}
+			}
+		}
+	}
+	
+	function Uninstall()
+	{
+ 		global $wpdb;
+
+		// Delete blog tables
+		$sql = "DROP TABLE " . RC_CWP_TABLE_POST_META;
+		$wpdb->query($sql);
+		include_once "canvas-install.php";
+		canvas_clean_deactivate();
+
+		// Remove options
+		delete_option(RC_CWP_OPTION_KEY);
+
+		// Delete meta data
+		$sql = "DELETE FROM $wpdb->postmeta WHERE meta_key = '" . RC_CWP_POST_WRITE_PANEL_ID_META_KEY . "'";
+ 		$wpdb->query($sql);
+
+		if (get_option("Flutter_notTopAdmin")) return;
+
+
+		
+		RCCWP_Application::DeleteModulesFolders();	
+
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANELS;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_GROUP_FIELDS;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANEL_CATEGORY;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_STANDARD_FIELDS;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANEL_STANDARD_FIELD;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES;
+		$wpdb->query($sql);
+
+		$sql = "DROP TABLE " . RC_CWP_TABLE_MODULES;
+		$wpdb->query($sql);
+		
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANEL_GROUPS;
+		$wpdb->query($sql);
+
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANEL_HIDDEN_EXTERNAL_FIELD; 
+		$wpdb->query($sql);
+
+		$sql = "DROP TABLE " . RC_CWP_TABLE_PANEL_MODULES; 
+		$wpdb->query($sql);
+		
+		global $canvas;
+		$wpdb->query("DROP TABLE IF EXISTS ".$canvas->duplicates."");
+
+		if (RCCWP_Application::is_mu_top_admin()){
+			update_site_option('FLUTTER_fist_time', '');
+		}
+		else{
+			update_option('FLUTTER_fist_time', '');
+		}
+	
+		
+	}
+
+	function DeleteModulesFolders()
+	{
+		$customModules = RCCWP_CustomWriteModule::GetCustomModules();
+		foreach($customModules as $customModule)
+			RCCWP_CustomWriteModule::Delete($customModule->id);
+	}
+	
+	function InCustomWritePanel()
+	{
+		return RCCWP_Application::InWritePostPanel() && isset($_REQUEST['custom-write-panel-id']);
+	}
+	
+	function InWritePostPanel()
+	{
+		return (strstr($_SERVER['REQUEST_URI'], '/wp-admin/post-new.php') ||
+			strstr($_SERVER['REQUEST_URI'], '/wp-admin/post.php') ||
+			strstr($_SERVER['REQUEST_URI'], '/wp-admin/page-new.php') ||
+			strstr($_SERVER['REQUEST_URI'], '/wp-admin/page.php'));
+	}
+
+	function IsWordpressMu(){
+		global $is_wordpress_mu; //$wpdb, $wp_version, $wpmu_version;
+
+		if  ($is_wordpress_mu){      // (isset($wpmu_version)) || (strpos($wp_version, 'wordpress-mu')) ) {
+			return true;
+		}
+
+		return false;
+	}
+
+	function CheckInstallation(){
+	
+		if (stripos($_GET['page'], "flutter") === false && $_GET['page'] != "RCCWP_OptionsPage.php" && !isset($_GET['custom-write-panel-id'])) return;
+		
+		$dir_list = "";
+
+		if (!is_writable(FLUTTER_IMAGES_CACHE_DIR)){
+			$dir_list.= "<li>".FLUTTER_IMAGES_CACHE_DIR . "</li>";
+		}
+
+		if (!is_writable(FLUTTER_UPLOAD_FILES_DIR)){
+			$dir_list.= "<li>".FLUTTER_UPLOAD_FILES_DIR . "</li>";
+		}
+
+
+		if (!is_writable(FLUTTER_MODULES_DIR)){
+			$dir_list.= "<li>".FLUTTER_MODULES_DIR . "</li>";
+		}
+
+		if (!is_writable(FLUTTER_PURIFIER_CACHE_DIR)){
+			$dir_list.= "<li>".FLUTTER_PURIFIER_CACHE_DIR . "</li>";
+		}
+
+		if ($dir_list != ""){
+			echo "<div id='flutter-install-error-message' class='error'><p><strong>".__('Flutter is not ready yet.')."</strong> ".__('The following folders must be writable (usually chmod 777 is neccesary):')."</p><ul>";
+			echo $dir_list;
+			echo "</ul></div>";
+		}
+
+	}
+	
+	
+	/**
+	 * Checks for the existance of unzip
+	 * 
+	 * @access private 
+	 */
+
+	function CheckDecompressionProgramUnzip() {
+
+		$return = exec("unzip -help", $output, $returnValue);
+		if ( 0 != $returnValue ) return false;
+
+		return true;
+	}
+	
+	/*
+	 * Checks for the existance of zip
+	 */
+
+	function CheckCompressionProgramZip() {
+
+		$return = exec("zip -help", $output, $returnValue);
+		if ( 0 != $returnValue ) return false;
+
+		return true;
+	}
+	
+	
+}
+?>
Index: /afridex/plugins/Flutter/canvas-core.php
===================================================================
--- /afridex/plugins/Flutter/canvas-core.php (revision 21)
+++ /afridex/plugins/Flutter/canvas-core.php (revision 21)
@@ -0,0 +1,560 @@
+<?php
+
+/*
+
+___Canvas Core Functions________________________________________
+
+Core functions for generating the Canvas GUI
+and manipulating layout contents.
+
+________________________________________________________________
+
+*/
+
+
+
+require_once('canvas-globals.php');
+require_once('canvas-install.php');
+require_once('canvas-xml.php');
+require_once('canvas-import_functions.php');
+require_once('canvas-admin.php');
+require_once('Ink/ink.php');
+
+define(MODULE_TEMPLATE_SIZE_SMALL, -1);
+define(MODULE_TEMPLATE_SIZE_MEDIUM, -2);
+define(MODULE_TEMPLATE_SIZE_LARGE, -3);
+define(MODULE_TEMPLATE_SIZE_FULL, -4);
+
+
+
+function canvas_build_canvas($curr_page) {
+	global $current_zones, $wpdb, $canvas;
+	if(isset($_GET["template"])) $template = ABSPATH.'wp-content/themes/'.$_GET["template"];
+		else $template = TEMPLATEPATH;
+ 	if(file_exists($template . "/canvas.php")) {
+			
+			if(get_option('canvas_auto_publish') != 'true') echo '<div id="publish"><a href="javascript:void(0)"><img src="'.CANVASURI.'images/publish.gif" title="Publish Your Changes" alt="Publish Changes" id="publish_image" /></a></div>';
+				else echo '<div id="publish" class="publish_nobutton"></div>';
+			echo
+<<<EOF
+			<a id="publish" href="?page=FlutterEditCanvas&defaultmods=1">Load all Default Modules</a>
+			<a id="publish" href="?page=FlutterEditCanvas&resetmods=1" onclick="return confirmBeforeReset();">Reset Modules</a>
+			<script type="text/javascript" language="javascript">
+				function confirmBeforeReset()
+				{
+					return confirm("You're about put all your modules back in the shelf. Are you sure you want to do this?");
+				}
+			</script>
+EOF;
+		
+			include($template . "/canvas.php");
+
+			//Put modules whose canvas no more exists back to the shelf
+			$currDBModules = $wpdb->get_results("SELECT block_id, zone FROM ".$canvas->main." WHERE zone <> 'shelf' AND theme = '".get_option('template')."' AND page='$curr_page'"); 
+			$updated = false;
+			foreach ($currDBModules as $currDBModule){
+				$found = false;
+				foreach($current_zones as $current_zone){
+					if ($currDBModule->zone == $current_zone) $found = true;
+				}
+				if (!$found){
+					$updated = true;
+					$wpdb->query("UPDATE ".$canvas->main." SET zone = 'shelf' WHERE block_id = '".$currDBModule->block_id."'");
+				}
+			}
+			if ($updated) {
+				wp_redirect("?page=FlutterEditCanvas");
+				return;
+			}
+			
+			echo '<br />'; 
+		} else { canvas_no_theme(); }
+}
+
+function canvas_check_theme() {
+	global $wpdb, $canvas, $table_prefix;
+	$table = $table_prefix . 'ink';
+	$theme = get_option('template');
+
+	$filename = get_template_directory().'/canvas.php';
+
+	// Install Canvas
+	if(!$wpdb->get_results("SELECT block_id FROM ".$canvas->main." WHERE theme = '".get_option('template')."'")){ 
+		//canvas_install();
+		if(file_exists($filename)) {
+			if(!get_option('canvas_first_install')) update_option('canvas_first_install', time());
+			if($wpdb->get_results("SELECT block_id FROM ".$canvas->main." WHERE theme = '".get_option('template')."'") && !$wpdb->get_results("SELECT block_id FROM ".$canvas->main." WHERE zone != 'shelf' AND theme = '".get_option('template')."'")) {
+				$xmlfile = get_template_directory().'/canvas.xml';
+				if(file_exists($xmlfile)) canvas_import_layout($xmlfile);
+			}
+		}
+	}
+
+	// Install Ink
+	if (!$wpdb->get_results("SELECT * FROM ".$table." WHERE theme = '$theme'")) {
+		if(file_exists($filename)) {
+			$file_data = implode('', file($filename));
+			if(preg_match("|Ink Elements:(.*)|i", $file_data, $ink_string)) $ink_string = trim($ink_string[1]);
+			if(function_exists('ink_elements')) ink_elements($ink_string);
+		}
+	}
+
+}
+
+function canvas_create_draggable($block_id, $zone, $position, $module_name, $module_title, $content, $type, $group, $template_size, $template_name) {
+
+	// Get plugin sizes
+	$moduleTemplatesFolder = dirname(__FILE__)."/modules/".$module_name."/templates";
+	$moduleTemplateFolder = dirname(__FILE__)."/modules/".$module_name."/templates/default";
+
+	if ($zone=="shelf") $template_name="";
+
+
+	$module_title_min = $module_title;
+	if (strlen($module_title) > 10 ){	
+		$module_title_min = substr($module_title,0,10);//."...";
+	}
+
+	$class = 'container';
+	if($type == 'plugin') $class = 'container-plugin';
+	$class .= ' '.strtolower($group);
+	echo '<div class="'.$class.'" id="canvas_'.$zone.'_'.$position.'_'.$block_id.'" title="'.strip_tags($module_title." - ".$content).'">'."\n";
+	echo '<h6 class="handle"><span>'.$module_title_min.'</span></h6>'."\n";
+	$module_size_id = 'canvas_'.$zone.'_'.$position.'_'.$block_id.'_module_size';
+	$module_name_id = 'canvas_'.$zone.'_'.$position.'_'.$block_id.'_module_name';
+	$module_template_name_id = 'canvas_'.$zone.'_'.$position.'_'.$block_id.'_module_template_name';
+//echo($template_size);
+	
+
+	echo 
+<<<EOF
+	<!--<div id="module_size_select" class="module_size_select">
+	
+	<select name="$module_size_id" id="$module_size_id" style="height:18px;font-size:11px;" onchange="Canvas.changePublishImg();">
+		$otherSizesStr
+	</select>
+	<select name="$module_template_name_id" id="$module_template_name_id" style="height:18px;font-size:11px;"  onchange="Canvas.changePublishImg();">
+		$templatesNamesStr
+	</select>
+	</div>--->
+	<input type="hidden" name="$module_size_id" id="$module_size_id" value="$template_size" />
+	<input type="hidden" name="$module_template_name_id" id="$module_template_name_id" value="$template_name" />	
+	<input id="$module_name_id" type="hidden" value="$module_name"/>
+EOF;
+	echo canvas_create_plugin_form($zone, $position, $block_id, $type)."\n";
+	echo '<div class="content">'.$content."</div>\n";
+
+ 	echo '</div>'."\n";
+}
+
+function canvas_create_management_block($block,$style='',$delete_button='true') {
+	if ($block->is_original) $delete_button='false';
+
+	if($block->uri != '') $link = '<a href="'.$block->uri.'">'.$block->author.'</a>';
+		else $link = $block->author;
+	if($block->type == 'plugin') $class = ' class="plugin '.strtolower($block->block_group).'"';
+		else $class = ' class="'.strtolower($block->block_group).'"';
+	echo "\t".'<li id="block_'.$block->block_id.'"'.$class.$style.'>';
+	if($delete_button == 'true') echo '<a class="delete_block" id="'.$block->block_id.'" href="javascript:void(0)"><img src="'.CANVASURI.'images/delete.png" alt="Delete" title="Delete block"/></a>';
+	echo '<a class="duplicate_block" id="'.$block->block_id.'" href="javascript:void(0)"><img src="'.CANVASURI.'images/duplicate.png" alt="Duplicate" title="Duplicate block"/></a>';
+	echo '<a class="block_info" id="'.$block->block_id.'" href="javascript:void(0)"><img src="'.CANVASURI.'images/block_info.png" alt="Info" title="Block information"/></a>';
+	if ($block->is_original)
+		echo '<h3><span id="title_'.$block->block_id.'">'.$block->module_title.'</span> by '.$link.'.</h3><p>'.wptexturize($block->description).'</p>';
+	else
+		echo '<h3><span class="title" id="title_'.$block->block_id.'">'.$block->module_title.'</span> by '.$link.'.</h3><p>'.wptexturize($block->description).'</p>';
+	echo '<div class="info" id="info_'.$block->block_id.'" style="display: none;"><div>'.
+			'<strong>Module Folder:</strong> modules/'.$block->module_name.
+			'<br/><strong>Module ID:</strong> '.$block->block_id.
+			'<br/><strong>Path:</strong> '.$block->path.
+			'<br/><strong>Zone:</strong> '.$block->zone.
+			'<br/><strong>Position:</strong> '.$block->position.
+			'<br/><strong>Author URI:</strong> '.$block->uri.'</div></div>';
+	echo '</li>'."\n";
+}
+
+function canvas_create_plugin_form($zone,$position,$block_id,$block_type) {
+	if($block_type == 'plugin'){
+		echo '<a href="'.CANVASURI.'canvas-plugin_form.php?zone='.$zone.'&position='.$position.'&block_id='.$block_id.'" class="lbOn"><img class="lbLink" src="'.CANVASURI.'images/plugin_options.gif" title="Module Settings" alt="Module Settings"/></a>';
+		echo '<a href="'.CANVASURI.'canvas-plugin_info.php?block_id='.$block_id.'" class="lbOn"><img class="lbLinkInfo" src="'.CANVASURI.'images/block_info.png" title="Module Information" alt="Module Information"/></a>';
+	}
+}
+
+
+
+function canvas_database_plugin($matches, $type = 'select', $variable_value, $where_cond = '') {
+	global $wpdb, $canvas;
+	$ALLOW_TABLES = array("sitecategories");
+
+	$content = '';
+	if(!isset($matches[3])) $matches[3] = $matches[2];
+	if ($where_cond != '') {
+		$where_cond = str_replace('%prefix%', $wpdb->prefix, $where_cond);
+		$where_cond = " WHERE " . $where_cond;
+	}
+
+
+	// We need to find out whether the table name requires user prefix or global prefix
+	$req_table_prefix = $wpdb->prefix;
+	if (!$wpdb->get_var("SHOW TABLES LIKE '".$req_table_prefix.$matches[1]."'") == $req_table_prefix.$matches[1]){
+		if (in_array($matches[1], $ALLOW_TABLES))
+			$req_table_prefix = (isset($wpdb->base_prefix)?$wpdb->base_prefix:$wpdb->prefix);
+	}
+	
+
+	if($wpdb->get_var("SHOW TABLES LIKE '".$req_table_prefix.$matches[1]."'") == $req_table_prefix.$matches[1]) {
+		//echo("<br/>SELECT ".$matches[2].", ".$matches[3]." FROM ".$req_table_prefix.$matches[1]."  TBL ".$where_cond);
+		if($data = $wpdb->get_results("SELECT ".$matches[2].", ".$matches[3]." FROM ".$req_table_prefix.$matches[1]."  TBL ".$where_cond)) {
+			foreach($data as $row) {
+				if($type == 'select') {
+					$content .= '<option value="'.$row->$matches[3].'" ';
+					if ($row->$matches[3] == $variable_value) $content .= ' selected="selected" ';
+					$content .= '>&nbsp;'.$row->$matches[2]."</option>\n";
+				} elseif($type == 'radio') {
+					$content .= '<label><input type="radio" value="'.$row->$matches[3].'" ';
+					if ($row->$matches[3] == $variable_value) $content .= ' checked="checked" ';
+					$content .= '/>&nbsp;'.$row->$matches[2].'</label>';
+				}
+			}
+		}
+	}
+	
+	return $content;
+}
+
+
+/**
+*	
+*	@param $zone_name (String) The zone name
+*	@param $template_name (String) The name of the template to grab. The default value is "default".
+*	@param $template_size (Integer) Grabs the specified version of the template. The default value is MODULE_TEMPLATE_SIZE_SMALL. The value of $template_size could be 
+*		- MODULE_TEMPLATE_SIZE_SMALL, MODULE_TEMPLATE_SIZE_MEDIUM, MODULE_TEMPLATE_SIZE_LARGE, or MODULE_TEMPLATE_SIZE_FULL. 
+*		- The maximum width of the module that can be placed in this canvas.
+*	@param $allow_other_sizes (Boolean) If you tried to put a version of a module larger the zone version, and this value is set to True, it will just give a warning. Otherwise, it will give error. The default value is true.
+*	@param $default_module (String) This will bring up a certain module by default.
+*	@param $allow_other_modules (Boolean) If set to True, then the admin can put another module in the space. Otherwise, you shouldnât be able to move the module. The default value is true.
+*	@param $styles (String) A string that will be printed in the style attribute of the canvas div.  
+**/
+
+function canvas_define_zone($zone_name, $template_name = "default", $template_size = MODULE_TEMPLATE_SIZE_SMALL, $allow_other_sizes = TRUE, $default_module = '', $allow_other_modules = TRUE, $styles='') {
+	$parent='';
+	global $zones, $parents, $current_zones;
+	$current_zones[] = $zone_name;
+	if($_GET["template"] != '') $theme = $_GET["template"];
+		else $theme = get_option('template');
+	$parents[] = $parent;
+	$zones[] = $zone_name;
+	echo '<div class="canvas_droppable_zone '.$zone_name.'" id="'.$zone_name.'"';
+	if($styles != '') echo ' style="'.$styles.'"';
+	echo '>'."\n";
+	get_canvas_draggables($zone_name, $theme);
+	echo '<input type="hidden" id="'.$zone_name.'_template_name" value="'.$template_name.'" />';
+	echo '<input type="hidden" id="'.$zone_name.'_template_size" value="'.$template_size.'" />';
+	echo '<input type="hidden" id="'.$zone_name.'_allow_other_sizes" value="'.$allow_other_sizes.'" />';
+	echo '<input type="hidden" id="'.$zone_name.'_allow_other_modules" value="'.$allow_other_modules.'" />';
+	echo '<input type="hidden" id="'.$zone_name.'_default_module" value="'.$default_module.'" />';
+	echo '</div>'."\n";
+}
+
+function canvas_execute_function($function_name, $block_id) {
+	global $wpdb, $canvas;
+	if($var_data = $wpdb->get_results("SELECT variable_name, value, type FROM ".$canvas->variables." WHERE parent = '$block_id'")) {
+		$url_string = '';
+		$vars = array();
+		if (!empty($var_data)) {
+		  foreach($var_data as $var) {
+			$url_string .= '&'.$var->variable_name.'='.$var->value;
+			//if ($function_name)
+			//	$vars[str_replace($function_name.'_', '', $var->variable_name)] = $var->value;
+			//else
+			switch($var->type){
+				case 'fileupload':
+					$vars[$var->variable_name] = FLUTTER_URI.'files_flutter/'.$var->value;
+					break;
+				default:
+					$vars[$var->variable_name] = $var->value;
+			}
+		  }
+		
+
+		  $url_string = str_replace($function_name.'_', '', $url_string);
+		  $url_string .= '&block_id='.$block_id;
+		}
+		$url_string = substr($url_string, 1);
+		if(is_callable($function_name)) $function_name($url_string, $vars);
+	}
+	return $vars;
+}
+
+function canvas_getNodeByAttr($attr, $nodeID, $xmldoc) {
+	if ($xmldoc->hasChildNodes()) {
+		$children = $xmldoc->childNodes;
+		foreach($children as $node) {
+			if($node->hasAttributes()) {
+				$attributes = $node->attributes;
+				foreach ($attributes as $attribute) {
+					if(($attribute->name == $attr) && (stripos($attribute->value, $nodeID)) !== false) {
+						return $node;
+					} else {
+						if(canvas_getNodeByAttr($attr,$nodeID,$node))
+							return canvas_getNodeByAttr($attr,$nodeID,$node);
+					}
+				}
+			} else {
+				if(canvas_getNodeByAttr($attr,$nodeID,$node))
+					return canvas_getNodeByAttr($attr,$nodeID,$node);
+			}
+		}
+	} else {
+		return null;
+	}
+}
+
+function canvas_head() {
+	global $installed, $canvas, $wpdb;
+	$load_default_modules = 0;
+
+	if($_GET["resetmods"]){
+		$wpdb->query("UPDATE ".$canvas->main." SET zone = 'shelf', template_name='', template_size=''");
+		$wpdb->query("UPDATE ".$canvas->variables." SET default_value = '__RESETDEFAULT__'");
+		echo '<META HTTP-EQUIV=Refresh CONTENT="0; URL=?page=FlutterEditCanvas&resetmodsmsg=1">';
+		//wp_redirect("?page=FlutterEditCanvas&resetmodsmsg=1");
+		die();
+	}
+
+	if ($wpdb->get_results("SELECT block_id FROM ".$canvas->main." WHERE theme = '".get_option('template')."' AND zone <>'shelf'")) {
+		$load_default_modules = 0;
+	}
+	else{
+		$load_default_modules = 1;
+	}
+	if(isset($_GET["defaultmods"])) $load_default_modules = 1; 
+
+	if($_GET["page"] == 'FlutterEditCanvas' || $_GET["page"] == 'FlutterInk') : 
+
+
+					?>
+<link rel="stylesheet" href="<?php echo CANVASURI; ?>stylesheet.css" type="text/css" media="screen" />
+<link rel="stylesheet" href="<?php echo CANVASURI; ?>lightbox.css" type="text/css" media="screen" />
+		<?php if(!isset($_GET["template"])) : ?>
+<link rel="stylesheet" href="<?php bloginfo('stylesheet_directory') ?>/canvas.css" type="text/css" media="screen" />
+		<?php else : ?>
+<link rel="stylesheet" href="<?php echo get_bloginfo('wpurl').'/wp-content/themes/'.$_GET["template"]; ?>/canvas.css" type="text/css" media="screen" />
+		<?php endif; ?>
+<script language="JavaScript" type="text/javascript" > 
+	JS_CANVASURI="<?php echo CANVASURI; ?>";
+
+	// The following piece of code checks whether 'www' is included in teh original URL or not,
+	// The AJAX js must be called from exactl same URL, otherwise the browser will prevent it 
+	// for security reason (calling a URL from another place).
+	var originalHost = location.host;
+	if (originalHost.substring(0,4) == "www."){
+		if (JS_CANVASURI.indexOf("://www.") == -1)
+			alert("The registered URL in wordpress does not have a 'www', please remove 'www' from the URL.");
+	}
+	else {
+		if (JS_CANVASURI.indexOf("://www.") > -1)
+			alert("The registered URL in wordpress has a 'www', please add 'www' to the URL.");
+	}
+
+
+	LOAD_DEFAULT_MODULES=<?php echo $load_default_modules; ?>;
+</script>
+<script language="JavaScript" type="text/javascript" src="<?php echo CANVASURI; ?>js/prototype.js"></script>
+<script language="JavaScript" type="text/javascript" src="<?php echo CANVASURI; ?>js/scriptaculous/scriptaculous.js"></script>
+<script language="JavaScript" type="text/javascript" src="<?php echo CANVASURI; ?>js/lightbox.js"></script>
+<script language="JavaScript" type="text/javascript" src="<?php echo CANVASURI; ?>js/canvas-ajax.js.php"></script>
+	<?php endif;
+	if($_GET["content"] == 'manage') : ?>
+<script language="JavaScript" type="text/javascript" src="<?php echo CANVASURI; ?>js/canvas-management.js"></script>
+	<?php endif;
+	if($_GET["content"] == 'options') : ?>
+<script language="JavaScript" type="text/javascript" src="<?php echo CANVASURI; ?>js/canvas-options.js"></script>
+	<?php endif;
+}
+
+function canvas_include_block($filename) {
+	include($filename);
+}
+
+function canvas_interrupt() {
+	if(file_exists(get_template_directory().'/canvas.php'))
+		ob_start();
+}
+
+function canvas_no_theme() {
+	$theme = current_theme_info();
+	echo '<div class="wrap"><h2>Theme Not Supported</h2><p>The theme you are currently using ('.$theme->title.' by '.$theme->author.') doesn\'t seem to support Canvas. At this point in the game, Canvas only supports themes that are built to run with the Canvas layout and styling system. To use Canvas, you\'ll have to either <a href="themes.php">select</a> a different theme from your collection.</p>';
+	$themes = get_themes();
+	$theme_names = array_keys($themes);
+	natcasesort($theme_names);
+	$canvas_themes = array();
+	foreach($theme_names as $index => $theme_name) {
+		$stylesheet_dir = $themes[$theme_name]['Stylesheet Dir'];
+		if(file_exists(ABSPATH . $stylesheet_dir . '/canvas.php')) {
+			$canvas_themes[$index]['title'] = $themes[$theme_name]['Title'];
+			$canvas_themes[$index]['template'] = $themes[$theme_name]['Template'];
+			$canvas_themes[$index]['directory'] = $stylesheet_dir;
+		}
+	}
+	if(!empty($canvas_themes)) {
+		echo '<p>Although your current theme isn\'t compatible, you do have some Canvas compatible themes available. Select a theme from the listing below to edit an inactive theme.</p>'."\n";
+		foreach($canvas_themes as $theme) {
+			$link = 'themes.php?page=Main.php&amp;template='.$theme['template'];
+			echo '<h3><p><a href="'.$link.'">&raquo; '.$theme['title'].'</a></p></h3>'."\n";
+		}
+	}
+	echo '</div>'."\n";
+}
+
+function canvas_show_zone_options($zone, $theme) {
+	$content_position = canvas_zone_handler($zone);
+	
+	if(true) {return; $display = ' style="display: none;"'; $toggle_style = ' style="background-color: #cfcfcf;"'; }
+		else { $display = ''; $toggle_style = ''; }
+	$content = '<h8 id="togglezoneoption_'.$zone.'"'.$toggle_style.'></h8>'."\n";
+	$content .= '<span class="canvas_zone_options" id="options_'.$zone.'"'.$display.'>'."\n";
+	$content .= '<select class="zone_content_options" id="zoneoption_'.$zone.'" name="zoneoption_'.$zone.'">'."\n";
+	$content .= '<option value="before">Insert blocks before original content.'."\n";
+	$content .= '<option value="append">Insert blocks after original content.'."\n";
+	$content .= '<option value="replace">Replace original content.'."\n";
+	$content .= '</select>'."\n";
+	$content .= '<img src="'.CANVASURI.'images/replace_up.png" title="Replace original content" onclick="Canvas.zoneHandler(\''.$zone.'\', \'replace\')" />';
+	$content .= '<img src="'.CANVASURI.'images/append_up.png" title="Insert after original content" onclick="Canvas.zoneHandler(\''.$zone.'\', \'append\')" />';
+	$content .= '<img src="'.CANVASURI.'images/before_up.png" title="Insert before original content" onclick="Canvas.zoneHandler(\''.$zone.'\', \'before\')" />';
+	$content .= '</span>'."\n";
+	if($content_position != '') $content = str_replace($content_position.'_up', $content_position.'_down', $content);
+	$content = str_replace('value="'.$content_position.'"', 'value="'.$content_position.'" selected="selected"', $content);
+
+	echo $content;
+}
+
+function mod_var($var_name){
+	global $mod_vars; 
+	//return $mod_vars[$function_name][$var_name];
+	return $mod_vars[$var_name];
+}
+
+/**
+*	Used inside any theme file in order to display a canvas whenever this page is displayed.
+*	
+**/
+function canvas_zone($zone_name) {
+	global $wpdb, $canvas;
+	if($zone_data = $wpdb->get_results("SELECT module_id, block_id, path, type, position, zone, template_name, template_size FROM ".$canvas->main." WHERE zone = '$zone_name' AND theme = '".get_option('template')."' ORDER BY position")) {
+		foreach($zone_data as $zone) {
+
+			// Get module name
+			include_once('RCCWP_CustomWriteModule.php');
+			$customWriteModule = RCCWP_CustomWriteModule::Get($zone->module_id);
+			$zone->module_name = $customWriteModule->name;
+
+			$dir = get_module_template_folder($zone->module_name, $zone->template_name, $zone->template_size);
+			global $mod_vars;
+			$mod_vars = canvas_execute_function($zone->path, $zone->block_id);
+			if (!file_exists($dir.'default.php')) return;
+			include($dir.'default.php');
+
+		}
+	}
+}
+
+
+/**
+*	Used inside any theme file in order to grab the specified module directly without the need to define it in the canvas.php.
+*	
+*	@param $module_name (String) The zone name
+*	@param $template_name (String) The name of the template to grab. The default value is "default".
+*	@param $template_size (Integer) Grabs the specified version of the template. The default value is "small".
+**/
+
+function get_module($module_name, $template_name = "default", $template_size = MODULE_TEMPLATE_SIZE_SMALL) {
+	global $wpdb, $canvas;
+	
+	// Load variables
+	global $mod_vars;
+	include_once("RCCWP_CustomWriteModule.php");
+	$mod = RCCWP_CustomWriteModule::GetByName($module_name);
+	$mod_vars = canvas_execute_function('', $mod->id);
+
+	// Load template file
+	$dir = get_module_template_folder($module_name, $template_name, $template_size)  ;
+	if (!file_exists($dir.'default.php')) return;
+	include($dir.'default.php');
+}
+
+
+function get_module_template_folder($module_name, $template_name, $template_size) {
+	switch($template_size){
+		case MODULE_TEMPLATE_SIZE_SMALL:
+			$template_size = "small";
+			break;
+		case MODULE_TEMPLATE_SIZE_MEDIUM:
+			$template_size = "medium";
+			break;
+		case MODULE_TEMPLATE_SIZE_LARGE:
+			$template_size = "large";
+			break;
+		case MODULE_TEMPLATE_SIZE_FULL:
+			$template_size = "full";
+			break;
+	}
+
+	if (empty($template_size))
+		$template_size = "small";
+
+	if (empty($template_name))
+		$template_name = "default";
+
+	return CANVASPATH.'/modules/'.$module_name.'/templates/'.$template_name.'/'.$template_size.'/'  ;
+}
+
+function canvas_zone_handler($zone) {
+	global $wpdb, $canvas;
+	$position = $wpdb->get_var("SELECT value FROM ".$canvas->zone_options." WHERE option_name = 'zone_handler' AND zone = '$zone' AND theme = '".get_option('template')."'");
+	if($position != '') return $position;
+		else return 'before';
+}
+
+function get_canvas_draggables($zone, $theme = '') {
+	include_once('RCCWP_Options.php');
+
+	if (isset($_GET['t_page'])) 
+		$curr_page = $_GET['t_page'];
+	else
+		$curr_page = 'home'; // Attempt to find the main page
+	
+	if(RCCWP_Options::Get('canvas_show_zone_name') && $zone != 'shelf') echo '<strong>'.$zone.'</strong>';
+	if($zone != 'shelf') canvas_show_zone_options($zone, $theme);
+	if($theme == '') $theme = get_option('template');
+	global $wpdb, $canvas; 
+	if($wpdb->get_var("SHOW TABLES LIKE '".$canvas->main."'") == $canvas->main)
+		$results = $wpdb->get_results("SELECT * FROM ".$canvas->main." WHERE theme = '$theme' AND page='$curr_page' AND zone LIKE '$zone' ORDER BY position");
+
+    	if ($results) {
+        	foreach($results as $result) {
+			// Get module name
+			include_once('RCCWP_CustomWriteModule.php');
+			$customWriteModule = RCCWP_CustomWriteModule::Get($result->module_id);
+			$result->module_name = $customWriteModule->name;
+			$result->description = $customWriteModule->description;
+   			if ($result->duplicate_id ==0 )
+				$result->module_title = $customWriteModule->name;
+			else
+				$result->module_title = $wpdb->get_var("SELECT duplicate_name FROM ".$canvas->duplicates." WHERE duplicate_id = ".$result->duplicate_id);
+			canvas_create_draggable($result->block_id, $zone, $result->position, $result->module_name, $result->module_title, '<p>'.$result->description.'</p>', $result->type, $result->block_group, $result->template_size, $result->template_name);
+        	}
+      	}
+}
+
+
+if(!function_exists('htmlspecialchars_decode')) {
+	function htmlspecialchars_decode($str, $quote_style = ENT_COMPAT) {
+		return strtr($str, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
+	}
+}
+
+
+
+
+
+?>
Index: /afridex/plugins/Flutter/RCCWP_SWFUpload.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_SWFUpload.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_SWFUpload.php (revision 21)
@@ -0,0 +1,66 @@
+<?php
+class RCCWP_SWFUpload
+{
+	function Body($inputName, $fileType, $isCanvas = 0, $urlInputSize = false)
+	{
+		include_once('RCCWP_Options.php');
+
+		if (!$urlInputSize) $urlInputSize = 20;
+
+		if ($isCanvas==0) {
+			$iframeInputSize = $urlInputSize;
+			$iframeWidth = 380;
+			$iframeHeight = 40;
+		}
+		else{
+			$isCanvas = 1;
+			$iframeWidth = 150;
+			$iframeHeight = 60;
+			$iframeInputSize = 3;
+			$inputSizeParam = "&inputSize=$iframeInputSize";
+		}
+		//$iframeWidth = $iframeInputSize * 28;
+
+		$iframePath = CANVASURI."RCCWP_upload.php?input_name=".urlencode($inputName)."&type=$fileType&imageThumbID=img_thumb_$inputName&canvas=$isCanvas".$inputSizeParam ;
+		$enableSWFU = RCCWP_Options::Get('enable-swfupload') ;
+		if( $enableSWFU == 0  || $enableSWFU == ''){
+		?>
+			<div id='upload_iframe_<?php echo $inputName?>'>
+			<iframe id='upload_internal_iframe_<?php echo $inputName?>' src='<?php echo $iframePath;?>' frameborder='' scrolling='no' style="border-width: 0px; height: <?php echo $iframeHeight ?>px; width: <?php echo $iframeWidth ?>px;vertical-align:top;"></iframe>
+			</div>
+		<?php
+		}
+		?>
+		
+			<table border="0" style="width:100%">
+
+				<?php
+				if( $enableSWFU )
+				{
+				?>
+					<tr  style="background:transparent" id="swfuploadRow_<?php echo $inputName ?>">
+						<td style="border-bottom-width: 0px;padding: 0px"><label for="swfupload" >Upload:</label></td>
+						<td style="border-bottom-width: 0px"><input type="button" value="Browse" onclick="openFile('<?php echo $inputName ?>')" class="button" /></td>
+					</tr>
+				<?php
+				}
+				?>
+
+				<tr style="background:transparent">
+					<td style="border-bottom-width: 0px;padding: 0"><label for="upload_url" >Or URL:</label></td>
+					<td style="border-bottom-width: 0px">
+						<input id="upload_url_<?php echo $inputName ?>"
+							name="upload_url_<?php echo $inputName ?>"
+							type="text"
+							size="<?php echo $urlInputSize ?>"
+							/>
+						<input type="button" onclick="uploadurl('<?php echo $inputName ?>','<?php echo $fileType ?>')" value="Upload" class="button" style="width:70px"/>
+					</td>
+				</tr>
+
+			</table>
+		
+		<?php
+	}
+}
+?>
Index: /afridex/plugins/Flutter/css/cropper.css
===================================================================
--- /afridex/plugins/Flutter/css/cropper.css (revision 21)
+++ /afridex/plugins/Flutter/css/cropper.css (revision 21)
@@ -0,0 +1,182 @@
+.imgCrop_wrap {
+	/* width: 500px;   @done_in_js */
+	/* height: 375px;  @done_in_js */
+	position: relative;
+	cursor: crosshair;
+}
+
+/* an extra classname is applied for Opera < 9.0 to fix it's lack of opacity support */
+.imgCrop_wrap.opera8 .imgCrop_overlay,
+.imgCrop_wrap.opera8 .imgCrop_clickArea { 
+	background-color: transparent;
+}
+
+/* fix for IE displaying all boxes at line-height by default, although they are still 1 pixel high until we combine them with the pointless span */
+.imgCrop_wrap,
+.imgCrop_wrap * {
+	font-size: 0;
+}
+
+.imgCrop_overlay {
+	background-color: #000;
+	opacity: 0.5;
+	filter:alpha(opacity=50);
+	position: absolute;
+	width: 100%;
+	height: 100%;
+}
+
+.imgCrop_selArea {
+	position: absolute;
+	/* @done_in_js 
+	top: 20px;
+	left: 20px;
+	width: 200px;
+	height: 200px;
+	background: transparent url(castle.jpg) no-repeat  -210px -110px;
+	*/
+	cursor: move;
+	z-index: 2;
+}
+
+/* clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */
+.imgCrop_clickArea {
+	width: 100%;
+	height: 100%;
+	background-color: #FFF;
+	opacity: 0.01;
+	filter:alpha(opacity=01);
+}
+
+.imgCrop_marqueeHoriz {
+	position: absolute;
+	width: 100%;
+	height: 1px;
+	background: transparent url(../images/marqueeHoriz.gif) repeat-x 0 0;
+	z-index: 3;
+}
+
+.imgCrop_marqueeVert {
+	position: absolute;
+	height: 100%;
+	width: 1px;
+	background: transparent url(../images/marqueeVert.gif) repeat-y 0 0;
+	z-index: 3;
+}
+
+/* 
+ *  FIX MARCHING ANTS IN IE
+ *	As IE <6 tries to load background images we can uncomment the follwoing hack 
+ *  to remove that issue, not as pretty - but is anything in IE?
+ *  And yes I do know that 'filter' is evil, but it will make it look semi decent in IE
+ *
+* html .imgCrop_marqueeHoriz,
+* html .imgCrop_marqueeVert {
+	background: transparent;
+	filter: Invert; 
+}
+* html .imgCrop_marqueeNorth { border-top: 1px dashed #000; }
+* html .imgCrop_marqueeEast  { border-right: 1px dashed #000; }
+* html .imgCrop_marqueeSouth { border-bottom: 1px dashed #000; }
+* html .imgCrop_marqueeWest  { border-left: 1px dashed #000; }
+*/
+
+.imgCrop_marqueeNorth { top: 0; left: 0; }
+.imgCrop_marqueeEast  { top: 0; right: 0; }
+.imgCrop_marqueeSouth { bottom: 0px; left: 0; }
+.imgCrop_marqueeWest  { top: 0; left: 0; }
+
+
+.imgCrop_handle {
+	position: absolute;
+	border: 1px solid #333;
+	width: 6px;
+	height: 6px;
+	background: #FFF;
+	opacity: 0.5;
+	filter:alpha(opacity=50);
+	z-index: 4;
+}
+
+/* fix IE 5 box model */
+* html .imgCrop_handle {
+	width: 8px;
+	height: 8px;
+	wid\th: 6px;
+	hei\ght: 6px;
+}
+
+.imgCrop_handleN {
+	top: -3px;
+	left: 0;
+	/* margin-left: 49%;    @done_in_js */
+	cursor: n-resize;
+}
+
+.imgCrop_handleNE { 
+	top: -3px;
+	right: -3px;
+	cursor: ne-resize;
+}
+
+.imgCrop_handleE {
+	top: 0;
+	right: -3px;
+	/* margin-top: 49%;    @done_in_js */
+	cursor: e-resize;
+}
+
+.imgCrop_handleSE {
+	right: -3px;
+	bottom: -3px;
+	cursor: se-resize;
+}
+
+.imgCrop_handleS {
+	right: 0;
+	bottom: -3px;
+	/* margin-right: 49%; @done_in_js */
+	cursor: s-resize;
+}
+
+.imgCrop_handleSW {
+	left: -3px;
+	bottom: -3px;
+	cursor: sw-resize;
+}
+
+.imgCrop_handleW {
+	top: 0;
+	left: -3px;
+	/* margin-top: 49%;  @done_in_js */
+	cursor: w-resize;
+}
+
+.imgCrop_handleNW {
+	top: -3px;
+	left: -3px;
+	cursor: nw-resize;
+}
+
+/**
+ * Create an area to click & drag around on as the default browser behaviour is to let you drag the image 
+ */
+.imgCrop_dragArea {
+	width: 100%;
+	height: 100%;
+	z-index: 200;
+	position: absolute;
+	top: 0;
+	left: 0;
+}
+
+.imgCrop_previewWrap {
+	/* width: 200px;  @done_in_js */
+	/* height: 200px; @done_in_js */
+	overflow: hidden;
+	position: relative;
+}
+
+.imgCrop_previewWrap img {
+	position: absolute;
+}
Index: /afridex/plugins/Flutter/css/greybox.css
===================================================================
--- /afridex/plugins/Flutter/css/greybox.css (revision 21)
+++ /afridex/plugins/Flutter/css/greybox.css (revision 21)
@@ -0,0 +1,54 @@
+#GB_overlay {
+  background-image: url(../images/overlay.png); 
+  position: absolute;
+  margin: auto;
+  top: 0;
+  left: 0;
+  z-index: 100;
+  width:  100%;
+  height: 100%;
+}
+
+* html #GB_overlay {
+  background-color: #000;
+  background-color: transparent;
+  background-image: url(../images/blank.gif);
+  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../images/overlay.png", sizingMethod="scale");
+}
+
+#GB_window {
+  top: 10px;
+  left: 0px;
+  position: absolute;
+  background: #fff;
+  border: 5px solid #aaa;
+  overflow: auto;
+  width: 400px;
+  height: 400px;
+  z-index: 150;
+}
+
+#GB_frame {
+  border: 0;
+  overflow: auto;
+  width: 100%;
+  height: 378px;
+}
+
+#GB_caption {
+  font: 12px bold helvetica, verdana, sans-serif;
+  color: #fff;
+  background: #888;
+  padding: 2px 0 2px 5px;
+  margin: 0;
+  text-align: left;
+}
+
+#GB_window img {
+  position: absolute;
+  top: 2px;
+  right: 5px;
+  cursor: pointer;
+  cursor: hand;
+}
+
Index: /afridex/plugins/Flutter/css/docs/layout.css
===================================================================
--- /afridex/plugins/Flutter/css/docs/layout.css (revision 21)
+++ /afridex/plugins/Flutter/css/docs/layout.css (revision 21)
@@ -0,0 +1,83 @@
+#header {
+	z-index:	100;
+	position: 	absolute;
+	top:		0px;
+	left:		0px;
+	width:		100%;
+	height:		30px;
+}
+#nav {
+	z-index:	200;
+	position: 	absolute;
+	top:		30px;
+	left:		0px;
+	width:		220px;
+	height:		1600px;
+	clip:		auto;
+	overflow:	auto;
+
+}
+#body {
+	position: 	absolute;
+	top:		40px;
+	left:		245px;
+	
+}
+#content {
+	clear:		both;
+	top:		-1px;
+}
+#packagePosition {
+	position:	absolute;
+	right:		5px;
+	top:		0px;
+	width:		35%;
+	height:		100%;
+}
+#packageTitle {
+	position:	absolute;
+	right:		0px;
+	margin-top:	5px;
+}
+#packageTitle2 {
+	position:	absolute;
+	right:		-3px;
+	top:		-2px;
+}
+#elementPath {
+	position:	absolute;
+	right:		0px;
+	bottom:		0px;
+}
+#navLinks {
+	position:	absolute;
+	top:		0px;
+	left:		10px;
+	height:		100%;
+	margin-top:	5px;
+}
+.leftCol {
+	width:		auto;
+	float:		left;
+}
+.middleCol {
+	width:		auto;
+	float:		left;
+}
+.rightCol {
+	width:		auto;
+	float:		left;
+}
+#credit {
+	margin-top:	20px;
+	margin-bottom:	50px;
+}
+
+/** Fixed layout for nav on mozilla */
+head:first-child+body div#header {
+	position:	fixed;
+}
+head:first-child+body div#nav {
+	position:	fixed;
+	height:		94%
+}
Index: /afridex/plugins/Flutter/css/docs/style.css
===================================================================
--- /afridex/plugins/Flutter/css/docs/style.css (revision 21)
+++ /afridex/plugins/Flutter/css/docs/style.css (revision 21)
@@ -0,0 +1,244 @@
+BODY {
+    background: #FFFFFF;
+	font-family:		Arial;
+	margin:			0px;
+	padding:		0px;
+}
+A {
+	color:                  #0000CC;
+	text-decoration:	none;
+	font-weight:		normal;
+	font-size:		100%;
+}
+A:Hover {
+	color:			white;
+	background-color:	#334B66;
+	font-weight:		bold;
+	text-decoration:	none;
+}
+	
+#packageTitle {
+	font-size:		110%;
+	font-weight:		bold;
+	text-align:		right;
+	color:			#444444;
+}
+#packageTitle2 {
+	font-size:		160%;
+	font-weight:		bold;
+	text-align:		right;
+	color:			#334B66;
+	background-color:       #6699CC;
+	display:		none;
+}
+#packageLinks {
+	background-color:       #6699CC;
+}
+#header {
+	background-color:       #E0ECFF;
+	border-bottom:		solid #C3D9FF 4px;
+}
+#nav {
+	background-color:       #E0ECFF;
+	padding:		4px;
+	border-right:		solid #C3D9FF 4px;
+}
+#index {
+	padding:		18px;
+}
+hr {
+	width:			80%;
+	background-color:	#6699CC;
+	color:			#6699CC;
+	margin-top:		15px;
+	margin-bottom:		15px;
+	clear:			both;
+}
+.links {
+	text-align:		left;
+	width:			98%;
+	margin:			auto;
+}
+UL {
+	margin:			0px;
+	padding:		0px;
+	padding-left:		5px;
+	list-style-type:	none;
+}
+li {
+	text-indent:		-15px;
+	padding-bottom:		2px;
+	padding-left:		14px;
+}
+dd {
+	margin-bottom:		.5em;
+}
+.small {
+	color:			#444444;
+	font-weight:		bold;
+	font-size:		80%;
+}
+h3 {
+}
+.middleCol {
+	margin-left:		-1px;
+	border-right:		dotted gray 1px;
+	border-left:		dotted gray 1px;
+	padding:		5px;
+}
+.leftCol {
+	border-right:		dotted gray 1px;
+	padding:		5px;
+}
+.rightCol {
+	margin-left:		-1px;
+	border-left:		dotted gray 1px;
+	padding:		5px;
+}
+#elementPath {
+	font-size:		14px;
+	font-weight:		bold;
+	color:			#334B66;
+}
+.constructor {
+	/*border:			dashed #334B66 1px;*/
+	font-weight:		bold;
+}
+#credit {
+	text-align:		center;
+	color:			#334B66;
+	font-weight:		bold;
+}
+div.contents {
+	border:			solid #334B66 1px;
+	padding:		3px;
+	margin-bottom:		5px;
+	clear:			all;
+}
+H1 {
+	margin:			0px;
+}
+H2 {
+	margin:			0px;
+	margin-bottom:		2px;
+}
+H3 {
+	margin:			0px;
+}
+H4 {
+	margin:			0px;
+}
+#classTree {
+	padding:		0px;
+	margin:			0px;
+}
+div.indent {
+	margin-left:		15px;
+}
+.warning {
+	color:			red;
+	background-color:	#334B66;
+	font-weight:		bold;
+}
+code {
+	font-family:		fixed;
+	padding:		3px;
+	color:			#334B66;
+	background-color:	#dddddd;
+}
+.type {
+	color:                  #334B66;
+}
+.value {
+	color:                  #334B66;
+	border:			dotted #334B66 1px;
+}
+.top {
+	color:                  #334B66;
+	border-bottom:		dotted #334B66 1px;
+	padding-bottom:		4px;
+}
+.php-src, .php, .listing {
+	font-family:		fixed;
+	padding:		3px;
+	color:			#334B66;
+	background-color:	#dddddd;
+	font-family: 'Courier New', Courier, monospace; font-weight: normal;
+}
+DIV#nav DL {
+	margin:			0px;
+	padding:		0px;
+	list-style-type:	none;
+}
+div.classtree {
+	font-size:		130%;
+	font-weight:		bold;
+	background-color:			#CC6633;
+	border:		dotted #334B66 2px;
+}
+span.linenumber,p.linenumber {
+	font-weight:		bold,italic;
+}
+span.smalllinenumber {
+	font-weight:		bold,italic;
+	font-size:		9pt;
+}
+ul {
+	margin-left:		0px;
+	padding-left:		8px;
+}
+/* Syntax highlighting */
+
+.src-code { background-color: #f5f5f5; border: 1px solid #ccc9a4; padding: 0px; margin : 0px}
+.src-line {  font-family: 'Courier New', Courier, monospace; font-weight: normal; }
+/*.src-code pre {	}*/
+
+.src-comm { color: green; }
+.src-id {  }
+.src-inc { color: #0000FF; }
+.src-key { color: #0000FF; }
+.src-num { color: #CC0000; }
+.src-str { color: #66cccc; }
+.src-sym { font-weight: bold; }
+.src-var { }
+
+.src-php { font-weight: bold; }
+
+.src-doc { color: #009999 }
+.src-doc-close-template { color: #0000FF }
+.src-doc-coretag { color: #0099FF; font-weight: bold }
+.src-doc-inlinetag { color: #0099FF }
+.src-doc-internal { color: #6699cc }
+.src-doc-tag { color: #0080CC }
+.src-doc-template { color: #0000FF }
+.src-doc-type { font-style: italic }
+.src-doc-var { font-style: italic }
+
+.tute-tag { color: #009999 }
+.tute-attribute-name { color: #0000FF }
+.tute-attribute-value { color: #0099FF }
+.tute-entity { font-weight: bold; }
+.tute-comment { font-style: italic }
+.tute-inline-tag { color: #636311; font-weight: bold }
+
+/* tutorial */
+
+.authors {  }
+.author { font-style: italic; font-weight: bold }
+.author-blurb { margin: .5em 0em .5em 2em; font-size: 85%; font-weight: normal; font-style: normal }
+.example { border: 1px dashed #999999; background-color: #EEEEEE; padding: .5em; }
+.listing { border: 1px dashed #999999; background-color: #EEEEEE; padding: .5em; white-space: nowrap; }
+.release-info { font-size: 85%; font-style: italic; margin: 1em 0em }
+.ref-title-box {  }
+.ref-title {  }
+.ref-purpose { font-style: italic; color: #666666 }
+.ref-synopsis {  }
+.title { font-weight: bold; margin: 1em 0em 0em 0em; padding: .25em;
+	border: 2px solid #CC6633; background-color: #6699CC }
+.cmd-synopsis { margin: 1em 0em }
+.cmd-title { font-weight: bold }
+.toc { margin-left: 2em; padding-left: 0em }
+
+#packages{
+	margin-top: 15px;
+}
Index: /afridex/plugins/Flutter/css/epoch_styles.css
===================================================================
--- /afridex/plugins/Flutter/css/epoch_styles.css (revision 21)
+++ /afridex/plugins/Flutter/css/epoch_styles.css (revision 21)
@@ -0,0 +1,120 @@
+/*!!
+Epoch DHTML JavaScript Calendar - Version 2.0.2
+English Edition
+CSS Style File
+(c) 2006-2007 MeanFreePath
+Free for NON-COMMERCIAL use - see website for details and updates
+http://www.meanfreepath.com/javascript_calendar/index.html
+!!*/
+
+table.calendar {
+	font-family: Helvetica, Arial, sans-serif;
+	font-size: 0.8em;
+	border-collapse: collapse;
+	background-color: white;
+	border: solid #999999 1px;
+	background-color: white;
+	width: 215px;
+	text-align: center;
+	/*prevent user from selecting text in Mozilla & Safari - check calendar constructor for IE code)*/
+	-moz-user-select: none;
+    /*-khtml-user-select: none;*/
+}
+table.calendar a {
+}
+table.calendar a:hover {
+}
+table.calendar input, table.calendar select {
+	font-size: 10px;
+}
+table.calendar td, table.calendar th {
+	border: 0;
+	font-size: 10px;
+	text-align: center;
+}
+div.mainheading {
+	margin: 2px;
+}
+.closeBtn {
+	/*float: right;
+	width: 15px;
+	/*font-size: 1.5em;
+	height: 13px;
+
+	padding: 0 0 3px 0;
+	margin: 1px 8px 0 0;
+	border: solid black 1px;*/
+}
+/*all styles related to the main calendar grid*/
+table.cells {
+	border-collapse: collapse;
+	border: solid #CCCCCC 1px;
+	cursor: pointer;
+	empty-cells: show;
+	margin: 0 6px 0 6px;
+}
+/*the day headings*/
+table.cells th {
+	border: solid #CCCCCC 1px;
+	text-align: left;
+	font-weight: bold;
+	color: #0054E3;
+	width: 22px;
+}
+table.cells th.wkhead {
+	border-right: double #CCCCCC 3px;
+	cursor: default;
+	width: 22px;
+}
+/*The date cells*/
+table.cells td {
+	border: solid #CCCCCC 1px;
+	vertical-align: top;
+	text-align: left;
+	font-weight: bold;
+	height: 20px; /*IE doesn't like ems*/
+}
+table.cells td.wkhead {
+	background-color: white;
+	text-align: center;
+	border-right: double #CCCCCC 3px;
+	color: #0054E3;
+}
+table.cells td.noselect {
+	background-color: #EEEEEE;
+	color: #BBBBBB;
+	text-decoration: line-through;
+	cursor: default;
+}
+table.cells td.hlday {
+	background-color: #99FF99;
+}
+table.cells td.wkday {
+	background-color: #DDDDDD;
+}
+table.cells td.wkend {
+	background-color: #DDDDDD;
+}
+table.cells td.curdate {
+
+}
+table.cells td.cell_selected {
+	background-color: #99CCFF;
+	color: black;
+}
+table.cells td.notmnth {
+	background-color: #FFFFFF;
+	color: #CCCCCC;
+}
+table.cells td.notallowed {
+	background-color: white;
+	color: #EEEEEE;
+	font-style: italic;
+}
+table.cells td.hover {
+	background-color: #999999;
+}
+table.cells td div {
+	padding: 1px;
+	margin: 0;
+}
Index: /afridex/plugins/Flutter/css/iframe.css
===================================================================
--- /afridex/plugins/Flutter/css/iframe.css (revision 21)
+++ /afridex/plugins/Flutter/css/iframe.css (revision 21)
@@ -0,0 +1,132 @@
+* {
+	margin: 0;
+	padding: 0;
+}
+
+body {
+	background: #FFF;
+	padding: 18px;
+}
+
+a {
+	color: #09719D;
+	cursor: pointer;
+}
+a:hover {
+	color: #0FB8FF;
+}
+a.kbd {
+	background-color: #E5E5E5;
+	border-color: #ECECEC #ACACAC #ACACAC #ECECEC;
+	border-style: solid;
+	border-width: 2px;
+	color: #858585;
+	font: normal 12px/18px Arial, sans-serif;
+	margin-right: 6px;
+	padding: 1px 3px;
+}
+a:hover.kbd {
+	background-color: #0FB8FF;
+	border-color: #0FB8FF;
+	color: #FFF;
+}
+
+code {
+	background-color: #E5E5E5;
+	color: #666;
+	display: block;
+	font: normal 10px/14px "Lucida Console", Monaco, monospace;
+	float: left;
+	margin: 9px 0 18px 0;
+	width: 592px;
+	padding: 9px 18px;
+}
+
+em {
+	color: #998000;
+	font-style: normal;
+}
+
+form div {
+	clear: both;
+	margin-bottom: 18px;
+	overflow: hidden;
+}
+form fieldset {
+	border: #E5E5E5 2px solid;
+	clear: both;
+	margin-bottom: 9px;
+	overflow: hidden;
+	padding: 9px 18px;
+}
+form input,
+form select {
+	border-color: #ACACAC #E1E1E1 #E1E1E1 #ACACAC;
+	border-style: solid;
+	border-width: 2px;
+	float: left;
+	font: normal 12px Arial, sans-serif;
+	margin-right: 6px;
+	width: 125px;
+}
+
+form.long input, 
+form.long select {
+	width: 175px;
+}
+
+form.short input, 
+form.short select {
+	width: 75px;
+}
+
+form.long input.alternate,
+form.long select.alternate,
+form.long input.calendar,
+form.long select.calendar {
+	width: 149px;
+}
+
+form.short input.alternate,
+form.short select.alternate,
+form.short input.calendar,
+form.short select.calendar {
+	width: 49px;
+}
+
+form input {
+	padding: 1px 3px;
+}
+
+form label {
+	color: #262626;
+	font: normal 12px/18px Arial, sans-serif;
+	float: left;
+	margin-right: 6px;
+	text-align: right;
+	width: 50px;
+}
+form legend {
+	background: #E5E5E5;
+	color: #262626;
+	font: normal 12px/18px Arial, sans-serif;
+	margin-bottom: 9px;
+	padding: 2px 11px;
+}
+
+h1 {
+	color: #262626;
+	font: normal 14px/18px Arial, sans-serif;
+	margin-bottom: 18px;
+}
+
+p {
+	color: #666;
+	font: normal 12px/18px Arial, sans-serif;
+	margin-bottom: 9px;
+}
+
+strong {
+	color: #262626;
+	font-weight: normal;
+}
Index: /afridex/plugins/Flutter/RCCWP_Query.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_Query.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_Query.php (revision 21)
@@ -0,0 +1,83 @@
+<?php
+
+define('RC_CWP_QUERY_PERFIX', 'x_');
+define('RC_CWP_QUERY_ORDERBY', 'customorderby');
+
+class RCCWP_Query
+{
+
+	function FilterPrepare(&$qs)
+	{
+		global $curr_qs_vars;
+		$curr_qs_vars = $qs->query_vars;
+		return $qs;
+	}
+
+
+	
+	function FilterCustomPostsWhere($where)
+	{
+		global $wpdb;
+		global $curr_qs_vars; 
+		
+		foreach ($curr_qs_vars as $queryVarKey => $queryVarValue){
+			if (substr($queryVarKey, 0, strlen(RC_CWP_QUERY_PERFIX)) == RC_CWP_QUERY_PERFIX){	
+				$customKey = substr($queryVarKey, strlen(RC_CWP_QUERY_PERFIX));
+				$customVal = $queryVarValue;
+				$where = $where . " AND 0 < (SELECT count($wpdb->postmeta.meta_value)
+						FROM $wpdb->postmeta
+						WHERE $wpdb->postmeta.post_id = $wpdb->posts.ID and $wpdb->postmeta.meta_key = '$customKey' and $wpdb->postmeta.meta_value = '$customVal') ";
+			}
+		}
+
+		//Add orderby 
+		if (get_query_var(RC_CWP_QUERY_ORDERBY)){
+			$newOrderby = get_query_var(RC_CWP_QUERY_ORDERBY);
+			$newOrderbyFieldName = 	substr($newOrderby, strlen(RC_CWP_QUERY_PERFIX));
+			$where = $where . " AND pmeta.meta_key = '$newOrderbyFieldName' ";
+		}
+		
+		return $where;
+
+	
+	}
+
+	function FilterCustomPostsOrderby($orderby)
+	{
+		global $wpdb;
+		
+		if (get_query_var(RC_CWP_QUERY_ORDERBY)){
+			$newOrderby = get_query_var(RC_CWP_QUERY_ORDERBY);
+			$newOrderbyFieldName = 	substr($newOrderby, strlen(RC_CWP_QUERY_PERFIX));
+			$orderby = "pmeta.meta_value ".get_query_var('order');
+		}
+		
+		return $orderby;
+
+	}
+
+	function FilterCustomPostsFields($fields) {
+		global $wpdb;
+		if (get_query_var(RC_CWP_QUERY_ORDERBY)){
+			$newOrderby = get_query_var(RC_CWP_QUERY_ORDERBY);
+			$newOrderbyFieldName = 	substr($newOrderby, strlen(RC_CWP_QUERY_PERFIX));
+			$fields = $fields. " , pmeta.meta_value ";
+		}
+		
+		return $fields;
+	}
+
+	function FilterCustomPostsJoin($join) {
+		global $wpdb;
+
+		if (get_query_var(RC_CWP_QUERY_ORDERBY)){
+			$join = $join . " INNER JOIN $wpdb->postmeta pmeta ON $wpdb->posts.ID = pmeta.post_id "; 
+			
+		}
+		
+		return $join;
+	}
+
+
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_EditnPlace.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_EditnPlace.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_EditnPlace.php (revision 21)
@@ -0,0 +1,156 @@
+<?php
+
+// Flutter paths
+require_once "RCCWP_Constant.php";
+/*
+preg_match('/wp-content(.*)(RCCWP_EditnPlace\.php)$/',__FILE__,$flutterpath);
+$flutterpath = str_replace('\\', '/', $flutterpath);
+define(FLUTTER_PATH, str_replace('/RCCWP_EditnPlace.php', '', str_replace('\\', '/', __FILE__)));
+define(FLUTTER_URI, get_bloginfo('wpurl').'/wp-content'.$flutterpath[1]); //returns "http://127.0.0.1/wp-content/plugins/Flutter/"
+define(FLUTTER_URI_RELATIVE, 'wp-content'.$flutterpath[1]); //returns "wp-content/plugins/Flutter/"
+*/
+
+class RCCWP_EditnPlace
+{
+
+	function EditnHeader (){
+		global $post, $wp_version;
+
+		// Is EIP enabled?
+		include_once('RCCWP_Options.php');
+		$enableEditnplace = RCCWP_Options::Get('enable-editnplace');
+		if (0 == $enableEditnplace) return;
+
+		
+		$post_id = $post->ID;
+
+		$FLUTTER_URI = FLUTTER_URI;
+		$nicedit_path = FLUTTER_URI."js/nicEdit.js";
+		$prototype_path = FLUTTER_URI."js/prototype.js";
+		$editnplace_path = FLUTTER_URI."js/editnplace.js";
+		$arrow_image_path = FLUTTER_URI."images/arrow.gif";
+		
+	
+		if ( is_user_logged_in() && 
+		     current_user_can('edit_posts', $post_id)
+			){
+
+		echo <<<EOD
+
+			<script language="JavaScript" type="text/javascript" > 
+				var JS_FLUTTER_URI = '$FLUTTER_URI';
+			</script>
+			<script type="text/javascript" src="$nicedit_path"></script>
+			<script type="text/javascript" src="$prototype_path"></script>
+			<script type="text/javascript" src="$editnplace_path"></script>
+			
+			<style type="text/css">
+				
+				/*<![CDATA[*/
+				
+				#savingDiv{
+					font-size: medium; 
+					font-weight: bold;
+				}
+				
+				.EIP_title:hover, .EIP_content:hover, 
+				.EIP_textbox:hover, .EIP_mulittextbox:hover {
+					background-color: #FFFFCC;
+				}
+				
+				.EIPSaveCancel{
+					padding: 5px;
+					margin-top: -1px;
+					z-index: 1000;
+					border-color:#CCC;
+					border-width:1px;
+					border-style:solid;
+					background-color:white;
+					position:fixed;
+					top:0px !important;
+					width:100% !important;
+					left: 0px  !important;
+					/*position:absolute;
+					padding-top:2px;
+					padding-bottom:2px;
+					z-index: 1000;*/
+				}
+				
+				.EIPSaveStatus{
+					position:absolute;
+					font-size: 14px;
+					z-index: 1000;
+				}
+				
+				.EIPnicPanelDiv{
+					position: absolute;
+					background-image: url($arrow_image_path);
+					width:154px;
+					height:38px;
+					z-index: 1000;
+				}
+				
+				div.nicEdit-panel{
+					background-color: white !important;
+					width:140px  !important;
+				}
+				
+				div.nicEdit-panelContain{
+					background-color: white !important;
+					border-bottom: 0px	!important;
+					border-left: 0px	!important;
+					border-right: 0px	!important;
+					width: 92%	!important;
+					margin-left: 2px	!important;
+					margin-top: 1px	!important;
+				}
+				
+				.nicEdit-selected{
+					/*background-color: #FFFFCC  !important;*/
+					border: thin inset   !important;
+					padding: 10px;
+				}
+				.nicEdit-button {
+					background-color: white !important;
+					border: 0px !important;
+				}
+
+				/*]]>*/
+			
+			</style>
+
+EOD;
+		}
+	}
+
+
+}
+
+
+function EIP_title(){
+	global $post;
+	$post_id = $post->ID;
+	echo " EIP_title "." EIP_postid$post_id ";
+}
+
+function EIP_content(){
+	global $post;
+	$post_id = $post->ID;
+	echo " EIP_content "." EIP_postid$post_id ";
+}
+
+function EIP_textbox($meta_id){
+	global $post;
+	$post_id = $post->ID;
+	return " EIP_textbox "." EIP_postid$post_id "." EIP_mid_".$meta_id;
+}
+
+function EIP_mulittextbox($meta_id){
+	global $post;
+	$post_id = $post->ID;
+	return " EIP_mulittextbox "." EIP_postid$post_id "." EIP_mid_".$meta_id;
+}
+
+
+
+?>
Index: /afridex/plugins/Flutter/phpthumb.unsharp.php
===================================================================
--- /afridex/plugins/Flutter/phpthumb.unsharp.php (revision 21)
+++ /afridex/plugins/Flutter/phpthumb.unsharp.php (revision 21)
@@ -0,0 +1,162 @@
+<?php
+//////////////////////////////////////////////////////////////
+////                                                      ////
+////              p h p U n s h a r p M a s k             ////
+////                                                      ////
+////    Unsharp mask algorithm by Torstein Hønsi 2003.    ////
+////               thoensi_at_netcom_dot_no               ////
+////               Please leave this notice.              ////
+////                                                      ////
+//////////////////////////////////////////////////////////////
+/// From: http://vikjavev.no/hovudsida/umtestside.php       //
+//                                                          //
+//  Reformatted by James Heinrich <info@silisoftware.com>   //
+//  for use in phpThumb() on 3 February 2003.               //
+//                                                          //
+//  phpThumb() is found at http://phpthumb.sourceforge.net ///
+//////////////////////////////////////////////////////////////
+
+/*
+WARNING! Due to a known bug in PHP 4.3.2 this script is not working well in this version.
+The sharpened images get too dark. The bug is fixed in version 4.3.3.
+
+Unsharp masking is a traditional darkroom technique that has proven very suitable for
+digital imaging. The principle of unsharp masking is to create a blurred copy of the image
+and compare it to the underlying original. The difference in colour values
+between the two images is greatest for the pixels near sharp edges. When this
+difference is subtracted from the original image, the edges will be
+accentuated.
+
+The Amount parameter simply says how much of the effect you want. 100 is 'normal'.
+Radius is the radius of the blurring circle of the mask. 'Threshold' is the least
+difference in colour values that is allowed between the original and the mask. In practice
+this means that low-contrast areas of the picture are left unrendered whereas edges
+are treated normally. This is good for pictures of e.g. skin or blue skies.
+
+Any suggenstions for improvement of the algorithm, expecially regarding the speed
+and the roundoff errors in the Gaussian blur process, are welcome.
+*/
+
+class phpUnsharpMask {
+
+	function applyUnsharpMask(&$img, $amount, $radius, $threshold) {
+
+		// $img is an image that is already created within php using
+		// imgcreatetruecolor. No url! $img must be a truecolor image.
+
+		// Attempt to calibrate the parameters to Photoshop:
+		$amount = min($amount, 500);
+		$amount = $amount * 0.016;
+		if ($amount == 0) {
+			return true;
+		}
+
+		$radius = min($radius, 50);
+		$radius = $radius * 2;
+
+		$threshold = min($threshold, 255);
+
+		$radius = abs(round($radius)); 	// Only integers make sense.
+		if ($radius == 0) {
+			return true;
+		}
+
+		$w = ImageSX($img);
+		$h = ImageSY($img);
+		$imgCanvas  = ImageCreateTrueColor($w, $h);
+		$imgCanvas2 = ImageCreateTrueColor($w, $h);
+		ImageCopy($imgCanvas,  $img, 0, 0, 0, 0, $w, $h);
+		ImageCopy($imgCanvas2, $img, 0, 0, 0, 0, $w, $h);
+
+
+		$builtinFilterSucceeded = false;
+		if ($radius == 1) {
+			if (phpthumb_functions::version_compare_replacement(phpversion(), '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
+				if (ImageFilter($imgCanvas, IMG_FILTER_GAUSSIAN_BLUR) && ImageFilter($imgCanvas2, IMG_FILTER_GAUSSIAN_BLUR)) {
+					$builtinFilterSucceeded = true;
+				}
+			}
+		}
+
+		if (!$builtinFilterSucceeded) {
+			$imgBlur  = ImageCreateTrueColor($w, $h);
+			$imgBlur2 = ImageCreateTrueColor($w, $h);
+
+			///////////////////////////
+			//
+			// Gaussian blur matrix:
+			//	1  2  1
+			//	2  4  2
+			//	1  2  1
+			//
+			///////////////////////////
+
+			// Move copies of the image around one pixel at the time and merge them with weight
+			// according to the matrix. The same matrix is simply repeated for higher radii.
+			for ($i = 0; $i < $radius; $i++)	{
+				ImageCopy     ($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1);            // up left
+				ImageCopyMerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w,     $h,     50);        // down right
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h,     33.33333);  // down left
+				ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w,     $h - 1, 25);        // up right
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h,     33.33333);  // left
+				ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w,     $h,     25);        // right
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w,     $h - 1, 20 );       // up
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w,     $h,     16.666667); // down
+				ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w,     $h,     50);        // center
+				ImageCopy     ($imgCanvas, $imgBlur, 0, 0, 0, 0, $w,     $h);
+
+				// During the loop above the blurred copy darkens, possibly due to a roundoff
+				// error. Therefore the sharp picture has to go through the same loop to
+				// produce a similar image for comparison. This is not a good thing, as processing
+				// time increases heavily.
+				ImageCopy     ($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 20 );
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 16.666667);
+				ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
+				ImageCopy     ($imgCanvas2, $imgBlur2, 0, 0, 0, 0, $w, $h);
+			}
+			ImageDestroy($imgBlur);
+			ImageDestroy($imgBlur2);
+		}
+
+		// Calculate the difference between the blurred pixels and the original
+		// and set the pixels
+		for ($x = 0; $x < $w; $x++)	{ // each row
+			for ($y = 0; $y < $h; $y++)	{ // each pixel
+
+				$rgbOrig = ImageColorAt($imgCanvas2, $x, $y);
+				$rOrig = (($rgbOrig >> 16) & 0xFF);
+				$gOrig = (($rgbOrig >>  8) & 0xFF);
+				$bOrig =  ($rgbOrig        & 0xFF);
+
+				$rgbBlur = ImageColorAt($imgCanvas, $x, $y);
+				$rBlur = (($rgbBlur >> 16) & 0xFF);
+				$gBlur = (($rgbBlur >>  8) & 0xFF);
+				$bBlur =  ($rgbBlur        & 0xFF);
+
+				// When the masked pixels differ less from the original
+				// than the threshold specifies, they are set to their original value.
+				$rNew = (abs($rOrig - $rBlur) >= $threshold) ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) : $rOrig;
+				$gNew = (abs($gOrig - $gBlur) >= $threshold) ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) : $gOrig;
+				$bNew = (abs($bOrig - $bBlur) >= $threshold) ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) : $bOrig;
+
+				if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
+					$pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
+					ImageSetPixel($img, $x, $y, $pixCol);
+				}
+			}
+		}
+		ImageDestroy($imgCanvas);
+		ImageDestroy($imgCanvas2);
+
+		return true;
+	}
+
+}
+
+?>
Index: /afridex/plugins/Flutter/canvas-xml.php
===================================================================
--- /afridex/plugins/Flutter/canvas-xml.php (revision 21)
+++ /afridex/plugins/Flutter/canvas-xml.php (revision 21)
@@ -0,0 +1,192 @@
+<?php
+/*
+
+___Canvas XML Functions_________________________________________
+
+Imports function name and variable definitions from an XML file.
+Contains the XML and directory scanning functions.
+
+________________________________________________________________
+
+*/
+
+
+function canvas_get_plugin($filename, $modTitle) {
+	global $plugin, $current_function, $current_variable, $current_tag, $current_option;
+	if (!($xmlparser = xml_parser_create())) echo("ERROR: Cannot create parser for ".$filename);
+	xml_set_element_handler($xmlparser, "startElement", "endElement");
+
+	$current_function = trim($modTitle);
+	$plugin = new importedFunction();
+	$plugin->functionName = $current_function;
+
+	xml_set_character_data_handler($xmlparser, "canvas_import_XML_content");
+
+	if (!($fp = fopen($filename, "r"))) echo("ERROR: Can't open ".$filename);
+	while ($data = fread($fp, 4096)) {
+		$data=eregi_replace(">"."[[:space:]]+"."<","><",$data);
+        $data=eregi_replace(">"."[[:space:]]+",">",$data);
+        $data=eregi_replace("[[:space:]]+"."<","<",$data);
+		
+		if (!xml_parse($xmlparser, $data, feof($fp))) {
+			echo(sprintf("XML error: %s at line %d of".$filename, 
+				xml_error_string(xml_get_error_code($xml_parser)), 
+				xml_get_current_line_number($xml_parser)));
+		}
+	}
+	xml_parser_free($xmlparser);
+	
+	return $plugin;
+}
+
+function canvas_import_XML_content($parser, $data) {
+	global $plugin, $current_function, $current_variable, $current_option, $current_tag;
+	
+	$functionNameKey = "^CANVAS^FUNCTION^NAME";
+	$functionUBIKey = "^CANVAS^FUNCTION^UBI";
+	$functionPathKey = "^CANVAS^FUNCTION^PATH";
+	$functionAuthorKey = "^CANVAS^FUNCTION^AUTHOR";
+	$functionUriKey = "^CANVAS^FUNCTION^URI";
+	$functionDescKey = "^CANVAS^FUNCTION^DESCRIPTION";
+	$functionGroupKey = "^CANVAS^FUNCTION^GROUP";
+	$functionNumvarsKey = "^CANVAS^FUNCTION^NUMVARS";
+	$variablesKey = "^CANVAS^FUNCTION^VARIABLES";
+	$variableKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE";
+	$variableNameKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^NAME";
+	$variableDescriptionKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^DESCRIPTION";
+	$variableTypeKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^TYPE";
+	$variableDirectoryKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^DIRECTORY";
+	$variableDefaultKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^DEFAULT";
+    	$variableNumoptsKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^NUMOPTIONS";
+	$optionsKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^OPTIONS";
+	$optionKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^OPTIONS^OPTION";
+    	$optionTextKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^OPTIONS^OPTION^DISPLAYTEXT";
+    	$optionValueKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^OPTIONS^OPTION^VALUE";
+	$optionParamsKey = "^CANVAS^FUNCTION^VARIABLES^VARIABLE^OPTIONS^OPTION^PARAMS";	
+
+	if($current_tag == $functionNameKey) {
+		$current_function = trim($data);
+		$plugin = new importedFunction();
+		$plugin->functionName = $current_function;
+	} elseif($current_tag == $functionUBIKey) {
+		$plugin->functionUBI = trim($data);
+	} elseif($current_tag == $functionPathKey) {
+		$plugin->functionPath = trim($data);
+	} elseif($current_tag == $functionAuthorKey) {
+		$plugin->functionAuthor = trim($data);
+	} elseif($current_tag == $functionUriKey) {
+		$plugin->functionUri = trim($data);
+	} elseif($current_tag == $functionDescKey) {
+		$plugin->functionDesc = trim($data);
+	} elseif($current_tag == $functionGroupKey) {
+		if(trim($data) == '') $data = 'Other';
+		$plugin->functionGroup = $data;
+	} elseif($current_tag == $variableNameKey) {
+		$current_variable = trim($data);
+		$plugin->variables[$current_variable]['name'] = $current_variable;
+	} elseif($current_tag == $variableDescriptionKey) {
+		$plugin->variables[$current_variable]['description'] = trim($data);
+	} elseif($current_tag == $variableTypeKey) {
+		$plugin->variables[$current_variable]['type'] = trim($data);
+	} elseif($current_tag == $variableDirectoryKey) {
+		$plugin->variables[$current_variable]['option']['gallery']['name'] = 'Image Directory URI';
+		$plugin->variables[$current_variable]['option']['gallery']['value'] = trim($data);
+	} elseif($current_tag == $variableDefaultKey) {
+		$plugin->variables[$current_variable]['default'] = trim($data);
+	} elseif($current_tag == $optionTextKey) {
+        	$current_option = trim($data);
+	    	$plugin->variables[$current_variable]['option'][$current_option]['name'] = $current_option;
+    	} elseif($current_tag == $optionValueKey) {
+	    $plugin->variables[$current_variable]['option'][$current_option]['value'] = trim($data);
+    	} elseif($current_tag == $optionParamsKey) {
+	    $plugin->variables[$current_variable]['option'][$current_option]['params'] = trim($data);
+    	}
+
+
+}
+
+function canvas_import_layout_XML($parser, $data) {
+	global $imported_theme, $imported_blocks, $imported_variables, $id, $variable, $current_tag;
+	
+	$themeTitle = "^CANVAS^EXPORT^META^TITLE";
+	$themeAuthor = "^CANVAS^EXPORT^META^AUTHOR";
+	$themeURI = "^CANVAS^EXPORT^META^URI";
+	$theme = "^CANVAS^EXPORT^META^THEME";
+	$blockName = "^CANVAS^EXPORT^LAYOUT^BLOCK^NAME";
+	$blockType = "^CANVAS^EXPORT^LAYOUT^BLOCK^TYPE";
+	$blockZone = "^CANVAS^EXPORT^LAYOUT^BLOCK^ZONE";
+	$blockPosition = "^CANVAS^EXPORT^LAYOUT^BLOCK^POSITION";
+	$blockAuthor = "^CANVAS^EXPORT^LAYOUT^BLOCK^AUTHOR";
+	$blockDesc = "^CANVAS^EXPORT^LAYOUT^BLOCK^DESCRIPTION";
+	$blockGroup = "^CANVAS^EXPORT^LAYOUT^BLOCK^GROUP";
+	$blockURI = "^CANVAS^EXPORT^LAYOUT^BLOCK^URI";
+	$blockPath = "^CANVAS^EXPORT^LAYOUT^BLOCK^PATH";
+	$blockUBI = "^CANVAS^EXPORT^LAYOUT^BLOCK^UBI";
+	$blockID = "^CANVAS^EXPORT^LAYOUT^BLOCK^ID";
+	$variableName = "^CANVAS^EXPORT^LAYOUT^BLOCK^VARIABLES^VARIABLE^NAME";
+	$variableValue = "^CANVAS^EXPORT^LAYOUT^BLOCK^VARIABLES^VARIABLE^VALUE";
+
+	if($current_tag == $theme) {
+		$imported_theme['theme'] = trim($data);
+	} elseif($current_tag == $themeTitle) {
+		$imported_theme['title'] = trim($data);
+	} elseif($current_tag == $themeAuthor) {
+		$imported_theme['author'] = trim($data);
+	} elseif($current_tag == $themeURI) {
+		$imported_theme['uri'] = trim($data);
+	} elseif($current_tag == $blockID) {
+		$id = trim($data);
+	} elseif($current_tag == $blockUBI) {
+		$imported_blocks[$id]['ubi'] = trim($data);
+	} elseif($current_tag == $blockName) {
+		$imported_blocks[$id]['name'] = trim($data);
+	} elseif($current_tag == $blockType) {
+		$imported_blocks[$id]['type'] = trim($data);
+	} elseif($current_tag == $blockZone) {
+		$imported_blocks[$id]['zone'] = trim($data);
+	} elseif($current_tag == $blockPosition) {
+		$imported_blocks[$id]['position'] = trim($data);
+	} elseif($current_tag == $blockAuthor) {
+		$imported_blocks[$id]['author'] = trim($data);
+	} elseif($current_tag == $blockDesc) {
+		$imported_blocks[$id]['description'] = trim($data);
+	} elseif($current_tag == $blockGroup) {
+		$imported_blocks[$id]['group'] = trim($data);
+	} elseif($current_tag == $blockURI) {
+		$imported_blocks[$id]['uri'] = trim($data);
+	} elseif($current_tag == $blockPath) {
+		$imported_blocks[$id]['path'] = trim($data);
+	} elseif($current_tag == $variableName) {
+		$variable = trim($data);
+		$imported_variables[$id][$variable]['name'] = trim($data);
+	} elseif($current_tag == $variableValue) {
+		$imported_variables[$id][$variable]['value'] = trim($data);
+	}
+}
+
+function endElement($parser, $name) {
+	global $current_tag;
+	$caret_pos = strrpos($current_tag,'^');
+	$current_tag = substr($current_tag,0,$caret_pos);
+}
+
+function startElement($parser, $name, $attrs) {
+	global $current_tag;
+	$current_tag .= "^$name";
+}
+
+// Scans the directory (included for PHP 4 users)
+if(!function_exists(scandirectory)) {
+	function scandirectory($dir = './', $sort = 0) {
+		$dir_open = @opendir($dir);
+		if (!$dir_open) return false;
+		while(($dir_content = readdir($dir_open)) !== false) {
+			$files[] = $dir_content;
+		}
+		if ($sort == 1) rsort($files, SORT_STRING);
+		else sort($files, SORT_STRING);
+		return $files;
+	}
+}
+
+?>
Index: /afridex/plugins/Flutter/RCCWP_CreateCustomWriteModulePage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CreateCustomWriteModulePage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CreateCustomWriteModulePage.php (revision 21)
@@ -0,0 +1,29 @@
+<?php
+include_once('RCCWP_CustomWriteModulePage.php');
+class RCCWP_CreateCustomWriteModulePage
+{
+	function Main()
+	{
+		?>
+
+		<div class="wrap">
+
+		<h2><?php _e('Create Custom Write Module'); ?></h2>
+		
+		<form action="?page=FlutterManageModules&view-modules=1" method="post" id="create-new-write-module-form">
+		
+		<?php RCCWP_CustomWriteModulePage::Content(); ?>
+		
+		<p class="submit" >
+			<input name="cancel-create-custom-write-module" type="submit" id="cancel-create-custom-write-module" value="<?php _e('Cancel'); ?>" /> 
+			<input name="finish-create-custom-write-module" type="submit" id="finish-create-custom-write-module" value="<?php _e('Finish'); ?>" />
+		</p>
+		
+		</form>
+
+		</div>
+
+		<?php
+	}
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_ModuleDuplicatePage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_ModuleDuplicatePage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_ModuleDuplicatePage.php (revision 21)
@@ -0,0 +1,67 @@
+<?php
+include_once('RCCWP_CustomGroup.php');
+
+class RCCWP_ModuleDuplicatePage
+{
+	function Content($moduleDuplicate = null)
+	{
+		$customGroupName = "";
+		if (isset($_GET['custom-write-module-id']) )
+			$moduleID = $_GET['custom-write-module-id'];
+		if (isset($_POST['custom-write-module-id']) )
+			$moduleID = $_POST['custom-write-module-id'];
+
+		if ($moduleDuplicate != null)
+		{
+			$moduleDuplicateName = $moduleDuplicate->duplicate_name;
+		?>
+		<input type="hidden" name="module-duplicate-id" value="<?php echo $_REQUEST['module-duplicate-id']?>" />
+		<?php
+		}
+		
+  		?>
+		<?php if($moduleID) { ?>
+  			<input type="hidden" name="custom-write-module-id" value="<?php echo $moduleID?>">
+		<?php } ?>
+
+		<table class="optiontable" border="0">
+		<tbody>
+
+		<tr valign="top">
+			<th scope="row"  align="right">Name:</th>
+			<td><input name="module-duplicate-name" id="module-duplicate-name" size="40" type="text" value="<?php echo $moduleDuplicateName?>" /></td>
+		</tr>
+
+		</tbody>
+		</table>
+		
+		<?php
+	}
+	
+	function Edit()
+	{
+		$moduleDuplicate = RCCWP_ModuleDuplicate::Get((int)$_REQUEST['module-duplicate-id']);
+		?>
+		<div class="wrap">
+		
+		<h2>Edit Module Duplicate</h2>
+		
+		<form action="" method="post" id="edit-module-duplicate-form">
+		
+		<?php
+		RCCWP_ModuleDuplicatePage::Content($moduleDuplicate);
+		?>
+		
+		<p class="submit" >
+			<input name="cancel-edit-module-duplicate" type="submit" id="cancel-edit-module-duplicate" value="<?php _e('Cancel'); ?>" /> 
+			<input name="submit-edit-module-duplicate" type="submit" id="submit-edit-module-duplicate" value="<?php _e('Update'); ?>" />
+		</p>
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+	
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_GetFile.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_GetFile.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_GetFile.php (revision 21)
@@ -0,0 +1,140 @@
+<?php
+
+	require_once('../../../wp-config.php');
+
+	if ( ( isset($_SERVER['HTTPS']) && 'on' == strtolower($_SERVER['HTTPS']) ) && empty($_COOKIE[SECURE_AUTH_COOKIE]) && !empty($_REQUEST['auth_cookie']) )
+		$_COOKIE[SECURE_AUTH_COOKIE] = $_REQUEST['auth_cookie'];
+	elseif ( empty($_COOKIE[AUTH_COOKIE]) && !empty($_REQUEST['auth_cookie']) )
+		$_COOKIE[AUTH_COOKIE] = $_REQUEST['auth_cookie'];
+	unset($current_user);
+
+	if (!(is_user_logged_in() && current_user_can('edit_posts')))
+  		die("Athentication failed!");
+
+	$imagesExts = array('gif','jpg','png');
+	$audiosExts = array('wav','mp3');
+	
+	$acceptedExtsString = "";
+	
+	function DownloadFile(){
+		global $acceptedExtsString, $imagesExts, $audiosExts;
+	
+		$url = $_POST['upload_url'];
+	
+		// Prepare accpeted extensions
+		$acceptedExts = array();
+	
+		if ('1' == $_POST['type'])
+			$acceptedExts = $imagesExts;
+		elseif ('2' == $_POST['type']) 	
+			$acceptedExts = $audiosExts;
+	
+	
+		//Retrieve file
+		if ($fp_source = @fopen($url, 'rb')) {
+			//Get target filename
+			$exploded_url = explode( '.', $url );
+	
+			$ext = array_pop( $exploded_url );
+	
+			// Check extension
+			if (false != $acceptedExts)
+				if (false === array_search(strtolower($ext), $acceptedExts)){
+					foreach($acceptedExts as $acceptedExt)
+						if ($acceptedExtsString == "")
+							$acceptedExtsString = $acceptedExt;
+						else
+							$acceptedExtsString = $acceptedExtsString." - ".$acceptedExt;
+					return false;
+				}
+			
+	
+			$filename = time() . '_' . str_replace( 'rc_cwp_meta_', '', $_POST["input_name"]) . '.' . $ext;
+			
+			$directory = dirname(__FILE__) . '/files_flutter/';
+	
+			$fp_dest = @fopen($directory . $filename,"wb");
+			if ($fp_dest == false) return false;
+	
+			while(!feof($fp_source)) {
+				set_time_limit(30);
+	
+				//if (connection_status()!=0) return false;
+	
+				$readData = fread($fp_source, 1024*2);
+				//if ($readData == false) return false;
+				
+				fwrite($fp_dest,$readData);
+				
+			}
+			fclose($fp_source) ;
+			fclose($fp_dest) ;
+			//chmod($directory . $filename, 0644);
+	
+			return $filename;
+		}
+		return false;
+	
+	}
+
+	if (isset($_POST['upload_url']) && (!empty($_POST['upload_url'])))  // file was send from browser
+	{
+		if ( (substr($_POST['upload_url'],0,4) != "http") && (substr($_POST['upload_url'],0,3) != "ftp"))
+			$_POST['upload_url'] = "http://".$_POST['upload_url'];
+
+		$filename = DownloadFile();
+		
+
+		if (false == $filename) {
+			if ($acceptedExtsString != "") $infoStr = ". Make sure the file ends with: $acceptedExtsString";
+// 			$result_msg = "Error downloading file: ".$_POST['upload_url'].$infoStr;
+			$result_msg = "<font color='red'><b>Upload Unsuccessful!</b></font>";
+		}
+		else{
+// 			$result_msg = 'The URL '.$_POST['upload_url'].' was downloaded successfuly. Please remember to click the save button.';
+			$result_msg = "<font color='green'><b>Successful upload!</b></font>" ;
+			$operationSuccess = "true";
+		}
+		include_once("RCCWP_WritePostPage.php") ;
+		$edit_anchor = RCCWP_WritePostPage::snipshot_anchor(FLUTTER_URI.'files_flutter/'.$filename) ;
+		echo $result_msg."*".$filename."*".$edit_anchor ;
+	}
+
+if( isset($_FILES['Filedata'] ) )
+{
+	if ($_FILES['Filedata']['error'] == UPLOAD_ERR_OK)  // no error
+	{
+		$special_chars = array (' ','`','"','\'','\\','/'," ","#","$","%","^","&","*","!","~","â","\"","â","'","=","?","/","[","]","(",")","|","<",">",";","\\",",");
+		$filename = str_replace($special_chars,'',$_FILES['Filedata']['name']);
+		$filename = time() . $filename;
+		@move_uploaded_file( $_FILES['Filedata']['tmp_name'], dirname(__FILE__) . '/files_flutter/' . $filename );
+		@chmod(dirname(__FILE__) . '/files_flutter/' . $filename, 0644);
+
+// 		$result_msg = 'The file '.$_FILES['Filedata']['name'].' was uploaded successfuly. Please remember to click the save button.';
+		$result_msg = "<font color='green'><b>Successful upload!</b></font>" ;
+		$operationSuccess = "true";
+	}
+	elseif ($_FILES['Filedata']['error'] == UPLOAD_ERR_INI_SIZE)
+		$result_msg = 'The uploaded file exceeds the maximum upload limit';
+	else 
+		$result_msg = "<font color='red'><b>Upload Unsuccessful!</b></font>";
+
+	include_once("RCCWP_WritePostPage.php") ;
+	$edit_anchor = RCCWP_WritePostPage::snipshot_anchor(FLUTTER_URI.'files_flutter/'.$filename) ;
+	echo $result_msg."*".$filename."*".$edit_anchor ;
+}
+/*
+	if( isset($_FILES['Filedata'] ) )
+	{
+		$path = dirname(__FILE__) . '/files_flutter/';
+		$path = $path . basename( $_FILES['Filedata']['name'] );
+		if( move_uploaded_file($_FILES['Filedata']['tmp_name'], $path) )
+		{
+			echo 'The file was uploaded successfuly. Please remember to click the save button.'.$_FILES['Filedata']['name'] ;
+		}
+		else
+		{
+			echo $_FILES[0]['error'] ;
+		}
+	}*/
+?>
Index: /afridex/plugins/Flutter/RCCWP_CustomGroupPage.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_CustomGroupPage.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_CustomGroupPage.php (revision 21)
@@ -0,0 +1,191 @@
+<?php
+include_once('RCCWP_CustomGroup.php');
+
+class RCCWP_CustomGroupPage
+{
+	function Content($customGroup = null)
+	{
+		$customGroupName = "";
+		if (isset($_GET['custom-write-panel-id']) )
+			$customWritePanelId = $_GET['custom-write-panel-id'];
+		if (isset($_POST['custom-write-panel-id']) )
+			$customWritePanelId = $_POST['custom-write-panel-id'];
+
+		if ($customGroup != null)
+		{
+			$customGroupName = $customGroup->name;
+			$customGroupDuplicate = $customGroup->duplicate;
+			$customGroupAtRight = $customGroup->at_right;
+		}
+		
+  		?>
+		<?php if($customWritePanelId) { ?>
+  			<input type="hidden" name="custom-write-panel-id" value="<?php echo $customWritePanelId?>">
+		<?php } ?>
+
+		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+		<tbody>
+
+		<tr valign="top">
+			<th scope="row"  align="right">Name:</th>
+			<td><input name="custom-group-name" id="custom-group-name" size="40" type="text" value="<?php echo $customGroupName?>" /></td>
+		</tr>
+
+		<tr>
+			<th scope="row" align="right">Duplication:</th>
+			<td><input name="custom-group-duplicate" id="custom-group-duplicate" type="checkbox" value="1" <?php echo $customGroupDuplicate == 0 ? "":"checked" ?> />&nbsp;The group can be duplicated</td>
+		</tr>
+		
+		<tr>
+			<th scope="row" align="right">Position:</th>
+			<td><input name="custom-group-at_right" id="custom-group-at_right" type="checkbox" value="1" <?php echo $customGroupAtRight == 0 ? "":"checked" ?> />&nbsp;Add the group on the right.</td>
+		</tr>
+
+		<?php
+		if (!isset($customGroup)) :
+		?>
+		<tr>
+			<th scope="row" align="right">Custom Fields:</th>
+			<td>Add custom fields later by editing this custom group.</td>
+		</tr>
+		<?php
+		endif;
+		?>
+		</tbody>
+		</table>
+		
+		<?php
+	}
+	
+	function Edit()
+	{
+		$customGroup = RCCWP_CustomGroup::Get((int)$_REQUEST['custom-group-id']);
+		?>
+		<div class="wrap">
+		
+		<h2>Edit Group - <?php echo $customGroup->name?></h2>
+		
+		<form action="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('submit-edit-custom-group')."&custom-group-id={$customGroup->id}"?>" method="post" id="edit-custom-group-form">
+		
+		<?php
+		RCCWP_CustomGroupPage::Content($customGroup);
+		?>
+		
+		<p class="submit" >
+			<a style="color:black" href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('cancel-edit-custom-group')?>" class="button"><?php _e('Cancel'); ?></a> 
+			<input type="submit" id="submit-edit-custom-group" value="<?php _e('Update'); ?>" />
+		</p>
+		</form>
+		
+		</div>
+		
+		<?php
+	}
+	
+	function GetCustomFields($customGroupId)
+	{
+		global $wpdb;
+		$sql = "SELECT cf.id, cf.name, tt.name AS type, cf.description, cf.display_order, co.options, co.default_option AS default_value, tt.has_options, cp.properties, tt.has_properties, tt.allow_multiple_values FROM " . RC_CWP_TABLE_GROUP_FIELDS .
+			" cf LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_OPTIONS . " co ON cf.id = co.custom_field_id" .
+			" LEFT JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_PROPERTIES . " cp ON cf.id = cp.custom_field_id" .
+			" JOIN " . RC_CWP_TABLE_CUSTOM_FIELD_TYPES . " tt ON cf.type = tt.id" . 
+			" WHERE group_id = " . $customGroupId .
+			" ORDER BY cf.display_order";
+		$results =$wpdb->get_results($sql);
+		if (!isset($results))
+			$results = array();
+		
+		for ($i = 0; $i < $wpdb->num_rows; ++$i)
+		{
+			$results[$i]->options = unserialize($results[$i]->options);
+			$results[$i]->properties = unserialize($results[$i]->properties);
+			$results[$i]->default_value = unserialize($results[$i]->default_value);
+		}
+		
+		return $results;
+	}
+	
+	/*function View($param = 23)
+	{
+
+		if(isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id']) )
+			$customWritePanelId = (int)$_GET['custom-write-panel-id'];
+		if(isset($_POST['custom-write-panel-id']) && !empty($_POST['custom-write-panel-id']) )
+			$customWritePanelId = (int)$_POST['custom-write-panel-id'];
+
+		if(isset($_GET['custom-group-id']) && !empty($_GET['custom-group-id']) )
+			$customGroupId = (int)$_GET['custom-group-id'];
+		if(isset($_POST['custom-group-id']) && !empty($_POST['custom-group-id']) )
+			$customGroupId = (int)$_POST['custom-group-id'];
+
+		$customGroup = RCCWP_CustomGroup::Get($customGroupId);
+
+		?>
+
+		<div class="wrap">
+
+		<h2>Custom Group Info</h2>
+		<h4><a href="<?php echo RCCWP_ManagementPage::GetCustomWriteModuleEditUrl($customWritePanelId); ?>"> Â« Back to Module</a></h4>
+		<form action="" method="post" id="view-group-form">
+		
+		<input type="hidden" name="custom-group-id" value="<?php echo $customGroupId?>" />
+			
+  		<table class="form-table" width="100%" border="0" cellspacing="0" cellpadding="6">
+  		<tbody>
+  		<tr>
+			<th scope="row" align="right">Name:</th>
+			<td><?php echo $customGroup->name ?></td>
+		</tr>
+  		</tbody>
+  		</table>
+		  
+		
+		<a href="<?php echo RCCWP_ManagementPage::GetCustomWritePanelGenericUrl('edit-custom-group')."&custom-group-id=$customGroupId"?>" class="button-secondary"><?php _e('Edit Group'); ?></a>		
+		
+		
+		<h3>Custom Fields</h3>
+
+		<div class="tablenav"><div class="alignright">
+			<input name="flutter_action" type="hidden" value="create-custom-field" />
+			<input name="create-custom-field" type="submit" id="create-custom-field" value="Create Custom Field"  class="button-secondary"  />
+		</div></div>
+		<br class="clear"/>
+		
+  		<table cellpadding="3" cellspacing="3" width="100%" class="widefat">
+  		<thead>
+  		<tr>
+  			<th scope="col">Order</th>
+			<th scope="col">Name</th>
+			<th scope="col">Type</th>
+<!--			<th scope="col">Description</th> -->
+			<th scope="col" colspan="2">Action</th>
+		</tr>
+  		</thead>
+  		<tbody>
+  		<?php
+  		$custom_fields = RCCWP_CustomGroup::GetCustomFields($customGroupId);
+  		foreach ($custom_fields as $field) :
+  			$class = $class == '' ? 'alternate' : '';
+  		?>
+  			<tr class="<?php echo $class?>">
+  				<td align="right" width="3"><?php echo $field->display_order?></td>
+  				<td align="center"><?php echo $field->name?></td>
+  				<td><?php echo $field->type?></td>
+<!--  				<td><?php echo $field->description?></td> -->
+  				<td><a href="<?php echo RCCWP_ManagementPage::GetCustomFieldEditUrl($customWritePanelId, $customGroupId, $field->id)?>" class="edit">Edit</a></td>
+  				<td><a href="<?php echo RCCWP_ManagementPage::GetCustomFieldDeleteUrl($customGroupId, $field->id)?>" class="delete">Delete</a></td>
+  			</tr>
+  		<?php
+  		endforeach;
+  		?>
+  		</tbody>
+  		</table>
+			
+		</form>
+
+		</div>
+		
+		<?php
+	}*/
+}
+?>
Index: /afridex/plugins/Flutter/RCCWP_Post.php
===================================================================
--- /afridex/plugins/Flutter/RCCWP_Post.php (revision 21)
+++ /afridex/plugins/Flutter/RCCWP_Post.php (revision 21)
@@ -0,0 +1,281 @@
+<?php
+class RCCWP_Post
+{	
+	
+	function SaveCustomFields($postId){
+		if(!wp_verify_nonce($_REQUEST['rc-custom-write-panel-verify-key'], 'rc-custom-write-panel'))
+			return $postId;
+        		
+		if (!current_user_can('edit_post', $postId))
+			return $postId;
+		
+		RCCWP_Post::SetCustomWritePanel($postId);
+		RCCWP_Post::PrepareFieldsValues($postId);
+		RCCWP_Post::SetMetaValues($postId);
+		
+		return $postId;
+	}
+	
+		
+	/*
+	 * Attach a custom write panel to the current post by saving the custom write panel id
+	 * as a meta value for the post
+	 */
+	function SetCustomWritePanel($postId)
+	{
+		
+		$customWritePanelId = $_POST['rc-cwp-custom-write-panel-id'];
+		if (isset($customWritePanelId))
+		{
+			if (!empty($customWritePanelId))
+			{
+				
+				if (!update_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY, $customWritePanelId))
+				{
+
+					add_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY, $customWritePanelId);
+				}
+			}
+			else
+			{
+				delete_post_meta($postId, RC_CWP_POST_WRITE_PANEL_ID_META_KEY);
+			}
+		}
+	}
+	
+	/**
+	 * Save all custom field values meta values for the post, this function assumes that 
+	 * $_POST['rc_cwp_meta_keys'] contains the names of the fields, while $_POST[{FIELD_NAME}]
+	 * contains the value of the field named {FIELD_NAME}
+	 *
+	 * @param unknown_type $postId
+	 * @return unknown
+	 */
+	function SetMetaValues($postId)
+	{
+		global $wpdb;
+			
+		$customWritePanelId = $_POST['rc-cwp-custom-write-panel-id'];
+		$customFieldKeys = $_POST['rc_cwp_meta_keys'];
+		
+		if (!empty($customWritePanelId) && !empty($customFieldKeys) )
+		{
+				
+			// --- Delete old values
+			foreach ($customFieldKeys as $key)
+			{
+				if (!empty($key))
+				{
+					list($customFieldId, $groupCounter, $fieldCounter, $rawCustomFieldName) = split("_", $key, 4);
+					$customFieldName = $wpdb->escape(stripslashes(trim(RC_Format::GetFieldName($rawCustomFieldName))));
+					delete_post_meta($postId, $customFieldName);	
+				}
+			}
+			$wpdb->query("DELETE FROM ". RC_CWP_TABLE_POST_META .
+				" WHERE post_id=$postId");
+
+			// --- Make sure all groups/fields duplicates are in sequence, 
+			//		i.e. there is no gap due to removing items
+			
+			$arr = ARRAY();
+			foreach($customFieldKeys as $key=>$value)
+			{
+				list($customFieldId, $groupCounter, $fieldCounter, $rawCustomFieldName) = split("_", $value, 4);
+				$arr[$key]->id = $customFieldId ;
+				$arr[$key]->gc = $groupCounter ;
+				$arr[$key]->fc = $fieldCounter ;
+				$arr[$key]->fn = $rawCustomFieldName ;
+				$arr[$key]->ov = $value ;
+			}
+
+
+			for($i=0;$i<$key;$i++){
+				for($j=0;$j<$key;$j++){
+					if( $arr[$i]->id == $arr[$j]->id )
+					{
+						if( $arr[$i]->gc == $arr[$j]->gc )
+						{
+							if( $arr[$i]->fc < $arr[$j]->fc )
+							{
+								$t = $arr[$i] ;
+								$arr[$i] = $arr[$j] ;
+								$arr[$j] = $t ;
+							}
+						}
+						else if( $arr[$i]->gc < $arr[$j]->gc )
+						{
+							$t = $arr[$i] ;
+							$arr[$i] = $arr[$j] ;
+							$arr[$j] = $t ;
+						}
+					}
+					else if( $arr[$i]->id < $arr[$j]->id )
+					{
+						$t = $arr[$i] ;
+						$arr[$i] = $arr[$j] ;
+						$arr[$j] = $t ;
+					}
+				}
+			}
+			
+			for($i=0;$i<$key;$i++)
+			{
+				if( $arr[$i]->id != $currentFieldID )
+				{
+					$currentFieldID = $arr[$i]->id ;
+					$currentG = $arr[$i]->gc ;
+					$GC = 1 ;
+					$FC = 1 ;
+				}
+				else if( $arr[$i]->gc != $currentG )
+				{
+					$GC ++ ;
+					$FC = 1 ;
+					$currentG = $arr[$i]->gc ;
+				}
+				else $FC ++ ;
+				$arr[$i]->fc = $FC ;
+				$arr[$i]->gc = $GC ;
+			}
+
+
+			// --- Add new meta data
+			foreach ($arr as $key)
+			{
+				if (!empty($key))
+				{
+//					list($customFieldId, $groupCounter, $fieldCounter, $rawCustomFieldName) = split("_", $key, 4);
+					$customFieldValue = $_POST[$key->ov];
+				
+					$customFieldName = $wpdb->escape(stripslashes(trim(RC_Format::GetFieldName($key->fn))));
+					
+					// Prepare field value
+					if (is_array($customFieldValue))
+					{
+						$finalValue = array();
+						foreach ($customFieldValue as $value)
+						{
+							$value = stripslashes(trim($value));
+							array_push($finalValue, $value);
+							//add_post_meta($postId, $customFieldName, $value);
+						}
+						
+						
+					}
+					else
+					{
+						$finalValue = stripslashes(trim($customFieldValue));
+					}
+					
+					// Add field value meta data
+					add_post_meta($postId, $customFieldName, $finalValue);
+					$fieldMetaID = $wpdb->insert_id;
+
+					// Add field extended properties
+					$wpdb->query("INSERT INTO ". RC_CWP_TABLE_POST_META .
+								" (id, field_name, group_count, field_count, post_id) ".
+								" VALUES ($fieldMetaID, '$customFieldName', ".$key->gc.", ".$key->fc.", $postId)");
+				}
+			}
+	 	}	
+	}
+	
+	/**
+	 * This function prepares some custom fields before saving it. It reads $_REQUEST and:
+	 * 1. Adds params to photos uploaded (Image field)
+	 * 2. Formats dates (Date Field) 
+	 *
+	 */
+	function PrepareFieldsValues($postId)
+	{
+			
+		// Add params to photos
+		if( isset( $_REQUEST['rc_cwp_meta_photos'] ) ) 
+		{
+			foreach( $_REQUEST['rc_cwp_meta_photos'] as $meta_name )
+			{		
+				$slashPos = strrpos($_POST[$meta_name], "/");
+				if (!($slashPos === FALSE))
+					$_POST[$meta_name] = substr($_POST[$meta_name], $slashPos+1);
+
+				// if photo is new, add params
+				/*if( isset( $_REQUEST[ $meta_name . '_params' ] ) && $_REQUEST[ $meta_name . '_params' ] )
+				{
+					if( ! strpos( $_POST[$meta_name], $_REQUEST[ $meta_name . '_params' ] ) )
+					{
+						$_POST[$meta_name] .= $_REQUEST[$meta_name . '_params'];
+					}
+				}*/
+				
+				//Rename photo if it is edited using editnplace to avoid phpthumb cache
+				if ($_POST[$meta_name.'_dorename'] == 1){
+					$oldFilename = $_POST[$meta_name]; 
+					$newFilename = time().substr($oldFilename, 10);
+					rename(FLUTTER_UPLOAD_FILES_DIR.$oldFilename, FLUTTER_UPLOAD_FILES_DIR.$newFilename);
+					$_POST[$meta_name] = $newFilename;
+				}
+				
+			}
+		}
+
+		// Format Dates
+		if( isset( $_REQUEST['rc_cwp_meta_date'] ) )
+		{
+			foreach( $_REQUEST['rc_cwp_meta_date'] as $meta_name )
+			{
+				$metaDate = strtotime($_POST[$meta_name]);
+				$formatted_date = strftime("%Y-%m-%d", $metaDate);
+				$_POST[$meta_name] = $formatted_date;
+			}
+		}
+	}
+	
+	/**
+	 * Get a custom write panel by reading $_REQUEST['custom-write-panel-id'] or the
+	 * To see whether $_GET['post'] has a custom write panel associated to it.
+	 *
+	 * @return Custom Write Panel as an object, returns null if there is no write panels.
+	 */
+	function GetCustomWritePanel()
+	{
+		
+		if (isset($_GET['post']))
+		{
+
+			$customWritePanelId = get_post_meta((int)$_GET['post'], RC_CWP_POST_WRITE_PANEL_ID_META_KEY, true);
+		
+		
+			if (empty($customWritePanelId))
+			{
+				$customWritePanelId = (int)$_REQUEST['custom-write-panel-id'];
+			}
+		}
+		else if (isset($_REQUEST['custom-write-panel-id']))
+		{
+			$customWritePanelId = (int)$_REQUEST['custom-write-panel-id'];
+		}
+		
+		if (isset($customWritePanelId))
+		{
+			include_once('RCCWP_Application.php');
+			$customWritePanel = RCCWP_CustomWritePanel::Get($customWritePanelId);
+		}
+		
+		return $customWritePanel;
+	}
+
+
+	/**
+	 *
+	 *
+	*/
+	function DeletePostMetaData($postId)
+	{
+		global $wpdb;
+		$wpdb->query("DELETE FROM " . RC_CWP_TABLE_POST_META . " WHERE post_id =" . $postId) ;
+	}
+
+	
+	
+}
+?>
Index: /afridex/plugins/Flutter/Main.php
===================================================================
--- /afridex/plugins/Flutter/Main.php (revision 21)
+++ /afridex/plugins/Flutter/Main.php (revision 21)
@@ -0,0 +1,356 @@
+<?php
+/*
+Plugin Name: Flutter
+Plugin URI: http://freshoutcreative.com/goodies
+Description: Create page templates with custom file uploads and integrated photo resizing.
+Author: Freshout
+Version: .2.26
+Author URI: http://freshoutcreative.com
+*/
+
+
+// To generate PHPDoc, use the following command:
+// phpdoc -o HTML:Smarty:default -t ./docs -d . -i purifier_lib/ -po FlutterDatabaseObjects -ti 'Flutter API Documentation'
+
+
+// Globals
+global $wpdb, $canvas, $main_page, $table_prefix, $zones, $parents, $installed, $main_page, $post;
+global $current_user;
+global $wp_filesystem;
+global $FIELD_TYPES;
+
+// Classes
+require_once 'classes/FlutterPanelFields.php';
+
+// Include Flutter API related files
+require_once 'RCCWP_CustomGroup.php';
+require_once 'RCCWP_CustomField.php';
+require_once 'RCCWP_CustomWriteModule.php';
+require_once 'RCCWP_CustomWritePanel.php';
+
+// Include files containing Flutter public functions
+require_once 'canvas-core.php';
+require_once 'get-custom.php';
+
+// Include other files used in this script
+require_once 'RCCWP_Menu.php';
+require_once 'RCCWP_CreateCustomFieldPage.php';
+
+
+
+global $is_wordpress_mu;
+if(isset($current_blog)) 
+	$is_wordpress_mu=true;
+else
+	$is_wordpress_mu=false;
+
+if (is_admin())
+{
+	
+	require_once ('RCCWP_Constant.php');
+	require_once ('RCCWP_Application.php');
+	require_once ('RCCWP_WritePostPage.php');
+	
+
+	//add_action('activate_Flutter/RCCWP_Application.php', array('RCCWP_Application', 'Install'));
+	//add_action('activate_Flutter.php', array('RCCWP_Application', 'Install'));
+
+	
+	register_activation_hook(dirname(__FILE__) . '/Main.php', array('RCCWP_Application', 'Install'));
+
+	//if($is_wordpress_mu)
+	if(isset($current_blog))
+		RCCWP_Application::Install();
+	add_action('admin_menu', array('RCCWP_Application', 'ContinueInstallation'));
+
+	if (get_option(RC_CWP_OPTION_KEY) !== false)
+	{
+		
+
+		//register_deactivation_hook(dirname(__FILE__) . '/Main.php', array('RCCWP_Application', 'Uninstall'));
+			
+		
+		require_once ('RCCWP_Processor.php');
+		add_action('init', array('RCCWP_Processor', 'Main'));
+		
+
+		add_action('admin_menu', array('RCCWP_Menu', 'AttachCustomWritePanelMenuItems'));
+		add_action('admin_menu', array('RCCWP_Menu', 'DetachWpWritePanelMenuItems'));
+		add_action('admin_menu', array('RCCWP_Menu', 'AttachOptionsMenuItem'));
+		
+		add_filter('posts_where', array('RCCWP_Menu', 'FilterPostsPagesList'));
+		add_action('admin_head', array('RCCWP_Menu', 'HighlightCustomPanel'));
+		add_action('admin_head', array('RCCWP_CreateCustomFieldPage', 'AddAjaxDynamicList'));
+
+		//add_filter('the_posts', array('RCCWP_Menu', 'FilterPostsPagesList'));
+		//add_filter('get_pages', array('RCCWP_Menu', 'FilterPostsPagesList'));
+
+		// -- Hook all functions related to saving posts in order to save custom fields values
+		require_once ('RCCWP_Post.php');	
+		add_action('edit_post', array('RCCWP_Post', 'SaveCustomFields'));
+		add_action('save_post', array('RCCWP_Post', 'SaveCustomFields'));
+		add_action('publish_post', array('RCCWP_Post', 'SaveCustomFields'));
+ 		add_action('delete_post', array('RCCWP_Post','DeletePostMetaData')) ;
+		
+
+		
+		add_filter('wp_redirect', array('RCCWP_Processor', 'Redirect'));
+
+		add_action('shutdown', array('RCCWP_Processor', 'FlushAllOutputBuffer'));
+
+		add_action('admin_notices', array('RCCWP_Application', 'CheckInstallation'));  
+		add_action('admin_notices', array('RCCWP_WritePostPage', 'FormError'));
+		
+	}
+}
+
+add_action('admin_print_scripts', array('RCCWP_Menu', 'AddThickbox'));
+add_action('admin_menu', array('RCCWP_Menu', 'AttachFlutterMenus'));
+
+
+require_once ('RCCWP_EditnPlace.php');
+//add_filter('the_title', array('RCCWP_EditnPlace', 'EditnTitle'),10, 2);
+//add_filter('the_content', array('RCCWP_EditnPlace', 'EditnContent'),10, 2);
+add_action('wp_head', array('RCCWP_EditnPlace', 'EditnHeader'));
+
+require_once ('RCCWP_Query.php');
+add_action('pre_get_posts', array('RCCWP_Query', 'FilterPrepare'));
+add_filter('posts_where', array('RCCWP_Query', 'FilterCustomPostsWhere'));
+add_filter('posts_orderby', array('RCCWP_Query', 'FilterCustomPostsOrderby'));
+add_filter('posts_fields', array('RCCWP_Query', 'FilterCustomPostsFields'));
+add_filter('posts_join_paged', array('RCCWP_Query', 'FilterCustomPostsJoin'));
+
+
+add_action('edit_page_form','cwp_add_pages_identifiers');
+add_action('edit_form_advanced','cwp_add_type_identifier');
+
+
+
+
+
+add_action('admin_head', 'canvas_head');
+add_action('switch_theme', 'canvas_check_theme');
+
+add_action('admin_head', 'add_ink_js');
+add_action('wp_head', 'output_ink');
+add_action('wp_head', 'ink_highlight');
+
+add_action('template_redirect', 'canvas_interrupt');
+
+
+// HTML Purifier.
+require_once('RCCWP_HTML_Purifier.php');
+//remove_action ('init', 'kses_init');
+		//remove_action ('set_current_user', 'kses_init');
+add_filter('pre_comment_content', array('RCCWP_HTML_Purifier', 'ApplyHTMLPurifier'));
+add_filter('title_save_pre', array('RCCWP_HTML_Purifier', 'ApplyHTMLPurifier'));
+add_filter('content_save_pre', array('RCCWP_HTML_Purifier', 'ApplyHTMLPurifier'));
+add_filter('excerpt_save_pre', array('RCCWP_HTML_Purifier', 'ApplyHTMLPurifier'));
+add_filter('content_filtered_save_pre', array('RCCWP_HTML_Purifier', 'ApplyHTMLPurifier'));
+
+
+
+
+function cwp_add_type_identifier(){
+
+	global $wpdb;
+	global $post;
+	
+	if( isset($_GET['custom-write-panel-id']) && !empty($_GET['custom-write-panel-id']))
+	{
+		$getPostID = $wpdb->get_results("SELECT id, type FROM ". RC_CWP_TABLE_PANELS ." WHERE id='".$_GET['custom-write-panel-id']."'");
+		echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"". $getPostID[0]->type ."\" />";
+
+	}
+	else{
+		if($post->post_type == 'page') { 
+			echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"page\" />";
+ 		} else {
+			echo "<input type=\"hidden\" id=\"post_type\" name=\"post_type\" value=\"post\" />";
+ 		}
+
+ 	}
+}
+
+function cwp_add_pages_identifiers(){
+	global $post;
+	global $wpdb;
+
+	$key = wp_create_nonce('rc-custom-write-panel');
+	$id = "";
+	$result = $wpdb->get_results( " SELECT meta_value
+					FROM $wpdb->postmeta
+					WHERE post_id = '$post->ID' and meta_key = '_rc_cwp_write_panel_id'", ARRAY_A );
+	
+	if (count($result) > 0)
+		$id = $result[0]['meta_value'];
+	echo 
+<<<EOF
+		<input type="hidden" name="rc-custom-write-panel-verify-key" id="rc-custom-write-panel-verify-key" value="$key" />
+		
+EOF;
+}
+
+if ( !function_exists('sys_get_temp_dir')) {
+  function sys_get_temp_dir() {
+    if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
+    if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
+    if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
+    $tempfile=tempnam(uniqid(rand(),TRUE),'');
+    if (file_exists($tempfile)) {
+    unlink($tempfile);
+    return realpath(dirname($tempfile));
+    }
+  }
+}
+
+if ( !function_exists('chmod_R')) {
+	function chmod_R($path, $filemode) {
+	    if (!is_dir($path))
+	        return chmod($path, $filemode);
+	
+	    $dh = opendir($path);
+	    while ($file = readdir($dh)) {
+	        if($file != '.' && $file != '..') {
+	            $fullpath = $path.'/'.$file;
+	            if(is_link($fullpath))
+	                return FALSE;
+	            elseif(!is_dir($fullpath))
+	                if (!chmod($fullpath, $filemode))
+	                    return FALSE;
+	            elseif(!chmod_R($fullpath, $filemode))
+	                return FALSE;
+	        }
+	    }
+	
+	    closedir($dh);
+	
+	    if(chmod($path, $filemode))
+	        return TRUE;
+	    else
+	        return FALSE;
+	}
+}
+
+function advancedRmdir($path) { //mappÃ¡t tÃ¶rÃ¶l akkor is, ha nem ÃŒres
+    $origipath = $path;
+    $handler = opendir($path);
+    while (true) {
+        $item = readdir($handler);
+        if ($item == "." or $item == "..") {
+            continue;
+        } elseif (gettype($item) == "boolean") {
+            closedir($handler);
+            if (!@rmdir($path)) {
+                return false;
+            }
+            if ($path == $origipath) {
+                break;
+            }
+            $path = substr($path, 0, strrpos($path, "/"));
+            $handler = opendir($path);
+        } elseif (is_dir($path."/".$item)) {
+            closedir($handler);
+            $path = $path."/".$item;
+            $handler = opendir($path);
+        } else {
+            unlink($path."/".$item);
+        }
+    }
+    return true;
+}
+
+if ( !function_exists('dircopy')) {
+
+	 /* Copies a dir to another. Optionally caching the dir/file structure, used to synchronize similar destination dir (web farm).
+     *
+     * @param $src_dir str Source directory to copy.
+     * @param $dst_dir str Destination directory to copy to.
+     * @param $verbose bool Show or hide file copied messages
+     * @param $use_cached_dir_trees bool Set to true to cache src/dst dir/file structure. Used to sync to web farms
+     *                     (avoids loading the same dir tree in web farms; making sync much faster).
+     * @return Number of files copied/updated.
+     * @example
+     *     To copy a dir:
+     *         dircopy("c:\max\pics", "d:\backups\max\pics");
+     *
+     *     To sync to web farms (webfarm 2 to 4 must have same dir/file structure (run once with cache off to make sure if necessary)):
+     *        dircopy("//webfarm1/wwwroot", "//webfarm2/wwwroot", false, true);
+     *        dircopy("//webfarm1/wwwroot", "//webfarm3/wwwroot", false, true);
+     *        dircopy("//webfarm1/wwwroot", "//webfarm4/wwwroot", false, true);
+     */
+    function dircopy($src_dir, $dst_dir, $verbose = false, $use_cached_dir_trees = false)
+    {   
+        static $cached_src_dir;
+        static $src_tree;
+        static $dst_tree;
+        $num = 0;
+
+        if (($slash = substr($src_dir, -1)) == "\\" || $slash == "/") $src_dir = substr($src_dir, 0, strlen($src_dir) - 1);
+        if (($slash = substr($dst_dir, -1)) == "\\" || $slash == "/") $dst_dir = substr($dst_dir, 0, strlen($dst_dir) - 1); 
+
+        if (!$use_cached_dir_trees || !isset($src_tree) || $cached_src_dir != $src_dir)
+        {
+            $src_tree = get_dir_tree($src_dir);
+            $cached_src_dir = $src_dir;
+            $src_changed = true; 
+        }
+        if (!$use_cached_dir_trees || !isset($dst_tree) || $src_changed)
+            $dst_tree = get_dir_tree($dst_dir);
+        if (!is_dir($dst_dir)) mkdir($dst_dir, 0777, true); 
+
+          foreach ($src_tree as $file => $src_mtime)
+        {
+            if (!isset($dst_tree[$file]) && $src_mtime === false) // dir
+                mkdir("$dst_dir/$file");
+            elseif (!isset($dst_tree[$file]) && $src_mtime || isset($dst_tree[$file]) && $src_mtime > $dst_tree[$file])  // file
+            {
+                if (copy("$src_dir/$file", "$dst_dir/$file"))
+                {
+                    if($verbose) echo "Copied '$src_dir/$file' to '$dst_dir/$file'<br>\r\n";
+                    touch("$dst_dir/$file", $src_mtime);
+                    $num++;
+                } else
+                    echo "<font color='red'>File '$src_dir/$file' could not be copied!</font><br>\r\n";
+            }       
+        }
+
+        return $num;
+    }
+
+    /* Creates a directory / file tree of a given root directory
+     *
+     * @param $dir str Directory or file without ending slash
+     * @param $root bool Must be set to true on initial call to create new tree.
+     * @return Directory & file in an associative array with file modified time as value.
+     */
+    function get_dir_tree($dir, $root = true)
+    {
+        static $tree;
+        static $base_dir_length;
+
+        if ($root)
+        {
+            $tree = array(); 
+            $base_dir_length = strlen($dir) + 1; 
+        }
+
+        if (is_file($dir))
+        {
+            //if (substr($dir, -8) != "/CVS/Tag" && substr($dir, -9) != "/CVS/Root"  && substr($dir, -12) != "/CVS/Entries")
+            $tree[substr($dir, $base_dir_length)] = filemtime($dir);
+        } elseif (is_dir($dir) && $di = dir($dir)) // add after is_dir condition to ignore CVS folders: && substr($dir, -4) != "/CVS"
+        {
+            if (!$root) $tree[substr($dir, $base_dir_length)] = false; 
+            while (($file = $di->read()) !== false)
+                if ($file != "." && $file != "..")
+                    get_dir_tree("$dir/$file", false); 
+            $di->close();
+        }
+
+        if ($root)
+            return $tree;    
+    }
+ }
+
+?>
Index: /afridex/plugins/Flutter/readme.txt
===================================================================
--- /afridex/plugins/Flutter/readme.txt (revision 21)
+++ /afridex/plugins/Flutter/readme.txt (revision 21)
@@ -0,0 +1,71 @@
+=== Flutter ===
+Contributors: alphaoide, coffee2code, freshout
+Tags: custom write panel, custom, write panel
+Requires at least: 2.3
+Tested up to: 2.5
+Stable tag: .11
+
+Flutter is for creating customized write panels with file and photo
+uploads.  Includes automatic photo resizing via phpThumb and freshout.
+
+
+== Description ==
+
+WordPress gets pretty close to becoming a practical content management solution with this plugin.
+Create multiple custom write panels, add custom fields (images, drop downs, file upload, images, checkboxes, textareas, etc..), photos can be automatically resized, cropped, watermarked, drop shadowed, and modified in many other ways (this plugin comes with php_thumb).
+In addition to being able to upload files from your computers, you can also retrieve Images from 3rd party sites on the fly.
+The core of this plugin was written by  Scott Reiley (http://www.coffee2code.com/wp-plugins/), Joshua Siguar (http://rhymedcode.net/projects/custom-write-panel), and Eric Pujol (http://phpthumb.sourceforge.net/).  All we did is add a bit of freshness.
+
+
+== Installation ==
+
+Follow the following steps to install this plugin.
+
+1. Download plugin to the `/wp-content/plugins/` folder.
+2. Activate the plugin through the 'Plugins' menu in WordPress.
+
+
+== Frequently Asked Questions ==
+
+= How do I create a new custom write panel? =
+
+1. Go to 'Flutter' > 'Modules' click on 'Create Custom Write module' and follow the on-screen instruction.
+2. Go to 'Flutter' > 'Write Panels' click on 'Create Custom Write panel' and add the module to the panel.
+3. Go to 'Write' menu and you should see your new custom write panel beside 'page' sub-menu.
+
+
+= How do I add a custom input field? =
+
+After creating a custom module, click on 'View' to view the module info, click 'Create Custom Field'
+and follow on-screen instruction.
+
+
+= How do I get custom field to show up in my post? =
+
+There three different functions (get, get_image, get_audio) that you can use to retrieve the values for the fields youâve created.
+
+You can always use the âgetâ function to grab the ânakedâ variable but weâve also built the âget_imageâ and âget_audioâ functions that will spit out the variable with html tags.
+
+Grabbing the Values
+get(âvariableâ) = naked variable
+get_image(âvariableâ) = variable with image tag
+get_audio(âvariableâ) = variable with single mp3 swf player
+
+An Example
+Letâs say you want to have an events page for your site so you created a new write panel and added a custom Image field called âFlyerâ, a Date field called âDateâ and an audio field called "MP3". So now how do we display it on the post page?
+
+To display the Flyer image: <?php echo get_image('Photos'); ?>" />
+To display the Date field: <?php echo get('Date'); ?>
+To display the MP3 Player (with the song): <?php echo get_audio('MP3'); ?> 
+
+
+= Is this Fresh Page? =
+
+Yes, this is the same plugin as Fresh Page.
+
+
+== Credits ==
+
+* ["Joshua Sigar"](http://rhymedcode.net) The creator of Custom Write Panel
+* ["James Heinrich"](info@silisoftware.com) The creator of phpThumb
+* ["Scott Reilly"](http://www.coffee2code.com) The creator of get-custom plugin
Index: /afridex/plugins/dailytop10/README.TXT
===================================================================
--- /afridex/plugins/dailytop10/README.TXT (revision 21)
+++ /afridex/plugins/dailytop10/README.TXT (revision 21)
@@ -0,0 +1,60 @@
+Plugin Name: Daily Top 10 Posts
+
+INSTALLATION
+------------
+1. Download the plugin from http://www.alleba.com/blog/wp-downloads/dailytop10.zip
+2. Extract and upload dailytop10.php to 'yourserver.com/wp-content/plugins/'
+3. Login to your Wordpress admin panel and browse to the Plugins section.
+4. Activate The Daily Top 10 Posts plugin.
+
+INSTRUCTIONS
+------------
+1. Go to Admin Panel > Design > Theme Editor and click on Single Post (single.php).
+
+	 #Find this line:
+		
+		<?php the_time('F jS, Y') ?> //date
+		
+	 #Right after it, insert this line:
+		<br /><?php if (function_exists('todays_overall_count')) { todays_overall_count($post->ID, 'Visited', 'times', 'so far today', '0', 'show'); } ?>
+		
+   #The line will display something like "Visited 300 times, 25 so far today" while viewing an individual post.
+   #You may edit the wording to suit your preference.
+   #If you wish to leave a word/phrase empty, use two quotes '' instead of just leaving it completely blank.
+   #Insert only one instance of this line to avoid double tracking.
+   #Change '0' to '1' if you wish to track unique sessions.
+   #Change 'show' to 'noshow' if you donot wish to display the post count information.
+
+2. To display the number of views per post on the main index page, click on Main Index Template (index.php) in the Theme Editor panel.
+
+   #Find this line:
+   
+    <?php the_time('F jS, Y') ?>
+   
+   #Right after it, insert this line:
+		
+		<br /><?php if (function_exists('todays_overall_main')) { todays_overall_main($post->ID, 'Viewed', 'times', 'so far today'); } ?>
+	 
+	 #The line will display something like "Visited 300 times, 25 so far today" under each post heading 
+	 #while browsing your main page.
+   #You may edit the wording to suit your preference.
+   #If you wish to leave a word/phrase empty, use two quotes '' instead of just leaving it completely blank.
+   
+3. To add the sidebar widget, you may add it directly in the widgets panel of your theme (Design > Widgets).  If your theme is not widget-ready, click on sidebar.php in the Theme Editor panel and do the following:
+
+	 #Add the following code:
+	 
+	  <h3>Top Posts for Today</h3>
+    <?php if (function_exists('todays_count_widget')) { todays_count_widget('views', 'ul'); }?>
+    
+   #You may edit the word "views" to your liking (e.g. visits, pageviews or leave it empty '').
+   #The list format defaults to an unordered list (ul).  If you would like an ordered list, change it to 'ol'.
+
+4. To add the sidebar widget to show your most popular posts overall, you may add it directly in the widgets panel of your theme (Design > Widgets).  If your theme is not widget-ready, click on sidebar.php in the Theme Editor panel and do the following:
+	 #Add the following code:
+	 
+	  <h3>Overall Top Posts</h3>
+    <?php if (function_exists('todays_overall_count_widget')) { todays_overall_count_widget('views', 'ul'); } ?>
+    
+   #You may edit the word "views" to your liking (e.g. visits, pageviews or leave it empty '').
+   #The list format defaults to an unordered list (ul).  If you would like an ordered list, change it to 'ol'.
Index: /afridex/plugins/dailytop10/dailytop10.php
===================================================================
--- /afridex/plugins/dailytop10/dailytop10.php (revision 21)
+++ /afridex/plugins/dailytop10/dailytop10.php (revision 21)
@@ -0,0 +1,433 @@
+<?php
+/*
+Plugin Name: Daily Top 10 Posts
+Plugin URI: http://www.alleba.com/blog/2007/03/27/wordpress-plugin-daily-top-10-posts/
+Description: Tracks the number of pageviews per blog post for the current day and cumulatively with options to display sidebar widgets for both.  Features dashboard widgets too.
+Author: Andrew dela Serna
+Author URI: http://www.alleba.com/blog/
+Version: 0.4
+
+  Copyright 2007 Andrew dela Serna (email andrew@alleba.com)
+
+    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 2 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, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+INSTALLATION
+------------
+1. Download the plugin from http://www.alleba.com/blog/wp-downloads/dailytop10.zip
+2. Extract and upload dailytop10.php to 'yourserver.com/wp-content/plugins/'
+3. Login to your Wordpress admin panel and browse to the Plugins section.
+4. Activate The Daily Top 10 Posts plugin.
+
+INSTRUCTIONS
+------------
+1. Go to Admin Panel > Design > Theme Editor and click on Single Post (single.php).
+
+	 #Find this line:
+		
+		<?php the_time('F jS, Y') ?> //date
+		
+	 #Right after it, insert this line:
+		<br /><?php if (function_exists('todays_overall_count')) { todays_overall_count($post->ID, 'Visited', 'times', 'so far today', '0', 'show'); } ?>
+		
+   #The line will display something like "Visited 300 times, 25 so far today" while viewing an individual post.
+   #You may edit the wording to suit your preference.
+   #If you wish to leave a word/phrase empty, use two quotes '' instead of just leaving it completely blank.
+   #Insert only one instance of this line to avoid double tracking.
+   #Change '0' to '1' if you wish to track unique sessions.
+   #Change 'show' to 'noshow' if you donot wish to display the post count information.
+
+2. To display the number of views per post on the main index page, click on Main Index Template (index.php) in the Theme Editor panel.
+
+   #Find this line:
+   
+    <?php the_time('F jS, Y') ?>
+   
+   #Right after it, insert this line:
+		
+		<br /><?php if (function_exists('todays_overall_main')) { todays_overall_main($post->ID, 'Viewed', 'times', 'so far today'); } ?>
+	 
+	 #The line will display something like "Visited 300 times, 25 so far today" under each post heading 
+	 #while browsing your main page.
+   #You may edit the wording to suit your preference.
+   #If you wish to leave a word/phrase empty, use two quotes '' instead of just leaving it completely blank.
+   
+3. To add the sidebar widget, you may add it directly in the widgets panel of your theme (Design > Widgets).  If your theme is not widget-ready, click on sidebar.php in the Theme Editor panel and do the following:
+
+	 #Add the following code:
+	 
+	  <h3>Top Posts for Today</h3>
+    <?php if (function_exists('todays_count_widget')) { todays_count_widget('views', 'ul'); }?>
+    
+   #You may edit the word "views" to your liking (e.g. visits, pageviews or leave it empty '').
+   #The list format defaults to an unordered list (ul).  If you would like an ordered list, change it to 'ol'.
+
+4. To add the sidebar widget to show your most popular posts overall, you may add it directly in the widgets panel of your theme (Design > Widgets).  If your theme is not widget-ready, click on sidebar.php in the Theme Editor panel and do the following:
+	 #Add the following code:
+	 
+	  <h3>Overall Top Posts</h3>
+    <?php if (function_exists('todays_overall_count_widget')) { todays_overall_count_widget('views', 'ul'); } ?>
+    
+   #You may edit the word "views" to your liking (e.g. visits, pageviews or leave it empty '').
+   #The list format defaults to an unordered list (ul).  If you would like an ordered list, change it to 'ol'.
+
+CREDITS
+-------
+1. Thanks to Abe (www.yugatech.com/blog) for the whole idea of this plugin.
+*/
+
+#SET TIMEZONE OFFSET (UTC/GMT).  To know your timezone, visit http://www.timeanddate.com/worldclock/ and click on the appropriate city.  If you live in the Philippines, the offset is +8.
+$OFFSET = 0;
+#If you wish not to offset the date in which post views are recorded, leave it as is (0).
+
+add_action('init', 'jal_install');
+
+$table_name = $wpdb->prefix . "dailytopten";
+$table_name_all = $wpdb->prefix . "dailytoptenall";
+$table_posts = $wpdb->prefix . "posts";
+
+function jal_install () {
+   global $wpdb;
+   global $table_name;
+   global $table_name_all;
+   
+   if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
+   	$sql = "CREATE TABLE " . $table_name . " (
+	  id mediumint(9) NOT NULL AUTO_INCREMENT,
+	  time date DEFAULT '0000-00-00' NOT NULL,
+	  postnum int NOT NULL,
+	  postcount int DEFAULT '0' NOT NULL,
+	  UNIQUE KEY id (id)
+	  )
+	;";
+	
+	require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
+      dbDelta($sql);
+  }
+    if($wpdb->get_var("SHOW TABLES LIKE '$table_name_all'") != $table_name_all) {
+   	$sqall = "CREATE TABLE " . $table_name_all . " (
+	  id mediumint(9) NOT NULL AUTO_INCREMENT,
+	  postnum int NOT NULL,
+	  postcount int DEFAULT '0' NOT NULL,
+	  UNIQUE KEY id (id)
+	  )
+	;";
+	require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
+      dbDelta($sqall);
+   }
+  }
+function todays_date()
+{
+    global $OFFSET;
+    $format = "Y-m-d";
+    if ($offset) {
+    $nowtime = gmdate($format, time() + 3600*$OFFSET);
+    } else {
+    $nowtime = date($format, time());
+    }
+    return $nowtime;
+}
+function has_viewed($postno, $all) {
+	if ($all == 1) {
+	$temp = explode("&", $_SESSION["a"]);
+        if(!in_array($postno, $temp)){
+        $_SESSION["a"] .= $postno."&";
+        return 0;
+        } else {
+        return 1;	
+   }
+  } 
+   else if ($all == 2) {
+   $temp = explode("&", $_SESSION["n"]);
+        if(!in_array($postno, $temp)){
+        $_SESSION["n"] .= $postno."&";
+        return 0;
+        } else {
+        return 1;
+    }
+   }
+  }
+  
+function todays_count($postnum, $unique) {
+	global $wpdb;
+	global $table_name;
+	if ($unique) {
+	$viewed = has_viewed($postnum,2);	
+	}
+	$nowisnow = todays_date();
+	$checkpost = $wpdb->get_row("SELECT id, postnum, time, postcount FROM $table_name WHERE postnum = '$postnum'");
+	$postid = $checkpost->id;
+	$postnumb = $checkpost->postnum;
+	$posttime = $checkpost->time;
+	$postcount = $checkpost->postcount;
+	if (!$postid) {
+	$wpdb->query("INSERT INTO $table_name (time, postnum, postcount) VALUES ('$nowisnow', $postnum, 1)");
+	}
+	else if ($posttime == $nowisnow) {
+	if (!$viewed) {
+	$wpdb->query("UPDATE $table_name SET postcount = postcount+1, time = '$nowisnow'	WHERE postnum = '$postnum'");
+   }
+	}
+	else {
+	if (!$viewed) {
+	$wpdb->query("UPDATE $table_name SET postcount = 1, time = '$nowisnow'	WHERE postnum = '$postnum'");
+	 }
+	}
+	$post_c = $wpdb->get_var("SELECT postcount FROM $table_name WHERE postnum = '$postnum'");
+	return $post_c;
+	}
+
+function todays_count_widget($views,$list) {
+	global $wpdb;
+	global $table_name;
+	global $table_posts;
+	$home_url = get_settings('home');
+	$perma = get_settings('permalink_structure');
+	$nowisnow = todays_date();
+	$todays_widget_temp = "";
+	trim($views)!='' ? ($views = " ".$views) : ($views = "");
+	((strtolower($list) == 'ul') || (strtolower($list) == 'ol')) ? $list = strtolower($list) : $list = 'ul';
+	if ($list == 'ul') {$lbeg = '<ul>'; $lend = '</ul>';} else {$lbeg = '<ol>'; $lend = '</ol>';}
+	$todays_widget = $lbeg."\n";;
+	$widgets = $wpdb->get_results("SELECT * from $table_name inner join $table_posts on $table_posts.ID=$table_name.postnum WHERE time = '$nowisnow' and postcount > 0 and post_status = 'publish' ORDER BY postcount DESC LIMIT 10");
+	$widgets_num = $wpdb->get_var("SELECT COUNT(*) FROM $table_name inner join $table_posts on $table_posts.ID=$table_name.postnum WHERE time = '$nowisnow' and postcount > 0 and post_status = 'publish'");
+	
+	if ($widgets_num) {
+	foreach ($widgets as $widget) {
+	$postnum = $widget->postnum;
+	$postcount = $widget->postcount;
+  $id_post = $widget->ID;
+  $title_post = $widget->post_title;
+  if ($perma) {
+  $home_url_perma = get_permalink($id_post);
+  $todays_widget_temp .= "<li><a href=\"$home_url_perma\">$title_post</a> ($postcount views)</li>\n";
+  } else {
+  $todays_widget_temp .= "<li><a href=\"$home_url/?p=$id_post\" title=\"$text\">$title_post</a> ($postcount$views)</li>\n";
+	  }
+	 }
+  }
+	if (!$todays_widget_temp) {
+	$todays_widget_temp .= "<li>No posts viewed yet.</li>";
+	}
+	$todays_widget .= $todays_widget_temp;
+	$todays_widget .= $lend."\n";;
+	echo $todays_widget;
+  }
+  
+function todays_overall_main($postnum, $visited, $times, $sofar) {
+	global $wpdb;
+	global $table_name;
+	global $table_name_all;
+	$nowisnow = todays_date();
+	$show_overall_main= "";
+  $post_c = $wpdb->get_var("SELECT postcount FROM $table_name WHERE time = '$nowisnow' AND postnum = '$postnum'");
+  $post_d = $wpdb->get_var("SELECT postcount FROM $table_name_all WHERE postnum = '$postnum'");
+  if ($post_d>0) {
+	$show_overall_main = "$visited $post_d $times";
+	if ($post_c) {$show_overall_main .= ", $post_c $sofar";}
+  }
+	echo $show_overall_main;
+	}
+	
+function todays_overall_count($postnum, $visited, $times, $sofar, $unique, $show) {
+	global $wpdb;
+	global $table_name_all;
+	if ($unique) {
+	$viewed = has_viewed($postnum,1);
+	}
+	$checkpost = $wpdb->get_row("SELECT id, postnum, postcount FROM $table_name_all WHERE postnum = '$postnum'");
+	$postid = $checkpost->id;
+	$postnumb = $checkpost->postnum;
+	$postcount = $checkpost->postcount;
+	if (!$postid) {
+	$wpdb->query("INSERT INTO $table_name_all (postnum, postcount) VALUES ($postnum, 1)");
+	$show_overall = "Visited 1 times";
+	$show_today = todays_count($postnum, $unique);
+	}
+	else {
+	if (!$viewed) {
+	$wpdb->query("UPDATE $table_name_all SET postcount = postcount+1 WHERE postnum = '$postnum'");
+	$postcount_ap = $postcount+1;
+  } else {
+  $postcount_ap = $postcount;
+  }
+	$show_today = todays_count($postnum, $unique);
+	if ($postcount_ap>0) {
+	$show_overall = "$visited $postcount_ap $times";
+	if ($show_today) {$show_overall .= ", $show_today $sofar";}
+	 }
+  }
+  if ($show != 'noshow') {
+	echo $show_overall;
+   }
+	}
+	
+function todays_overall_count_widget($views,$list) {
+	global $wpdb;
+	global $table_name_all;
+	global $table_posts;
+	$home_url = get_settings('home');
+	$perma = get_settings('permalink_structure');
+	$nowisnow = todays_date();
+	$todays_overall_widget_temp = "";
+	trim($views)!='' ? ($views = " ".$views) : ($views = "");
+	((strtolower($list) == 'ul') || (strtolower($list) == 'ol')) ? $list = strtolower($list) : $list = 'ul';
+	if ($list == 'ul') {$lbeg = '<ul>'; $lend = '</ul>';} else {$lbeg = '<ol>'; $lend = '</ol>';}
+	$todays_overall_widget = $lbeg."\n";
+	$widgets = $wpdb->get_results("SELECT * from $table_name_all inner join $table_posts on $table_posts.ID=$table_name_all.postnum WHERE postcount > 0 and post_status = 'publish' ORDER BY postcount DESC LIMIT 10");
+	$widgets_num = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_all inner join $table_posts on $table_posts.ID=$table_name_all.postnum WHERE postcount > 0 and post_status = 'publish'");
+	
+	if ($widgets_num) {
+	foreach ($widgets as $widget) {
+	$postnum = $widget->postnum;
+	$postcount = $widget->postcount;
+  $id_post = $widget->ID;
+  $title_post = $widget->post_title;
+  if ($perma) {
+  $home_url_perma = get_permalink($id_post);
+  $todays_overall_widget_temp .= "<li><a href=\"$home_url_perma\">$title_post</a> ($postcount views)</li>\n";
+  } else {
+  $todays_overall_widget_temp .= "<li><a href=\"$home_url/?p=$id_post\" title=\"$text\">$title_post</a> ($postcount$views)</li>\n";
+	  }
+	 }
+  }
+	if (!$todays_overall_widget_temp) {
+	$todays_overall_widget_temp .= "<li>No posts viewed yet.</li>\n";
+	}
+	$todays_overall_widget .= $todays_overall_widget_temp;
+	$todays_overall_widget .= $lend."\n";
+	echo $todays_overall_widget;
+  }
+
+function admin_todays_count_widget() {
+	global $wpdb;
+	global $table_name;
+	global $table_posts;
+	$home_url = get_settings('home');
+	$perma = get_settings('permalink_structure');
+	$nowisnow = todays_date();
+	$todays_widget_temp = "";
+	$widgets = $wpdb->get_results("SELECT * FROM $table_name inner join $table_posts on $table_posts.ID=$table_name.postnum WHERE time = '$nowisnow' and postcount > 0 and post_status = 'publish' ORDER BY postcount DESC LIMIT 10");
+	$widgets_num = $wpdb->get_var("SELECT COUNT(*) FROM $table_name inner join $table_posts on $table_posts.ID=$table_name.postnum WHERE time = '$nowisnow' and postcount > 0 and post_status = 'publish'");
+	if ($widgets_num) {
+	foreach ($widgets as $widget) {
+	$postnum = $widget->postnum;
+	$postcount = $widget->postcount;
+  $id_post = $widget->ID;
+  $title_post = $widget->post_title;
+  if ($perma) {
+  $home_url_perma = get_permalink($id_post);
+  $todays_widget_temp .= "<h5 style=\"font-size: 12px; display: inline;\"><a href=\"$home_url_perma\">$title_post</a></h5> <span style=\"font-size: 12px; color: #999999;\">| $postcount views</span><br>";
+  } else {
+  $todays_widget_temp .= "<h5 style=\"font-size: 12px; display: inline;\"><a href=\"$home_url/?p=$id_post\" title=\"$text\">$title_post</a></h5> <span style=\"font-size: 12px; color: #999999;\">| $postcount views</span><br>";
+	  }
+	 }
+  }
+	if (!$todays_widget_temp) {
+	$todays_widget_temp .= "No posts viewed yet.";
+	}
+	$todays_widget .= $todays_widget_temp;
+	echo $todays_widget;
+  }
+
+function admin_rightnow() {
+	global $wpdb;
+	global $table_name;
+	global $table_posts;
+	$home_url = get_settings('home');
+	$perma = get_settings('permalink_structure');
+	$nowisnow = todays_date();
+	//$todays_widget = "<ul>";
+	$rightnow_widget_temp = "";
+	$widgets = $wpdb->get_results("SELECT * FROM $table_name inner join $table_posts on $table_posts.ID=$table_name.postnum WHERE time = '$nowisnow' and postcount > 0 and post_status = 'publish' ORDER BY postcount DESC LIMIT 1");
+	$widgets_num = $wpdb->get_var("SELECT COUNT(*) FROM $table_name inner join $table_posts on $table_posts.ID=$table_name.postnum WHERE time = '$nowisnow' and postcount > 0 and post_status = 'publish'");
+	if ($widgets_num) {
+  $rightnow_widget_temp = "<h3>Top Post of the Day</h3><p>";
+	foreach ($widgets as $widget) {
+	$postnum = $widget->postnum;
+	$postcount = $widget->postcount;
+  $id_post = $widget->ID;
+  $title_post = $widget->post_title;
+  if ($perma) {
+  $home_url_perma = get_permalink($id_post);
+  $rightnow_widget_temp .= "The <a href=\"http://www.alleba.com/blog/2007/03/27/wordpress-plugin-daily-top-10-posts/\">most popular blog post</a> of the day is <a href=\"$home_url_perma\">$title_post</a>, viewed $postcount times so far. <a href=\"#topten\" class=\"rbutton\">more...</a></p>";
+  } else {
+  $rightnow_widget_temp .= "The <a href=\"http://www.alleba.com/blog/2007/03/27/wordpress-plugin-daily-top-10-posts/\">most popular blog post</a> of the day is <a href=\"$home_url/?p=$id_post\">$title_post</a>, viewed $postcount times so far. <a href=\"#topten\" class=\"rbutton\">more...</a></p>";
+	  }
+   }
+  }
+	if (!$rightnow_widget_temp) {
+	$rightnow_widget_temp = "<h3>Top Post of the Day</h3><p>";
+	$rightnow_widget_temp .= "No posts viewed yet today.</p>";
+	}
+	$rightnow_widget .= $rightnow_widget_temp;
+	echo $rightnow_widget;
+  }
+add_action('activity_box_end', 'admin_rightnow');
+add_action('wp_dashboard_setup', 'dailytopten_register_dashboard_widget');
+function dailytopten_register_dashboard_widget() {
+	wp_register_sidebar_widget('dashboard_dailytopten', __('<a name="topten"></a>Top Blog Posts of the Day', 'dailytopten'), 'dashboard_dailytopten',
+		array(
+		'width' => 'half',
+		'height' => 'single',
+		)
+	);
+}
+ 
+add_filter('wp_dashboard_widgets', 'dailytopten_add_dashboard_widget');
+function dailytopten_add_dashboard_widget($widgets) {
+	global $wp_registered_widgets;
+	if (!isset($wp_registered_widgets['dashboard_dailytopten'])) {
+		return $widgets;
+	}
+	array_splice($widgets, sizeof($widgets)-1, 0, 'dashboard_dailytopten');
+	return $widgets;
+}
+
+function dashboard_dailytopten($sidebar_args) {
+	global $wpdb;
+	extract($sidebar_args, EXTR_SKIP);
+	echo $before_widget;
+	echo $before_title;
+	echo $widget_name;
+	echo $after_title;
+	admin_todays_count_widget();
+	echo $after_widget;
+}
+
+function widget_dailytopten_init() {
+        if(!function_exists('register_sidebar_widget')) { return; }
+        function widget_dailytopten($args) {
+            extract($args);
+            echo $before_widget . $before_title . "Top Posts of the Day" . $after_title;
+            todays_count_widget('views', 'ul');
+            echo $after_widget;
+        }
+        register_sidebar_widget('Daily Top 10 Posts','widget_dailytopten');
+    }
+add_action('plugins_loaded', 'widget_dailytopten_init');
+
+function widget_dailytopten_overall_init() {
+        if(!function_exists('register_sidebar_widget')) { return; }
+        function widget_dailytopten_overall($args) {
+            extract($args);
+            echo $before_widget . $before_title . "Top Posts Overall" . $after_title;
+            todays_overall_count_widget('views', 'ul');
+            echo $after_widget;
+        }
+        register_sidebar_widget('Overall Top 10 Posts','widget_dailytopten_overall');
+    }
+add_action('plugins_loaded', 'widget_dailytopten_overall_init');
+
+?>
Index: /afridex/plugins/get-a-post.php
===================================================================
--- /afridex/plugins/get-a-post.php (revision 21)
+++ /afridex/plugins/get-a-post.php (revision 21)
@@ -0,0 +1,94 @@
+<?php
+/*
+Plugin Name: Get-a-Post
+Plugin URI: http://guff.szub.net/get-a-post
+Description: Display a specific post (or Page) with standard WP template tags.
+Version: R1.4
+Author: Kaf Oseo
+Author URI: http://szub.net
+
+	Copyright (c) 2004-2006, 2008 Kaf Oseo (http://szub.net)
+	Get-a-Post is released under the GNU General Public License, version 2 (GPL2)
+	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+	This is a WordPress plugin (http://wordpress.org).
+
+~Changelog:
+R1.4 (Jan-27-2008)
+'Rich' release. Adds a 'GETRANDOM' id, which retrieves (surprise) a
+random published post. Random rules!
+
+R1.3 (Apr-21-2006)
+Bug fix (sorry for the delay). Added a 'GETSTICKY' id, which gets a
+post using 'sticky' for a custom field key and '1' for the value.
+
+R1.2 (Mar-03-2006)
+Use 'GETPAGE' (all caps) as argument for latest Page, and 'GETPOST'
+or no argument for latest post. Tentative suppport for WordPress 2.1.
+
+R1.1 (May-04-2005]
+Caches post-meta (custom field) data for use with other plugins or
+template tags. Code tweaks. Explicit GPL licensing.
+
+R1 (Mar-01-2005)
+"Clementine" release. Handles Pages under WordPress 1.5+. Accepts a
+post name (Post/Page slug) or numeric ID as argument.
+
+0.3 (Jan-29-2005)
+Intializes post object data to avoid needing to run get_a_post() in
+"The Loop" of a template.
+
+0.2 (Jan-28-2005)
+Changes for support under WordPress 1.5.
+*/
+
+function get_a_post($id='GETPOST') {
+	global $post, $tableposts, $tablepostmeta, $wp_version, $wpdb;
+
+	if($wp_version < 1.5)
+		$table = $tableposts;
+	else
+		$table = $wpdb->posts;
+
+	$now = current_time('mysql');
+	$name_or_id = '';
+	$orderby = 'post_date';
+
+	if( !$id || 'GETPOST' == $id || 'GETRANDOM' == $id ) {
+		if( $wp_version < 2.1 )
+			$query_suffix = "post_status = 'publish'";
+		else
+			$query_suffix = "post_type = 'post' AND post_status = 'publish'";
+	} elseif('GETPAGE' == $id) {
+		if($wp_version < 2.1)
+			$query_suffix = "post_status = 'static'";
+		else
+			$query_suffix = "post_type = 'page' AND post_status = 'publish'";
+	} elseif('GETSTICKY' == $id) {
+		if($wp_version < 1.5)
+			$table .= ', ' . $tablepostmeta;
+		else
+			$table .= ', ' . $wpdb->postmeta;
+		$query_suffix = "ID = post_id AND meta_key = 'sticky' AND meta_value = 1";
+	} else {
+		$query_suffix = "(post_status = 'publish' OR post_status = 'static')";
+
+		if(is_numeric($id)) {
+			$name_or_id = "ID = '$id' AND";
+		} else {
+			$name_or_id = "post_name = '$id' AND";
+		}
+	}
+
+	if('GETRANDOM' == $id)
+		$orderby = 'RAND()';
+
+	$post = $wpdb->get_row("SELECT * FROM $table WHERE $name_or_id post_date <= '$now' AND $query_suffix ORDER BY $orderby DESC LIMIT 1");
+	get_post_custom($post->ID);
+
+	if($wp_version < 1.5)
+		start_wp();
+	else
+		setup_postdata($post);
+}
+?>
Index: /afridex/plugins/search-everything/readme-JAPANESE.txt
===================================================================
--- /afridex/plugins/search-everything/readme-JAPANESE.txt (revision 21)
+++ /afridex/plugins/search-everything/readme-JAPANESE.txt (revision 21)
@@ -0,0 +1,54 @@
+Search Everything (SE) v4.7 - æ€çŽ¢å¯Ÿè±¡ã«ããã³ã³ãã³ããè¿œå ãããã©ã°ã€ã³
+GNU General Public License
+Developed by Daniel Cameron http://dancameron.org/
+Translation by Naoko McCracken http://detlog.org/
+å¯Ÿå¿ãã¹ãæžã¿ã®WordPressããŒãžã§ã³: 2.1ã2.6ïŒæ¥æ¬èªã¿ã°æªå¯Ÿå¿ïŒ
+
+ãã®ãã©ã°ã€ã³ã®ç¿»èš³çé
+åžããŒãžïŒ http://wppluginsj.sourceforge.jp/i18n-ja_jp/search-everything/
+ãã®ãã©ã°ã€ã³ã«é¢ããè¿œå æ
+å ±ïŒè±èªïŒïŒ http://wordpress.org/extend/plugins/search-everything/
+
+âã¯ããã«
+------------------------
+ããã©ã«ãã®ç¶æ
+ã®WordPressã§ã¯ãå
+¬éãããšæçš¿ã®æ¬æã®ã¿ãæ€çŽ¢ãããŸãããã®ãã©ã°ã€ã³ãäœ¿ã£ãŠãæ€çŽ¢å¯Ÿè±¡ã«ããã³ã³ãã³ããè¿œå ããããšãã§ããŸãã
+
+ - ããŒãžäœææ©èœã§è¿œå ããå
+šããŒãž
+ - ãã¹ã¯ãŒãä¿è­·ãããŠããªãå
+šããŒãž
+ - ã¿ã°ïŒãã ãçŸæç¹ã§ã¯æ¥æ¬èªã¯ããŸãè¡ããªãããã§ãïŒ
+ - ãã¹ãŠã®ã³ã¡ã³ã
+ - æ¿èªæžã¿ã®ã³ã¡ã³ãã®ã¿
+ - æªå
+¬éã®èçš¿èšäº
+ - æçš¿ã®èŠçŽ
+ - ã¢ã¿ããã¡ã³ãïŒèšäºå
+ã§äœ¿çšäž­ã®ã¢ããã­ãŒããã¡ã€ã«ïŒ
+ - ã«ã¹ã¿ã ãã£ãŒã«ãã§è¿œå ããèšäºã®ã¡ã¿ããŒã¿
+ - ã«ããŽãªãŒ
+ - IDæå®ããä»¥å€ã®å
+šããŒãž
+ - IDæå®ããä»¥å€ã®å
+šã«ããŽãªãŒ
+
+âã€ã³ã¹ããŒã«
+------------------------
+1. ZIPãã¡ã€ã«ãè§£åããŸãã
+
+2. search-everythingãã©ã«ãããã®ãŸãŸãã©ã°ã€ã³ãã£ã¬ã¯ããªïŒ/wp-content/plugins/ïŒã«ç§»åããŸãã
+
+- wp-content
+	- plugins
+	  - search-everything
+	  	  | search_everything.php
+	  	  | SE-Admin.php
+		  - lang
+		     | SE4-ja.mo
+
+2. ãã©ã°ã€ã³ç®¡çç»é¢ã«ã­ã°ã€ã³ãããSearch Everythingããæå¹åããŸãã
+
+3. ãèš­å®ãã¡ãã¥ãŒã®ãµãã¡ãã¥ãŒãšããŠçŸããããSearch Everythingãç®¡çããã«ãžç§»åããèš­å®ãå€æŽããŸããæ€çŽ¢ãããã³ã³ãã³ãã«ãã§ãã¯ãå
+¥ãããèš­å®ãæŽæ°ããã¿ã³ãã¯ãªãã¯ããŸãã
Index: /afridex/plugins/search-everything/search_everything.php
===================================================================
--- /afridex/plugins/search-everything/search_everything.php (revision 21)
+++ /afridex/plugins/search-everything/search_everything.php (revision 21)
@@ -0,0 +1,380 @@
+<?php
+/*
+Plugin Name: Search Everything
+Plugin URI: http://dancameron.org/wordpress/
+Description: Adds search functionality with little setup. Including options to search pages, excerpts, attachments, drafts, comments, tags and custom fields (metadata). Also offers the ability to exclude specific pages and posts. Does not search password-protected content.
+Version: 4.7.6.2
+Author: Dan Cameron
+Author URI: http://dancameron.org/
+*/
+
+/*
+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, version 2.
+
+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.
+*/
+
+if ( !defined('WP_CONTENT_DIR') )
+	define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
+define('SE_ABSPATH', WP_CONTENT_DIR.'/plugins/' . dirname(plugin_basename(__FILE__)) . '/');
+
+$SE = new SearchEverything();
+//add filters based upon option settings
+
+Class SearchEverything {
+
+	var $login = false;
+	var $options;
+	var $wp_ver23;
+	var $wp_ver25;
+
+	function SearchEverything(){
+		global $wp_version;
+		$this->wp_ver23 = ($wp_version >= '2.3');
+		$this->wp_ver25 = ($wp_version >= '2.5');
+		$this->options = get_option('SE4_options');
+
+		if (is_admin()) {
+			include ( SE_ABSPATH  . 'SE-Admin.php' );
+			$SEAdmin = new SearchEverythingAdmin();
+		}
+
+		//add filters based upon option settings
+		if ("true" == $this->options['SE4_use_tag_search']) {
+			add_filter('posts_where', array(&$this, 'SE4_search_tags'));
+			add_filter('posts_join', array(&$this, 'SE4_terms_join'));
+			$this->SE4_log("searching tags");
+		}
+
+		if ("true" == $this->options['SE4_use_category_search']) {
+			add_filter('posts_where', array(&$this, 'SE4_search_categories'));
+			add_filter('posts_join', array(&$this, 'SE4_terms_join'));
+			$this->SE4_log("searching categories");
+		}
+
+		if ("true" == $this->options['SE4_use_page_search']) {
+			add_filter('posts_where', array(&$this, 'SE4_search_pages'));
+			$this->SE4_log("searching pages");
+		}
+
+		if ("true" == $this->options['SE4_use_excerpt_search']) {
+			add_filter('posts_where', array(&$this, 'SE4_search_excerpt'));
+			$this->SE4_log("searching excerpts");
+		}
+
+		if ("true" == $this->options['SE4_use_comment_search']) {
+			add_filter('posts_where', array(&$this, 'SE4_search_comments'));
+			add_filter('posts_join', array(&$this, 'SE4_comments_join'));
+			$this->SE4_log("searching comments");
+		}
+
+		if ("true" == $this->options['SE4_use_draft_search']) {
+			add_filter('posts_where', array(&$this, 'SE4_search_draft_posts'));
+			$this->SE4_log("searching drafts");
+		}
+
+		if ("true" == $this->options['SE4_use_attachment_search']) {
+			add_filter('posts_where', array(&$this, 'SE4_search_attachments'));
+			$this->SE4_log("searching attachments");
+		}
+
+		if ("true" == $this->options['SE4_use_metadata_search']) {
+			add_filter('posts_where', array(&$this, 'SE4_search_metadata'));
+			add_filter('posts_join', array(&$this, 'SE4_search_metadata_join'));
+			$this->SE4_log("searching metadata");
+		}
+
+		if ("true" == $this->options['SE4_exclude_posts']) {
+			add_filter('posts_where', array(&$this, 'SE4_exclude_posts'));
+			$this->SE4_log("searching excluding posts");
+		}
+
+		if ("true" == $this->options['SE4_exclude_categories']) {
+			add_filter('posts_where', array(&$this, 'SE4_exclude_categories'));
+			add_filter('posts_join', array(&$this, 'SE4_exclude_categories_join'));
+			$this->SE4_log("searching excluding categories");
+		}
+		
+		
+		// Add registration of bo_revisions hook handler
+		// right before the following line already existant
+		add_filter('posts_where', array(&$this, 'SE4_no_revisions'));
+		
+		
+		//Duplicate fix provided by Tiago.Pocinho
+		add_filter('posts_request', array(&$this, 'SE4_distinct'));
+	}
+
+	// Exclude post revisions
+	function SE4_no_revisions($where) {
+	  global $wp_query;
+	  if (!empty($wp_query->query_vars['s'])) {
+	    $where = 'AND (' . substr($where, strpos($where, 'AND')+3) . ') AND post_type != \'revision\'';
+	  }
+	  return $where;
+	}
+	
+	// Logs search into a file
+	function SE4_log($msg) {
+
+		if ($this->logging) {
+			$fp = fopen("logfile.log","a+");
+			if ( !$fp ) { echo 'unable to write to log file!'; }
+			$date = date("Y-m-d H:i:s ");
+			$source = "search_everything plugin: ";
+			fwrite($fp, "\n\n".$date."\n".$source."\n".$msg);
+			fclose($fp);
+		}
+		return true;
+	}
+
+	//Duplicate fix provided by Tiago.Pocinho
+	function SE4_distinct($query){
+		global $wp_query;
+		if (!empty($wp_query->query_vars['s'])) {
+			if (strstr($where, 'DISTINCT')) {
+			} else {
+				$query = str_replace('SELECT', 'SELECT DISTINCT', $query);
+			}
+		}
+		return $query;
+	}
+
+	function SE4_exclude_posts($where) {
+		global $wp_query;
+		if (!empty($wp_query->query_vars['s'])) {
+			$excl_list = implode(',', explode(',', trim($this->options['SE4_exclude_posts_list'])));
+			$where = str_replace('"', '\'', $where);
+			$where = 'AND ('.substr($where, strpos($where, 'AND')+3).' )';
+			$where .= ' AND (ID NOT IN ( '.$excl_list.' ))';
+		}
+
+		$this->SE4_log("ex posts where: ".$where);
+		return $where;
+	}
+
+	//search pages (except password protected pages provided by loops)
+	function SE4_search_pages($where) {
+		global $wp_query;
+		if (!empty($wp_query->query_vars['s'])) {
+
+			$where = str_replace('"', '\'', $where);
+			if ('true' == $this->options['SE4_approved_pages_only']) {
+				$where = str_replace('post_type = \'post\' AND ', 'post_password = \'\' AND ', $where);
+			}
+			else { // < v 2.1
+				$where = str_replace('post_type = \'post\' AND ', '', $where);
+			}
+		}
+
+		$this->SE4_log("pages where: ".$where);
+		return $where;
+	}
+
+	//search excerpts provided by Dennis Turner, fixed by GvA
+	function SE4_search_excerpt($where) {
+		global $wp_query;
+		if (!empty($wp_query->query_vars['s'])) {
+				$where = str_replace('"', '\'', $where);
+				$where = str_replace(' OR (wp_posts.post_content LIKE \'%' . 
+				$wp_query->query_vars['s'] . '%\'', ' OR (wp_posts.post_content LIKE \'%' . 
+				$wp_query->query_vars['s'] . '%\') OR (wp_posts.post_excerpt LIKE \'%' . 
+				$wp_query->query_vars['s'] . '%\'', $where);
+		}
+
+		$this->SE4_log("excerpts where: ".$where);
+		return $where;
+	}
+	
+	
+	//search drafts
+	function SE4_search_draft_posts($where) {
+		global $wp_query;
+		if (!empty($wp_query->query_vars['s'])) {
+			$where = str_replace('"', '\'', $where);
+			$where = str_replace(' AND (post_status = \'publish\'', ' AND (post_status = \'publish\' or post_status = \'draft\'', $where);
+		}
+
+		$this->SE4_log("drafts where: ".$where);
+		return $where;
+	}
+
+	//search attachments
+	function SE4_search_attachments($where) {
+		global $wp_query;
+		if (!empty($wp_query->query_vars['s'])) {
+			$where = str_replace('"', '\'', $where);
+			$where = str_replace(' AND (post_status = \'publish\'', ' AND (post_status = \'publish\' or post_status = \'attachment\'', $where);
+			$where = str_replace('AND post_status != \'attachment\'','',$where);
+		}
+
+		$this->SE4_log("attachments where: ".$where);
+		return $where;
+	}
+
+	//search comments
+	function SE4_search_comments($where) {
+	global $wp_query, $wpdb;
+		if (!empty($wp_query->query_vars['s'])) {
+			if ('true' == $this->options['SE4_approved_comments_only']) {
+				$comment_approved = " AND c.comment_approved =  '1'";
+	  		} else {
+				$comment_approved = '';
+	  		}
+
+			if ($this->wp_ver23) {
+				$where .= " OR ( c.comment_post_ID = ".$wpdb->posts . ".ID " . $comment_approved . " AND c.comment_content LIKE '%" . $wpdb->escape($wp_query->query_vars['s']) . "%') ";
+	  		}
+		}
+
+		$this->SE4_log("comments where: ".$where);
+
+		return $where;
+	}
+
+	//search metadata
+	function SE4_search_metadata($where) {
+		global $wp_query, $wpdb;
+		if (!empty($wp_query->query_vars['s'])) {
+			if ($this->wp_ver23)
+				$where .= " OR (m.meta_value LIKE '%" . $wpdb->escape($wp_query->query_vars['s']) . "%') ";
+			else
+				$where .= " OR meta_value LIKE '%" . $wpdb->escape($wp_query->query_vars['s']) . "%' ";
+		}
+
+		$this->SE4_log("metadata where: ".$where);
+
+		return $where;
+	}
+
+	//search tags
+	function SE4_search_tags($where) {
+	global $wp_query, $wpdb;
+		if (!empty($wp_query->query_vars['s'])) {
+			//$where .= " OR ( tter.slug LIKE '%" . $wpdb->escape($wp_query->query_vars['s']) . "%') ";
+			$where .= " OR ( tter.name LIKE '%" . str_replace(' ', '-',$wpdb->escape($wp_query->query_vars['s'])) . "%') ";
+			}
+
+		$this->SE4_log("tags where: ".$where);
+
+		return $where;
+	}
+
+	//search categories
+	function SE4_search_categories ( $where ) {
+
+		global $wp_query, $wpdb;
+
+		if ( ! empty($wp_query->query_vars['s']) ) {
+			$where .= " OR ( tter.slug LIKE '%" . sanitize_title_with_dashes( $wp_query->query_vars['s'] ) . "%') ";
+		}
+
+		$this->SE4_log("categories where: ".$where);
+
+		return $where;
+
+	}
+
+	//exlude some categories from search
+	function SE4_exclude_categories($where) {
+		global $wp_query, $wpdb;
+		if (!empty($wp_query->query_vars['s'])) {
+			if (trim($this->options['SE4_exclude_categories_list']) != '') {
+				$excl_list = implode("','", explode(',', "'".trim($this->options['SE4_exclude_categories_list'])."'" ));
+				$where = str_replace('"', '\'', $where);
+				$where = 'AND ('.substr($where, strpos($where, 'AND')+3).' )';
+				if ($this->wp_ver23)
+					$where .= " AND ( ctax.term_id NOT IN ( ".$excl_list." ))";
+					else
+					$where .= ' AND (c.category_id NOT IN ( '.$excl_list.' ))';
+			}
+		}
+
+		$this->SE4_log("ex cats where: ".$where);
+		return $where;
+	}
+
+	//join for excluding categories - Deprecated in 2.3
+	function SE4_exclude_categories_join($join) {
+		global $wp_query, $wpdb;
+
+		if (!empty($wp_query->query_vars['s'])) {
+
+			if ($this->wp_ver23) {
+							$join .= " LEFT JOIN $wpdb->term_relationships AS crel ON ($wpdb->posts.ID = crel.object_id) LEFT JOIN $wpdb->term_taxonomy AS ctax ON (ctax.taxonomy = 'category' AND crel.term_taxonomy_id = ctax.term_taxonomy_id) LEFT JOIN $wpdb->terms AS cter ON (ctax.term_id = cter.term_id) ";
+				  		} else {
+							$join .= "LEFT JOIN $wpdb->post2cat AS c ON $wpdb->posts.ID = c.post_id";
+						}
+		}
+		$this->SE4_log("category join: ".$join);
+		return $join;
+	}
+
+	//join for searching comments
+	function SE4_comments_join($join) {
+		global $wp_query, $wpdb;
+
+		if (!empty($wp_query->query_vars['s'])) {
+
+			if ($this->wp_ver23) {
+				$join .= " LEFT JOIN $wpdb->comments AS c ON ( comment_post_ID = ID " . $comment_approved . ") ";
+				} else {
+
+				if ('true' == $this->options['SE4_approved_comments_only']) {
+					$comment_approved = " AND comment_approved =  '1'";
+		  		} else {
+					$comment_approved = '';
+				}
+
+				$join .= "LEFT JOIN $wpdb->comments ON ( comment_post_ID = ID " . $comment_approved . ") ";
+
+		    	}
+	    	}
+
+		$this->SE4_log("comments join: ".$join);
+		return $join;
+	}
+
+	//join for searching metadata
+	function SE4_search_metadata_join($join) {
+		global $wp_query, $wpdb;
+
+		if (!empty($wp_query->query_vars['s'])) {
+
+			if ($this->wp_ver23)
+				$join .= " LEFT JOIN $wpdb->postmeta AS m ON ($wpdb->posts.ID = m.post_id) ";
+			else
+				$join .= "LEFT JOIN $wpdb->postmeta ON $wpdb->posts.ID = $wpdb->postmeta.post_id ";
+		}
+		$this->SE4_log("metadata join: ".$join);
+		return $join;
+	}
+
+	//join for searching tags
+	function SE4_terms_join($join) {
+		global $wp_query, $wpdb;
+
+		if (!empty($wp_query->query_vars['s'])) {
+
+			// if we're searching for categories
+			if ( $this->options['SE4_use_category_search'] ) {
+				$on[] = "ttax.taxonomy = 'category'";
+			}
+
+			// if we're searching for tags
+			if ( $this->options['SE4_use_tag_search'] ) {
+				$on[] = "ttax.taxonomy = 'post_tag'";
+			}
+
+			// build our final string
+			$on = ' ( ' . implode( ' OR ', $on ) . ' ) ';
+
+			$join .= " LEFT JOIN $wpdb->term_relationships AS trel ON ($wpdb->posts.ID = trel.object_id) LEFT JOIN $wpdb->term_taxonomy AS ttax ON ( " . $on . " AND trel.term_taxonomy_id = ttax.term_taxonomy_id) LEFT JOIN $wpdb->terms AS tter ON (ttax.term_id = tter.term_id) ";
+			}
+
+		$this->SE4_log("tags join: ".$join);
+		return $join;
+	}
+}
+
+?>
Index: /afridex/plugins/search-everything/gpl.txt
===================================================================
--- /afridex/plugins/search-everything/gpl.txt (revision 21)
+++ /afridex/plugins/search-everything/gpl.txt (revision 21)
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 2 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, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /afridex/plugins/search-everything/lang/SE4-nl.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-nl.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-nl.po (revision 21)
@@ -0,0 +1,109 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2008-07-16 17:36+0100\n"
+"Last-Translator: Joeke-Remkus de Vries <jr@de-fries.nl>\n"
+"Language-Team: I-Dimensie <7@idimensie.nl>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Dutch\n"
+"X-Poedit-Country: NETHERLANDS\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr "Search Everything Options <strong>Bijgewerkt</strong>"
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "De geselecteerde opties hieronder zullen gebruikt worden in alle zoekopdrachten op deze site; als extra bij de standaard zoekfunctionaliteit."
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr "SE Zoek Opties"
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr "Gebruik dit formulier om je zoek opties te configureren."
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr "Zoek Opties Formulier"
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr "Bepaalde pagina's of berichten uitsluiten"
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "Komma gescheiden Post ID's (bijv: 1, 5 ,9 )"
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr "CategoriÃ«n uitsluiten"
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "Komma gescheiden categorie ID's (bijv: 1, 4)"
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr "Alle pagina's doorzoeken (niet wachtwoord beschermd)"
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr "Alleen goedgekeurde pagina's doorzoeken?"
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr "Alle tags doorzoeken"
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr "Alle reacties doorzoeken"
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr "Alleen goedgekeurde reacties doorzoeken?"
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr "Alle excerpts doorzoeken"
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr "Alle concepten doorzoeken"
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr "Alle bijlage doorzoeken"
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr "Alle custom fields (metadata) doorzoeken"
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr "Update Opties"
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr "SE Zoek Formulier"
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr "Gebruik dit zoekformulier om een live zoek test te doen."
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr "Site Doorzoeken"
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr "Voor zoektermen in"
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr "Voer Test Zoekopdracht uit"
+
Index: /afridex/plugins/search-everything/lang/SE4-sv_SE.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-sv_SE.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-sv_SE.po (revision 21)
@@ -0,0 +1,110 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything 4.7\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: Mikael Jorhult <mikael@mishkin.se>\n"
+"Language-Team: mishkin.se <mikael@mishkin.se>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1\n"
+"X-Poedit-Language: Swedish\n"
+"X-Poedit-Country: Sweden\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr "Search Everything InstÃ€llningar <strong>uppdaterades</strong>."
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "InstÃ€llningarna nedan anvÃ€nds vid varje sÃ¶kning som gÃ¶rs pÃ¥ webbplatsen."
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr "SE sÃ¶kinstÃ€llningar"
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr "AnvÃ€nd detta formulÃ€r fÃ¶r att konfigurera dina sÃ¶kinstÃ€llningar."
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr "FormulÃ€r fÃ¶r sÃ¶kinstÃ€llningar"
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr "Exkludera vissa inlÃ€gg eller sidor via ID"
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "ID fÃ¶r inlÃ€gg separerade med komma (exempel: 1, 5, 9)"
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr "Exkludera kategorier"
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "ID fÃ¶r kategorier separerade med komma (exempel: 1, 4)"
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr "SÃ¶k pÃ¥ alla sidor (icke-lÃ¶senordsskyddade)"
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr "SÃ¶k endast pÃ¥ godkÃ€nda sidor?"
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr "SÃ¶k alla etiketter"
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr "SÃ¶k i alla kommentarer"
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr "SÃ¶k endast godkÃ€nda kommentarer?"
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr "SÃ¶k i alla utdrag"
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr "SÃ¶k i alla utkast"
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr "SÃ¶k i alla bifogade filer"
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr "SÃ¶k i alla egna fÃ€lt (metadata)"
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr "Uppdatera instÃ€llningar"
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr "SE SÃ¶kformulÃ€r"
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr "AnvÃ€nd detta sÃ¶kformulÃ€r fÃ¶r att sÃ¶ka pÃ¥ webbplatsen."
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr "SÃ¶k pÃ¥ webbplatsen"
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr "Skriv in sÃ¶kord"
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr "KÃ¶r testsÃ¶kning"
+
Index: /afridex/plugins/search-everything/lang/SE4.pot
===================================================================
--- /afridex/plugins/search-everything/lang/SE4.pot (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4.pot (revision 21)
@@ -0,0 +1,110 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2007-11-15 20:36+0100\n"
+"Last-Translator: alohastone <alohastone@gmail.com>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-KeywordsList: __;_e\n"
+"X-Poedit-Basepath: C:\\Dokumente und Einstellungen\\AlohaStone\\Eigene Dateien\\Eigene Websites\\ver 2.3\\wordpress\\wp-content\\plugins\\\n"
+"X-Poedit-SearchPath-0: search-everything\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr ""
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr ""
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr ""
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr ""
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr ""
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr ""
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr ""
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr ""
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr ""
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr ""
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr ""
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr ""
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr ""
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr ""
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr ""
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr ""
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr ""
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr ""
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr ""
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr ""
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr ""
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr ""
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr ""
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr ""
+
Index: /afridex/plugins/search-everything/lang/SE4-tr_TR.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-tr_TR.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-tr_TR.po (revision 21)
@@ -0,0 +1,105 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2007-10-11 01:59+0200\n"
+"Last-Translator: Baris Unver <baris.unver@beyn.org>\n"
+"Language-Team: Baris Unver <baris.unver@beyn.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Turkish\n"
+"X-Poedit-Country: TURKEY\n"
+"X-Poedit-KeywordsList: __;_e\n"
+"X-Poedit-Basepath: c:\\wamp\\www\\wordpress212\\wp-content\\plugins\\\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"X-Poedit-SearchPath-0: search-everything\n"
+
+#: search-everything/SE-Admin.php:
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "AÅaÄÄ±da belirleyeceÄiniz seÃ§enekler, Wordpress'in varsayÄ±lan arama kutusuyla yapÄ±lan aramalarda etkili olacaktÄ±r."
+
+#: search-everything/SE-Admin.php:
+msgid "Exclude some post or page IDs"
+msgstr "BelirteceÄim yazÄ±larÄ± hariÃ§ tut"
+
+#: search-everything/SE-Admin.php:
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "VirgÃŒlle ayÄ±rarak ID(ler) girin (Ã¶rn. 1, 5, 9)"
+
+#: search-everything/SE-Admin.php:
+msgid "Exclude Categories <strong><small>(Wordpress 2.2 Only)</strong></small>"
+msgstr "BelirteceÄim kategorileri hariÃ§ tut <strong><small>(YalnÄ±zca Wordpress 2.2)</strong></small>"
+
+#: search-everything/SE-Admin.php:
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "VirgÃŒlle ayÄ±rarak ID(ler) girin (Ã¶rn. 1, 4)"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every page (non-password protected)"
+msgstr "SayfalarÄ± ara (Åifre konmamÄ±ÅlarÄ±)"
+
+#: search-everything/SE-Admin.php:
+msgid "Search approved pages only?"
+msgstr "YalnÄ±zca onaylanmÄ±Å sayfalarÄ± ara"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every comment"
+msgstr "YorumlarÄ± ara"
+
+#: search-everything/SE-Admin.php:
+msgid "Search approved comments only?"
+msgstr "YalnÄ±zca onaylanmÄ±Å yorumlarÄ± ara"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every excerpt"
+msgstr "Excerpt'leri ara"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every draft"
+msgstr "TaslaklarÄ± ara"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every attachment"
+msgstr "Eklentileri ara"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every custom field (metadata)"
+msgstr "Ãzel alanlarÄ± ara (metadata)"
+
+#: search-everything/SE-Admin.php:
+msgid "Update Options"
+msgstr "SeÃ§enekleri GÃŒncelle"
+
+#: search-everything/SE-Admin.php:
+msgid "SE Search Form"
+msgstr "SE Arama Formu"
+
+#: search-everything/SE-Admin.php:
+msgid "SE Search Options"
+msgstr "SE Arama SeÃ§enekleri"
+
+#: search-everything/SE-Admin.php:
+msgid "Use this form to configure your search options."
+msgstr "Bu bÃ¶lÃŒmÃŒ arama seÃ§eneklerini belirlemek iÃ§in kullanÄ±n."
+
+#: search-everything/SE-Admin.php:
+msgid "Search Options Form"
+msgstr "Arama SeÃ§enekleri Formu"
+
+#: search-everything/SE-Admin.php:
+msgid "Use this search form to run a live search test."
+msgstr "Ãrnek bir arama yapmak iÃ§in bu formu kullanÄ±n."
+
+#: search-everything/SE-Admin.php:
+msgid "Site Search"
+msgstr "Site AramasÄ±"
+
+#: search-everything/SE-Admin.php:
+msgid "Enter search terms"
+msgstr "Arama kriterlerini girin"
+
+#: search-everything/SE-Admin.php:
+msgid "Run Test Search"
+msgstr "Deneme AramasÄ±"
+
Index: /afridex/plugins/search-everything/lang/SE4-ja.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-ja.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-ja.po (revision 21)
@@ -0,0 +1,131 @@
+# Japanese Language File for Search Everything 3.01
+# (search_everything-ja_UTF.po)
+# This file is distributed under the same license as the WordPress package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything 3.01\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2008-02-29 11:50-0500\n"
+"Last-Translator: Naoko McCracken <info@nao-net.com>\n"
+"Language-Team: WordPress J-Series Project <http://wppluginsj.sourceforge.jp/i18n-ja_jp/>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Japanese\n"
+"X-Poedit-Country: JAPAN\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"X-Poedit-KeywordsList: _e;__\n"
+"X-Poedit-Basepath: .\n"
+"X-Poedit-SearchPath-0: .\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr "Search Everything ã®èš­å®ã<strong>æŽæ°ããŸãã</strong>ã"
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "ä»¥äžã§éžæããèš­å®ã¯ãããã©ã«ãã®èšäºæ€çŽ¢æ©èœã«å ãããµã€ãå
+ã®æ€çŽ¢ã¯ãšãªãã¹ãŠã«é©çšãããŸãã"
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr "SE æ€çŽ¢èš­å®"
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr "ä»¥äžã®ãã©ãŒã ã§æ€çŽ¢èš­å®ãè¡ã£ãŠãã ããã"
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr "æ€çŽ¢èš­å®ãã©ãŒã "
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr "æå®ãã ID ã®æçš¿ãããŒãžãæ€çŽ¢å¯Ÿè±¡ã«å«ããªã"
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "æçš¿ ID ãåè§ã³ã³ãåºåãã§èšå
+¥ããŠãã ããïŒäŸ: 1, 5, 9ïŒã"
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr "æå®ããã«ããŽãªãŒãæ€çŽ¢å¯Ÿè±¡ã«å«ããªã"
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "ã«ããŽãªãŒ ID ãåè§ã³ã³ãåºåãã§èšå
+¥ããŠãã ããïŒäŸ: 1, 4ïŒã"
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr "ãã¹ã¯ãŒãä¿è­·ãããŠããªãããŒãžããã¹ãŠæ€çŽ¢"
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr "æ¿èªããããŒãžã®ã¿ãæ€çŽ¢ããŸããïŒ"
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr "ã¿ã°ããã¹ãŠæ€çŽ¢"
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr "ã³ã¡ã³ãããã¹ãŠæ€çŽ¢"
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr "æ¿èªããã³ã¡ã³ãã®ã¿ãæ€çŽ¢ããŸããïŒ"
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr "èšäºã®èŠçŽããã¹ãŠæ€çŽ¢"
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr "æªå
+¬éã®èšäºããã¹ãŠæ€çŽ¢"
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr "ã¡ãã£ã¢ããã¹ãŠæ€çŽ¢"
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr "ã«ã¹ã¿ã ãã£ãŒã«ãïŒã¡ã¿ããŒã¿ïŒããã¹ãŠæ€çŽ¢"
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr "èš­å®ãæŽæ°"
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr "SE æ€çŽ¢ãã©ãŒã "
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr "ãã®æ€çŽ¢ãã©ãŒã ã§çŸåšé©çšãããèš­å®ãå
+ã«ããæ€çŽ¢ãã¹ããè¡ãããšãã§ããŸãã"
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr "ãµã€ãã®æ€çŽ¢"
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr "æ€çŽ¢ã­ãŒã¯ãŒããå
+¥å"
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr "æ€çŽ¢ã®ãã¹ã"
+
+#~ msgid "Search Everything"
+#~ msgstr "æ€çŽ¢å¯Ÿè±¡èš­å®"
+#~ msgid "Save"
+#~ msgstr "ä¿å­"
+#~ msgid "You may have to update your options twice before it sticks."
+#~ msgstr "å€æŽãé©çšããããŸã§ãèš­å®ã2åæŽæ°ããå¿
+èŠãããå ŽåããããŸãã"
+
Index: /afridex/plugins/search-everything/lang/SE4-ru_RU.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-ru_RU.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-ru_RU.po (revision 21)
@@ -0,0 +1,113 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2007-11-15 20:36+0100\n"
+"Last-Translator: Silver Ghost <silver@silverghost.org.ua>\n"
+"Language-Team: RUSSIAN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr "ÐÐ°ÑÑÑÐŸÐ¹ÐºÐž Search Everything <strong>ÐŸÐ±ÐœÐŸÐ²Ð»ÐµÐœÑ</strong>"
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "ÐÐ°ÑÑÑÐŸÐ¹ÐºÐž, Ð²ÑÐ±ÑÐ°ÐœÐœÑÐµ ÐœÐžÐ¶Ðµ, Ð±ÑÐŽÑÑ Ð¿ÑÐžÐŒÐµÐœÐµÐœÑ Ðº ÐºÐ°Ð¶ÐŽÐŸÐŒÑ Ð¿ÐŸÐžÑÐºÐŸÐ²ÐŸÐŒÑ Ð·Ð°Ð¿ÑÐŸÑÑ ÐœÐ° ÑÑÐŸÐŒ ÑÐ°Ð¹ÑÐµ. Ð Ð¿ÑÐŸÑÐžÐ²ÐœÐŸÐŒ ÑÐ»ÑÑÐ°Ðµ, Ð±ÑÐŽÐµÑ ÐžÑÐ¿ÐŸÐ»ÑÐ·ÐŸÐ²Ð°Ðœ Ð²ÑÑÑÐŸÐµÐœÐœÑÐ¹ Ð¿ÐŸÐžÑÐº."
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr "SE ÐœÐ°ÑÑÑÐŸÐ¹ÐºÐž Ð¿ÐŸÐžÑÐºÐ°"
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr "ÐÑÐ¿ÐŸÐ»ÑÐ·ÑÐ¹ÑÐµ ÑÑÑ ÑÐŸÑÐŒÑ ÐŽÐ»Ñ ÐœÐ°ÑÑÑÐŸÐ¹ÐºÐž Ð¿Ð°ÑÐ°ÐŒÐµÑÑÐŸÐ² Ð¿ÐŸÐžÑÐºÐ°."
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr "Ð€ÐŸÑÐŒÐ° ÐœÐ°ÑÑÑÐŸÐ¹ÐºÐž Ð¿ÐŸÐžÑÐºÐ°"
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr "ÐÑÐºÐ»ÑÑÐžÑÑ ÐœÐµÐºÐŸÑÐŸÑÑÐµ Ð·Ð°Ð¿ÐžÑÐž ÐžÐ»Ðž ID ÑÑÑÐ°ÐœÐžÑ"
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "ID Ð·Ð°Ð¿ÐžÑÐµÐ¹, ÑÐ°Ð·ÐŽÐµÐ»ÐµÐœÐœÑÐµ Ð·Ð°Ð¿ÑÑÐŸÐ¹ (Ð¿ÑÐžÐŒÐµÑ: 1, 5, 9)"
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr "ÐÑÐºÐ»ÑÑÐžÑÑ ÐºÐ°ÑÐµÐ³ÐŸÑÐžÐž"
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "ID ÐºÐ°ÑÐµÐ³ÐŸÑÐžÐ¹, ÑÐ°Ð·ÐŽÐµÐ»ÐµÐœÐœÑÐµ Ð·Ð°Ð¿ÑÑÐŸÐ¹ (Ð¿ÑÐžÐŒÐµÑ: 1, 4)"
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr "ÐÑÐºÐ°ÑÑ ÐœÐ° ÐºÐ°Ð¶ÐŽÐŸÐ¹ ÑÑÑÐ°ÐœÐžÑÐµ (ÐœÐµ Ð·Ð°ÑÐžÑÐµÐœÐœÐŸÐ¹ Ð¿Ð°ÑÐŸÐ»ÐµÐŒ)"
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr "ÐÑÐºÐ°ÑÑ ÑÐŸÐ»ÑÐºÐŸ ÐœÐ° ÐŸÐ¿ÑÐ±Ð»ÐžÐºÐŸÐ²Ð°ÐœÐœÑÑ
+ ÑÑÑÐ°ÐœÐžÑÐ°Ñ
+"
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr "ÐÑÐºÐ°ÑÑ ÐºÐ°Ð¶ÐŽÑÐ¹ ÑÑÐ³"
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr "ÐÑÐºÐ°ÑÑ ÐºÐ°Ð¶ÐŽÑÐ¹ ÐºÐŸÐŒÐŒÐµÐœÑÐ°ÑÐžÐ¹"
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr "ÐÑÐºÐ°ÑÑ ÑÐŸÐ»ÑÐºÐŸ Ð² ÐŸÐ¿ÑÐ±Ð»ÐžÐºÐŸÐ²Ð°ÐœÐœÑÑ
+ ÐºÐŸÐŒÐŒÐµÐœÑÐ°ÑÐžÑÑ
+"
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr "ÐÑÐºÐ°ÑÑ ÐºÐ°Ð¶ÐŽÑÑ Ð²ÑÐŽÐµÑÐ¶ÐºÑ"
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr "ÐÑÐºÐ°ÑÑ ÐºÐ°Ð¶ÐŽÑÐ¹ ÑÐµÑÐœÐŸÐ²ÐžÐº"
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr "ÐÑÐºÐ°ÑÑ ÐºÐ°Ð¶ÐŽÑÐ¹ Ð¿ÑÐžÐºÑÐµÐ¿Ð»ÐµÐœÐœÑÐ¹ ÑÐ°Ð¹Ð»"
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr "ÐÑÐºÐ°ÑÑ ÐºÐ°Ð¶ÐŽÐŸÐµ ÑÐ²ÐŸÐµ Ð¿ÐŸÐ»Ðµ (ÐŒÐµÑÐ°ÐŽÐ°ÐœÐœÑÐµ)"
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr "ÐÐ±ÐœÐŸÐ²ÐžÑÑ ÐœÐ°ÑÑÑÐŸÐ¹ÐºÐž"
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr "SE ÑÐŸÑÐŒÐ° Ð¿ÐŸÐžÑÐºÐ°"
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr "ÐÑÐ¿ÐŸÐ»ÑÐ·ÑÐ¹ÑÐµ ÑÑÑ ÑÐŸÑÐŒÑ ÐŽÐ»Ñ ÑÐµÑÑÐžÑÐŸÐ²Ð°ÐœÐžÑ Ð¿ÐŸÐžÑÐºÐ°."
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr "ÐÐŸÐžÑÐº Ð¿ÐŸ ÑÐ°Ð¹ÑÑ"
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr "ÐÐ²ÐµÐŽÐžÑÐµ, ÑÑÐŸ Ð±ÑÐŽÐµÐŒ ÐžÑÐºÐ°ÑÑ"
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr "ÐÑÐºÐ°ÑÑ"
+
+msgid "Search every category"
+msgstr "ÐÑÐºÐ°ÑÑ Ð² ÐºÐ°Ð¶ÐŽÐŸÐ¹ ÐºÐ°ÑÐµÐ³ÐŸÑÐžÐž"
Index: /afridex/plugins/search-everything/lang/SE4-es_ES.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-es_ES.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-es_ES.po (revision 21)
@@ -0,0 +1,113 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything in italiano\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2008-07-22 12:32+0100\n"
+"Last-Translator: Alejandro Urrutia <alejandro@theindependentproject.com>\n"
+"Language-Team: Gianni Diurno | http://gidibao.net/ <gidibao@gmail.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-KeywordsList: __;_e\n"
+"X-Poedit-Basepath: C:\\Dokumente und Einstellungen\\AlohaStone\\Eigene Dateien\\Eigene Websites\\ver 2.3\\wordpress\\wp-content\\plugins\\\n"
+"X-Poedit-Language: Italian\n"
+"X-Poedit-Country: ITALY\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"X-Poedit-SearchPath-0: search-everything\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr "Opciones de Search Everything <strong>actualizadas</strong>."
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "Las opciones seleccionadas debajo serÃ¡n usadas en cada bÃºsqueda del sitio, en adiciÃ³n a la bÃºsqueda integrada del blog."
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr "Opciones de bÃºsqueda de SE"
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr "Use Ã©ste formulario para configurar sus opciones de bÃºsqueda."
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr "Formulario de opciones de bÃºsqueda"
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr "ExluÃ­r algunas pÃ¡ginas o entradas segÃºn su ID"
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "ID de entradas separadas por coma (ejemplo: 1, 5, 9)"
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr "ExcluÃ­r categorÃ­as"
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "ID de categorÃ­as separadas por coma (ejemplo: 1, 5, 9)"
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr "Buscar en todas las pÃ¡ginas (no protegidas por contraseÃ±a)"
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr "Â¿Buscar solamente pÃ¡ginas aprobadas? "
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr "Buscar en todas las etiquetas"
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr "Buscar en todos los comentarios"
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr "Â¿Buscar solamente comentarios aprobados?"
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr "Buscar en todos los resÃºmenes"
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr "Buscar en todos los borradores"
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr "Buscar en todos los adjuntos"
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr "Buscar en todos los campos personalizados (metadata)"
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr "Actualizar opciones"
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr "Formulario de bÃºsqueda de SE"
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr "Usar Ã©ste formulario de bÃºsqueda para ejecutar un test de bÃºsqueda en vivo."
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr "BÃºsqueda en el sitio"
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr "Introduzca tÃ©rminos de bÃºsqueda"
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr "Ejecute test de bÃºsqueda"
+
Index: /afridex/plugins/search-everything/lang/SE4-fr_FR.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-fr_FR.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-fr_FR.po (revision 21)
@@ -0,0 +1,104 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2007-10-11 01:58+0200\n"
+"Last-Translator: Baris Unver <baris.unver@beyn.org>\n"
+"Language-Team: Alakhnor\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: French\n"
+"X-Poedit-Country: FRANCE\n"
+"X-Poedit-KeywordsList: __;_e\n"
+"X-Poedit-Basepath: c:\\wamp\\www\\le-hibootest\\wp-content\\plugins\\\n"
+"X-Poedit-SearchPath-0: search-everything\n"
+
+#: search-everything/SE-Admin.php:
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "Les options sÃ©lectionnÃ©es ci-dessous seront utilisÃ©es dans les recherches effectuÃ©es sur ce site en s'ajoutant Ã  la fonction de recherche existante."
+
+#: search-everything/SE-Admin.php:
+msgid "Exclude some post or page IDs"
+msgstr "Exclure des ID d'articles ou de pages"
+
+#: search-everything/SE-Admin.php:
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "Liste d'IDs d'articles sÃ©parÃ©es par des virgules (ex : 1,5,9)"
+
+#: search-everything/SE-Admin.php:
+msgid "Exclude Categories <strong><small>(Wordpress 2.2 Only)</strong></small>"
+msgstr "Exclure des ID de catÃ©gories <strong><small>(Wordpress 2.2 seulement)</strong></small>"
+
+#: search-everything/SE-Admin.php:
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "Liste d'IDs de catÃ©gories sÃ©parÃ©es par des virgules (ex : 1,4)"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every page (non-password protected)"
+msgstr "Chercher dans toutes les pages (non-protÃ©gÃ©es par mot de passe)"
+
+#: search-everything/SE-Admin.php:
+msgid "Search approved pages only?"
+msgstr "Chercher dans les pages validÃ©es seulement"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every comment"
+msgstr "Chercher dans tous les commentaires"
+
+#: search-everything/SE-Admin.php:
+msgid "Search approved comments only?"
+msgstr "Chercher dans les commentaires validÃ©s seulement"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every excerpt"
+msgstr "Chercher dans tous les excerpt"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every draft"
+msgstr "Chercher dans tous les brouillons"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every attachment"
+msgstr "Chercher dans tous les documents joints"
+
+#: search-everything/SE-Admin.php:
+msgid "Search every custom field (metadata)"
+msgstr "Chercher dans tous les champs personnalisÃ©s (Meta-data)"
+
+#: search-everything/SE-Admin.php:
+msgid "Update Options"
+msgstr "Mise Ã  jour"
+
+#: search-everything/SE-Admin.php:
+msgid "SE Search Form"
+msgstr "Formulaire de recherche de SE"
+
+#: search-everything/SE-Admin.php:
+msgid "SE Search Options"
+msgstr "Options de recherche de SE"
+
+#: search-everything/SE-Admin.php:
+msgid "Use this form to configure your search options."
+msgstr "Utilisez ce formulaire pour configurer vos options de recherche."
+
+#: search-everything/SE-Admin.php:
+msgid "Search Options Form"
+msgstr "Formulaire d'option de recherche"
+
+#: search-everything/SE-Admin.php:
+msgid "Use this search form to run a live search test."
+msgstr "Utilisez ce champ pour effectuer une recherche rÃ©elle."
+
+#: search-everything/SE-Admin.php:
+msgid "Site Search"
+msgstr "Recherche sur le site"
+
+#: search-everything/SE-Admin.php:
+msgid "Enter search terms"
+msgstr "Entrez les termes de recherche"
+
+#: search-everything/SE-Admin.php:
+msgid "Run Test Search"
+msgstr "Lancer la recherche"
+
Index: /afridex/plugins/search-everything/lang/SE4-de_DE.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-de_DE.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-de_DE.po (revision 21)
@@ -0,0 +1,107 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2007-11-15 20:55+0100\n"
+"Last-Translator: alohastone <alohastone@gmail.com>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr "Search Everything Einstellungen <strong>aktualisiert</strong>."
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "Die folgenden Einstellungen werden in jeder Suchanfrage auf dieser Site verwendet; zusÃ€tzlich zu der integrierten Beitragssuche."
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr "SE Such-Einstellungen"
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr "Benutze dieses Formular um SE zu konfigurieren."
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr "Formular fÃŒr die Such-Einstellungen"
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr "Beitrags- oder Seiten-IDs ausschlieÃen"
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "Durch Komma getrennte Beitrags-IDs (Beispiel: 1, 5, 9)"
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr "Kategorien ausschlieÃen"
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "Durch Komma getrennte Kategorie-IDs (Beispiel: 1, 4)"
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr "Durchsuche jede Seite (nicht passwortgeschÃŒtzt)"
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr "Nur freigegebene Seiten durchsuchen?"
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr "Durchsuche jeden Tag"
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr "Durchsuche jeden Kommentar"
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr "Nur freigegebene Kommentare durchsuchen?"
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr "Durchsuche jede Kurzfassung"
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr "Durchsuche jeden Entwurf"
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr "Durchsuche jeden Dateianhang"
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr "Durchsuche jedes benutzerdefinierte Feld (Metadaten)"
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr "Einstellungen speichern"
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr "SE Suchfeld"
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr "Mit diesem Suchfeld kannst du SE testen."
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr "Site Suche"
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr "Suchbegriffe eingeben"
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr "Testsuche starten"
+
Index: /afridex/plugins/search-everything/lang/SE4-it_IT.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-it_IT.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-it_IT.po (revision 21)
@@ -0,0 +1,113 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything in italiano\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2008-06-22 11:53+0100\n"
+"Last-Translator: Gianni Diurno (aka gidibao) <gidibao@gmail.com>\n"
+"Language-Team: Gianni Diurno | http://gidibao.net/ <gidibao@gmail.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-KeywordsList: __;_e\n"
+"X-Poedit-Basepath: C:\\Dokumente und Einstellungen\\AlohaStone\\Eigene Dateien\\Eigene Websites\\ver 2.3\\wordpress\\wp-content\\plugins\\\n"
+"X-Poedit-Language: Italian\n"
+"X-Poedit-Country: ITALY\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"X-Poedit-SearchPath-0: search-everything\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr "Le opzioni di Search Everything sono state <strong>aggiornate</strong>."
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "Le opzioni selezionate verranno utilizzate per ogni interrogazione di ricerca (in aggiunta a quella di base) agli articoli di questo sito."
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr "Opzioni di ricerca SE"
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr "Utilizza questo modulo per configurare le tue opzioni di ricerca."
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr "Modulo delle opzioni di ricerca"
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr "Escludi l'ID di alcune pagine o articoli"
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "Separa l'ID dei post con una virgola (ad esempio: 1, 5, 9)"
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr "Escludi le categorie"
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "Separa l'ID delle categorie con una virgola (ad esempio: 1, 5, 9)"
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr "Ricerca estesa ad ogni pagina (non protetta da password)"
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr "Vuoi che la ricerca sia ristretta solamente alle pagine? "
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr "Ricerca estesa ad ogni tag"
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr "Ricerca estesa ad ogni commento"
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr "Vuoi che la ricerca sia ristretta solamente ai commenti?"
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr "Ricerca estesa ad ogni estratto"
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr "Ricerca estesa ad ogni bozza"
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr "Ricerca estesa ad ogni allegato"
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr "Ricerca per ogni campo personalizzato (metadata)"
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr "Aggiona le opzioni"
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr "Modulo di ricerca SE"
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr "Usa questo modulo di ricerca per avviare un test live di ricerca."
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr "Ricerca nel sito"
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr "Inserisci i termini di ricerca"
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr "Avvia il test di ricerca"
+
Index: /afridex/plugins/search-everything/lang/SE4-hu_HU.po
===================================================================
--- /afridex/plugins/search-everything/lang/SE4-hu_HU.po (revision 21)
+++ /afridex/plugins/search-everything/lang/SE4-hu_HU.po (revision 21)
@@ -0,0 +1,113 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Search Everything magyar fordÃ­tÃ¡s\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2008-08-07 15:25+0100\n"
+"Last-Translator: JÃ¡nos CsÃ¡rdi-Braunstein <csardijanos@gmail.com>\n"
+"Language-Team: JÃ¡nos CsÃ¡rdi-Braunstein <csardijanos@gmail.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-KeywordsList: __;_e\n"
+"X-Poedit-Basepath: C:\\Dokumente und Einstellungen\\AlohaStone\\Eigene Dateien\\Eigene Websites\\ver 2.3\\wordpress\\wp-content\\plugins\\\n"
+"X-Poedit-Language: Hungarian\n"
+"X-Poedit-Country: HUNGARY\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"X-Poedit-SearchPath-0: search-everything\n"
+
+#: search-everything/SE-Admin.php:29
+msgid "Search Everything Options  <strong>Updated</strong>."
+msgstr "Search Everything beÃ¡llÃ­tÃ¡sai  <strong>mentve</strong>."
+
+#: search-everything/SE-Admin.php:59
+msgid "The options selected below will be used in every search query on this site; in addition to the built-in post search."
+msgstr "Amiket itt beÃ¡llÃ­tasz, minden az oldaladon tÃ¶rtÃ©nÅ keresÃ©sre hatÃ¡ssal van (persze csak akkor, ha a Wordpress keresÅjÃ©vel keresel, kÃŒlsÅ keresÅkre nincs hatÃ¡sa, pl. Google). HasznÃ¡ld a lenti keresÅ mezÅt a beÃ¡llÃ­tÃ¡sok kiprÃ³bÃ¡lÃ¡sÃ¡hoz."
+
+#: search-everything/SE-Admin.php:63
+msgid "SE Search Options"
+msgstr "SE keresÃ©si opciÃ³k"
+
+#: search-everything/SE-Admin.php:64
+msgid "Use this form to configure your search options."
+msgstr "HasznÃ¡ld ezt az oldalt a keresÃ©s beÃ¡llÃ­tÃ¡sÃ¡hoz"
+
+#: search-everything/SE-Admin.php:67
+msgid "Search Options Form"
+msgstr "KeresÃ©si beÃ¡llÃ­tÃ¡sok"
+
+#: search-everything/SE-Admin.php:69
+msgid "Exclude some post or page IDs"
+msgstr "NÃ©hÃ¡ny oldal vagy bejegyzÃ©s kihagyÃ¡sa a keresÃ©sbÅl"
+
+#: search-everything/SE-Admin.php:70
+msgid "Comma separated Post IDs (example: 1, 5, 9)"
+msgstr "Ãrd be vesszÅvel azoknak az oldalaknak, vagy bejegyzÃ©seknek az azonosÃ­tÃ³jÃ¡t, amiken nem szeretnÃ©l keresni (pl. 1, 4, 5)"
+
+#: search-everything/SE-Admin.php:75
+msgid "Exclude Categories"
+msgstr "Kihagyni kÃ­vÃ¡nt kategÃ³riÃ¡k"
+
+#: search-everything/SE-Admin.php:76
+msgid "Comma separated category IDs (example: 1, 4)"
+msgstr "Ãrd be vesszÅvel azoknak a kategÃ³riÃ¡knak az azonosÃ­tÃ³jÃ¡t, amiken nem szeretnÃ©l keresni (pl. 1, 5)"
+
+#: search-everything/SE-Admin.php:81
+msgid "Search every page (non-password protected)"
+msgstr "KeresÃ©s minden oldalon (nem jelszÃ³ vÃ©detteken)"
+
+#: search-everything/SE-Admin.php:83
+msgid "Search approved pages only?"
+msgstr "Csak a kÃ¶zzÃ©tett oldalak kÃ¶zÃ¶tt?"
+
+#: search-everything/SE-Admin.php:89
+msgid "Search every tag"
+msgstr "KeresÃ©s minden cimke kÃ¶zÃ¶tt"
+
+#: search-everything/SE-Admin.php:94
+msgid "Search every comment"
+msgstr "KeresÃ©s minden hozzÃ¡szÃ³lÃ¡s kÃ¶zÃ¶tt"
+
+#: search-everything/SE-Admin.php:97
+msgid "Search approved comments only?"
+msgstr "Csak az elfogadott hozzÃ¡szÃ³lÃ¡sok kÃ¶zÃ¶tt?"
+
+#: search-everything/SE-Admin.php:100
+msgid "Search every excerpt"
+msgstr "KeresÃ©s minden kivonat kÃ¶zÃ¶tt"
+
+#: search-everything/SE-Admin.php:103
+msgid "Search every draft"
+msgstr "KeresÃ©s minden piszkozat kÃ¶zÃ¶tt"
+
+#: search-everything/SE-Admin.php:106
+msgid "Search every attachment"
+msgstr "KeresÃ©s minden feltÃ¶ltÃ¶tt fÃ¡jl kÃ¶zÃ¶tt"
+
+#: search-everything/SE-Admin.php:109
+msgid "Search every custom field (metadata)"
+msgstr "KeresÃ©s minden sajÃ¡t mezÅ kÃ¶zÃ¶tt (metadata)"
+
+#: search-everything/SE-Admin.php:115
+msgid "Update Options"
+msgstr "BeÃ¡llÃ­tÃ¡sok mentÃ©se"
+
+#: search-everything/SE-Admin.php:121
+msgid "SE Search Form"
+msgstr "SE keresÃ©s"
+
+#: search-everything/SE-Admin.php:122
+msgid "Use this search form to run a live search test."
+msgstr "HasznÃ¡ld ezt a keresÅt az keresÅs kiprÃ³bÃ¡lÃ¡sÃ¡hoz"
+
+#: search-everything/SE-Admin.php:125
+msgid "Site Search"
+msgstr "KeresÃ©s az oldalon"
+
+#: search-everything/SE-Admin.php:127
+msgid "Enter search terms"
+msgstr "Ãrd be amire keresni szeretnÃ©l"
+
+#: search-everything/SE-Admin.php:129
+msgid "Run Test Search"
+msgstr "KeresÃ©s"
+
Index: /afridex/plugins/search-everything/SE-Admin.php
===================================================================
--- /afridex/plugins/search-everything/SE-Admin.php (revision 21)
+++ /afridex/plugins/search-everything/SE-Admin.php (revision 21)
@@ -0,0 +1,217 @@
+<?php
+
+Class SearchEverythingAdmin {
+
+	var $version = '4.7.6';
+
+	function SearchEverythingAdmin() {
+
+		// Load language file
+		$locale = get_locale();
+		if ( !empty($locale) )
+			load_textdomain('SearchEverything', SE_ABSPATH .'lang/SE4-'.$locale.'.mo');
+
+
+		add_action('admin_head', array(&$this, 'SE4_options_style'));
+		add_action('admin_menu', array(&$this, 'SE4_add_options_panel'));
+
+        }
+
+	function SE4_add_options_panel() {
+		add_options_page('Search', 'Search Everything', 7, 'manage_search', array(&$this, 'SE4_option_page'));
+	}
+
+	//build admin interface
+	function SE4_option_page() {
+		global $wpdb, $table_prefix, $wp_version;
+
+		if($_POST['action'] == "save") {
+			echo "<div class=\"updated fade\" id=\"limitcatsupdatenotice\"><p>" . __("Search Everything Options  <strong>Updated</strong>.") . "</p></div>";
+
+			$new_options = array(
+				'SE4_exclude_categories'		=> $_POST["exclude_categories"],
+				'SE4_exclude_categories_list'	=> $_POST["exclude_categories_list"],
+				'SE4_exclude_posts'				=> $_POST["exclude_posts"],
+				'SE4_exclude_posts_list'		=> $_POST["exclude_posts_list"],
+				'SE4_use_page_search'			=> $_POST["search_pages"],
+				'SE4_use_comment_search'		=> $_POST["search_comments"],
+				'SE4_use_tag_search'			=> $_POST["search_tags"],
+				'SE4_use_category_search'		=> $_POST["search_categories"],
+				'SE4_approved_comments_only'	=> $_POST["appvd_comments"],
+				'SE4_approved_pages_only'		=> $_POST["appvd_pages"],
+				'SE4_use_excerpt_search'		=> $_POST["search_excerpt"],
+				'SE4_use_draft_search'			=> $_POST["search_drafts"],
+				'SE4_use_attachment_search'		=> $_POST["search_attachments"],
+				'SE4_use_metadata_search'		=> $_POST["search_metadata"]
+			);
+
+			update_option("search_tag", $_POST["SE4_use_tag_search"]);
+			update_option("SE4_options", $new_options);
+
+		}
+
+		$options = get_option('SE4_options');
+		$search_tag = get_option('search_tag');
+
+		?>
+
+		<div class="wrap">
+			<h2>Search Everything (SE) Version: <?php echo $this->version; ?></h2>
+
+		   	<table class="form-table">
+				<tr valign="top">
+					<td colspan="4" bgcolor="#DDD"><?php _e('Use this form to configure your search options.', 'SearchEverything'); ?><br />
+					<?php _e('The options selected below will be used in every search query on this site; in addition to the built-in post search.','SearchEverything'); ?></td>
+				</tr>
+				<tr>
+					<td>
+						<form method="post">
+		      				<legend><?php _e('Search Options Form', 'SearchEverything'); ?></legend>
+			        		<p><input type="checkbox" id="exclude_posts" name="exclude_posts" value="true" <?php if($options['SE4_exclude_posts'] == 'true') { echo 'checked="true"'; } ?> />
+			       			<label for="exclude_posts"><?php _e('Exclude some post or page IDs','SearchEverything'); ?></label><br />
+			       			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<label for="exclude_posts_list" class="SE_text_label"><?php _e('Comma separated Post IDs (example: 1, 5, 9)','SearchEverything'); ?></label><br />
+			        		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="text" size="50" class="SE_text_input" id="exclude_posts_list" name="exclude_posts_list" value="<?php echo $options['SE4_exclude_posts_list'];?>" /></p>
+
+			        		<p><input type="checkbox" id="exclude_categories" name="exclude_categories"	value="true" <?php if($options['SE4_exclude_categories'] == 'true') { echo 'checked="true"'; } ?> />
+			       			<label for="exclude_categories"><?php _e('Exclude Categories','SearchEverything'); ?></label><br />
+			       			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<label for="exclude_categories_list" class="SE_text_label"><?php _e('Comma separated category IDs (example: 1, 4)','SearchEverything'); ?></label><br />
+			         		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="text" size="50" class="SE_text_input" id="exclude_categories_list" name="exclude_categories_list" value="<?php echo $options['SE4_exclude_categories_list'];?>" /></p>
+
+		         			<p><input type="checkbox" id="search_pages" name="search_pages" value="true" <?php if($options['SE4_use_page_search'] == 'true') { echo 'checked="true"'; } ?>  />
+		       				<label for="search_pages"><?php _e('Search every page (non-password protected)','SearchEverything'); ?></label></p>
+							<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="checkbox" class="SE_text_input" id="appvd_pages" name="appvd_pages" value="true"  <?php if($options['SE4_approved_pages_only'] == 'true') { echo 'checked="true"'; } ?>
+		      				<label for="appvd_pages"><?php _e('Search approved pages only?','SearchEverything'); ?></label></p>
+
+							<?php
+							// Show tags only for WP 2.3+
+							If ($wp_version >= '2.3') { ?>
+								<p><input type="checkbox" id="search_tags" name="search_tags" value="true" <?php if($options['SE4_use_tag_search'] == 'true') { echo 'checked="true"'; } ?> />
+								<label for="search_tags"><?php _e('Search every tag name','SearchEverything'); ?></label></p>
+							<?php } ?>
+
+							<?php
+							// Show categories only for WP 2.5+
+							If ($wp_version >= '2.5') { ?>
+								<p><input type="checkbox" id="search_categories" name="search_categories" value="true"  <?php if($options['SE4_use_category_search'] == 'true') { echo 'checked="true"'; } ?> />
+								<label for="search_categories"><?php _e('Search every category name','SearchEverything'); ?></label></p>
+							<?php } ?>
+
+		         			<p><input type="checkbox" id="search_comments" name="search_comments" value="true" <?php if($options['SE4_use_comment_search'] == 'true') { echo 'checked="true"'; } ?> />
+		       				<label for="search_comments"><?php _e('Search every comment','SearchEverything'); ?></label></p>
+
+				 			<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="checkbox" class="SE_text_input" id="appvd_comments" name="appvd_comments" value="true"  <?php if($options['SE4_approved_comments_only'] == 'true') { echo 'checked="true"'; } ?> />
+		       				<label for="appvd_comments"><?php _e('Search approved comments only?','SearchEverything'); ?></label></p>
+
+			         		<p><input type="checkbox" id="search_excerpt" name="search_excerpt" value="true"  <?php if($options['SE4_use_excerpt_search'] == 'true') { echo 'checked="true"'; } ?> />
+			       			<label for="search_excerpt"><?php _e('Search every excerpt','SearchEverything'); ?></label></p>
+								<?php
+								// Show drafts only for WP 2.3+
+								If ($wp_version < '2.5') { ?>
+			         		<p><input type="checkbox" id="search_drafts" name="search_drafts" value="true"  <?php if($options['SE4_use_draft_search'] == 'true') { echo 'checked="true"'; } ?> />
+			       			<label for="search_drafts"><?php _e('Search every draft','SearchEverything'); ?></label></p>
+								<?php } ?>
+			         		<p><input type="checkbox" id="search_attachments" name="search_attachments" value="true"  <?php if($options['SE4_use_attachment_search'] == 'true') { echo 'checked="true"'; } ?> />
+			       			<label for="search_attachments"><?php _e('Search every attachment','SearchEverything'); ?></label></p>
+	
+			         		<p><input type="checkbox" id="search_metadata" name="search_metadata" value="true"  <?php if($options['SE4_use_metadata_search'] == 'true') { echo 'checked="true"'; } ?> />
+			       			<label for="search_metadata"><?php _e('Search every custom field (metadata)','SearchEverything'); ?></label></p>
+				    	
+			    	</td>
+			    </tr>
+		    </table>
+			<div class="submit">
+				<input type="hidden" name="action" value="save" />
+				<input type="submit" value="<?php _e('Update Options', 'SearchEverything') ?>" />
+			</div>
+			</form>
+		</div>
+	    	
+		<div class="wrap">
+			<h2><?php _e('SE Search Form', 'SearchEverything'); ?></h2>
+		   	<table class="form-table">
+				<tr valign="top">
+					<td colspan="4" bgcolor="#DDD"><?php _e('Use this search form to run a live search test.', 'SearchEverything'); ?></td>
+				</tr>
+				<tr>
+					<td>
+			       		<legend><?php _e('Site Search', 'SearchEverything'); ?></legend>
+			        	<form method="get" id="searchform" action="<?php bloginfo('home'); ?>"><p class="srch submit">
+							<label for="s"><?php _e('Enter search terms', 'SearchEverything'); ?><br /></label>
+			          		<input type="text" class="srch-txt" value="<?php echo wp_specialchars($s, 1); ?>" name="s" id="s" size="30" />
+						  	<input type="submit" class="SE4_btn" id="searchsubmit" value="<?php _e('Run Test Search', 'SearchEverything'); ?>" /></p>
+			      		</form>
+					</td>
+				</tr>
+			</table>
+		</div>
+		
+		<div class="wrap">
+			<h2>SE Project Information</h2>
+		   	<table class="form-table">
+				<tr valign="top">
+					<td colspan="4" bgcolor="#DDD">
+		       			As of 2.5 I'm taking a hiatus from SE development; however I'm still committing feature updates and bug fixes from the community.<br/>
+		       			You should not fret, the development since Version One has primarily come from the WordPress community and as a Search Everything user myself, I&#8217;m grateful for their dedicated and continued support:
+				        <ul class="SE_lists">
+							<li><a href="http://striderweb.com/">Stephen Rider</a></li>
+							<li><a href="http://chrismeller.com/">Chris Meller</a></li>
+							<li>jdleung</li>
+							<li>Alakhnor</li>
+							<li><a href="http://kinrowan.net/">Cori Schlegel</a></li>
+							<li><a href="http://green-beast.com/">Mike Cherim</a></li>
+				        	<li><a href="http://blog.saddey.net/">Saddy</a></li>
+				        	<li><a href="http://www.reaper-x.com/">Reaper</a></li>
+							<li><a href="http://beyn.org/">BarÄ±Å Ãnver</a> (localization support)</li>
+							<li><a href="http://www.alohastone.com">alohastone</a> (localization support)</li>
+							<li><a href="http://www.fratelliditalia.eu">Domiziano Galia</a></li>
+							<li><a href="http://meandmymac.net">Arnan de Gans</a> (Options panel)</li>
+				        	<li>Uli Iserloh</li>
+				        </ul>
+					</td>
+				</tr>
+				<tr>
+					<td bgcolor="#DDD">
+						If you&#8217;d like to contribute there&#8217;s a lot to do:
+				        <ul class="SE_lists">
+							<strong><li>Search Options for Visitor.</li></strong>
+							<li>More meta data functions.</li>
+							<li>Search Bookmarks.</li>
+							<li>&#8230;anything else you want to add.</li>
+				        </ul>
+					</td>
+				</tr>
+				<tr>
+					<td bgcolor="#DDD">
+		     			The current project home is at <a href="http://scatter3d.com/">scatter3d.com</a>. If you want to contribute <a href="mailto:dancameron+se@gmail.com">e-mail me</a> your modifications.<br />
+		     			Respectfully,<br />
+						<a href="http://dancameron.org/">Dan Cameron</a>
+					</td>
+				</tr>
+			</table>
+		</div>
+
+		<?php
+	}	//end SE4_option_page
+
+	//styling options page
+	function SE4_options_style() {
+		?>
+		<style type="text/css" media="screen">
+			div.SE4 p.submit, div.SE4 form p.submit, div.SE4 p.submit input { text-align:left; }
+			#SE4_options_panel p.submit { text-align:left; }
+		  	form#searchform label, form#searchform input, form#SE_form label, form#SE_form input { margin-left:10px; }
+		  	input.SE4_btn { cursor:pointer; margin-left:5px; }
+		  	p.srch { margin:0; margin-bottom:20px; }
+		  	p.submit input.srch-txt { background-color:#f4f4f4; background-image:none; border:1px solid #999; padding:6px; }
+		  	p.submit input.srch-txt:focus, p.submit input.srch-txt:active { background-color:#fff; background-image:none; border:1px solid #111; padding:6px; }
+		  	p.sig { margin-left:25px; }
+		  	span.SE_notice { color:#cd0000; font-weight:bold; padding-left:10px; }
+			label.SE_text_label { cursor:text; }
+			form#SE_form label.SE_text_label, form#SE_form input.SE_text_input { margin-left:38px; }
+			ul.SE_lists li { list-style-type:square; }
+		</style>
+		<?php
+	}
+
+}
+?>
Index: /afridex/plugins/search-everything/readme.txt
===================================================================
--- /afridex/plugins/search-everything/readme.txt (revision 21)
+++ /afridex/plugins/search-everything/readme.txt (revision 21)
@@ -0,0 +1,85 @@
+=== Plugin Name ===
+Contributors: dancameron
+Donate link: http://www.amazon.com/gp/registry/wishlist/3EM84J7FVHE6S/ref=wl_web/
+Tags: search, tag search, category search, category exclusion, comment search, page search, admin, seo
+Requires at least: 2.1
+Tested up to: 2.6.1
+Stable tag: 4.7.6
+
+Increases Wordpress' default search functionality through an options panel. Options include searching pages, excerpts, attachments, drafts, comments and custom fields (metadata).
+
+== Description ==
+
+Search Everything increases the ability of the default Wordpress Search, options included:
+
+* Search Every Page
+* Search Every Tag
+* Search Every Category
+* Search non-password protected pages only
+* Search Every Comment
+* Search only approved comments
+* Search Every Draft
+* Search Every Excerpt
+* Search Every Attachment
+* Search Every Custom Field (metadata)
+* Exclude Posts from search
+* Exclude Categories from search
+
+... I'm still looking for some help so please contact me if you'd like to contribute.
+
+
+== Update Log ==
+
+*4.7.6.2 - Bug fix, some people returning no results. Matching release number with WP version.
+*4.7.5 - Post Revision Update for Wordpress 2.6 (necessary update for 2.6 users), removed future posts from results
+*4.7.1 - Swedish, Spanish and Dutch
+*4.7 - Wordpress 2.6 updates and more
+*4.6.2 - REQUIRED UPDATE from 4.6 release. Admin panel bug fixes (proper wp_version checks, disabled draft support for 2.5 and fixed the options from not updating); Russian and Italian language suuport.
+*4.6 - Admin refresh and Excerpt Fix
+*4.5 - Japanese Language Support, Category Fix for Wordpress 2.5, Tag search fix (name) other minors
+* 4.2.2 - Localization PO and MO updates, German Translation
+* 4.2 - Full 2.3 Support with Tag Searching and Category Exclusion.  Major Performance tweaks for searching tags and comments (4.2.1).
+* 4.1 - Major Plugin Architecture change, better localization support
+* 4.0.3 - Localization pot and Turkish translation (props Baris Unver)
+* 4.0.2 - CSS Bug fix - minor
+* 4.0.1 - Bug fix~Extraneous 's' in options panel
+* 4.0.0 - Wordpress 2.3 Support, Password protected pages option, Overhaul of Admin area
+
+[SVN Browser](http://searcheverything.scatter3d.com:3000/browser "Project Home and SVN Browser")
+
+== Installation ==
+
+Installation Instructions:
+
+1. Download the plugin and unzip it (didn't you already do this?).
+2. Put the 'search-everything' directory into your wp-content/plugins/ directory.
+3. Go to the Plugins page in your WordPress Administration area and click 'Activate' next to Search Everything.
+4. Go to the Options >  Search Everything and configure your site.
+5. That's it. Have fun and if you can contribute (see notes), at the very least post your likes and dislikes about the plugin linking to http://dancameron.org/wordpress/ so your readers to find the plugin and I can see your feedback.
+	
+	
+	
+== Frequently Asked Questions ==
+
+= It doesn't work =
+
+Read the installation guide.
+
+= It *still* doesn't work =
+
+Open up a thread in the support forums tagged 'searcheverything' and 'dancameron'.
+
+
+== Screenshots ==
+
+1. Screenshot of the options panel
+
+== Help ==
+
+I do need help with this plugin. If you can possibly do my day job and take my girls to the park I would appreciate it; otherwise contribute your fixes and suggestions to me in the forums or through the contact form on my site.
+
+
+== Support ==
+
+Create a new topic in the forums with 'searcheverything' and 'dancameron' as the tag for your support related questions.
+
Index: /afridex/plugins/hello.php
===================================================================
--- /afridex/plugins/hello.php (revision 21)
+++ /afridex/plugins/hello.php (revision 21)
@@ -0,0 +1,74 @@
+<?php
+/*
+Plugin Name: Hello Dolly
+Plugin URI: http://wordpress.org/#
+Description: This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from <cite>Hello, Dolly</cite> in the upper right of your admin screen on every page.
+Author: Matt Mullenweg
+Version: 1.5
+Author URI: http://ma.tt/
+*/
+
+// These are the lyrics to Hello Dolly
+$lyrics = "Hello, Dolly
+Well, hello, Dolly
+It's so nice to have you back where you belong
+You're lookin' swell, Dolly
+I can tell, Dolly
+You're still glowin', you're still crowin'
+You're still goin' strong
+We feel the room swayin'
+While the band's playin'
+One of your old favourite songs from way back when
+So, take her wrap, fellas
+Find her an empty lap, fellas
+Dolly'll never go away again
+Hello, Dolly
+Well, hello, Dolly
+It's so nice to have you back where you belong
+You're lookin' swell, Dolly
+I can tell, Dolly
+You're still glowin', you're still crowin'
+You're still goin' strong
+We feel the room swayin'
+While the band's playin'
+One of your old favourite songs from way back when
+Golly, gee, fellas
+Find her a vacant knee, fellas
+Dolly'll never go away
+Dolly'll never go away
+Dolly'll never go away again";
+
+// Here we split it into lines
+$lyrics = explode("\n", $lyrics);
+// And then randomly choose a line
+$chosen = wptexturize( $lyrics[ mt_rand(0, count($lyrics) - 1) ] );
+
+// This just echoes the chosen line, we'll position it later
+function hello_dolly() {
+	global $chosen;
+	echo "<p id='dolly'>$chosen</p>";
+}
+
+// Now we set that function up to execute when the admin_footer action is called
+add_action('admin_footer', 'hello_dolly');
+
+// We need some CSS to position the paragraph
+function dolly_css() {
+	echo "
+	<style type='text/css'>
+	#dolly {
+		position: absolute;
+		top: 2.3em;
+		margin: 0;
+		padding: 0;
+		right: 10px;
+		font-size: 16px;
+		color: #d54e21;
+	}
+	</style>
+	";
+}
+
+add_action('admin_head', 'dolly_css');
+
+?>
Index: /afridex/plugins/wordpress-23-related-posts-plugin/wp_related_posts.php
===================================================================
--- /afridex/plugins/wordpress-23-related-posts-plugin/wp_related_posts.php (revision 21)
+++ /afridex/plugins/wordpress-23-related-posts-plugin/wp_related_posts.php (revision 21)
@@ -0,0 +1,411 @@
+<?php
+/*
+Plugin Name: WordPress Related Posts
+Version: 0.9
+Plugin URI: http://fairyfish.net/2007/09/12/wordpress-23-related-posts-plugin/
+Description: Generate a related posts list via tags of WordPress
+Author: Denis
+Author URI: http://fairyfish.net/
+
+Copyright (c) 2007
+Released under the GPL license
+http://www.gnu.org/licenses/gpl.txt
+
+    This file is part of WordPress.
+    WordPress 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 2 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, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+	INSTALL: 
+	Just install the plugin in your blog and activate
+*/
+
+add_action('init', 'init_textdomain');
+function init_textdomain(){
+  load_plugin_textdomain('wp_related_posts',PLUGINDIR . '/' . dirname(plugin_basename (__FILE__)) . '/lang');
+}
+
+function wp_get_related_posts() {
+	global $wpdb, $post,$table_prefix;
+	$wp_rp = get_option("wp_rp");
+	
+	$exclude = explode(",",$wp_rp["wp_rp_exclude"]);
+	$limit = $wp_rp["wp_rp_limit"];
+	$wp_rp_title = $wp_rp["wp_rp_title"];
+	$wp_no_rp = $wp_rp["wp_no_rp"];
+	$wp_no_rp_text = $wp_rp["wp_no_rp_text"];
+	$show_date = $wp_rp["wp_rp_date"];
+	$show_comments_count = $wp_rp["wp_rp_comments"];
+	
+	if ( $exclude != '' ) {
+		$q = "SELECT tt.term_id FROM ". $table_prefix ."term_taxonomy tt, " . $table_prefix . "term_relationships tr WHERE tt.taxonomy = 'category' AND tt.term_taxonomy_id = tr.term_taxonomy_id AND tr.object_id = $post->ID";
+
+		$cats = $wpdb->get_results($q);
+		
+		foreach(($cats) as $cat) {
+			if (in_array($cat->term_id, $exclude) != false){
+				return;
+			}
+		}
+	}
+		
+	if(!$post->ID){return;}
+	$now = current_time('mysql', 1);
+	$tags = wp_get_post_tags($post->ID);
+
+	//print_r($tags);
+	
+	$taglist = "'" . $tags[0]->term_id. "'";
+	
+	$tagcount = count($tags);
+	if ($tagcount > 1) {
+		for ($i = 1; $i <= $tagcount; $i++) {
+			$taglist = $taglist . ", '" . $tags[$i]->term_id . "'";
+		}
+	}
+		
+	if ($limit) {
+		$limitclause = "LIMIT $limit";
+	}	else {
+		$limitclause = "LIMIT 10";
+	}
+	
+	$q = "SELECT DISTINCT p.ID, p.post_title, p.post_date, p.comment_count, count(t_r.object_id) as cnt FROM $wpdb->term_taxonomy t_t, $wpdb->term_relationships t_r, $wpdb->posts p WHERE t_t.taxonomy ='post_tag' AND t_t.term_taxonomy_id = t_r.term_taxonomy_id AND t_r.object_id  = p.ID AND (t_t.term_id IN ($taglist)) AND p.ID != $post->ID AND p.post_status = 'publish' AND p.post_date_gmt < '$now' GROUP BY t_r.object_id ORDER BY cnt DESC, p.post_date_gmt DESC $limitclause;";
+
+	//echo $q;
+
+	$related_posts = $wpdb->get_results($q);
+	$output = "";
+	
+	if (!$related_posts){
+		
+		if(!$wp_no_rp || ($wp_no_rp == "popularity" && !function_exists('akpc_most_popular'))) $wp_no_rp = "text";
+		
+		if($wp_no_rp == "text"){
+			if(!$wp_no_rp_text) $wp_no_rp_text= __("No Related Post",'wp_related_posts');
+			$output  .= '<li>'.$wp_no_rp_text .'</li>';
+		}	else{
+			if($wp_no_rp == "random"){
+				if(!$wp_no_rp_text) $wp_no_rp_text= __("Random Posts",'wp_related_posts');
+				$related_posts = wp_get_random_posts($limitclause);
+			}	elseif($wp_no_rp == "commented"){
+				if(!$wp_no_rp_text) $wp_no_rp_text= __("Most Commented Posts",'wp_related_posts');
+				$related_posts = wp_get_most_commented_posts($limitclause);
+			}	elseif($wp_no_rp == "popularity"){
+				if(!$wp_no_rp_text) $wp_no_rp_text= __("Most Popular Posts",'wp_related_posts');
+				$related_posts = wp_get_most_popular_posts($limitclause);
+			}else{
+				return __("Something wrong",'wp_related_posts');;
+			}
+			$wp_rp_title = $wp_no_rp_text;
+		}
+	}		
+		
+	foreach ($related_posts as $related_post ){
+		$output .= '<li>';
+		
+		if ($show_date){
+			$dateformat = get_option('date_format');
+			$output .=   mysql2date($dateformat, $related_post->post_date) . " -- ";
+		}
+		
+		$output .=  '<a href="'.get_permalink($related_post->ID).'" title="'.wptexturize($related_post->post_title).'">'.wptexturize($related_post->post_title).'';
+		
+		if ($show_comments_count){
+			$output .=  " (" . $related_post->comment_count . ")";
+		}
+		
+		$output .=  '</a></li>';
+	}
+	
+	$output = '<ul class="related_post">' . $output . '</ul>';
+		
+	if($wp_rp_title != '') $output =  '<h3>'.$wp_rp_title .'</h3>'. $output;
+	
+	return $output;
+}
+
+function wp_related_posts(){
+
+    global $id;
+    
+    $preview = $_GET['preview'];
+    
+    $output_old = get_post_meta($id, "related_posts", $single = true);
+    
+    if($output_old){
+      $time = time();
+      if(($time - $output_old["time"])<600){
+        echo $output_old["related_posts"];
+        return;
+      }
+    }
+		
+	$output = wp_get_related_posts() ;
+	
+  $output_new = array("time"=>time(),"related_posts"=>$output);
+    if($output_old){
+      update_post_meta($id, 'related_posts', $output_new);
+    }else{
+      if(!add_post_meta($id, 'related_posts', $output_new, true)){
+        update_post_meta($id, 'related_posts', $output_new);
+      }
+    }
+
+	echo $output;
+}
+
+function wp23_related_posts() {
+	wp_related_posts();
+}
+
+function wp_related_posts_for_feed($content=""){
+	$wp_rp = get_option("wp_rp");
+	$wp_rp_rss = ($wp_rp["wp_rp_rss"] == 'yes') ? 1 : 0;
+	if ( (! is_feed()) || (! $wp_rp_rss)) return $content;
+	
+	$output = wp_get_related_posts() ;
+	$content = $content . $output;
+	
+	return $content;
+}
+
+add_filter('the_content', 'wp_related_posts_for_feed',1);
+
+function wp_related_posts_auto($content=""){
+	$wp_rp = get_option("wp_rp");
+	$wp_rp_auto = ($wp_rp["wp_rp_auto"] == 'yes') ? 1 : 0;
+	if ( (! is_single()) || (! $wp_rp_auto)) return $content;
+	
+	$output = wp_get_related_posts() ;
+	$content = $content . $output;
+	
+	return $content;
+}
+
+add_filter('the_content', 'wp_related_posts_auto',99);
+
+function wp_get_random_posts ($limitclause="") {
+    global $wpdb, $tableposts, $post;
+		
+    $q = "SELECT ID, post_title, post_date, comment_count FROM $tableposts WHERE post_status = 'publish' AND post_type = 'post' AND ID != $post->ID ORDER BY RAND() $limitclause";
+    return $wpdb->get_results($q);
+}
+
+function wp_random_posts ($number = 10){
+	$limitclause="LIMIT " . $number;
+	$random_posts = wp_get_random_posts ($limitclause);
+	
+	foreach ($random_posts as $random_post ){
+		$output .= '<li>';
+		
+		$output .=  '<a href="'.get_permalink($random_post->ID).'" title="'.wptexturize($random_post->post_title).'">'.wptexturize($random_post->post_title).'</a></li>';
+	}
+	
+	$output = '<ul class="randome_post">' . $output . '</ul>';
+	
+	echo $output;
+}
+
+function wp_get_most_commented_posts($limitclause="") {
+	global $wpdb; 
+    $q = "SELECT ID, post_title, post_date, COUNT($wpdb->comments.comment_post_ID) AS 'comment_count' FROM $wpdb->posts, $wpdb->comments WHERE comment_approved = '1' AND $wpdb->posts.ID=$wpdb->comments.comment_post_ID AND post_status = 'publish' GROUP BY $wpdb->comments.comment_post_ID ORDER BY comment_count DESC $limitclause"; 
+    return $wpdb->get_results($q);
+} 
+
+function wp_most_commented_posts ($number = 10){
+	$limitclause="LIMIT " . $number;
+	$most_commented_posts = wp_get_most_commented_posts ($limitclause);
+	
+	foreach ($most_commented_posts as $most_commented_post ){
+		$output .= '<li>';
+		
+		$output .=  '<a href="'.get_permalink($most_commented_post->ID).'" title="'.wptexturize($most_commented_post->post_title).'">'.wptexturize($most_commented_post->post_title).'</a></li>';
+	}
+	
+	$output = '<ul class="most_commented_post">' . $output . '</ul>';
+	
+	echo $output;
+}
+
+function wp_get_most_popular_posts ($limitclause="") {
+    global $wpdb, $table_prefix;
+		
+    $q = $sql = "SELECT p.ID, p.post_title, p.post_date, p.comment_count FROM ". $table_prefix ."ak_popularity as akpc,".$table_prefix ."posts as p WHERE p.ID = akpc.post_id ORDER BY akpc.total DESC $limitclause";;
+    return $wpdb->get_results($q);
+}
+
+function wp_most_popular_posts ($number = 10){
+	$limitclause="LIMIT " . $number;
+	$most_popular_posts = wp_get_most_popular_posts ($limitclause);
+	
+	foreach ($most_popular_posts as $most_popular_post ){
+		$output .= '<li>';
+		
+		$output .=  '<a href="'.get_permalink($most_popular_post->ID).'" title="'.wptexturize($most_popular_post->post_title).'">'.wptexturize($most_popular_post->post_title).'</a></li>';
+	}
+	
+	$output = '<ul class="most_popular_post">' . $output . '</ul>';
+	
+	echo $output;
+}
+
+add_action('admin_menu', 'wp_add_related_posts_options_page');
+
+function wp_add_related_posts_options_page() {
+	if (function_exists('add_options_page')) {
+		add_options_page( __('WordPress Related Posts','wp_related_posts'), __('WordPress Related Posts','wp_related_posts'), 8, basename(__FILE__), 'wp_related_posts_options_subpanel');
+	}
+}
+
+function wp_related_posts_options_subpanel() {
+	if($_POST["wp_rp_Submit"]){
+		$message = "WordPress Related Posts Setting Updated";
+	
+		$wp_rp_saved = get_option("wp_rp");
+	
+		$wp_rp = array (
+			"wp_rp_title" 	=> $_POST['wp_rp_title_option'],
+			"wp_no_rp"		=> $_POST['wp_no_rp_option'],
+			"wp_no_rp_text"	=> $_POST['wp_no_rp_text_option'],
+			"wp_rp_limit"	=> $_POST['wp_rp_limit_option'],
+			'wp_rp_exclude'	=> $_POST['wp_rp_exclude_option'],
+			'wp_rp_auto'	=> $_POST['wp_rp_auto_option'],
+			'wp_rp_rss'		=> $_POST['wp_rp_rss_option'],
+			'wp_rp_comments'=> $_POST['wp_rp_comments_option'],
+			'wp_rp_date'	=> $_POST['wp_rp_date_option']
+		);
+		
+		if ($wp_rp_saved != $wp_rp)
+			if(!update_option("wp_rp",$wp_rp))
+				$message = "Update Failed";
+		
+		echo '<div id="message" class="updated fade"><p>'.$message.'.</p></div>';
+	}
+	
+	$wp_rp = get_option("wp_rp");
+?>
+    <div class="wrap">
+        <h2 id="write-post"><?php _e("Related Posts Options&hellip;",'wp_related_posts');?></h2>
+        <p><?php _e("WordPress Related Posts Plugin will generate a related posts via WordPress tags, and add the related posts to feed.",'wp_related_posts');?></p>
+        <div style="float:right;">
+          <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
+          <input type="hidden" name="cmd" value="_donations">
+          <input type="hidden" name="business" value="honghua.deng@gmail.com">
+          <input type="hidden" name="item_name" value="Donate to fairyfish.net">
+          <input type="hidden" name="no_shipping" value="0">
+          <input type="hidden" name="no_note" value="1">
+          <input type="hidden" name="currency_code" value="USD">
+          <input type="hidden" name="tax" value="0">
+          <input type="hidden" name="lc" value="US">
+          <input type="hidden" name="bn" value="PP-DonationsBF">
+          <input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
+          <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1"><br />
+          </form>
+        </div>
+        <h3><?php _e("Related Posts Preference",'wp_related_posts');?></h3>
+        <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo basename(__FILE__); ?>">
+        
+        <table class="form-table">
+          <tr>
+            <th><?php _e("Related Posts Title:",'wp_related_posts'); ?></th>
+            <td>
+              <input type="text" name="wp_rp_title_option" value="<?php echo $wp_rp["wp_rp_title"]; ?>" />
+            </td>
+          </tr>
+          <tr>
+            <th><?php _e("When No Related Posts, Dispaly:",'wp_related_posts'); ?></th>
+            <td>
+              <?php $wp_no_rp = $wp_rp["wp_no_rp"]; ?>
+              <select name="wp_no_rp_option" >
+              <option value="text" <?php if($wp_no_rp == 'text') echo 'selected' ?> ><?php _e("Text: 'No Related Posts'",'wp_related_posts'); ?></option>
+              <option value="random" <?php if($wp_no_rp == 'random') echo 'selected' ?>><?php _e("Random Posts",'wp_related_posts'); ?></option>
+              <option value="commented" <?php if($wp_no_rp == 'commented') echo 'selected' ?>><?php _e("Most Commented Posts",'wp_related_posts'); ?></option>
+              <?php if (function_exists('akpc_most_popular')){ ?>
+              <option value="popularity" <?php if($wp_no_rp == 'popularity') echo 'selected' ?>><?php _e("Most Popular Posts",'wp_related_posts'); ?></option>
+              <?php } ?> 
+              </select>
+            </td>
+          </tr>
+          <tr>
+            <th><?php _e("No Related Post's Title or Text:",'wp_related_posts'); ?></th>
+            <td>
+              <input type="text" name="wp_no_rp_text_option" value="<?php echo $wp_rp["wp_no_rp_text"]; ?>" />
+            </td>
+          </tr>
+          <tr>
+            <th><?php _e("Limit:",'wp_related_posts');?></th>
+            <td>
+              <input type="text" name="wp_rp_limit_option" value="<?php echo $wp_rp["wp_rp_limit"]; ?>" />
+            </td>
+          </tr>
+          <tr>
+            <th><?php _e("Exclude(category IDs):",'wp_related_posts');?></th>
+            <td>
+              <input type="text" name="wp_rp_exclude_option" value="<?php echo $wp_rp["wp_rp_exclude"]; ?>" />
+            </td>
+          </tr>
+          <tr>
+            <th><?php _e("Other Setting:",'wp_related_posts');?></th>
+            <td>
+              <label>
+              <?php
+              if ( $wp_rp["wp_rp_auto"] == 'yes' ) {
+              echo '<input name="wp_rp_auto_option" type="checkbox" value="yes" checked>';
+              } else {
+              echo '<input name="wp_rp_auto_option" type="checkbox" value="yes">';
+              }
+              ?>
+              <?php _e("Auto Insert Related Posts",'wp_related_posts');?>
+              </label>
+              <br />
+              <label>
+              <?php
+              if ( $wp_rp["wp_rp_rss"] == 'yes' ) {
+                echo '<input name="wp_rp_rss_option" type="checkbox" value="yes" checked>';
+              } else {
+                echo '<input name="wp_rp_rss_option" type="checkbox" value="yes">';
+              }
+              ?>
+              <?php _e("Related Posts for RSS",'wp_related_posts');?>
+              </label>
+              <br />
+              <label>
+              <?php
+              if ( $wp_rp["wp_rp_comments"] == 'yes' ) {
+                echo '<input name="wp_rp_comments_option" type="checkbox" value="yes" checked>';
+              } else {
+                echo '<input name="wp_rp_comments_option" type="checkbox" value="yes">';
+              }
+              ?>
+              <?php _e("Display Comments Count",'wp_related_posts');?>
+              </label>
+              <br />
+              <label>
+              <?php
+              if ( $wp_rp["wp_rp_date"] == 'yes' ) {
+                echo '<input name="wp_rp_date_option" type="checkbox" value="yes" checked>';
+              } else {
+                echo '<input name="wp_rp_date_option" type="checkbox" value="yes">';
+              }
+              ?>
+              <?php _e("Display Post Date",'wp_related_posts');?>
+              </label>
+              <br />
+            </td>
+          </tr>
+        </table>
+        <p class="submit"><input type="submit" value="<?php _e("Update Preferences &raquo;",'wp_related_posts');?>" name="wp_rp_Submit" /></p>
+        </form>
+      </div>
+<?php }?>
Index: /afridex/plugins/wordpress-23-related-posts-plugin/readme.txt
===================================================================
--- /afridex/plugins/wordpress-23-related-posts-plugin/readme.txt (revision 21)
+++ /afridex/plugins/wordpress-23-related-posts-plugin/readme.txt (revision 21)
@@ -0,0 +1,31 @@
+=== Plugin Name ===
+Contributors: denishua
+Tags: Related,Posts
+Donate link: http://fairyfish.net/donate/
+Requires at least: 2.3
+Tested up to: 2.5
+Stable tag: 0.9
+
+WordPress Related Posts Plugin will generate a related posts via WordPress tags, and add the related posts to feed.
+
+== Description ==
+
+WordPress Related Posts Plugin will generate a related posts via WordPress tags, and add the related posts to feed.
+
+Please search and submit your transaltion here: <a href="http://fairyfish.net/2008/06/06/wordpress-related-posts-plugin-translation/">http://fairyfish.net/2008/06/06/wordpress-related-posts-plugin-translation/</a> 
+
+== Installation ==
+
+1. Upload the folder WPRP to the `/wp-content/plugins/` directory
+1. Activate the plugin through the 'Plugins' menu in WordPress
+1. Place `<?php wp_related_posts(); ?>` in your templates
+1. Navigate to Manage > Option > WordPress Related Posts to configure plugin output.
+
+
+== Upgrade ==
+
+1. Delete the old plugin folder `WP23RP`.
+1. Upload the folder WPRP to the `/wp-content/plugins/` directory
+1. Activate the plugin through the 'Plugins' menu in WordPress
+1. Place `<?php wp_related_posts(); ?>` in your templates
+1. Navigate to Manage > Option > WordPress Related Posts to configure plugin output.
Index: /afridex/plugins/rb-internal-links/rb-internal-links.php
===================================================================
--- /afridex/plugins/rb-internal-links/rb-internal-links.php (revision 21)
+++ /afridex/plugins/rb-internal-links/rb-internal-links.php (revision 21)
@@ -0,0 +1,291 @@
+<?php
+/*
+Plugin Name: RB Internal Links
+Version: 0.21
+Plugin URI: http://www.blograndom.com/blog/extras/
+Author: Cohen
+Author URI: http://www.blograndom.com/
+Description: Use wiki style tags to link to internal posts and pages within your blog without hardcoding them.
+
+Installation: See readme.
+
+*/
+
+$rbinternal_version = '0.21';
+
+// set up important actions and filters 
+add_filter('the_content', 'rbinternal_parse_content', 1);
+add_filter('the_content_rss', 'rbinternal_parse_content', 1);
+add_filter('the_excerpt', 'rbinternal_parse_content', 1);
+add_action('init', 'rbinternal_init');
+add_action('admin_head', 'rbinternal_admin_header', 10);
+add_action('admin_menu', 'rbinternal_add_pages');
+add_filter('tiny_mce_version', 'rbinternal_refresh_mce'); // update tinymce cache	
+
+
+$rbinternal_url = get_settings('siteurl').'/wp-content/plugins/rb-internal-links/';
+
+function rbinternal_init(){
+
+	global $rbinternal_version;
+	$current_version = get_option('rbinternal_version');
+	
+	if($rbinternal_version != $current_version){
+		rbinternal_update($rbinternal_version, $current_version);
+		update_option('rbinternal_version', $rbinternal_version);
+	}
+	
+
+	rbinternal_addbuttons();
+	
+}
+
+function rbinternal_update($version, $current_version){
+	
+	// set some default options
+	add_option('rbinternal_tinymce', 1);
+	add_option('rbinternal_post_orderby', 'post_date');
+	add_option('rbinternal_post_sort', 'DESC');
+	add_option('rbinternal_page_orderby', 'post_title');
+	add_option('rbinternal_page_sort', 'ASC');
+	add_option('rbinternal_return_par	am', 'ID');
+		
+}
+
+function rbinternal_refresh_mce($ver) {
+  $ver += 36;
+  return $ver;
+}
+
+// this function gets the url based on post id or slug
+function rbinternal_get_url($id, &$text, $type) {
+	global $wpdb;
+	
+	if(empty($id)) return false;
+	
+	switch($type){
+		case 'post':
+			$field = (is_numeric($id))? 'ID' : 'post_name';
+			$post = $wpdb->get_row("SELECT ID, post_title FROM $wpdb->posts WHERE $field = '$id'");
+			if(empty($post->post_title)) return false;
+			elseif(empty($text)) $text = $post->post_title;
+			
+			return get_permalink($post->ID);
+		case 'category':
+			return get_category_link($id);
+		default:
+			return false;
+	}
+	
+}
+
+// posts and content get sent to this function which will look for our bbcode
+function rbinternal_parse_content($content) {
+
+	if(strpos($content, "{{post") !== FALSE OR strpos($content, "<!--intlink") !== FALSE)
+		$content = preg_replace("/({{|<!--)(post|intlink)([^}}].*?|[^-->].*?)(}}|-->)/ei", "rbinternal_parse_params('\\2', '\\3')", $content);
+		// {{ * }} for backwards compatibility
+		
+	return $content;
+	
+}
+
+function rbinternal_parse_params($verb, $paramStr){
+
+		$paramStr = stripslashes($paramStr);
+  	$paramStr = str_replace('&quot;', '"', $paramStr);
+  	$paramStr = str_replace("&#8221;", '"', $paramStr);
+		
+		preg_match_all("/(\w+)\=\"([^\"].*?)\"/i", $paramStr, $matches);
+		
+		if(is_array($matches[1]) AND is_array($matches[2]))
+			foreach($matches[1] AS $i=>$key)
+				$params[$key] = isset($matches[2][$i])? $matches[2][$i] : false;
+		
+		return rbinternal_render_content($verb, $params);
+
+}
+
+function rbinternal_render_content($verb, $params){
+	
+	if(!isset($params['type'])) $params['type'] = 'post';
+	
+	switch($verb){
+		case 'intlink':
+		case 'post': //backwards compatibility
+			if(!isset($params['id'])) return false;
+			if(!isset($params['text'])) $params['text'] = 0;
+			$html = '<a href="'. rbinternal_get_url($params['id'], $params['text'], $params['type']) . ((isset($params['anchor']))? '#' . $params['anchor'] : '') . '" '; 
+			if(isset($params['class'])) $html .= 'class="'. $params['class'] . '" ';
+			if(isset($params['target'])) $html .= 'target="'. $params['target'] . '" '; 
+			$html .= '>'. $params['text'] .'</a>';
+			
+			return $html;
+		default:
+			return '[rbinternal code not found]';
+	}
+}
+
+
+// tinyMCE functions
+function rbinternal_addbuttons() {    
+	global $wp_db_version;    
+	// Check for WordPress 2.5+ and that its turned on
+	if($wp_db_version >= 7098 AND get_option('rbinternal_tinymce') == 1){
+		// Don't bother doing this stuff if the current user lacks permissions
+	   if ( ! current_user_can('edit_posts') && ! current_user_can('edit_pages') )
+	     return;
+	 
+	   // Add only in Rich Editor mode
+	   if ( get_user_option('rich_editing') == 'true') {
+	     add_filter("mce_external_plugins", "rbinternal_external_plugins_25");
+	     add_filter('mce_buttons', 'rbinternal_mce_buttons');
+	     add_filter("mce_css", "rbinternal_mce_css");
+	   }
+	// Check for WordPress 2.1+ and that its turned on
+	}elseif(3664 <= $wp_db_version AND get_option('rbinternal_tinymce') == 1){  
+		if ('true' == get_user_option('rich_editing')) {
+		add_filter("mce_plugins", "rbinternal_mce_plugins", 10);
+		add_filter("mce_buttons", "rbinternal_mce_buttons", 10);
+		add_action('tinymce_before_init','rbinternal_external_plugins');
+		}
+	}
+}
+// pre v2.5 tinymce plugin load
+function rbinternal_mce_plugins($plugins) {    
+	array_push($plugins, "-rbinternallinks");    
+	return $plugins;
+}
+// pre 2.5 plugin load
+function rbinternal_external_plugins() {	
+	global $rbinternal_url;
+	echo 'tinyMCE.loadPlugin("rbinternallinks", "'.$rbinternal_url.'tmce/rb-internal-links/");' . "\n"; 
+	return;
+}
+// Load the TinyMCE plugin : editor_plugin.js (wp2.5)
+function rbinternal_external_plugins_25($plugin_array) {
+	global $rbinternal_url;
+   $plugin_array['rbinternallinks'] = $rbinternal_url.'tmce/rb-internal-links/editor_plugin_25.js';
+   return $plugin_array;
+}
+// 2.5 + pre 2.5 button load
+function rbinternal_mce_buttons($buttons) {
+	array_push($buttons, "separator", "rbinternallinks");
+	return $buttons;
+}
+// 2.5 + mce css load
+function rbinternal_mce_css($css){
+	global $rbinternal_url;
+	return $css . ',' . $rbinternal_url.'styles.css';
+}
+
+
+
+function rbinternal_admin_header(){
+	global $rbinternal_url;
+	echo '<script language="JavaScript" type="text/javascript">' . "\n" . '// <![CDATA[ ' . "\n";
+	echo 'var rbinternal_url="'. $rbinternal_url .'";' . "\n";
+	echo '// ]]>' . "\n" . '</script>';
+}
+
+function rbinternal_add_pages(){
+	add_options_page('RB Internal Links', 'RB Internal Links', 8, __FILE__, 'rbinternal_admin_page');
+}
+
+function rbinternal_admin_page(){
+
+	if( $_POST['rbinternal_submit'] == 'Y' ) {
+    // Read their posted value
+		$rbinternal_tinymce = isset($_REQUEST['rbinternal_tinymce'])? $_REQUEST['rbinternal_tinymce'] : 0;
+    $rbinternal_post_orderby = $_REQUEST['rbinternal_post_orderby'];
+    $rbinternal_post_sort = $_REQUEST['rbinternal_post_sort'];
+    $rbinternal_page_orderby = $_REQUEST['rbinternal_page_orderby'];
+ 		$rbinternal_page_sort = $_REQUEST['rbinternal_page_sort'];
+		$rbinternal_return_param = $_REQUEST['rbinternal_return_param'];
+		
+		// Save the posted value in the database
+    update_option('rbinternal_tinymce', $rbinternal_tinymce);
+    update_option('rbinternal_post_orderby', $rbinternal_post_orderby);
+    update_option('rbinternal_post_sort', $rbinternal_post_sort);
+    update_option('rbinternal_page_orderby', $rbinternal_page_orderby);
+    update_option('rbinternal_page_sort', $rbinternal_page_sort);
+		update_option('rbinternal_return_param', $rbinternal_return_param);
+
+    // Put an options updated message on the screen
+?>
+<div class="updated"><p><strong>Settings updated.</strong></p></div>
+<?php
+    }else{
+      $rbinternal_tinymce = get_option('rbinternal_tinymce');
+      $rbinternal_post_orderby = get_option('rbinternal_post_orderby');
+      $rbinternal_post_sort = get_option('rbinternal_post_sort');
+      $rbinternal_page_orderby = get_option('rbinternal_page_orderby');
+ 			$rbinternal_page_sort = get_option('rbinternal_page_sort');
+			$rbinternal_return_param = get_option('rbinternal_return_param');
+	}
+?>
+
+<div class="wrap">
+<h2>RB Internal Link Settings</h2>
+<form name="form1" method="post" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI']); ?>">
+<input type="hidden" name="rbinternal_submit" value="Y">
+
+<h3>General</h3>
+<ul>
+<li><input type="checkbox" name="rbinternal_tinymce" value="1" <?php if($rbinternal_tinymce == 1) echo 'checked="checked"'; ?>> Enable wysiwyg editor plugin.</li>
+<li>
+	Return the 
+	<select name="rbinternal_return_param">
+	<option value="ID" <?php if($rbinternal_return_param == 'ID') echo 'selected="selected"'; ?>>article ID</option>
+	<option value="slug" <?php if($rbinternal_return_param == 'slug') echo 'selected="selected"'; ?>>article slug</option>
+	</select>
+	to the editor
+</li>
+<li>
+	Order the list of posts by 
+	<select name="rbinternal_post_orderby">
+	<option value="post_date" <?php if($rbinternal_post_orderby == 'post_date') echo 'selected="selected"'; ?>>Post Date</option>
+	<option value="post_title" <?php if($rbinternal_post_orderby == 'post_title') echo 'selected="selected"'; ?>>Post Title</option>
+	<option value="post_modified" <?php if($rbinternal_post_orderby == 'post_modified') echo 'selected="selected"'; ?>>Modified Date</option>
+	<option value="ID" <?php if($rbinternal_post_orderby == 'ID') echo 'selected="selected"'; ?>>Post ID</option>
+	<option value="post_author" <?php if($rbinternal_post_orderby == 'post_author') echo 'selected="selected"'; ?>>Post Author</option>
+	<option value="post_name" <?php if($rbinternal_post_orderby == 'post_name') echo 'selected="selected"'; ?>>Post Slug</option>
+	</select> 
+	and sort 
+	<select name="rbinternal_post_sort">
+	<option value="ASC" <?php if($rbinternal_post_sort == 'ASC') echo 'selected="selected"'; ?>>Ascending</option>
+	<option value="DESC" <?php if($rbinternal_post_sort == 'DESC') echo 'selected="selected"'; ?>>Descending</option>
+	</select>
+</li>
+<li>
+	Order the list of pages by 
+	<select name="rbinternal_page_orderby">
+	<option value="post_date" <?php if($rbinternal_page_orderby == 'post_date') echo 'selected="selected"'; ?>>Post Date</option>
+	<option value="post_title" <?php if($rbinternal_page_orderby == 'post_title') echo 'selected="selected"'; ?>>Post Title</option>
+	<option value="post_modified" <?php if($rbinternal_page_orderby == 'post_modified') echo 'selected="selected"'; ?>>Modified Date</option>
+	<option value="ID" <?php if($rbinternal_page_orderby == 'ID') echo 'selected="selected"'; ?>>Post ID</option>
+	<option value="post_author" <?php if($rbinternal_page_orderby == 'post_author') echo 'selected="selected"'; ?>>Post Author</option>
+	<option value="post_name" <?php if($rbinternal_page_orderby == 'post_name') echo 'selected="selected"'; ?>>Post Slug</option>
+	</select> 
+	and sort 
+	<select name="rbinternal_page_sort">
+	<option value="ASC" <?php if($rbinternal_page_sort == 'ASC') echo 'selected="selected"'; ?>>Ascending</option>
+	<option value="DESC" <?php if($rbinternal_page_sort == 'DESC') echo 'selected="selected"'; ?>>Descending</option>
+	</select>
+</li>
+</ul>
+
+<p class="submit">
+<input type="submit" name="Submit" value="Update Options" />
+</p>
+
+<h2>Debug:</h2>
+<p>DB Version: <?php global $wp_db_version; echo $wp_db_version; ?></p>
+<p>Rich Editing Enabled: <?php if ('true' == get_user_option('rich_editing')) echo 'Yes'; else echo 'No'; ?></p>
+<p>Plugin wysiwyg enabled: <?php if (get_option('rbinternal_tinymce') == 1) echo 'Yes'; else echo 'No'; ?></p>
+
+</form>
+</div>
+<?php
+}
+?>
Index: /afridex/plugins/rb-internal-links/tmce/rb-internal-links/editor_plugin.js
===================================================================
--- /afridex/plugins/rb-internal-links/tmce/rb-internal-links/editor_plugin.js (revision 21)
+++ /afridex/plugins/rb-internal-links/tmce/rb-internal-links/editor_plugin.js (revision 21)
@@ -0,0 +1,110 @@
+var TinyMCE_RBInternalPlugin = {
+	getInfo : function() {
+		return {
+			longname : 'RBInternalPlugin',
+			author : 'Cohen',
+			authorurl : 'http://www.blograndom.com',
+			infourl : '',
+			version : "1.0"
+		};
+	},
+	getControlHTML : function(cn) {
+		switch (cn) {
+			case "rbinternallinks":
+				var button = '<a id="mce_editor_0_rbinternallinks" href="javascript:tinyMCE.execInstanceCommand(\'{$editor_id}\',\'mceRBInternalLinkInsert\');" onclick="tinyMCE.execInstanceCommand(\'{$editor_id}\',\'mceRBInternalLinkInsert\');return false;" onmousedown="return false;" class="mceButtonNormal" target="_self"><img src="{$pluginurl}/images/icon.gif" title="RB Internal Links"></a>';
+				return button;
+		}
+		return "";
+	},
+	cleanup : function(type, content) {
+		switch (type) {
+
+			case "insert_to_editor":
+				var startPos = 0;
+				
+				// Parse all <!--intlink * --> tags and replace them with images
+				while ((startPos = content.indexOf('<!--intlink', startPos)) != -1) {
+					var endPos = content.indexOf('-->', startPos) + 3;
+					// Insert code
+					var linkCode = content.substring(startPos+11, endPos-3);
+					// Find link text
+					var textStartPos = linkCode.indexOf('text="', 0);
+					var textEndPos = linkCode.indexOf('"', textStartPos+6);
+					var linkText = linkCode.substring(textStartPos+6, textEndPos);
+					// Get the rest of the content
+					var contentAfter = content.substring(endPos);
+					// Put it all together
+					content = content.substring(0, startPos);
+					content += '<span class="rbIntLinkText">';
+					content += linkText;
+					content += '<!--pintlink' + linkCode + '-->';
+					content += '</span>';
+					content += contentAfter;
+
+					startPos++;
+				}
+				var startPos = 0;
+
+				break;
+
+			case "get_from_editor":
+				// Parse all parsed internal links back to <!--intlink * -->
+				var startPos = -1;
+				while ((startPos = content.indexOf('<span class="rbIntLinkText">', startPos+1)) != -1) {
+					var endPos = content.indexOf('--></span>', startPos);
+						
+						nContent = content.substring(0, startPos);
+						chunkAfter = content.substring(endPos+10);
+						
+						if((codeStartPos = content.indexOf('<!--pintlink', startPos+28)) != -1){ // start position + span code
+							codeEndPos = endPos; // end position
+							linkCode = content.substring(codeStartPos+12, codeEndPos);
+						}else
+							linkCode = 'error';
+						
+						nContent += '<!--intlink' + linkCode + '-->';
+						
+						nContent += chunkAfter;
+						content = nContent;
+						
+				}
+
+				break;
+		}
+
+		// Pass through to next handler in chain
+		return content;
+	},
+	initInstance : function(inst) {
+		tinyMCE.importCSS(inst.getDoc(), rbinternal_url + 'styles.css');
+	},
+	execCommand : function(editor_id, element, command, user_interface, value) {
+		switch (command) {
+			case "mceRBInternalLinkInsert":
+			
+				var selectedHTML = tinyMCE.getInstanceById(editor_id).selection.getSelectedHTML();
+				
+				var template = new Array();
+
+				template['file']   = rbinternal_url + 'tmce_plugin.php?linktext=' + escape(selectedHTML);
+				template['width']  = 355;
+				template['height'] = 400;
+
+				// Language specific width and height addons
+				template['width']  += tinyMCE.getLang('lang_advimage_delta_width', 0);
+				template['height'] += tinyMCE.getLang('lang_advimage_delta_height', 0);
+
+				var inst = tinyMCE.getInstanceById(editor_id);
+				var elm = inst.getFocusElement();
+
+				if (elm != null && tinyMCE.getAttrib(elm, 'class').indexOf('mceItem') != -1)
+					return true;
+
+				tinyMCE.openWindow(template, {editor_id : editor_id, inline : "yes"});
+
+			return true;
+		}
+		return false;
+	}
+};
+tinyMCE.addPlugin("rbinternallinks", TinyMCE_RBInternalPlugin);
Index: /afridex/plugins/rb-internal-links/tmce/rb-internal-links/langs/en.js
===================================================================
--- /afridex/plugins/rb-internal-links/tmce/rb-internal-links/langs/en.js (revision 21)
+++ /afridex/plugins/rb-internal-links/tmce/rb-internal-links/langs/en.js (revision 21)
@@ -0,0 +1,4 @@
+// English lang variables  
+tinyMCE.addToLang('', {  
+	ril_desc : "Insert an internal link" 
+});
Index: /afridex/plugins/rb-internal-links/tmce/rb-internal-links/editor_plugin_25.js
===================================================================
--- /afridex/plugins/rb-internal-links/tmce/rb-internal-links/editor_plugin_25.js (revision 21)
+++ /afridex/plugins/rb-internal-links/tmce/rb-internal-links/editor_plugin_25.js (revision 21)
@@ -0,0 +1,134 @@
+(function() {
+	// Load plugin specific language pack
+	//tinymce.PluginManager.requireLangPack('example');
+
+	tinymce.create('tinymce.plugins.rbinternallinks', {
+		/**
+		 * Initializes the plugin, this will be executed after the plugin has been created.
+		 * This call is done before the editor instance has finished it's initialization so use the onInit event
+		 * of the editor instance to intercept that event.
+		 *
+		 * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
+		 * @param {string} url Absolute URL to where the plugin is located.
+		 */
+		init : function(ed, url) {
+			// Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample');
+			ed.addCommand('rbinternallinks', function() {
+				ed.windowManager.open({
+					file : url + '../../../tmce_plugin.php?linktext=' + escape(tinyMCE.activeEditor.selection.getContent()),
+					width : 355,
+					height : 400,
+					inline : 1
+				}, {
+					plugin_url : url // Plugin absolute URL
+				});
+			});
+
+			// Register example button
+			ed.addButton('rbinternallinks', {
+				title : 'RB Internal Links',
+				cmd : 'rbinternallinks',
+				image : url + '/images/icon.gif'
+			});
+
+			// Add a node change handler, selects the button in the UI when a image is selected
+			ed.onNodeChange.add(function(ed, cm, n) {
+				//cm.setActive('example', n.nodeName == 'IMG');
+			});
+			
+			// Replace morebreak with images
+			ed.onBeforeSetContent.add(function(ed, o) {
+				
+				var startPos = 0;
+				
+				// Parse all <!--intlink * --> tags and replace them with images
+				while ((startPos = o.content.indexOf('<!--intlink', startPos)) != -1) {
+					var endPos = o.content.indexOf('-->', startPos) + 3;
+					// Insert code
+					var linkCode = o.content.substring(startPos+11, endPos-3);
+					// Find link text
+					var textStartPos = linkCode.indexOf('text="', 0);
+					var textEndPos = linkCode.indexOf('"', textStartPos+6);
+					var linkText = linkCode.substring(textStartPos+6, textEndPos);
+					// Get the rest of the content
+					var contentAfter = o.content.substring(endPos);
+					// Put it all together
+					o.content = o.content.substring(0, startPos);
+					o.content += '<span class="rbIntLinkText">';
+					o.content += linkText;
+					o.content += '<!--pintlink' + linkCode + '-->';
+					o.content += '</span>';
+					o.content += contentAfter;
+
+					startPos++;
+				}
+				var startPos = 0;
+
+			});
+
+			// Replace images with morebreak
+			ed.onPostProcess.add(function(ed, o) {
+				if (o.get){
+					// Parse all parsed internal links back to <!--intlink * -->
+					var startPos = -1;
+					var rbinternal_i=0;
+					while ((startPos = o.content.indexOf('<span class="rbIntLinkText">', startPos+1)) != -1) {
+						rbinternal_i=rbinternal_i+1;
+						var endPos = o.content.indexOf('--></span>', startPos);
+							
+							nContent = o.content.substring(0, startPos);
+							chunkAfter = o.content.substring(endPos+10);
+							
+							if((codeStartPos = o.content.indexOf('<!--pintlink', startPos+28)) != -1){ // start position + span code
+								codeEndPos = endPos; // end position
+								linkCode = o.content.substring(codeStartPos+12, codeEndPos);
+							}else
+								linkCode = 'error';
+							
+							nContent += '<!--intlink' + linkCode + '-->';
+							
+							nContent += chunkAfter;
+							o.content = nContent;
+					
+						if(rbinternal_i > 500){ alert('RB Internal Links: Javascript seems to be stuck in a loop, exiting processing.'); break; }
+							
+					}
+				}
+			});
+		},
+
+		/**
+		 * Creates control instances based in the incomming name. This method is normally not
+		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
+		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
+		 * method can be used to create those.
+		 *
+		 * @param {String} n Name of the control to create.
+		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
+		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
+		 */
+		createControl : function(n, cm) {
+			return null;
+		},
+
+
+		/**
+		 * Returns information about the plugin as a name/value array.
+		 * The current keys are longname, author, authorurl, infourl and version.
+		 *
+		 * @return {Object} Name/value array containing information about the plugin.
+		 */
+		getInfo : function() {
+			return {
+				longname : 'RB Internal Links',
+				author : 'Cohen',
+				authorurl : 'http://www.blograndom.com',
+				infourl : 'http://www.blograndom.com/blog/extras/rb-internal-links-plugin/',
+				version : "1.0"
+			};
+		}
+	});
+
+	// Register plugin
+	tinymce.PluginManager.add('rbinternallinks', tinymce.plugins.rbinternallinks);
+})();
Index: /afridex/plugins/rb-internal-links/styles.css
===================================================================
--- /afridex/plugins/rb-internal-links/styles.css (revision 21)
+++ /afridex/plugins/rb-internal-links/styles.css (revision 21)
@@ -0,0 +1,4 @@
+.rbIntLinkText{
+	background-color: #FFFFAA;
+	border: 1px #FFFFAA solid;
+}
Index: /afridex/plugins/rb-internal-links/readme.txt
===================================================================
--- /afridex/plugins/rb-internal-links/readme.txt (revision 21)
+++ /afridex/plugins/rb-internal-links/readme.txt (revision 21)
@@ -0,0 +1,76 @@
+=== RB Internal Links ===
+Contributors: cohen
+Donate link: http://www.blograndom.com
+Tags: links, posts, slugs, permalinks
+Requires at least: 2.1
+Tested up to: 2.5
+Stable tag: 0.21
+
+Link to internal posts and pages in your blog in a similar to style to wiki. No need to remember full URLs, post ids or slugs. Wysiwyg plugin to help find previous posts and pages. 
+
+== Description ==
+
+An easy way to link to other posts and articles in your blog. As well as quickly typing in the link tag there is a tinymce wysiwyg 
+plugin that allows you to search for posts for category.
+
+I saw a need for this plugin and thought I'd give it a go. If you would like to help improve this plugin please contact me via my blog.
+
+== Installation ==
+
+1. Upload the "rb-internal-links" folder to your wp-content/plugins/ directory.
+2. Activate the plug-in
+3. Browse to Options > RB Internal Links to take a look at preferences
+4. Start internally linking
+
+== Usage ==
+
+For more information visit http://www.blograndom.com/blog/extras/rb-internal-link-plugin/
+
+To manually link a page:
+
+	At the place you would like the link to appear write ( ignoring the ticks - i.e. ` ):
+		`<!--intlink id="post-slug" text="link text"-->` OR `<!--post id="post-id" text="link text"-->`
+	...exchanging post-slug or post-id for the post or page slug/id you would like to link to.
+
+	Note:
+	- text is the text or html you would like to put within the anchor tag and should be ok with anything other than { or ".
+	- you can specify a class for the link using ... `class="my_class"` ...
+	- you can specify a target for the link using ... `target="_new"` ...
+	- you can put html in to the text parameter (e.g. <img src='myimage.gif' />), just don't use " (quote marks) in the html
+	
+Using the tinymce wysiwyg editor:
+
+	Make sure you've ticked "enable wysiwyg editor" on the plugin preferences page.
+	A new icon will appear on the wysiwyg toolbar, it looks like a page with a link over it
+	Select the text you wish to turn in to a link (optional)
+	Click on the icon, a toolbox should popup
+	Select the category of the post you need or alternatively choose pages for a list of pages
+	Click on the post or page you wish to link to
+	Fill in the optional properties for the link
+	Click "Insert Link"
+	
+== To do ==
+- Made a <!--extlink for items in the blog roll
+- Take changes from the highlighted text in Visual mode and apply them to the link code
+
+== Change Log ==
+v0.15 (27/01/2008)
+- Fixed bug with visual mode insertion hiding newly inserted link
+- Added anchor parameter for linking to anchors within pages
+v0.14 (19/12/2007)
+- Changed CDATA comment tags from /* */ to //, which should resolve issues with the tinymce icon
+v0.13 (23/10/2007)
+- Changed charset to utf-8 for tinymce plugin (thanks to vanco)
+- Put in option for ID or slug returned to editor, default to be ID (good idea, thanks again vanco)
+- Support for creating an internal link with an image
+- The intlink code is now hidden in visual mode, replaced with the highlighted text
+- The parsing engine has been completely rewritten for speed and scalability
+- Some code changes that should help people with problems related to the wysiwyg icon not turning up
+- Added filter for 'the_content_rss'
+
+v0.12 (21/10/2007)
+- Stopped selected text with quote marks in from breaking the wysiwyg plugin
+
+v0.11  (19/10/2007)
+- Updating some spelling mistakes
+- Added debug information for missing wysiwyg icon
Index: /afridex/plugins/rb-internal-links/tmce_plugin.php
===================================================================
--- /afridex/plugins/rb-internal-links/tmce_plugin.php (revision 21)
+++ /afridex/plugins/rb-internal-links/tmce_plugin.php (revision 21)
@@ -0,0 +1,182 @@
+<?php 
+
+include("../../../wp-config.php");
+$linktext = isset($_REQUEST['linktext'])? str_replace('\"', '\'', $_REQUEST['linktext']) : ''; // get preselected html and remove "'s
+
+$action = $_REQUEST['action'];
+switch($action){
+	case 'get_pages':
+		$returnDiv = $_REQUEST['returnDiv'];
+		$category = $_REQUEST['category'];
+		if($category == 'pages')
+			$articles = get_pages('numberposts=0&orderby='.get_option('rbinternal_page_orderby').'&order='.get_option('rbinternal_page_sort'));
+		else 
+			$articles = get_posts('numberposts=0&category='.$category.'&orderby='.get_option('rbinternal_post_orderby').'&order='.get_option('rbinternal_post_sort'));
+		$posts = '<ul>';
+		foreach($articles AS $article)
+			$posts .= '<li id="article_'.$article->ID.'"><span class="date">'.substr($article->post_date, 0, 10).'</span><a href="javascript:;" onclick="properties(\''.( get_option('rbinternal_return_param' == 'slug')? $article->post_name : $article->ID ).'\', \'post\');">'.$article->post_title.'</a></li>';
+		if(empty($articles)) $posts .= '<li>No posts found.</li>';
+		$posts .= '</ul>';
+		die("document.getElementById('".$returnDiv."').innerHTML = \"".addslashes(str_replace("\n", '', $posts))."\"");
+
+}
+
+?>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>RB Internal Linker</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+	<script language="javascript" type="text/javascript">
+	var rbinternal_url = "<?php echo $rbinternal_url; ?>"; 
+	</script>
+	<script language="javascript" type="text/javascript" src="../../../wp-includes/js/tinymce/tiny_mce_popup.js"></script>
+  <?php wp_print_scripts( array( 'sack' )); ?>
+	<script language="javascript" type="text/javascript">
+	var the_postId = 0;
+	
+	function showcategories(){
+	  document.getElementById('categoriesContainer').style.display='block';
+		document.getElementById('pagesContainer').style.display='none';
+		document.getElementById('propertiesContainer').style.display='none';
+		document.getElementById('pages').innerHTML='';
+		document.getElementById('linkText').value = '<?php echo addslashes($linktext); ?>';
+		document.getElementById('linkClass').value = '';
+		document.getElementById('linkTarget').value = '';
+	}
+	
+	
+	function getposts(category){
+  	var mysack = new sack( "tmce_plugin.php" );
+  	mysack.execute = 1;
+  	mysack.method = 'POST';
+  	mysack.setVar("action", "get_pages");
+		mysack.setVar("returnDiv", "pages");
+		mysack.setVar("category", category);
+  	mysack.onError = function() { alert('AJAX error in voting' )};
+    mysack.runAJAX();
+  
+		document.getElementById('pages').innerHTML = 'Loading posts...';
+  	document.getElementById('categoriesContainer').style.display='none';
+		document.getElementById('pagesContainer').style.display='block';
+  
+  	return true;
+  
+  }
+	
+	function properties(postId, linkType){
+		the_postId = postId;
+		the_linkType = linkType;
+		document.getElementById('categoriesContainer').style.display='none';
+		document.getElementById('pagesContainer').style.display='none';
+		document.getElementById('propertiesContainer').style.display='block';
+	}
+	
+	function insertLink(){
+	
+	if(the_postId == 0){ showcategories(); return false; }
+	
+	the_text = document.getElementById('linkText').value;
+	the_class = document.getElementById('linkClass').value;
+	the_target = document.getElementById('linkTarget').value;
+	the_anchor = document.getElementById('linkAnchor').value;
+	
+	rCode = '<span class="rbIntLinkText">' + the_text + '<!--pintlink id="' + the_postId + '" type="' + the_linkType + '" text="' + the_text + '"';
+	if(the_class != '') rCode += ' class="' + the_class + '"';
+	if(the_target != '') rCode += ' target="' + the_target + '"';
+	if(the_anchor != '') rCode += ' anchor="' + the_anchor + '"';
+	rCode += '-->';
+	
+	tinyMCEPopup.execCommand("mceInsertContent", false, rCode);
+	tinyMCEPopup.close();
+
+	}
+	
+	</script>
+	
+	<style>
+	body{font-family: arial, verdana, tahoma, sans-serif; font-size: 12px;}
+	#container{ scroll: auto; overflow: auto; width: 100%; height: 100%; }
+	h2{font-size: 14px; font-weight: bold; margin-bottom: 8px;}
+	a{text-decoration: none;}
+	a:hover{text-decoration: underline;}
+	ul{list-style-type: none; margin: 0; padding: 0;}
+	li{position: relative; float: left; clear: left; padding: 2px;}
+	ul.children{position: relative; float: left; clear: left; margin-left: 10px;}
+	.options{position: relative; float: left; clear: left; margin-left: 10px; color: #999999; }
+	.options a:link{color: #999999; }
+	.date{background-color: #E5F3FF; margin-right: 2px; padding: 1px;}
+	.row{position: relative; float: left; clear: left; margin-bottom: 5px;}
+	.action{position: relative; float: left; clear: left; padding: 2px; background-color: #E5F3FF; width: 330px; text-align: right;}
+	.textinput{ width: 250px; }
+	.textinputsmall{ width: 150px; }
+	label{position: relative; float: left; clear: left; margin: 0 5px 5px 0; width: 80px; padding-top: 2px;}
+	.back{position: relative; float: left; clear: left; margin-top: 10px;}
+	</style>	
+	
+</head>
+<body>
+
+<div id="container">
+
+<div id="categoriesContainer">
+
+  <h2>Categories</h2>
+  <ul>
+  <?php
+  $categories = get_categories(array('type' => 'post', 'orderby' => 'name', 'hide_empty' => true));
+  rbinternal_tier_categories($categories, 0);
+  
+  function rbinternal_tier_categories($data, $parent){
+  	foreach($data AS $cat){
+  		if($cat->category_parent == $parent){
+  			echo '<li id="category_'.$cat->cat_ID.'"><a href="javascript:;" onclick="getposts('.$cat->cat_ID.');">'.$cat->cat_name.'</a>';
+  			echo '<span class="options"><a href="javascript:;" onclick="getposts('.$cat->cat_ID.');">browse</a> | <a href="javascript:;" onclick="properties('.$cat->cat_ID.', \'category\');">link to</a></span>';
+  			echo '<ul class="children">';
+  			echo rbinternal_tier_categories($data, $cat->cat_ID);
+  			echo '</ul></li>';
+  		}
+  	}	
+  }
+  
+  ?>
+	<li id="category_pages"><a href="javascript:;" onclick="getposts('pages');">Pages</a>
+  </ul>
+
+</div>
+
+<div id="pagesContainer" style="display: none;">
+  <h2>Pages</h2>
+  <div id="pages"></div>
+  <a href="javascript:;" onclick="showcategories();" class="back">Back to categories</a>
+</div>
+
+<div id="propertiesContainer" style="display: none;">
+	<h2>Properties</h2>
+	<div class="row">
+		<label>Link Text:</label><input type="text" class="textinput" id="linkText" value="<?php echo $linktext; ?>" />
+	</div>
+	<div class="row">
+		<label>Link Class:</label><input type="text" class="textinputsmall" id="linkClass" value="" />
+	</div>
+	<div class="row">
+	<label>Link Target:</label>
+		<select class="textinput" id="linkTarget" value="">
+			<option value=""></option>
+			<option value="_blank">_blank</option>
+			<option value="_parent">_parent</option>
+			<option value="_self">_self</option>
+			<option value="_top">_top</option>
+		</select>
+	</div>
+	<div class="row">
+		<label>Link anchor:</label>#<input type="text" class="textinputsmall" id="linkAnchor" value="" />
+	</div>
+	<div class="action"><input type="button" value="Insert Link" onclick="insertLink();" /></div>
+	<a href="javascript:;" onclick="showcategories();" class="back">Back to categories</a>
+</div>
+
+</div>
+
+</body>
+</html>
Index: /afridex/plugins/akismet/akismet.php
===================================================================
--- /afridex/plugins/akismet/akismet.php (revision 21)
+++ /afridex/plugins/akismet/akismet.php (revision 21)
@@ -0,0 +1,856 @@
+<?php
+/*
+Plugin Name: Akismet
+Plugin URI: http://akismet.com/
+Description: Akismet checks your comments against the Akismet web service to see if they look like spam or not. You need a <a href="http://wordpress.com/api-keys/">WordPress.com API key</a> to use it. You can review the spam it catches under "Comments." To show off your Akismet stats just put <code>&lt;?php akismet_counter(); ?></code> in your template. See also: <a href="http://wordpress.org/extend/plugins/stats/">WP Stats plugin</a>.
+Version: 2.1.8
+Author: Matt Mullenweg
+Author URI: http://ma.tt/
+*/
+
+// If you hardcode a WP.com API key here, all key config screens will be hidden
+$wpcom_api_key = '';
+
+function akismet_init() {
+	global $wpcom_api_key, $akismet_api_host, $akismet_api_port;
+
+	if ( $wpcom_api_key )
+		$akismet_api_host = $wpcom_api_key . '.rest.akismet.com';
+	else
+		$akismet_api_host = get_option('wordpress_api_key') . '.rest.akismet.com';
+
+	$akismet_api_port = 80;
+	add_action('admin_menu', 'akismet_config_page');
+}
+add_action('init', 'akismet_init');
+
+if ( !function_exists('wp_nonce_field') ) {
+	function akismet_nonce_field($action = -1) { return; }
+	$akismet_nonce = -1;
+} else {
+	function akismet_nonce_field($action = -1) { return wp_nonce_field($action); }
+	$akismet_nonce = 'akismet-update-key';
+}
+
+if ( !function_exists('number_format_i18n') ) {
+	function number_format_i18n( $number, $decimals = null ) { return number_format( $number, $decimals ); }
+}
+
+function akismet_config_page() {
+	if ( function_exists('add_submenu_page') )
+		add_submenu_page('plugins.php', __('Akismet Configuration'), __('Akismet Configuration'), 'manage_options', 'akismet-key-config', 'akismet_conf');
+
+}
+
+function akismet_conf() {
+	global $akismet_nonce, $wpcom_api_key;
+
+	if ( isset($_POST['submit']) ) {
+		if ( function_exists('current_user_can') && !current_user_can('manage_options') )
+			die(__('Cheatin&#8217; uh?'));
+
+		check_admin_referer( $akismet_nonce );
+		$key = preg_replace( '/[^a-h0-9]/i', '', $_POST['key'] );
+
+		if ( empty($key) ) {
+			$key_status = 'empty';
+			$ms[] = 'new_key_empty';
+			delete_option('wordpress_api_key');
+		} else {
+			$key_status = akismet_verify_key( $key );
+		}
+
+		if ( $key_status == 'valid' ) {
+			update_option('wordpress_api_key', $key);
+			$ms[] = 'new_key_valid';
+		} else if ( $key_status == 'invalid' ) {
+			$ms[] = 'new_key_invalid';
+		} else if ( $key_status == 'failed' ) {
+			$ms[] = 'new_key_failed';
+		}
+
+		if ( isset( $_POST['akismet_discard_month'] ) )
+			update_option( 'akismet_discard_month', 'true' );
+		else
+			update_option( 'akismet_discard_month', 'false' );
+	}
+
+	if ( $key_status != 'valid' ) {
+		$key = get_option('wordpress_api_key');
+		if ( empty( $key ) ) {
+			if ( $key_status != 'failed' ) {
+				if ( akismet_verify_key( '1234567890ab' ) == 'failed' )
+					$ms[] = 'no_connection';
+				else
+					$ms[] = 'key_empty';
+			}
+			$key_status = 'empty';
+		} else {
+			$key_status = akismet_verify_key( $key );
+		}
+		if ( $key_status == 'valid' ) {
+			$ms[] = 'key_valid';
+		} else if ( $key_status == 'invalid' ) {
+			delete_option('wordpress_api_key');
+			$ms[] = 'key_empty';
+		} else if ( !empty($key) && $key_status == 'failed' ) {
+			$ms[] = 'key_failed';
+		}
+	}
+
+	$messages = array(
+		'new_key_empty' => array('color' => 'aa0', 'text' => __('Your key has been cleared.')),
+		'new_key_valid' => array('color' => '2d2', 'text' => __('Your key has been verified. Happy blogging!')),
+		'new_key_invalid' => array('color' => 'd22', 'text' => __('The key you entered is invalid. Please double-check it.')),
+		'new_key_failed' => array('color' => 'd22', 'text' => __('The key you entered could not be verified because a connection to akismet.com could not be established. Please check your server configuration.')),
+		'no_connection' => array('color' => 'd22', 'text' => __('There was a problem connecting to the Akismet server. Please check your server configuration.')),
+		'key_empty' => array('color' => 'aa0', 'text' => sprintf(__('Please enter an API key. (<a href="%s" style="color:#fff">Get your key.</a>)'), 'http://wordpress.com/profile/')),
+		'key_valid' => array('color' => '2d2', 'text' => __('This key is valid.')),
+		'key_failed' => array('color' => 'aa0', 'text' => __('The key below was previously validated but a connection to akismet.com can not be established at this time. Please check your server configuration.')));
+?>
+<?php if ( !empty($_POST ) ) : ?>
+<div id="message" class="updated fade"><p><strong><?php _e('Options saved.') ?></strong></p></div>
+<?php endif; ?>
+<div class="wrap">
+<h2><?php _e('Akismet Configuration'); ?></h2>
+<div class="narrow">
+<form action="" method="post" id="akismet-conf" style="margin: auto; width: 400px; ">
+<?php if ( !$wpcom_api_key ) { ?>
+	<p><?php printf(__('For many people, <a href="%1$s">Akismet</a> will greatly reduce or even completely eliminate the comment and trackback spam you get on your site. If one does happen to get through, simply mark it as "spam" on the moderation screen and Akismet will learn from the mistakes. If you don\'t have a WordPress.com account yet, you can get one at <a href="%2$s">WordPress.com</a>.'), 'http://akismet.com/', 'http://wordpress.com/api-keys/'); ?></p>
+
+<?php akismet_nonce_field($akismet_nonce) ?>
+<h3><label for="key"><?php _e('WordPress.com API Key'); ?></label></h3>
+<?php foreach ( $ms as $m ) : ?>
+	<p style="padding: .5em; background-color: #<?php echo $messages[$m]['color']; ?>; color: #fff; font-weight: bold;"><?php echo $messages[$m]['text']; ?></p>
+<?php endforeach; ?>
+<p><input id="key" name="key" type="text" size="15" maxlength="12" value="<?php echo get_option('wordpress_api_key'); ?>" style="font-family: 'Courier New', Courier, mono; font-size: 1.5em;" /> (<?php _e('<a href="http://faq.wordpress.com/2005/10/19/api-key/">What is this?</a>'); ?>)</p>
+<?php if ( $invalid_key ) { ?>
+<h3><?php _e('Why might my key be invalid?'); ?></h3>
+<p><?php _e('This can mean one of two things, either you copied the key wrong or that the plugin is unable to reach the Akismet servers, which is most often caused by an issue with your web host around firewalls or similar.'); ?></p>
+<?php } ?>
+<?php } ?>
+<p><label><input name="akismet_discard_month" id="akismet_discard_month" value="true" type="checkbox" <?php if ( get_option('akismet_discard_month') == 'true' ) echo ' checked="checked" '; ?> /> <?php _e('Automatically discard spam comments on posts older than a month.'); ?></label></p>
+	<p class="submit"><input type="submit" name="submit" value="<?php _e('Update options &raquo;'); ?>" /></p>
+</form>
+</div>
+</div>
+<?php
+}
+
+function akismet_verify_key( $key ) {
+	global $akismet_api_host, $akismet_api_port, $wpcom_api_key;
+	$blog = urlencode( get_option('home') );
+	if ( $wpcom_api_key )
+		$key = $wpcom_api_key;
+	$response = akismet_http_post("key=$key&blog=$blog", 'rest.akismet.com', '/1.1/verify-key', $akismet_api_port);
+	if ( !is_array($response) || !isset($response[1]) || $response[1] != 'valid' && $response[1] != 'invalid' )
+		return 'failed';
+	return $response[1];
+}
+
+if ( !get_option('wordpress_api_key') && !$wpcom_api_key && !isset($_POST['submit']) ) {
+	function akismet_warning() {
+		echo "
+		<div id='akismet-warning' class='updated fade'><p><strong>".__('Akismet is almost ready.')."</strong> ".sprintf(__('You must <a href="%1$s">enter your WordPress.com API key</a> for it to work.'), "plugins.php?page=akismet-key-config")."</p></div>
+		";
+	}
+	add_action('admin_notices', 'akismet_warning');
+	return;
+}
+
+// Returns array with headers in $response[0] and body in $response[1]
+function akismet_http_post($request, $host, $path, $port = 80) {
+	global $wp_version;
+
+	$http_request  = "POST $path HTTP/1.0\r\n";
+	$http_request .= "Host: $host\r\n";
+	$http_request .= "Content-Type: application/x-www-form-urlencoded; charset=" . get_option('blog_charset') . "\r\n";
+	$http_request .= "Content-Length: " . strlen($request) . "\r\n";
+	$http_request .= "User-Agent: WordPress/$wp_version | Akismet/2.0\r\n";
+	$http_request .= "\r\n";
+	$http_request .= $request;
+
+	$response = '';
+	if( false != ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) {
+		fwrite($fs, $http_request);
+
+		while ( !feof($fs) )
+			$response .= fgets($fs, 1160); // One TCP-IP packet
+		fclose($fs);
+		$response = explode("\r\n\r\n", $response, 2);
+	}
+	return $response;
+}
+
+function akismet_auto_check_comment( $comment ) {
+	global $akismet_api_host, $akismet_api_port;
+
+	$comment['user_ip']    = preg_replace( '/[^0-9., ]/', '', $_SERVER['REMOTE_ADDR'] );
+	$comment['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
+	$comment['referrer']   = $_SERVER['HTTP_REFERER'];
+	$comment['blog']       = get_option('home');
+
+	$ignore = array( 'HTTP_COOKIE' );
+
+	foreach ( $_SERVER as $key => $value )
+		if ( !in_array( $key, $ignore ) )
+			$comment["$key"] = $value;
+
+	$query_string = '';
+	foreach ( $comment as $key => $data )
+		$query_string .= $key . '=' . urlencode( stripslashes($data) ) . '&';
+
+	$response = akismet_http_post($query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port);
+	if ( 'true' == $response[1] ) {
+		add_filter('pre_comment_approved', create_function('$a', 'return \'spam\';'));
+		update_option( 'akismet_spam_count', get_option('akismet_spam_count') + 1 );
+
+		do_action( 'akismet_spam_caught' );
+
+		$post = get_post( $comment['comment_post_ID'] );
+		$last_updated = strtotime( $post->post_modified_gmt );
+		$diff = time() - $last_updated;
+		$diff = $diff / 86400;
+
+		if ( $post->post_type == 'post' && $diff > 30 && get_option( 'akismet_discard_month' ) == 'true' )
+			die;
+	}
+	akismet_delete_old();
+	return $comment;
+}
+
+function akismet_delete_old() {
+	global $wpdb;
+	$now_gmt = current_time('mysql', 1);
+	$wpdb->query("DELETE FROM $wpdb->comments WHERE DATE_SUB('$now_gmt', INTERVAL 15 DAY) > comment_date_gmt AND comment_approved = 'spam'");
+	$n = mt_rand(1, 5000);
+	if ( $n == 11 ) // lucky number
+		$wpdb->query("OPTIMIZE TABLE $wpdb->comments");
+}
+
+function akismet_submit_nonspam_comment ( $comment_id ) {
+	global $wpdb, $akismet_api_host, $akismet_api_port;
+	$comment_id = (int) $comment_id;
+
+	$comment = $wpdb->get_row("SELECT * FROM $wpdb->comments WHERE comment_ID = '$comment_id'");
+	if ( !$comment ) // it was deleted
+		return;
+	$comment->blog = get_option('home');
+	$query_string = '';
+	foreach ( $comment as $key => $data )
+		$query_string .= $key . '=' . urlencode( stripslashes($data) ) . '&';
+	$response = akismet_http_post($query_string, $akismet_api_host, "/1.1/submit-ham", $akismet_api_port);
+}
+
+function akismet_submit_spam_comment ( $comment_id ) {
+	global $wpdb, $akismet_api_host, $akismet_api_port;
+	$comment_id = (int) $comment_id;
+
+	$comment = $wpdb->get_row("SELECT * FROM $wpdb->comments WHERE comment_ID = '$comment_id'");
+	if ( !$comment ) // it was deleted
+		return;
+	if ( 'spam' != $comment->comment_approved )
+		return;
+	$comment->blog = get_option('home');
+	$query_string = '';
+	foreach ( $comment as $key => $data )
+		$query_string .= $key . '=' . urlencode( stripslashes($data) ) . '&';
+
+	$response = akismet_http_post($query_string, $akismet_api_host, "/1.1/submit-spam", $akismet_api_port);
+}
+
+add_action('wp_set_comment_status', 'akismet_submit_spam_comment');
+add_action('edit_comment', 'akismet_submit_spam_comment');
+add_action('preprocess_comment', 'akismet_auto_check_comment', 1);
+
+// Total spam in queue
+// get_option( 'akismet_spam_count' ) is the total caught ever
+function akismet_spam_count( $type = false ) {
+	global $wpdb;
+
+	if ( !$type ) { // total
+		$count = wp_cache_get( 'akismet_spam_count', 'widget' );
+		if ( false === $count ) {
+			if ( function_exists('wp_count_comments') ) {
+				$count = wp_count_comments();
+				$count = $count->spam;
+			} else {
+				$count = (int) $wpdb->get_var("SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = 'spam'");
+			}
+			wp_cache_set( 'akismet_spam_count', $count, 'widget', 3600 );
+		}
+		return $count;
+	} elseif ( 'comments' == $type || 'comment' == $type ) { // comments
+		$type = '';
+	} else { // pingback, trackback, ...
+		$type  = $wpdb->escape( $type );
+	}
+
+	return (int) $wpdb->get_var("SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = 'spam' AND comment_type='$type'");
+}
+
+function akismet_spam_comments( $type = false, $page = 1, $per_page = 50 ) {
+	global $wpdb;
+
+	$page = (int) $page;
+	if ( $page < 2 )
+		$page = 1;
+
+	$per_page = (int) $per_page;
+	if ( $per_page < 1 )
+		$per_page = 50;
+
+	$start = ( $page - 1 ) * $per_page;
+	$end = $start + $per_page;
+
+	if ( $type ) {
+		if ( 'comments' == $type || 'comment' == $type )
+			$type = '';
+		else
+			$type = $wpdb->escape( $type );
+		return $wpdb->get_results( "SELECT * FROM $wpdb->comments WHERE comment_approved = 'spam' AND comment_type='$type' ORDER BY comment_date DESC LIMIT $start, $end");
+	}
+
+	// All
+	return $wpdb->get_results( "SELECT * FROM $wpdb->comments WHERE comment_approved = 'spam' ORDER BY comment_date DESC LIMIT $start, $end");
+}
+
+// Totals for each comment type
+// returns array( type => count, ... )
+function akismet_spam_totals() {
+	global $wpdb;
+	$totals = $wpdb->get_results( "SELECT comment_type, COUNT(*) AS cc FROM $wpdb->comments WHERE comment_approved = 'spam' GROUP BY comment_type" );
+	$return = array();
+	foreach ( $totals as $total )
+		$return[$total->comment_type ? $total->comment_type : 'comment'] = $total->cc;
+	return $return;
+}
+
+function akismet_manage_page() {
+	global $wpdb, $submenu, $wp_db_version;
+
+	// WP 2.7 has its own spam management page
+	if ( 8645 <= $wp_db_version )
+		return;
+
+	$count = sprintf(__('Akismet Spam (%s)'), akismet_spam_count());
+	if ( isset( $submenu['edit-comments.php'] ) )
+		add_submenu_page('edit-comments.php', __('Akismet Spam'), $count, 'moderate_comments', 'akismet-admin', 'akismet_caught' );
+	elseif ( function_exists('add_management_page') )
+		add_management_page(__('Akismet Spam'), $count, 'moderate_comments', 'akismet-admin', 'akismet_caught');
+}
+
+function akismet_caught() {
+	global $wpdb, $comment, $akismet_caught, $akismet_nonce;
+
+	akismet_recheck_queue();
+	if (isset($_POST['submit']) && 'recover' == $_POST['action'] && ! empty($_POST['not_spam'])) {
+		check_admin_referer( $akismet_nonce );
+		if ( function_exists('current_user_can') && !current_user_can('moderate_comments') )
+			die(__('You do not have sufficient permission to moderate comments.'));
+
+		$i = 0;
+		foreach ($_POST['not_spam'] as $comment):
+			$comment = (int) $comment;
+			if ( function_exists('wp_set_comment_status') )
+				wp_set_comment_status($comment, 'approve');
+			else
+				$wpdb->query("UPDATE $wpdb->comments SET comment_approved = '1' WHERE comment_ID = '$comment'");
+			akismet_submit_nonspam_comment($comment);
+			++$i;
+		endforeach;
+		$to = add_query_arg( 'recovered', $i, $_SERVER['HTTP_REFERER'] );
+		wp_redirect( $to );
+		exit;
+	}
+	if ('delete' == $_POST['action']) {
+		check_admin_referer( $akismet_nonce );
+		if ( function_exists('current_user_can') && !current_user_can('moderate_comments') )
+			die(__('You do not have sufficient permission to moderate comments.'));
+
+		$delete_time = $wpdb->escape( $_POST['display_time'] );
+		$nuked = $wpdb->query( "DELETE FROM $wpdb->comments WHERE comment_approved = 'spam' AND '$delete_time' > comment_date_gmt" );
+		wp_cache_delete( 'akismet_spam_count', 'widget' );
+		$to = add_query_arg( 'deleted', 'all', $_SERVER['HTTP_REFERER'] );
+		wp_redirect( $to );
+		exit;
+	}
+
+if ( isset( $_GET['recovered'] ) ) {
+	$i = (int) $_GET['recovered'];
+	echo '<div class="updated"><p>' . sprintf(__('%1$s comments recovered.'), $i) . "</p></div>";
+}
+
+if (isset( $_GET['deleted'] ) )
+	echo '<div class="updated"><p>' . __('All spam deleted.') . '</p></div>';
+
+if ( isset( $GLOBALS['submenu']['edit-comments.php'] ) )
+	$link = 'edit-comments.php';
+else
+	$link = 'edit.php';
+?>
+<style type="text/css">
+.akismet-tabs {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+	clear: both;
+	border-bottom: 1px solid #ccc;
+	height: 31px;
+	margin-bottom: 20px;
+	background: #ddd;
+	border-top: 1px solid #bdbdbd;
+}
+.akismet-tabs li {
+	float: left;
+	margin: 5px 0 0 20px;
+}
+.akismet-tabs a {
+	display: block;
+	padding: 4px .5em 3px;
+	border-bottom: none;
+	color: #036;
+}
+.akismet-tabs .active a {
+	background: #fff;
+	border: 1px solid #ccc;
+	border-bottom: none;
+	color: #000;
+	font-weight: bold;
+	padding-bottom: 4px;
+}
+#akismetsearch {
+	float: right;
+	margin-top: -.5em;
+}
+
+#akismetsearch p {
+	margin: 0;
+	padding: 0;
+}
+</style>
+<div class="wrap">
+<h2><?php _e('Caught Spam') ?></h2>
+<?php
+$count = get_option( 'akismet_spam_count' );
+if ( $count ) {
+?>
+<p><?php printf(__('Akismet has caught <strong>%1$s spam</strong> for you since you first installed it.'), number_format_i18n($count) ); ?></p>
+<?php
+}
+
+$spam_count = akismet_spam_count();
+
+if ( 0 == $spam_count ) {
+	echo '<p>'.__('You have no spam currently in the queue. Must be your lucky day. :)').'</p>';
+	echo '</div>';
+} else {
+	echo '<p>'.__('You can delete all of the spam from your database with a single click. This operation cannot be undone, so you may wish to check to ensure that no legitimate comments got through first. Spam is automatically deleted after 15 days, so don&#8217;t sweat it.').'</p>';
+?>
+<?php if ( !isset( $_POST['s'] ) ) { ?>
+<form method="post" action="<?php echo attribute_escape( add_query_arg( 'noheader', 'true' ) ); ?>">
+<?php akismet_nonce_field($akismet_nonce) ?>
+<input type="hidden" name="action" value="delete" />
+<?php printf(__('There are currently %1$s comments identified as spam.'), $spam_count); ?>&nbsp; &nbsp; <input type="submit" class="button delete" name="Submit" value="<?php _e('Delete all'); ?>" />
+<input type="hidden" name="display_time" value="<?php echo current_time('mysql', 1); ?>" />
+</form>
+<?php } ?>
+</div>
+<div class="wrap">
+<?php if ( isset( $_POST['s'] ) ) { ?>
+<h2><?php _e('Search'); ?></h2>
+<?php } else { ?>
+<?php echo '<p>'.__('These are the latest comments identified as spam by Akismet. If you see any mistakes, simply mark the comment as "not spam" and Akismet will learn from the submission. If you wish to recover a comment from spam, simply select the comment, and click Not Spam. After 15 days we clean out the junk for you.').'</p>'; ?>
+<?php } ?>
+<?php
+if ( isset( $_POST['s'] ) ) {
+	$s = $wpdb->escape($_POST['s']);
+	$comments = $wpdb->get_results("SELECT * FROM $wpdb->comments  WHERE
+		(comment_author LIKE '%$s%' OR
+		comment_author_email LIKE '%$s%' OR
+		comment_author_url LIKE ('%$s%') OR
+		comment_author_IP LIKE ('%$s%') OR
+		comment_content LIKE ('%$s%') ) AND
+		comment_approved = 'spam'
+		ORDER BY comment_date DESC");
+} else {
+	if ( isset( $_GET['apage'] ) )
+		$page = (int) $_GET['apage'];
+	else
+		$page = 1;
+
+	if ( $page < 2 )
+		$page = 1;
+
+	$current_type = false;
+	if ( isset( $_GET['ctype'] ) )
+		$current_type = preg_replace( '|[^a-z]|', '', $_GET['ctype'] );
+
+	$comments = akismet_spam_comments( $current_type, $page );
+	$total = akismet_spam_count( $current_type );
+	$totals = akismet_spam_totals();
+?>
+<ul class="akismet-tabs">
+<li <?php if ( !isset( $_GET['ctype'] ) ) echo ' class="active"'; ?>><a href="edit-comments.php?page=akismet-admin"><?php _e('All'); ?></a></li>
+<?php
+foreach ( $totals as $type => $type_count ) {
+	if ( 'comment' == $type ) {
+		$type = 'comments';
+		$show = __('Comments');
+	} else {
+		$show = ucwords( $type );
+	}
+	$type_count = number_format_i18n( $type_count );
+	$extra = $current_type === $type ? ' class="active"' : '';
+	echo "<li $extra><a href='edit-comments.php?page=akismet-admin&amp;ctype=$type'>$show ($type_count)</a></li>";
+}
+do_action( 'akismet_tabs' ); // so plugins can add more tabs easily
+?>
+</ul>
+<?php
+}
+
+if ($comments) {
+?>
+<form method="post" action="<?php echo attribute_escape("$link?page=akismet-admin"); ?>" id="akismetsearch">
+<p>  <input type="text" name="s" value="<?php if (isset($_POST['s'])) echo attribute_escape($_POST['s']); ?>" size="17" />
+  <input type="submit" class="button" name="submit" value="<?php echo attribute_escape(__('Search Spam &raquo;')) ?>"  />  </p>
+</form>
+<?php if ( $total > 50 ) {
+$total_pages = ceil( $total / 50 );
+$r = '';
+if ( 1 < $page ) {
+	$args['apage'] = ( 1 == $page - 1 ) ? '' : $page - 1;
+	$r .=  '<a class="prev" href="' . clean_url(add_query_arg( $args )) . '">'. __('&laquo; Previous Page') .'</a>' . "\n";
+}
+if ( ( $total_pages = ceil( $total / 50 ) ) > 1 ) {
+	for ( $page_num = 1; $page_num <= $total_pages; $page_num++ ) :
+		if ( $page == $page_num ) :
+			$r .=  "<strong>$page_num</strong>\n";
+		else :
+			$p = false;
+			if ( $page_num < 3 || ( $page_num >= $page - 3 && $page_num <= $page + 3 ) || $page_num > $total_pages - 3 ) :
+				$args['apage'] = ( 1 == $page_num ) ? '' : $page_num;
+				$r .= '<a class="page-numbers" href="' . clean_url(add_query_arg($args)) . '">' . ( $page_num ) . "</a>\n";
+				$in = true;
+			elseif ( $in == true ) :
+				$r .= "...\n";
+				$in = false;
+			endif;
+		endif;
+	endfor;
+}
+if ( ( $page ) * 50 < $total || -1 == $total ) {
+	$args['apage'] = $page + 1;
+	$r .=  '<a class="next" href="' . clean_url(add_query_arg($args)) . '">'. __('Next Page &raquo;') .'</a>' . "\n";
+}
+echo "<p>$r</p>";
+?>
+
+<?php } ?>
+<form style="clear: both;" method="post" action="<?php echo attribute_escape( add_query_arg( 'noheader', 'true' ) ); ?>">
+<?php akismet_nonce_field($akismet_nonce) ?>
+<input type="hidden" name="action" value="recover" />
+<ul id="spam-list" class="commentlist" style="list-style: none; margin: 0; padding: 0;">
+<?php
+$i = 0;
+foreach($comments as $comment) {
+	$i++;
+	$comment_date = mysql2date(get_option("date_format") . " @ " . get_option("time_format"), $comment->comment_date);
+	$post = get_post($comment->comment_post_ID);
+	$post_title = $post->post_title;
+	if ($i % 2) $class = 'class="alternate"';
+	else $class = '';
+	echo "\n\t<li id='comment-$comment->comment_ID' $class>";
+	?>
+
+<p><strong><?php comment_author() ?></strong> <?php if ($comment->comment_author_email) { ?>| <?php comment_author_email_link() ?> <?php } if ($comment->comment_author_url && 'http://' != $comment->comment_author_url) { ?> | <?php comment_author_url_link() ?> <?php } ?>| <?php _e('IP:') ?> <a href="http://ws.arin.net/cgi-bin/whois.pl?queryinput=<?php comment_author_IP() ?>"><?php comment_author_IP() ?></a></p>
+
+<?php comment_text() ?>
+
+<p><label for="spam-<?php echo $comment->comment_ID; ?>">
+<input type="checkbox" id="spam-<?php echo $comment->comment_ID; ?>" name="not_spam[]" value="<?php echo $comment->comment_ID; ?>" />
+<?php _e('Not Spam') ?></label> &#8212; <?php comment_date('M j, g:i A');  ?> &#8212; [
+<?php
+$post = get_post($comment->comment_post_ID);
+$post_title = wp_specialchars( $post->post_title, 'double' );
+$post_title = ('' == $post_title) ? "# $comment->comment_post_ID" : $post_title;
+?>
+ <a href="<?php echo get_permalink($comment->comment_post_ID); ?>" title="<?php echo $post_title; ?>"><?php _e('View Post') ?></a> ] </p>
+
+
+<?php
+}
+?>
+</ul>
+<?php if ( $total > 50 ) {
+$total_pages = ceil( $total / 50 );
+$r = '';
+if ( 1 < $page ) {
+	$args['apage'] = ( 1 == $page - 1 ) ? '' : $page - 1;
+	$r .=  '<a class="prev" href="' . clean_url(add_query_arg( $args )) . '">'. __('&laquo; Previous Page') .'</a>' . "\n";
+}
+if ( ( $total_pages = ceil( $total / 50 ) ) > 1 ) {
+	for ( $page_num = 1; $page_num <= $total_pages; $page_num++ ) :
+		if ( $page == $page_num ) :
+			$r .=  "<strong>$page_num</strong>\n";
+		else :
+			$p = false;
+			if ( $page_num < 3 || ( $page_num >= $page - 3 && $page_num <= $page + 3 ) || $page_num > $total_pages - 3 ) :
+				$args['apage'] = ( 1 == $page_num ) ? '' : $page_num;
+				$r .= '<a class="page-numbers" href="' . clean_url(add_query_arg($args)) . '">' . ( $page_num ) . "</a>\n";
+				$in = true;
+			elseif ( $in == true ) :
+				$r .= "...\n";
+				$in = false;
+			endif;
+		endif;
+	endfor;
+}
+if ( ( $page ) * 50 < $total || -1 == $total ) {
+	$args['apage'] = $page + 1;
+	$r .=  '<a class="next" href="' . clean_url(add_query_arg($args)) . '">'. __('Next Page &raquo;') .'</a>' . "\n";
+}
+echo "<p>$r</p>";
+}
+?>
+<p class="submit">
+<input type="submit" name="submit" value="<?php echo attribute_escape(__('De-spam marked comments &raquo;')); ?>" />
+</p>
+<p><?php _e('Comments you de-spam will be submitted to Akismet as mistakes so it can learn and get better.'); ?></p>
+</form>
+<?php
+} else {
+?>
+<p><?php _e('No results found.'); ?></p>
+<?php } ?>
+
+<?php if ( !isset( $_POST['s'] ) ) { ?>
+<form method="post" action="<?php echo attribute_escape( add_query_arg( 'noheader', 'true' ) ); ?>">
+<?php akismet_nonce_field($akismet_nonce) ?>
+<p><input type="hidden" name="action" value="delete" />
+<?php printf(__('There are currently %1$s comments identified as spam.'), $spam_count); ?>&nbsp; &nbsp; <input type="submit" name="Submit" class="button" value="<?php echo attribute_escape(__('Delete all')); ?>" />
+<input type="hidden" name="display_time" value="<?php echo current_time('mysql', 1); ?>" /></p>
+</form>
+<?php } ?>
+</div>
+<?php
+	}
+}
+
+add_action('admin_menu', 'akismet_manage_page');
+
+// WP < 2.5
+function akismet_stats() {
+	if ( !function_exists('did_action') || did_action( 'rightnow_end' ) ) // We already displayed this info in the "Right Now" section
+		return;
+	if ( !$count = get_option('akismet_spam_count') )
+		return;
+	$path = plugin_basename(__FILE__);
+	echo '<h3>'.__('Spam').'</h3>';
+	global $submenu;
+	if ( isset( $submenu['edit-comments.php'] ) )
+		$link = 'edit-comments.php';
+	else
+		$link = 'edit.php';
+	echo '<p>'.sprintf(__('<a href="%1$s">Akismet</a> has protected your site from <a href="%2$s">%3$s spam comments</a>.'), 'http://akismet.com/', clean_url("$link?page=akismet-admin"), number_format_i18n($count) ).'</p>';
+}
+
+add_action('activity_box_end', 'akismet_stats');
+
+// WP 2.5+
+function akismet_rightnow() {
+	global $submenu;
+	if ( isset( $submenu['edit-comments.php'] ) )
+		$link = 'edit-comments.php';
+	else
+		$link = 'edit.php';
+
+	if ( $count = get_option('akismet_spam_count') ) {
+		$intro = sprintf( __ngettext(
+			'<a href="%1$s">Akismet</a> has protected your site from %2$s spam comment already,',
+			'<a href="%1$s">Akismet</a> has protected your site from %2$s spam comments already,',
+			$count
+		), 'http://akismet.com/', number_format_i18n( $count ) );
+	} else {
+		$intro = sprintf( __('<a href="%1$s">Akismet</a> blocks spam from getting to your blog,'), 'http://akismet.com/' );
+	}
+
+	if ( $queue_count = akismet_spam_count() ) {
+		$queue_text = sprintf( __ngettext(
+			'and there\'s <a href="%2$s">%1$s comment</a> in your spam queue right now.',
+			'and there are <a href="%2$s">%1$s comments</a> in your spam queue right now.',
+			$queue_count
+		), number_format_i18n( $queue_count ), clean_url("$link?page=akismet-admin") );
+	} else {
+		$queue_text = sprintf( __( "but there's nothing in your <a href='%1\$s'>spam queue</a> at the moment." ), clean_url("$link?page=akismet-admin") );
+	}
+
+	$text = sprintf( _c( '%1$s %2$s|akismet_rightnow' ), $intro, $queue_text );
+
+	echo "<p class='akismet-right-now'>$text</p>\n";
+}
+	
+add_action('rightnow_end', 'akismet_rightnow');
+
+// For WP <= 2.3.x
+if ( 'moderation.php' == $pagenow ) {
+	function akismet_recheck_button( $page ) {
+		global $submenu;
+		if ( isset( $submenu['edit-comments.php'] ) )
+			$link = 'edit-comments.php';
+		else
+			$link = 'edit.php';
+		$button = "<a href='$link?page=akismet-admin&amp;recheckqueue=true&amp;noheader=true' style='display: block; width: 100px; position: absolute; right: 7%; padding: 5px; font-size: 14px; text-decoration: underline; background: #fff; border: 1px solid #ccc;'>" . __('Recheck Queue for Spam') . "</a>";
+		$page = str_replace( '<div class="wrap">', '<div class="wrap">' . $button, $page );
+		return $page;
+	}
+
+	if ( $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = '0'" ) )
+		ob_start( 'akismet_recheck_button' );
+}
+
+// For WP >= 2.5
+function akismet_check_for_spam_button($comment_status) {
+	if ( 'approved' == $comment_status )
+		return;
+	if ( function_exists('plugins_url') )
+		$link = 'admin.php?action=akismet_recheck_queue';
+	else
+		$link = 'edit-comments.php?page=akismet-admin&amp;recheckqueue=true&amp;noheader=true';
+	echo "</div><div class='alignleft'><a class='button-secondary checkforspam' href='$link'>" . __('Check for Spam') . "</a>";
+}
+add_action('manage_comments_nav', 'akismet_check_for_spam_button');
+
+function akismet_recheck_queue() {
+	global $wpdb, $akismet_api_host, $akismet_api_port;
+
+	if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) )
+		return;
+
+	$moderation = $wpdb->get_results( "SELECT * FROM $wpdb->comments WHERE comment_approved = '0'", ARRAY_A );
+	foreach ( (array) $moderation as $c ) {
+		$c['user_ip']    = $c['comment_author_IP'];
+		$c['user_agent'] = $c['comment_agent'];
+		$c['referrer']   = '';
+		$c['blog']       = get_option('home');
+		$id = (int) $c['comment_ID'];
+
+		$query_string = '';
+		foreach ( $c as $key => $data )
+		$query_string .= $key . '=' . urlencode( stripslashes($data) ) . '&';
+
+		$response = akismet_http_post($query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port);
+		if ( 'true' == $response[1] ) {
+			$wpdb->query( "UPDATE $wpdb->comments SET comment_approved = 'spam' WHERE comment_ID = $id" );
+		}
+	}
+	wp_redirect( $_SERVER['HTTP_REFERER'] );
+	exit;
+}
+
+add_action('admin_action_akismet_recheck_queue', 'akismet_recheck_queue');
+
+function akismet_check_db_comment( $id ) {
+	global $wpdb, $akismet_api_host, $akismet_api_port;
+
+	$id = (int) $id;
+	$c = $wpdb->get_row( "SELECT * FROM $wpdb->comments WHERE comment_ID = '$id'", ARRAY_A );
+	if ( !$c )
+		return;
+
+	$c['user_ip']    = $c['comment_author_IP'];
+	$c['user_agent'] = $c['comment_agent'];
+	$c['referrer']   = '';
+	$c['blog']       = get_option('home');
+	$id = $c['comment_ID'];
+
+	$query_string = '';
+	foreach ( $c as $key => $data )
+	$query_string .= $key . '=' . urlencode( stripslashes($data) ) . '&';
+
+	$response = akismet_http_post($query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port);
+	return $response[1];
+}
+
+// This option causes tons of FPs, was removed in 2.1
+function akismet_kill_proxy_check( $option ) { return 0; }
+add_filter('option_open_proxy_check', 'akismet_kill_proxy_check');
+
+// Widget stuff
+function widget_akismet_register() {
+	if ( function_exists('register_sidebar_widget') ) :
+	function widget_akismet($args) {
+		extract($args);
+		$options = get_option('widget_akismet');
+		$count = number_format_i18n(get_option('akismet_spam_count'));
+		?>
+			<?php echo $before_widget; ?>
+				<?php echo $before_title . $options['title'] . $after_title; ?>
+				<div id="akismetwrap"><div id="akismetstats"><a id="aka" href="http://akismet.com" title=""><?php printf( __( '%1$s %2$sspam comments%3$s %4$sblocked by%5$s<br />%6$sAkismet%7$s' ), '<div id="akismet1"><span id="akismetcount">' . $count . '</span>', '<span id="akismetsc">', '</span></div>', '<div id="akismet2"><span id="akismetbb">', '</span>', '<span id="akismeta">', '</span></div>' ); ?></a></div></div>
+			<?php echo $after_widget; ?>
+	<?php
+	}
+
+	function widget_akismet_style() {
+		?>
+<style type="text/css">
+#aka,#aka:link,#aka:hover,#aka:visited,#aka:active{color:#fff;text-decoration:none}
+#aka:hover{border:none;text-decoration:none}
+#aka:hover #akismet1{display:none}
+#aka:hover #akismet2,#akismet1{display:block}
+#akismet2{display:none;padding-top:2px}
+#akismeta{font-size:16px;font-weight:bold;line-height:18px;text-decoration:none}
+#akismetcount{display:block;font:15px Verdana,Arial,Sans-Serif;font-weight:bold;text-decoration:none}
+#akismetwrap #akismetstats{background:url(<?php echo get_option('siteurl'); ?>/wp-content/plugins/akismet/akismet.gif) no-repeat top left;border:none;color:#fff;font:11px 'Trebuchet MS','Myriad Pro',sans-serif;height:40px;line-height:100%;overflow:hidden;padding:8px 0 0;text-align:center;width:120px}
+</style>
+		<?php
+	}
+
+	function widget_akismet_control() {
+		$options = $newoptions = get_option('widget_akismet');
+		if ( $_POST["akismet-submit"] ) {
+			$newoptions['title'] = strip_tags(stripslashes($_POST["akismet-title"]));
+			if ( empty($newoptions['title']) ) $newoptions['title'] = 'Spam Blocked';
+		}
+		if ( $options != $newoptions ) {
+			$options = $newoptions;
+			update_option('widget_akismet', $options);
+		}
+		$title = htmlspecialchars($options['title'], ENT_QUOTES);
+	?>
+				<p><label for="akismet-title"><?php _e('Title:'); ?> <input style="width: 250px;" id="akismet-title" name="akismet-title" type="text" value="<?php echo $title; ?>" /></label></p>
+				<input type="hidden" id="akismet-submit" name="akismet-submit" value="1" />
+	<?php
+	}
+
+	register_sidebar_widget('Akismet', 'widget_akismet', null, 'akismet');
+	register_widget_control('Akismet', 'widget_akismet_control', null, 75, 'akismet');
+	if ( is_active_widget('widget_akismet') )
+		add_action('wp_head', 'widget_akismet_style');
+	endif;
+}
+
+add_action('init', 'widget_akismet_register');
+
+// Counter for non-widget users
+function akismet_counter() {
+?>
+<style type="text/css">
+#akismetwrap #aka,#aka:link,#aka:hover,#aka:visited,#aka:active{color:#fff;text-decoration:none}
+#aka:hover{border:none;text-decoration:none}
+#aka:hover #akismet1{display:none}
+#aka:hover #akismet2,#akismet1{display:block}
+#akismet2{display:none;padding-top:2px}
+#akismeta{font-size:16px;font-weight:bold;line-height:18px;text-decoration:none}
+#akismetcount{display:block;font:15px Verdana,Arial,Sans-Serif;font-weight:bold;text-decoration:none}
+#akismetwrap #akismetstats{background:url(<?php echo get_option('siteurl'); ?>/wp-content/plugins/akismet/akismet.gif) no-repeat top left;border:none;color:#fff;font:11px 'Trebuchet MS','Myriad Pro',sans-serif;height:40px;line-height:100%;overflow:hidden;padding:8px 0 0;text-align:center;width:120px}
+</style>
+<?php
+$count = number_format_i18n(get_option('akismet_spam_count'));
+?>
+<div id="akismetwrap"><div id="akismetstats"><a id="aka" href="http://akismet.com" title=""><div id="akismet1"><span id="akismetcount"><?php echo $count; ?></span> <span id="akismetsc"><?php _e('spam comments') ?></span></div> <div id="akismet2"><span id="akismetbb"><?php _e('blocked by') ?></span><br /><span id="akismeta">Akismet</span></div></a></div></div>
+<?php
+}
+
+?>
Index: /afridex/plugins/akismet/readme.txt
===================================================================
--- /afridex/plugins/akismet/readme.txt (revision 21)
+++ /afridex/plugins/akismet/readme.txt (revision 21)
@@ -0,0 +1,22 @@
+=== Akismet ===
+Contributors: matt, ryan, andy, mdawaffe
+Tags: akismet, comments, spam
+
+Akismet checks your comments against the Akismet web service to see if they look like spam or not.
+
+== Description ==
+
+Akismet checks your comments against the Akismet web service to see if they look like spam or not and lets you
+review the spam it catches under your blog's "Comments" admin screen.
+
+Want to show off how much spam Akismet has caught for you? Just put `<?php akismet_counter(); ?>` in your template.
+
+See also: [WP Stats plugin](http://wordpress.org/extend/plugins/stats/).
+
+PS: You'll need a [WordPress.com API key](http://wordpress.com/api-keys/) to use it.
+
+== Installation ==
+
+Upload the Akismet plugin to your blog, Activate it, then enter your [WordPress.com API key](http://wordpress.com/api-keys/).
+
+1, 2, 3: You're done!
Index: /afridex/plugins/kb-linker/kb_linker.php
===================================================================
--- /afridex/plugins/kb-linker/kb_linker.php (revision 21)
+++ /afridex/plugins/kb-linker/kb_linker.php (revision 21)
@@ -0,0 +1,239 @@
+<?php
+/*
+Plugin Name: KB Linker
+Plugin URI: http://adambrown.info/b/widgets/kb-linker/
+Description: Looks for user-defined phrases in posts and automatically links them. Example: Link every occurrence of "Wordpress" to wordpress.org.
+Author: Adam R. Brown
+Version: 1.102
+Author URI: http://adambrown.info/
+*/
+
+//	OPTIONAL SETTINGS
+	/*special characters for foreign languages. Add any you want to the array below. Char goes on left, HTML entity on right. The German codes are here as examples.
+		See http://www.w3schools.com/tags/ref_entities.asp for HTML entities.	*/
+	$kblinker_special_chars = array(
+				'ä' => '&#228;',
+				'Ä' => '&#196;',
+				'ö' => '&#246;',
+				'Ö' => '&#214;',
+				'ü' => '&#252;',
+				'Ü' => '&#220;',
+				'ß' => '&#223;'
+	);
+	/* 	would you like to add title="" tags to the links with the keyword in them? true or false. */
+	define( 'KBLINKER_USE_TITLES' , true );
+	/* 	if the preceding setting is TRUE, you can customize the text before or after the keyword below. For example, if you want titles to say "More about KEYWORD.",
+		then make before = 'More about ' (note the space) and after = '.' 	*/
+	$kblinker_title_text = array(
+		'before' => 'More about ', // default: 'More about '
+		'after' => ' &raquo;',	// default: '$raquo;' (an arrow pointing to the right)
+	);
+//	END OF SETTINGS. NO MORE EDITING REQUIRED.
+
+
+
+
+
+/*	DEVELOPMENT NOTES
+
+	CHANGE LOG
+	1.01	initial release
+	1.02	quick fix to check is_array before extracting on line 105
+	1.03	- added support for German (or other) special characters
+		- added support for opening links in different targets
+	1.04	add 'i' tag to the tag-detection regexes (it was already in the main replacement one, causing some errors)
+	1.05	bugfix
+	1.06	bugfix
+	1.10	- puts keyword in link's "title" tags
+		- won't link the same URL more than once per post, even if multiple keywords are associated with it
+		- won't link a page to itself (imperfect)
+	1.101	bugfix
+	1.102	bugfix
+
+	IMPORTANT NOTE TO ANYBODY CONSIDERING ADDING THIS PLUGIN TO A WP-MU INSTALLATION:
+	If you aren't sure whether you are using a WP-MU blog, then you aren't. Trust me. If this warning applies to you, then you will know it.
+	For WP-MU administrators: You should not use this plugin. Your users could use it to place (potentially malicious) javascript into their blogs.
+	This plugin is PERFECTLY SAFE for non-WP-MU blogs, so ignore this message if you're using regular wordpress (you probably are).
+
+	KNOWN BUGS
+	- Pre 1.04: If a tag or attribute contained the keyword, but with different capitalization than specified in options, it was replaced. That causes problems.
+	- 1.04: Fixed that bug, but in return, you might find capitalization changing (within tags and attributes) to match that given in the linker's options. Sorry.
+	- 1.05 Fixed the capitalization bug
+
+	GENERAL NOTE FOR DEVELOPERS/HACKERS/ETC:
+		You will notice that I've included extensive commenting in the code below. I do not have time to support this plugin. The commenting is there to make this plugin
+		easy to modify. Please try making modifications on your own before posting a support question at the plugin's URI.
+		That being said, you are welcome to post well-informed support questions on my site.
+
+	DATABASE STRUCTURE
+		the options->KB Linker page will create a set of matching terms and URLs that gets stored as a list.
+		structure of option "kb_linker":
+			pairs => array( see below)
+			text=>	same content as pairs, but in unprocessed form (for displaying in the option's form)
+			plurals => 1, 0		if 1, we should look for variants of the keywords ending in s or es
+*/
+
+function kb_linker($content){
+	global $kblinker_special_chars,$kblinker_title_text;
+
+	$option = get_option('kb_linker');
+	if (is_array($option)){
+		extract($option);
+	}
+
+	// uncomment for testing (to override options):
+	#$pairs = array( 'contributor'=>'http://google.com', 'a'=>'http://yahoo.com/', 'scripting'=>'scripting', 'don'=>'don', 'first post'=>'firstpost.org', 'first'=>'first.org', 'wp'=>'WP.ORG');
+
+	// die if option isn't set yet
+	if ( !is_array($pairs) )
+		return $content;
+
+	// let's make use of that special chars setting.
+	if (is_array($kblinker_special_chars)){
+		foreach ($kblinker_special_chars as $char => $code){
+			$content = str_replace($code,$char,$content);
+		}
+	}
+
+	// needed below...
+	$usedUrls = array();
+	$currentUrl = 'http://' . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]; // may not work on all hosting setups.
+
+	// most of the action is in here.
+	foreach ($pairs as $keyword => $url){
+		if (in_array( $url, $usedUrls )) // don't link to the same URL more than once
+			continue;
+		if (strpos( $content, $url )){ // we've already used this URL, or it was manually inserted by author into post
+			$usedUrls[] = $url;
+			continue;
+		}
+		if ($url == $currentUrl){ // don't link a page to itself
+			$usedUrls[] = $url;
+			continue;
+		}
+
+		// first, let's check whether we've got a "target" attribute specified.
+		if (false!==strpos( $url, ' ' ) ){	// Let's not waste CPU resources unless we see a ' ' in the URL:
+			$target = trim(   substr( $url, strpos($url,' ') )   );
+			$target = ' target="'.$target.'"';
+			$url = substr( $url, 0, strpos($url,' ') );
+		}else{
+			$target='';
+		}
+		
+		// let's escape any '&' in the URL.
+		$url = str_replace( '&amp;', '&', $url ); // this might seem unnecessary, but it prevents the next line from double-escaping the &
+		$url = str_replace( '&', '&amp;', $url );
+		
+		// we don't want to link the keyword if it is already linked.
+		// so let's find all instances where the keyword is in a link and precede it with &&&, which will be sufficient to avoid linking it. We use &&&, since WP would pass that
+		// to us as &amp;&amp;&amp; (if it occured in a post), so it would never be in the $content on its own.
+		// this has two steps. First, look for the keyword as linked text:
+		$content = preg_replace( '|(<a[^>]+>)(.*)('.$keyword.')(.*)(</a[^>]*>)|Ui', '$1$2&&&$3$4$5', $content);
+
+		// Next, look for the keyword inside tags. E.g. if they're linking every occurrence of "Google" manually, we don't want to find 
+		// <a href="http://google.com"> and change it to <a href="http://<a href="http://www.google.com">.com">
+		// More broadly, we don't want them linking anything that might be in a tag. (e.g. linking "strong" would screw up <strong>). 
+		// if you get problems with KB linker creating links where it shouldn't, this is the regex you should tinker with, most likely. Here goes:
+		$content = preg_replace( '|(<[^>]*)('.$keyword.')(.*>)|Ui', '$1&&&$2$3', $content);
+
+		// I'm sure a true master of regular expressions wouldn't need the previous two steps, and would simply write the replacement expression (below) better. But this works for me.
+	
+		// set the title attribute:
+		if (KBLINKER_USE_TITLES)
+			$title = ' title="'.$kblinker_title_text['before'].$keyword.$kblinker_title_text['after'].'"';
+		
+		// now that we've taken the keyword out of any links it appears in, let's look for the keyword elsewhere.
+		if ( 1 != $plurals ){	 // we do basically the same thing whether we're looking for plurals or not. Let's do non-plurals option first:
+			$content = preg_replace( '|(?<=[\s>;"\'/])('.$keyword.')(?=[\s<&.,!\'";:\-/])|i', '<a href="'.$url.'" class="kblinker"'.$target.$title.'>$1</a>', $content, 1);	// that "1" at the end limits it to replacing the keyword only once per post.
+			/* some notes about that regular expression to make modifying it easier for you if you're new to these things:
+			(?<=[\s>;"\'])
+				(?<=	marks it as a lookbehind assertion
+				to ensure that we are linking only complete words, we want keyword preceded by one of space, tag (>), entity (;) or certain kinds of punctuation (escaped with \ when necessary)
+				Note that '&' is NOT one of the allowed lookbehinds (or our '&&&' trick wouldn't work)
+			(?=[\s<&.,\'";:\-])
+				(?=	marks this as a lookahead assertion
+				again, we link only complete words. Must be followed by space, tag (<), entity (&), or certain kinds of punctuation. 
+				Note that some of the punctuations are escaped with \
+			*/
+		}else{	// if they want us to look for plurals too:
+			// this regex is almost identical to the non-plurals one, we just add an s? where necessary:
+			$content = preg_replace( '|(?<=[\s>;"\'/])('.$keyword.'s?)(?=[\s<&.,!\'";:\-/])|i', '<a href="'.$url.'" class="kblinker"'.$target.$title.'>$1</a>', $content, 1);	// that "1" at the end limits it to replacing once per post.
+		}
+	}
+	// get rid of our '&&&' things.
+	$content = str_replace( '&&&', '', $content);
+	return $content;
+}
+
+
+function kb_linker_options_page(){
+	$sample = 'wordpress->http://wordpress.org/
+google->http://www.google.com/ _blank
+kb linker->http://adambrown.info/b/widgets/kb-linker/
+knuckleheads->http://www.house.gov/';
+
+	if ( $_POST['kb_linker'] ){
+		$pairs = str_replace("\r", '', $_POST['kb_linker']);
+		$pairs = explode("\n", $pairs);
+		foreach( $pairs as $pair ){
+			$pair = trim( $pair ); // no leading or trailing spaces. Can mess with the "target" thing in function kb_linker()
+			$pair = explode( "->", $pair );
+			if ( ( '' != $pair[0] ) && ( '' != $pair[1] ) )
+				$new[ $pair[0] ] = $pair[1];
+		}
+		$pairs = $new;	// contains the pairs as an array for use by the filter
+		$text = $_POST['kb_linker'];	// contains the pairs as entered in the form for display below
+		
+		$plurals = ( 1 == $_POST['kb_plurals'] ) ? 1 : 0;
+		$option = array( 'pairs'=>$pairs, 'text'=>$text, 'plurals'=>$plurals );	// store both versions of the option, pairs and text
+		update_option( 'kb_linker', $option );
+		print '<div id="message" class="updated fade"><p><strong>KB Linker options updated.</strong> <a href="'.get_bloginfo('url').'">View site &raquo;</a></p></div>';
+	}else{
+		$option = get_option('kb_linker');
+		if (is_array($option)){
+			extract($option);
+		}else{
+			$text = $sample;
+			$plurals = 0;
+		}
+	}
+
+	$checked = ( 1 == $plurals ) ? 'checked="checked"' : '' ;
+
+	print '
+	<div class="wrap">
+	<h2>KB Linker</h2>
+	<p>KB Linker will link phrases you specify to sites you specify. For example, you could make it so that whenever "Wordpress" occurs in a post it is automatically linked to wordpress.org.</p>
+	<p>Enter your keyword-URL pairs in the box below. Each pair should appear on its own line. Separate each keyword from its respective link with "->". Look at the bottom of this page for important details. Below are a few examples to get you going. Note that the link to Google will open in a new window, since it is followed with "&nbsp;_blank" (note the space).</p>
+	<blockquote><pre>'.$sample.'</pre></blockquote>
+	<p>Alright, knock yourself out:</p>
+	
+	<form method="post" action="http://'.$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'].'">
+	<textarea id="kb_linker" name="kb_linker" rows="10" cols="45" class="widefat">'.$text.'</textarea>
+	<p><input type="checkbox" '.$checked.' name="kb_plurals" id="kb_plurals" value="1" /> Also link the keyword if it ends in <i>s</i> (i.e. plurals in certain languages)</p>
+	<p class="submit" style="width:420px;"><input type="submit" value="Submit &raquo;" /></p>
+	</form>
+	
+	<p>Considerations:</p>
+	<ul>
+		<li>URLs should be valid (i.e. begin with http://)</li>
+		<li>The same URL can appear on more than one line (i.e. with more than one keyword).</li>
+		<li>Because a word can only link to one site, a keyword should not appear on more than one line. If it does, only the last instance of the keyword will be matched to its URL.</li>
+		<li>If one of your keywords is a substring of the other--e.g. "download wordpress" and "wordpress"--then you should list the shorter one later than the first one.</li>
+		<li>Keywords are case-insensitive (e.g. "wordpress" is the same as "WoRdPrEsS").</li>
+		<li>Spaces count, so "wordpress" is not the same as "wordpress ".</li>
+		<li>Keywords will be linked only if they occur in your post as a word (or phrase), not as a partial word. So if one of your keywords is "a" (for some strange reason), it will be linked only when it occurs as the word "a"--when the letter "a" occurs within a word, it will not be linked.</li>
+		<li>You can use any valid target attribute, not just "_blank"--see <a href="http://www.w3schools.com/tags/tag_a.asp">W3C</a> for a list of valid targets.</li>
+	</ul>
+	</div>
+	';
+}
+
+function kb_linker_admin_page(){
+	add_submenu_page('options-general.php', 'KB Linker', 'KB Linker', 5, 'kb_linker.php', 'kb_linker_options_page');
+}
+
+add_filter('the_content', 'kb_linker', 9); // run before Dagon Design Sitemap Generator (runs at priority 10)
+add_action('admin_menu', 'kb_linker_admin_page');
+?>
Index: /afridex/plugins/kb-linker/README.txt
===================================================================
--- /afridex/plugins/kb-linker/README.txt (revision 21)
+++ /afridex/plugins/kb-linker/README.txt (revision 21)
@@ -0,0 +1,63 @@
+=== KB Linker ===
+Contributors: adamrbrown
+Donate link: http://adambrown.info/b/widgets/donate/
+Tags: link, seo, links
+Requires at least: 2.0
+Tested up to: 2.5
+Stable tag: trunk
+
+Looks for user-defined phrases in posts and automatically links them to specified URLs. Example: Link every occurrence of "Wordpress" to wp.org.
+
+== Description ==
+
+KB Linker will link phrases you specify to sites you specify. For example, you could make it so that whenever "Wordpress" occurs in a post it is automatically linked to wordpress.org. All you do is enter keyword/URL pairs into the Options=>KB Linker page in your admin menu. 
+
+You'll find more detailed instructions on the admin page. But really, it's pretty easy. And KB Linker does the hard work of (a) making sure that words aren't already linked and (b) making sure only whole words, not partial words, get linked.
+
+The plugin file is commented extensively, so if you want to tweak its behavior, give it a go.
+
+= Support =
+
+If you post your support questions as comments below, I probably won't see them. If the notes in the plugin file doesn't answer your questions, you can post support questions at the [KB Linker plugin page](http://adambrown.info/b/widgets/kb-linker/) on my site.
+
+== Installation ==
+
+1. Upload `kb_linker.php` to the `/wp-content/plugins/` directory.
+1. Activate the plugin through the 'Plugins' menu in WordPress.
+1. Go to the new 'Options => KB Linker' admin page. Follow the instructions.
+
+All done. Go to your blog's home page and admire all the automatically inserted links.
+
+== License ==
+
+This plugin is provided "as is" and without any warranty or expectation of function. I'll probably try to help you if you ask nicely, but I can't promise anything. You are welcome to use this plugin and modify it however you want, as long as you give credit where it is due. 
+
+But please don't redistribute this plugin from anywhere other than right here. Unless months go by and it looks like I've completely abandoned this thing, let's not get forks going without a darn good reason; that just confuses people. But send me your improvements and I'll add them in and include a shout-out to you here.
+
+== Screenshots ==
+
+Ain't none. Sorry.
+
+== Frequently Asked Questions ==
+
+= Why would I want this? =
+
+Well, that's obviously up to you. Maybe you don't. But if there is something that you're always linking to, this plugin might help you. For example, I've got a travel blog. I have set it up so that everytime I mention certain cities, the city's name gets linked automatically to a map of that city.
+
+This plugin could also serve SEO (search engine optimization) purposes if you're into that sort of thing; you could use it to ensure plenty of internal cross-linking between your posts.
+
+= Can I make the links open in new windows? =
+
+Yes, as of version 1.03. See the instructions on the plugin's admin page.
+
+= I have problems on my non-English blog. =
+
+Tweak the settings at the top of the plugin file.
+
+= Does the plugin do X? =
+
+Probably not. But this is a simple piece of code, so try modifying it to suit your whims. Take a look at the plugin file. I marked up the code heavily to make it easy to tweak. After looking at the code, if you have a question, you can post a comment on the plugin's page.
+
+= I have a question that isn't addressed here. =
+
+If you post your support questions as comments below, I probably won't see them. You can post support questions at the [KB Linker plugin page](http://adambrown.info/b/widgets/kb-linker/) on my site. But first, PLEASE read through the notes at the top of the plugin file.
