diff --git a/check.php b/check.php new file mode 100644 index 0000000..b63e606 --- /dev/null +++ b/check.php @@ -0,0 +1,106 @@ + str_replace(APP_PATH, "", $sf), 'md5' => md5_file($sf)); + } + } + self::message(0, '', $files); + } + + //下载文件 + static function download() + { + global $_GPC; + $entry = APP_PATH . trim($_GPC['path']); + if (is_file($entry) && self::files_filter($entry)) { + $content = file_get_contents($entry); + self::message(0, '', array('path' => $_GPC['path'], 'content' => base64_encode($content))); + } + self::message(1, '路径错误,文件不存在'); + } + + //移除无需更新文件 + static function files_filter($file) + { + $file_type = [ + '.log', + '.txt', + '.zip', + 'check.php', + 'install.php', + '.md', + 'LICENSE', + APP_PATH . 'addons/weliam_smartcity/data/', + 'manifest.xml', + APP_PATH . 'addons/weliam_smartcity/plugin/weliam_house/house/runtime/', + '/attachment/',//临时附件文件 + '/data/tpl',//页面模板缓存文件 + '/data/log',//日志文件 + ]; + foreach ($file_type as $value) { + if (strpos($file, $value) !== FALSE) { + return FALSE; + } + } + return TRUE; + } + + //根据路径返回当前路径下所有文件 + static function files_tree($path) + { + $files = array(); + $ds = glob($path . '/*'); + if (is_array($ds)) { + foreach ($ds as $entry) { + if (is_file($entry)) { + $files[] = $entry; + } + if (is_dir($entry)) { + $rs = self::files_tree($entry); + foreach ($rs as $f) { + $files[] = $f; + } + } + } + } + return $files; + } + + //调试函数 + static function wl_debug($array = array()) + { + echo "
";
+        print_r($array);
+        exit();
+    }
+
+    //数据输出函数
+    static function message($code = 0, $message = array(), $data = array())
+    {
+        die(json_encode(array('code' => $code, 'message' => $message, 'data' => $data)));
+    }
+}
+
diff --git a/framework/library/phpexcel/PHPExcel/CachedObjectStorageFactory.php b/framework/library/phpexcel/PHPExcel/CachedObjectStorageFactory.php
new file mode 100644
index 0000000..8d394fe
--- /dev/null
+++ b/framework/library/phpexcel/PHPExcel/CachedObjectStorageFactory.php
@@ -0,0 +1,251 @@
+ array(
+                                                    ),
+        self::cache_in_memory_gzip          => array(
+                                                    ),
+        self::cache_in_memory_serialized    => array(
+                                                    ),
+        self::cache_igbinary                => array(
+                                                    ),
+        self::cache_to_phpTemp              => array( 'memoryCacheSize' => '1MB'
+                                                    ),
+        self::cache_to_discISAM             => array( 'dir'             => NULL
+                                                    ),
+        self::cache_to_apc                  => array( 'cacheTime'       => 600
+                                                    ),
+        self::cache_to_memcache             => array( 'memcacheServer'  => 'localhost',
+                                                      'memcachePort'    => 11211,
+                                                      'cacheTime'       => 600
+                                                    ),
+        self::cache_to_wincache             => array( 'cacheTime'       => 600
+                                                    ),
+        self::cache_to_sqlite               => array(
+                                                    ),
+        self::cache_to_sqlite3              => array(
+                                                    ),
+    );
+
+
+    /**
+     * Arguments for the active cache storage method
+     *
+     * @var array of mixed array
+     */
+    private static $_storageMethodParameters = array();
+
+
+    /**
+     * Return the current cache storage method
+     *
+     * @return string|NULL
+     **/
+    public static function getCacheStorageMethod()
+    {
+        return self::$_cacheStorageMethod;
+    }   //    function getCacheStorageMethod()
+
+
+    /**
+     * Return the current cache storage class
+     *
+     * @return PHPExcel_CachedObjectStorage_ICache|NULL
+     **/
+    public static function getCacheStorageClass()
+    {
+        return self::$_cacheStorageClass;
+    }   //    function getCacheStorageClass()
+
+
+    /**
+     * Return the list of all possible cache storage methods
+     *
+     * @return string[]
+     **/
+    public static function getAllCacheStorageMethods()
+    {
+        return self::$_storageMethods;
+    }   //    function getCacheStorageMethods()
+
+
+    /**
+     * Return the list of all available cache storage methods
+     *
+     * @return string[]
+     **/
+    public static function getCacheStorageMethods()
+    {
+        $activeMethods = array();
+        foreach(self::$_storageMethods as $storageMethod) {
+            $cacheStorageClass = 'PHPExcel_CachedObjectStorage_' . $storageMethod;
+            if (call_user_func(array($cacheStorageClass, 'cacheMethodIsAvailable'))) {
+                $activeMethods[] = $storageMethod;
+            }
+        }
+        return $activeMethods;
+    }   //    function getCacheStorageMethods()
+
+
+    /**
+     * Identify the cache storage method to use
+     *
+     * @param    string            $method        Name of the method to use for cell cacheing
+     * @param    array of mixed    $arguments    Additional arguments to pass to the cell caching class
+     *                                        when instantiating
+     * @return boolean
+     **/
+    public static function initialize($method = self::cache_in_memory, $arguments = array())
+    {
+        if (!in_array($method,self::$_storageMethods)) {
+            return FALSE;
+        }
+
+        $cacheStorageClass = 'PHPExcel_CachedObjectStorage_'.$method;
+        if (!call_user_func(array( $cacheStorageClass,
+                                   'cacheMethodIsAvailable'))) {
+            return FALSE;
+        }
+
+        self::$_storageMethodParameters[$method] = self::$_storageMethodDefaultParameters[$method];
+        foreach($arguments as $k => $v) {
+            if (array_key_exists($k, self::$_storageMethodParameters[$method])) {
+                self::$_storageMethodParameters[$method][$k] = $v;
+            }
+        }
+
+        if (self::$_cacheStorageMethod === NULL) {
+            self::$_cacheStorageClass = 'PHPExcel_CachedObjectStorage_' . $method;
+            self::$_cacheStorageMethod = $method;
+        }
+        return TRUE;
+    }   //    function initialize()
+
+
+    /**
+     * Initialise the cache storage
+     *
+     * @param    PHPExcel_Worksheet     $parent        Enable cell caching for this worksheet
+     * @return    PHPExcel_CachedObjectStorage_ICache
+     **/
+    public static function getInstance(PHPExcel_Worksheet $parent)
+    {
+        $cacheMethodIsAvailable = TRUE;
+        if (self::$_cacheStorageMethod === NULL) {
+            $cacheMethodIsAvailable = self::initialize();
+        }
+
+        if ($cacheMethodIsAvailable) {
+            $instance = new self::$_cacheStorageClass( $parent,
+                                                       self::$_storageMethodParameters[self::$_cacheStorageMethod]
+                                                     );
+            if ($instance !== NULL) {
+                return $instance;
+            }
+        }
+
+        return FALSE;
+    }   //    function getInstance()
+
+
+    /**
+     * Clear the cache storage
+     *
+     **/
+	public static function finalize()
+	{
+		self::$_cacheStorageMethod = NULL;
+		self::$_cacheStorageClass = NULL;
+		self::$_storageMethodParameters = array();
+	}
+
+}
diff --git a/framework/library/phpexcel/PHPExcel/Calculation.php b/framework/library/phpexcel/PHPExcel/Calculation.php
new file mode 100644
index 0000000..4345ee5
--- /dev/null
+++ b/framework/library/phpexcel/PHPExcel/Calculation.php
@@ -0,0 +1,3891 @@
+=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})');
+		//	Named Range of cells
+		define('CALCULATION_REGEXP_NAMEDRANGE','((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)');
+	} else {
+		//	Cell reference (cell or range of cells, with or without a sheet reference)
+		define('CALCULATION_REGEXP_CELLREF','(((\w*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d+)');
+		//	Named Range of cells
+		define('CALCULATION_REGEXP_NAMEDRANGE','(((\w*)|(\'.*\')|(\".*\"))!)?([_A-Z][_A-Z0-9\.]*)');
+	}
+}
+
+
+/**
+ * PHPExcel_Calculation (Multiton)
+ *
+ * @category	PHPExcel
+ * @package		PHPExcel_Calculation
+ * @copyright	Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel)
+ */
+class PHPExcel_Calculation {
+
+	/** Constants				*/
+	/** Regular Expressions		*/
+	//	Numeric operand
+	const CALCULATION_REGEXP_NUMBER		= '[-+]?\d*\.?\d+(e[-+]?\d+)?';
+	//	String operand
+	const CALCULATION_REGEXP_STRING		= '"(?:[^"]|"")*"';
+	//	Opening bracket
+	const CALCULATION_REGEXP_OPENBRACE	= '\(';
+	//	Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
+	const CALCULATION_REGEXP_FUNCTION	= '@?([A-Z][A-Z0-9\.]*)[\s]*\(';
+	//	Cell reference (cell or range of cells, with or without a sheet reference)
+	const CALCULATION_REGEXP_CELLREF	= CALCULATION_REGEXP_CELLREF;
+	//	Named Range of cells
+	const CALCULATION_REGEXP_NAMEDRANGE	= CALCULATION_REGEXP_NAMEDRANGE;
+	//	Error
+	const CALCULATION_REGEXP_ERROR		= '\#[A-Z][A-Z0_\/]*[!\?]?';
+
+
+	/** constants */
+	const RETURN_ARRAY_AS_ERROR = 'error';
+	const RETURN_ARRAY_AS_VALUE = 'value';
+	const RETURN_ARRAY_AS_ARRAY = 'array';
+
+	private static $returnArrayAsType	= self::RETURN_ARRAY_AS_VALUE;
+
+
+	/**
+	 * Instance of this class
+	 *
+	 * @access	private
+	 * @var PHPExcel_Calculation
+	 */
+	private static $_instance;
+
+
+	/**
+	 * Instance of the workbook this Calculation Engine is using
+	 *
+	 * @access	private
+	 * @var PHPExcel
+	 */
+    private $_workbook;
+
+	/**
+	 * List of instances of the calculation engine that we've instantiated for individual workbooks
+	 *
+	 * @access	private
+	 * @var PHPExcel_Calculation[]
+	 */
+    private static $_workbookSets;
+
+	/**
+	 * Calculation cache
+	 *
+	 * @access	private
+	 * @var array
+	 */
+	private $_calculationCache = array ();
+
+
+	/**
+	 * Calculation cache enabled
+	 *
+	 * @access	private
+	 * @var boolean
+	 */
+	private $_calculationCacheEnabled = TRUE;
+
+
+	/**
+	 * List of operators that can be used within formulae
+	 * The true/false value indicates whether it is a binary operator or a unary operator
+	 *
+	 * @access	private
+	 * @var array
+	 */
+	private static $_operators			= array('+' => TRUE,	'-' => TRUE,	'*' => TRUE,	'/' => TRUE,
+												'^' => TRUE,	'&' => TRUE,	'%' => FALSE,	'~' => FALSE,
+												'>' => TRUE,	'<' => TRUE,	'=' => TRUE,	'>=' => TRUE,
+												'<=' => TRUE,	'<>' => TRUE,	'|' => TRUE,	':' => TRUE
+											   );
+
+
+	/**
+	 * List of binary operators (those that expect two operands)
+	 *
+	 * @access	private
+	 * @var array
+	 */
+	private static $_binaryOperators	= array('+' => TRUE,	'-' => TRUE,	'*' => TRUE,	'/' => TRUE,
+												'^' => TRUE,	'&' => TRUE,	'>' => TRUE,	'<' => TRUE,
+												'=' => TRUE,	'>=' => TRUE,	'<=' => TRUE,	'<>' => TRUE,
+												'|' => TRUE,	':' => TRUE
+											   );
+
+	/**
+	 * The debug log generated by the calculation engine
+	 *
+	 * @access	private
+	 * @var PHPExcel_CalcEngine_Logger
+	 *
+	 */
+	private $debugLog;
+
+	/**
+	 * Flag to determine how formula errors should be handled
+	 *		If true, then a user error will be triggered
+	 *		If false, then an exception will be thrown
+	 *
+	 * @access	public
+	 * @var boolean
+	 *
+	 */
+	public $suppressFormulaErrors = FALSE;
+
+	/**
+	 * Error message for any error that was raised/thrown by the calculation engine
+	 *
+	 * @access	public
+	 * @var string
+	 *
+	 */
+	public $formulaError = NULL;
+
+	/**
+	 * An array of the nested cell references accessed by the calculation engine, used for the debug log
+	 *
+	 * @access	private
+	 * @var array of string
+	 *
+	 */
+	private $_cyclicReferenceStack;
+
+	/**
+	 * Current iteration counter for cyclic formulae
+	 * If the value is 0 (or less) then cyclic formulae will throw an exception,
+	 *    otherwise they will iterate to the limit defined here before returning a result
+	 *
+	 * @var integer
+	 *
+	 */
+	private $_cyclicFormulaCount = 0;
+
+	private $_cyclicFormulaCell = '';
+
+	/**
+	 * Number of iterations for cyclic formulae
+	 *
+	 * @var integer
+	 *
+	 */
+	public $cyclicFormulaCount = 0;
+
+	/**
+	 * Precision used for calculations
+	 *
+	 * @var integer
+	 *
+	 */
+	private $_savedPrecision	= 14;
+
+
+	/**
+	 * The current locale setting
+	 *
+	 * @var string
+	 *
+	 */
+	private static $_localeLanguage = 'en_us';					//	US English	(default locale)
+
+	/**
+	 * List of available locale settings
+	 * Note that this is read for the locale subdirectory only when requested
+	 *
+	 * @var string[]
+	 *
+	 */
+	private static $_validLocaleLanguages = array(	'en'		//	English		(default language)
+												 );
+	/**
+	 * Locale-specific argument separator for function arguments
+	 *
+	 * @var string
+	 *
+	 */
+	private static $_localeArgumentSeparator = ',';
+	private static $_localeFunctions = array();
+
+	/**
+	 * Locale-specific translations for Excel constants (True, False and Null)
+	 *
+	 * @var string[]
+	 *
+	 */
+	public static $_localeBoolean = array(	'TRUE'	=> 'TRUE',
+											'FALSE'	=> 'FALSE',
+											'NULL'	=> 'NULL'
+										  );
+
+
+	/**
+	 * Excel constant string translations to their PHP equivalents
+	 * Constant conversion from text name/value to actual (datatyped) value
+	 *
+	 * @var string[]
+	 *
+	 */
+	private static $_ExcelConstants = array('TRUE'	=> TRUE,
+											'FALSE'	=> FALSE,
+											'NULL'	=> NULL
+										   );
+
+	//	PHPExcel functions
+	private static $_PHPExcelFunctions = array(	// PHPExcel functions
+				'ABS'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'abs',
+												 'argumentCount'	=>	'1'
+												),
+				'ACCRINT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::ACCRINT',
+												 'argumentCount'	=>	'4-7'
+												),
+				'ACCRINTM'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::ACCRINTM',
+												 'argumentCount'	=>	'3-5'
+												),
+				'ACOS'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'acos',
+												 'argumentCount'	=>	'1'
+												),
+				'ACOSH'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'acosh',
+												 'argumentCount'	=>	'1'
+												),
+				'ADDRESS'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::CELL_ADDRESS',
+												 'argumentCount'	=>	'2-5'
+												),
+				'AMORDEGRC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::AMORDEGRC',
+												 'argumentCount'	=>	'6,7'
+												),
+				'AMORLINC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::AMORLINC',
+												 'argumentCount'	=>	'6,7'
+												),
+				'AND'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Logical::LOGICAL_AND',
+												 'argumentCount'	=>	'1+'
+												),
+				'AREAS'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1'
+												),
+				'ASC'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1'
+												),
+				'ASIN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'asin',
+												 'argumentCount'	=>	'1'
+												),
+				'ASINH'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'asinh',
+												 'argumentCount'	=>	'1'
+												),
+				'ATAN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'atan',
+												 'argumentCount'	=>	'1'
+												),
+				'ATAN2'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::ATAN2',
+												 'argumentCount'	=>	'2'
+												),
+				'ATANH'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'atanh',
+												 'argumentCount'	=>	'1'
+												),
+				'AVEDEV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::AVEDEV',
+												 'argumentCount'	=>	'1+'
+												),
+				'AVERAGE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::AVERAGE',
+												 'argumentCount'	=>	'1+'
+												),
+				'AVERAGEA'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::AVERAGEA',
+												 'argumentCount'	=>	'1+'
+												),
+				'AVERAGEIF'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::AVERAGEIF',
+												 'argumentCount'	=>	'2,3'
+												),
+				'AVERAGEIFS'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'3+'
+												),
+				'BAHTTEXT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1'
+												),
+				'BESSELI'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::BESSELI',
+												 'argumentCount'	=>	'2'
+												),
+				'BESSELJ'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::BESSELJ',
+												 'argumentCount'	=>	'2'
+												),
+				'BESSELK'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::BESSELK',
+												 'argumentCount'	=>	'2'
+												),
+				'BESSELY'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::BESSELY',
+												 'argumentCount'	=>	'2'
+												),
+				'BETADIST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::BETADIST',
+												 'argumentCount'	=>	'3-5'
+												),
+				'BETAINV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::BETAINV',
+												 'argumentCount'	=>	'3-5'
+												),
+				'BIN2DEC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::BINTODEC',
+												 'argumentCount'	=>	'1'
+												),
+				'BIN2HEX'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::BINTOHEX',
+												 'argumentCount'	=>	'1,2'
+												),
+				'BIN2OCT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::BINTOOCT',
+												 'argumentCount'	=>	'1,2'
+												),
+				'BINOMDIST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::BINOMDIST',
+												 'argumentCount'	=>	'4'
+												),
+				'CEILING'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::CEILING',
+												 'argumentCount'	=>	'2'
+												),
+				'CELL'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1,2'
+												),
+				'CHAR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::CHARACTER',
+												 'argumentCount'	=>	'1'
+												),
+				'CHIDIST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::CHIDIST',
+												 'argumentCount'	=>	'2'
+												),
+				'CHIINV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::CHIINV',
+												 'argumentCount'	=>	'2'
+												),
+				'CHITEST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'2'
+												),
+				'CHOOSE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::CHOOSE',
+												 'argumentCount'	=>	'2+'
+												),
+				'CLEAN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::TRIMNONPRINTABLE',
+												 'argumentCount'	=>	'1'
+												),
+				'CODE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::ASCIICODE',
+												 'argumentCount'	=>	'1'
+												),
+				'COLUMN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::COLUMN',
+												 'argumentCount'	=>	'-1',
+												 'passByReference'	=>	array(TRUE)
+												),
+				'COLUMNS'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::COLUMNS',
+												 'argumentCount'	=>	'1'
+												),
+				'COMBIN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::COMBIN',
+												 'argumentCount'	=>	'2'
+												),
+				'COMPLEX'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::COMPLEX',
+												 'argumentCount'	=>	'2,3'
+												),
+				'CONCATENATE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::CONCATENATE',
+												 'argumentCount'	=>	'1+'
+												),
+				'CONFIDENCE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::CONFIDENCE',
+												 'argumentCount'	=>	'3'
+												),
+				'CONVERT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::CONVERTUOM',
+												 'argumentCount'	=>	'3'
+												),
+				'CORREL'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::CORREL',
+												 'argumentCount'	=>	'2'
+												),
+				'COS'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'cos',
+												 'argumentCount'	=>	'1'
+												),
+				'COSH'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'cosh',
+												 'argumentCount'	=>	'1'
+												),
+				'COUNT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::COUNT',
+												 'argumentCount'	=>	'1+'
+												),
+				'COUNTA'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::COUNTA',
+												 'argumentCount'	=>	'1+'
+												),
+				'COUNTBLANK'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::COUNTBLANK',
+												 'argumentCount'	=>	'1'
+												),
+				'COUNTIF'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::COUNTIF',
+												 'argumentCount'	=>	'2'
+												),
+				'COUNTIFS'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'2'
+												),
+				'COUPDAYBS'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::COUPDAYBS',
+												 'argumentCount'	=>	'3,4'
+												),
+				'COUPDAYS'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::COUPDAYS',
+												 'argumentCount'	=>	'3,4'
+												),
+				'COUPDAYSNC'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::COUPDAYSNC',
+												 'argumentCount'	=>	'3,4'
+												),
+				'COUPNCD'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::COUPNCD',
+												 'argumentCount'	=>	'3,4'
+												),
+				'COUPNUM'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::COUPNUM',
+												 'argumentCount'	=>	'3,4'
+												),
+				'COUPPCD'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::COUPPCD',
+												 'argumentCount'	=>	'3,4'
+												),
+				'COVAR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::COVAR',
+												 'argumentCount'	=>	'2'
+												),
+				'CRITBINOM'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::CRITBINOM',
+												 'argumentCount'	=>	'3'
+												),
+				'CUBEKPIMEMBER'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_CUBE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'?'
+												),
+				'CUBEMEMBER'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_CUBE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'?'
+												),
+				'CUBEMEMBERPROPERTY'	=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_CUBE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'?'
+												),
+				'CUBERANKEDMEMBER'		=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_CUBE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'?'
+												),
+				'CUBESET'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_CUBE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'?'
+												),
+				'CUBESETCOUNT'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_CUBE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'?'
+												),
+				'CUBEVALUE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_CUBE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'?'
+												),
+				'CUMIPMT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::CUMIPMT',
+												 'argumentCount'	=>	'6'
+												),
+				'CUMPRINC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::CUMPRINC',
+												 'argumentCount'	=>	'6'
+												),
+				'DATE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::DATE',
+												 'argumentCount'	=>	'3'
+												),
+				'DATEDIF'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::DATEDIF',
+												 'argumentCount'	=>	'2,3'
+												),
+				'DATEVALUE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::DATEVALUE',
+												 'argumentCount'	=>	'1'
+												),
+				'DAVERAGE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DAVERAGE',
+												 'argumentCount'	=>	'3'
+												),
+				'DAY'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::DAYOFMONTH',
+												 'argumentCount'	=>	'1'
+												),
+				'DAYS360'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::DAYS360',
+												 'argumentCount'	=>	'2,3'
+												),
+				'DB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::DB',
+												 'argumentCount'	=>	'4,5'
+												),
+				'DCOUNT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DCOUNT',
+												 'argumentCount'	=>	'3'
+												),
+				'DCOUNTA'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DCOUNTA',
+												 'argumentCount'	=>	'3'
+												),
+				'DDB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::DDB',
+												 'argumentCount'	=>	'4,5'
+												),
+				'DEC2BIN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::DECTOBIN',
+												 'argumentCount'	=>	'1,2'
+												),
+				'DEC2HEX'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::DECTOHEX',
+												 'argumentCount'	=>	'1,2'
+												),
+				'DEC2OCT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::DECTOOCT',
+												 'argumentCount'	=>	'1,2'
+												),
+				'DEGREES'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'rad2deg',
+												 'argumentCount'	=>	'1'
+												),
+				'DELTA'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::DELTA',
+												 'argumentCount'	=>	'1,2'
+												),
+				'DEVSQ'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::DEVSQ',
+												 'argumentCount'	=>	'1+'
+												),
+				'DGET'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DGET',
+												 'argumentCount'	=>	'3'
+												),
+				'DISC'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::DISC',
+												 'argumentCount'	=>	'4,5'
+												),
+				'DMAX'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DMAX',
+												 'argumentCount'	=>	'3'
+												),
+				'DMIN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DMIN',
+												 'argumentCount'	=>	'3'
+												),
+				'DOLLAR'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::DOLLAR',
+												 'argumentCount'	=>	'1,2'
+												),
+				'DOLLARDE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::DOLLARDE',
+												 'argumentCount'	=>	'2'
+												),
+				'DOLLARFR'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::DOLLARFR',
+												 'argumentCount'	=>	'2'
+												),
+				'DPRODUCT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DPRODUCT',
+												 'argumentCount'	=>	'3'
+												),
+				'DSTDEV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DSTDEV',
+												 'argumentCount'	=>	'3'
+												),
+				'DSTDEVP'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DSTDEVP',
+												 'argumentCount'	=>	'3'
+												),
+				'DSUM'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DSUM',
+												 'argumentCount'	=>	'3'
+												),
+				'DURATION'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'5,6'
+												),
+				'DVAR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DVAR',
+												 'argumentCount'	=>	'3'
+												),
+				'DVARP'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Database::DVARP',
+												 'argumentCount'	=>	'3'
+												),
+				'EDATE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::EDATE',
+												 'argumentCount'	=>	'2'
+												),
+				'EFFECT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::EFFECT',
+												 'argumentCount'	=>	'2'
+												),
+				'EOMONTH'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::EOMONTH',
+												 'argumentCount'	=>	'2'
+												),
+				'ERF'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::ERF',
+												 'argumentCount'	=>	'1,2'
+												),
+				'ERFC'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::ERFC',
+												 'argumentCount'	=>	'1'
+												),
+				'ERROR.TYPE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::ERROR_TYPE',
+												 'argumentCount'	=>	'1'
+												),
+				'EVEN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::EVEN',
+												 'argumentCount'	=>	'1'
+												),
+				'EXACT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'2'
+												),
+				'EXP'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'exp',
+												 'argumentCount'	=>	'1'
+												),
+				'EXPONDIST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::EXPONDIST',
+												 'argumentCount'	=>	'3'
+												),
+				'FACT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::FACT',
+												 'argumentCount'	=>	'1'
+												),
+				'FACTDOUBLE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::FACTDOUBLE',
+												 'argumentCount'	=>	'1'
+												),
+				'FALSE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Logical::FALSE',
+												 'argumentCount'	=>	'0'
+												),
+				'FDIST'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'3'
+												),
+				'FIND'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::SEARCHSENSITIVE',
+												 'argumentCount'	=>	'2,3'
+												),
+				'FINDB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::SEARCHSENSITIVE',
+												 'argumentCount'	=>	'2,3'
+												),
+				'FINV'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'3'
+												),
+				'FISHER'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::FISHER',
+												 'argumentCount'	=>	'1'
+												),
+				'FISHERINV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::FISHERINV',
+												 'argumentCount'	=>	'1'
+												),
+				'FIXED'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::FIXEDFORMAT',
+												 'argumentCount'	=>	'1-3'
+												),
+				'FLOOR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::FLOOR',
+												 'argumentCount'	=>	'2'
+												),
+				'FORECAST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::FORECAST',
+												 'argumentCount'	=>	'3'
+												),
+				'FREQUENCY'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'2'
+												),
+				'FTEST'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'2'
+												),
+				'FV'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::FV',
+												 'argumentCount'	=>	'3-5'
+												),
+				'FVSCHEDULE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::FVSCHEDULE',
+												 'argumentCount'	=>	'2'
+												),
+				'GAMMADIST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::GAMMADIST',
+												 'argumentCount'	=>	'4'
+												),
+				'GAMMAINV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::GAMMAINV',
+												 'argumentCount'	=>	'3'
+												),
+				'GAMMALN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::GAMMALN',
+												 'argumentCount'	=>	'1'
+												),
+				'GCD'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::GCD',
+												 'argumentCount'	=>	'1+'
+												),
+				'GEOMEAN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::GEOMEAN',
+												 'argumentCount'	=>	'1+'
+												),
+				'GESTEP'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::GESTEP',
+												 'argumentCount'	=>	'1,2'
+												),
+				'GETPIVOTDATA'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'2+'
+												),
+				'GROWTH'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::GROWTH',
+												 'argumentCount'	=>	'1-4'
+												),
+				'HARMEAN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::HARMEAN',
+												 'argumentCount'	=>	'1+'
+												),
+				'HEX2BIN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::HEXTOBIN',
+												 'argumentCount'	=>	'1,2'
+												),
+				'HEX2DEC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::HEXTODEC',
+												 'argumentCount'	=>	'1'
+												),
+				'HEX2OCT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::HEXTOOCT',
+												 'argumentCount'	=>	'1,2'
+												),
+				'HLOOKUP'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'3,4'
+												),
+				'HOUR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::HOUROFDAY',
+												 'argumentCount'	=>	'1'
+												),
+				'HYPERLINK'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::HYPERLINK',
+												 'argumentCount'	=>	'1,2',
+												 'passCellReference'=>	TRUE
+												),
+				'HYPGEOMDIST'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::HYPGEOMDIST',
+												 'argumentCount'	=>	'4'
+												),
+				'IF'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Logical::STATEMENT_IF',
+												 'argumentCount'	=>	'1-3'
+												),
+				'IFERROR'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Logical::IFERROR',
+												 'argumentCount'	=>	'2'
+												),
+				'IMABS'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMABS',
+												 'argumentCount'	=>	'1'
+												),
+				'IMAGINARY'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMAGINARY',
+												 'argumentCount'	=>	'1'
+												),
+				'IMARGUMENT'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMARGUMENT',
+												 'argumentCount'	=>	'1'
+												),
+				'IMCONJUGATE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMCONJUGATE',
+												 'argumentCount'	=>	'1'
+												),
+				'IMCOS'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMCOS',
+												 'argumentCount'	=>	'1'
+												),
+				'IMDIV'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMDIV',
+												 'argumentCount'	=>	'2'
+												),
+				'IMEXP'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMEXP',
+												 'argumentCount'	=>	'1'
+												),
+				'IMLN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMLN',
+												 'argumentCount'	=>	'1'
+												),
+				'IMLOG10'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMLOG10',
+												 'argumentCount'	=>	'1'
+												),
+				'IMLOG2'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMLOG2',
+												 'argumentCount'	=>	'1'
+												),
+				'IMPOWER'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMPOWER',
+												 'argumentCount'	=>	'2'
+												),
+				'IMPRODUCT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMPRODUCT',
+												 'argumentCount'	=>	'1+'
+												),
+				'IMREAL'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMREAL',
+												 'argumentCount'	=>	'1'
+												),
+				'IMSIN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMSIN',
+												 'argumentCount'	=>	'1'
+												),
+				'IMSQRT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMSQRT',
+												 'argumentCount'	=>	'1'
+												),
+				'IMSUB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMSUB',
+												 'argumentCount'	=>	'2'
+												),
+				'IMSUM'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::IMSUM',
+												 'argumentCount'	=>	'1+'
+												),
+				'INDEX'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::INDEX',
+												 'argumentCount'	=>	'1-4'
+												),
+				'INDIRECT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::INDIRECT',
+												 'argumentCount'	=>	'1,2',
+												 'passCellReference'=>	TRUE
+												),
+				'INFO'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1'
+												),
+				'INT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::INT',
+												 'argumentCount'	=>	'1'
+												),
+				'INTERCEPT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::INTERCEPT',
+												 'argumentCount'	=>	'2'
+												),
+				'INTRATE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::INTRATE',
+												 'argumentCount'	=>	'4,5'
+												),
+				'IPMT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::IPMT',
+												 'argumentCount'	=>	'4-6'
+												),
+				'IRR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::IRR',
+												 'argumentCount'	=>	'1,2'
+												),
+				'ISBLANK'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_BLANK',
+												 'argumentCount'	=>	'1'
+												),
+				'ISERR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_ERR',
+												 'argumentCount'	=>	'1'
+												),
+				'ISERROR'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_ERROR',
+												 'argumentCount'	=>	'1'
+												),
+				'ISEVEN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_EVEN',
+												 'argumentCount'	=>	'1'
+												),
+				'ISLOGICAL'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_LOGICAL',
+												 'argumentCount'	=>	'1'
+												),
+				'ISNA'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_NA',
+												 'argumentCount'	=>	'1'
+												),
+				'ISNONTEXT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_NONTEXT',
+												 'argumentCount'	=>	'1'
+												),
+				'ISNUMBER'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_NUMBER',
+												 'argumentCount'	=>	'1'
+												),
+				'ISODD'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_ODD',
+												 'argumentCount'	=>	'1'
+												),
+				'ISPMT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::ISPMT',
+												 'argumentCount'	=>	'4'
+												),
+				'ISREF'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1'
+												),
+				'ISTEXT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::IS_TEXT',
+												 'argumentCount'	=>	'1'
+												),
+				'JIS'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1'
+												),
+				'KURT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::KURT',
+												 'argumentCount'	=>	'1+'
+												),
+				'LARGE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::LARGE',
+												 'argumentCount'	=>	'2'
+												),
+				'LCM'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::LCM',
+												 'argumentCount'	=>	'1+'
+												),
+				'LEFT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::LEFT',
+												 'argumentCount'	=>	'1,2'
+												),
+				'LEFTB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::LEFT',
+												 'argumentCount'	=>	'1,2'
+												),
+				'LEN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::STRINGLENGTH',
+												 'argumentCount'	=>	'1'
+												),
+				'LENB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::STRINGLENGTH',
+												 'argumentCount'	=>	'1'
+												),
+				'LINEST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::LINEST',
+												 'argumentCount'	=>	'1-4'
+												),
+				'LN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'log',
+												 'argumentCount'	=>	'1'
+												),
+				'LOG'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::LOG_BASE',
+												 'argumentCount'	=>	'1,2'
+												),
+				'LOG10'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'log10',
+												 'argumentCount'	=>	'1'
+												),
+				'LOGEST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::LOGEST',
+												 'argumentCount'	=>	'1-4'
+												),
+				'LOGINV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::LOGINV',
+												 'argumentCount'	=>	'3'
+												),
+				'LOGNORMDIST'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::LOGNORMDIST',
+												 'argumentCount'	=>	'3'
+												),
+				'LOOKUP'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::LOOKUP',
+												 'argumentCount'	=>	'2,3'
+												),
+				'LOWER'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::LOWERCASE',
+												 'argumentCount'	=>	'1'
+												),
+				'MATCH'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::MATCH',
+												 'argumentCount'	=>	'2,3'
+												),
+				'MAX'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::MAX',
+												 'argumentCount'	=>	'1+'
+												),
+				'MAXA'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::MAXA',
+												 'argumentCount'	=>	'1+'
+												),
+				'MAXIF'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::MAXIF',
+												 'argumentCount'	=>	'2+'
+												),
+				'MDETERM'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::MDETERM',
+												 'argumentCount'	=>	'1'
+												),
+				'MDURATION'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'5,6'
+												),
+				'MEDIAN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::MEDIAN',
+												 'argumentCount'	=>	'1+'
+												),
+				'MEDIANIF'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'2+'
+												),
+				'MID'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::MID',
+												 'argumentCount'	=>	'3'
+												),
+				'MIDB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::MID',
+												 'argumentCount'	=>	'3'
+												),
+				'MIN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::MIN',
+												 'argumentCount'	=>	'1+'
+												),
+				'MINA'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::MINA',
+												 'argumentCount'	=>	'1+'
+												),
+				'MINIF'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::MINIF',
+												 'argumentCount'	=>	'2+'
+												),
+				'MINUTE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::MINUTEOFHOUR',
+												 'argumentCount'	=>	'1'
+												),
+				'MINVERSE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::MINVERSE',
+												 'argumentCount'	=>	'1'
+												),
+				'MIRR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::MIRR',
+												 'argumentCount'	=>	'3'
+												),
+				'MMULT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::MMULT',
+												 'argumentCount'	=>	'2'
+												),
+				'MOD'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::MOD',
+												 'argumentCount'	=>	'2'
+												),
+				'MODE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::MODE',
+												 'argumentCount'	=>	'1+'
+												),
+				'MONTH'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::MONTHOFYEAR',
+												 'argumentCount'	=>	'1'
+												),
+				'MROUND'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::MROUND',
+												 'argumentCount'	=>	'2'
+												),
+				'MULTINOMIAL'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::MULTINOMIAL',
+												 'argumentCount'	=>	'1+'
+												),
+				'N'						=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::N',
+												 'argumentCount'	=>	'1'
+												),
+				'NA'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::NA',
+												 'argumentCount'	=>	'0'
+												),
+				'NEGBINOMDIST'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::NEGBINOMDIST',
+												 'argumentCount'	=>	'3'
+												),
+				'NETWORKDAYS'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::NETWORKDAYS',
+												 'argumentCount'	=>	'2+'
+												),
+				'NOMINAL'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::NOMINAL',
+												 'argumentCount'	=>	'2'
+												),
+				'NORMDIST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::NORMDIST',
+												 'argumentCount'	=>	'4'
+												),
+				'NORMINV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::NORMINV',
+												 'argumentCount'	=>	'3'
+												),
+				'NORMSDIST'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::NORMSDIST',
+												 'argumentCount'	=>	'1'
+												),
+				'NORMSINV'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::NORMSINV',
+												 'argumentCount'	=>	'1'
+												),
+				'NOT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Logical::NOT',
+												 'argumentCount'	=>	'1'
+												),
+				'NOW'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::DATETIMENOW',
+												 'argumentCount'	=>	'0'
+												),
+				'NPER'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::NPER',
+												 'argumentCount'	=>	'3-5'
+												),
+				'NPV'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::NPV',
+												 'argumentCount'	=>	'2+'
+												),
+				'OCT2BIN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::OCTTOBIN',
+												 'argumentCount'	=>	'1,2'
+												),
+				'OCT2DEC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::OCTTODEC',
+												 'argumentCount'	=>	'1'
+												),
+				'OCT2HEX'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+												 'functionCall'		=>	'PHPExcel_Calculation_Engineering::OCTTOHEX',
+												 'argumentCount'	=>	'1,2'
+												),
+				'ODD'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::ODD',
+												 'argumentCount'	=>	'1'
+												),
+				'ODDFPRICE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'8,9'
+												),
+				'ODDFYIELD'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'8,9'
+												),
+				'ODDLPRICE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'7,8'
+												),
+				'ODDLYIELD'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'7,8'
+												),
+				'OFFSET'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::OFFSET',
+												 'argumentCount'	=>	'3,5',
+												 'passCellReference'=>	TRUE,
+												 'passByReference'	=>	array(TRUE)
+												),
+				'OR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Logical::LOGICAL_OR',
+												 'argumentCount'	=>	'1+'
+												),
+				'PEARSON'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::CORREL',
+												 'argumentCount'	=>	'2'
+												),
+				'PERCENTILE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::PERCENTILE',
+												 'argumentCount'	=>	'2'
+												),
+				'PERCENTRANK'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::PERCENTRANK',
+												 'argumentCount'	=>	'2,3'
+												),
+				'PERMUT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::PERMUT',
+												 'argumentCount'	=>	'2'
+												),
+				'PHONETIC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1'
+												),
+				'PI'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'pi',
+												 'argumentCount'	=>	'0'
+												),
+				'PMT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::PMT',
+												 'argumentCount'	=>	'3-5'
+												),
+				'POISSON'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::POISSON',
+												 'argumentCount'	=>	'3'
+												),
+				'POWER'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::POWER',
+												 'argumentCount'	=>	'2'
+												),
+				'PPMT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::PPMT',
+												 'argumentCount'	=>	'4-6'
+												),
+				'PRICE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::PRICE',
+												 'argumentCount'	=>	'6,7'
+												),
+				'PRICEDISC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::PRICEDISC',
+												 'argumentCount'	=>	'4,5'
+												),
+				'PRICEMAT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::PRICEMAT',
+												 'argumentCount'	=>	'5,6'
+												),
+				'PROB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'3,4'
+												),
+				'PRODUCT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::PRODUCT',
+												 'argumentCount'	=>	'1+'
+												),
+				'PROPER'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::PROPERCASE',
+												 'argumentCount'	=>	'1'
+												),
+				'PV'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::PV',
+												 'argumentCount'	=>	'3-5'
+												),
+				'QUARTILE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::QUARTILE',
+												 'argumentCount'	=>	'2'
+												),
+				'QUOTIENT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::QUOTIENT',
+												 'argumentCount'	=>	'2'
+												),
+				'RADIANS'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'deg2rad',
+												 'argumentCount'	=>	'1'
+												),
+				'RAND'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::RAND',
+												 'argumentCount'	=>	'0'
+												),
+				'RANDBETWEEN'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::RAND',
+												 'argumentCount'	=>	'2'
+												),
+				'RANK'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::RANK',
+												 'argumentCount'	=>	'2,3'
+												),
+				'RATE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::RATE',
+												 'argumentCount'	=>	'3-6'
+												),
+				'RECEIVED'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::RECEIVED',
+												 'argumentCount'	=>	'4-5'
+												),
+				'REPLACE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::REPLACE',
+												 'argumentCount'	=>	'4'
+												),
+				'REPLACEB'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::REPLACE',
+												 'argumentCount'	=>	'4'
+												),
+				'REPT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'str_repeat',
+												 'argumentCount'	=>	'2'
+												),
+				'RIGHT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::RIGHT',
+												 'argumentCount'	=>	'1,2'
+												),
+				'RIGHTB'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::RIGHT',
+												 'argumentCount'	=>	'1,2'
+												),
+				'ROMAN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::ROMAN',
+												 'argumentCount'	=>	'1,2'
+												),
+				'ROUND'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'round',
+												 'argumentCount'	=>	'2'
+												),
+				'ROUNDDOWN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::ROUNDDOWN',
+												 'argumentCount'	=>	'2'
+												),
+				'ROUNDUP'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::ROUNDUP',
+												 'argumentCount'	=>	'2'
+												),
+				'ROW'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::ROW',
+												 'argumentCount'	=>	'-1',
+												 'passByReference'	=>	array(TRUE)
+												),
+				'ROWS'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::ROWS',
+												 'argumentCount'	=>	'1'
+												),
+				'RSQ'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::RSQ',
+												 'argumentCount'	=>	'2'
+												),
+				'RTD'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1+'
+												),
+				'SEARCH'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::SEARCHINSENSITIVE',
+												 'argumentCount'	=>	'2,3'
+												),
+				'SEARCHB'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::SEARCHINSENSITIVE',
+												 'argumentCount'	=>	'2,3'
+												),
+				'SECOND'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::SECONDOFMINUTE',
+												 'argumentCount'	=>	'1'
+												),
+				'SERIESSUM'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SERIESSUM',
+												 'argumentCount'	=>	'4'
+												),
+				'SIGN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SIGN',
+												 'argumentCount'	=>	'1'
+												),
+				'SIN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'sin',
+												 'argumentCount'	=>	'1'
+												),
+				'SINH'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'sinh',
+												 'argumentCount'	=>	'1'
+												),
+				'SKEW'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::SKEW',
+												 'argumentCount'	=>	'1+'
+												),
+				'SLN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::SLN',
+												 'argumentCount'	=>	'3'
+												),
+				'SLOPE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::SLOPE',
+												 'argumentCount'	=>	'2'
+												),
+				'SMALL'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::SMALL',
+												 'argumentCount'	=>	'2'
+												),
+				'SQRT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'sqrt',
+												 'argumentCount'	=>	'1'
+												),
+				'SQRTPI'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SQRTPI',
+												 'argumentCount'	=>	'1'
+												),
+				'STANDARDIZE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::STANDARDIZE',
+												 'argumentCount'	=>	'3'
+												),
+				'STDEV'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::STDEV',
+												 'argumentCount'	=>	'1+'
+												),
+				'STDEVA'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::STDEVA',
+												 'argumentCount'	=>	'1+'
+												),
+				'STDEVP'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::STDEVP',
+												 'argumentCount'	=>	'1+'
+												),
+				'STDEVPA'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::STDEVPA',
+												 'argumentCount'	=>	'1+'
+												),
+				'STEYX'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::STEYX',
+												 'argumentCount'	=>	'2'
+												),
+				'SUBSTITUTE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::SUBSTITUTE',
+												 'argumentCount'	=>	'3,4'
+												),
+				'SUBTOTAL'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SUBTOTAL',
+												 'argumentCount'	=>	'2+'
+												),
+				'SUM'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SUM',
+												 'argumentCount'	=>	'1+'
+												),
+				'SUMIF'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SUMIF',
+												 'argumentCount'	=>	'2,3'
+												),
+				'SUMIFS'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'?'
+												),
+				'SUMPRODUCT'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SUMPRODUCT',
+												 'argumentCount'	=>	'1+'
+												),
+				'SUMSQ'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SUMSQ',
+												 'argumentCount'	=>	'1+'
+												),
+				'SUMX2MY2'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SUMX2MY2',
+												 'argumentCount'	=>	'2'
+												),
+				'SUMX2PY2'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SUMX2PY2',
+												 'argumentCount'	=>	'2'
+												),
+				'SUMXMY2'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::SUMXMY2',
+												 'argumentCount'	=>	'2'
+												),
+				'SYD'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::SYD',
+												 'argumentCount'	=>	'4'
+												),
+				'T'						=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::RETURNSTRING',
+												 'argumentCount'	=>	'1'
+												),
+				'TAN'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'tan',
+												 'argumentCount'	=>	'1'
+												),
+				'TANH'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'tanh',
+												 'argumentCount'	=>	'1'
+												),
+				'TBILLEQ'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::TBILLEQ',
+												 'argumentCount'	=>	'3'
+												),
+				'TBILLPRICE'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::TBILLPRICE',
+												 'argumentCount'	=>	'3'
+												),
+				'TBILLYIELD'			=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::TBILLYIELD',
+												 'argumentCount'	=>	'3'
+												),
+				'TDIST'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::TDIST',
+												 'argumentCount'	=>	'3'
+												),
+				'TEXT'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::TEXTFORMAT',
+												 'argumentCount'	=>	'2'
+												),
+				'TIME'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::TIME',
+												 'argumentCount'	=>	'3'
+												),
+				'TIMEVALUE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::TIMEVALUE',
+												 'argumentCount'	=>	'1'
+												),
+				'TINV'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::TINV',
+												 'argumentCount'	=>	'2'
+												),
+				'TODAY'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::DATENOW',
+												 'argumentCount'	=>	'0'
+												),
+				'TRANSPOSE'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::TRANSPOSE',
+												 'argumentCount'	=>	'1'
+												),
+				'TREND'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::TREND',
+												 'argumentCount'	=>	'1-4'
+												),
+				'TRIM'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::TRIMSPACES',
+												 'argumentCount'	=>	'1'
+												),
+				'TRIMMEAN'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::TRIMMEAN',
+												 'argumentCount'	=>	'2'
+												),
+				'TRUE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Logical::TRUE',
+												 'argumentCount'	=>	'0'
+												),
+				'TRUNC'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+												 'functionCall'		=>	'PHPExcel_Calculation_MathTrig::TRUNC',
+												 'argumentCount'	=>	'1,2'
+												),
+				'TTEST'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'4'
+												),
+				'TYPE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::TYPE',
+												 'argumentCount'	=>	'1'
+												),
+				'UPPER'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_TextData::UPPERCASE',
+												 'argumentCount'	=>	'1'
+												),
+				'USDOLLAR'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'2'
+												),
+				'VALUE'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'1'
+												),
+				'VAR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::VARFunc',
+												 'argumentCount'	=>	'1+'
+												),
+				'VARA'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::VARA',
+												 'argumentCount'	=>	'1+'
+												),
+				'VARP'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::VARP',
+												 'argumentCount'	=>	'1+'
+												),
+				'VARPA'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::VARPA',
+												 'argumentCount'	=>	'1+'
+												),
+				'VDB'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'5-7'
+												),
+				'VERSION'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::VERSION',
+												 'argumentCount'	=>	'0'
+												),
+				'VLOOKUP'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+												 'functionCall'		=>	'PHPExcel_Calculation_LookupRef::VLOOKUP',
+												 'argumentCount'	=>	'3,4'
+												),
+				'WEEKDAY'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::DAYOFWEEK',
+												 'argumentCount'	=>	'1,2'
+												),
+				'WEEKNUM'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::WEEKOFYEAR',
+												 'argumentCount'	=>	'1,2'
+												),
+				'WEIBULL'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::WEIBULL',
+												 'argumentCount'	=>	'4'
+												),
+				'WORKDAY'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::WORKDAY',
+												 'argumentCount'	=>	'2+'
+												),
+				'XIRR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::XIRR',
+												 'argumentCount'	=>	'2,3'
+												),
+				'XNPV'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::XNPV',
+												 'argumentCount'	=>	'3'
+												),
+				'YEAR'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::YEAR',
+												 'argumentCount'	=>	'1'
+												),
+				'YEARFRAC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+												 'functionCall'		=>	'PHPExcel_Calculation_DateTime::YEARFRAC',
+												 'argumentCount'	=>	'2,3'
+												),
+				'YIELD'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Functions::DUMMY',
+												 'argumentCount'	=>	'6,7'
+												),
+				'YIELDDISC'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::YIELDDISC',
+												 'argumentCount'	=>	'4,5'
+												),
+				'YIELDMAT'				=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Financial::YIELDMAT',
+												 'argumentCount'	=>	'5,6'
+												),
+				'ZTEST'					=> array('category'			=>	PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+												 'functionCall'		=>	'PHPExcel_Calculation_Statistical::ZTEST',
+												 'argumentCount'	=>	'2-3'
+												)
+			);
+
+
+	//	Internal functions used for special control purposes
+	private static $_controlFunctions = array(
+				'MKMATRIX'	=> array('argumentCount'	=>	'*',
+									 'functionCall'		=>	'self::_mkMatrix'
+									)
+			);
+
+
+
+
+	private function __construct(PHPExcel $workbook = NULL) {
+		$setPrecision = (PHP_INT_SIZE == 4) ? 14 : 16;
+		$this->_savedPrecision = ini_get('precision');
+		if ($this->_savedPrecision < $setPrecision) {
+			ini_set('precision',$setPrecision);
+		}
+
+		if ($workbook !== NULL) {
+			self::$_workbookSets[$workbook->getID()] = $this;
+		}
+
+		$this->_workbook = $workbook;
+		$this->_cyclicReferenceStack = new PHPExcel_CalcEngine_CyclicReferenceStack();
+	    $this->_debugLog = new PHPExcel_CalcEngine_Logger($this->_cyclicReferenceStack);
+	}	//	function __construct()
+
+
+	public function __destruct() {
+		if ($this->_savedPrecision != ini_get('precision')) {
+			ini_set('precision',$this->_savedPrecision);
+		}
+	}
+
+	private static function _loadLocales() {
+		$localeFileDirectory = PHPEXCEL_ROOT.'PHPExcel/locale/';
+		foreach (glob($localeFileDirectory.'/*',GLOB_ONLYDIR) as $filename) {
+			$filename = substr($filename,strlen($localeFileDirectory)+1);
+			if ($filename != 'en') {
+				self::$_validLocaleLanguages[] = $filename;
+			}
+		}
+	}
+
+	/**
+	 * Get an instance of this class
+	 *
+	 * @access	public
+	 * @param   PHPExcel $workbook  Injected workbook for working with a PHPExcel object,
+	 *									or NULL to create a standalone claculation engine
+	 * @return PHPExcel_Calculation
+	 */
+	public static function getInstance(PHPExcel $workbook = NULL) {
+		if ($workbook !== NULL) {
+    		if (isset(self::$_workbookSets[$workbook->getID()])) {
+    			return self::$_workbookSets[$workbook->getID()];
+    		}
+			return new PHPExcel_Calculation($workbook);
+		}
+
+		if (!isset(self::$_instance) || (self::$_instance === NULL)) {
+			self::$_instance = new PHPExcel_Calculation();
+		}
+
+		return self::$_instance;
+	}	//	function getInstance()
+
+	/**
+	 * Unset an instance of this class
+	 *
+	 * @access	public
+	 * @param   PHPExcel $workbook  Injected workbook identifying the instance to unset
+	 */
+	public static function unsetInstance(PHPExcel $workbook = NULL) {
+		if ($workbook !== NULL) {
+    		if (isset(self::$_workbookSets[$workbook->getID()])) {
+    			unset(self::$_workbookSets[$workbook->getID()]);
+    		}
+		}
+    }
+
+	/**
+	 * Flush the calculation cache for any existing instance of this class
+	 *		but only if a PHPExcel_Calculation instance exists
+	 *
+	 * @access	public
+	 * @return null
+	 */
+	public function flushInstance() {
+		$this->clearCalculationCache();
+	}	//	function flushInstance()
+
+
+	/**
+	 * Get the debuglog for this claculation engine instance
+	 *
+	 * @access	public
+	 * @return PHPExcel_CalcEngine_Logger
+	 */
+	public function getDebugLog() {
+		return $this->_debugLog;
+	}
+
+	/**
+	 * __clone implementation. Cloning should not be allowed in a Singleton!
+	 *
+	 * @access	public
+	 * @throws	PHPExcel_Calculation_Exception
+	 */
+	public final function __clone() {
+		throw new PHPExcel_Calculation_Exception ('Cloning the calculation engine is not allowed!');
+	}	//	function __clone()
+
+
+	/**
+	 * Return the locale-specific translation of TRUE
+	 *
+	 * @access	public
+	 * @return	 string		locale-specific translation of TRUE
+	 */
+	public static function getTRUE() {
+		return self::$_localeBoolean['TRUE'];
+	}
+
+	/**
+	 * Return the locale-specific translation of FALSE
+	 *
+	 * @access	public
+	 * @return	 string		locale-specific translation of FALSE
+	 */
+	public static function getFALSE() {
+		return self::$_localeBoolean['FALSE'];
+	}
+
+	/**
+	 * Set the Array Return Type (Array or Value of first element in the array)
+	 *
+	 * @access	public
+	 * @param	 string	$returnType			Array return type
+	 * @return	 boolean					Success or failure
+	 */
+	public static function setArrayReturnType($returnType) {
+		if (($returnType == self::RETURN_ARRAY_AS_VALUE) ||
+			($returnType == self::RETURN_ARRAY_AS_ERROR) ||
+			($returnType == self::RETURN_ARRAY_AS_ARRAY)) {
+			self::$returnArrayAsType = $returnType;
+			return TRUE;
+		}
+		return FALSE;
+	}	//	function setArrayReturnType()
+
+
+	/**
+	 * Return the Array Return Type (Array or Value of first element in the array)
+	 *
+	 * @access	public
+	 * @return	 string		$returnType			Array return type
+	 */
+	public static function getArrayReturnType() {
+		return self::$returnArrayAsType;
+	}	//	function getArrayReturnType()
+
+
+	/**
+	 * Is calculation caching enabled?
+	 *
+	 * @access	public
+	 * @return boolean
+	 */
+	public function getCalculationCacheEnabled() {
+		return $this->_calculationCacheEnabled;
+	}	//	function getCalculationCacheEnabled()
+
+	/**
+	 * Enable/disable calculation cache
+	 *
+	 * @access	public
+	 * @param boolean $pValue
+	 */
+	public function setCalculationCacheEnabled($pValue = TRUE) {
+		$this->_calculationCacheEnabled = $pValue;
+		$this->clearCalculationCache();
+	}	//	function setCalculationCacheEnabled()
+
+
+	/**
+	 * Enable calculation cache
+	 */
+	public function enableCalculationCache() {
+		$this->setCalculationCacheEnabled(TRUE);
+	}	//	function enableCalculationCache()
+
+
+	/**
+	 * Disable calculation cache
+	 */
+	public function disableCalculationCache() {
+		$this->setCalculationCacheEnabled(FALSE);
+	}	//	function disableCalculationCache()
+
+
+	/**
+	 * Clear calculation cache
+	 */
+	public function clearCalculationCache() {
+		$this->_calculationCache = array();
+	}	//	function clearCalculationCache()
+
+	/**
+	 * Clear calculation cache for a specified worksheet
+	 *
+	 * @param string $worksheetName
+	 */
+	public function clearCalculationCacheForWorksheet($worksheetName) {
+		if (isset($this->_calculationCache[$worksheetName])) {
+			unset($this->_calculationCache[$worksheetName]);
+		}
+	}	//	function clearCalculationCacheForWorksheet()
+
+	/**
+	 * Rename calculation cache for a specified worksheet
+	 *
+	 * @param string $fromWorksheetName
+	 * @param string $toWorksheetName
+	 */
+	public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName) {
+		if (isset($this->_calculationCache[$fromWorksheetName])) {
+			$this->_calculationCache[$toWorksheetName] = &$this->_calculationCache[$fromWorksheetName];
+			unset($this->_calculationCache[$fromWorksheetName]);
+		}
+	}	//	function renameCalculationCacheForWorksheet()
+
+
+	/**
+	 * Get the currently defined locale code
+	 *
+	 * @return string
+	 */
+	public function getLocale() {
+		return self::$_localeLanguage;
+	}	//	function getLocale()
+
+
+	/**
+	 * Set the locale code
+	 *
+	 * @param string $locale  The locale to use for formula translation
+	 * @return boolean
+	 */
+	public function setLocale($locale = 'en_us') {
+		//	Identify our locale and language
+		$language = $locale = strtolower($locale);
+		if (strpos($locale,'_') !== FALSE) {
+			list($language) = explode('_',$locale);
+		}
+
+		if (count(self::$_validLocaleLanguages) == 1)
+			self::_loadLocales();
+
+		//	Test whether we have any language data for this language (any locale)
+		if (in_array($language,self::$_validLocaleLanguages)) {
+			//	initialise language/locale settings
+			self::$_localeFunctions = array();
+			self::$_localeArgumentSeparator = ',';
+			self::$_localeBoolean = array('TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL');
+			//	Default is English, if user isn't requesting english, then read the necessary data from the locale files
+			if ($locale != 'en_us') {
+				//	Search for a file with a list of function names for locale
+				$functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_',DIRECTORY_SEPARATOR,$locale).DIRECTORY_SEPARATOR.'functions';
+				if (!file_exists($functionNamesFile)) {
+					//	If there isn't a locale specific function file, look for a language specific function file
+					$functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'functions';
+					if (!file_exists($functionNamesFile)) {
+						return FALSE;
+					}
+				}
+				//	Retrieve the list of locale or language specific function names
+				$localeFunctions = file($functionNamesFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+				foreach ($localeFunctions as $localeFunction) {
+					list($localeFunction) = explode('##',$localeFunction);	//	Strip out comments
+					if (strpos($localeFunction,'=') !== FALSE) {
+						list($fName,$lfName) = explode('=',$localeFunction);
+						$fName = trim($fName);
+						$lfName = trim($lfName);
+						if ((isset(self::$_PHPExcelFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
+							self::$_localeFunctions[$fName] = $lfName;
+						}
+					}
+				}
+				//	Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
+				if (isset(self::$_localeFunctions['TRUE'])) { self::$_localeBoolean['TRUE'] = self::$_localeFunctions['TRUE']; }
+				if (isset(self::$_localeFunctions['FALSE'])) { self::$_localeBoolean['FALSE'] = self::$_localeFunctions['FALSE']; }
+
+				$configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_',DIRECTORY_SEPARATOR,$locale).DIRECTORY_SEPARATOR.'config';
+				if (!file_exists($configFile)) {
+					$configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'config';
+				}
+				if (file_exists($configFile)) {
+					$localeSettings = file($configFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+					foreach ($localeSettings as $localeSetting) {
+						list($localeSetting) = explode('##',$localeSetting);	//	Strip out comments
+						if (strpos($localeSetting,'=') !== FALSE) {
+							list($settingName,$settingValue) = explode('=',$localeSetting);
+							$settingName = strtoupper(trim($settingName));
+							switch ($settingName) {
+								case 'ARGUMENTSEPARATOR' :
+									self::$_localeArgumentSeparator = trim($settingValue);
+									break;
+							}
+						}
+					}
+				}
+			}
+
+			self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
+			self::$functionReplaceFromLocale = self::$functionReplaceToLocale = NULL;
+			self::$_localeLanguage = $locale;
+			return TRUE;
+		}
+		return FALSE;
+	}	//	function setLocale()
+
+
+
+	public static function _translateSeparator($fromSeparator,$toSeparator,$formula,&$inBraces) {
+		$strlen = mb_strlen($formula);
+		for ($i = 0; $i < $strlen; ++$i) {
+			$chr = mb_substr($formula,$i,1);
+			switch ($chr) {
+				case '{' :	$inBraces = TRUE;
+							break;
+				case '}' :	$inBraces = FALSE;
+							break;
+				case $fromSeparator :
+							if (!$inBraces) {
+								$formula = mb_substr($formula,0,$i).$toSeparator.mb_substr($formula,$i+1);
+							}
+			}
+		}
+		return $formula;
+	}
+
+	private static function _translateFormula($from,$to,$formula,$fromSeparator,$toSeparator) {
+		//	Convert any Excel function names to the required language
+		if (self::$_localeLanguage !== 'en_us') {
+			$inBraces = FALSE;
+			//	If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
+			if (strpos($formula,'"') !== FALSE) {
+				//	So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
+				//		the formula
+				$temp = explode('"',$formula);
+				$i = FALSE;
+				foreach($temp as &$value) {
+					//	Only count/replace in alternating array entries
+					if ($i = !$i) {
+						$value = preg_replace($from,$to,$value);
+						$value = self::_translateSeparator($fromSeparator,$toSeparator,$value,$inBraces);
+					}
+				}
+				unset($value);
+				//	Then rebuild the formula string
+				$formula = implode('"',$temp);
+			} else {
+				//	If there's no quoted strings, then we do a simple count/replace
+				$formula = preg_replace($from,$to,$formula);
+				$formula = self::_translateSeparator($fromSeparator,$toSeparator,$formula,$inBraces);
+			}
+		}
+
+		return $formula;
+	}
+
+	private static $functionReplaceFromExcel	= NULL;
+	private static $functionReplaceToLocale		= NULL;
+
+	public function _translateFormulaToLocale($formula) {
+		if (self::$functionReplaceFromExcel === NULL) {
+			self::$functionReplaceFromExcel = array();
+			foreach(array_keys(self::$_localeFunctions) as $excelFunctionName) {
+				self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelFunctionName).'([\s]*\()/Ui';
+			}
+			foreach(array_keys(self::$_localeBoolean) as $excelBoolean) {
+				self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
+			}
+
+		}
+
+		if (self::$functionReplaceToLocale === NULL) {
+			self::$functionReplaceToLocale = array();
+			foreach(array_values(self::$_localeFunctions) as $localeFunctionName) {
+				self::$functionReplaceToLocale[] = '$1'.trim($localeFunctionName).'$2';
+			}
+			foreach(array_values(self::$_localeBoolean) as $localeBoolean) {
+				self::$functionReplaceToLocale[] = '$1'.trim($localeBoolean).'$2';
+			}
+		}
+
+		return self::_translateFormula(self::$functionReplaceFromExcel,self::$functionReplaceToLocale,$formula,',',self::$_localeArgumentSeparator);
+	}	//	function _translateFormulaToLocale()
+
+
+	private static $functionReplaceFromLocale	= NULL;
+	private static $functionReplaceToExcel		= NULL;
+
+	public function _translateFormulaToEnglish($formula) {
+		if (self::$functionReplaceFromLocale === NULL) {
+			self::$functionReplaceFromLocale = array();
+			foreach(array_values(self::$_localeFunctions) as $localeFunctionName) {
+				self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($localeFunctionName).'([\s]*\()/Ui';
+			}
+			foreach(array_values(self::$_localeBoolean) as $excelBoolean) {
+				self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
+			}
+		}
+
+		if (self::$functionReplaceToExcel === NULL) {
+			self::$functionReplaceToExcel = array();
+			foreach(array_keys(self::$_localeFunctions) as $excelFunctionName) {
+				self::$functionReplaceToExcel[] = '$1'.trim($excelFunctionName).'$2';
+			}
+			foreach(array_keys(self::$_localeBoolean) as $excelBoolean) {
+				self::$functionReplaceToExcel[] = '$1'.trim($excelBoolean).'$2';
+			}
+		}
+
+		return self::_translateFormula(self::$functionReplaceFromLocale,self::$functionReplaceToExcel,$formula,self::$_localeArgumentSeparator,',');
+	}	//	function _translateFormulaToEnglish()
+
+
+	public static function _localeFunc($function) {
+		if (self::$_localeLanguage !== 'en_us') {
+			$functionName = trim($function,'(');
+			if (isset(self::$_localeFunctions[$functionName])) {
+				$brace = ($functionName != $function);
+				$function = self::$_localeFunctions[$functionName];
+				if ($brace) { $function .= '('; }
+			}
+		}
+		return $function;
+	}
+
+
+
+
+	/**
+	 * Wrap string values in quotes
+	 *
+	 * @param mixed $value
+	 * @return mixed
+	 */
+	public static function _wrapResult($value) {
+		if (is_string($value)) {
+			//	Error values cannot be "wrapped"
+			if (preg_match('/^'.self::CALCULATION_REGEXP_ERROR.'$/i', $value, $match)) {
+				//	Return Excel errors "as is"
+				return $value;
+			}
+			//	Return strings wrapped in quotes
+			return '"'.$value.'"';
+		//	Convert numeric errors to NaN error
+		} else if((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
+			return PHPExcel_Calculation_Functions::NaN();
+		}
+
+		return $value;
+	}	//	function _wrapResult()
+
+
+	/**
+	 * Remove quotes used as a wrapper to identify string values
+	 *
+	 * @param mixed $value
+	 * @return mixed
+	 */
+	public static function _unwrapResult($value) {
+		if (is_string($value)) {
+			if ((isset($value{0})) && ($value{0} == '"') && (substr($value,-1) == '"')) {
+				return substr($value,1,-1);
+			}
+		//	Convert numeric errors to NaN error
+		} else if((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
+			return PHPExcel_Calculation_Functions::NaN();
+		}
+		return $value;
+	}	//	function _unwrapResult()
+
+
+
+
+	/**
+	 * Calculate cell value (using formula from a cell ID)
+	 * Retained for backward compatibility
+	 *
+	 * @access	public
+	 * @param	PHPExcel_Cell	$pCell	Cell to calculate
+	 * @return	mixed
+	 * @throws	PHPExcel_Calculation_Exception
+	 */
+	public function calculate(PHPExcel_Cell $pCell = NULL) {
+		try {
+			return $this->calculateCellValue($pCell);
+		} catch (PHPExcel_Exception $e) {
+			throw new PHPExcel_Calculation_Exception($e->getMessage());
+		}
+	}	//	function calculate()
+
+
+	/**
+	 * Calculate the value of a cell formula
+	 *
+	 * @access	public
+	 * @param	PHPExcel_Cell	$pCell		Cell to calculate
+	 * @param	Boolean			$resetLog	Flag indicating whether the debug log should be reset or not
+	 * @return	mixed
+	 * @throws	PHPExcel_Calculation_Exception
+	 */
+	public function calculateCellValue(PHPExcel_Cell $pCell = NULL, $resetLog = TRUE) {
+		if ($pCell === NULL) {
+			return NULL;
+		}
+
+		$returnArrayAsType = self::$returnArrayAsType;
+		if ($resetLog) {
+			//	Initialise the logging settings if requested
+			$this->formulaError = null;
+			$this->_debugLog->clearLog();
+			$this->_cyclicReferenceStack->clear();
+			$this->_cyclicFormulaCount = 1;
+
+			self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
+		}
+
+		//	Execute the calculation for the cell formula
+		try {
+			$result = self::_unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
+		} catch (PHPExcel_Exception $e) {
+			throw new PHPExcel_Calculation_Exception($e->getMessage());
+		}
+
+		if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
+			self::$returnArrayAsType = $returnArrayAsType;
+			$testResult = PHPExcel_Calculation_Functions::flattenArray($result);
+			if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
+				return PHPExcel_Calculation_Functions::VALUE();
+			}
+			//	If there's only a single cell in the array, then we allow it
+			if (count($testResult) != 1) {
+				//	If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
+				$r = array_keys($result);
+				$r = array_shift($r);
+				if (!is_numeric($r)) { return PHPExcel_Calculation_Functions::VALUE(); }
+				if (is_array($result[$r])) {
+					$c = array_keys($result[$r]);
+					$c = array_shift($c);
+					if (!is_numeric($c)) {
+						return PHPExcel_Calculation_Functions::VALUE();
+					}
+				}
+			}
+			$result = array_shift($testResult);
+		}
+		self::$returnArrayAsType = $returnArrayAsType;
+
+
+		if ($result === NULL) {
+			return 0;
+		} elseif((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
+			return PHPExcel_Calculation_Functions::NaN();
+		}
+		return $result;
+	}	//	function calculateCellValue(
+
+
+	/**
+	 * Validate and parse a formula string
+	 *
+	 * @param	string		$formula		Formula to parse
+	 * @return	array
+	 * @throws	PHPExcel_Calculation_Exception
+	 */
+	public function parseFormula($formula) {
+		//	Basic validation that this is indeed a formula
+		//	We return an empty array if not
+		$formula = trim($formula);
+		if ((!isset($formula{0})) || ($formula{0} != '=')) return array();
+		$formula = ltrim(substr($formula,1));
+		if (!isset($formula{0})) return array();
+
+		//	Parse the formula and return the token stack
+		return $this->_parseFormula($formula);
+	}	//	function parseFormula()
+
+
+	/**
+	 * Calculate the value of a formula
+	 *
+	 * @param	string			$formula	Formula to parse
+	 * @param	string			$cellID		Address of the cell to calculate
+	 * @param	PHPExcel_Cell	$pCell		Cell to calculate
+	 * @return	mixed
+	 * @throws	PHPExcel_Calculation_Exception
+	 */
+	public function calculateFormula($formula, $cellID=NULL, PHPExcel_Cell $pCell = NULL) {
+		//	Initialise the logging settings
+		$this->formulaError = null;
+		$this->_debugLog->clearLog();
+		$this->_cyclicReferenceStack->clear();
+
+		//	Disable calculation cacheing because it only applies to cell calculations, not straight formulae
+		//	But don't actually flush any cache
+		$resetCache = $this->getCalculationCacheEnabled();
+		$this->_calculationCacheEnabled = FALSE;
+		//	Execute the calculation
+		try {
+			$result = self::_unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
+		} catch (PHPExcel_Exception $e) {
+			throw new PHPExcel_Calculation_Exception($e->getMessage());
+		}
+
+		//	Reset calculation cacheing to its previous state
+		$this->_calculationCacheEnabled = $resetCache;
+
+		return $result;
+	}	//	function calculateFormula()
+
+
+    public function getValueFromCache($worksheetName, $cellID, &$cellValue) {
+		// Is calculation cacheing enabled?
+		// Is the value present in calculation cache?
+//echo 'Test cache for ',$worksheetName,'!',$cellID,PHP_EOL;
+		$this->_debugLog->writeDebugLog('Testing cache value for cell ', $worksheetName, '!', $cellID);
+		if (($this->_calculationCacheEnabled) && (isset($this->_calculationCache[$worksheetName][$cellID]))) {
+//echo 'Retrieve from cache',PHP_EOL;
+			$this->_debugLog->writeDebugLog('Retrieving value for cell ', $worksheetName, '!', $cellID, ' from cache');
+			// Return the cached result
+			$cellValue = $this->_calculationCache[$worksheetName][$cellID];
+			return TRUE;
+		}
+		return FALSE;
+    }
+
+    public function saveValueToCache($worksheetName, $cellID, $cellValue) {
+		if ($this->_calculationCacheEnabled) {
+			$this->_calculationCache[$worksheetName][$cellID] = $cellValue;
+		}
+	}
+
+	/**
+	 * Parse a cell formula and calculate its value
+	 *
+	 * @param	string			$formula	The formula to parse and calculate
+	 * @param	string			$cellID		The ID (e.g. A3) of the cell that we are calculating
+	 * @param	PHPExcel_Cell	$pCell		Cell to calculate
+	 * @return	mixed
+	 * @throws	PHPExcel_Calculation_Exception
+	 */
+	public function _calculateFormulaValue($formula, $cellID=null, PHPExcel_Cell $pCell = null) {
+		$cellValue = '';
+
+		//	Basic validation that this is indeed a formula
+		//	We simply return the cell value if not
+		$formula = trim($formula);
+		if ($formula{0} != '=') return self::_wrapResult($formula);
+		$formula = ltrim(substr($formula,1));
+		if (!isset($formula{0})) return self::_wrapResult($formula);
+
+		$pCellParent = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
+		$wsTitle = ($pCellParent !== NULL) ? $pCellParent->getTitle() : "\x00Wrk";
+
+		if (($cellID !== NULL) && ($this->getValueFromCache($wsTitle, $cellID, $cellValue))) {
+			return $cellValue;
+		}
+
+		if (($wsTitle{0} !== "\x00") && ($this->_cyclicReferenceStack->onStack($wsTitle.'!'.$cellID))) {
+			if ($this->cyclicFormulaCount <= 0) {
+				return $this->_raiseFormulaError('Cyclic Reference in Formula');
+			} elseif (($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) &&
+					  ($this->_cyclicFormulaCell == $wsTitle.'!'.$cellID)) {
+				return $cellValue;
+			} elseif ($this->_cyclicFormulaCell == $wsTitle.'!'.$cellID) {
+				++$this->_cyclicFormulaCount;
+				if ($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) {
+					return $cellValue;
+				}
+			} elseif ($this->_cyclicFormulaCell == '') {
+				$this->_cyclicFormulaCell = $wsTitle.'!'.$cellID;
+				if ($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) {
+					return $cellValue;
+				}
+			}
+		}
+
+		//	Parse the formula onto the token stack and calculate the value
+		$this->_cyclicReferenceStack->push($wsTitle.'!'.$cellID);
+		$cellValue = $this->_processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
+		$this->_cyclicReferenceStack->pop();
+
+		// Save to calculation cache
+		if ($cellID !== NULL) {
+			$this->saveValueToCache($wsTitle, $cellID, $cellValue);
+		}
+
+		//	Return the calculated value
+		return $cellValue;
+	}	//	function _calculateFormulaValue()
+
+
+	/**
+	 * Ensure that paired matrix operands are both matrices and of the same size
+	 *
+	 * @param	mixed		&$operand1	First matrix operand
+	 * @param	mixed		&$operand2	Second matrix operand
+	 * @param	integer		$resize		Flag indicating whether the matrices should be resized to match
+	 *										and (if so), whether the smaller dimension should grow or the
+	 *										larger should shrink.
+	 *											0 = no resize
+	 *											1 = shrink to fit
+	 *											2 = extend to fit
+	 */
+	private static function _checkMatrixOperands(&$operand1,&$operand2,$resize = 1) {
+		//	Examine each of the two operands, and turn them into an array if they aren't one already
+		//	Note that this function should only be called if one or both of the operand is already an array
+		if (!is_array($operand1)) {
+			list($matrixRows,$matrixColumns) = self::_getMatrixDimensions($operand2);
+			$operand1 = array_fill(0,$matrixRows,array_fill(0,$matrixColumns,$operand1));
+			$resize = 0;
+		} elseif (!is_array($operand2)) {
+			list($matrixRows,$matrixColumns) = self::_getMatrixDimensions($operand1);
+			$operand2 = array_fill(0,$matrixRows,array_fill(0,$matrixColumns,$operand2));
+			$resize = 0;
+		}
+
+		list($matrix1Rows,$matrix1Columns) = self::_getMatrixDimensions($operand1);
+		list($matrix2Rows,$matrix2Columns) = self::_getMatrixDimensions($operand2);
+		if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
+			$resize = 1;
+		}
+
+		if ($resize == 2) {
+			//	Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
+			self::_resizeMatricesExtend($operand1,$operand2,$matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns);
+		} elseif ($resize == 1) {
+			//	Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
+			self::_resizeMatricesShrink($operand1,$operand2,$matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns);
+		}
+		return array( $matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns);
+	}	//	function _checkMatrixOperands()
+
+
+	/**
+	 * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0
+	 *
+	 * @param	mixed		&$matrix		matrix operand
+	 * @return	array		An array comprising the number of rows, and number of columns
+	 */
+	public static function _getMatrixDimensions(&$matrix) {
+		$matrixRows = count($matrix);
+		$matrixColumns = 0;
+		foreach($matrix as $rowKey => $rowValue) {
+			$matrixColumns = max(count($rowValue),$matrixColumns);
+			if (!is_array($rowValue)) {
+				$matrix[$rowKey] = array($rowValue);
+			} else {
+				$matrix[$rowKey] = array_values($rowValue);
+			}
+		}
+		$matrix = array_values($matrix);
+		return array($matrixRows,$matrixColumns);
+	}	//	function _getMatrixDimensions()
+
+
+	/**
+	 * Ensure that paired matrix operands are both matrices of the same size
+	 *
+	 * @param	mixed		&$matrix1		First matrix operand
+	 * @param	mixed		&$matrix2		Second matrix operand
+	 * @param	integer		$matrix1Rows	Row size of first matrix operand
+	 * @param	integer		$matrix1Columns	Column size of first matrix operand
+	 * @param	integer		$matrix2Rows	Row size of second matrix operand
+	 * @param	integer		$matrix2Columns	Column size of second matrix operand
+	 */
+	private static function _resizeMatricesShrink(&$matrix1,&$matrix2,$matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns) {
+		if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+			if ($matrix2Rows < $matrix1Rows) {
+				for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
+					unset($matrix1[$i]);
+				}
+			}
+			if ($matrix2Columns < $matrix1Columns) {
+				for ($i = 0; $i < $matrix1Rows; ++$i) {
+					for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+						unset($matrix1[$i][$j]);
+					}
+				}
+			}
+		}
+
+		if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+			if ($matrix1Rows < $matrix2Rows) {
+				for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
+					unset($matrix2[$i]);
+				}
+			}
+			if ($matrix1Columns < $matrix2Columns) {
+				for ($i = 0; $i < $matrix2Rows; ++$i) {
+					for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+						unset($matrix2[$i][$j]);
+					}
+				}
+			}
+		}
+	}	//	function _resizeMatricesShrink()
+
+
+	/**
+	 * Ensure that paired matrix operands are both matrices of the same size
+	 *
+	 * @param	mixed		&$matrix1	First matrix operand
+	 * @param	mixed		&$matrix2	Second matrix operand
+	 * @param	integer		$matrix1Rows	Row size of first matrix operand
+	 * @param	integer		$matrix1Columns	Column size of first matrix operand
+	 * @param	integer		$matrix2Rows	Row size of second matrix operand
+	 * @param	integer		$matrix2Columns	Column size of second matrix operand
+	 */
+	private static function _resizeMatricesExtend(&$matrix1,&$matrix2,$matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns) {
+		if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+			if ($matrix2Columns < $matrix1Columns) {
+				for ($i = 0; $i < $matrix2Rows; ++$i) {
+					$x = $matrix2[$i][$matrix2Columns-1];
+					for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+						$matrix2[$i][$j] = $x;
+					}
+				}
+			}
+			if ($matrix2Rows < $matrix1Rows) {
+				$x = $matrix2[$matrix2Rows-1];
+				for ($i = 0; $i < $matrix1Rows; ++$i) {
+					$matrix2[$i] = $x;
+				}
+			}
+		}
+
+		if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+			if ($matrix1Columns < $matrix2Columns) {
+				for ($i = 0; $i < $matrix1Rows; ++$i) {
+					$x = $matrix1[$i][$matrix1Columns-1];
+					for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+						$matrix1[$i][$j] = $x;
+					}
+				}
+			}
+			if ($matrix1Rows < $matrix2Rows) {
+				$x = $matrix1[$matrix1Rows-1];
+				for ($i = 0; $i < $matrix2Rows; ++$i) {
+					$matrix1[$i] = $x;
+				}
+			}
+		}
+	}	//	function _resizeMatricesExtend()
+
+
+	/**
+	 * Format details of an operand for display in the log (based on operand type)
+	 *
+	 * @param	mixed		$value	First matrix operand
+	 * @return	mixed
+	 */
+	private function _showValue($value) {
+		if ($this->_debugLog->getWriteDebugLog()) {
+			$testArray = PHPExcel_Calculation_Functions::flattenArray($value);
+			if (count($testArray) == 1) {
+				$value = array_pop($testArray);
+			}
+
+			if (is_array($value)) {
+				$returnMatrix = array();
+				$pad = $rpad = ', ';
+				foreach($value as $row) {
+					if (is_array($row)) {
+						$returnMatrix[] = implode($pad,array_map(array($this,'_showValue'),$row));
+						$rpad = '; ';
+					} else {
+						$returnMatrix[] = $this->_showValue($row);
+					}
+				}
+				return '{ '.implode($rpad,$returnMatrix).' }';
+			} elseif(is_string($value) && (trim($value,'"') == $value)) {
+				return '"'.$value.'"';
+			} elseif(is_bool($value)) {
+				return ($value) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
+			}
+		}
+		return PHPExcel_Calculation_Functions::flattenSingleValue($value);
+	}	//	function _showValue()
+
+
+	/**
+	 * Format type and details of an operand for display in the log (based on operand type)
+	 *
+	 * @param	mixed		$value	First matrix operand
+	 * @return	mixed
+	 */
+	private function _showTypeDetails($value) {
+		if ($this->_debugLog->getWriteDebugLog()) {
+			$testArray = PHPExcel_Calculation_Functions::flattenArray($value);
+			if (count($testArray) == 1) {
+				$value = array_pop($testArray);
+			}
+
+			if ($value === NULL) {
+				return 'a NULL value';
+			} elseif (is_float($value)) {
+				$typeString = 'a floating point number';
+			} elseif(is_int($value)) {
+				$typeString = 'an integer number';
+			} elseif(is_bool($value)) {
+				$typeString = 'a boolean';
+			} elseif(is_array($value)) {
+				$typeString = 'a matrix';
+			} else {
+				if ($value == '') {
+					return 'an empty string';
+				} elseif ($value{0} == '#') {
+					return 'a '.$value.' error';
+				} else {
+					$typeString = 'a string';
+				}
+			}
+			return $typeString.' with a value of '.$this->_showValue($value);
+		}
+	}	//	function _showTypeDetails()
+
+
+	private static function _convertMatrixReferences($formula) {
+		static $matrixReplaceFrom = array('{',';','}');
+		static $matrixReplaceTo = array('MKMATRIX(MKMATRIX(','),MKMATRIX(','))');
+
+		//	Convert any Excel matrix references to the MKMATRIX() function
+		if (strpos($formula,'{') !== FALSE) {
+			//	If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
+			if (strpos($formula,'"') !== FALSE) {
+				//	So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
+				//		the formula
+				$temp = explode('"',$formula);
+				//	Open and Closed counts used for trapping mismatched braces in the formula
+				$openCount = $closeCount = 0;
+				$i = FALSE;
+				foreach($temp as &$value) {
+					//	Only count/replace in alternating array entries
+					if ($i = !$i) {
+						$openCount += substr_count($value,'{');
+						$closeCount += substr_count($value,'}');
+						$value = str_replace($matrixReplaceFrom,$matrixReplaceTo,$value);
+					}
+				}
+				unset($value);
+				//	Then rebuild the formula string
+				$formula = implode('"',$temp);
+			} else {
+				//	If there's no quoted strings, then we do a simple count/replace
+				$openCount = substr_count($formula,'{');
+				$closeCount = substr_count($formula,'}');
+				$formula = str_replace($matrixReplaceFrom,$matrixReplaceTo,$formula);
+			}
+			//	Trap for mismatched braces and trigger an appropriate error
+			if ($openCount < $closeCount) {
+				if ($openCount > 0) {
+					return $this->_raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
+				} else {
+					return $this->_raiseFormulaError("Formula Error: Unexpected '}' encountered");
+				}
+			} elseif ($openCount > $closeCount) {
+				if ($closeCount > 0) {
+					return $this->_raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
+				} else {
+					return $this->_raiseFormulaError("Formula Error: Unexpected '{' encountered");
+				}
+			}
+		}
+
+		return $formula;
+	}	//	function _convertMatrixReferences()
+
+
+	private static function _mkMatrix() {
+		return func_get_args();
+	}	//	function _mkMatrix()
+
+
+	//	Binary Operators
+	//	These operators always work on two values
+	//	Array key is the operator, the value indicates whether this is a left or right associative operator
+	private static $_operatorAssociativity	= array(
+		'^' => 0,															//	Exponentiation
+		'*' => 0, '/' => 0, 												//	Multiplication and Division
+		'+' => 0, '-' => 0,													//	Addition and Subtraction
+		'&' => 0,															//	Concatenation
+		'|' => 0, ':' => 0,													//	Intersect and Range
+		'>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0		//	Comparison
+	);
+
+	//	Comparison (Boolean) Operators
+	//	These operators work on two values, but always return a boolean result
+	private static $_comparisonOperators	= array('>' => TRUE, '<' => TRUE, '=' => TRUE, '>=' => TRUE, '<=' => TRUE, '<>' => TRUE);
+
+	//	Operator Precedence
+	//	This list includes all valid operators, whether binary (including boolean) or unary (such as %)
+	//	Array key is the operator, the value is its precedence
+	private static $_operatorPrecedence	= array(
+		':' => 8,																//	Range
+		'|' => 7,																//	Intersect
+		'~' => 6,																//	Negation
+		'%' => 5,																//	Percentage
+		'^' => 4,																//	Exponentiation
+		'*' => 3, '/' => 3, 													//	Multiplication and Division
+		'+' => 2, '-' => 2,														//	Addition and Subtraction
+		'&' => 1,																//	Concatenation
+		'>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0			//	Comparison
+	);
+
+	// Convert infix to postfix notation
+	private function _parseFormula($formula, PHPExcel_Cell $pCell = NULL) {
+		if (($formula = self::_convertMatrixReferences(trim($formula))) === FALSE) {
+			return FALSE;
+		}
+
+		//	If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
+		//		so we store the parent worksheet so that we can re-attach it when necessary
+		$pCellParent = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
+
+		$regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION.
+							   '|'.self::CALCULATION_REGEXP_NUMBER.
+							   '|'.self::CALCULATION_REGEXP_STRING.
+							   '|'.self::CALCULATION_REGEXP_OPENBRACE.
+							   '|'.self::CALCULATION_REGEXP_CELLREF.
+							   '|'.self::CALCULATION_REGEXP_NAMEDRANGE.
+							   '|'.self::CALCULATION_REGEXP_ERROR.
+							 ')/si';
+
+		//	Start with initialisation
+		$index = 0;
+		$stack = new PHPExcel_Calculation_Token_Stack;
+		$output = array();
+		$expectingOperator = FALSE;					//	We use this test in syntax-checking the expression to determine when a
+													//		- is a negation or + is a positive operator rather than an operation
+		$expectingOperand = FALSE;					//	We use this test in syntax-checking the expression to determine whether an operand
+													//		should be null in a function call
+		//	The guts of the lexical parser
+		//	Loop through the formula extracting each operator and operand in turn
+		while(TRUE) {
+//echo 'Assessing Expression '.substr($formula, $index),PHP_EOL;
+			$opCharacter = $formula{$index};	//	Get the first character of the value at the current index position
+//echo 'Initial character of expression block is '.$opCharacter,PHP_EOL;
+			if ((isset(self::$_comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$_comparisonOperators[$formula{$index+1}]))) {
+				$opCharacter .= $formula{++$index};
+//echo 'Initial character of expression block is comparison operator '.$opCharacter.PHP_EOL;
+			}
+
+			//	Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
+			$isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
+//echo '$isOperandOrFunction is '.(($isOperandOrFunction) ? 'True' : 'False').PHP_EOL;
+//var_dump($match);
+
+			if ($opCharacter == '-' && !$expectingOperator) {				//	Is it a negation instead of a minus?
+//echo 'Element is a Negation operator',PHP_EOL;
+				$stack->push('Unary Operator','~');							//	Put a negation on the stack
+				++$index;													//		and drop the negation symbol
+			} elseif ($opCharacter == '%' && $expectingOperator) {
+//echo 'Element is a Percentage operator',PHP_EOL;
+				$stack->push('Unary Operator','%');							//	Put a percentage on the stack
+				++$index;
+			} elseif ($opCharacter == '+' && !$expectingOperator) {			//	Positive (unary plus rather than binary operator plus) can be discarded?
+//echo 'Element is a Positive number, not Plus operator',PHP_EOL;
+				++$index;													//	Drop the redundant plus symbol
+			} elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) {	//	We have to explicitly deny a tilde or pipe, because they are legal
+				return $this->_raiseFormulaError("Formula Error: Illegal character '~'");				//		on the stack but not in the input expression
+
+			} elseif ((isset(self::$_operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) {	//	Are we putting an operator on the stack?
+//echo 'Element with value '.$opCharacter.' is an Operator',PHP_EOL;
+				while($stack->count() > 0 &&
+					($o2 = $stack->last()) &&
+					isset(self::$_operators[$o2['value']]) &&
+					@(self::$_operatorAssociativity[$opCharacter] ? self::$_operatorPrecedence[$opCharacter] < self::$_operatorPrecedence[$o2['value']] : self::$_operatorPrecedence[$opCharacter] <= self::$_operatorPrecedence[$o2['value']])) {
+					$output[] = $stack->pop();								//	Swap operands and higher precedence operators from the stack to the output
+				}
+				$stack->push('Binary Operator',$opCharacter);	//	Finally put our current operator onto the stack
+				++$index;
+				$expectingOperator = FALSE;
+
+			} elseif ($opCharacter == ')' && $expectingOperator) {			//	Are we expecting to close a parenthesis?
+//echo 'Element is a Closing bracket',PHP_EOL;
+				$expectingOperand = FALSE;
+				while (($o2 = $stack->pop()) && $o2['value'] != '(') {		//	Pop off the stack back to the last (
+					if ($o2 === NULL) return $this->_raiseFormulaError('Formula Error: Unexpected closing brace ")"');
+					else $output[] = $o2;
+				}
+				$d = $stack->last(2);
+				if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) {	//	Did this parenthesis just close a function?
+					$functionName = $matches[1];										//	Get the function name
+//echo 'Closed Function is '.$functionName,PHP_EOL;
+					$d = $stack->pop();
+					$argumentCount = $d['value'];		//	See how many arguments there were (argument count is the next value stored on the stack)
+//if ($argumentCount == 0) {
+//	echo 'With no arguments',PHP_EOL;
+//} elseif ($argumentCount == 1) {
+//	echo 'With 1 argument',PHP_EOL;
+//} else {
+//	echo 'With '.$argumentCount.' arguments',PHP_EOL;
+//}
+					$output[] = $d;						//	Dump the argument count on the output
+					$output[] = $stack->pop();			//	Pop the function and push onto the output
+					if (isset(self::$_controlFunctions[$functionName])) {
+//echo 'Built-in function '.$functionName,PHP_EOL;
+						$expectedArgumentCount = self::$_controlFunctions[$functionName]['argumentCount'];
+						$functionCall = self::$_controlFunctions[$functionName]['functionCall'];
+					} elseif (isset(self::$_PHPExcelFunctions[$functionName])) {
+//echo 'PHPExcel function '.$functionName,PHP_EOL;
+						$expectedArgumentCount = self::$_PHPExcelFunctions[$functionName]['argumentCount'];
+						$functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall'];
+					} else {	// did we somehow push a non-function on the stack? this should never happen
+						return $this->_raiseFormulaError("Formula Error: Internal error, non-function on stack");
+					}
+					//	Check the argument count
+					$argumentCountError = FALSE;
+					if (is_numeric($expectedArgumentCount)) {
+						if ($expectedArgumentCount < 0) {
+//echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount),PHP_EOL;
+							if ($argumentCount > abs($expectedArgumentCount)) {
+								$argumentCountError = TRUE;
+								$expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount);
+							}
+						} else {
+//echo '$expectedArgumentCount is numeric '.$expectedArgumentCount,PHP_EOL;
+							if ($argumentCount != $expectedArgumentCount) {
+								$argumentCountError = TRUE;
+								$expectedArgumentCountString = $expectedArgumentCount;
+							}
+						}
+					} elseif ($expectedArgumentCount != '*') {
+						$isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/',$expectedArgumentCount,$argMatch);
+//print_r($argMatch);
+//echo PHP_EOL;
+						switch ($argMatch[2]) {
+							case '+' :
+								if ($argumentCount < $argMatch[1]) {
+									$argumentCountError = TRUE;
+									$expectedArgumentCountString = $argMatch[1].' or more ';
+								}
+								break;
+							case '-' :
+								if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
+									$argumentCountError = TRUE;
+									$expectedArgumentCountString = 'between '.$argMatch[1].' and '.$argMatch[3];
+								}
+								break;
+							case ',' :
+								if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
+									$argumentCountError = TRUE;
+									$expectedArgumentCountString = 'either '.$argMatch[1].' or '.$argMatch[3];
+								}
+								break;
+						}
+					}
+					if ($argumentCountError) {
+						return $this->_raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, ".$expectedArgumentCountString." expected");
+					}
+				}
+				++$index;
+
+			} elseif ($opCharacter == ',') {			//	Is this the separator for function arguments?
+//echo 'Element is a Function argument separator',PHP_EOL;
+				while (($o2 = $stack->pop()) && $o2['value'] != '(') {		//	Pop off the stack back to the last (
+					if ($o2 === NULL) return $this->_raiseFormulaError("Formula Error: Unexpected ,");
+					else $output[] = $o2;	// pop the argument expression stuff and push onto the output
+				}
+				//	If we've a comma when we're expecting an operand, then what we actually have is a null operand;
+				//		so push a null onto the stack
+				if (($expectingOperand) || (!$expectingOperator)) {
+					$output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => NULL);
+				}
+				// make sure there was a function
+				$d = $stack->last(2);
+				if (!preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches))
+					return $this->_raiseFormulaError("Formula Error: Unexpected ,");
+				$d = $stack->pop();
+				$stack->push($d['type'],++$d['value'],$d['reference']);	// increment the argument count
+				$stack->push('Brace', '(');	// put the ( back on, we'll need to pop back to it again
+				$expectingOperator = FALSE;
+				$expectingOperand = TRUE;
+				++$index;
+
+			} elseif ($opCharacter == '(' && !$expectingOperator) {
+//				echo 'Element is an Opening Bracket
'; + $stack->push('Brace', '('); + ++$index; + + } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number? + $expectingOperator = TRUE; + $expectingOperand = FALSE; + $val = $match[1]; + $length = strlen($val); +// echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function
'; + + if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $val, $matches)) { + $val = preg_replace('/\s/','',$val); +// echo 'Element '.$val.' is a Function
'; + if (isset(self::$_PHPExcelFunctions[strtoupper($matches[1])]) || isset(self::$_controlFunctions[strtoupper($matches[1])])) { // it's a function + $stack->push('Function', strtoupper($val)); + $ax = preg_match('/^\s*(\s*\))/i', substr($formula, $index+$length), $amatch); + if ($ax) { + $stack->push('Operand Count for Function '.strtoupper($val).')', 0); + $expectingOperator = TRUE; + } else { + $stack->push('Operand Count for Function '.strtoupper($val).')', 1); + $expectingOperator = FALSE; + } + $stack->push('Brace', '('); + } else { // it's a var w/ implicit multiplication + $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => NULL); + } + } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) { +// echo 'Element '.$val.' is a Cell reference
'; + // Watch for this case-change when modifying to allow cell references in different worksheets... + // Should only be applied to the actual cell column, not the worksheet name + + // If the last entry on the stack was a : operator, then we have a cell range reference + $testPrevOp = $stack->last(1); + if ($testPrevOp['value'] == ':') { + // If we have a worksheet reference, then we're playing with a 3D reference + if ($matches[2] == '') { + // Otherwise, we 'inherit' the worksheet reference from the start cell reference + // The start of the cell range reference should be the last entry in $output + $startCellRef = $output[count($output)-1]['value']; + preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $startCellRef, $startMatches); + if ($startMatches[2] > '') { + $val = $startMatches[2].'!'.$val; + } + } else { + return $this->_raiseFormulaError("3D Range references are not yet supported"); + } + } + + $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $val); +// $expectingOperator = FALSE; + } else { // it's a variable, constant, string, number or boolean +// echo 'Element is a Variable, Constant, String, Number or Boolean
'; + // If the last entry on the stack was a : operator, then we may have a row or column range reference + $testPrevOp = $stack->last(1); + if ($testPrevOp['value'] == ':') { + $startRowColRef = $output[count($output)-1]['value']; + $rangeWS1 = ''; + if (strpos('!',$startRowColRef) !== FALSE) { + list($rangeWS1,$startRowColRef) = explode('!',$startRowColRef); + } + if ($rangeWS1 != '') $rangeWS1 .= '!'; + $rangeWS2 = $rangeWS1; + if (strpos('!',$val) !== FALSE) { + list($rangeWS2,$val) = explode('!',$val); + } + if ($rangeWS2 != '') $rangeWS2 .= '!'; + if ((is_integer($startRowColRef)) && (ctype_digit($val)) && + ($startRowColRef <= 1048576) && ($val <= 1048576)) { + // Row range + $endRowColRef = ($pCellParent !== NULL) ? $pCellParent->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007 + $output[count($output)-1]['value'] = $rangeWS1.'A'.$startRowColRef; + $val = $rangeWS2.$endRowColRef.$val; + } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) && + (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) { + // Column range + $endRowColRef = ($pCellParent !== NULL) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007 + $output[count($output)-1]['value'] = $rangeWS1.strtoupper($startRowColRef).'1'; + $val = $rangeWS2.$val.$endRowColRef; + } + } + + $localeConstant = FALSE; + if ($opCharacter == '"') { +// echo 'Element is a String
'; + // UnEscape any quotes within the string + $val = self::_wrapResult(str_replace('""','"',self::_unwrapResult($val))); + } elseif (is_numeric($val)) { +// echo 'Element is a Number
'; + if ((strpos($val,'.') !== FALSE) || (stripos($val,'e') !== FALSE) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { +// echo 'Casting '.$val.' to float
'; + $val = (float) $val; + } else { +// echo 'Casting '.$val.' to integer
'; + $val = (integer) $val; + } + } elseif (isset(self::$_ExcelConstants[trim(strtoupper($val))])) { + $excelConstant = trim(strtoupper($val)); +// echo 'Element '.$excelConstant.' is an Excel Constant
'; + $val = self::$_ExcelConstants[$excelConstant]; + } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$_localeBoolean)) !== FALSE) { +// echo 'Element '.$localeConstant.' is an Excel Constant
'; + $val = self::$_ExcelConstants[$localeConstant]; + } + $details = array('type' => 'Value', 'value' => $val, 'reference' => NULL); + if ($localeConstant) { $details['localeValue'] = $localeConstant; } + $output[] = $details; + } + $index += $length; + + } elseif ($opCharacter == '$') { // absolute row or column range + ++$index; + } elseif ($opCharacter == ')') { // miscellaneous error checking + if ($expectingOperand) { + $output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => NULL); + $expectingOperand = FALSE; + $expectingOperator = TRUE; + } else { + return $this->_raiseFormulaError("Formula Error: Unexpected ')'"); + } + } elseif (isset(self::$_operators[$opCharacter]) && !$expectingOperator) { + return $this->_raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'"); + } else { // I don't even want to know what you did to get here + return $this->_raiseFormulaError("Formula Error: An unexpected error occured"); + } + // Test for end of formula string + if ($index == strlen($formula)) { + // Did we end with an operator?. + // Only valid for the % unary operator + if ((isset(self::$_operators[$opCharacter])) && ($opCharacter != '%')) { + return $this->_raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands"); + } else { + break; + } + } + // Ignore white space + while (($formula{$index} == "\n") || ($formula{$index} == "\r")) { + ++$index; + } + if ($formula{$index} == ' ') { + while ($formula{$index} == ' ') { + ++$index; + } + // If we're expecting an operator, but only have a space between the previous and next operands (and both are + // Cell References) then we have an INTERSECTION operator +// echo 'Possible Intersect Operator
'; + if (($expectingOperator) && (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'.*/Ui', substr($formula, $index), $match)) && + ($output[count($output)-1]['type'] == 'Cell Reference')) { +// echo 'Element is an Intersect Operator
'; + while($stack->count() > 0 && + ($o2 = $stack->last()) && + isset(self::$_operators[$o2['value']]) && + @(self::$_operatorAssociativity[$opCharacter] ? self::$_operatorPrecedence[$opCharacter] < self::$_operatorPrecedence[$o2['value']] : self::$_operatorPrecedence[$opCharacter] <= self::$_operatorPrecedence[$o2['value']])) { + $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output + } + $stack->push('Binary Operator','|'); // Put an Intersect Operator on the stack + $expectingOperator = FALSE; + } + } + } + + while (($op = $stack->pop()) !== NULL) { // pop everything off the stack and push onto output + if ((is_array($op) && $op['value'] == '(') || ($op === '(')) + return $this->_raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced + $output[] = $op; + } + return $output; + } // function _parseFormula() + + + private static function _dataTestReference(&$operandData) + { + $operand = $operandData['value']; + if (($operandData['reference'] === NULL) && (is_array($operand))) { + $rKeys = array_keys($operand); + $rowKey = array_shift($rKeys); + $cKeys = array_keys(array_keys($operand[$rowKey])); + $colKey = array_shift($cKeys); + if (ctype_upper($colKey)) { + $operandData['reference'] = $colKey.$rowKey; + } + } + return $operand; + } + + // evaluate postfix notation + private function _processTokenStack($tokens, $cellID = NULL, PHPExcel_Cell $pCell = NULL) { + if ($tokens == FALSE) return FALSE; + + // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection), + // so we store the parent cell collection so that we can re-attach it when necessary + $pCellWorksheet = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL; + $pCellParent = ($pCell !== NULL) ? $pCell->getParent() : null; + $stack = new PHPExcel_Calculation_Token_Stack; + + // Loop through each token in turn + foreach ($tokens as $tokenData) { +// print_r($tokenData); +// echo '
'; + $token = $tokenData['value']; +// echo 'Token is '.$token.'
'; + // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack + if (isset(self::$_binaryOperators[$token])) { +// echo 'Token is a binary operator
'; + // We must have two operands, error if we don't + if (($operand2Data = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); + if (($operand1Data = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); + + $operand1 = self::_dataTestReference($operand1Data); + $operand2 = self::_dataTestReference($operand2Data); + + // Log what we're doing + if ($token == ':') { + $this->_debugLog->writeDebugLog('Evaluating Range ', $this->_showValue($operand1Data['reference']), ' ', $token, ' ', $this->_showValue($operand2Data['reference'])); + } else { + $this->_debugLog->writeDebugLog('Evaluating ', $this->_showValue($operand1), ' ', $token, ' ', $this->_showValue($operand2)); + } + + // Process the operation in the appropriate manner + switch ($token) { + // Comparison (Boolean) Operators + case '>' : // Greater than + case '<' : // Less than + case '>=' : // Greater than or Equal to + case '<=' : // Less than or Equal to + case '=' : // Equality + case '<>' : // Inequality + $this->_executeBinaryComparisonOperation($cellID,$operand1,$operand2,$token,$stack); + break; + // Binary Operators + case ':' : // Range + $sheet1 = $sheet2 = ''; + if (strpos($operand1Data['reference'],'!') !== FALSE) { + list($sheet1,$operand1Data['reference']) = explode('!',$operand1Data['reference']); + } else { + $sheet1 = ($pCellParent !== NULL) ? $pCellWorksheet->getTitle() : ''; + } + if (strpos($operand2Data['reference'],'!') !== FALSE) { + list($sheet2,$operand2Data['reference']) = explode('!',$operand2Data['reference']); + } else { + $sheet2 = $sheet1; + } + if ($sheet1 == $sheet2) { + if ($operand1Data['reference'] === NULL) { + if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) { + $operand1Data['reference'] = $pCell->getColumn().$operand1Data['value']; + } elseif (trim($operand1Data['reference']) == '') { + $operand1Data['reference'] = $pCell->getCoordinate(); + } else { + $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow(); + } + } + if ($operand2Data['reference'] === NULL) { + if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) { + $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value']; + } elseif (trim($operand2Data['reference']) == '') { + $operand2Data['reference'] = $pCell->getCoordinate(); + } else { + $operand2Data['reference'] = $operand2Data['value'].$pCell->getRow(); + } + } + + $oData = array_merge(explode(':',$operand1Data['reference']),explode(':',$operand2Data['reference'])); + $oCol = $oRow = array(); + foreach($oData as $oDatum) { + $oCR = PHPExcel_Cell::coordinateFromString($oDatum); + $oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1; + $oRow[] = $oCR[1]; + } + $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow); + if ($pCellParent !== NULL) { + $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($sheet1), FALSE); + } else { + return $this->_raiseFormulaError('Unable to access Cell Reference'); + } + $stack->push('Cell Reference',$cellValue,$cellRef); + } else { + $stack->push('Error',PHPExcel_Calculation_Functions::REF(),NULL); + } + + break; + case '+' : // Addition + $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'plusEquals',$stack); + break; + case '-' : // Subtraction + $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'minusEquals',$stack); + break; + case '*' : // Multiplication + $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'arrayTimesEquals',$stack); + break; + case '/' : // Division + $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'arrayRightDivide',$stack); + break; + case '^' : // Exponential + $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'power',$stack); + break; + case '&' : // Concatenation + // If either of the operands is a matrix, we need to treat them both as matrices + // (converting the other operand to a matrix if need be); then perform the required + // matrix operation + if (is_bool($operand1)) { + $operand1 = ($operand1) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE']; + } + if (is_bool($operand2)) { + $operand2 = ($operand2) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE']; + } + if ((is_array($operand1)) || (is_array($operand2))) { + // Ensure that both operands are arrays/matrices + self::_checkMatrixOperands($operand1,$operand2,2); + try { + // Convert operand 1 from a PHP array to a matrix + $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1); + // Perform the required operation against the operand 1 matrix, passing in operand 2 + $matrixResult = $matrix->concat($operand2); + $result = $matrixResult->getArray(); + } catch (PHPExcel_Exception $ex) { + $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); + $result = '#VALUE!'; + } + } else { + $result = '"'.str_replace('""','"',self::_unwrapResult($operand1,'"').self::_unwrapResult($operand2,'"')).'"'; + } + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result)); + $stack->push('Value',$result); + break; + case '|' : // Intersect + $rowIntersect = array_intersect_key($operand1,$operand2); + $cellIntersect = $oCol = $oRow = array(); + foreach(array_keys($rowIntersect) as $row) { + $oRow[] = $row; + foreach($rowIntersect[$row] as $col => $data) { + $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1; + $cellIntersect[$row] = array_intersect_key($operand1[$row],$operand2[$row]); + } + } + $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow); + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($cellIntersect)); + $stack->push('Value',$cellIntersect,$cellRef); + break; + } + + // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on + } elseif (($token === '~') || ($token === '%')) { +// echo 'Token is a unary operator
'; + if (($arg = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); + $arg = $arg['value']; + if ($token === '~') { +// echo 'Token is a negation operator
'; + $this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->_showValue($arg)); + $multiplier = -1; + } else { +// echo 'Token is a percentile operator
'; + $this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->_showValue($arg)); + $multiplier = 0.01; + } + if (is_array($arg)) { + self::_checkMatrixOperands($arg,$multiplier,2); + try { + $matrix1 = new PHPExcel_Shared_JAMA_Matrix($arg); + $matrixResult = $matrix1->arrayTimesEquals($multiplier); + $result = $matrixResult->getArray(); + } catch (PHPExcel_Exception $ex) { + $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); + $result = '#VALUE!'; + } + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result)); + $stack->push('Value',$result); + } else { + $this->_executeNumericBinaryOperation($cellID,$multiplier,$arg,'*','arrayTimesEquals',$stack); + } + + } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) { + $cellRef = NULL; +// echo 'Element '.$token.' is a Cell reference
'; + if (isset($matches[8])) { +// echo 'Reference is a Range of cells
'; + if ($pCell === NULL) { +// We can't access the range, so return a REF error + $cellValue = PHPExcel_Calculation_Functions::REF(); + } else { + $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10]; + if ($matches[2] > '') { + $matches[2] = trim($matches[2],"\"'"); + if ((strpos($matches[2],'[') !== FALSE) || (strpos($matches[2],']') !== FALSE)) { + // It's a Reference to an external workbook (not currently supported) + return $this->_raiseFormulaError('Unable to access External Workbook'); + } + $matches[2] = trim($matches[2],"\"'"); +// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
'; + $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]); + if ($pCellParent !== NULL) { + $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE); + } else { + return $this->_raiseFormulaError('Unable to access Cell Reference'); + } + $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue)); +// $cellRef = $matches[2].'!'.$cellRef; + } else { +// echo '$cellRef='.$cellRef.' in current worksheet
'; + $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet'); + if ($pCellParent !== NULL) { + $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE); + } else { + return $this->_raiseFormulaError('Unable to access Cell Reference'); + } + $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->_showTypeDetails($cellValue)); + } + } + } else { +// echo 'Reference is a single Cell
'; + if ($pCell === NULL) { +// We can't access the cell, so return a REF error + $cellValue = PHPExcel_Calculation_Functions::REF(); + } else { + $cellRef = $matches[6].$matches[7]; + if ($matches[2] > '') { + $matches[2] = trim($matches[2],"\"'"); + if ((strpos($matches[2],'[') !== FALSE) || (strpos($matches[2],']') !== FALSE)) { + // It's a Reference to an external workbook (not currently supported) + return $this->_raiseFormulaError('Unable to access External Workbook'); + } +// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
'; + $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]); + if ($pCellParent !== NULL) { + if ($this->_workbook->getSheetByName($matches[2])->cellExists($cellRef)) { + $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE); + $pCell->attach($pCellParent); + } else { + $cellValue = NULL; + } + } else { + return $this->_raiseFormulaError('Unable to access Cell Reference'); + } + $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue)); +// $cellRef = $matches[2].'!'.$cellRef; + } else { +// echo '$cellRef='.$cellRef.' in current worksheet
'; + $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet'); + if ($pCellParent->isDataSet($cellRef)) { + $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE); + $pCell->attach($pCellParent); + } else { + $cellValue = NULL; + } + $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->_showTypeDetails($cellValue)); + } + } + } + $stack->push('Value',$cellValue,$cellRef); + + // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on + } elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) { +// echo 'Token is a function
'; + $functionName = $matches[1]; + $argCount = $stack->pop(); + $argCount = $argCount['value']; + if ($functionName != 'MKMATRIX') { + $this->_debugLog->writeDebugLog('Evaluating Function ', self::_localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's')); + } + if ((isset(self::$_PHPExcelFunctions[$functionName])) || (isset(self::$_controlFunctions[$functionName]))) { // function + if (isset(self::$_PHPExcelFunctions[$functionName])) { + $functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall']; + $passByReference = isset(self::$_PHPExcelFunctions[$functionName]['passByReference']); + $passCellReference = isset(self::$_PHPExcelFunctions[$functionName]['passCellReference']); + } elseif (isset(self::$_controlFunctions[$functionName])) { + $functionCall = self::$_controlFunctions[$functionName]['functionCall']; + $passByReference = isset(self::$_controlFunctions[$functionName]['passByReference']); + $passCellReference = isset(self::$_controlFunctions[$functionName]['passCellReference']); + } + // get the arguments for this function +// echo 'Function '.$functionName.' expects '.$argCount.' arguments
'; + $args = $argArrayVals = array(); + for ($i = 0; $i < $argCount; ++$i) { + $arg = $stack->pop(); + $a = $argCount - $i - 1; + if (($passByReference) && + (isset(self::$_PHPExcelFunctions[$functionName]['passByReference'][$a])) && + (self::$_PHPExcelFunctions[$functionName]['passByReference'][$a])) { + if ($arg['reference'] === NULL) { + $args[] = $cellID; + if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($cellID); } + } else { + $args[] = $arg['reference']; + if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($arg['reference']); } + } + } else { + $args[] = self::_unwrapResult($arg['value']); + if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($arg['value']); } + } + } + // Reverse the order of the arguments + krsort($args); + if (($passByReference) && ($argCount == 0)) { + $args[] = $cellID; + $argArrayVals[] = $this->_showValue($cellID); + } +// echo 'Arguments are: '; +// print_r($args); +// echo '
'; + if ($functionName != 'MKMATRIX') { + if ($this->_debugLog->getWriteDebugLog()) { + krsort($argArrayVals); + $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', implode(self::$_localeArgumentSeparator.' ',PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )'); + } + } + // Process each argument in turn, building the return value as an array +// if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) { +// $operand1 = $args[1]; +// $this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->_showValue($operand1)); +// $result = array(); +// $row = 0; +// foreach($operand1 as $args) { +// if (is_array($args)) { +// foreach($args as $arg) { +// $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($arg), ' )'); +// $r = call_user_func_array($functionCall,$arg); +// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r)); +// $result[$row][] = $r; +// } +// ++$row; +// } else { +// $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($args), ' )'); +// $r = call_user_func_array($functionCall,$args); +// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r)); +// $result[] = $r; +// } +// } +// } else { + // Process the argument with the appropriate function call + if ($passCellReference) { + $args[] = $pCell; + } + if (strpos($functionCall,'::') !== FALSE) { + $result = call_user_func_array(explode('::',$functionCall),$args); + } else { + foreach($args as &$arg) { + $arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg); + } + unset($arg); + $result = call_user_func_array($functionCall,$args); + } +// } + if ($functionName != 'MKMATRIX') { + $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($result)); + } + $stack->push('Value',self::_wrapResult($result)); + } + + } else { + // if the token is a number, boolean, string or an Excel error, push it onto the stack + if (isset(self::$_ExcelConstants[strtoupper($token)])) { + $excelConstant = strtoupper($token); +// echo 'Token is a PHPExcel constant: '.$excelConstant.'
'; + $stack->push('Constant Value',self::$_ExcelConstants[$excelConstant]); + $this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->_showTypeDetails(self::$_ExcelConstants[$excelConstant])); + } elseif ((is_numeric($token)) || ($token === NULL) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) { +// echo 'Token is a number, boolean, string, null or an Excel error
'; + $stack->push('Value',$token); + // if the token is a named range, push the named range name onto the stack + } elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) { +// echo 'Token is a named range
'; + $namedRange = $matches[6]; +// echo 'Named Range is '.$namedRange.'
'; + $this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange); + $cellValue = $this->extractNamedRange($namedRange, ((NULL !== $pCell) ? $pCellWorksheet : NULL), FALSE); + $pCell->attach($pCellParent); + $this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->_showTypeDetails($cellValue)); + $stack->push('Named Range',$cellValue,$namedRange); + } else { + return $this->_raiseFormulaError("undefined variable '$token'"); + } + } + } + // when we're out of tokens, the stack should have a single element, the final result + if ($stack->count() != 1) return $this->_raiseFormulaError("internal error"); + $output = $stack->pop(); + $output = $output['value']; + +// if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) { +// return array_shift(PHPExcel_Calculation_Functions::flattenArray($output)); +// } + return $output; + } // function _processTokenStack() + + + private function _validateBinaryOperand($cellID, &$operand, &$stack) { + // Numbers, matrices and booleans can pass straight through, as they're already valid + if (is_string($operand)) { + // We only need special validations for the operand if it is a string + // Start by stripping off the quotation marks we use to identify true excel string values internally + if ($operand > '' && $operand{0} == '"') { $operand = self::_unwrapResult($operand); } + // If the string is a numeric value, we treat it as a numeric, so no further testing + if (!is_numeric($operand)) { + // If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations + if ($operand > '' && $operand{0} == '#') { + $stack->push('Value', $operand); + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($operand)); + return FALSE; + } elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) { + // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations + $stack->push('Value', '#VALUE!'); + $this->_debugLog->writeDebugLog('Evaluation Result is a ', $this->_showTypeDetails('#VALUE!')); + return FALSE; + } + } + } + + // return a true if the value of the operand is one that we can use in normal binary operations + return TRUE; + } // function _validateBinaryOperand() + + + private function _executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays=FALSE) { + // If we're dealing with matrix operations, we want a matrix result + if ((is_array($operand1)) || (is_array($operand2))) { + $result = array(); + if ((is_array($operand1)) && (!is_array($operand2))) { + foreach($operand1 as $x => $operandData) { + $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2)); + $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2,$operation,$stack); + $r = $stack->pop(); + $result[$x] = $r['value']; + } + } elseif ((!is_array($operand1)) && (is_array($operand2))) { + foreach($operand2 as $x => $operandData) { + $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operand1), ' ', $operation, ' ', $this->_showValue($operandData)); + $this->_executeBinaryComparisonOperation($cellID,$operand1,$operandData,$operation,$stack); + $r = $stack->pop(); + $result[$x] = $r['value']; + } + } else { + if (!$recursingArrays) { self::_checkMatrixOperands($operand1,$operand2,2); } + foreach($operand1 as $x => $operandData) { + $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2[$x])); + $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2[$x],$operation,$stack,TRUE); + $r = $stack->pop(); + $result[$x] = $r['value']; + } + } + // Log the result details + $this->_debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->_showTypeDetails($result)); + // And push the result onto the stack + $stack->push('Array',$result); + return TRUE; + } + + // Simple validate the two operands if they are string values + if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') { $operand1 = self::_unwrapResult($operand1); } + if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') { $operand2 = self::_unwrapResult($operand2); } + + // execute the necessary operation + switch ($operation) { + // Greater than + case '>': + $result = ($operand1 > $operand2); + break; + // Less than + case '<': + $result = ($operand1 < $operand2); + break; + // Equality + case '=': + $result = ($operand1 == $operand2); + break; + // Greater than or equal + case '>=': + $result = ($operand1 >= $operand2); + break; + // Less than or equal + case '<=': + $result = ($operand1 <= $operand2); + break; + // Inequality + case '<>': + $result = ($operand1 != $operand2); + break; + } + + // Log the result details + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result)); + // And push the result onto the stack + $stack->push('Value',$result); + return TRUE; + } // function _executeBinaryComparisonOperation() + + + private function _executeNumericBinaryOperation($cellID,$operand1,$operand2,$operation,$matrixFunction,&$stack) { + // Validate the two operands + if (!$this->_validateBinaryOperand($cellID,$operand1,$stack)) return FALSE; + if (!$this->_validateBinaryOperand($cellID,$operand2,$stack)) return FALSE; + + $executeMatrixOperation = FALSE; + // If either of the operands is a matrix, we need to treat them both as matrices + // (converting the other operand to a matrix if need be); then perform the required + // matrix operation + if ((is_array($operand1)) || (is_array($operand2))) { + // Ensure that both operands are arrays/matrices + $executeMatrixOperation = TRUE; + $mSize = array(); + list($mSize[],$mSize[],$mSize[],$mSize[]) = self::_checkMatrixOperands($operand1,$operand2,2); + + // But if they're both single cell matrices, then we can treat them as simple values + if (array_sum($mSize) == 4) { + $executeMatrixOperation = FALSE; + $operand1 = $operand1[0][0]; + $operand2 = $operand2[0][0]; + } + } + + if ($executeMatrixOperation) { + try { + // Convert operand 1 from a PHP array to a matrix + $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1); + // Perform the required operation against the operand 1 matrix, passing in operand 2 + $matrixResult = $matrix->$matrixFunction($operand2); + $result = $matrixResult->getArray(); + } catch (PHPExcel_Exception $ex) { + $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); + $result = '#VALUE!'; + } + } else { + if ((PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) && + ((is_string($operand1) && !is_numeric($operand1)) || (is_string($operand2) && !is_numeric($operand2)))) { + $result = PHPExcel_Calculation_Functions::VALUE(); + } else { + // If we're dealing with non-matrix operations, execute the necessary operation + switch ($operation) { + // Addition + case '+': + $result = $operand1+$operand2; + break; + // Subtraction + case '-': + $result = $operand1-$operand2; + break; + // Multiplication + case '*': + $result = $operand1*$operand2; + break; + // Division + case '/': + if ($operand2 == 0) { + // Trap for Divide by Zero error + $stack->push('Value','#DIV/0!'); + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails('#DIV/0!')); + return FALSE; + } else { + $result = $operand1/$operand2; + } + break; + // Power + case '^': + $result = pow($operand1,$operand2); + break; + } + } + } + + // Log the result details + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result)); + // And push the result onto the stack + $stack->push('Value',$result); + return TRUE; + } // function _executeNumericBinaryOperation() + + + // trigger an error, but nicely, if need be + protected function _raiseFormulaError($errorMessage) { + $this->formulaError = $errorMessage; + $this->_cyclicReferenceStack->clear(); + if (!$this->suppressFormulaErrors) throw new PHPExcel_Calculation_Exception($errorMessage); + trigger_error($errorMessage, E_USER_ERROR); + } // function _raiseFormulaError() + + + /** + * Extract range values + * + * @param string &$pRange String based range representation + * @param PHPExcel_Worksheet $pSheet Worksheet + * @param boolean $resetLog Flag indicating whether calculation log should be reset or not + * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. + * @throws PHPExcel_Calculation_Exception + */ + public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = NULL, $resetLog = TRUE) { + // Return value + $returnValue = array (); + +// echo 'extractCellRange('.$pRange.')',PHP_EOL; + if ($pSheet !== NULL) { + $pSheetName = $pSheet->getTitle(); +// echo 'Passed sheet name is '.$pSheetName.PHP_EOL; +// echo 'Range reference is '.$pRange.PHP_EOL; + if (strpos ($pRange, '!') !== false) { +// echo '$pRange reference includes sheet reference',PHP_EOL; + list($pSheetName,$pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true); +// echo 'New sheet name is '.$pSheetName,PHP_EOL; +// echo 'Adjusted Range reference is '.$pRange,PHP_EOL; + $pSheet = $this->_workbook->getSheetByName($pSheetName); + } + + // Extract range + $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange); + $pRange = $pSheetName.'!'.$pRange; + if (!isset($aReferences[1])) { + // Single cell in range + sscanf($aReferences[0],'%[A-Z]%d', $currentCol, $currentRow); + $cellValue = NULL; + if ($pSheet->cellExists($aReferences[0])) { + $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog); + } else { + $returnValue[$currentRow][$currentCol] = NULL; + } + } else { + // Extract cell data for all cells in the range + foreach ($aReferences as $reference) { + // Extract range + sscanf($reference,'%[A-Z]%d', $currentCol, $currentRow); + $cellValue = NULL; + if ($pSheet->cellExists($reference)) { + $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog); + } else { + $returnValue[$currentRow][$currentCol] = NULL; + } + } + } + } + + // Return + return $returnValue; + } // function extractCellRange() + + + /** + * Extract range values + * + * @param string &$pRange String based range representation + * @param PHPExcel_Worksheet $pSheet Worksheet + * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. + * @param boolean $resetLog Flag indicating whether calculation log should be reset or not + * @throws PHPExcel_Calculation_Exception + */ + public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = NULL, $resetLog = TRUE) { + // Return value + $returnValue = array (); + +// echo 'extractNamedRange('.$pRange.')
'; + if ($pSheet !== NULL) { + $pSheetName = $pSheet->getTitle(); +// echo 'Current sheet name is '.$pSheetName.'
'; +// echo 'Range reference is '.$pRange.'
'; + if (strpos ($pRange, '!') !== false) { +// echo '$pRange reference includes sheet reference',PHP_EOL; + list($pSheetName,$pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true); +// echo 'New sheet name is '.$pSheetName,PHP_EOL; +// echo 'Adjusted Range reference is '.$pRange,PHP_EOL; + $pSheet = $this->_workbook->getSheetByName($pSheetName); + } + + // Named range? + $namedRange = PHPExcel_NamedRange::resolveRange($pRange, $pSheet); + if ($namedRange !== NULL) { + $pSheet = $namedRange->getWorksheet(); +// echo 'Named Range '.$pRange.' ('; + $pRange = $namedRange->getRange(); + $splitRange = PHPExcel_Cell::splitRange($pRange); + // Convert row and column references + if (ctype_alpha($splitRange[0][0])) { + $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow(); + } elseif(ctype_digit($splitRange[0][0])) { + $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1]; + } +// echo $pRange.') is in sheet '.$namedRange->getWorksheet()->getTitle().'
'; + +// if ($pSheet->getTitle() != $namedRange->getWorksheet()->getTitle()) { +// if (!$namedRange->getLocalOnly()) { +// $pSheet = $namedRange->getWorksheet(); +// } else { +// return $returnValue; +// } +// } + } else { + return PHPExcel_Calculation_Functions::REF(); + } + + // Extract range + $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange); +// var_dump($aReferences); + if (!isset($aReferences[1])) { + // Single cell (or single column or row) in range + list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]); + $cellValue = NULL; + if ($pSheet->cellExists($aReferences[0])) { + $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog); + } else { + $returnValue[$currentRow][$currentCol] = NULL; + } + } else { + // Extract cell data for all cells in the range + foreach ($aReferences as $reference) { + // Extract range + list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference); +// echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'
'; + $cellValue = NULL; + if ($pSheet->cellExists($reference)) { + $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog); + } else { + $returnValue[$currentRow][$currentCol] = NULL; + } + } + } +// print_r($returnValue); +// echo '
'; + } + + // Return + return $returnValue; + } // function extractNamedRange() + + + /** + * Is a specific function implemented? + * + * @param string $pFunction Function Name + * @return boolean + */ + public function isImplemented($pFunction = '') { + $pFunction = strtoupper ($pFunction); + if (isset(self::$_PHPExcelFunctions[$pFunction])) { + return (self::$_PHPExcelFunctions[$pFunction]['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY'); + } else { + return FALSE; + } + } // function isImplemented() + + + /** + * Get a list of all implemented functions as an array of function objects + * + * @return array of PHPExcel_Calculation_Function + */ + public function listFunctions() { + // Return value + $returnValue = array(); + // Loop functions + foreach(self::$_PHPExcelFunctions as $functionName => $function) { + if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') { + $returnValue[$functionName] = new PHPExcel_Calculation_Function($function['category'], + $functionName, + $function['functionCall'] + ); + } + } + + // Return + return $returnValue; + } // function listFunctions() + + + /** + * Get a list of all Excel function names + * + * @return array + */ + public function listAllFunctionNames() { + return array_keys(self::$_PHPExcelFunctions); + } // function listAllFunctionNames() + + /** + * Get a list of implemented Excel function names + * + * @return array + */ + public function listFunctionNames() { + // Return value + $returnValue = array(); + // Loop functions + foreach(self::$_PHPExcelFunctions as $functionName => $function) { + if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') { + $returnValue[] = $functionName; + } + } + + // Return + return $returnValue; + } // function listFunctionNames() + +} // class PHPExcel_Calculation + diff --git a/framework/library/phpexcel/PHPExcel/Cell.php b/framework/library/phpexcel/PHPExcel/Cell.php new file mode 100644 index 0000000..e652a55 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Cell.php @@ -0,0 +1,978 @@ +_parent->updateCacheData($this); + + return $this; + } + + public function detach() { + $this->_parent = NULL; + } + + public function attach(PHPExcel_CachedObjectStorage_CacheBase $parent) { + + + $this->_parent = $parent; + } + + + /** + * Create a new Cell + * + * @param mixed $pValue + * @param string $pDataType + * @param PHPExcel_Worksheet $pSheet + * @throws PHPExcel_Exception + */ + public function __construct($pValue = NULL, $pDataType = NULL, PHPExcel_Worksheet $pSheet = NULL) + { + // Initialise cell value + $this->_value = $pValue; + + // Set worksheet cache + $this->_parent = $pSheet->getCellCacheController(); + + // Set datatype? + if ($pDataType !== NULL) { + if ($pDataType == PHPExcel_Cell_DataType::TYPE_STRING2) + $pDataType = PHPExcel_Cell_DataType::TYPE_STRING; + $this->_dataType = $pDataType; + } else { + if (!self::getValueBinder()->bindValue($this, $pValue)) { + throw new PHPExcel_Exception("Value could not be bound to cell."); + } + } + + // set default index to cellXf + $this->_xfIndex = 0; + } + + /** + * Get cell coordinate column + * + * @return string + */ + public function getColumn() + { + return $this->_parent->getCurrentColumn(); + } + + /** + * Get cell coordinate row + * + * @return int + */ + public function getRow() + { + return $this->_parent->getCurrentRow(); + } + + /** + * Get cell coordinate + * + * @return string + */ + public function getCoordinate() + { + return $this->_parent->getCurrentAddress(); + } + + /** + * Get cell value + * + * @return mixed + */ + public function getValue() + { + return $this->_value; + } + + /** + * Get cell value with formatting + * + * @return string + */ + public function getFormattedValue() + { + return (string) PHPExcel_Style_NumberFormat::toFormattedString( + $this->getCalculatedValue(), + $this->getWorksheet()->getParent()->getCellXfByIndex($this->getXfIndex()) + ->getNumberFormat()->getFormatCode() + ); + } + + /** + * Set cell value + * + * Sets the value for a cell, automatically determining the datatype using the value binder + * + * @param mixed $pValue Value + * @return PHPExcel_Cell + * @throws PHPExcel_Exception + */ + public function setValue($pValue = NULL) + { + if (!self::getValueBinder()->bindValue($this, $pValue)) { + throw new PHPExcel_Exception("Value could not be bound to cell."); + } + return $this; + } + + /** + * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder) + * + * @param mixed $pValue Value + * @param string $pDataType Explicit data type + * @return PHPExcel_Cell + * @throws PHPExcel_Exception + */ + public function setValueExplicit($pValue = NULL, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING) + { + // set the value according to data type + switch ($pDataType) { + case PHPExcel_Cell_DataType::TYPE_STRING2: + $pDataType = PHPExcel_Cell_DataType::TYPE_STRING; + case PHPExcel_Cell_DataType::TYPE_STRING: + case PHPExcel_Cell_DataType::TYPE_NULL: + case PHPExcel_Cell_DataType::TYPE_INLINE: + $this->_value = PHPExcel_Cell_DataType::checkString($pValue); + break; + case PHPExcel_Cell_DataType::TYPE_NUMERIC: + $this->_value = (float)$pValue; + break; + case PHPExcel_Cell_DataType::TYPE_FORMULA: + $this->_value = (string)$pValue; + break; + case PHPExcel_Cell_DataType::TYPE_BOOL: + $this->_value = (bool)$pValue; + break; + case PHPExcel_Cell_DataType::TYPE_ERROR: + $this->_value = PHPExcel_Cell_DataType::checkErrorCode($pValue); + break; + default: + throw new PHPExcel_Exception('Invalid datatype: ' . $pDataType); + break; + } + + // set the datatype + $this->_dataType = $pDataType; + + return $this->notifyCacheController(); + } + + /** + * Get calculated cell value + * + * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling + * + * @param boolean $resetLog Whether the calculation engine logger should be reset or not + * @return mixed + * @throws PHPExcel_Exception + */ + public function getCalculatedValue($resetLog = TRUE) + { +//echo 'Cell '.$this->getCoordinate().' value is a '.$this->_dataType.' with a value of '.$this->getValue().PHP_EOL; + if ($this->_dataType == PHPExcel_Cell_DataType::TYPE_FORMULA) { + try { +//echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value'.PHP_EOL; + $result = PHPExcel_Calculation::getInstance( + $this->getWorksheet()->getParent() + )->calculateCellValue($this,$resetLog); +//echo $this->getCoordinate().' calculation result is '.$result.PHP_EOL; + // We don't yet handle array returns + if (is_array($result)) { + while (is_array($result)) { + $result = array_pop($result); + } + } + } catch ( PHPExcel_Exception $ex ) { + if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->_calculatedValue !== NULL)) { +//echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().PHP_EOL; + return $this->_calculatedValue; // Fallback for calculations referencing external files. + } +//echo 'Calculation Exception: '.$ex->getMessage().PHP_EOL; + $result = '#N/A'; + throw new PHPExcel_Calculation_Exception( + $this->getWorksheet()->getTitle().'!'.$this->getCoordinate().' -> '.$ex->getMessage() + ); + } + + if ($result === '#Not Yet Implemented') { +//echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().PHP_EOL; + return $this->_calculatedValue; // Fallback if calculation engine does not support the formula. + } +//echo 'Returning calculated value of '.$result.' for cell '.$this->getCoordinate().PHP_EOL; + return $result; + } elseif($this->_value instanceof PHPExcel_RichText) { +// echo 'Cell value for '.$this->getCoordinate().' is rich text: Returning data value of '.$this->_value.'
'; + return $this->_value->getPlainText(); + } +// echo 'Cell value for '.$this->getCoordinate().' is not a formula: Returning data value of '.$this->_value.'
'; + return $this->_value; + } + + /** + * Set old calculated value (cached) + * + * @param mixed $pValue Value + * @return PHPExcel_Cell + */ + public function setCalculatedValue($pValue = NULL) + { + if ($pValue !== NULL) { + $this->_calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue; + } + + return $this->notifyCacheController(); + } + + /** + * Get old calculated value (cached) + * This returns the value last calculated by MS Excel or whichever spreadsheet program was used to + * create the original spreadsheet file. + * Note that this value is not guaranteed to refelect the actual calculated value because it is + * possible that auto-calculation was disabled in the original spreadsheet, and underlying data + * values used by the formula have changed since it was last calculated. + * + * @return mixed + */ + public function getOldCalculatedValue() + { + return $this->_calculatedValue; + } + + /** + * Get cell data type + * + * @return string + */ + public function getDataType() + { + return $this->_dataType; + } + + /** + * Set cell data type + * + * @param string $pDataType + * @return PHPExcel_Cell + */ + public function setDataType($pDataType = PHPExcel_Cell_DataType::TYPE_STRING) + { + if ($pDataType == PHPExcel_Cell_DataType::TYPE_STRING2) + $pDataType = PHPExcel_Cell_DataType::TYPE_STRING; + + $this->_dataType = $pDataType; + + return $this->notifyCacheController(); + } + + /** + * Does this cell contain Data validation rules? + * + * @return boolean + * @throws PHPExcel_Exception + */ + public function hasDataValidation() + { + if (!isset($this->_parent)) { + throw new PHPExcel_Exception('Cannot check for data validation when cell is not bound to a worksheet'); + } + + return $this->getWorksheet()->dataValidationExists($this->getCoordinate()); + } + + /** + * Get Data validation rules + * + * @return PHPExcel_Cell_DataValidation + * @throws PHPExcel_Exception + */ + public function getDataValidation() + { + if (!isset($this->_parent)) { + throw new PHPExcel_Exception('Cannot get data validation for cell that is not bound to a worksheet'); + } + + return $this->getWorksheet()->getDataValidation($this->getCoordinate()); + } + + /** + * Set Data validation rules + * + * @param PHPExcel_Cell_DataValidation $pDataValidation + * @return PHPExcel_Cell + * @throws PHPExcel_Exception + */ + public function setDataValidation(PHPExcel_Cell_DataValidation $pDataValidation = NULL) + { + if (!isset($this->_parent)) { + throw new PHPExcel_Exception('Cannot set data validation for cell that is not bound to a worksheet'); + } + + $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation); + + return $this->notifyCacheController(); + } + + /** + * Does this cell contain a Hyperlink? + * + * @return boolean + * @throws PHPExcel_Exception + */ + public function hasHyperlink() + { + if (!isset($this->_parent)) { + throw new PHPExcel_Exception('Cannot check for hyperlink when cell is not bound to a worksheet'); + } + + return $this->getWorksheet()->hyperlinkExists($this->getCoordinate()); + } + + /** + * Get Hyperlink + * + * @return PHPExcel_Cell_Hyperlink + * @throws PHPExcel_Exception + */ + public function getHyperlink() + { + if (!isset($this->_parent)) { + throw new PHPExcel_Exception('Cannot get hyperlink for cell that is not bound to a worksheet'); + } + + return $this->getWorksheet()->getHyperlink($this->getCoordinate()); + } + + /** + * Set Hyperlink + * + * @param PHPExcel_Cell_Hyperlink $pHyperlink + * @return PHPExcel_Cell + * @throws PHPExcel_Exception + */ + public function setHyperlink(PHPExcel_Cell_Hyperlink $pHyperlink = NULL) + { + if (!isset($this->_parent)) { + throw new PHPExcel_Exception('Cannot set hyperlink for cell that is not bound to a worksheet'); + } + + $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink); + + return $this->notifyCacheController(); + } + + /** + * Get parent worksheet + * + * @return PHPExcel_Worksheet + */ + public function getParent() { + return $this->_parent; + } + + /** + * Get parent worksheet + * + * @return PHPExcel_Worksheet + */ + public function getWorksheet() { + return $this->_parent->getParent(); + } + + /** + * Get cell style + * + * @return PHPExcel_Style + */ + public function getStyle() + { + return $this->getWorksheet()->getParent()->getCellXfByIndex($this->getXfIndex()); + } + + /** + * Re-bind parent + * + * @param PHPExcel_Worksheet $parent + * @return PHPExcel_Cell + */ + public function rebindParent(PHPExcel_Worksheet $parent) { + $this->_parent = $parent->getCellCacheController(); + + return $this->notifyCacheController(); + } + + /** + * Is cell in a specific range? + * + * @param string $pRange Cell range (e.g. A1:A1) + * @return boolean + */ + public function isInRange($pRange = 'A1:A1') + { + list($rangeStart,$rangeEnd) = self::rangeBoundaries($pRange); + + // Translate properties + $myColumn = self::columnIndexFromString($this->getColumn()); + $myRow = $this->getRow(); + + // Verify if cell is in range + return (($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) && + ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow) + ); + } + + /** + * Coordinate from string + * + * @param string $pCoordinateString + * @return array Array containing column and row (indexes 0 and 1) + * @throws PHPExcel_Exception + */ + public static function coordinateFromString($pCoordinateString = 'A1') + { + if (preg_match("/^([$]?[A-Z]{1,3})([$]?\d{1,7})$/", $pCoordinateString, $matches)) { + return array($matches[1],$matches[2]); + } elseif ((strpos($pCoordinateString,':') !== FALSE) || (strpos($pCoordinateString,',') !== FALSE)) { + throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells'); + } elseif ($pCoordinateString == '') { + throw new PHPExcel_Exception('Cell coordinate can not be zero-length string'); + } + + throw new PHPExcel_Exception('Invalid cell coordinate '.$pCoordinateString); + } + + /** + * Make string row, column or cell coordinate absolute + * + * @param string $pCoordinateString e.g. 'A' or '1' or 'A1' + * Note that this value can be a row or column reference as well as a cell reference + * @return string Absolute coordinate e.g. '$A' or '$1' or '$A$1' + * @throws PHPExcel_Exception + */ + public static function absoluteReference($pCoordinateString = 'A1') + { + if (strpos($pCoordinateString,':') === FALSE && strpos($pCoordinateString,',') === FALSE) { + // Split out any worksheet name from the reference + $worksheet = ''; + $cellAddress = explode('!',$pCoordinateString); + if (count($cellAddress) > 1) { + list($worksheet,$pCoordinateString) = $cellAddress; + } + if ($worksheet > '') $worksheet .= '!'; + + // Create absolute coordinate + if (ctype_digit($pCoordinateString)) { + return $worksheet . '$' . $pCoordinateString; + } elseif (ctype_alpha($pCoordinateString)) { + return $worksheet . '$' . strtoupper($pCoordinateString); + } + return $worksheet . self::absoluteCoordinate($pCoordinateString); + } + + throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells'); + } + + /** + * Make string coordinate absolute + * + * @param string $pCoordinateString e.g. 'A1' + * @return string Absolute coordinate e.g. '$A$1' + * @throws PHPExcel_Exception + */ + public static function absoluteCoordinate($pCoordinateString = 'A1') + { + if (strpos($pCoordinateString,':') === FALSE && strpos($pCoordinateString,',') === FALSE) { + // Split out any worksheet name from the coordinate + $worksheet = ''; + $cellAddress = explode('!',$pCoordinateString); + if (count($cellAddress) > 1) { + list($worksheet,$pCoordinateString) = $cellAddress; + } + if ($worksheet > '') $worksheet .= '!'; + + // Create absolute coordinate + list($column, $row) = self::coordinateFromString($pCoordinateString); + $column = ltrim($column,'$'); + $row = ltrim($row,'$'); + return $worksheet . '$' . $column . '$' . $row; + } + + throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells'); + } + + /** + * Split range into coordinate strings + * + * @param string $pRange e.g. 'B4:D9' or 'B4:D9,H2:O11' or 'B4' + * @return array Array containg one or more arrays containing one or two coordinate strings + * e.g. array('B4','D9') or array(array('B4','D9'),array('H2','O11')) + * or array('B4') + */ + public static function splitRange($pRange = 'A1:A1') + { + // Ensure $pRange is a valid range + if(empty($pRange)) { + $pRange = self::DEFAULT_RANGE; + } + + $exploded = explode(',', $pRange); + $counter = count($exploded); + for ($i = 0; $i < $counter; ++$i) { + $exploded[$i] = explode(':', $exploded[$i]); + } + return $exploded; + } + + /** + * Build range from coordinate strings + * + * @param array $pRange Array containg one or more arrays containing one or two coordinate strings + * @return string String representation of $pRange + * @throws PHPExcel_Exception + */ + public static function buildRange($pRange) + { + // Verify range + if (!is_array($pRange) || empty($pRange) || !is_array($pRange[0])) { + throw new PHPExcel_Exception('Range does not contain any information'); + } + + // Build range + $imploded = array(); + $counter = count($pRange); + for ($i = 0; $i < $counter; ++$i) { + $pRange[$i] = implode(':', $pRange[$i]); + } + $imploded = implode(',', $pRange); + + return $imploded; + } + + /** + * Calculate range boundaries + * + * @param string $pRange Cell range (e.g. A1:A1) + * @return array Range coordinates array(Start Cell, End Cell) + * where Start Cell and End Cell are arrays (Column Number, Row Number) + */ + public static function rangeBoundaries($pRange = 'A1:A1') + { + // Ensure $pRange is a valid range + if(empty($pRange)) { + $pRange = self::DEFAULT_RANGE; + } + + // Uppercase coordinate + $pRange = strtoupper($pRange); + + // Extract range + if (strpos($pRange, ':') === FALSE) { + $rangeA = $rangeB = $pRange; + } else { + list($rangeA, $rangeB) = explode(':', $pRange); + } + + // Calculate range outer borders + $rangeStart = self::coordinateFromString($rangeA); + $rangeEnd = self::coordinateFromString($rangeB); + + // Translate column into index + $rangeStart[0] = self::columnIndexFromString($rangeStart[0]); + $rangeEnd[0] = self::columnIndexFromString($rangeEnd[0]); + + return array($rangeStart, $rangeEnd); + } + + /** + * Calculate range dimension + * + * @param string $pRange Cell range (e.g. A1:A1) + * @return array Range dimension (width, height) + */ + public static function rangeDimension($pRange = 'A1:A1') + { + // Calculate range outer borders + list($rangeStart,$rangeEnd) = self::rangeBoundaries($pRange); + + return array( ($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1) ); + } + + /** + * Calculate range boundaries + * + * @param string $pRange Cell range (e.g. A1:A1) + * @return array Range coordinates array(Start Cell, End Cell) + * where Start Cell and End Cell are arrays (Column ID, Row Number) + */ + public static function getRangeBoundaries($pRange = 'A1:A1') + { + // Ensure $pRange is a valid range + if(empty($pRange)) { + $pRange = self::DEFAULT_RANGE; + } + + // Uppercase coordinate + $pRange = strtoupper($pRange); + + // Extract range + if (strpos($pRange, ':') === FALSE) { + $rangeA = $rangeB = $pRange; + } else { + list($rangeA, $rangeB) = explode(':', $pRange); + } + + return array( self::coordinateFromString($rangeA), self::coordinateFromString($rangeB)); + } + + /** + * Column index from string + * + * @param string $pString + * @return int Column index (base 1 !!!) + */ + public static function columnIndexFromString($pString = 'A') + { + // Using a lookup cache adds a slight memory overhead, but boosts speed + // caching using a static within the method is faster than a class static, + // though it's additional memory overhead + static $_indexCache = array(); + + if (isset($_indexCache[$pString])) + return $_indexCache[$pString]; + + // It's surprising how costly the strtoupper() and ord() calls actually are, so we use a lookup array rather than use ord() + // and make it case insensitive to get rid of the strtoupper() as well. Because it's a static, there's no significant + // memory overhead either + static $_columnLookup = array( + 'A' => 1, 'B' => 2, 'C' => 3, 'D' => 4, 'E' => 5, 'F' => 6, 'G' => 7, 'H' => 8, 'I' => 9, 'J' => 10, 'K' => 11, 'L' => 12, 'M' => 13, + 'N' => 14, 'O' => 15, 'P' => 16, 'Q' => 17, 'R' => 18, 'S' => 19, 'T' => 20, 'U' => 21, 'V' => 22, 'W' => 23, 'X' => 24, 'Y' => 25, 'Z' => 26, + 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'g' => 7, 'h' => 8, 'i' => 9, 'j' => 10, 'k' => 11, 'l' => 12, 'm' => 13, + 'n' => 14, 'o' => 15, 'p' => 16, 'q' => 17, 'r' => 18, 's' => 19, 't' => 20, 'u' => 21, 'v' => 22, 'w' => 23, 'x' => 24, 'y' => 25, 'z' => 26 + ); + + // We also use the language construct isset() rather than the more costly strlen() function to match the length of $pString + // for improved performance + if (isset($pString{0})) { + if (!isset($pString{1})) { + $_indexCache[$pString] = $_columnLookup[$pString]; + return $_indexCache[$pString]; + } elseif(!isset($pString{2})) { + $_indexCache[$pString] = $_columnLookup[$pString{0}] * 26 + $_columnLookup[$pString{1}]; + return $_indexCache[$pString]; + } elseif(!isset($pString{3})) { + $_indexCache[$pString] = $_columnLookup[$pString{0}] * 676 + $_columnLookup[$pString{1}] * 26 + $_columnLookup[$pString{2}]; + return $_indexCache[$pString]; + } + } + throw new PHPExcel_Exception("Column string index can not be " . ((isset($pString{0})) ? "longer than 3 characters" : "empty")); + } + + /** + * String from columnindex + * + * @param int $pColumnIndex Column index (base 0 !!!) + * @return string + */ + public static function stringFromColumnIndex($pColumnIndex = 0) + { + // Using a lookup cache adds a slight memory overhead, but boosts speed + // caching using a static within the method is faster than a class static, + // though it's additional memory overhead + static $_indexCache = array(); + + if (!isset($_indexCache[$pColumnIndex])) { + // Determine column string + if ($pColumnIndex < 26) { + $_indexCache[$pColumnIndex] = chr(65 + $pColumnIndex); + } elseif ($pColumnIndex < 702) { + $_indexCache[$pColumnIndex] = chr(64 + ($pColumnIndex / 26)) . + chr(65 + $pColumnIndex % 26); + } else { + $_indexCache[$pColumnIndex] = chr(64 + (($pColumnIndex - 26) / 676)) . + chr(65 + ((($pColumnIndex - 26) % 676) / 26)) . + chr(65 + $pColumnIndex % 26); + } + } + return $_indexCache[$pColumnIndex]; + } + + /** + * Extract all cell references in range + * + * @param string $pRange Range (e.g. A1 or A1:C10 or A1:E10 A20:E25) + * @return array Array containing single cell references + */ + public static function extractAllCellReferencesInRange($pRange = 'A1') { + // Returnvalue + $returnValue = array(); + + // Explode spaces + $cellBlocks = explode(' ', str_replace('$', '', strtoupper($pRange))); + foreach ($cellBlocks as $cellBlock) { + // Single cell? + if (strpos($cellBlock,':') === FALSE && strpos($cellBlock,',') === FALSE) { + $returnValue[] = $cellBlock; + continue; + } + + // Range... + $ranges = self::splitRange($cellBlock); + foreach($ranges as $range) { + // Single cell? + if (!isset($range[1])) { + $returnValue[] = $range[0]; + continue; + } + + // Range... + list($rangeStart, $rangeEnd) = $range; + sscanf($rangeStart,'%[A-Z]%d', $startCol, $startRow); + sscanf($rangeEnd,'%[A-Z]%d', $endCol, $endRow); + $endCol++; + + // Current data + $currentCol = $startCol; + $currentRow = $startRow; + + // Loop cells + while ($currentCol != $endCol) { + while ($currentRow <= $endRow) { + $returnValue[] = $currentCol.$currentRow; + ++$currentRow; + } + ++$currentCol; + $currentRow = $startRow; + } + } + } + + // Sort the result by column and row + $sortKeys = array(); + foreach (array_unique($returnValue) as $coord) { + sscanf($coord,'%[A-Z]%d', $column, $row); + $sortKeys[sprintf('%3s%09d',$column,$row)] = $coord; + } + ksort($sortKeys); + + // Return value + return array_values($sortKeys); + } + + /** + * Compare 2 cells + * + * @param PHPExcel_Cell $a Cell a + * @param PHPExcel_Cell $b Cell b + * @return int Result of comparison (always -1 or 1, never zero!) + */ + public static function compareCells(PHPExcel_Cell $a, PHPExcel_Cell $b) + { + if ($a->getRow() < $b->getRow()) { + return -1; + } elseif ($a->getRow() > $b->getRow()) { + return 1; + } elseif (self::columnIndexFromString($a->getColumn()) < self::columnIndexFromString($b->getColumn())) { + return -1; + } else { + return 1; + } + } + + /** + * Get value binder to use + * + * @return PHPExcel_Cell_IValueBinder + */ + public static function getValueBinder() { + if (self::$_valueBinder === NULL) { + self::$_valueBinder = new PHPExcel_Cell_DefaultValueBinder(); + } + + return self::$_valueBinder; + } + + /** + * Set value binder to use + * + * @param PHPExcel_Cell_IValueBinder $binder + * @throws PHPExcel_Exception + */ + public static function setValueBinder(PHPExcel_Cell_IValueBinder $binder = NULL) { + if ($binder === NULL) { + throw new PHPExcel_Exception("A PHPExcel_Cell_IValueBinder is required for PHPExcel to function correctly."); + } + + self::$_valueBinder = $binder; + } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() { + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + if ((is_object($value)) && ($key != '_parent')) { + $this->$key = clone $value; + } else { + $this->$key = $value; + } + } + } + + /** + * Get index to cellXf + * + * @return int + */ + public function getXfIndex() + { + return $this->_xfIndex; + } + + /** + * Set index to cellXf + * + * @param int $pValue + * @return PHPExcel_Cell + */ + public function setXfIndex($pValue = 0) + { + $this->_xfIndex = $pValue; + + return $this->notifyCacheController(); + } + + /** + * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling + */ + public function setFormulaAttributes($pAttributes) + { + $this->_formulaAttributes = $pAttributes; + return $this; + } + + /** + * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling + */ + public function getFormulaAttributes() + { + return $this->_formulaAttributes; + } + + /** + * Convert to string + * + * @return string + */ + public function __toString() + { + return (string) $this->getValue(); + } + +} + diff --git a/framework/library/phpexcel/PHPExcel/Chart.php b/framework/library/phpexcel/PHPExcel/Chart.php new file mode 100644 index 0000000..25249a3 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Chart.php @@ -0,0 +1,551 @@ +_name = $name; + $this->_title = $title; + $this->_legend = $legend; + $this->_xAxisLabel = $xAxisLabel; + $this->_yAxisLabel = $yAxisLabel; + $this->_plotArea = $plotArea; + $this->_plotVisibleOnly = $plotVisibleOnly; + $this->_displayBlanksAs = $displayBlanksAs; + } + + /** + * Get Name + * + * @return string + */ + public function getName() { + return $this->_name; + } + + /** + * Get Worksheet + * + * @return PHPExcel_Worksheet + */ + public function getWorksheet() { + return $this->_worksheet; + } + + /** + * Set Worksheet + * + * @param PHPExcel_Worksheet $pValue + * @throws PHPExcel_Chart_Exception + * @return PHPExcel_Chart + */ + public function setWorksheet(PHPExcel_Worksheet $pValue = null) { + $this->_worksheet = $pValue; + + return $this; + } + + /** + * Get Title + * + * @return PHPExcel_Chart_Title + */ + public function getTitle() { + return $this->_title; + } + + /** + * Set Title + * + * @param PHPExcel_Chart_Title $title + * @return PHPExcel_Chart + */ + public function setTitle(PHPExcel_Chart_Title $title) { + $this->_title = $title; + + return $this; + } + + /** + * Get Legend + * + * @return PHPExcel_Chart_Legend + */ + public function getLegend() { + return $this->_legend; + } + + /** + * Set Legend + * + * @param PHPExcel_Chart_Legend $legend + * @return PHPExcel_Chart + */ + public function setLegend(PHPExcel_Chart_Legend $legend) { + $this->_legend = $legend; + + return $this; + } + + /** + * Get X-Axis Label + * + * @return PHPExcel_Chart_Title + */ + public function getXAxisLabel() { + return $this->_xAxisLabel; + } + + /** + * Set X-Axis Label + * + * @param PHPExcel_Chart_Title $label + * @return PHPExcel_Chart + */ + public function setXAxisLabel(PHPExcel_Chart_Title $label) { + $this->_xAxisLabel = $label; + + return $this; + } + + /** + * Get Y-Axis Label + * + * @return PHPExcel_Chart_Title + */ + public function getYAxisLabel() { + return $this->_yAxisLabel; + } + + /** + * Set Y-Axis Label + * + * @param PHPExcel_Chart_Title $label + * @return PHPExcel_Chart + */ + public function setYAxisLabel(PHPExcel_Chart_Title $label) { + $this->_yAxisLabel = $label; + + return $this; + } + + /** + * Get Plot Area + * + * @return PHPExcel_Chart_PlotArea + */ + public function getPlotArea() { + return $this->_plotArea; + } + + /** + * Get Plot Visible Only + * + * @return boolean + */ + public function getPlotVisibleOnly() { + return $this->_plotVisibleOnly; + } + + /** + * Set Plot Visible Only + * + * @param boolean $plotVisibleOnly + * @return PHPExcel_Chart + */ + public function setPlotVisibleOnly($plotVisibleOnly = true) { + $this->_plotVisibleOnly = $plotVisibleOnly; + + return $this; + } + + /** + * Get Display Blanks as + * + * @return string + */ + public function getDisplayBlanksAs() { + return $this->_displayBlanksAs; + } + + /** + * Set Display Blanks as + * + * @param string $displayBlanksAs + * @return PHPExcel_Chart + */ + public function setDisplayBlanksAs($displayBlanksAs = '0') { + $this->_displayBlanksAs = $displayBlanksAs; + } + + + /** + * Set the Top Left position for the chart + * + * @param string $cell + * @param integer $xOffset + * @param integer $yOffset + * @return PHPExcel_Chart + */ + public function setTopLeftPosition($cell, $xOffset=null, $yOffset=null) { + $this->_topLeftCellRef = $cell; + if (!is_null($xOffset)) + $this->setTopLeftXOffset($xOffset); + if (!is_null($yOffset)) + $this->setTopLeftYOffset($yOffset); + + return $this; + } + + /** + * Get the top left position of the chart + * + * @return array an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell + */ + public function getTopLeftPosition() { + return array( 'cell' => $this->_topLeftCellRef, + 'xOffset' => $this->_topLeftXOffset, + 'yOffset' => $this->_topLeftYOffset + ); + } + + /** + * Get the cell address where the top left of the chart is fixed + * + * @return string + */ + public function getTopLeftCell() { + return $this->_topLeftCellRef; + } + + /** + * Set the Top Left cell position for the chart + * + * @param string $cell + * @return PHPExcel_Chart + */ + public function setTopLeftCell($cell) { + $this->_topLeftCellRef = $cell; + + return $this; + } + + /** + * Set the offset position within the Top Left cell for the chart + * + * @param integer $xOffset + * @param integer $yOffset + * @return PHPExcel_Chart + */ + public function setTopLeftOffset($xOffset=null,$yOffset=null) { + if (!is_null($xOffset)) + $this->setTopLeftXOffset($xOffset); + if (!is_null($yOffset)) + $this->setTopLeftYOffset($yOffset); + + return $this; + } + + /** + * Get the offset position within the Top Left cell for the chart + * + * @return integer[] + */ + public function getTopLeftOffset() { + return array( 'X' => $this->_topLeftXOffset, + 'Y' => $this->_topLeftYOffset + ); + } + + public function setTopLeftXOffset($xOffset) { + $this->_topLeftXOffset = $xOffset; + + return $this; + } + + public function getTopLeftXOffset() { + return $this->_topLeftXOffset; + } + + public function setTopLeftYOffset($yOffset) { + $this->_topLeftYOffset = $yOffset; + + return $this; + } + + public function getTopLeftYOffset() { + return $this->_topLeftYOffset; + } + + /** + * Set the Bottom Right position of the chart + * + * @param string $cell + * @param integer $xOffset + * @param integer $yOffset + * @return PHPExcel_Chart + */ + public function setBottomRightPosition($cell, $xOffset=null, $yOffset=null) { + $this->_bottomRightCellRef = $cell; + if (!is_null($xOffset)) + $this->setBottomRightXOffset($xOffset); + if (!is_null($yOffset)) + $this->setBottomRightYOffset($yOffset); + + return $this; + } + + /** + * Get the bottom right position of the chart + * + * @return array an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell + */ + public function getBottomRightPosition() { + return array( 'cell' => $this->_bottomRightCellRef, + 'xOffset' => $this->_bottomRightXOffset, + 'yOffset' => $this->_bottomRightYOffset + ); + } + + public function setBottomRightCell($cell) { + $this->_bottomRightCellRef = $cell; + + return $this; + } + + /** + * Get the cell address where the bottom right of the chart is fixed + * + * @return string + */ + public function getBottomRightCell() { + return $this->_bottomRightCellRef; + } + + /** + * Set the offset position within the Bottom Right cell for the chart + * + * @param integer $xOffset + * @param integer $yOffset + * @return PHPExcel_Chart + */ + public function setBottomRightOffset($xOffset=null,$yOffset=null) { + if (!is_null($xOffset)) + $this->setBottomRightXOffset($xOffset); + if (!is_null($yOffset)) + $this->setBottomRightYOffset($yOffset); + + return $this; + } + + /** + * Get the offset position within the Bottom Right cell for the chart + * + * @return integer[] + */ + public function getBottomRightOffset() { + return array( 'X' => $this->_bottomRightXOffset, + 'Y' => $this->_bottomRightYOffset + ); + } + + public function setBottomRightXOffset($xOffset) { + $this->_bottomRightXOffset = $xOffset; + + return $this; + } + + public function getBottomRightXOffset() { + return $this->_bottomRightXOffset; + } + + public function setBottomRightYOffset($yOffset) { + $this->_bottomRightYOffset = $yOffset; + + return $this; + } + + public function getBottomRightYOffset() { + return $this->_bottomRightYOffset; + } + + + public function refresh() { + if ($this->_worksheet !== NULL) { + $this->_plotArea->refresh($this->_worksheet); + } + } + + public function render($outputDestination = null) { + $libraryName = PHPExcel_Settings::getChartRendererName(); + if (is_null($libraryName)) { + return false; + } + // Ensure that data series values are up-to-date before we render + $this->refresh(); + + $libraryPath = PHPExcel_Settings::getChartRendererPath(); + $includePath = str_replace('\\','/',get_include_path()); + $rendererPath = str_replace('\\','/',$libraryPath); + if (strpos($rendererPath,$includePath) === false) { + set_include_path(get_include_path() . PATH_SEPARATOR . $libraryPath); + } + + $rendererName = 'PHPExcel_Chart_Renderer_'.$libraryName; + $renderer = new $rendererName($this); + + if ($outputDestination == 'php://output') { + $outputDestination = null; + } + return $renderer->render($outputDestination); + } + +} diff --git a/framework/library/phpexcel/PHPExcel/Comment.php b/framework/library/phpexcel/PHPExcel/Comment.php new file mode 100644 index 0000000..ef1818e --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Comment.php @@ -0,0 +1,327 @@ +_author = 'Author'; + $this->_text = new PHPExcel_RichText(); + $this->_fillColor = new PHPExcel_Style_Color('FFFFFFE1'); + $this->_alignment = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL; + } + + /** + * Get Author + * + * @return string + */ + public function getAuthor() { + return $this->_author; + } + + /** + * Set Author + * + * @param string $pValue + * @return PHPExcel_Comment + */ + public function setAuthor($pValue = '') { + $this->_author = $pValue; + return $this; + } + + /** + * Get Rich text comment + * + * @return PHPExcel_RichText + */ + public function getText() { + return $this->_text; + } + + /** + * Set Rich text comment + * + * @param PHPExcel_RichText $pValue + * @return PHPExcel_Comment + */ + public function setText(PHPExcel_RichText $pValue) { + $this->_text = $pValue; + return $this; + } + + /** + * Get comment width (CSS style, i.e. XXpx or YYpt) + * + * @return string + */ + public function getWidth() { + return $this->_width; + } + + /** + * Set comment width (CSS style, i.e. XXpx or YYpt) + * + * @param string $value + * @return PHPExcel_Comment + */ + public function setWidth($value = '96pt') { + $this->_width = $value; + return $this; + } + + /** + * Get comment height (CSS style, i.e. XXpx or YYpt) + * + * @return string + */ + public function getHeight() { + return $this->_height; + } + + /** + * Set comment height (CSS style, i.e. XXpx or YYpt) + * + * @param string $value + * @return PHPExcel_Comment + */ + public function setHeight($value = '55.5pt') { + $this->_height = $value; + return $this; + } + + /** + * Get left margin (CSS style, i.e. XXpx or YYpt) + * + * @return string + */ + public function getMarginLeft() { + return $this->_marginLeft; + } + + /** + * Set left margin (CSS style, i.e. XXpx or YYpt) + * + * @param string $value + * @return PHPExcel_Comment + */ + public function setMarginLeft($value = '59.25pt') { + $this->_marginLeft = $value; + return $this; + } + + /** + * Get top margin (CSS style, i.e. XXpx or YYpt) + * + * @return string + */ + public function getMarginTop() { + return $this->_marginTop; + } + + /** + * Set top margin (CSS style, i.e. XXpx or YYpt) + * + * @param string $value + * @return PHPExcel_Comment + */ + public function setMarginTop($value = '1.5pt') { + $this->_marginTop = $value; + return $this; + } + + /** + * Is the comment visible by default? + * + * @return boolean + */ + public function getVisible() { + return $this->_visible; + } + + /** + * Set comment default visibility + * + * @param boolean $value + * @return PHPExcel_Comment + */ + public function setVisible($value = false) { + $this->_visible = $value; + return $this; + } + + /** + * Get fill color + * + * @return PHPExcel_Style_Color + */ + public function getFillColor() { + return $this->_fillColor; + } + + /** + * Set Alignment + * + * @param string $pValue + * @return PHPExcel_Comment + */ + public function setAlignment($pValue = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL) { + $this->_alignment = $pValue; + return $this; + } + + /** + * Get Alignment + * + * @return string + */ + public function getAlignment() { + return $this->_alignment; + } + + /** + * Get hash code + * + * @return string Hash code + */ + public function getHashCode() { + return md5( + $this->_author + . $this->_text->getHashCode() + . $this->_width + . $this->_height + . $this->_marginLeft + . $this->_marginTop + . ($this->_visible ? 1 : 0) + . $this->_fillColor->getHashCode() + . $this->_alignment + . __CLASS__ + ); + } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() { + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + if (is_object($value)) { + $this->$key = clone $value; + } else { + $this->$key = $value; + } + } + } + + /** + * Convert to string + * + * @return string + */ + public function __toString() { + return $this->_text->getPlainText(); + } + +} diff --git a/framework/library/phpexcel/PHPExcel/Style/Color.php b/framework/library/phpexcel/PHPExcel/Style/Color.php new file mode 100644 index 0000000..f473209 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Style/Color.php @@ -0,0 +1,429 @@ +_argb = $pARGB; + } + } + + /** + * Bind parent. Only used for supervisor + * + * @param mixed $parent + * @param string $parentPropertyName + * @return PHPExcel_Style_Color + */ + public function bindParent($parent, $parentPropertyName=NULL) + { + $this->_parent = $parent; + $this->_parentPropertyName = $parentPropertyName; + return $this; + } + + /** + * Get the shared style component for the currently active cell in currently active sheet. + * Only used for style supervisor + * + * @return PHPExcel_Style_Color + */ + public function getSharedComponent() + { + switch ($this->_parentPropertyName) { + case '_endColor': + return $this->_parent->getSharedComponent()->getEndColor(); break; + case '_color': + return $this->_parent->getSharedComponent()->getColor(); break; + case '_startColor': + return $this->_parent->getSharedComponent()->getStartColor(); break; + } + } + + /** + * Build style array from subcomponents + * + * @param array $array + * @return array + */ + public function getStyleArray($array) + { + switch ($this->_parentPropertyName) { + case '_endColor': + $key = 'endcolor'; + break; + case '_color': + $key = 'color'; + break; + case '_startColor': + $key = 'startcolor'; + break; + + } + return $this->_parent->getStyleArray(array($key => $array)); + } + + /** + * Apply styles from array + * + * + * $objPHPExcel->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray( array('rgb' => '808080') ); + * + * + * @param array $pStyles Array containing style information + * @throws PHPExcel_Exception + * @return PHPExcel_Style_Color + */ + public function applyFromArray($pStyles = NULL) { + if (is_array($pStyles)) { + if ($this->_isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); + } else { + if (array_key_exists('rgb', $pStyles)) { + $this->setRGB($pStyles['rgb']); + } + if (array_key_exists('argb', $pStyles)) { + $this->setARGB($pStyles['argb']); + } + } + } else { + throw new PHPExcel_Exception("Invalid style array passed."); + } + return $this; + } + + /** + * Get ARGB + * + * @return string + */ + public function getARGB() { + if ($this->_isSupervisor) { + return $this->getSharedComponent()->getARGB(); + } + return $this->_argb; + } + + /** + * Set ARGB + * + * @param string $pValue + * @return PHPExcel_Style_Color + */ + public function setARGB($pValue = PHPExcel_Style_Color::COLOR_BLACK) { + if ($pValue == '') { + $pValue = PHPExcel_Style_Color::COLOR_BLACK; + } + if ($this->_isSupervisor) { + $styleArray = $this->getStyleArray(array('argb' => $pValue)); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + } else { + $this->_argb = $pValue; + } + return $this; + } + + /** + * Get RGB + * + * @return string + */ + public function getRGB() { + if ($this->_isSupervisor) { + return $this->getSharedComponent()->getRGB(); + } + return substr($this->_argb, 2); + } + + /** + * Set RGB + * + * @param string $pValue RGB value + * @return PHPExcel_Style_Color + */ + public function setRGB($pValue = '000000') { + if ($pValue == '') { + $pValue = '000000'; + } + if ($this->_isSupervisor) { + $styleArray = $this->getStyleArray(array('argb' => 'FF' . $pValue)); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + } else { + $this->_argb = 'FF' . $pValue; + } + return $this; + } + + /** + * Get a specified colour component of an RGB value + * + * @private + * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE + * @param int $offset Position within the RGB value to extract + * @param boolean $hex Flag indicating whether the component should be returned as a hex or a + * decimal value + * @return string The extracted colour component + */ + private static function _getColourComponent($RGB,$offset,$hex=TRUE) { + $colour = substr($RGB, $offset, 2); + if (!$hex) + $colour = hexdec($colour); + return $colour; + } + + /** + * Get the red colour component of an RGB value + * + * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE + * @param boolean $hex Flag indicating whether the component should be returned as a hex or a + * decimal value + * @return string The red colour component + */ + public static function getRed($RGB,$hex=TRUE) { + return self::_getColourComponent($RGB, strlen($RGB) - 6, $hex); + } + + /** + * Get the green colour component of an RGB value + * + * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE + * @param boolean $hex Flag indicating whether the component should be returned as a hex or a + * decimal value + * @return string The green colour component + */ + public static function getGreen($RGB,$hex=TRUE) { + return self::_getColourComponent($RGB, strlen($RGB) - 4, $hex); + } + + /** + * Get the blue colour component of an RGB value + * + * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE + * @param boolean $hex Flag indicating whether the component should be returned as a hex or a + * decimal value + * @return string The blue colour component + */ + public static function getBlue($RGB,$hex=TRUE) { + return self::_getColourComponent($RGB, strlen($RGB) - 2, $hex); + } + + /** + * Adjust the brightness of a color + * + * @param string $hex The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE) + * @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1 + * @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE) + */ + public static function changeBrightness($hex, $adjustPercentage) { + $rgba = (strlen($hex) == 8); + + $red = self::getRed($hex, FALSE); + $green = self::getGreen($hex, FALSE); + $blue = self::getBlue($hex, FALSE); + if ($adjustPercentage > 0) { + $red += (255 - $red) * $adjustPercentage; + $green += (255 - $green) * $adjustPercentage; + $blue += (255 - $blue) * $adjustPercentage; + } else { + $red += $red * $adjustPercentage; + $green += $green * $adjustPercentage; + $blue += $blue * $adjustPercentage; + } + + if ($red < 0) $red = 0; + elseif ($red > 255) $red = 255; + if ($green < 0) $green = 0; + elseif ($green > 255) $green = 255; + if ($blue < 0) $blue = 0; + elseif ($blue > 255) $blue = 255; + + $rgb = strtoupper( str_pad(dechex($red), 2, '0', 0) . + str_pad(dechex($green), 2, '0', 0) . + str_pad(dechex($blue), 2, '0', 0) + ); + return (($rgba) ? 'FF' : '') . $rgb; + } + + /** + * Get indexed color + * + * @param int $pIndex Index entry point into the colour array + * @param boolean $background Flag to indicate whether default background or foreground colour + * should be returned if the indexed colour doesn't exist + * @return PHPExcel_Style_Color + */ + public static function indexedColor($pIndex, $background=FALSE) { + // Clean parameter + $pIndex = intval($pIndex); + + // Indexed colors + if (is_null(self::$_indexedColors)) { + self::$_indexedColors = array( + 1 => 'FF000000', // System Colour #1 - Black + 2 => 'FFFFFFFF', // System Colour #2 - White + 3 => 'FFFF0000', // System Colour #3 - Red + 4 => 'FF00FF00', // System Colour #4 - Green + 5 => 'FF0000FF', // System Colour #5 - Blue + 6 => 'FFFFFF00', // System Colour #6 - Yellow + 7 => 'FFFF00FF', // System Colour #7- Magenta + 8 => 'FF00FFFF', // System Colour #8- Cyan + 9 => 'FF800000', // Standard Colour #9 + 10 => 'FF008000', // Standard Colour #10 + 11 => 'FF000080', // Standard Colour #11 + 12 => 'FF808000', // Standard Colour #12 + 13 => 'FF800080', // Standard Colour #13 + 14 => 'FF008080', // Standard Colour #14 + 15 => 'FFC0C0C0', // Standard Colour #15 + 16 => 'FF808080', // Standard Colour #16 + 17 => 'FF9999FF', // Chart Fill Colour #17 + 18 => 'FF993366', // Chart Fill Colour #18 + 19 => 'FFFFFFCC', // Chart Fill Colour #19 + 20 => 'FFCCFFFF', // Chart Fill Colour #20 + 21 => 'FF660066', // Chart Fill Colour #21 + 22 => 'FFFF8080', // Chart Fill Colour #22 + 23 => 'FF0066CC', // Chart Fill Colour #23 + 24 => 'FFCCCCFF', // Chart Fill Colour #24 + 25 => 'FF000080', // Chart Line Colour #25 + 26 => 'FFFF00FF', // Chart Line Colour #26 + 27 => 'FFFFFF00', // Chart Line Colour #27 + 28 => 'FF00FFFF', // Chart Line Colour #28 + 29 => 'FF800080', // Chart Line Colour #29 + 30 => 'FF800000', // Chart Line Colour #30 + 31 => 'FF008080', // Chart Line Colour #31 + 32 => 'FF0000FF', // Chart Line Colour #32 + 33 => 'FF00CCFF', // Standard Colour #33 + 34 => 'FFCCFFFF', // Standard Colour #34 + 35 => 'FFCCFFCC', // Standard Colour #35 + 36 => 'FFFFFF99', // Standard Colour #36 + 37 => 'FF99CCFF', // Standard Colour #37 + 38 => 'FFFF99CC', // Standard Colour #38 + 39 => 'FFCC99FF', // Standard Colour #39 + 40 => 'FFFFCC99', // Standard Colour #40 + 41 => 'FF3366FF', // Standard Colour #41 + 42 => 'FF33CCCC', // Standard Colour #42 + 43 => 'FF99CC00', // Standard Colour #43 + 44 => 'FFFFCC00', // Standard Colour #44 + 45 => 'FFFF9900', // Standard Colour #45 + 46 => 'FFFF6600', // Standard Colour #46 + 47 => 'FF666699', // Standard Colour #47 + 48 => 'FF969696', // Standard Colour #48 + 49 => 'FF003366', // Standard Colour #49 + 50 => 'FF339966', // Standard Colour #50 + 51 => 'FF003300', // Standard Colour #51 + 52 => 'FF333300', // Standard Colour #52 + 53 => 'FF993300', // Standard Colour #53 + 54 => 'FF993366', // Standard Colour #54 + 55 => 'FF333399', // Standard Colour #55 + 56 => 'FF333333' // Standard Colour #56 + ); + } + + if (array_key_exists($pIndex, self::$_indexedColors)) { + return new PHPExcel_Style_Color(self::$_indexedColors[$pIndex]); + } + + if ($background) { + return new PHPExcel_Style_Color('FFFFFFFF'); + } + return new PHPExcel_Style_Color('FF000000'); + } + + /** + * Get hash code + * + * @return string Hash code + */ + public function getHashCode() { + if ($this->_isSupervisor) { + return $this->getSharedComponent()->getHashCode(); + } + return md5( + $this->_argb + . __CLASS__ + ); + } + +} diff --git a/framework/library/phpexcel/PHPExcel/Style/Conditional.php b/framework/library/phpexcel/PHPExcel/Style/Conditional.php new file mode 100644 index 0000000..6411b4f --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Style/Conditional.php @@ -0,0 +1,277 @@ +_conditionType = PHPExcel_Style_Conditional::CONDITION_NONE; + $this->_operatorType = PHPExcel_Style_Conditional::OPERATOR_NONE; + $this->_text = null; + $this->_condition = array(); + $this->_style = new PHPExcel_Style(FALSE, TRUE); + } + + /** + * Get Condition type + * + * @return string + */ + public function getConditionType() { + return $this->_conditionType; + } + + /** + * Set Condition type + * + * @param string $pValue PHPExcel_Style_Conditional condition type + * @return PHPExcel_Style_Conditional + */ + public function setConditionType($pValue = PHPExcel_Style_Conditional::CONDITION_NONE) { + $this->_conditionType = $pValue; + return $this; + } + + /** + * Get Operator type + * + * @return string + */ + public function getOperatorType() { + return $this->_operatorType; + } + + /** + * Set Operator type + * + * @param string $pValue PHPExcel_Style_Conditional operator type + * @return PHPExcel_Style_Conditional + */ + public function setOperatorType($pValue = PHPExcel_Style_Conditional::OPERATOR_NONE) { + $this->_operatorType = $pValue; + return $this; + } + + /** + * Get text + * + * @return string + */ + public function getText() { + return $this->_text; + } + + /** + * Set text + * + * @param string $value + * @return PHPExcel_Style_Conditional + */ + public function setText($value = null) { + $this->_text = $value; + return $this; + } + + /** + * Get Condition + * + * @deprecated Deprecated, use getConditions instead + * @return string + */ + public function getCondition() { + if (isset($this->_condition[0])) { + return $this->_condition[0]; + } + + return ''; + } + + /** + * Set Condition + * + * @deprecated Deprecated, use setConditions instead + * @param string $pValue Condition + * @return PHPExcel_Style_Conditional + */ + public function setCondition($pValue = '') { + if (!is_array($pValue)) + $pValue = array($pValue); + + return $this->setConditions($pValue); + } + + /** + * Get Conditions + * + * @return string[] + */ + public function getConditions() { + return $this->_condition; + } + + /** + * Set Conditions + * + * @param string[] $pValue Condition + * @return PHPExcel_Style_Conditional + */ + public function setConditions($pValue) { + if (!is_array($pValue)) + $pValue = array($pValue); + + $this->_condition = $pValue; + return $this; + } + + /** + * Add Condition + * + * @param string $pValue Condition + * @return PHPExcel_Style_Conditional + */ + public function addCondition($pValue = '') { + $this->_condition[] = $pValue; + return $this; + } + + /** + * Get Style + * + * @return PHPExcel_Style + */ + public function getStyle() { + return $this->_style; + } + + /** + * Set Style + * + * @param PHPExcel_Style $pValue + * @throws PHPExcel_Exception + * @return PHPExcel_Style_Conditional + */ + public function setStyle(PHPExcel_Style $pValue = null) { + $this->_style = $pValue; + return $this; + } + + /** + * Get hash code + * + * @return string Hash code + */ + public function getHashCode() { + return md5( + $this->_conditionType + . $this->_operatorType + . implode(';', $this->_condition) + . $this->_style->getHashCode() + . __CLASS__ + ); + } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() { + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + if (is_object($value)) { + $this->$key = clone $value; + } else { + $this->$key = $value; + } + } + } +} diff --git a/framework/library/phpexcel/PHPExcel/Worksheet/AutoFilter/Column.php b/framework/library/phpexcel/PHPExcel/Worksheet/AutoFilter/Column.php new file mode 100644 index 0000000..393388b --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Worksheet/AutoFilter/Column.php @@ -0,0 +1,394 @@ +_columnIndex = $pColumn; + $this->_parent = $pParent; + } + + /** + * Get AutoFilter Column Index + * + * @return string + */ + public function getColumnIndex() { + return $this->_columnIndex; + } + + /** + * Set AutoFilter Column Index + * + * @param string $pColumn Column (e.g. A) + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet_AutoFilter_Column + */ + public function setColumnIndex($pColumn) { + // Uppercase coordinate + $pColumn = strtoupper($pColumn); + if ($this->_parent !== NULL) { + $this->_parent->testColumnInRange($pColumn); + } + + $this->_columnIndex = $pColumn; + + return $this; + } + + /** + * Get this Column's AutoFilter Parent + * + * @return PHPExcel_Worksheet_AutoFilter + */ + public function getParent() { + return $this->_parent; + } + + /** + * Set this Column's AutoFilter Parent + * + * @param PHPExcel_Worksheet_AutoFilter + * @return PHPExcel_Worksheet_AutoFilter_Column + */ + public function setParent(PHPExcel_Worksheet_AutoFilter $pParent = NULL) { + $this->_parent = $pParent; + + return $this; + } + + /** + * Get AutoFilter Type + * + * @return string + */ + public function getFilterType() { + return $this->_filterType; + } + + /** + * Set AutoFilter Type + * + * @param string $pFilterType + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet_AutoFilter_Column + */ + public function setFilterType($pFilterType = self::AUTOFILTER_FILTERTYPE_FILTER) { + if (!in_array($pFilterType,self::$_filterTypes)) { + throw new PHPExcel_Exception('Invalid filter type for column AutoFilter.'); + } + + $this->_filterType = $pFilterType; + + return $this; + } + + /** + * Get AutoFilter Multiple Rules And/Or Join + * + * @return string + */ + public function getJoin() { + return $this->_join; + } + + /** + * Set AutoFilter Multiple Rules And/Or + * + * @param string $pJoin And/Or + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet_AutoFilter_Column + */ + public function setJoin($pJoin = self::AUTOFILTER_COLUMN_JOIN_OR) { + // Lowercase And/Or + $pJoin = strtolower($pJoin); + if (!in_array($pJoin,self::$_ruleJoins)) { + throw new PHPExcel_Exception('Invalid rule connection for column AutoFilter.'); + } + + $this->_join = $pJoin; + + return $this; + } + + /** + * Set AutoFilter Attributes + * + * @param string[] $pAttributes + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet_AutoFilter_Column + */ + public function setAttributes($pAttributes = array()) { + $this->_attributes = $pAttributes; + + return $this; + } + + /** + * Set An AutoFilter Attribute + * + * @param string $pName Attribute Name + * @param string $pValue Attribute Value + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet_AutoFilter_Column + */ + public function setAttribute($pName, $pValue) { + $this->_attributes[$pName] = $pValue; + + return $this; + } + + /** + * Get AutoFilter Column Attributes + * + * @return string + */ + public function getAttributes() { + return $this->_attributes; + } + + /** + * Get specific AutoFilter Column Attribute + * + * @param string $pName Attribute Name + * @return string + */ + public function getAttribute($pName) { + if (isset($this->_attributes[$pName])) + return $this->_attributes[$pName]; + return NULL; + } + + /** + * Get all AutoFilter Column Rules + * + * @throws PHPExcel_Exception + * @return array of PHPExcel_Worksheet_AutoFilter_Column_Rule + */ + public function getRules() { + return $this->_ruleset; + } + + /** + * Get a specified AutoFilter Column Rule + * + * @param integer $pIndex Rule index in the ruleset array + * @return PHPExcel_Worksheet_AutoFilter_Column_Rule + */ + public function getRule($pIndex) { + if (!isset($this->_ruleset[$pIndex])) { + $this->_ruleset[$pIndex] = new PHPExcel_Worksheet_AutoFilter_Column_Rule($this); + } + return $this->_ruleset[$pIndex]; + } + + /** + * Create a new AutoFilter Column Rule in the ruleset + * + * @return PHPExcel_Worksheet_AutoFilter_Column_Rule + */ + public function createRule() { + $this->_ruleset[] = new PHPExcel_Worksheet_AutoFilter_Column_Rule($this); + + return end($this->_ruleset); + } + + /** + * Add a new AutoFilter Column Rule to the ruleset + * + * @param PHPExcel_Worksheet_AutoFilter_Column_Rule $pRule + * @param boolean $returnRule Flag indicating whether the rule object or the column object should be returned + * @return PHPExcel_Worksheet_AutoFilter_Column|PHPExcel_Worksheet_AutoFilter_Column_Rule + */ + public function addRule(PHPExcel_Worksheet_AutoFilter_Column_Rule $pRule, $returnRule=TRUE) { + $pRule->setParent($this); + $this->_ruleset[] = $pRule; + + return ($returnRule) ? $pRule : $this; + } + + /** + * Delete a specified AutoFilter Column Rule + * If the number of rules is reduced to 1, then we reset And/Or logic to Or + * + * @param integer $pIndex Rule index in the ruleset array + * @return PHPExcel_Worksheet_AutoFilter_Column + */ + public function deleteRule($pIndex) { + if (isset($this->_ruleset[$pIndex])) { + unset($this->_ruleset[$pIndex]); + // If we've just deleted down to a single rule, then reset And/Or joining to Or + if (count($this->_ruleset) <= 1) { + $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR); + } + } + + return $this; + } + + /** + * Delete all AutoFilter Column Rules + * + * @return PHPExcel_Worksheet_AutoFilter_Column + */ + public function clearRules() { + $this->_ruleset = array(); + $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR); + + return $this; + } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() { + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + if (is_object($value)) { + if ($key == '_parent') { + // Detach from autofilter parent + $this->$key = NULL; + } else { + $this->$key = clone $value; + } + } elseif ((is_array($value)) && ($key == '_ruleset')) { + // The columns array of PHPExcel_Worksheet_AutoFilter objects + $this->$key = array(); + foreach ($value as $k => $v) { + $this->$key[$k] = clone $v; + // attach the new cloned Rule to this new cloned Autofilter Cloned object + $this->$key[$k]->setParent($this); + } + } else { + $this->$key = $value; + } + } + } + +} diff --git a/framework/library/phpexcel/PHPExcel/Worksheet/CellIterator.php b/framework/library/phpexcel/PHPExcel/Worksheet/CellIterator.php new file mode 100644 index 0000000..57a14e4 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Worksheet/CellIterator.php @@ -0,0 +1,161 @@ +_subject = $subject; + $this->_rowIndex = $rowIndex; + } + + /** + * Destructor + */ + public function __destruct() { + unset($this->_subject); + } + + /** + * Rewind iterator + */ + public function rewind() { + $this->_position = 0; + } + + /** + * Current PHPExcel_Cell + * + * @return PHPExcel_Cell + */ + public function current() { + return $this->_subject->getCellByColumnAndRow($this->_position, $this->_rowIndex); + } + + /** + * Current key + * + * @return int + */ + public function key() { + return $this->_position; + } + + /** + * Next value + */ + public function next() { + ++$this->_position; + } + + /** + * Are there any more PHPExcel_Cell instances available? + * + * @return boolean + */ + public function valid() { + // columnIndexFromString() returns an index based at one, + // treat it as a count when comparing it to the base zero + // position. + $columnCount = PHPExcel_Cell::columnIndexFromString($this->_subject->getHighestColumn()); + + if ($this->_onlyExistingCells) { + // If we aren't looking at an existing cell, either + // because the first column doesn't exist or next() has + // been called onto a nonexistent cell, then loop until we + // find one, or pass the last column. + while ($this->_position < $columnCount && + !$this->_subject->cellExistsByColumnAndRow($this->_position, $this->_rowIndex)) { + ++$this->_position; + } + } + + return $this->_position < $columnCount; + } + + /** + * Get loop only existing cells + * + * @return boolean + */ + public function getIterateOnlyExistingCells() { + return $this->_onlyExistingCells; + } + + /** + * Set the iterator to loop only existing cells + * + * @param boolean $value + */ + public function setIterateOnlyExistingCells($value = true) { + $this->_onlyExistingCells = $value; + } +} diff --git a/framework/library/phpexcel/PHPExcel/Worksheet/Column.php b/framework/library/phpexcel/PHPExcel/Worksheet/Column.php new file mode 100644 index 0000000..6d3f36d --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Worksheet/Column.php @@ -0,0 +1,86 @@ +parent = $parent; + $this->columnIndex = $columnIndex; + } + + /** + * Destructor + */ + public function __destruct() + { + unset($this->parent); + } + + /** + * Get column index + * + * @return string + */ + public function getColumnIndex() + { + return $this->columnIndex; + } + + /** + * Get cell iterator + * + * @param integer $startRow The row number at which to start iterating + * @param integer $endRow Optionally, the row number at which to stop iterating + * @return PHPExcel_Worksheet_CellIterator + */ + public function getCellIterator($startRow = 1, $endRow = null) + { + return new PHPExcel_Worksheet_ColumnCellIterator($this->parent, $this->columnIndex, $startRow, $endRow); + } +} diff --git a/framework/library/phpexcel/PHPExcel/Worksheet/ColumnCellIterator.php b/framework/library/phpexcel/PHPExcel/Worksheet/ColumnCellIterator.php new file mode 100644 index 0000000..7b8c219 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Worksheet/ColumnCellIterator.php @@ -0,0 +1,216 @@ +subject = $subject; + $this->columnIndex = PHPExcel_Cell::columnIndexFromString($columnIndex) - 1; + $this->resetEnd($endRow); + $this->resetStart($startRow); + } + + /** + * Destructor + */ + public function __destruct() + { + unset($this->subject); + } + + /** + * (Re)Set the start row and the current row pointer + * + * @param integer $startRow The row number at which to start iterating + * @return PHPExcel_Worksheet_ColumnCellIterator + * @throws PHPExcel_Exception + */ + public function resetStart($startRow = 1) + { + $this->startRow = $startRow; + $this->adjustForExistingOnlyRange(); + $this->seek($startRow); + + return $this; + } + + /** + * (Re)Set the end row + * + * @param integer $endRow The row number at which to stop iterating + * @return PHPExcel_Worksheet_ColumnCellIterator + * @throws PHPExcel_Exception + */ + public function resetEnd($endRow = null) + { + $this->endRow = ($endRow) ? $endRow : $this->subject->getHighestRow(); + $this->adjustForExistingOnlyRange(); + + return $this; + } + + /** + * Set the row pointer to the selected row + * + * @param integer $row The row number to set the current pointer at + * @return PHPExcel_Worksheet_ColumnCellIterator + * @throws PHPExcel_Exception + */ + public function seek($row = 1) + { + if (($row < $this->startRow) || ($row > $this->endRow)) { + throw new PHPExcel_Exception("Row $row is out of range ({$this->startRow} - {$this->endRow})"); + } elseif ($this->onlyExistingCells && !($this->subject->cellExistsByColumnAndRow($this->columnIndex, $row))) { + throw new PHPExcel_Exception('In "IterateOnlyExistingCells" mode and Cell does not exist'); + } + $this->position = $row; + + return $this; + } + + /** + * Rewind the iterator to the starting row + */ + public function rewind() + { + $this->position = $this->startRow; + } + + /** + * Return the current cell in this worksheet column + * + * @return PHPExcel_Worksheet_Row + */ + public function current() + { + return $this->subject->getCellByColumnAndRow($this->columnIndex, $this->position); + } + + /** + * Return the current iterator key + * + * @return int + */ + public function key() + { + return $this->position; + } + + /** + * Set the iterator to its next value + */ + public function next() + { + do { + ++$this->position; + } while (($this->onlyExistingCells) && + (!$this->subject->cellExistsByColumnAndRow($this->columnIndex, $this->position)) && + ($this->position <= $this->endRow)); + } + + /** + * Set the iterator to its previous value + */ + public function prev() + { + if ($this->position <= $this->startRow) { + throw new PHPExcel_Exception("Row is already at the beginning of range ({$this->startRow} - {$this->endRow})"); + } + + do { + --$this->position; + } while (($this->onlyExistingCells) && + (!$this->subject->cellExistsByColumnAndRow($this->columnIndex, $this->position)) && + ($this->position >= $this->startRow)); + } + + /** + * Indicate if more rows exist in the worksheet range of rows that we're iterating + * + * @return boolean + */ + public function valid() + { + return $this->position <= $this->endRow; + } + + /** + * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary + * + * @throws PHPExcel_Exception + */ + protected function adjustForExistingOnlyRange() + { + if ($this->onlyExistingCells) { + while ((!$this->subject->cellExistsByColumnAndRow($this->columnIndex, $this->startRow)) && + ($this->startRow <= $this->endRow)) { + ++$this->startRow; + } + if ($this->startRow > $this->endRow) { + throw new PHPExcel_Exception('No cells exist within the specified range'); + } + while ((!$this->subject->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) && + ($this->endRow >= $this->startRow)) { + --$this->endRow; + } + if ($this->endRow < $this->startRow) { + throw new PHPExcel_Exception('No cells exist within the specified range'); + } + } + } +} diff --git a/framework/library/phpexcel/PHPExcel/Worksheet/ColumnDimension.php b/framework/library/phpexcel/PHPExcel/Worksheet/ColumnDimension.php new file mode 100644 index 0000000..202c0f4 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Worksheet/ColumnDimension.php @@ -0,0 +1,266 @@ +_columnIndex = $pIndex; + + // set default index to cellXf + $this->_xfIndex = 0; + } + + /** + * Get ColumnIndex + * + * @return string + */ + public function getColumnIndex() { + return $this->_columnIndex; + } + + /** + * Set ColumnIndex + * + * @param string $pValue + * @return PHPExcel_Worksheet_ColumnDimension + */ + public function setColumnIndex($pValue) { + $this->_columnIndex = $pValue; + return $this; + } + + /** + * Get Width + * + * @return double + */ + public function getWidth() { + return $this->_width; + } + + /** + * Set Width + * + * @param double $pValue + * @return PHPExcel_Worksheet_ColumnDimension + */ + public function setWidth($pValue = -1) { + $this->_width = $pValue; + return $this; + } + + /** + * Get Auto Size + * + * @return bool + */ + public function getAutoSize() { + return $this->_autoSize; + } + + /** + * Set Auto Size + * + * @param bool $pValue + * @return PHPExcel_Worksheet_ColumnDimension + */ + public function setAutoSize($pValue = false) { + $this->_autoSize = $pValue; + return $this; + } + + /** + * Get Visible + * + * @return bool + */ + public function getVisible() { + return $this->_visible; + } + + /** + * Set Visible + * + * @param bool $pValue + * @return PHPExcel_Worksheet_ColumnDimension + */ + public function setVisible($pValue = true) { + $this->_visible = $pValue; + return $this; + } + + /** + * Get Outline Level + * + * @return int + */ + public function getOutlineLevel() { + return $this->_outlineLevel; + } + + /** + * Set Outline Level + * + * Value must be between 0 and 7 + * + * @param int $pValue + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet_ColumnDimension + */ + public function setOutlineLevel($pValue) { + if ($pValue < 0 || $pValue > 7) { + throw new PHPExcel_Exception("Outline level must range between 0 and 7."); + } + + $this->_outlineLevel = $pValue; + return $this; + } + + /** + * Get Collapsed + * + * @return bool + */ + public function getCollapsed() { + return $this->_collapsed; + } + + /** + * Set Collapsed + * + * @param bool $pValue + * @return PHPExcel_Worksheet_ColumnDimension + */ + public function setCollapsed($pValue = true) { + $this->_collapsed = $pValue; + return $this; + } + + /** + * Get index to cellXf + * + * @return int + */ + public function getXfIndex() + { + return $this->_xfIndex; + } + + /** + * Set index to cellXf + * + * @param int $pValue + * @return PHPExcel_Worksheet_ColumnDimension + */ + public function setXfIndex($pValue = 0) + { + $this->_xfIndex = $pValue; + return $this; + } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() { + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + if (is_object($value)) { + $this->$key = clone $value; + } else { + $this->$key = $value; + } + } + } + +} diff --git a/framework/library/phpexcel/PHPExcel/Worksheet/ColumnIterator.php b/framework/library/phpexcel/PHPExcel/Worksheet/ColumnIterator.php new file mode 100644 index 0000000..0db3e53 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Worksheet/ColumnIterator.php @@ -0,0 +1,201 @@ +subject = $subject; + $this->resetEnd($endColumn); + $this->resetStart($startColumn); + } + + /** + * Destructor + */ + public function __destruct() + { + unset($this->subject); + } + + /** + * (Re)Set the start column and the current column pointer + * + * @param integer $startColumn The column address at which to start iterating + * @return PHPExcel_Worksheet_ColumnIterator + * @throws PHPExcel_Exception + */ + public function resetStart($startColumn = 'A') + { + $startColumnIndex = PHPExcel_Cell::columnIndexFromString($startColumn) - 1; + if ($startColumnIndex > PHPExcel_Cell::columnIndexFromString($this->subject->getHighestColumn()) - 1) { + throw new PHPExcel_Exception("Start column ({$startColumn}) is beyond highest column ({$this->subject->getHighestColumn()})"); + } + + $this->startColumn = $startColumnIndex; + if ($this->endColumn < $this->startColumn) { + $this->endColumn = $this->startColumn; + } + $this->seek($startColumn); + + return $this; + } + + /** + * (Re)Set the end column + * + * @param string $endColumn The column address at which to stop iterating + * @return PHPExcel_Worksheet_ColumnIterator + */ + public function resetEnd($endColumn = null) + { + $endColumn = ($endColumn) ? $endColumn : $this->subject->getHighestColumn(); + $this->endColumn = PHPExcel_Cell::columnIndexFromString($endColumn) - 1; + + return $this; + } + + /** + * Set the column pointer to the selected column + * + * @param string $column The column address to set the current pointer at + * @return PHPExcel_Worksheet_ColumnIterator + * @throws PHPExcel_Exception + */ + public function seek($column = 'A') + { + $column = PHPExcel_Cell::columnIndexFromString($column) - 1; + if (($column < $this->startColumn) || ($column > $this->endColumn)) { + throw new PHPExcel_Exception("Column $column is out of range ({$this->startColumn} - {$this->endColumn})"); + } + $this->position = $column; + + return $this; + } + + /** + * Rewind the iterator to the starting column + */ + public function rewind() + { + $this->position = $this->startColumn; + } + + /** + * Return the current column in this worksheet + * + * @return PHPExcel_Worksheet_Column + */ + public function current() + { + return new PHPExcel_Worksheet_Column($this->subject, PHPExcel_Cell::stringFromColumnIndex($this->position)); + } + + /** + * Return the current iterator key + * + * @return string + */ + public function key() + { + return PHPExcel_Cell::stringFromColumnIndex($this->position); + } + + /** + * Set the iterator to its next value + */ + public function next() + { + ++$this->position; + } + + /** + * Set the iterator to its previous value + * + * @throws PHPExcel_Exception + */ + public function prev() + { + if ($this->position <= $this->startColumn) { + throw new PHPExcel_Exception( + "Column is already at the beginning of range (" . + PHPExcel_Cell::stringFromColumnIndex($this->endColumn) . " - " . + PHPExcel_Cell::stringFromColumnIndex($this->endColumn) . ")" + ); + } + + --$this->position; + } + + /** + * Indicate if more columns exist in the worksheet range of columns that we're iterating + * + * @return boolean + */ + public function valid() + { + return $this->position <= $this->endColumn; + } +} diff --git a/framework/library/phpexcel/PHPExcel/Worksheet/Dimension.php b/framework/library/phpexcel/PHPExcel/Worksheet/Dimension.php new file mode 100644 index 0000000..84f692c --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Worksheet/Dimension.php @@ -0,0 +1,178 @@ +xfIndex = $initialValue; + } + + /** + * Get Visible + * + * @return bool + */ + public function getVisible() + { + return $this->visible; + } + + /** + * Set Visible + * + * @param bool $pValue + * @return PHPExcel_Worksheet_Dimension + */ + public function setVisible($pValue = true) + { + $this->visible = $pValue; + return $this; + } + + /** + * Get Outline Level + * + * @return int + */ + public function getOutlineLevel() + { + return $this->outlineLevel; + } + + /** + * Set Outline Level + * + * Value must be between 0 and 7 + * + * @param int $pValue + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet_Dimension + */ + public function setOutlineLevel($pValue) + { + if ($pValue < 0 || $pValue > 7) { + throw new PHPExcel_Exception("Outline level must range between 0 and 7."); + } + + $this->outlineLevel = $pValue; + return $this; + } + + /** + * Get Collapsed + * + * @return bool + */ + public function getCollapsed() + { + return $this->collapsed; + } + + /** + * Set Collapsed + * + * @param bool $pValue + * @return PHPExcel_Worksheet_Dimension + */ + public function setCollapsed($pValue = true) + { + $this->collapsed = $pValue; + return $this; + } + + /** + * Get index to cellXf + * + * @return int + */ + public function getXfIndex() + { + return $this->xfIndex; + } + + /** + * Set index to cellXf + * + * @param int $pValue + * @return PHPExcel_Worksheet_Dimension + */ + public function setXfIndex($pValue = 0) + { + $this->xfIndex = $pValue; + return $this; + } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + if (is_object($value)) { + $this->$key = clone $value; + } else { + $this->$key = $value; + } + } + } +} diff --git a/framework/library/phpexcel/PHPExcel/Writer/CSV.php b/framework/library/phpexcel/PHPExcel/Writer/CSV.php new file mode 100644 index 0000000..0770c1d --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Writer/CSV.php @@ -0,0 +1,313 @@ +_phpExcel = $phpExcel; + } + + /** + * Save PHPExcel to file + * + * @param string $pFilename + * @throws PHPExcel_Writer_Exception + */ + public function save($pFilename = null) { + // Fetch sheet + $sheet = $this->_phpExcel->getSheet($this->_sheetIndex); + + $saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->getWriteDebugLog(); + PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog(FALSE); + $saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType(); + PHPExcel_Calculation::setArrayReturnType(PHPExcel_Calculation::RETURN_ARRAY_AS_VALUE); + + // Open file + $fileHandle = fopen($pFilename, 'wb+'); + if ($fileHandle === false) { + throw new PHPExcel_Writer_Exception("Could not open file $pFilename for writing."); + } + + if ($this->_excelCompatibility) { + // Write the UTF-16LE BOM code + fwrite($fileHandle, "\xFF\xFE"); // Excel uses UTF-16LE encoding + $this->setEnclosure(); // Default enclosure is " + $this->setDelimiter("\t"); // Excel delimiter is a TAB + } elseif ($this->_useBOM) { + // Write the UTF-8 BOM code + fwrite($fileHandle, "\xEF\xBB\xBF"); + } + + // Identify the range that we need to extract from the worksheet + $maxCol = $sheet->getHighestColumn(); + $maxRow = $sheet->getHighestRow(); + + // Write rows to file + for($row = 1; $row <= $maxRow; ++$row) { + // Convert the row to an array... + $cellsArray = $sheet->rangeToArray('A'.$row.':'.$maxCol.$row,'', $this->_preCalculateFormulas); + // ... and write to the file + $this->_writeLine($fileHandle, $cellsArray[0]); + } + + // Close file + fclose($fileHandle); + + PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType); + PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog); + } + + /** + * Get delimiter + * + * @return string + */ + public function getDelimiter() { + return $this->_delimiter; + } + + /** + * Set delimiter + * + * @param string $pValue Delimiter, defaults to , + * @return PHPExcel_Writer_CSV + */ + public function setDelimiter($pValue = ',') { + $this->_delimiter = $pValue; + return $this; + } + + /** + * Get enclosure + * + * @return string + */ + public function getEnclosure() { + return $this->_enclosure; + } + + /** + * Set enclosure + * + * @param string $pValue Enclosure, defaults to " + * @return PHPExcel_Writer_CSV + */ + public function setEnclosure($pValue = '"') { + if ($pValue == '') { + $pValue = null; + } + $this->_enclosure = $pValue; + return $this; + } + + /** + * Get line ending + * + * @return string + */ + public function getLineEnding() { + return $this->_lineEnding; + } + + /** + * Set line ending + * + * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL) + * @return PHPExcel_Writer_CSV + */ + public function setLineEnding($pValue = PHP_EOL) { + $this->_lineEnding = $pValue; + return $this; + } + + /** + * Get whether BOM should be used + * + * @return boolean + */ + public function getUseBOM() { + return $this->_useBOM; + } + + /** + * Set whether BOM should be used + * + * @param boolean $pValue Use UTF-8 byte-order mark? Defaults to false + * @return PHPExcel_Writer_CSV + */ + public function setUseBOM($pValue = false) { + $this->_useBOM = $pValue; + return $this; + } + + /** + * Get whether the file should be saved with full Excel Compatibility + * + * @return boolean + */ + public function getExcelCompatibility() { + return $this->_excelCompatibility; + } + + /** + * Set whether the file should be saved with full Excel Compatibility + * + * @param boolean $pValue Set the file to be written as a fully Excel compatible csv file + * Note that this overrides other settings such as useBOM, enclosure and delimiter + * @return PHPExcel_Writer_CSV + */ + public function setExcelCompatibility($pValue = false) { + $this->_excelCompatibility = $pValue; + return $this; + } + + /** + * Get sheet index + * + * @return int + */ + public function getSheetIndex() { + return $this->_sheetIndex; + } + + /** + * Set sheet index + * + * @param int $pValue Sheet index + * @return PHPExcel_Writer_CSV + */ + public function setSheetIndex($pValue = 0) { + $this->_sheetIndex = $pValue; + return $this; + } + + /** + * Write line to CSV file + * + * @param mixed $pFileHandle PHP filehandle + * @param array $pValues Array containing values in a row + * @throws PHPExcel_Writer_Exception + */ + private function _writeLine($pFileHandle = null, $pValues = null) { + if (is_array($pValues)) { + // No leading delimiter + $writeDelimiter = false; + + // Build the line + $line = ''; + + foreach ($pValues as $element) { + // Escape enclosures + $element = str_replace($this->_enclosure, $this->_enclosure . $this->_enclosure, $element); + + // Add delimiter + if ($writeDelimiter) { + $line .= $this->_delimiter; + } else { + $writeDelimiter = true; + } + + // Add enclosed string + $line .= $this->_enclosure . $element . $this->_enclosure; + } + + // Add line ending + $line .= $this->_lineEnding; + + // Write to file + if ($this->_excelCompatibility) { + fwrite($pFileHandle, mb_convert_encoding($line,"UTF-16LE","UTF-8")); + } else { + fwrite($pFileHandle, $line); + } + } else { + throw new PHPExcel_Writer_Exception("Invalid data row passed to CSV writer."); + } + } + +} diff --git a/framework/library/phpexcel/PHPExcel/Writer/Excel2007/Chart.php b/framework/library/phpexcel/PHPExcel/Writer/Excel2007/Chart.php new file mode 100644 index 0000000..5adfb7c --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Writer/Excel2007/Chart.php @@ -0,0 +1,1184 @@ +getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY); + } + // Ensure that data series values are up-to-date before we save + $pChart->refresh(); + + // XML header + $objWriter->startDocument('1.0','UTF-8','yes'); + + // c:chartSpace + $objWriter->startElement('c:chartSpace'); + $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + + $objWriter->startElement('c:date1904'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + $objWriter->startElement('c:lang'); + $objWriter->writeAttribute('val', "en-GB"); + $objWriter->endElement(); + $objWriter->startElement('c:roundedCorners'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $this->_writeAlternateContent($objWriter); + + $objWriter->startElement('c:chart'); + + $this->_writeTitle($pChart->getTitle(), $objWriter); + + $objWriter->startElement('c:autoTitleDeleted'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $this->_writePlotArea($pChart->getPlotArea(), + $pChart->getXAxisLabel(), + $pChart->getYAxisLabel(), + $objWriter, + $pChart->getWorksheet() + ); + + $this->_writeLegend($pChart->getLegend(), $objWriter); + + + $objWriter->startElement('c:plotVisOnly'); + $objWriter->writeAttribute('val', 1); + $objWriter->endElement(); + + $objWriter->startElement('c:dispBlanksAs'); + $objWriter->writeAttribute('val', "gap"); + $objWriter->endElement(); + + $objWriter->startElement('c:showDLblsOverMax'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $objWriter->endElement(); + + $this->_writePrintSettings($objWriter); + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * Write Chart Title + * + * @param PHPExcel_Chart_Title $title + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @throws PHPExcel_Writer_Exception + */ + private function _writeTitle(PHPExcel_Chart_Title $title = null, $objWriter) + { + if (is_null($title)) { + return; + } + + $objWriter->startElement('c:title'); + $objWriter->startElement('c:tx'); + $objWriter->startElement('c:rich'); + + $objWriter->startElement('a:bodyPr'); + $objWriter->endElement(); + + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); + + $objWriter->startElement('a:p'); + + $caption = $title->getCaption(); + if ((is_array($caption)) && (count($caption) > 0)) + $caption = $caption[0]; + $this->getParentWriter()->getWriterPart('stringtable')->writeRichTextForCharts($objWriter, $caption, 'a'); + + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + + $layout = $title->getLayout(); + $this->_writeLayout($layout, $objWriter); + + $objWriter->startElement('c:overlay'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write Chart Legend + * + * @param PHPExcel_Chart_Legend $legend + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @throws PHPExcel_Writer_Exception + */ + private function _writeLegend(PHPExcel_Chart_Legend $legend = null, $objWriter) + { + if (is_null($legend)) { + return; + } + + $objWriter->startElement('c:legend'); + + $objWriter->startElement('c:legendPos'); + $objWriter->writeAttribute('val', $legend->getPosition()); + $objWriter->endElement(); + + $layout = $legend->getLayout(); + $this->_writeLayout($layout, $objWriter); + + $objWriter->startElement('c:overlay'); + $objWriter->writeAttribute('val', ($legend->getOverlay()) ? '1' : '0'); + $objWriter->endElement(); + + $objWriter->startElement('c:txPr'); + $objWriter->startElement('a:bodyPr'); + $objWriter->endElement(); + + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); + + $objWriter->startElement('a:p'); + $objWriter->startElement('a:pPr'); + $objWriter->writeAttribute('rtl', 0); + + $objWriter->startElement('a:defRPr'); + $objWriter->endElement(); + $objWriter->endElement(); + + $objWriter->startElement('a:endParaRPr'); + $objWriter->writeAttribute('lang', "en-US"); + $objWriter->endElement(); + + $objWriter->endElement(); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write Chart Plot Area + * + * @param PHPExcel_Chart_PlotArea $plotArea + * @param PHPExcel_Chart_Title $xAxisLabel + * @param PHPExcel_Chart_Title $yAxisLabel + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @throws PHPExcel_Writer_Exception + */ + private function _writePlotArea(PHPExcel_Chart_PlotArea $plotArea, + PHPExcel_Chart_Title $xAxisLabel = NULL, + PHPExcel_Chart_Title $yAxisLabel = NULL, + $objWriter, + PHPExcel_Worksheet $pSheet) + { + if (is_null($plotArea)) { + return; + } + + $id1 = $id2 = 0; + $this->_seriesIndex = 0; + $objWriter->startElement('c:plotArea'); + + $layout = $plotArea->getLayout(); + + $this->_writeLayout($layout, $objWriter); + + $chartTypes = self::_getChartType($plotArea); + $catIsMultiLevelSeries = $valIsMultiLevelSeries = FALSE; + $plotGroupingType = ''; + foreach($chartTypes as $chartType) { + $objWriter->startElement('c:'.$chartType); + + $groupCount = $plotArea->getPlotGroupCount(); + for($i = 0; $i < $groupCount; ++$i) { + $plotGroup = $plotArea->getPlotGroupByIndex($i); + $groupType = $plotGroup->getPlotType(); + if ($groupType == $chartType) { + + $plotStyle = $plotGroup->getPlotStyle(); + if ($groupType === PHPExcel_Chart_DataSeries::TYPE_RADARCHART) { + $objWriter->startElement('c:radarStyle'); + $objWriter->writeAttribute('val', $plotStyle ); + $objWriter->endElement(); + } elseif ($groupType === PHPExcel_Chart_DataSeries::TYPE_SCATTERCHART) { + $objWriter->startElement('c:scatterStyle'); + $objWriter->writeAttribute('val', $plotStyle ); + $objWriter->endElement(); + } + + $this->_writePlotGroup($plotGroup, $chartType, $objWriter, $catIsMultiLevelSeries, $valIsMultiLevelSeries, $plotGroupingType, $pSheet); + } + } + + $this->_writeDataLbls($objWriter, $layout); + + if ($chartType === PHPExcel_Chart_DataSeries::TYPE_LINECHART) { + // Line only, Line3D can't be smoothed + + $objWriter->startElement('c:smooth'); + $objWriter->writeAttribute('val', (integer) $plotGroup->getSmoothLine() ); + $objWriter->endElement(); + } elseif (($chartType === PHPExcel_Chart_DataSeries::TYPE_BARCHART) || + ($chartType === PHPExcel_Chart_DataSeries::TYPE_BARCHART_3D)) { + + $objWriter->startElement('c:gapWidth'); + $objWriter->writeAttribute('val', 150 ); + $objWriter->endElement(); + + if ($plotGroupingType == 'percentStacked' || + $plotGroupingType == 'stacked') { + + $objWriter->startElement('c:overlap'); + $objWriter->writeAttribute('val', 100 ); + $objWriter->endElement(); + } + } elseif ($chartType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) { + + $objWriter->startElement('c:bubbleScale'); + $objWriter->writeAttribute('val', 25 ); + $objWriter->endElement(); + + $objWriter->startElement('c:showNegBubbles'); + $objWriter->writeAttribute('val', 0 ); + $objWriter->endElement(); + } elseif ($chartType === PHPExcel_Chart_DataSeries::TYPE_STOCKCHART) { + + $objWriter->startElement('c:hiLowLines'); + $objWriter->endElement(); + } + + // Generate 2 unique numbers to use for axId values +// $id1 = $id2 = rand(10000000,99999999); +// do { +// $id2 = rand(10000000,99999999); +// } while ($id1 == $id2); + $id1 = '75091328'; + $id2 = '75089408'; + + if (($chartType !== PHPExcel_Chart_DataSeries::TYPE_PIECHART) && + ($chartType !== PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) && + ($chartType !== PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) { + + $objWriter->startElement('c:axId'); + $objWriter->writeAttribute('val', $id1 ); + $objWriter->endElement(); + $objWriter->startElement('c:axId'); + $objWriter->writeAttribute('val', $id2 ); + $objWriter->endElement(); + } else { + $objWriter->startElement('c:firstSliceAng'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + if ($chartType === PHPExcel_Chart_DataSeries::TYPE_DONUTCHART) { + + $objWriter->startElement('c:holeSize'); + $objWriter->writeAttribute('val', 50); + $objWriter->endElement(); + } + } + + $objWriter->endElement(); + } + + if (($chartType !== PHPExcel_Chart_DataSeries::TYPE_PIECHART) && + ($chartType !== PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) && + ($chartType !== PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) { + + if ($chartType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) { + $this->_writeValAx($objWriter,$plotArea,$xAxisLabel,$chartType,$id1,$id2,$catIsMultiLevelSeries); + } else { + $this->_writeCatAx($objWriter,$plotArea,$xAxisLabel,$chartType,$id1,$id2,$catIsMultiLevelSeries); + } + + $this->_writeValAx($objWriter,$plotArea,$yAxisLabel,$chartType,$id1,$id2,$valIsMultiLevelSeries); + } + + $objWriter->endElement(); + } + + /** + * Write Data Labels + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param PHPExcel_Chart_Layout $chartLayout Chart layout + * @throws PHPExcel_Writer_Exception + */ + private function _writeDataLbls($objWriter, $chartLayout) + { + $objWriter->startElement('c:dLbls'); + + $objWriter->startElement('c:showLegendKey'); + $showLegendKey = (empty($chartLayout)) ? 0 : $chartLayout->getShowLegendKey(); + $objWriter->writeAttribute('val', ((empty($showLegendKey)) ? 0 : 1) ); + $objWriter->endElement(); + + + $objWriter->startElement('c:showVal'); + $showVal = (empty($chartLayout)) ? 0 : $chartLayout->getShowVal(); + $objWriter->writeAttribute('val', ((empty($showVal)) ? 0 : 1) ); + $objWriter->endElement(); + + $objWriter->startElement('c:showCatName'); + $showCatName = (empty($chartLayout)) ? 0 : $chartLayout->getShowCatName(); + $objWriter->writeAttribute('val', ((empty($showCatName)) ? 0 : 1) ); + $objWriter->endElement(); + + $objWriter->startElement('c:showSerName'); + $showSerName = (empty($chartLayout)) ? 0 : $chartLayout->getShowSerName(); + $objWriter->writeAttribute('val', ((empty($showSerName)) ? 0 : 1) ); + $objWriter->endElement(); + + $objWriter->startElement('c:showPercent'); + $showPercent = (empty($chartLayout)) ? 0 : $chartLayout->getShowPercent(); + $objWriter->writeAttribute('val', ((empty($showPercent)) ? 0 : 1) ); + $objWriter->endElement(); + + $objWriter->startElement('c:showBubbleSize'); + $showBubbleSize = (empty($chartLayout)) ? 0 : $chartLayout->getShowBubbleSize(); + $objWriter->writeAttribute('val', ((empty($showBubbleSize)) ? 0 : 1) ); + $objWriter->endElement(); + + $objWriter->startElement('c:showLeaderLines'); + $showLeaderLines = (empty($chartLayout)) ? 1 : $chartLayout->getShowLeaderLines(); + $objWriter->writeAttribute('val', ((empty($showLeaderLines)) ? 0 : 1) ); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write Category Axis + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param PHPExcel_Chart_PlotArea $plotArea + * @param PHPExcel_Chart_Title $xAxisLabel + * @param string $groupType Chart type + * @param string $id1 + * @param string $id2 + * @param boolean $isMultiLevelSeries + * @throws PHPExcel_Writer_Exception + */ + private function _writeCatAx($objWriter, PHPExcel_Chart_PlotArea $plotArea, $xAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries) + { + $objWriter->startElement('c:catAx'); + + if ($id1 > 0) { + $objWriter->startElement('c:axId'); + $objWriter->writeAttribute('val', $id1); + $objWriter->endElement(); + } + + $objWriter->startElement('c:scaling'); + $objWriter->startElement('c:orientation'); + $objWriter->writeAttribute('val', "minMax"); + $objWriter->endElement(); + $objWriter->endElement(); + + $objWriter->startElement('c:delete'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $objWriter->startElement('c:axPos'); + $objWriter->writeAttribute('val', "b"); + $objWriter->endElement(); + + if (!is_null($xAxisLabel)) { + $objWriter->startElement('c:title'); + $objWriter->startElement('c:tx'); + $objWriter->startElement('c:rich'); + + $objWriter->startElement('a:bodyPr'); + $objWriter->endElement(); + + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); + + $objWriter->startElement('a:p'); + $objWriter->startElement('a:r'); + + $caption = $xAxisLabel->getCaption(); + if (is_array($caption)) + $caption = $caption[0]; + $objWriter->startElement('a:t'); +// $objWriter->writeAttribute('xml:space', 'preserve'); + $objWriter->writeRawData(PHPExcel_Shared_String::ControlCharacterPHP2OOXML( $caption )); + $objWriter->endElement(); + + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + + $layout = $xAxisLabel->getLayout(); + $this->_writeLayout($layout, $objWriter); + + $objWriter->startElement('c:overlay'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $objWriter->endElement(); + + } + + $objWriter->startElement('c:numFmt'); + $objWriter->writeAttribute('formatCode', "General"); + $objWriter->writeAttribute('sourceLinked', 1); + $objWriter->endElement(); + + $objWriter->startElement('c:majorTickMark'); + $objWriter->writeAttribute('val', "out"); + $objWriter->endElement(); + + $objWriter->startElement('c:minorTickMark'); + $objWriter->writeAttribute('val', "none"); + $objWriter->endElement(); + + $objWriter->startElement('c:tickLblPos'); + $objWriter->writeAttribute('val', "nextTo"); + $objWriter->endElement(); + + if ($id2 > 0) { + $objWriter->startElement('c:crossAx'); + $objWriter->writeAttribute('val', $id2); + $objWriter->endElement(); + + $objWriter->startElement('c:crosses'); + $objWriter->writeAttribute('val', "autoZero"); + $objWriter->endElement(); + } + + $objWriter->startElement('c:auto'); + $objWriter->writeAttribute('val', 1); + $objWriter->endElement(); + + $objWriter->startElement('c:lblAlgn'); + $objWriter->writeAttribute('val', "ctr"); + $objWriter->endElement(); + + $objWriter->startElement('c:lblOffset'); + $objWriter->writeAttribute('val', 100); + $objWriter->endElement(); + + if ($isMultiLevelSeries) { + $objWriter->startElement('c:noMultiLvlLbl'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + } + $objWriter->endElement(); + + } + + + /** + * Write Value Axis + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param PHPExcel_Chart_PlotArea $plotArea + * @param PHPExcel_Chart_Title $yAxisLabel + * @param string $groupType Chart type + * @param string $id1 + * @param string $id2 + * @param boolean $isMultiLevelSeries + * @throws PHPExcel_Writer_Exception + */ + private function _writeValAx($objWriter, PHPExcel_Chart_PlotArea $plotArea, $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries) + { + $objWriter->startElement('c:valAx'); + + if ($id2 > 0) { + $objWriter->startElement('c:axId'); + $objWriter->writeAttribute('val', $id2); + $objWriter->endElement(); + } + + $objWriter->startElement('c:scaling'); + $objWriter->startElement('c:orientation'); + $objWriter->writeAttribute('val', "minMax"); + $objWriter->endElement(); + $objWriter->endElement(); + + $objWriter->startElement('c:delete'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $objWriter->startElement('c:axPos'); + $objWriter->writeAttribute('val', "l"); + $objWriter->endElement(); + + $objWriter->startElement('c:majorGridlines'); + $objWriter->endElement(); + + if (!is_null($yAxisLabel)) { + $objWriter->startElement('c:title'); + $objWriter->startElement('c:tx'); + $objWriter->startElement('c:rich'); + + $objWriter->startElement('a:bodyPr'); + $objWriter->endElement(); + + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); + + $objWriter->startElement('a:p'); + $objWriter->startElement('a:r'); + + $caption = $yAxisLabel->getCaption(); + if (is_array($caption)) + $caption = $caption[0]; + $objWriter->startElement('a:t'); +// $objWriter->writeAttribute('xml:space', 'preserve'); + $objWriter->writeRawData(PHPExcel_Shared_String::ControlCharacterPHP2OOXML( $caption )); + $objWriter->endElement(); + + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + + if ($groupType !== PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) { + $layout = $yAxisLabel->getLayout(); + $this->_writeLayout($layout, $objWriter); + } + + $objWriter->startElement('c:overlay'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + $objWriter->startElement('c:numFmt'); + $objWriter->writeAttribute('formatCode', "General"); + $objWriter->writeAttribute('sourceLinked', 1); + $objWriter->endElement(); + + $objWriter->startElement('c:majorTickMark'); + $objWriter->writeAttribute('val', "out"); + $objWriter->endElement(); + + $objWriter->startElement('c:minorTickMark'); + $objWriter->writeAttribute('val', "none"); + $objWriter->endElement(); + + $objWriter->startElement('c:tickLblPos'); + $objWriter->writeAttribute('val', "nextTo"); + $objWriter->endElement(); + + if ($id1 > 0) { + $objWriter->startElement('c:crossAx'); + $objWriter->writeAttribute('val', $id2); + $objWriter->endElement(); + + $objWriter->startElement('c:crosses'); + $objWriter->writeAttribute('val', "autoZero"); + $objWriter->endElement(); + + $objWriter->startElement('c:crossBetween'); + $objWriter->writeAttribute('val', "midCat"); + $objWriter->endElement(); + } + + if ($isMultiLevelSeries) { + if ($groupType !== PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) { + $objWriter->startElement('c:noMultiLvlLbl'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + } + } + $objWriter->endElement(); + + } + + + /** + * Get the data series type(s) for a chart plot series + * + * @param PHPExcel_Chart_PlotArea $plotArea + * @return string|array + * @throws PHPExcel_Writer_Exception + */ + private static function _getChartType($plotArea) + { + $groupCount = $plotArea->getPlotGroupCount(); + + if ($groupCount == 1) { + $chartType = array($plotArea->getPlotGroupByIndex(0)->getPlotType()); + } else { + $chartTypes = array(); + for($i = 0; $i < $groupCount; ++$i) { + $chartTypes[] = $plotArea->getPlotGroupByIndex($i)->getPlotType(); + } + $chartType = array_unique($chartTypes); + if (count($chartTypes) == 0) { + throw new PHPExcel_Writer_Exception('Chart is not yet implemented'); + } + } + + return $chartType; + } + + /** + * Write Plot Group (series of related plots) + * + * @param PHPExcel_Chart_DataSeries $plotGroup + * @param string $groupType Type of plot for dataseries + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param boolean &$catIsMultiLevelSeries Is category a multi-series category + * @param boolean &$valIsMultiLevelSeries Is value set a multi-series set + * @param string &$plotGroupingType Type of grouping for multi-series values + * @param PHPExcel_Worksheet $pSheet + * @throws PHPExcel_Writer_Exception + */ + private function _writePlotGroup( $plotGroup, + $groupType, + $objWriter, + &$catIsMultiLevelSeries, + &$valIsMultiLevelSeries, + &$plotGroupingType, + PHPExcel_Worksheet $pSheet + ) + { + if (is_null($plotGroup)) { + return; + } + + if (($groupType == PHPExcel_Chart_DataSeries::TYPE_BARCHART) || + ($groupType == PHPExcel_Chart_DataSeries::TYPE_BARCHART_3D)) { + $objWriter->startElement('c:barDir'); + $objWriter->writeAttribute('val', $plotGroup->getPlotDirection()); + $objWriter->endElement(); + } + + if (!is_null($plotGroup->getPlotGrouping())) { + $plotGroupingType = $plotGroup->getPlotGrouping(); + $objWriter->startElement('c:grouping'); + $objWriter->writeAttribute('val', $plotGroupingType); + $objWriter->endElement(); + } + + // Get these details before the loop, because we can use the count to check for varyColors + $plotSeriesOrder = $plotGroup->getPlotOrder(); + $plotSeriesCount = count($plotSeriesOrder); + + if (($groupType !== PHPExcel_Chart_DataSeries::TYPE_RADARCHART) && + ($groupType !== PHPExcel_Chart_DataSeries::TYPE_STOCKCHART)) { + + if ($groupType !== PHPExcel_Chart_DataSeries::TYPE_LINECHART) { + if (($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART) || + ($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) || + ($groupType == PHPExcel_Chart_DataSeries::TYPE_DONUTCHART) || + ($plotSeriesCount > 1)) { + $objWriter->startElement('c:varyColors'); + $objWriter->writeAttribute('val', 1); + $objWriter->endElement(); + } else { + $objWriter->startElement('c:varyColors'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + } + } + } + + foreach($plotSeriesOrder as $plotSeriesIdx => $plotSeriesRef) { + $objWriter->startElement('c:ser'); + + $objWriter->startElement('c:idx'); + $objWriter->writeAttribute('val', $this->_seriesIndex + $plotSeriesIdx); + $objWriter->endElement(); + + $objWriter->startElement('c:order'); + $objWriter->writeAttribute('val', $this->_seriesIndex + $plotSeriesRef); + $objWriter->endElement(); + + if (($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART) || + ($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) || + ($groupType == PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) { + + $objWriter->startElement('c:dPt'); + $objWriter->startElement('c:idx'); + $objWriter->writeAttribute('val', 3); + $objWriter->endElement(); + + $objWriter->startElement('c:bubble3D'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + + $objWriter->startElement('c:spPr'); + $objWriter->startElement('a:solidFill'); + $objWriter->startElement('a:srgbClr'); + $objWriter->writeAttribute('val', 'FF9900'); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + } + + // Labels + $plotSeriesLabel = $plotGroup->getPlotLabelByIndex($plotSeriesRef); + if ($plotSeriesLabel && ($plotSeriesLabel->getPointCount() > 0)) { + $objWriter->startElement('c:tx'); + $objWriter->startElement('c:strRef'); + $this->_writePlotSeriesLabel($plotSeriesLabel, $objWriter); + $objWriter->endElement(); + $objWriter->endElement(); + } + + // Formatting for the points + if ($groupType == PHPExcel_Chart_DataSeries::TYPE_LINECHART) { + $objWriter->startElement('c:spPr'); + $objWriter->startElement('a:ln'); + $objWriter->writeAttribute('w', 12700); + $objWriter->endElement(); + $objWriter->endElement(); + } + + $plotSeriesValues = $plotGroup->getPlotValuesByIndex($plotSeriesRef); + if ($plotSeriesValues) { + $plotSeriesMarker = $plotSeriesValues->getPointMarker(); + if ($plotSeriesMarker) { + $objWriter->startElement('c:marker'); + $objWriter->startElement('c:symbol'); + $objWriter->writeAttribute('val', $plotSeriesMarker); + $objWriter->endElement(); + + if ($plotSeriesMarker !== 'none') { + $objWriter->startElement('c:size'); + $objWriter->writeAttribute('val', 3); + $objWriter->endElement(); + } + $objWriter->endElement(); + } + } + + if (($groupType === PHPExcel_Chart_DataSeries::TYPE_BARCHART) || + ($groupType === PHPExcel_Chart_DataSeries::TYPE_BARCHART_3D) || + ($groupType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART)) { + + $objWriter->startElement('c:invertIfNegative'); + $objWriter->writeAttribute('val', 0); + $objWriter->endElement(); + } + + // Category Labels + $plotSeriesCategory = $plotGroup->getPlotCategoryByIndex($plotSeriesRef); + if ($plotSeriesCategory && ($plotSeriesCategory->getPointCount() > 0)) { + $catIsMultiLevelSeries = $catIsMultiLevelSeries || $plotSeriesCategory->isMultiLevelSeries(); + + if (($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART) || + ($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) || + ($groupType == PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) { + + if (!is_null($plotGroup->getPlotStyle())) { + $plotStyle = $plotGroup->getPlotStyle(); + if ($plotStyle) { + $objWriter->startElement('c:explosion'); + $objWriter->writeAttribute('val', 25); + $objWriter->endElement(); + } + } + } + + if (($groupType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) || + ($groupType === PHPExcel_Chart_DataSeries::TYPE_SCATTERCHART)) { + $objWriter->startElement('c:xVal'); + } else { + $objWriter->startElement('c:cat'); + } + + $this->_writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'str', $pSheet); + $objWriter->endElement(); + } + + // Values + if ($plotSeriesValues) { + $valIsMultiLevelSeries = $valIsMultiLevelSeries || $plotSeriesValues->isMultiLevelSeries(); + + if (($groupType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) || + ($groupType === PHPExcel_Chart_DataSeries::TYPE_SCATTERCHART)) { + $objWriter->startElement('c:yVal'); + } else { + $objWriter->startElement('c:val'); + } + + $this->_writePlotSeriesValues($plotSeriesValues, $objWriter, $groupType, 'num', $pSheet); + $objWriter->endElement(); + } + + if ($groupType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) { + $this->_writeBubbles($plotSeriesValues, $objWriter, $pSheet); + } + + $objWriter->endElement(); + + } + + $this->_seriesIndex += $plotSeriesIdx + 1; + } + + /** + * Write Plot Series Label + * + * @param PHPExcel_Chart_DataSeriesValues $plotSeriesLabel + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @throws PHPExcel_Writer_Exception + */ + private function _writePlotSeriesLabel($plotSeriesLabel, $objWriter) + { + if (is_null($plotSeriesLabel)) { + return; + } + + $objWriter->startElement('c:f'); + $objWriter->writeRawData($plotSeriesLabel->getDataSource()); + $objWriter->endElement(); + + $objWriter->startElement('c:strCache'); + $objWriter->startElement('c:ptCount'); + $objWriter->writeAttribute('val', $plotSeriesLabel->getPointCount() ); + $objWriter->endElement(); + + foreach($plotSeriesLabel->getDataValues() as $plotLabelKey => $plotLabelValue) { + $objWriter->startElement('c:pt'); + $objWriter->writeAttribute('idx', $plotLabelKey ); + + $objWriter->startElement('c:v'); + $objWriter->writeRawData( $plotLabelValue ); + $objWriter->endElement(); + $objWriter->endElement(); + } + $objWriter->endElement(); + + } + + /** + * Write Plot Series Values + * + * @param PHPExcel_Chart_DataSeriesValues $plotSeriesValues + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param string $groupType Type of plot for dataseries + * @param string $dataType Datatype of series values + * @param PHPExcel_Worksheet $pSheet + * @throws PHPExcel_Writer_Exception + */ + private function _writePlotSeriesValues( $plotSeriesValues, + $objWriter, + $groupType, + $dataType='str', + PHPExcel_Worksheet $pSheet + ) + { + if (is_null($plotSeriesValues)) { + return; + } + + if ($plotSeriesValues->isMultiLevelSeries()) { + $levelCount = $plotSeriesValues->multiLevelCount(); + + $objWriter->startElement('c:multiLvlStrRef'); + + $objWriter->startElement('c:f'); + $objWriter->writeRawData( $plotSeriesValues->getDataSource() ); + $objWriter->endElement(); + + $objWriter->startElement('c:multiLvlStrCache'); + + $objWriter->startElement('c:ptCount'); + $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount() ); + $objWriter->endElement(); + + for ($level = 0; $level < $levelCount; ++$level) { + $objWriter->startElement('c:lvl'); + + foreach($plotSeriesValues->getDataValues() as $plotSeriesKey => $plotSeriesValue) { + if (isset($plotSeriesValue[$level])) { + $objWriter->startElement('c:pt'); + $objWriter->writeAttribute('idx', $plotSeriesKey ); + + $objWriter->startElement('c:v'); + $objWriter->writeRawData( $plotSeriesValue[$level] ); + $objWriter->endElement(); + $objWriter->endElement(); + } + } + + $objWriter->endElement(); + } + + $objWriter->endElement(); + + $objWriter->endElement(); + } else { + $objWriter->startElement('c:'.$dataType.'Ref'); + + $objWriter->startElement('c:f'); + $objWriter->writeRawData( $plotSeriesValues->getDataSource() ); + $objWriter->endElement(); + + $objWriter->startElement('c:'.$dataType.'Cache'); + + if (($groupType != PHPExcel_Chart_DataSeries::TYPE_PIECHART) && + ($groupType != PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) && + ($groupType != PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) { + + if (($plotSeriesValues->getFormatCode() !== NULL) && + ($plotSeriesValues->getFormatCode() !== '')) { + $objWriter->startElement('c:formatCode'); + $objWriter->writeRawData( $plotSeriesValues->getFormatCode() ); + $objWriter->endElement(); + } + } + + $objWriter->startElement('c:ptCount'); + $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount() ); + $objWriter->endElement(); + + $dataValues = $plotSeriesValues->getDataValues(); + if (!empty($dataValues)) { + if (is_array($dataValues)) { + foreach($dataValues as $plotSeriesKey => $plotSeriesValue) { + $objWriter->startElement('c:pt'); + $objWriter->writeAttribute('idx', $plotSeriesKey ); + + $objWriter->startElement('c:v'); + $objWriter->writeRawData( $plotSeriesValue ); + $objWriter->endElement(); + $objWriter->endElement(); + } + } + } + + $objWriter->endElement(); + + $objWriter->endElement(); + } + } + + /** + * Write Bubble Chart Details + * + * @param PHPExcel_Chart_DataSeriesValues $plotSeriesValues + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @throws PHPExcel_Writer_Exception + */ + private function _writeBubbles($plotSeriesValues, $objWriter, PHPExcel_Worksheet $pSheet) + { + if (is_null($plotSeriesValues)) { + return; + } + + $objWriter->startElement('c:bubbleSize'); + $objWriter->startElement('c:numLit'); + + $objWriter->startElement('c:formatCode'); + $objWriter->writeRawData( 'General' ); + $objWriter->endElement(); + + $objWriter->startElement('c:ptCount'); + $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount() ); + $objWriter->endElement(); + + $dataValues = $plotSeriesValues->getDataValues(); + if (!empty($dataValues)) { + if (is_array($dataValues)) { + foreach($dataValues as $plotSeriesKey => $plotSeriesValue) { + $objWriter->startElement('c:pt'); + $objWriter->writeAttribute('idx', $plotSeriesKey ); + $objWriter->startElement('c:v'); + $objWriter->writeRawData( 1 ); + $objWriter->endElement(); + $objWriter->endElement(); + } + } + } + + $objWriter->endElement(); + $objWriter->endElement(); + + $objWriter->startElement('c:bubble3D'); + $objWriter->writeAttribute('val', 0 ); + $objWriter->endElement(); + } + + /** + * Write Layout + * + * @param PHPExcel_Chart_Layout $layout + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @throws PHPExcel_Writer_Exception + */ + private function _writeLayout(PHPExcel_Chart_Layout $layout = NULL, $objWriter) + { + $objWriter->startElement('c:layout'); + + if (!is_null($layout)) { + $objWriter->startElement('c:manualLayout'); + + $layoutTarget = $layout->getLayoutTarget(); + if (!is_null($layoutTarget)) { + $objWriter->startElement('c:layoutTarget'); + $objWriter->writeAttribute('val', $layoutTarget); + $objWriter->endElement(); + } + + $xMode = $layout->getXMode(); + if (!is_null($xMode)) { + $objWriter->startElement('c:xMode'); + $objWriter->writeAttribute('val', $xMode); + $objWriter->endElement(); + } + + $yMode = $layout->getYMode(); + if (!is_null($yMode)) { + $objWriter->startElement('c:yMode'); + $objWriter->writeAttribute('val', $yMode); + $objWriter->endElement(); + } + + $x = $layout->getXPosition(); + if (!is_null($x)) { + $objWriter->startElement('c:x'); + $objWriter->writeAttribute('val', $x); + $objWriter->endElement(); + } + + $y = $layout->getYPosition(); + if (!is_null($y)) { + $objWriter->startElement('c:y'); + $objWriter->writeAttribute('val', $y); + $objWriter->endElement(); + } + + $w = $layout->getWidth(); + if (!is_null($w)) { + $objWriter->startElement('c:w'); + $objWriter->writeAttribute('val', $w); + $objWriter->endElement(); + } + + $h = $layout->getHeight(); + if (!is_null($h)) { + $objWriter->startElement('c:h'); + $objWriter->writeAttribute('val', $h); + $objWriter->endElement(); + } + + $objWriter->endElement(); + } + + $objWriter->endElement(); + } + + /** + * Write Alternate Content block + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @throws PHPExcel_Writer_Exception + */ + private function _writeAlternateContent($objWriter) + { + $objWriter->startElement('mc:AlternateContent'); + $objWriter->writeAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006'); + + $objWriter->startElement('mc:Choice'); + $objWriter->writeAttribute('xmlns:c14', 'http://schemas.microsoft.com/office/drawing/2007/8/2/chart'); + $objWriter->writeAttribute('Requires', 'c14'); + + $objWriter->startElement('c14:style'); + $objWriter->writeAttribute('val', '102'); + $objWriter->endElement(); + $objWriter->endElement(); + + $objWriter->startElement('mc:Fallback'); + $objWriter->startElement('c:style'); + $objWriter->writeAttribute('val', '2'); + $objWriter->endElement(); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write Printer Settings + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @throws PHPExcel_Writer_Exception + */ + private function _writePrintSettings($objWriter) + { + $objWriter->startElement('c:printSettings'); + + $objWriter->startElement('c:headerFooter'); + $objWriter->endElement(); + + $objWriter->startElement('c:pageMargins'); + $objWriter->writeAttribute('footer', 0.3); + $objWriter->writeAttribute('header', 0.3); + $objWriter->writeAttribute('r', 0.7); + $objWriter->writeAttribute('l', 0.7); + $objWriter->writeAttribute('t', 0.75); + $objWriter->writeAttribute('b', 0.75); + $objWriter->endElement(); + + $objWriter->startElement('c:pageSetup'); + $objWriter->writeAttribute('orientation', "portrait"); + $objWriter->endElement(); + + $objWriter->endElement(); + } + +} diff --git a/framework/library/phpexcel/PHPExcel/Writer/Excel2007/Comments.php b/framework/library/phpexcel/PHPExcel/Writer/Excel2007/Comments.php new file mode 100644 index 0000000..e7b9c99 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Writer/Excel2007/Comments.php @@ -0,0 +1,268 @@ +getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY); + } + + // XML header + $objWriter->startDocument('1.0','UTF-8','yes'); + + // Comments cache + $comments = $pWorksheet->getComments(); + + // Authors cache + $authors = array(); + $authorId = 0; + foreach ($comments as $comment) { + if (!isset($authors[$comment->getAuthor()])) { + $authors[$comment->getAuthor()] = $authorId++; + } + } + + // comments + $objWriter->startElement('comments'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + + // Loop through authors + $objWriter->startElement('authors'); + foreach ($authors as $author => $index) { + $objWriter->writeElement('author', $author); + } + $objWriter->endElement(); + + // Loop through comments + $objWriter->startElement('commentList'); + foreach ($comments as $key => $value) { + $this->_writeComment($objWriter, $key, $value, $authors); + } + $objWriter->endElement(); + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * Write comment to XML format + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param string $pCellReference Cell reference + * @param PHPExcel_Comment $pComment Comment + * @param array $pAuthors Array of authors + * @throws PHPExcel_Writer_Exception + */ + public function _writeComment(PHPExcel_Shared_XMLWriter $objWriter = null, $pCellReference = 'A1', PHPExcel_Comment $pComment = null, $pAuthors = null) + { + // comment + $objWriter->startElement('comment'); + $objWriter->writeAttribute('ref', $pCellReference); + $objWriter->writeAttribute('authorId', $pAuthors[$pComment->getAuthor()]); + + // text + $objWriter->startElement('text'); + $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $pComment->getText()); + $objWriter->endElement(); + + $objWriter->endElement(); + } + + /** + * Write VML comments to XML format + * + * @param PHPExcel_Worksheet $pWorksheet + * @return string XML Output + * @throws PHPExcel_Writer_Exception + */ + public function writeVMLComments(PHPExcel_Worksheet $pWorksheet = null) + { + // Create XML writer + $objWriter = null; + if ($this->getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY); + } + + // XML header + $objWriter->startDocument('1.0','UTF-8','yes'); + + // Comments cache + $comments = $pWorksheet->getComments(); + + // xml + $objWriter->startElement('xml'); + $objWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml'); + $objWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office'); + $objWriter->writeAttribute('xmlns:x', 'urn:schemas-microsoft-com:office:excel'); + + // o:shapelayout + $objWriter->startElement('o:shapelayout'); + $objWriter->writeAttribute('v:ext', 'edit'); + + // o:idmap + $objWriter->startElement('o:idmap'); + $objWriter->writeAttribute('v:ext', 'edit'); + $objWriter->writeAttribute('data', '1'); + $objWriter->endElement(); + + $objWriter->endElement(); + + // v:shapetype + $objWriter->startElement('v:shapetype'); + $objWriter->writeAttribute('id', '_x0000_t202'); + $objWriter->writeAttribute('coordsize', '21600,21600'); + $objWriter->writeAttribute('o:spt', '202'); + $objWriter->writeAttribute('path', 'm,l,21600r21600,l21600,xe'); + + // v:stroke + $objWriter->startElement('v:stroke'); + $objWriter->writeAttribute('joinstyle', 'miter'); + $objWriter->endElement(); + + // v:path + $objWriter->startElement('v:path'); + $objWriter->writeAttribute('gradientshapeok', 't'); + $objWriter->writeAttribute('o:connecttype', 'rect'); + $objWriter->endElement(); + + $objWriter->endElement(); + + // Loop through comments + foreach ($comments as $key => $value) { + $this->_writeVMLComment($objWriter, $key, $value); + } + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * Write VML comment to XML format + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param string $pCellReference Cell reference + * @param PHPExcel_Comment $pComment Comment + * @throws PHPExcel_Writer_Exception + */ + public function _writeVMLComment(PHPExcel_Shared_XMLWriter $objWriter = null, $pCellReference = 'A1', PHPExcel_Comment $pComment = null) + { + // Metadata + list($column, $row) = PHPExcel_Cell::coordinateFromString($pCellReference); + $column = PHPExcel_Cell::columnIndexFromString($column); + $id = 1024 + $column + $row; + $id = substr($id, 0, 4); + + // v:shape + $objWriter->startElement('v:shape'); + $objWriter->writeAttribute('id', '_x0000_s' . $id); + $objWriter->writeAttribute('type', '#_x0000_t202'); + $objWriter->writeAttribute('style', 'position:absolute;margin-left:' . $pComment->getMarginLeft() . ';margin-top:' . $pComment->getMarginTop() . ';width:' . $pComment->getWidth() . ';height:' . $pComment->getHeight() . ';z-index:1;visibility:' . ($pComment->getVisible() ? 'visible' : 'hidden')); + $objWriter->writeAttribute('fillcolor', '#' . $pComment->getFillColor()->getRGB()); + $objWriter->writeAttribute('o:insetmode', 'auto'); + + // v:fill + $objWriter->startElement('v:fill'); + $objWriter->writeAttribute('color2', '#' . $pComment->getFillColor()->getRGB()); + $objWriter->endElement(); + + // v:shadow + $objWriter->startElement('v:shadow'); + $objWriter->writeAttribute('on', 't'); + $objWriter->writeAttribute('color', 'black'); + $objWriter->writeAttribute('obscured', 't'); + $objWriter->endElement(); + + // v:path + $objWriter->startElement('v:path'); + $objWriter->writeAttribute('o:connecttype', 'none'); + $objWriter->endElement(); + + // v:textbox + $objWriter->startElement('v:textbox'); + $objWriter->writeAttribute('style', 'mso-direction-alt:auto'); + + // div + $objWriter->startElement('div'); + $objWriter->writeAttribute('style', 'text-align:left'); + $objWriter->endElement(); + + $objWriter->endElement(); + + // x:ClientData + $objWriter->startElement('x:ClientData'); + $objWriter->writeAttribute('ObjectType', 'Note'); + + // x:MoveWithCells + $objWriter->writeElement('x:MoveWithCells', ''); + + // x:SizeWithCells + $objWriter->writeElement('x:SizeWithCells', ''); + + // x:Anchor + //$objWriter->writeElement('x:Anchor', $column . ', 15, ' . ($row - 2) . ', 10, ' . ($column + 4) . ', 15, ' . ($row + 5) . ', 18'); + + // x:AutoFill + $objWriter->writeElement('x:AutoFill', 'False'); + + // x:Row + $objWriter->writeElement('x:Row', ($row - 1)); + + // x:Column + $objWriter->writeElement('x:Column', ($column - 1)); + + $objWriter->endElement(); + + $objWriter->endElement(); + } +} diff --git a/framework/library/phpexcel/PHPExcel/Writer/Excel2007/ContentTypes.php b/framework/library/phpexcel/PHPExcel/Writer/Excel2007/ContentTypes.php new file mode 100644 index 0000000..c50c73d --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Writer/Excel2007/ContentTypes.php @@ -0,0 +1,261 @@ +getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY); + } + + // XML header + $objWriter->startDocument('1.0','UTF-8','yes'); + + // Types + $objWriter->startElement('Types'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/content-types'); + + // Theme + $this->_writeOverrideContentType( + $objWriter, '/xl/theme/theme1.xml', 'application/vnd.openxmlformats-officedocument.theme+xml' + ); + + // Styles + $this->_writeOverrideContentType( + $objWriter, '/xl/styles.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml' + ); + + // Rels + $this->_writeDefaultContentType( + $objWriter, 'rels', 'application/vnd.openxmlformats-package.relationships+xml' + ); + + // XML + $this->_writeDefaultContentType( + $objWriter, 'xml', 'application/xml' + ); + + // VML + $this->_writeDefaultContentType( + $objWriter, 'vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing' + ); + + // Workbook + $this->_writeOverrideContentType( + $objWriter, '/xl/workbook.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml' + ); + + // DocProps + $this->_writeOverrideContentType( + $objWriter, '/docProps/app.xml', 'application/vnd.openxmlformats-officedocument.extended-properties+xml' + ); + + $this->_writeOverrideContentType( + $objWriter, '/docProps/core.xml', 'application/vnd.openxmlformats-package.core-properties+xml' + ); + + $customPropertyList = $pPHPExcel->getProperties()->getCustomProperties(); + if (!empty($customPropertyList)) { + $this->_writeOverrideContentType( + $objWriter, '/docProps/custom.xml', 'application/vnd.openxmlformats-officedocument.custom-properties+xml' + ); + } + + // Worksheets + $sheetCount = $pPHPExcel->getSheetCount(); + for ($i = 0; $i < $sheetCount; ++$i) { + $this->_writeOverrideContentType( + $objWriter, '/xl/worksheets/sheet' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml' + ); + } + + // Shared strings + $this->_writeOverrideContentType( + $objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml' + ); + + // Add worksheet relationship content types + $chart = 1; + for ($i = 0; $i < $sheetCount; ++$i) { + $drawings = $pPHPExcel->getSheet($i)->getDrawingCollection(); + $drawingCount = count($drawings); + $chartCount = ($includeCharts) ? $pPHPExcel->getSheet($i)->getChartCount() : 0; + + // We need a drawing relationship for the worksheet if we have either drawings or charts + if (($drawingCount > 0) || ($chartCount > 0)) { + $this->_writeOverrideContentType( + $objWriter, '/xl/drawings/drawing' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.drawing+xml' + ); + } + + // If we have charts, then we need a chart relationship for every individual chart + if ($chartCount > 0) { + for ($c = 0; $c < $chartCount; ++$c) { + $this->_writeOverrideContentType( + $objWriter, '/xl/charts/chart' . $chart++ . '.xml', 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml' + ); + } + } + } + + // Comments + for ($i = 0; $i < $sheetCount; ++$i) { + if (count($pPHPExcel->getSheet($i)->getComments()) > 0) { + $this->_writeOverrideContentType( + $objWriter, '/xl/comments' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml' + ); + } + } + + // Add media content-types + $aMediaContentTypes = array(); + $mediaCount = $this->getParentWriter()->getDrawingHashTable()->count(); + for ($i = 0; $i < $mediaCount; ++$i) { + $extension = ''; + $mimeType = ''; + + if ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof PHPExcel_Worksheet_Drawing) { + $extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getExtension()); + $mimeType = $this->_getImageMimeType( $this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getPath() ); + } else if ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof PHPExcel_Worksheet_MemoryDrawing) { + $extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType()); + $extension = explode('/', $extension); + $extension = $extension[1]; + + $mimeType = $this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType(); + } + + if (!isset( $aMediaContentTypes[$extension]) ) { + $aMediaContentTypes[$extension] = $mimeType; + + $this->_writeDefaultContentType( + $objWriter, $extension, $mimeType + ); + } + } + + $sheetCount = $pPHPExcel->getSheetCount(); + for ($i = 0; $i < $sheetCount; ++$i) { + if (count($pPHPExcel->getSheet()->getHeaderFooter()->getImages()) > 0) { + foreach ($pPHPExcel->getSheet()->getHeaderFooter()->getImages() as $image) { + if (!isset( $aMediaContentTypes[strtolower($image->getExtension())]) ) { + $aMediaContentTypes[strtolower($image->getExtension())] = $this->_getImageMimeType( $image->getPath() ); + + $this->_writeDefaultContentType( + $objWriter, strtolower($image->getExtension()), $aMediaContentTypes[strtolower($image->getExtension())] + ); + } + } + } + } + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + /** + * Get image mime type + * + * @param string $pFile Filename + * @return string Mime Type + * @throws PHPExcel_Writer_Exception + */ + private function _getImageMimeType($pFile = '') + { + if (PHPExcel_Shared_File::file_exists($pFile)) { + $image = getimagesize($pFile); + return image_type_to_mime_type($image[2]); + } else { + throw new PHPExcel_Writer_Exception("File $pFile does not exist"); + } + } + + /** + * Write Default content type + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param string $pPartname Part name + * @param string $pContentType Content type + * @throws PHPExcel_Writer_Exception + */ + private function _writeDefaultContentType(PHPExcel_Shared_XMLWriter $objWriter = null, $pPartname = '', $pContentType = '') + { + if ($pPartname != '' && $pContentType != '') { + // Write content type + $objWriter->startElement('Default'); + $objWriter->writeAttribute('Extension', $pPartname); + $objWriter->writeAttribute('ContentType', $pContentType); + $objWriter->endElement(); + } else { + throw new PHPExcel_Writer_Exception("Invalid parameters passed."); + } + } + + /** + * Write Override content type + * + * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer + * @param string $pPartname Part name + * @param string $pContentType Content type + * @throws PHPExcel_Writer_Exception + */ + private function _writeOverrideContentType(PHPExcel_Shared_XMLWriter $objWriter = null, $pPartname = '', $pContentType = '') + { + if ($pPartname != '' && $pContentType != '') { + // Write content type + $objWriter->startElement('Override'); + $objWriter->writeAttribute('PartName', $pPartname); + $objWriter->writeAttribute('ContentType', $pContentType); + $objWriter->endElement(); + } else { + throw new PHPExcel_Writer_Exception("Invalid parameters passed."); + } + } +} diff --git a/framework/library/phpexcel/PHPExcel/Writer/OpenDocument/Cell/Comment.php b/framework/library/phpexcel/PHPExcel/Writer/OpenDocument/Cell/Comment.php new file mode 100644 index 0000000..f1e98a1 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Writer/OpenDocument/Cell/Comment.php @@ -0,0 +1,63 @@ + + */ +class PHPExcel_Writer_OpenDocument_Cell_Comment +{ + public static function write(PHPExcel_Shared_XMLWriter $objWriter, PHPExcel_Cell $cell) + { + $comments = $cell->getWorksheet()->getComments(); + if (!isset($comments[$cell->getCoordinate()])) { + return; + } + $comment = $comments[$cell->getCoordinate()]; + + $objWriter->startElement('office:annotation'); + //$objWriter->writeAttribute('draw:style-name', 'gr1'); + //$objWriter->writeAttribute('draw:text-style-name', 'P1'); + $objWriter->writeAttribute('svg:width', $comment->getWidth()); + $objWriter->writeAttribute('svg:height', $comment->getHeight()); + $objWriter->writeAttribute('svg:x', $comment->getMarginLeft()); + $objWriter->writeAttribute('svg:y', $comment->getMarginTop()); + //$objWriter->writeAttribute('draw:caption-point-x', $comment->getMarginLeft()); + //$objWriter->writeAttribute('draw:caption-point-y', $comment->getMarginTop()); + $objWriter->writeElement('dc:creator', $comment->getAuthor()); + // TODO: Not realized in PHPExcel_Comment yet. + //$objWriter->writeElement('dc:date', $comment->getDate()); + $objWriter->writeElement('text:p', $comment->getText()->getPlainText()); + //$objWriter->writeAttribute('draw:text-style-name', 'P1'); + $objWriter->endElement(); + } +} diff --git a/framework/library/phpexcel/PHPExcel/Writer/OpenDocument/Content.php b/framework/library/phpexcel/PHPExcel/Writer/OpenDocument/Content.php new file mode 100644 index 0000000..a34b167 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Writer/OpenDocument/Content.php @@ -0,0 +1,272 @@ + + */ +class PHPExcel_Writer_OpenDocument_Content extends PHPExcel_Writer_OpenDocument_WriterPart +{ + const NUMBER_COLS_REPEATED_MAX = 1024; + const NUMBER_ROWS_REPEATED_MAX = 1048576; + + /** + * Write content.xml to XML format + * + * @param PHPExcel $pPHPExcel + * @return string XML Output + * @throws PHPExcel_Writer_Exception + */ + public function write(PHPExcel $pPHPExcel = null) + { + if (!$pPHPExcel) { + $pPHPExcel = $this->getParentWriter()->getPHPExcel(); /* @var $pPHPExcel PHPExcel */ + } + + $objWriter = null; + if ($this->getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY); + } + + // XML header + $objWriter->startDocument('1.0', 'UTF-8'); + + // Content + $objWriter->startElement('office:document-content'); + $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'); + $objWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'); + $objWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'); + $objWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'); + $objWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'); + $objWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'); + $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); + $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); + $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'); + $objWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'); + $objWriter->writeAttribute('xmlns:presentation', 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0'); + $objWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'); + $objWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'); + $objWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'); + $objWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML'); + $objWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'); + $objWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'); + $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office'); + $objWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer'); + $objWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc'); + $objWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events'); + $objWriter->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms'); + $objWriter->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'); + $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + $objWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report'); + $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2'); + $objWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml'); + $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#'); + $objWriter->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table'); + $objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0'); + $objWriter->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0'); + $objWriter->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/'); + $objWriter->writeAttribute('office:version', '1.2'); + + $objWriter->writeElement('office:scripts'); + $objWriter->writeElement('office:font-face-decls'); + $objWriter->writeElement('office:automatic-styles'); + + $objWriter->startElement('office:body'); + $objWriter->startElement('office:spreadsheet'); + $objWriter->writeElement('table:calculation-settings'); + $this->writeSheets($objWriter); + $objWriter->writeElement('table:named-expressions'); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + + return $objWriter->getData(); + } + + /** + * Write sheets + * + * @param PHPExcel_Shared_XMLWriter $objWriter + */ + private function writeSheets(PHPExcel_Shared_XMLWriter $objWriter) + { + $pPHPExcel = $this->getParentWriter()->getPHPExcel(); /* @var $pPHPExcel PHPExcel */ + + $sheet_count = $pPHPExcel->getSheetCount(); + for ($i = 0; $i < $sheet_count; $i++) { + //$this->getWriterPart('Worksheet')->writeWorksheet()); + $objWriter->startElement('table:table'); + $objWriter->writeAttribute('table:name', $pPHPExcel->getSheet($i)->getTitle()); + $objWriter->writeElement('office:forms'); + $objWriter->startElement('table:table-column'); + $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX); + $objWriter->endElement(); + $this->writeRows($objWriter, $pPHPExcel->getSheet($i)); + $objWriter->endElement(); + } + } + + /** + * Write rows of the specified sheet + * + * @param PHPExcel_Shared_XMLWriter $objWriter + * @param PHPExcel_Worksheet $sheet + */ + private function writeRows(PHPExcel_Shared_XMLWriter $objWriter, PHPExcel_Worksheet $sheet) + { + $number_rows_repeated = self::NUMBER_ROWS_REPEATED_MAX; + $span_row = 0; + $rows = $sheet->getRowIterator(); + while ($rows->valid()) { + $number_rows_repeated--; + $row = $rows->current(); + if ($row->getCellIterator()->valid()) { + if ($span_row) { + $objWriter->startElement('table:table-row'); + if ($span_row > 1) { + $objWriter->writeAttribute('table:number-rows-repeated', $span_row); + } + $objWriter->startElement('table:table-cell'); + $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX); + $objWriter->endElement(); + $objWriter->endElement(); + $span_row = 0; + } + $objWriter->startElement('table:table-row'); + $this->writeCells($objWriter, $row); + $objWriter->endElement(); + } else { + $span_row++; + } + $rows->next(); + } + } + + /** + * Write cells of the specified row + * + * @param PHPExcel_Shared_XMLWriter $objWriter + * @param PHPExcel_Worksheet_Row $row + * @throws PHPExcel_Writer_Exception + */ + private function writeCells(PHPExcel_Shared_XMLWriter $objWriter, PHPExcel_Worksheet_Row $row) + { + $number_cols_repeated = self::NUMBER_COLS_REPEATED_MAX; + $prev_column = -1; + $cells = $row->getCellIterator(); + while ($cells->valid()) { + $cell = $cells->current(); + $column = PHPExcel_Cell::columnIndexFromString($cell->getColumn()) - 1; + + $this->writeCellSpan($objWriter, $column, $prev_column); + $objWriter->startElement('table:table-cell'); + + switch ($cell->getDataType()) { + case PHPExcel_Cell_DataType::TYPE_BOOL: + $objWriter->writeAttribute('office:value-type', 'boolean'); + $objWriter->writeAttribute('office:value', $cell->getValue()); + $objWriter->writeElement('text:p', $cell->getValue()); + break; + + case PHPExcel_Cell_DataType::TYPE_ERROR: + throw new PHPExcel_Writer_Exception('Writing of error not implemented yet.'); + break; + + case PHPExcel_Cell_DataType::TYPE_FORMULA: + try { + $formula_value = $cell->getCalculatedValue(); + } catch (Exception $e) { + $formula_value = $cell->getValue(); + } + $objWriter->writeAttribute('table:formula', 'of:' . $cell->getValue()); + if (is_numeric($formula_value)) { + $objWriter->writeAttribute('office:value-type', 'float'); + } else { + $objWriter->writeAttribute('office:value-type', 'string'); + } + $objWriter->writeAttribute('office:value', $formula_value); + $objWriter->writeElement('text:p', $formula_value); + break; + + case PHPExcel_Cell_DataType::TYPE_INLINE: + throw new PHPExcel_Writer_Exception('Writing of inline not implemented yet.'); + break; + + case PHPExcel_Cell_DataType::TYPE_NUMERIC: + $objWriter->writeAttribute('office:value-type', 'float'); + $objWriter->writeAttribute('office:value', $cell->getValue()); + $objWriter->writeElement('text:p', $cell->getValue()); + break; + + case PHPExcel_Cell_DataType::TYPE_STRING: + $objWriter->writeAttribute('office:value-type', 'string'); + $objWriter->writeElement('text:p', $cell->getValue()); + break; + } + PHPExcel_Writer_OpenDocument_Cell_Comment::write($objWriter, $cell); + $objWriter->endElement(); + $prev_column = $column; + $cells->next(); + } + $number_cols_repeated = $number_cols_repeated - $prev_column - 1; + if ($number_cols_repeated > 0) { + if ($number_cols_repeated > 1) { + $objWriter->startElement('table:table-cell'); + $objWriter->writeAttribute('table:number-columns-repeated', $number_cols_repeated); + $objWriter->endElement(); + } else { + $objWriter->writeElement('table:table-cell'); + } + } + } + + /** + * Write span + * + * @param PHPExcel_Shared_XMLWriter $objWriter + * @param integer $curColumn + * @param integer $prevColumn + */ + private function writeCellSpan(PHPExcel_Shared_XMLWriter $objWriter, $curColumn, $prevColumn) + { + $diff = $curColumn - $prevColumn - 1; + if (1 === $diff) { + $objWriter->writeElement('table:table-cell'); + } elseif ($diff > 1) { + $objWriter->startElement('table:table-cell'); + $objWriter->writeAttribute('table:number-columns-repeated', $diff); + $objWriter->endElement(); + } + } +} diff --git a/framework/library/phpexcel/PHPExcel/Writer/PDF/Core.php b/framework/library/phpexcel/PHPExcel/Writer/PDF/Core.php new file mode 100644 index 0000000..f058b59 --- /dev/null +++ b/framework/library/phpexcel/PHPExcel/Writer/PDF/Core.php @@ -0,0 +1,364 @@ + 'LETTER', // (8.5 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER_SMALL + => 'LETTER', // (8.5 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_TABLOID + => array(792.00, 1224.00), // (11 in. by 17 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_LEDGER + => array(1224.00, 792.00), // (17 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_LEGAL + => 'LEGAL', // (8.5 in. by 14 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_STATEMENT + => array(396.00, 612.00), // (5.5 in. by 8.5 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_EXECUTIVE + => 'EXECUTIVE', // (7.25 in. by 10.5 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A3 + => 'A3', // (297 mm by 420 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A4 + => 'A4', // (210 mm by 297 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A4_SMALL + => 'A4', // (210 mm by 297 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A5 + => 'A5', // (148 mm by 210 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_B4 + => 'B4', // (250 mm by 353 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_B5 + => 'B5', // (176 mm by 250 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_FOLIO + => 'FOLIO', // (8.5 in. by 13 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_QUARTO + => array(609.45, 779.53), // (215 mm by 275 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_STANDARD_1 + => array(720.00, 1008.00), // (10 in. by 14 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_STANDARD_2 + => array(792.00, 1224.00), // (11 in. by 17 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_NOTE + => 'LETTER', // (8.5 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_NO9_ENVELOPE + => array(279.00, 639.00), // (3.875 in. by 8.875 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_NO10_ENVELOPE + => array(297.00, 684.00), // (4.125 in. by 9.5 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_NO11_ENVELOPE + => array(324.00, 747.00), // (4.5 in. by 10.375 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_NO12_ENVELOPE + => array(342.00, 792.00), // (4.75 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_NO14_ENVELOPE + => array(360.00, 828.00), // (5 in. by 11.5 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_C + => array(1224.00, 1584.00), // (17 in. by 22 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_D + => array(1584.00, 2448.00), // (22 in. by 34 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_E + => array(2448.00, 3168.00), // (34 in. by 44 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_DL_ENVELOPE + => array(311.81, 623.62), // (110 mm by 220 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_C5_ENVELOPE + => 'C5', // (162 mm by 229 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_C3_ENVELOPE + => 'C3', // (324 mm by 458 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_C4_ENVELOPE + => 'C4', // (229 mm by 324 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_C6_ENVELOPE + => 'C6', // (114 mm by 162 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_C65_ENVELOPE + => array(323.15, 649.13), // (114 mm by 229 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_B4_ENVELOPE + => 'B4', // (250 mm by 353 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_B5_ENVELOPE + => 'B5', // (176 mm by 250 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_B6_ENVELOPE + => array(498.90, 354.33), // (176 mm by 125 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_ITALY_ENVELOPE + => array(311.81, 651.97), // (110 mm by 230 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_MONARCH_ENVELOPE + => array(279.00, 540.00), // (3.875 in. by 7.5 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_6_3_4_ENVELOPE + => array(261.00, 468.00), // (3.625 in. by 6.5 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_US_STANDARD_FANFOLD + => array(1071.00, 792.00), // (14.875 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_GERMAN_STANDARD_FANFOLD + => array(612.00, 864.00), // (8.5 in. by 12 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_GERMAN_LEGAL_FANFOLD + => 'FOLIO', // (8.5 in. by 13 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_ISO_B4 + => 'B4', // (250 mm by 353 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_JAPANESE_DOUBLE_POSTCARD + => array(566.93, 419.53), // (200 mm by 148 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_STANDARD_PAPER_1 + => array(648.00, 792.00), // (9 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_STANDARD_PAPER_2 + => array(720.00, 792.00), // (10 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_STANDARD_PAPER_3 + => array(1080.00, 792.00), // (15 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_INVITE_ENVELOPE + => array(623.62, 623.62), // (220 mm by 220 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER_EXTRA_PAPER + => array(667.80, 864.00), // (9.275 in. by 12 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_LEGAL_EXTRA_PAPER + => array(667.80, 1080.00), // (9.275 in. by 15 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_TABLOID_EXTRA_PAPER + => array(841.68, 1296.00), // (11.69 in. by 18 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A4_EXTRA_PAPER + => array(668.98, 912.76), // (236 mm by 322 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER_TRANSVERSE_PAPER + => array(595.80, 792.00), // (8.275 in. by 11 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A4_TRANSVERSE_PAPER + => 'A4', // (210 mm by 297 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER_EXTRA_TRANSVERSE_PAPER + => array(667.80, 864.00), // (9.275 in. by 12 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_SUPERA_SUPERA_A4_PAPER + => array(643.46, 1009.13), // (227 mm by 356 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_SUPERB_SUPERB_A3_PAPER + => array(864.57, 1380.47), // (305 mm by 487 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER_PLUS_PAPER + => array(612.00, 913.68), // (8.5 in. by 12.69 in.) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A4_PLUS_PAPER + => array(595.28, 935.43), // (210 mm by 330 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A5_TRANSVERSE_PAPER + => 'A5', // (148 mm by 210 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_JIS_B5_TRANSVERSE_PAPER + => array(515.91, 728.50), // (182 mm by 257 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A3_EXTRA_PAPER + => array(912.76, 1261.42), // (322 mm by 445 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A5_EXTRA_PAPER + => array(493.23, 666.14), // (174 mm by 235 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_ISO_B5_EXTRA_PAPER + => array(569.76, 782.36), // (201 mm by 276 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A2_PAPER + => 'A2', // (420 mm by 594 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A3_TRANSVERSE_PAPER + => 'A3', // (297 mm by 420 mm) + PHPExcel_Worksheet_PageSetup::PAPERSIZE_A3_EXTRA_TRANSVERSE_PAPER + => array(912.76, 1261.42) // (322 mm by 445 mm) + ); + + /** + * Create a new PHPExcel_Writer_PDF + * + * @param PHPExcel $phpExcel PHPExcel object + */ + public function __construct(PHPExcel $phpExcel) + { + parent::__construct($phpExcel); + $this->setUseInlineCss(TRUE); + $this->_tempDir = PHPExcel_Shared_File::sys_get_temp_dir(); + } + + /** + * Get Font + * + * @return string + */ + public function getFont() + { + return $this->_font; + } + + /** + * Set font. Examples: + * 'arialunicid0-chinese-simplified' + * 'arialunicid0-chinese-traditional' + * 'arialunicid0-korean' + * 'arialunicid0-japanese' + * + * @param string $fontName + */ + public function setFont($fontName) + { + $this->_font = $fontName; + return $this; + } + + /** + * Get Paper Size + * + * @return int + */ + public function getPaperSize() + { + return $this->_paperSize; + } + + /** + * Set Paper Size + * + * @param string $pValue Paper size + * @return PHPExcel_Writer_PDF + */ + public function setPaperSize($pValue = PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER) + { + $this->_paperSize = $pValue; + return $this; + } + + /** + * Get Orientation + * + * @return string + */ + public function getOrientation() + { + return $this->_orientation; + } + + /** + * Set Orientation + * + * @param string $pValue Page orientation + * @return PHPExcel_Writer_PDF + */ + public function setOrientation($pValue = PHPExcel_Worksheet_PageSetup::ORIENTATION_DEFAULT) + { + $this->_orientation = $pValue; + return $this; + } + + /** + * Get temporary storage directory + * + * @return string + */ + public function getTempDir() + { + return $this->_tempDir; + } + + /** + * Set temporary storage directory + * + * @param string $pValue Temporary storage directory + * @throws PHPExcel_Writer_Exception when directory does not exist + * @return PHPExcel_Writer_PDF + */ + public function setTempDir($pValue = '') + { + if (is_dir($pValue)) { + $this->_tempDir = $pValue; + } else { + throw new PHPExcel_Writer_Exception("Directory does not exist: $pValue"); + } + return $this; + } + + /** + * Save PHPExcel to PDF file, pre-save + * + * @param string $pFilename Name of the file to save as + * @throws PHPExcel_Writer_Exception + */ + protected function prepareForSave($pFilename = NULL) + { + // garbage collect + $this->_phpExcel->garbageCollect(); + + $this->_saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType(); + PHPExcel_Calculation::setArrayReturnType(PHPExcel_Calculation::RETURN_ARRAY_AS_VALUE); + + // Open file + $fileHandle = fopen($pFilename, 'w'); + if ($fileHandle === FALSE) { + throw new PHPExcel_Writer_Exception("Could not open file $pFilename for writing."); + } + + // Set PDF + $this->_isPdf = TRUE; + // Build CSS + $this->buildCSS(TRUE); + + return $fileHandle; + } + + /** + * Save PHPExcel to PDF file, post-save + * + * @param resource $fileHandle + * @throws PHPExcel_Writer_Exception + */ + protected function restoreStateAfterSave($fileHandle) + { + // Close file + fclose($fileHandle); + + PHPExcel_Calculation::setArrayReturnType($this->_saveArrayReturnType); + } + +} diff --git a/web/cityagent.php b/web/cityagent.php new file mode 100644 index 0000000..7e26948 --- /dev/null +++ b/web/cityagent.php @@ -0,0 +1,23 @@ +model('attachment'); + +$_W['catalog'] = 'web'; +$_W['plugin'] = $plugin = !empty($_GPC['p']) ? $_GPC['p'] : 'dashboard'; +$_W['controller'] = $controller = !empty($_GPC['ac']) ? $_GPC['ac'] : 'dashboard'; +$_W['method'] = $method = !empty($_GPC['do']) ? $_GPC['do'] : 'index'; +Func_loader::web('cover'); +$_W['wlsetting'] = Setting::wlsetting_load(); +$_W['attachurl'] = attachment_set_attach_url(); + +wl_new_method($plugin, $controller, $method, $_W['catalog']); \ No newline at end of file diff --git a/web/citystore.php b/web/citystore.php new file mode 100644 index 0000000..3a1e742 --- /dev/null +++ b/web/citystore.php @@ -0,0 +1,23 @@ +model('attachment'); + +$_W['catalog'] = 'web'; +$_W['plugin'] = $plugin = !empty($_GPC['p']) ? $_GPC['p'] : 'dashboard'; +$_W['controller'] = $controller = !empty($_GPC['ac']) ? $_GPC['ac'] : 'dashboard'; +$_W['method'] = $method = !empty($_GPC['do']) ? $_GPC['do'] : 'index'; +Func_loader::web('storecover'); +$_W['wlsetting'] = Setting::wlsetting_load(); +$_W['attachurl'] = attachment_set_attach_url(); + +wl_new_method($plugin, $controller, $method, $_W['catalog']); \ No newline at end of file diff --git a/web/citysys.php b/web/citysys.php new file mode 100644 index 0000000..e74094e --- /dev/null +++ b/web/citysys.php @@ -0,0 +1,24 @@ +model('attachment'); + +$_W['token'] = token(); +$_W['catalog'] = 'sys'; +$_W['plugin'] = $plugin = !empty($_GPC['p']) ? $_GPC['p'] : 'dashboard'; +$_W['controller'] = $controller = !empty($_GPC['ac']) ? $_GPC['ac'] : 'dashboard'; +$_W['method'] = $method = !empty($_GPC['do']) ? $_GPC['do'] : 'index'; +Func_loader::web('syscover'); +$_W['wlsetting'] = Setting::wlsetting_load(); +$_W['attachurl'] = attachment_set_attach_url(); + +wl_new_method($plugin, $controller, $method, $_W['catalog']); \ No newline at end of file diff --git a/web/common/common.func.php b/web/common/common.func.php new file mode 100644 index 0000000..c9d74b0 --- /dev/null +++ b/web/common/common.func.php @@ -0,0 +1,323 @@ +model('module'); +load()->model('miniapp'); + +function current_operate_is_controller() +{ + global $_W, $_GPC; + $result = 0; + if (!$_W['isfounder']) { + return $result; + } + $result = igetcookie('__iscontroller'); + if (isset($_GPC['iscontroller'])) { + if (1 == $_GPC['iscontroller']) { + $result = 1; + isetcookie('__iscontroller', $result); + return $result; + } + if (0 == $_GPC['iscontroller']) { + $result = 0; + } + } + + if (in_array(FRAME, array('welcome', 'module_manage', 'user_manage', 'permission', 'system', 'site'))) { + $result = 1; + } + if (in_array(FRAME, array('account', 'wxapp')) && (($_GPC['m'] || $_GPC['module_name']) != 'store')) { + $result = 0; + } + isetcookie('__iscontroller', $result); + return $result; +} + +function system_modules() +{ + return module_system(); +} + + +function url($segment, $params = array(), $contain_domain = false) +{ + return wurl($segment, $params, $contain_domain); +} + + +function message($msg, $redirect = '', $type = '', $tips = false, $extend = array()) +{ + global $_W, $_GPC; + + if ('refresh' == $redirect) { + $redirect = $_W['script_name'] . '?' . $_SERVER['QUERY_STRING']; + } + if ('referer' == $redirect) { + $redirect = referer(); + } + $redirect = safe_gpc_url($redirect); + + if ('' == $redirect) { + $type = in_array($type, array('success', 'error', 'info', 'warning', 'ajax', 'sql', 'expired')) ? $type : 'info'; + } else { + $type = in_array($type, array('success', 'error', 'info', 'warning', 'ajax', 'sql', 'expired')) ? $type : 'success'; + } + if ($_W['isajax'] || !empty($_GET['isajax']) || 'ajax' == $type) { + if ('ajax' != $type && !empty($_GPC['target'])) { + exit(' +"); + } else { + $vars = array(); + $vars['message'] = $msg; + $vars['redirect'] = $redirect; + $vars['type'] = $type; + exit(json_encode($vars)); + } + } + if (empty($msg) && !empty($redirect)) { + header('Location: ' . $redirect); + exit; + } + $label = $type; + if ('error' == $type || 'expired' == $type) { + $label = 'danger'; + } + if ('ajax' == $type || 'sql' == $type) { + $label = 'warning'; + } + + if ($tips) { + if (is_array($msg)) { + $message_cookie['title'] = 'MYSQL 错误'; + $message_cookie['msg'] = 'php echo cutstr(' . $msg['sql'] . ', 300, 1);'; + } else { + $message_cookie['title'] = $caption; + $message_cookie['msg'] = $msg; + } + $message_cookie['type'] = $label; + $message_cookie['redirect'] = $redirect ? $redirect : referer(); + $message_cookie['msg'] = rawurlencode($message_cookie['msg']); + $extend_button = array(); + if (!empty($extend) && is_array($extend)) { + foreach ($extend as $button) { + if (!empty($button['title']) && !empty($button['url'])) { + $button['url'] = safe_gpc_url($button['url'], false); + $button['title'] = rawurlencode($button['title']); + $extend_button[] = $button; + } + } + } + $message_cookie['extend'] = !empty($extend_button) ? $extend_button : ''; + + isetcookie('message', stripslashes(json_encode($message_cookie, JSON_UNESCAPED_UNICODE))); + header('Location: ' . $message_cookie['redirect']); + } else { + include template('common/message', TEMPLATE_INCLUDEPATH); + } + exit; +} + +function iajax($code = 0, $message = '', $redirect = '') +{ + message(error($code, $message), $redirect, 'ajax', false); +} + +function itoast($message, $redirect = '', $type = '', $extend = array()) +{ + message($message, $redirect, $type, true, $extend); +} + + +function checklogin($url = '') +{ + global $_W; + if (empty($_W['uid'])) { + $url = safe_gpc_url($url); + if (!empty($_W['setting']['copyright']['showhomepage'])) { + itoast('', url('account/welcome'), 'warning'); + } else { + itoast('', url('user/login', $url ? array('referer' => urlencode($url)) : ''), 'warning'); + } + } + $cookie = json_decode(authcode(igetcookie('__session'), 'DECODE'), true); + if (empty($cookie['rember'])) { + $session = authcode(json_encode($cookie), 'encode'); + $autosignout = (int)$_W['setting']['copyright']['autosignout'] > 0 ? (int)$_W['setting']['copyright']['autosignout'] * 60 : 0; + isetcookie('__session', $session, $autosignout, true); + } + + return true; +} + +function get_position_by_ip($ip = '') +{ + global $_W; + $ip = $ip ? $ip : $_W['clientip']; + $url = 'http://ip.taobao.com/outGetIpInfo?ip=' . $ip . '&accessKey=alibaba-inc'; + $ip_content = file_get_contents($url); + $ip_content = json_decode($ip_content, true); + if (empty($ip_content) || $ip_content['code'] != 0) { + $res = @file_get_contents('https://whois.pconline.com.cn/ipJson.jsp'); + $res = strtoutf8($res); + $json_matches = array(); + preg_match('/{IPCallBack\((.+?)\);\}/', $res, $json_matches); + if (empty($json_matches[1])) { + return error(-1, '获取地址失败,请重新配置Ip查询接口'); + } + $ip_content = array( + 'code' => 0, + 'data' => json_decode($json_matches[1], true) + ); + } + return $ip_content; +} + +function buildframes($framename = '') +{ + global $_W, $_GPC; + $frames = system_menu_permission_list(); + $frames = frames_top_menu($frames); + + return !empty($framename) ? ('system_welcome' == $framename ? $frames['account'] : $frames[$framename]) : $frames; +} + +function frames_top_menu($frames) +{ + global $_W, $top_nav; + if (empty($frames)) { + return array(); + } + + foreach ($frames as $menuid => $menu) { +// if ((!empty($menu['founder']) || in_array($menuid, array('module_manage', 'site', 'advertisement', 'appmarket'))) && !$_W['isadmin'] || +// ACCOUNT_MANAGE_NAME_CLERK == $_W['highest_role'] && in_array($menuid, array('account', 'wxapp', 'system', 'platform', 'welcome', 'account_manage')) && !$_W['isadmin'] && in_array($menuid, array('user_manage', 'permission')) || +// 'myself' == $menuid && $_W['isadmin'] || +// !$menu['is_display']) { +// continue; +// } + + $top_nav[] = array( + 'title' => $menu['title'], + 'name' => $menuid, + 'url' => $menu['url'], + 'blank' => $menu['blank'], + 'icon' => $menu['icon'], + 'is_display' => $menu['is_display'], + 'is_system' => $menu['is_system'], + ); + } + return $frames; +} + + +function filter_url($params) +{ + global $_W; + if (empty($params)) { + return ''; + } + $query_arr = array(); + $parse = parse_url($_W['siteurl']); + if (!empty($parse['query'])) { + $query = $parse['query']; + parse_str($query, $query_arr); + } + $params = explode(',', $params); + foreach ($params as $val) { + if (!empty($val)) { + $data = explode(':', $val); + $query_arr[$data[0]] = trim($data[1]); + } + } + $query_arr['page'] = 1; + $query = http_build_query($query_arr); + + return './index.php?' . $query; +} + +function url_params($url) +{ + $result = array(); + if (empty($url)) { + return $result; + } + $components = parse_url($url); + $params = explode('&', $components['query']); + foreach ($params as $param) { + if (!empty($param)) { + $param_array = explode('=', $param); + $result[$param_array[0]] = $param_array[1]; + } + } + + return $result; +} + +function frames_menu_append() +{ + $system_menu_default_permission = array( + 'founder' => array(), + 'vice_founder' => array( + 'system_setting_updatecache', + ), + 'owner' => array( + 'system_setting_updatecache', + ), + 'manager' => array( + 'system_setting_updatecache', + ), + 'operator' => array( + 'system_setting_updatecache', + ), + 'clerk' => array(), + 'expired' => array( + 'system_setting_updatecache', + ), + ); + + return $system_menu_default_permission; +} + + +function site_profile_perfect_tips() +{ + global $_W; + + if ($_W['isfounder'] && (empty($_W['setting']['site']) || empty($_W['setting']['site']['profile_perfect']))) { + if (!defined('SITE_PROFILE_PERFECT_TIPS')) { + $url = url('cloud/profile'); + + return <<'+ + ''+ + ''; + $('body').prepend(html); +}); +EOF; + define('SITE_PROFILE_PERFECT_TIPS', true); + } + } + + return ''; +} + +function strtoutf8($str) +{ + $current_encode = mb_detect_encoding($str, array('ASCII', 'GB2312', 'GBK', 'BIG5', 'UTF-8')); + return mb_convert_encoding($str, 'UTF-8', $current_encode); +} \ No newline at end of file diff --git a/web/resource/components/area/cascade.js b/web/resource/components/area/cascade.js new file mode 100644 index 0000000..8a80abb --- /dev/null +++ b/web/resource/components/area/cascade.js @@ -0,0 +1,166 @@ +/** + * @name jQuery Cascdejs plugin + * @author zdy + * @version 1.0 + */ + +//首先需要初始化 +var xmlDoc; +var TopnodeList; +var citys; +var countyNodes; +var nodeindex = 0; +var childnodeindex = 0; +//获取xml文件 +function cascdeInit(v1,v2,v3) { + //打开xlmdocm文档 + xmlDoc = loadXmlFile('./resource/components/area/Area.xml'); + var dropElement1 = document.getElementById("sel-provance"); + var dropElement2 = document.getElementById("sel-city"); + var dropElement3 = document.getElementById("sel-area"); + RemoveDropDownList(dropElement1); + RemoveDropDownList(dropElement2); + RemoveDropDownList(dropElement3); + if (window.ActiveXObject) { + TopnodeList = xmlDoc.selectSingleNode("address").childNodes; + } + else { + TopnodeList = xmlDoc.childNodes[0].getElementsByTagName("province"); + } + if (TopnodeList.length > 0) { + //省份列表 + var county; + var province; + var city; + for (var i = 0; i < TopnodeList.length; i++) { + //添加列表项目 + county = TopnodeList[i]; + var option = document.createElement("option"); + option.value = county.getAttribute("name"); + option.text = county.getAttribute("name"); + if (v1 == option.value) { + option.selected = true; + nodeindex = i; + } + dropElement1.add(option); + } + if (TopnodeList.length > 0) { + //城市列表 + citys = TopnodeList[nodeindex].getElementsByTagName("city") + for (var i = 0; i < citys.length; i++) { + var id = dropElement1.options[nodeindex].value; + //默认为第一个省份的城市 + province = TopnodeList[nodeindex].getElementsByTagName("city"); + var option = document.createElement("option"); + option.value = province[i] .getAttribute("name"); + option.text = province[i].getAttribute("name"); + if (v2 == option.value) { + option.selected = true; + childnodeindex = i; + } + dropElement2.add(option); + } + selectcounty(v3); + } + } +} + +/* +//依据省设置城市,县 +*/ +function selectCity() { + var dropElement1 = document.getElementById("sel-provance"); + var name = dropElement1.options[dropElement1.selectedIndex].value; + countyNodes = TopnodeList[dropElement1.selectedIndex]; + var province = document.getElementById("sel-city"); + var city = document.getElementById("sel-area"); + RemoveDropDownList(province); + RemoveDropDownList(city); + var citynodes; + var countycodes; + if (window.ActiveXObject) { + citynodes = xmlDoc.selectSingleNode('//address/province [@name="' + name + '"]').childNodes; + } else { + citynodes = countyNodes.getElementsByTagName("city") + } + if (window.ActiveXObject) { + countycodes = citynodes[0].childNodes; + } else { + countycodes = citynodes[0].getElementsByTagName("county") + } + + if (citynodes.length > 0) { + //城市 + for (var i = 0; i < citynodes.length; i++) { + var provinceNode = citynodes[i]; + var option = document.createElement("option"); + option.value = provinceNode.getAttribute("name"); + option.text = provinceNode.getAttribute("name"); + province.add(option); + } + if (countycodes.length > 0) { + //填充选择省份的第一个城市的县列表 + for (var i = 0; i < countycodes.length; i++) { + var dropElement2 = document.getElementById("sel-city"); + var dropElement3 = document.getElementById("sel-area"); + //取当天省份下第一个城市列表 + + //alert(cityNode.childNodes.length); + var option = document.createElement("option"); + option.value = countycodes[i].getAttribute("name"); + option.text = countycodes[i].getAttribute("name"); + dropElement3.add(option); + } + } + } +} +/* +//设置县,区 +*/ +function selectcounty(v3) { + var dropElement1 = document.getElementById("sel-provance"); + var dropElement2 = document.getElementById("sel-city"); + var name = dropElement2.options[dropElement2.selectedIndex].value; + var city = document.getElementById("sel-area"); + var countys = TopnodeList[dropElement1.selectedIndex].getElementsByTagName("city")[dropElement2.selectedIndex].getElementsByTagName("county") + RemoveDropDownList(city); + for (var i = 0; i < countys.length; i++) { + var countyNode = countys[i]; + var option = document.createElement("option"); + option.value = countyNode.getAttribute("name"); + option.text = countyNode.getAttribute("name"); + if(v3==option.value){ + option.selected=true; + } + city.add(option); + } +} +function RemoveDropDownList(obj) { + if (obj) { + var len = obj.options.length; + if (len > 0) { + for (var i = len; i >= 0; i--) { + obj.remove(i); + } + } + } +} +/* +//读取xml文件 +*/ +function loadXmlFile(xmlFile) { + var xmlDom = null; + if (window.ActiveXObject) { + xmlDom = new ActiveXObject("Microsoft.XMLDOM"); + xmlDom.async = false; + xmlDom.load(xmlFile) || xmlDom.loadXML(xmlFile);//如果用的是XML字符串//如果用的是xml文件 + } else if (document.implementation && document.implementation.createDocument) { + var xmlhttp = new window.XMLHttpRequest(); + xmlhttp.open("GET", xmlFile, false); + xmlhttp.send(null); + xmlDom = xmlhttp.responseXML; + } else { + xmlDom = null; + } + return xmlDom; +} \ No newline at end of file diff --git a/web/resource/components/chart/Chart.js b/web/resource/components/chart/Chart.js new file mode 100644 index 0000000..4b392d8 --- /dev/null +++ b/web/resource/components/chart/Chart.js @@ -0,0 +1,10635 @@ +/*! + * Chart.js + * http://chartjs.org/ + * Version: 2.3.0 + * + * Copyright 2016 Nick Downie + * Released under the MIT license + * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o lum2) { + return (lum1 + 0.05) / (lum2 + 0.05); + } + return (lum2 + 0.05) / (lum1 + 0.05); + }, + + level: function (color2) { + var contrastRatio = this.contrast(color2); + if (contrastRatio >= 7.1) { + return 'AAA'; + } + + return (contrastRatio >= 4.5) ? 'AA' : ''; + }, + + dark: function () { + // YIQ equation from http://24ways.org/2010/calculating-color-contrast + var rgb = this.values.rgb; + var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; + return yiq < 128; + }, + + light: function () { + return !this.dark(); + }, + + negate: function () { + var rgb = []; + for (var i = 0; i < 3; i++) { + rgb[i] = 255 - this.values.rgb[i]; + } + this.setValues('rgb', rgb); + return this; + }, + + lighten: function (ratio) { + var hsl = this.values.hsl; + hsl[2] += hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + darken: function (ratio) { + var hsl = this.values.hsl; + hsl[2] -= hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + saturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] += hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + desaturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] -= hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + whiten: function (ratio) { + var hwb = this.values.hwb; + hwb[1] += hwb[1] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + blacken: function (ratio) { + var hwb = this.values.hwb; + hwb[2] += hwb[2] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + greyscale: function () { + var rgb = this.values.rgb; + // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale + var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; + this.setValues('rgb', [val, val, val]); + return this; + }, + + clearer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha - (alpha * ratio)); + return this; + }, + + opaquer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha + (alpha * ratio)); + return this; + }, + + rotate: function (degrees) { + var hsl = this.values.hsl; + var hue = (hsl[0] + degrees) % 360; + hsl[0] = hue < 0 ? 360 + hue : hue; + this.setValues('hsl', hsl); + return this; + }, + + /** + * Ported from sass implementation in C + * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 + */ + mix: function (mixinColor, weight) { + var color1 = this; + var color2 = mixinColor; + var p = weight === undefined ? 0.5 : weight; + + var w = 2 * p - 1; + var a = color1.alpha() - color2.alpha(); + + var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + return this + .rgb( + w1 * color1.red() + w2 * color2.red(), + w1 * color1.green() + w2 * color2.green(), + w1 * color1.blue() + w2 * color2.blue() + ) + .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); + }, + + toJSON: function () { + return this.rgb(); + }, + + clone: function () { + // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, + // making the final build way to big to embed in Chart.js. So let's do it manually, + // assuming that values to clone are 1 dimension arrays containing only numbers, + // except 'alpha' which is a number. + var result = new Color(); + var source = this.values; + var target = result.values; + var value, type; + + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + value = source[prop]; + type = ({}).toString.call(value); + if (type === '[object Array]') { + target[prop] = value.slice(0); + } else if (type === '[object Number]') { + target[prop] = value; + } else { + console.error('unexpected color value:', value); + } + } + } + + return result; + } +}; + +Color.prototype.spaces = { + rgb: ['red', 'green', 'blue'], + hsl: ['hue', 'saturation', 'lightness'], + hsv: ['hue', 'saturation', 'value'], + hwb: ['hue', 'whiteness', 'blackness'], + cmyk: ['cyan', 'magenta', 'yellow', 'black'] +}; + +Color.prototype.maxes = { + rgb: [255, 255, 255], + hsl: [360, 100, 100], + hsv: [360, 100, 100], + hwb: [360, 100, 100], + cmyk: [100, 100, 100, 100] +}; + +Color.prototype.getValues = function (space) { + var values = this.values; + var vals = {}; + + for (var i = 0; i < space.length; i++) { + vals[space.charAt(i)] = values[space][i]; + } + + if (values.alpha !== 1) { + vals.a = values.alpha; + } + + // {r: 255, g: 255, b: 255, a: 0.4} + return vals; +}; + +Color.prototype.setValues = function (space, vals) { + var values = this.values; + var spaces = this.spaces; + var maxes = this.maxes; + var alpha = 1; + var i; + + if (space === 'alpha') { + alpha = vals; + } else if (vals.length) { + // [10, 10, 10] + values[space] = vals.slice(0, space.length); + alpha = vals[space.length]; + } else if (vals[space.charAt(0)] !== undefined) { + // {r: 10, g: 10, b: 10} + for (i = 0; i < space.length; i++) { + values[space][i] = vals[space.charAt(i)]; + } + + alpha = vals.a; + } else if (vals[spaces[space][0]] !== undefined) { + // {red: 10, green: 10, blue: 10} + var chans = spaces[space]; + + for (i = 0; i < space.length; i++) { + values[space][i] = vals[chans[i]]; + } + + alpha = vals.alpha; + } + + values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); + + if (space === 'alpha') { + return false; + } + + var capped; + + // cap values of the space prior converting all values + for (i = 0; i < space.length; i++) { + capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); + values[space][i] = Math.round(capped); + } + + // convert to all the other color spaces + for (var sname in spaces) { + if (sname !== space) { + values[sname] = convert[space][sname](values[space]); + } + } + + return true; +}; + +Color.prototype.setSpace = function (space, args) { + var vals = args[0]; + + if (vals === undefined) { + // color.rgb() + return this.getValues(space); + } + + // color.rgb(10, 10, 10) + if (typeof vals === 'number') { + vals = Array.prototype.slice.call(args); + } + + this.setValues(space, vals); + return this; +}; + +Color.prototype.setChannel = function (space, index, val) { + var svalues = this.values[space]; + if (val === undefined) { + // color.red() + return svalues[index]; + } else if (val === svalues[index]) { + // color.red(color.red()) + return this; + } + + // color.red(100) + svalues[index] = val; + this.setValues(space, svalues); + + return this; +}; + +if (typeof window !== 'undefined') { + window.Color = Color; +} + +module.exports = Color; + +},{"2":2,"5":5}],4:[function(require,module,exports){ +/* MIT license */ + +module.exports = { + rgb2hsl: rgb2hsl, + rgb2hsv: rgb2hsv, + rgb2hwb: rgb2hwb, + rgb2cmyk: rgb2cmyk, + rgb2keyword: rgb2keyword, + rgb2xyz: rgb2xyz, + rgb2lab: rgb2lab, + rgb2lch: rgb2lch, + + hsl2rgb: hsl2rgb, + hsl2hsv: hsl2hsv, + hsl2hwb: hsl2hwb, + hsl2cmyk: hsl2cmyk, + hsl2keyword: hsl2keyword, + + hsv2rgb: hsv2rgb, + hsv2hsl: hsv2hsl, + hsv2hwb: hsv2hwb, + hsv2cmyk: hsv2cmyk, + hsv2keyword: hsv2keyword, + + hwb2rgb: hwb2rgb, + hwb2hsl: hwb2hsl, + hwb2hsv: hwb2hsv, + hwb2cmyk: hwb2cmyk, + hwb2keyword: hwb2keyword, + + cmyk2rgb: cmyk2rgb, + cmyk2hsl: cmyk2hsl, + cmyk2hsv: cmyk2hsv, + cmyk2hwb: cmyk2hwb, + cmyk2keyword: cmyk2keyword, + + keyword2rgb: keyword2rgb, + keyword2hsl: keyword2hsl, + keyword2hsv: keyword2hsv, + keyword2hwb: keyword2hwb, + keyword2cmyk: keyword2cmyk, + keyword2lab: keyword2lab, + keyword2xyz: keyword2xyz, + + xyz2rgb: xyz2rgb, + xyz2lab: xyz2lab, + xyz2lch: xyz2lch, + + lab2xyz: lab2xyz, + lab2rgb: lab2rgb, + lab2lch: lab2lch, + + lch2lab: lch2lab, + lch2xyz: lch2xyz, + lch2rgb: lch2rgb +} + + +function rgb2hsl(rgb) { + var r = rgb[0]/255, + g = rgb[1]/255, + b = rgb[2]/255, + min = Math.min(r, g, b), + max = Math.max(r, g, b), + delta = max - min, + h, s, l; + + if (max == min) + h = 0; + else if (r == max) + h = (g - b) / delta; + else if (g == max) + h = 2 + (b - r) / delta; + else if (b == max) + h = 4 + (r - g)/ delta; + + h = Math.min(h * 60, 360); + + if (h < 0) + h += 360; + + l = (min + max) / 2; + + if (max == min) + s = 0; + else if (l <= 0.5) + s = delta / (max + min); + else + s = delta / (2 - max - min); + + return [h, s * 100, l * 100]; +} + +function rgb2hsv(rgb) { + var r = rgb[0], + g = rgb[1], + b = rgb[2], + min = Math.min(r, g, b), + max = Math.max(r, g, b), + delta = max - min, + h, s, v; + + if (max == 0) + s = 0; + else + s = (delta/max * 1000)/10; + + if (max == min) + h = 0; + else if (r == max) + h = (g - b) / delta; + else if (g == max) + h = 2 + (b - r) / delta; + else if (b == max) + h = 4 + (r - g) / delta; + + h = Math.min(h * 60, 360); + + if (h < 0) + h += 360; + + v = ((max / 255) * 1000) / 10; + + return [h, s, v]; +} + +function rgb2hwb(rgb) { + var r = rgb[0], + g = rgb[1], + b = rgb[2], + h = rgb2hsl(rgb)[0], + w = 1/255 * Math.min(r, Math.min(g, b)), + b = 1 - 1/255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +} + +function rgb2cmyk(rgb) { + var r = rgb[0] / 255, + g = rgb[1] / 255, + b = rgb[2] / 255, + c, m, y, k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; + return [c * 100, m * 100, y * 100, k * 100]; +} + +function rgb2keyword(rgb) { + return reverseKeywords[JSON.stringify(rgb)]; +} + +function rgb2xyz(rgb) { + var r = rgb[0] / 255, + g = rgb[1] / 255, + b = rgb[2] / 255; + + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y *100, z * 100]; +} + +function rgb2lab(rgb) { + var xyz = rgb2xyz(rgb), + x = xyz[0], + y = xyz[1], + z = xyz[2], + l, a, b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +} + +function rgb2lch(args) { + return lab2lch(rgb2lab(args)); +} + +function hsl2rgb(hsl) { + var h = hsl[0] / 360, + s = hsl[1] / 100, + l = hsl[2] / 100, + t1, t2, t3, rgb, val; + + if (s == 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) + t2 = l * (1 + s); + else + t2 = l + s - l * s; + t1 = 2 * l - t2; + + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { + t3 = h + 1 / 3 * - (i - 1); + t3 < 0 && t3++; + t3 > 1 && t3--; + + if (6 * t3 < 1) + val = t1 + (t2 - t1) * 6 * t3; + else if (2 * t3 < 1) + val = t2; + else if (3 * t3 < 2) + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + else + val = t1; + + rgb[i] = val * 255; + } + + return rgb; +} + +function hsl2hsv(hsl) { + var h = hsl[0], + s = hsl[1] / 100, + l = hsl[2] / 100, + sv, v; + + if(l === 0) { + // no need to do calc on black + // also avoids divide by 0 error + return [0, 0, 0]; + } + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + v = (l + s) / 2; + sv = (2 * s) / (l + s); + return [h, sv * 100, v * 100]; +} + +function hsl2hwb(args) { + return rgb2hwb(hsl2rgb(args)); +} + +function hsl2cmyk(args) { + return rgb2cmyk(hsl2rgb(args)); +} + +function hsl2keyword(args) { + return rgb2keyword(hsl2rgb(args)); +} + + +function hsv2rgb(hsv) { + var h = hsv[0] / 60, + s = hsv[1] / 100, + v = hsv[2] / 100, + hi = Math.floor(h) % 6; + + var f = h - Math.floor(h), + p = 255 * v * (1 - s), + q = 255 * v * (1 - (s * f)), + t = 255 * v * (1 - (s * (1 - f))), + v = 255 * v; + + switch(hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +} + +function hsv2hsl(hsv) { + var h = hsv[0], + s = hsv[1] / 100, + v = hsv[2] / 100, + sl, l; + + l = (2 - s) * v; + sl = s * v; + sl /= (l <= 1) ? l : 2 - l; + sl = sl || 0; + l /= 2; + return [h, sl * 100, l * 100]; +} + +function hsv2hwb(args) { + return rgb2hwb(hsv2rgb(args)) +} + +function hsv2cmyk(args) { + return rgb2cmyk(hsv2rgb(args)); +} + +function hsv2keyword(args) { + return rgb2keyword(hsv2rgb(args)); +} + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +function hwb2rgb(hwb) { + var h = hwb[0] / 360, + wh = hwb[1] / 100, + bl = hwb[2] / 100, + ratio = wh + bl, + i, v, f, n; + + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; + if ((i & 0x01) != 0) { + f = 1 - f; + } + n = wh + f * (v - wh); // linear interpolation + + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + + return [r * 255, g * 255, b * 255]; +} + +function hwb2hsl(args) { + return rgb2hsl(hwb2rgb(args)); +} + +function hwb2hsv(args) { + return rgb2hsv(hwb2rgb(args)); +} + +function hwb2cmyk(args) { + return rgb2cmyk(hwb2rgb(args)); +} + +function hwb2keyword(args) { + return rgb2keyword(hwb2rgb(args)); +} + +function cmyk2rgb(cmyk) { + var c = cmyk[0] / 100, + m = cmyk[1] / 100, + y = cmyk[2] / 100, + k = cmyk[3] / 100, + r, g, b; + + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); + return [r * 255, g * 255, b * 255]; +} + +function cmyk2hsl(args) { + return rgb2hsl(cmyk2rgb(args)); +} + +function cmyk2hsv(args) { + return rgb2hsv(cmyk2rgb(args)); +} + +function cmyk2hwb(args) { + return rgb2hwb(cmyk2rgb(args)); +} + +function cmyk2keyword(args) { + return rgb2keyword(cmyk2rgb(args)); +} + + +function xyz2rgb(xyz) { + var x = xyz[0] / 100, + y = xyz[1] / 100, + z = xyz[2] / 100, + r, g, b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // assume sRGB + r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r = (r * 12.92); + + g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g = (g * 12.92); + + b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b = (b * 12.92); + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +} + +function xyz2lab(xyz) { + var x = xyz[0], + y = xyz[1], + z = xyz[2], + l, a, b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +} + +function xyz2lch(args) { + return lab2lch(xyz2lab(args)); +} + +function lab2xyz(lab) { + var l = lab[0], + a = lab[1], + b = lab[2], + x, y, z, y2; + + if (l <= 8) { + y = (l * 100) / 903.3; + y2 = (7.787 * (y / 100)) + (16 / 116); + } else { + y = 100 * Math.pow((l + 16) / 116, 3); + y2 = Math.pow(y / 100, 1/3); + } + + x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); + + z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); + + return [x, y, z]; +} + +function lab2lch(lab) { + var l = lab[0], + a = lab[1], + b = lab[2], + hr, h, c; + + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + if (h < 0) { + h += 360; + } + c = Math.sqrt(a * a + b * b); + return [l, c, h]; +} + +function lab2rgb(args) { + return xyz2rgb(lab2xyz(args)); +} + +function lch2lab(lch) { + var l = lch[0], + c = lch[1], + h = lch[2], + a, b, hr; + + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); + return [l, a, b]; +} + +function lch2xyz(args) { + return lab2xyz(lch2lab(args)); +} + +function lch2rgb(args) { + return lab2rgb(lch2lab(args)); +} + +function keyword2rgb(keyword) { + return cssKeywords[keyword]; +} + +function keyword2hsl(args) { + return rgb2hsl(keyword2rgb(args)); +} + +function keyword2hsv(args) { + return rgb2hsv(keyword2rgb(args)); +} + +function keyword2hwb(args) { + return rgb2hwb(keyword2rgb(args)); +} + +function keyword2cmyk(args) { + return rgb2cmyk(keyword2rgb(args)); +} + +function keyword2lab(args) { + return rgb2lab(keyword2rgb(args)); +} + +function keyword2xyz(args) { + return rgb2xyz(keyword2rgb(args)); +} + +var cssKeywords = { + aliceblue: [240,248,255], + antiquewhite: [250,235,215], + aqua: [0,255,255], + aquamarine: [127,255,212], + azure: [240,255,255], + beige: [245,245,220], + bisque: [255,228,196], + black: [0,0,0], + blanchedalmond: [255,235,205], + blue: [0,0,255], + blueviolet: [138,43,226], + brown: [165,42,42], + burlywood: [222,184,135], + cadetblue: [95,158,160], + chartreuse: [127,255,0], + chocolate: [210,105,30], + coral: [255,127,80], + cornflowerblue: [100,149,237], + cornsilk: [255,248,220], + crimson: [220,20,60], + cyan: [0,255,255], + darkblue: [0,0,139], + darkcyan: [0,139,139], + darkgoldenrod: [184,134,11], + darkgray: [169,169,169], + darkgreen: [0,100,0], + darkgrey: [169,169,169], + darkkhaki: [189,183,107], + darkmagenta: [139,0,139], + darkolivegreen: [85,107,47], + darkorange: [255,140,0], + darkorchid: [153,50,204], + darkred: [139,0,0], + darksalmon: [233,150,122], + darkseagreen: [143,188,143], + darkslateblue: [72,61,139], + darkslategray: [47,79,79], + darkslategrey: [47,79,79], + darkturquoise: [0,206,209], + darkviolet: [148,0,211], + deeppink: [255,20,147], + deepskyblue: [0,191,255], + dimgray: [105,105,105], + dimgrey: [105,105,105], + dodgerblue: [30,144,255], + firebrick: [178,34,34], + floralwhite: [255,250,240], + forestgreen: [34,139,34], + fuchsia: [255,0,255], + gainsboro: [220,220,220], + ghostwhite: [248,248,255], + gold: [255,215,0], + goldenrod: [218,165,32], + gray: [128,128,128], + green: [0,128,0], + greenyellow: [173,255,47], + grey: [128,128,128], + honeydew: [240,255,240], + hotpink: [255,105,180], + indianred: [205,92,92], + indigo: [75,0,130], + ivory: [255,255,240], + khaki: [240,230,140], + lavender: [230,230,250], + lavenderblush: [255,240,245], + lawngreen: [124,252,0], + lemonchiffon: [255,250,205], + lightblue: [173,216,230], + lightcoral: [240,128,128], + lightcyan: [224,255,255], + lightgoldenrodyellow: [250,250,210], + lightgray: [211,211,211], + lightgreen: [144,238,144], + lightgrey: [211,211,211], + lightpink: [255,182,193], + lightsalmon: [255,160,122], + lightseagreen: [32,178,170], + lightskyblue: [135,206,250], + lightslategray: [119,136,153], + lightslategrey: [119,136,153], + lightsteelblue: [176,196,222], + lightyellow: [255,255,224], + lime: [0,255,0], + limegreen: [50,205,50], + linen: [250,240,230], + magenta: [255,0,255], + maroon: [128,0,0], + mediumaquamarine: [102,205,170], + mediumblue: [0,0,205], + mediumorchid: [186,85,211], + mediumpurple: [147,112,219], + mediumseagreen: [60,179,113], + mediumslateblue: [123,104,238], + mediumspringgreen: [0,250,154], + mediumturquoise: [72,209,204], + mediumvioletred: [199,21,133], + midnightblue: [25,25,112], + mintcream: [245,255,250], + mistyrose: [255,228,225], + moccasin: [255,228,181], + navajowhite: [255,222,173], + navy: [0,0,128], + oldlace: [253,245,230], + olive: [128,128,0], + olivedrab: [107,142,35], + orange: [255,165,0], + orangered: [255,69,0], + orchid: [218,112,214], + palegoldenrod: [238,232,170], + palegreen: [152,251,152], + paleturquoise: [175,238,238], + palevioletred: [219,112,147], + papayawhip: [255,239,213], + peachpuff: [255,218,185], + peru: [205,133,63], + pink: [255,192,203], + plum: [221,160,221], + powderblue: [176,224,230], + purple: [128,0,128], + rebeccapurple: [102, 51, 153], + red: [255,0,0], + rosybrown: [188,143,143], + royalblue: [65,105,225], + saddlebrown: [139,69,19], + salmon: [250,128,114], + sandybrown: [244,164,96], + seagreen: [46,139,87], + seashell: [255,245,238], + sienna: [160,82,45], + silver: [192,192,192], + skyblue: [135,206,235], + slateblue: [106,90,205], + slategray: [112,128,144], + slategrey: [112,128,144], + snow: [255,250,250], + springgreen: [0,255,127], + steelblue: [70,130,180], + tan: [210,180,140], + teal: [0,128,128], + thistle: [216,191,216], + tomato: [255,99,71], + turquoise: [64,224,208], + violet: [238,130,238], + wheat: [245,222,179], + white: [255,255,255], + whitesmoke: [245,245,245], + yellow: [255,255,0], + yellowgreen: [154,205,50] +}; + +var reverseKeywords = {}; +for (var key in cssKeywords) { + reverseKeywords[JSON.stringify(cssKeywords[key])] = key; +} + +},{}],5:[function(require,module,exports){ +var conversions = require(4); + +var convert = function() { + return new Converter(); +} + +for (var func in conversions) { + // export Raw versions + convert[func + "Raw"] = (function(func) { + // accept array or plain args + return function(arg) { + if (typeof arg == "number") + arg = Array.prototype.slice.call(arguments); + return conversions[func](arg); + } + })(func); + + var pair = /(\w+)2(\w+)/.exec(func), + from = pair[1], + to = pair[2]; + + // export rgb2hsl and ["rgb"]["hsl"] + convert[from] = convert[from] || {}; + + convert[from][to] = convert[func] = (function(func) { + return function(arg) { + if (typeof arg == "number") + arg = Array.prototype.slice.call(arguments); + + var val = conversions[func](arg); + if (typeof val == "string" || val === undefined) + return val; // keyword + + for (var i = 0; i < val.length; i++) + val[i] = Math.round(val[i]); + return val; + } + })(func); +} + + +/* Converter does lazy conversion and caching */ +var Converter = function() { + this.convs = {}; +}; + +/* Either get the values for a space or + set the values for a space, depending on args */ +Converter.prototype.routeSpace = function(space, args) { + var values = args[0]; + if (values === undefined) { + // color.rgb() + return this.getValues(space); + } + // color.rgb(10, 10, 10) + if (typeof values == "number") { + values = Array.prototype.slice.call(args); + } + + return this.setValues(space, values); +}; + +/* Set the values for a space, invalidating cache */ +Converter.prototype.setValues = function(space, values) { + this.space = space; + this.convs = {}; + this.convs[space] = values; + return this; +}; + +/* Get the values for a space. If there's already + a conversion for the space, fetch it, otherwise + compute it */ +Converter.prototype.getValues = function(space) { + var vals = this.convs[space]; + if (!vals) { + var fspace = this.space, + from = this.convs[fspace]; + vals = convert[fspace][space](from); + + this.convs[space] = vals; + } + return vals; +}; + +["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { + Converter.prototype[space] = function(vals) { + return this.routeSpace(space, arguments); + } +}); + +module.exports = convert; +},{"4":4}],6:[function(require,module,exports){ +module.exports = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; +},{}],7:[function(require,module,exports){ +/** + * @namespace Chart + */ +var Chart = require(27)(); + +require(26)(Chart); +require(22)(Chart); +require(25)(Chart); +require(21)(Chart); +require(23)(Chart); +require(24)(Chart); +require(28)(Chart); +require(32)(Chart); +require(30)(Chart); +require(31)(Chart); +require(33)(Chart); +require(29)(Chart); +require(34)(Chart); + +require(35)(Chart); +require(36)(Chart); +require(37)(Chart); +require(38)(Chart); + +require(41)(Chart); +require(39)(Chart); +require(40)(Chart); +require(42)(Chart); +require(43)(Chart); +require(44)(Chart); + +// Controllers must be loaded after elements +// See Chart.core.datasetController.dataElementType +require(15)(Chart); +require(16)(Chart); +require(17)(Chart); +require(18)(Chart); +require(19)(Chart); +require(20)(Chart); + +require(8)(Chart); +require(9)(Chart); +require(10)(Chart); +require(11)(Chart); +require(12)(Chart); +require(13)(Chart); +require(14)(Chart); + +window.Chart = module.exports = Chart; + +},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"8":8,"9":9}],8:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Bar = function(context, config) { + config.type = 'bar'; + + return new Chart(context, config); + }; + +}; + +},{}],9:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Bubble = function(context, config) { + config.type = 'bubble'; + return new Chart(context, config); + }; + +}; + +},{}],10:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Doughnut = function(context, config) { + config.type = 'doughnut'; + + return new Chart(context, config); + }; + +}; + +},{}],11:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Line = function(context, config) { + config.type = 'line'; + + return new Chart(context, config); + }; + +}; + +},{}],12:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.PolarArea = function(context, config) { + config.type = 'polarArea'; + + return new Chart(context, config); + }; + +}; + +},{}],13:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Radar = function(context, config) { + config.options = Chart.helpers.configMerge({aspectRatio: 1}, config.options); + config.type = 'radar'; + + return new Chart(context, config); + }; + +}; + +},{}],14:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var defaultConfig = { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // scatter should not use a category axis + position: 'bottom', + id: 'x-axis-1' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-1' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(tooltipItem) { + return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; + } + } + } + }; + + // Register the default config for this type + Chart.defaults.scatter = defaultConfig; + + // Scatter charts use line controllers + Chart.controllers.scatter = Chart.controllers.line; + + Chart.Scatter = function(context, config) { + config.type = 'scatter'; + return new Chart(context, config); + }; + +}; + +},{}],15:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.bar = { + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + + // Specific to Bar Controller + categoryPercentage: 0.8, + barPercentage: 0.9, + + // grid line settings + gridLines: { + offsetGridLines: true + } + }], + yAxes: [{ + type: 'linear' + }] + } + }; + + Chart.controllers.bar = Chart.DatasetController.extend({ + + dataElementType: Chart.elements.Rectangle, + + initialize: function(chart, datasetIndex) { + Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex); + + // Use this to indicate that this is a bar dataset. + this.getMeta().bar = true; + }, + + // Get the number of datasets that display bars. We use this to correctly calculate the bar width + getBarCount: function() { + var me = this; + var barCount = 0; + helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { + var meta = me.chart.getDatasetMeta(datasetIndex); + if (meta.bar && me.chart.isDatasetVisible(datasetIndex)) { + ++barCount; + } + }, me); + return barCount; + }, + + update: function(reset) { + var me = this; + helpers.each(me.getMeta().data, function(rectangle, index) { + me.updateElement(rectangle, index, reset); + }, me); + }, + + updateElement: function(rectangle, index, reset) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var scaleBase = yScale.getBasePixel(); + var rectangleElementOptions = me.chart.options.elements.rectangle; + var custom = rectangle.custom || {}; + var dataset = me.getDataset(); + + helpers.extend(rectangle, { + // Utility + _xScale: xScale, + _yScale: yScale, + _datasetIndex: me.index, + _index: index, + + // Desired view properties + _model: { + x: me.calculateBarX(index, me.index), + y: reset ? scaleBase : me.calculateBarY(index, me.index), + + // Tooltip + label: me.chart.data.labels[index], + datasetLabel: dataset.label, + + // Appearance + base: reset ? scaleBase : me.calculateBarBase(me.index, index), + width: me.calculateBarWidth(index), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), + borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, + borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) + } + }); + rectangle.pivot(); + }, + + calculateBarBase: function(datasetIndex, index) { + var me = this; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var base = 0; + + if (yScale.options.stacked) { + var chart = me.chart; + var datasets = chart.data.datasets; + var value = Number(datasets[datasetIndex].data[index]); + + for (var i = 0; i < datasetIndex; i++) { + var currentDs = datasets[i]; + var currentDsMeta = chart.getDatasetMeta(i); + if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { + var currentVal = Number(currentDs.data[index]); + base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); + } + } + + return yScale.getPixelForValue(base); + } + + return yScale.getBasePixel(); + }, + + getRuler: function(index) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var datasetCount = me.getBarCount(); + + var tickWidth; + + if (xScale.options.type === 'category') { + tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index); + } else { + // Average width + tickWidth = xScale.width / xScale.ticks.length; + } + var categoryWidth = tickWidth * xScale.options.categoryPercentage; + var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2; + var fullBarWidth = categoryWidth / datasetCount; + + if (xScale.ticks.length !== me.chart.data.labels.length) { + var perc = xScale.ticks.length / me.chart.data.labels.length; + fullBarWidth = fullBarWidth * perc; + } + + var barWidth = fullBarWidth * xScale.options.barPercentage; + var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage); + + return { + datasetCount: datasetCount, + tickWidth: tickWidth, + categoryWidth: categoryWidth, + categorySpacing: categorySpacing, + fullBarWidth: fullBarWidth, + barWidth: barWidth, + barSpacing: barSpacing + }; + }, + + calculateBarWidth: function(index) { + var xScale = this.getScaleForId(this.getMeta().xAxisID); + if (xScale.options.barThickness) { + return xScale.options.barThickness; + } + var ruler = this.getRuler(index); + return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth; + }, + + // Get bar index from the given dataset index accounting for the fact that not all bars are visible + getBarIndex: function(datasetIndex) { + var barIndex = 0; + var meta, j; + + for (j = 0; j < datasetIndex; ++j) { + meta = this.chart.getDatasetMeta(j); + if (meta.bar && this.chart.isDatasetVisible(j)) { + ++barIndex; + } + } + + return barIndex; + }, + + calculateBarX: function(index, datasetIndex) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var barIndex = me.getBarIndex(datasetIndex); + + var ruler = me.getRuler(index); + var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); + leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0; + + if (xScale.options.stacked) { + return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing; + } + + return leftTick + + (ruler.barWidth / 2) + + ruler.categorySpacing + + (ruler.barWidth * barIndex) + + (ruler.barSpacing / 2) + + (ruler.barSpacing * barIndex); + }, + + calculateBarY: function(index, datasetIndex) { + var me = this; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var value = Number(me.getDataset().data[index]); + + if (yScale.options.stacked) { + + var sumPos = 0, + sumNeg = 0; + + for (var i = 0; i < datasetIndex; i++) { + var ds = me.chart.data.datasets[i]; + var dsMeta = me.chart.getDatasetMeta(i); + if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i)) { + var stackedVal = Number(ds.data[index]); + if (stackedVal < 0) { + sumNeg += stackedVal || 0; + } else { + sumPos += stackedVal || 0; + } + } + } + + if (value < 0) { + return yScale.getPixelForValue(sumNeg + value); + } + return yScale.getPixelForValue(sumPos + value); + } + + return yScale.getPixelForValue(value); + }, + + draw: function(ease) { + var me = this; + var easingDecimal = ease || 1; + helpers.each(me.getMeta().data, function(rectangle, index) { + var d = me.getDataset().data[index]; + if (d !== null && d !== undefined && !isNaN(d)) { + rectangle.transition(easingDecimal).draw(); + } + }, me); + }, + + setHoverStyle: function(rectangle) { + var dataset = this.chart.data.datasets[rectangle._datasetIndex]; + var index = rectangle._index; + + var custom = rectangle.custom || {}; + var model = rectangle._model; + model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); + }, + + removeHoverStyle: function(rectangle) { + var dataset = this.chart.data.datasets[rectangle._datasetIndex]; + var index = rectangle._index; + var custom = rectangle.custom || {}; + var model = rectangle._model; + var rectangleElementOptions = this.chart.options.elements.rectangle; + + model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); + model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); + model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); + } + + }); + + + // including horizontalBar in the bar file, instead of a file of its own + // it extends bar (like pie extends doughnut) + Chart.defaults.horizontalBar = { + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'linear', + position: 'bottom' + }], + yAxes: [{ + position: 'left', + type: 'category', + + // Specific to Horizontal Bar Controller + categoryPercentage: 0.8, + barPercentage: 0.9, + + // grid line settings + gridLines: { + offsetGridLines: true + } + }] + }, + elements: { + rectangle: { + borderSkipped: 'left' + } + }, + tooltips: { + callbacks: { + title: function(tooltipItems, data) { + // Pick first xLabel for now + var title = ''; + + if (tooltipItems.length > 0) { + if (tooltipItems[0].yLabel) { + title = tooltipItems[0].yLabel; + } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { + title = data.labels[tooltipItems[0].index]; + } + } + + return title; + }, + label: function(tooltipItem, data) { + var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; + return datasetLabel + ': ' + tooltipItem.xLabel; + } + } + } + }; + + Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ + updateElement: function(rectangle, index, reset) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var scaleBase = xScale.getBasePixel(); + var custom = rectangle.custom || {}; + var dataset = me.getDataset(); + var rectangleElementOptions = me.chart.options.elements.rectangle; + + helpers.extend(rectangle, { + // Utility + _xScale: xScale, + _yScale: yScale, + _datasetIndex: me.index, + _index: index, + + // Desired view properties + _model: { + x: reset ? scaleBase : me.calculateBarX(index, me.index), + y: me.calculateBarY(index, me.index), + + // Tooltip + label: me.chart.data.labels[index], + datasetLabel: dataset.label, + + // Appearance + base: reset ? scaleBase : me.calculateBarBase(me.index, index), + height: me.calculateBarHeight(index), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), + borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, + borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) + }, + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + + var halfHeight = vm.height / 2, + topY = vm.y - halfHeight, + bottomY = vm.y + halfHeight, + right = vm.base - (vm.base - vm.x), + halfStroke = vm.borderWidth / 2; + + // Canvas doesn't allow us to stroke inside the width so we can + // adjust the sizes to fit if we're setting a stroke on the line + if (vm.borderWidth) { + topY += halfStroke; + bottomY -= halfStroke; + right += halfStroke; + } + + ctx.beginPath(); + + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + ctx.lineWidth = vm.borderWidth; + + // Corner points, from bottom-left to bottom-right clockwise + // | 1 2 | + // | 0 3 | + var corners = [ + [vm.base, bottomY], + [vm.base, topY], + [right, topY], + [right, bottomY] + ]; + + // Find first (starting) corner with fallback to 'bottom' + var borders = ['bottom', 'left', 'top', 'right']; + var startCorner = borders.indexOf(vm.borderSkipped, 0); + if (startCorner === -1) { + startCorner = 0; + } + + function cornerAt(cornerIndex) { + return corners[(startCorner + cornerIndex) % 4]; + } + + // Draw rectangle from 'startCorner' + ctx.moveTo.apply(ctx, cornerAt(0)); + for (var i = 1; i < 4; i++) { + ctx.lineTo.apply(ctx, cornerAt(i)); + } + + ctx.fill(); + if (vm.borderWidth) { + ctx.stroke(); + } + }, + + inRange: function(mouseX, mouseY) { + var vm = this._view; + var inRange = false; + + if (vm) { + if (vm.x < vm.base) { + inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.x && mouseX <= vm.base); + } else { + inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.base && mouseX <= vm.x); + } + } + + return inRange; + } + }); + + rectangle.pivot(); + }, + + calculateBarBase: function(datasetIndex, index) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var base = 0; + + if (xScale.options.stacked) { + var chart = me.chart; + var datasets = chart.data.datasets; + var value = Number(datasets[datasetIndex].data[index]); + + for (var i = 0; i < datasetIndex; i++) { + var currentDs = datasets[i]; + var currentDsMeta = chart.getDatasetMeta(i); + if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i)) { + var currentVal = Number(currentDs.data[index]); + base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); + } + } + + return xScale.getPixelForValue(base); + } + + return xScale.getBasePixel(); + }, + + getRuler: function(index) { + var me = this; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var datasetCount = me.getBarCount(); + + var tickHeight; + if (yScale.options.type === 'category') { + tickHeight = yScale.getPixelForTick(index + 1) - yScale.getPixelForTick(index); + } else { + // Average width + tickHeight = yScale.width / yScale.ticks.length; + } + var categoryHeight = tickHeight * yScale.options.categoryPercentage; + var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2; + var fullBarHeight = categoryHeight / datasetCount; + + if (yScale.ticks.length !== me.chart.data.labels.length) { + var perc = yScale.ticks.length / me.chart.data.labels.length; + fullBarHeight = fullBarHeight * perc; + } + + var barHeight = fullBarHeight * yScale.options.barPercentage; + var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage); + + return { + datasetCount: datasetCount, + tickHeight: tickHeight, + categoryHeight: categoryHeight, + categorySpacing: categorySpacing, + fullBarHeight: fullBarHeight, + barHeight: barHeight, + barSpacing: barSpacing + }; + }, + + calculateBarHeight: function(index) { + var me = this; + var yScale = me.getScaleForId(me.getMeta().yAxisID); + if (yScale.options.barThickness) { + return yScale.options.barThickness; + } + var ruler = me.getRuler(index); + return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight; + }, + + calculateBarX: function(index, datasetIndex) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var value = Number(me.getDataset().data[index]); + + if (xScale.options.stacked) { + + var sumPos = 0, + sumNeg = 0; + + for (var i = 0; i < datasetIndex; i++) { + var ds = me.chart.data.datasets[i]; + var dsMeta = me.chart.getDatasetMeta(i); + if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) { + var stackedVal = Number(ds.data[index]); + if (stackedVal < 0) { + sumNeg += stackedVal || 0; + } else { + sumPos += stackedVal || 0; + } + } + } + + if (value < 0) { + return xScale.getPixelForValue(sumNeg + value); + } + return xScale.getPixelForValue(sumPos + value); + } + + return xScale.getPixelForValue(value); + }, + + calculateBarY: function(index, datasetIndex) { + var me = this; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var barIndex = me.getBarIndex(datasetIndex); + + var ruler = me.getRuler(index); + var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); + topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0; + + if (yScale.options.stacked) { + return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing; + } + + return topTick + + (ruler.barHeight / 2) + + ruler.categorySpacing + + (ruler.barHeight * barIndex) + + (ruler.barSpacing / 2) + + (ruler.barSpacing * barIndex); + } + }); +}; + +},{}],16:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.bubble = { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // bubble should probably use a linear scale by default + position: 'bottom', + id: 'x-axis-0' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-0' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(tooltipItem, data) { + var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; + var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + return datasetLabel + ': (' + dataPoint.x + ', ' + dataPoint.y + ', ' + dataPoint.r + ')'; + } + } + } + }; + + Chart.controllers.bubble = Chart.DatasetController.extend({ + + dataElementType: Chart.elements.Point, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var points = meta.data; + + // Update Points + helpers.each(points, function(point, index) { + me.updateElement(point, index, reset); + }); + }, + + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + + var custom = point.custom || {}; + var dataset = me.getDataset(); + var data = dataset.data[index]; + var pointElementOptions = me.chart.options.elements.point; + var dsIndex = me.index; + + helpers.extend(point, { + // Utility + _xScale: xScale, + _yScale: yScale, + _datasetIndex: dsIndex, + _index: index, + + // Desired view properties + _model: { + x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), + y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), + // Appearance + radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), + + // Tooltip + hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) + } + }); + + // Trick to reset the styles of the point + Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions); + + var model = point._model; + model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y)); + + point.pivot(); + }, + + getRadius: function(value) { + return value.r || this.chart.options.elements.point.radius; + }, + + setHoverStyle: function(point) { + var me = this; + Chart.DatasetController.prototype.setHoverStyle.call(me, point); + + // Radius + var dataset = me.chart.data.datasets[point._datasetIndex]; + var index = point._index; + var custom = point.custom || {}; + var model = point._model; + model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]); + }, + + removeHoverStyle: function(point) { + var me = this; + Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point); + + var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index]; + var custom = point.custom || {}; + var model = point._model; + + model.radius = custom.radius ? custom.radius : me.getRadius(dataVal); + } + }); +}; + +},{}],17:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers, + defaults = Chart.defaults; + + defaults.doughnut = { + animation: { + // Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + // Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false + }, + aspectRatio: 1, + hover: { + mode: 'single' + }, + legendCallback: function(chart) { + var text = []; + text.push('
    '); + + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + + if (datasets.length) { + for (var i = 0; i < datasets[0].data.length; ++i) { + text.push('
  • '); + if (labels[i]) { + text.push(labels[i]); + } + text.push('
  • '); + } + } + + text.push('
'); + return text.join(''); + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var ds = data.datasets[0]; + var arc = meta.data[i]; + var custom = arc && arc.custom || {}; + var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + var arcOpts = chart.options.elements.arc; + var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); + var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); + var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); + + return { + text: label, + fillStyle: fill, + strokeStyle: stroke, + lineWidth: bw, + hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + // toggle visibility of index if exists + if (meta.data[index]) { + meta.data[index].hidden = !meta.data[index].hidden; + } + } + + chart.update(); + } + }, + + // The percentage of the chart that we cut out of the middle. + cutoutPercentage: 50, + + // The rotation of the chart, where the first data arc begins. + rotation: Math.PI * -0.5, + + // The total circumference of the chart. + circumference: Math.PI * 2.0, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(tooltipItem, data) { + return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + } + } + } + }; + + defaults.pie = helpers.clone(defaults.doughnut); + helpers.extend(defaults.pie, { + cutoutPercentage: 0 + }); + + + Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ + + dataElementType: Chart.elements.Arc, + + linkScales: helpers.noop, + + // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly + getRingIndex: function(datasetIndex) { + var ringIndex = 0; + + for (var j = 0; j < datasetIndex; ++j) { + if (this.chart.isDatasetVisible(j)) { + ++ringIndex; + } + } + + return ringIndex; + }, + + update: function(reset) { + var me = this; + var chart = me.chart, + chartArea = chart.chartArea, + opts = chart.options, + arcOpts = opts.elements.arc, + availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth, + availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth, + minSize = Math.min(availableWidth, availableHeight), + offset = { + x: 0, + y: 0 + }, + meta = me.getMeta(), + cutoutPercentage = opts.cutoutPercentage, + circumference = opts.circumference; + + // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc + if (circumference < Math.PI * 2.0) { + var startAngle = opts.rotation % (Math.PI * 2.0); + startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); + var endAngle = startAngle + circumference; + var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; + var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; + var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); + var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); + var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); + var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); + var cutout = cutoutPercentage / 100.0; + var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; + var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; + var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; + minSize = Math.min(availableWidth / size.width, availableHeight / size.height); + offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; + } + + chart.borderWidth = me.getMaxBorderWidth(meta.data); + chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); + chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 1, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); + chart.offsetX = offset.x * chart.outerRadius; + chart.offsetY = offset.y * chart.outerRadius; + + meta.total = me.calculateTotal(); + + me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); + me.innerRadius = me.outerRadius - chart.radiusLength; + + helpers.each(meta.data, function(arc, index) { + me.updateElement(arc, index, reset); + }); + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart, + chartArea = chart.chartArea, + opts = chart.options, + animationOpts = opts.animation, + centerX = (chartArea.left + chartArea.right) / 2, + centerY = (chartArea.top + chartArea.bottom) / 2, + startAngle = opts.rotation, // non reset case handled later + endAngle = opts.rotation, // non reset case handled later + dataset = me.getDataset(), + circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), + innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, + outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, + valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + + helpers.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + + // Desired view properties + _model: { + x: centerX + chart.offsetX, + y: centerY + chart.offsetY, + startAngle: startAngle, + endAngle: endAngle, + circumference: circumference, + outerRadius: outerRadius, + innerRadius: innerRadius, + label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) + } + }); + + var model = arc._model; + // Resets the visual styles + this.removeHoverStyle(arc); + + // Set correct angles if not resetting + if (!reset || !animationOpts.animateRotate) { + if (index === 0) { + model.startAngle = opts.rotation; + } else { + model.startAngle = me.getMeta().data[index - 1]._model.endAngle; + } + + model.endAngle = model.startAngle + model.circumference; + } + + arc.pivot(); + }, + + removeHoverStyle: function(arc) { + Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); + }, + + calculateTotal: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var total = 0; + var value; + + helpers.each(meta.data, function(element, index) { + value = dataset.data[index]; + if (!isNaN(value) && !element.hidden) { + total += Math.abs(value); + } + }); + + /* if (total === 0) { + total = NaN; + }*/ + + return total; + }, + + calculateCircumference: function(value) { + var total = this.getMeta().total; + if (total > 0 && !isNaN(value)) { + return (Math.PI * 2.0) * (value / total); + } + return 0; + }, + + // gets the max border or hover width to properly scale pie charts + getMaxBorderWidth: function(elements) { + var max = 0, + index = this.index, + length = elements.length, + borderWidth, + hoverWidth; + + for (var i = 0; i < length; i++) { + borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; + hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; + + max = borderWidth > max ? borderWidth : max; + max = hoverWidth > max ? hoverWidth : max; + } + return max; + } + }); +}; + +},{}],18:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.line = { + showLines: true, + spanGaps: false, + + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + id: 'x-axis-0' + }], + yAxes: [{ + type: 'linear', + id: 'y-axis-0' + }] + } + }; + + function lineEnabled(dataset, options) { + return helpers.getValueOrDefault(dataset.showLine, options.showLines); + } + + Chart.controllers.line = Chart.DatasetController.extend({ + + datasetElementType: Chart.elements.Line, + + dataElementType: Chart.elements.Point, + + addElementAndReset: function(index) { + var me = this; + var options = me.chart.options; + var meta = me.getMeta(); + + Chart.DatasetController.prototype.addElementAndReset.call(me, index); + + // Make sure bezier control points are updated + if (lineEnabled(me.getDataset(), options) && meta.dataset._model.tension !== 0) { + me.updateBezierControlPoints(); + } + }, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data || []; + var options = me.chart.options; + var lineElementOptions = options.elements.line; + var scale = me.getScaleForId(meta.yAxisID); + var i, ilen, custom; + var dataset = me.getDataset(); + var showLine = lineEnabled(dataset, options); + + // Update Line + if (showLine) { + custom = line.custom || {}; + + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { + dataset.lineTension = dataset.tension; + } + + // Utility + line._scale = scale; + line._datasetIndex = me.index; + // Data + line._children = points; + // Model + line._model = { + // Appearance + // The default behavior of lines is to break at null values, according + // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 + // This option gives linse the ability to span gaps + spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, + tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), + borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), + borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), + borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), + borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), + borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), + borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), + fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), + steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), + cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), + // Scale + scaleTop: scale.top, + scaleBottom: scale.bottom, + scaleZero: scale.getBasePixel() + }; + + line.pivot(); + } + + // Update Points + for (i=0, ilen=points.length; i'); + + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + + if (datasets.length) { + for (var i = 0; i < datasets[0].data.length; ++i) { + text.push('
  • '); + if (labels[i]) { + text.push(labels[i]); + } + text.push('
  • '); + } + } + + text.push(''); + return text.join(''); + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var ds = data.datasets[0]; + var arc = meta.data[i]; + var custom = arc.custom || {}; + var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + var arcOpts = chart.options.elements.arc; + var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); + var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); + var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); + + return { + text: label, + fillStyle: fill, + strokeStyle: stroke, + lineWidth: bw, + hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + meta.data[index].hidden = !meta.data[index].hidden; + } + + chart.update(); + } + }, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(tooltipItem, data) { + return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; + } + } + } + }; + + Chart.controllers.polarArea = Chart.DatasetController.extend({ + + dataElementType: Chart.elements.Arc, + + linkScales: helpers.noop, + + update: function(reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var meta = me.getMeta(); + var opts = chart.options; + var arcOpts = opts.elements.arc; + var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); + chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); + chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); + + me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); + me.innerRadius = me.outerRadius - chart.radiusLength; + + meta.count = me.countVisibleElements(); + + helpers.each(meta.data, function(arc, index) { + me.updateElement(arc, index, reset); + }); + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var opts = chart.options; + var animationOpts = opts.animation; + var scale = chart.scale; + var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + var labels = chart.data.labels; + + var circumference = me.calculateCircumference(dataset.data[index]); + var centerX = scale.xCenter; + var centerY = scale.yCenter; + + // If there is NaN data before us, we need to calculate the starting angle correctly. + // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data + var visibleCount = 0; + var meta = me.getMeta(); + for (var i = 0; i < index; ++i) { + if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { + ++visibleCount; + } + } + + // var negHalfPI = -0.5 * Math.PI; + var datasetStartAngle = opts.startAngle; + var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + var startAngle = datasetStartAngle + (circumference * visibleCount); + var endAngle = startAngle + (arc.hidden ? 0 : circumference); + + var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + + helpers.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + _scale: scale, + + // Desired view properties + _model: { + x: centerX, + y: centerY, + innerRadius: 0, + outerRadius: reset ? resetRadius : distance, + startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, + endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, + label: getValueAtIndexOrDefault(labels, index, labels[index]) + } + }); + + // Apply border and fill style + me.removeHoverStyle(arc); + + arc.pivot(); + }, + + removeHoverStyle: function(arc) { + Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); + }, + + countVisibleElements: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var count = 0; + + helpers.each(meta.data, function(element, index) { + if (!isNaN(dataset.data[index]) && !element.hidden) { + count++; + } + }); + + return count; + }, + + calculateCircumference: function(value) { + var count = this.getMeta().count; + if (count > 0 && !isNaN(value)) { + return (2 * Math.PI) / count; + } + return 0; + } + }); +}; + +},{}],20:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.radar = { + scale: { + type: 'radialLinear' + }, + elements: { + line: { + tension: 0 // no bezier in radar + } + } + }; + + Chart.controllers.radar = Chart.DatasetController.extend({ + + datasetElementType: Chart.elements.Line, + + dataElementType: Chart.elements.Point, + + linkScales: helpers.noop, + + addElementAndReset: function(index) { + Chart.DatasetController.prototype.addElementAndReset.call(this, index); + + // Make sure bezier control points are updated + this.updateBezierControlPoints(); + }, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data; + var custom = line.custom || {}; + var dataset = me.getDataset(); + var lineElementOptions = me.chart.options.elements.line; + var scale = me.chart.scale; + + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { + dataset.lineTension = dataset.tension; + } + + helpers.extend(meta.dataset, { + // Utility + _datasetIndex: me.index, + // Data + _children: points, + _loop: true, + // Model + _model: { + // Appearance + tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), + borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), + borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), + fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), + borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), + borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), + borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), + borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), + + // Scale + scaleTop: scale.top, + scaleBottom: scale.bottom, + scaleZero: scale.getBasePosition() + } + }); + + meta.dataset.pivot(); + + // Update Points + helpers.each(points, function(point, index) { + me.updateElement(point, index, reset); + }, me); + + + // Update bezier control points + me.updateBezierControlPoints(); + }, + updateElement: function(point, index, reset) { + var me = this; + var custom = point.custom || {}; + var dataset = me.getDataset(); + var scale = me.chart.scale; + var pointElementOptions = me.chart.options.elements.point; + var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); + + helpers.extend(point, { + // Utility + _datasetIndex: me.index, + _index: index, + _scale: scale, + + // Desired view properties + _model: { + x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales + y: reset ? scale.yCenter : pointPosition.y, + + // Appearance + tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.tension, me.chart.options.elements.line.tension), + radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), + borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), + pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), + + // Tooltip + hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) + } + }); + + point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); + }, + updateBezierControlPoints: function() { + var chartArea = this.chart.chartArea; + var meta = this.getMeta(); + + helpers.each(meta.data, function(point, index) { + var model = point._model; + var controlPoints = helpers.splineCurve( + helpers.previousItem(meta.data, index, true)._model, + model, + helpers.nextItem(meta.data, index, true)._model, + model.tension + ); + + // Prevent the bezier going outside of the bounds of the graph + model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); + model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); + + model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); + model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); + + // Now pivot the point for animation + point.pivot(); + }); + }, + + draw: function(ease) { + var meta = this.getMeta(); + var easingDecimal = ease || 1; + + // Transition Point Locations + helpers.each(meta.data, function(point) { + point.transition(easingDecimal); + }); + + // Transition and Draw the line + meta.dataset.transition(easingDecimal).draw(); + + // Draw the points + helpers.each(meta.data, function(point) { + point.draw(); + }); + }, + + setHoverStyle: function(point) { + // Point + var dataset = this.chart.data.datasets[point._datasetIndex]; + var custom = point.custom || {}; + var index = point._index; + var model = point._model; + + model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); + model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); + }, + + removeHoverStyle: function(point) { + var dataset = this.chart.data.datasets[point._datasetIndex]; + var custom = point.custom || {}; + var index = point._index; + var model = point._model; + var pointElementOptions = this.chart.options.elements.point; + + model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.radius, index, pointElementOptions.radius); + model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); + model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); + model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); + } + }); +}; + +},{}],21:[function(require,module,exports){ +/* global window: false */ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.global.animation = { + duration: 1000, + easing: 'easeOutQuart', + onProgress: helpers.noop, + onComplete: helpers.noop + }; + + Chart.Animation = Chart.Element.extend({ + currentStep: null, // the current animation step + numSteps: 60, // default number of steps + easing: '', // the easing to use for this animation + render: null, // render function used by the animation service + + onAnimationProgress: null, // user specified callback to fire on each step of the animation + onAnimationComplete: null // user specified callback to fire when the animation finishes + }); + + Chart.animationService = { + frameDuration: 17, + animations: [], + dropFrames: 0, + request: null, + addAnimation: function(chartInstance, animationObject, duration, lazy) { + var me = this; + + if (!lazy) { + chartInstance.animating = true; + } + + for (var index = 0; index < me.animations.length; ++index) { + if (me.animations[index].chartInstance === chartInstance) { + // replacing an in progress animation + me.animations[index].animationObject = animationObject; + return; + } + } + + me.animations.push({ + chartInstance: chartInstance, + animationObject: animationObject + }); + + // If there are no animations queued, manually kickstart a digest, for lack of a better word + if (me.animations.length === 1) { + me.requestAnimationFrame(); + } + }, + // Cancel the animation for a given chart instance + cancelAnimation: function(chartInstance) { + var index = helpers.findIndex(this.animations, function(animationWrapper) { + return animationWrapper.chartInstance === chartInstance; + }); + + if (index !== -1) { + this.animations.splice(index, 1); + chartInstance.animating = false; + } + }, + requestAnimationFrame: function() { + var me = this; + if (me.request === null) { + // Skip animation frame requests until the active one is executed. + // This can happen when processing mouse events, e.g. 'mousemove' + // and 'mouseout' events will trigger multiple renders. + me.request = helpers.requestAnimFrame.call(window, function() { + me.request = null; + me.startDigest(); + }); + } + }, + startDigest: function() { + var me = this; + + var startTime = Date.now(); + var framesToDrop = 0; + + if (me.dropFrames > 1) { + framesToDrop = Math.floor(me.dropFrames); + me.dropFrames = me.dropFrames % 1; + } + + var i = 0; + while (i < me.animations.length) { + if (me.animations[i].animationObject.currentStep === null) { + me.animations[i].animationObject.currentStep = 0; + } + + me.animations[i].animationObject.currentStep += 1 + framesToDrop; + + if (me.animations[i].animationObject.currentStep > me.animations[i].animationObject.numSteps) { + me.animations[i].animationObject.currentStep = me.animations[i].animationObject.numSteps; + } + + me.animations[i].animationObject.render(me.animations[i].chartInstance, me.animations[i].animationObject); + if (me.animations[i].animationObject.onAnimationProgress && me.animations[i].animationObject.onAnimationProgress.call) { + me.animations[i].animationObject.onAnimationProgress.call(me.animations[i].chartInstance, me.animations[i]); + } + + if (me.animations[i].animationObject.currentStep === me.animations[i].animationObject.numSteps) { + if (me.animations[i].animationObject.onAnimationComplete && me.animations[i].animationObject.onAnimationComplete.call) { + me.animations[i].animationObject.onAnimationComplete.call(me.animations[i].chartInstance, me.animations[i]); + } + + // executed the last frame. Remove the animation. + me.animations[i].chartInstance.animating = false; + + me.animations.splice(i, 1); + } else { + ++i; + } + } + + var endTime = Date.now(); + var dropFrames = (endTime - startTime) / me.frameDuration; + + me.dropFrames += dropFrames; + + // Do we have more stuff to animate? + if (me.animations.length > 0) { + me.requestAnimationFrame(); + } + } + }; +}; + +},{}],22:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + // Global Chart canvas helpers object for drawing items to canvas + var helpers = Chart.canvasHelpers = {}; + + helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { + var type, edgeLength, xOffset, yOffset, height, size; + + if (typeof pointStyle === 'object') { + type = pointStyle.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2); + return; + } + } + + if (isNaN(radius) || radius <= 0) { + return; + } + + switch (pointStyle) { + // Default includes circle + default: + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.closePath(); + ctx.fill(); + break; + case 'triangle': + ctx.beginPath(); + edgeLength = 3 * radius / Math.sqrt(3); + height = edgeLength * Math.sqrt(3) / 2; + ctx.moveTo(x - edgeLength / 2, y + height / 3); + ctx.lineTo(x + edgeLength / 2, y + height / 3); + ctx.lineTo(x, y - 2 * height / 3); + ctx.closePath(); + ctx.fill(); + break; + case 'rect': + size = 1 / Math.SQRT2 * radius; + ctx.beginPath(); + ctx.fillRect(x - size, y - size, 2 * size, 2 * size); + ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); + break; + case 'rectRot': + size = 1 / Math.SQRT2 * radius; + ctx.beginPath(); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y - size); + ctx.closePath(); + ctx.fill(); + break; + case 'cross': + ctx.beginPath(); + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y - radius); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + case 'crossRot': + ctx.beginPath(); + xOffset = Math.cos(Math.PI / 4) * radius; + yOffset = Math.sin(Math.PI / 4) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x - xOffset, y + yOffset); + ctx.lineTo(x + xOffset, y - yOffset); + ctx.closePath(); + break; + case 'star': + ctx.beginPath(); + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y - radius); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + xOffset = Math.cos(Math.PI / 4) * radius; + yOffset = Math.sin(Math.PI / 4) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x - xOffset, y + yOffset); + ctx.lineTo(x + xOffset, y - yOffset); + ctx.closePath(); + break; + case 'line': + ctx.beginPath(); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + case 'dash': + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + } + + ctx.stroke(); + }; +}; + +},{}],23:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + // Create a dictionary of chart types, to allow for extension of existing types + Chart.types = {}; + + // Store a reference to each instance - allowing us to globally resize chart instances on window resize. + // Destroy method on the chart will remove the instance of the chart from this reference. + Chart.instances = {}; + + // Controllers available for dataset visualization eg. bar, line, slice, etc. + Chart.controllers = {}; + + /** + * @class Chart.Controller + * The main controller of a chart. + */ + Chart.Controller = function(instance) { + + this.chart = instance; + this.config = instance.config; + this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {}); + this.id = helpers.uid(); + + Object.defineProperty(this, 'data', { + get: function() { + return this.config.data; + } + }); + + // Add the chart instance to the global namespace + Chart.instances[this.id] = this; + + if (this.options.responsive) { + // Silent resize before chart draws + this.resize(true); + } + + this.initialize(); + + return this; + }; + + helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller */ { + + initialize: function() { + var me = this; + // Before init plugin notification + Chart.plugins.notify('beforeInit', [me]); + + me.bindEvents(); + + // Make sure controllers are built first so that each dataset is bound to an axis before the scales + // are built + me.ensureScalesHaveIDs(); + me.buildOrUpdateControllers(); + me.buildScales(); + me.updateLayout(); + me.resetElements(); + me.initToolTip(); + me.update(); + + // After init plugin notification + Chart.plugins.notify('afterInit', [me]); + + return me; + }, + + clear: function() { + helpers.clear(this.chart); + return this; + }, + + stop: function() { + // Stops any current animation loop occuring + Chart.animationService.cancelAnimation(this); + return this; + }, + + resize: function(silent) { + var me = this; + var chart = me.chart; + var canvas = chart.canvas; + var newWidth = helpers.getMaximumWidth(canvas); + var aspectRatio = chart.aspectRatio; + var newHeight = (me.options.maintainAspectRatio && isNaN(aspectRatio) === false && isFinite(aspectRatio) && aspectRatio !== 0) ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas); + + var sizeChanged = chart.width !== newWidth || chart.height !== newHeight; + + if (!sizeChanged) { + return me; + } + + canvas.width = chart.width = newWidth; + canvas.height = chart.height = newHeight; + + helpers.retinaScale(chart); + + // Notify any plugins about the resize + var newSize = {width: newWidth, height: newHeight}; + Chart.plugins.notify('resize', [me, newSize]); + + // Notify of resize + if (me.options.onResize) { + me.options.onResize(me, newSize); + } + + if (!silent) { + me.stop(); + me.update(me.options.responsiveAnimationDuration); + } + + return me; + }, + + ensureScalesHaveIDs: function() { + var options = this.options; + var scalesOptions = options.scales || {}; + var scaleOptions = options.scale; + + helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { + xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); + }); + + helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { + yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); + }); + + if (scaleOptions) { + scaleOptions.id = scaleOptions.id || 'scale'; + } + }, + + /** + * Builds a map of scale ID to scale object for future lookup. + */ + buildScales: function() { + var me = this; + var options = me.options; + var scales = me.scales = {}; + var items = []; + + if (options.scales) { + items = items.concat( + (options.scales.xAxes || []).map(function(xAxisOptions) { + return {options: xAxisOptions, dtype: 'category'}; + }), + (options.scales.yAxes || []).map(function(yAxisOptions) { + return {options: yAxisOptions, dtype: 'linear'}; + }) + ); + } + + if (options.scale) { + items.push({options: options.scale, dtype: 'radialLinear', isDefault: true}); + } + + helpers.each(items, function(item) { + var scaleOptions = item.options; + var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); + var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); + if (!scaleClass) { + return; + } + + var scale = new scaleClass({ + id: scaleOptions.id, + options: scaleOptions, + ctx: me.chart.ctx, + chart: me + }); + + scales[scale.id] = scale; + + // TODO(SB): I think we should be able to remove this custom case (options.scale) + // and consider it as a regular scale part of the "scales"" map only! This would + // make the logic easier and remove some useless? custom code. + if (item.isDefault) { + me.scale = scale; + } + }); + + Chart.scaleService.addScalesToLayout(this); + }, + + updateLayout: function() { + Chart.layoutService.update(this, this.chart.width, this.chart.height); + }, + + buildOrUpdateControllers: function() { + var me = this; + var types = []; + var newControllers = []; + + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + var meta = me.getDatasetMeta(datasetIndex); + if (!meta.type) { + meta.type = dataset.type || me.config.type; + } + + types.push(meta.type); + + if (meta.controller) { + meta.controller.updateIndex(datasetIndex); + } else { + meta.controller = new Chart.controllers[meta.type](me, datasetIndex); + newControllers.push(meta.controller); + } + }, me); + + if (types.length > 1) { + for (var i = 1; i < types.length; i++) { + if (types[i] !== types[i - 1]) { + me.isCombo = true; + break; + } + } + } + + return newControllers; + }, + + resetElements: function() { + var me = this; + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + me.getDatasetMeta(datasetIndex).controller.reset(); + }, me); + }, + + update: function(animationDuration, lazy) { + var me = this; + Chart.plugins.notify('beforeUpdate', [me]); + + // In case the entire data object changed + me.tooltip._data = me.data; + + // Make sure dataset controllers are updated and new controllers are reset + var newControllers = me.buildOrUpdateControllers(); + + // Make sure all dataset controllers have correct meta data counts + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); + }, me); + + Chart.layoutService.update(me, me.chart.width, me.chart.height); + + // Apply changes to the dataets that require the scales to have been calculated i.e BorderColor chages + Chart.plugins.notify('afterScaleUpdate', [me]); + + // Can only reset the new controllers after the scales have been updated + helpers.each(newControllers, function(controller) { + controller.reset(); + }); + + me.updateDatasets(); + + // Do this before render so that any plugins that need final scale updates can use it + Chart.plugins.notify('afterUpdate', [me]); + + me.render(animationDuration, lazy); + }, + + /** + * @method beforeDatasetsUpdate + * @description Called before all datasets are updated. If a plugin returns false, + * the datasets update will be cancelled until another chart update is triggered. + * @param {Object} instance the chart instance being updated. + * @returns {Boolean} false to cancel the datasets update. + * @memberof Chart.PluginBase + * @since version 2.1.5 + * @instance + */ + + /** + * @method afterDatasetsUpdate + * @description Called after all datasets have been updated. Note that this + * extension will not be called if the datasets update has been cancelled. + * @param {Object} instance the chart instance being updated. + * @memberof Chart.PluginBase + * @since version 2.1.5 + * @instance + */ + + /** + * Updates all datasets unless a plugin returns false to the beforeDatasetsUpdate + * extension, in which case no datasets will be updated and the afterDatasetsUpdate + * notification will be skipped. + * @protected + * @instance + */ + updateDatasets: function() { + var me = this; + var i, ilen; + + if (Chart.plugins.notify('beforeDatasetsUpdate', [me])) { + for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.getDatasetMeta(i).controller.update(); + } + + Chart.plugins.notify('afterDatasetsUpdate', [me]); + } + }, + + render: function(duration, lazy) { + var me = this; + Chart.plugins.notify('beforeRender', [me]); + + var animationOptions = me.options.animation; + if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { + var animation = new Chart.Animation(); + animation.numSteps = (duration || animationOptions.duration) / 16.66; // 60 fps + animation.easing = animationOptions.easing; + + // render function + animation.render = function(chartInstance, animationObject) { + var easingFunction = helpers.easingEffects[animationObject.easing]; + var stepDecimal = animationObject.currentStep / animationObject.numSteps; + var easeDecimal = easingFunction(stepDecimal); + + chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep); + }; + + // user events + animation.onAnimationProgress = animationOptions.onProgress; + animation.onAnimationComplete = animationOptions.onComplete; + + Chart.animationService.addAnimation(me, animation, duration, lazy); + } else { + me.draw(); + if (animationOptions && animationOptions.onComplete && animationOptions.onComplete.call) { + animationOptions.onComplete.call(me); + } + } + return me; + }, + + draw: function(ease) { + var me = this; + var easingDecimal = ease || 1; + me.clear(); + + Chart.plugins.notify('beforeDraw', [me, easingDecimal]); + + // Draw all the scales + helpers.each(me.boxes, function(box) { + box.draw(me.chartArea); + }, me); + if (me.scale) { + me.scale.draw(); + } + + Chart.plugins.notify('beforeDatasetsDraw', [me, easingDecimal]); + + // Draw each dataset via its respective controller (reversed to support proper line stacking) + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + if (me.isDatasetVisible(datasetIndex)) { + me.getDatasetMeta(datasetIndex).controller.draw(ease); + } + }, me, true); + + Chart.plugins.notify('afterDatasetsDraw', [me, easingDecimal]); + + // Finally draw the tooltip + me.tooltip.transition(easingDecimal).draw(); + + Chart.plugins.notify('afterDraw', [me, easingDecimal]); + }, + + // Get the single element that was clicked on + // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw + getElementAtEvent: function(e) { + var me = this; + var eventPosition = helpers.getRelativePosition(e, me.chart); + var elementsArray = []; + + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + if (me.isDatasetVisible(datasetIndex)) { + var meta = me.getDatasetMeta(datasetIndex); + helpers.each(meta.data, function(element) { + if (element.inRange(eventPosition.x, eventPosition.y)) { + elementsArray.push(element); + return elementsArray; + } + }); + } + }); + + return elementsArray.slice(0, 1); + }, + + getElementsAtEvent: function(e) { + var me = this; + var eventPosition = helpers.getRelativePosition(e, me.chart); + var elementsArray = []; + + var found = function() { + if (me.data.datasets) { + for (var i = 0; i < me.data.datasets.length; i++) { + var meta = me.getDatasetMeta(i); + if (me.isDatasetVisible(i)) { + for (var j = 0; j < meta.data.length; j++) { + if (meta.data[j].inRange(eventPosition.x, eventPosition.y)) { + return meta.data[j]; + } + } + } + } + } + }.call(me); + + if (!found) { + return elementsArray; + } + + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + if (me.isDatasetVisible(datasetIndex)) { + var meta = me.getDatasetMeta(datasetIndex), + element = meta.data[found._index]; + if (element && !element._view.skip) { + elementsArray.push(element); + } + } + }, me); + + return elementsArray; + }, + + getElementsAtXAxis: function(e) { + var me = this; + var eventPosition = helpers.getRelativePosition(e, me.chart); + var elementsArray = []; + + var found = function() { + if (me.data.datasets) { + for (var i = 0; i < me.data.datasets.length; i++) { + var meta = me.getDatasetMeta(i); + if (me.isDatasetVisible(i)) { + for (var j = 0; j < meta.data.length; j++) { + if (meta.data[j].inLabelRange(eventPosition.x, eventPosition.y)) { + return meta.data[j]; + } + } + } + } + } + }.call(me); + + if (!found) { + return elementsArray; + } + + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + if (me.isDatasetVisible(datasetIndex)) { + var meta = me.getDatasetMeta(datasetIndex); + var index = helpers.findIndex(meta.data, function(it) { + return found._model.x === it._model.x; + }); + if (index !== -1 && !meta.data[index]._view.skip) { + elementsArray.push(meta.data[index]); + } + } + }, me); + + return elementsArray; + }, + + getElementsAtEventForMode: function(e, mode) { + var me = this; + switch (mode) { + case 'single': + return me.getElementAtEvent(e); + case 'label': + return me.getElementsAtEvent(e); + case 'dataset': + return me.getDatasetAtEvent(e); + case 'x-axis': + return me.getElementsAtXAxis(e); + default: + return e; + } + }, + + getDatasetAtEvent: function(e) { + var elementsArray = this.getElementAtEvent(e); + + if (elementsArray.length > 0) { + elementsArray = this.getDatasetMeta(elementsArray[0]._datasetIndex).data; + } + + return elementsArray; + }, + + getDatasetMeta: function(datasetIndex) { + var me = this; + var dataset = me.data.datasets[datasetIndex]; + if (!dataset._meta) { + dataset._meta = {}; + } + + var meta = dataset._meta[me.id]; + if (!meta) { + meta = dataset._meta[me.id] = { + type: null, + data: [], + dataset: null, + controller: null, + hidden: null, // See isDatasetVisible() comment + xAxisID: null, + yAxisID: null + }; + } + + return meta; + }, + + getVisibleDatasetCount: function() { + var count = 0; + for (var i = 0, ilen = this.data.datasets.length; i