<?php
	/**
	 * This abstract class should never be instantiated.  It contains static methods,
	 * variables and constants to be used throughout the application.
	 *
	 * The static method "Initialize" should be called at the begin of the script by
	 * prepend.inc.
	 */
	abstract class QApplicationBase extends QBaseClass {
		//////////////////////////
		// Public Static Variables
		//////////////////////////

		/**
		 * The cache provider object used for caching ORM objects
		 * It is initialized below in Initialize(), based on the CACHE_PROVIDER and CACHE_PROVIDER_OPTIONS
		 * variables defined in configuration.inc.php
		 *
		 * @var QAbstractCacheProvider 
		 */
		public static $objCacheProvider = null;

		/**
		 * @var bool Set to true to turn on short-term caching. This is an in-memory cache that caches database
		 * objects only for as long as a single http request lasts. Depending on your application, this may speed
		 * up your database accesses. It DOES increase the amount of memory used in a request.
		 * */
		public static $blnLocalCache = false;

		/**
		 * Internal bitmask signifying which BrowserType the user is using
		 * Use the QApplication::IsBrowser() method to do browser checking
		 *
		 * @var integer BrowserType
		 */
		protected static $BrowserType = QBrowserType::Unsupported;

		/**
		 * @var float Major version number of browser
		 */
		public static $BrowserVersion = null;

		/**
		 * Definition of CacheControl for the HTTP header.  In general, it is
		 * recommended to keep this as "private".  But this can/should be overriden
		 * for file/scripts that have special caching requirements (e.g. dynamically
		 * created images like QImageLabel).
		 *
		 * @var string CacheControl
		 */
		public static $CacheControl = 'private';

        /**
         * @var #P#C\QCrossScripting.Purify|?
         * Defines the default mode for controls that need protection against
         * cross-site scripting. Can be overridden at the individual control level,
         * or for all controls by overriding it in QApplication.
         *
         * Set to QCrossScripting::Legacy for backward compatibility reasons for legacy applications;
         * For new applications the recommended setting is QCrossScripting::Purify.
         */
        public static $DefaultCrossScriptingMode = QCrossScripting::Legacy;

		/**
		 * Path of the "web root" or "document root" of the web server
		 * Like "/home/www/htdocs" on Linux/Unix or "c:\inetpub\wwwroot" on Windows
		 *
		 * @var string DocumentRoot
		 */
		public static $DocumentRoot;

		/**
		 * Whether or not we are currently trying to Process the Output of the page.
		 * Used by the OutputPage PHP output_buffering handler.  As of PHP 5.2,
		 * this gets called whenever ob_get_contents() is called.  Because some
		 * classes like QFormBase utilizes ob_get_contents() to perform template
		 * evaluation without wanting to actually perform OutputPage, this flag
		 * can be set/modified by QFormBase::EvaluateTemplate accordingly to
		 * prevent OutputPage from executing.
		 *
		 * Also set this to false if you are outputting custom headers, especially
		 * if you send your own "Content-Type" header.
		 *
		 * @var boolean ProcessOutput
		 */
		public static $ProcessOutput = true;

		/**
		 * Full path of the actual PHP script being run
		 * Like "/home/www/htdocs/folder/script.php" on Linux/Unix
		 * or "c:\inetpub\wwwroot" on Windows
		 *
		 * @var string ScriptFilename
		 */
		public static $ScriptFilename;

		/**
		 * Web-relative path of the actual PHP script being run
		 * So for "http://www.domain.com/folder/script.php",
		 * QApplication::$ScriptName would be "/folder/script.php"
		 *
		 * @var string ScriptName
		 */
		public static $ScriptName;

		/**
		 * Extended Path Information after the script URL (if applicable)
		 * So for "http://www.domain.com/folder/script.php/15/225"
		 * QApplication::$PathInfo would be "/15/255"
		 *
		 * @var string PathInfo
		 */
		public static $PathInfo;

		/**
		 * Query String after the script URL (if applicable)
		 * So for "http://www.domain.com/folder/script.php?item=15&value=22"
		 * QApplication::$QueryString would be "item=15&value=22"
		 *
		 * @var string QueryString
		 */
		public static $QueryString;

		/**
		 * The full Request URI that was requested
		 * So for "http://www.domain.com/folder/script.php/15/25/?item=15&value=22"
		 * QApplication::$RequestUri would be "/folder/script.php/15/25/?item=15&value=22"
		 *
		 * @var string RequestUri
		 */
		public static $RequestUri;

		/**
		 * The IP address of the server running the script/PHP application
		 * This is either the LOCAL_ADDR or the SERVER_ADDR server constant, depending
		 * on the server type, OS and configuration.
		 *
		 * @var string ServerAddress
		 */
		public static $ServerAddress;

		/**
		 * The encoding type for the application (e.g. UTF-8, ISO-8859-1, etc.). This is the encoding that will be
		 * used for internal variables, for web pages, and what will get stored in the database. This also affects
		 * how code generation is done, so if you change this, be sure to code generate again.
		 *
		 * @var string EncodingType
		 */
		public static $EncodingType = "UTF-8";

		/**
		 * The content type to output.
		 *
		 * @var string ContentType
		 */
		public static $ContentType = "text/html";


		/**
		 * An array of Database objects, as initialized by QApplication::InitializeDatabaseConnections()
		 *
		 * @var QDatabaseBase[] Database
		 */
		public static $Database;

		/**
		 * A flag to indicate whether or not this script is run as a CLI (Command Line Interface)
		 *
		 * @var boolean CliMode
		 */
		public static $CliMode;

		/**
		 * Class File Array - used by QApplication::AutoLoad to more quickly load
		 * core class objects without making a file_exists call.
		 *
		 * @var array ClassFile
		 */
		public static $ClassFile;

		/**
		 * Preloaded Class File Array - used by QApplication::Initialize to load
		 * any core class objects during Initailize()
		 *
		 * @var array ClassFile
		 */
		public static $PreloadedClassFile;

		/**
		 * The QRequestMode enumerated value for the current request mode
		 *
		 * @var string RequestMode
		 */
		public static $RequestMode;

		/**
		 * 2-letter country code to set for internationalization and localization
		 * (e.g. us, uk, jp)
		 *
		 * @var string CountryCode
		 */
		public static $CountryCode;

		/**
		 * 2-letter language code to set for internationalization and localization
		 * (e.g. en, jp, etc.)
		 *
		 * @var string LanguageCode
		 */
		public static $LanguageCode;

		/**
		 * The instance of the active QI18n object (which contains translation strings), if any.
		 *
		 * @var QTranslationBase $LanguageObject
		 */
		public static $LanguageObject;

		/**
		 * True to force drawing to be minimized.
		 *
		 * @var bool
		 */
		public static $Minimize = false;

		////////////////////////
		// Public Overrides
		////////////////////////
		/**
		 * This faux constructor method throws a caller exception.
		 * The Application object should never be instantiated, and this constructor
		 * override simply guarantees it.
		 *
		 * @throws QCallerException
		 */
		public final function __construct() {
			throw new QCallerException('Application should never be instantiated.  All methods and variables are publically statically accessible.');
		}


		////////////////////////
		// Public Static Methods
		////////////////////////

		/**
		 * This should be the first call to initialize all the static variables
		 * The application object also has static methods that are miscellaneous web
		 * development utilities, etc.
		 *
		 * @throws Exception
		 * @return void
		 */
		public static function Initialize() {
			self::$EncodingType = defined('__QAPPLICATION_ENCODING_TYPE__') ? __QAPPLICATION_ENCODING_TYPE__ : self::$EncodingType;

			// Are we running as CLI?
			if (PHP_SAPI == 'cli')
				QApplication::$CliMode = true;
			else
				QApplication::$CliMode = false;

			// Setup Server Address
			if (array_key_exists('LOCAL_ADDR', $_SERVER))
				QApplication::$ServerAddress = $_SERVER['LOCAL_ADDR'];
			else if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER))
				QApplication::$ServerAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
			else if (array_key_exists('SERVER_ADDR', $_SERVER))
				QApplication::$ServerAddress = $_SERVER['SERVER_ADDR'];

			// Setup ScriptFilename and ScriptName
			QApplication::$ScriptFilename = $_SERVER['SCRIPT_FILENAME'];
			QApplication::$ScriptName = $_SERVER['SCRIPT_NAME'];
			
			// Ensure both are set, or we'll have to abort
			if ((!QApplication::$ScriptFilename) || (!QApplication::$ScriptName)) {
				throw new Exception('Error on QApplication::Initialize() - ScriptFilename or ScriptName was not set');
			}

			// Setup PathInfo and QueryString (if applicable)
			QApplication::$PathInfo = null;
			if(array_key_exists('PATH_INFO', $_SERVER)) {
				QApplication::$PathInfo = urlencode(trim($_SERVER['PATH_INFO']));
				QApplication::$PathInfo = str_ireplace('%2f', '/', QApplication::$PathInfo);
			}
			QApplication::$QueryString = array_key_exists('QUERY_STRING', $_SERVER) ? $_SERVER['QUERY_STRING'] : null;

			// Setup RequestUri
			if (defined('__URL_REWRITE__')) {
				switch (strtolower(__URL_REWRITE__)) {
					case 'apache':
						QApplication::$RequestUri = $_SERVER['REQUEST_URI'];
						break;

					case 'none':
						QApplication::$RequestUri = sprintf('%s%s%s',
							QApplication::$ScriptName, QApplication::$PathInfo,
							(QApplication::$QueryString) ? sprintf('?%s', QApplication::$QueryString) : null);
						break;

					default:
						throw new Exception('Invalid URL Rewrite type: ' . __URL_REWRITE__);
				}
			} else {
				QApplication::$RequestUri = sprintf('%s%s%s',
					QApplication::$ScriptName, QApplication::$PathInfo,
					(QApplication::$QueryString) ? sprintf('?%s', QApplication::$QueryString) : null);
			}

			// Setup DocumentRoot
			QApplication::$DocumentRoot = trim(__DOCROOT__);

			// Setup Browser Type
			if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) {
				$strUserAgent = trim(strtolower($_SERVER['HTTP_USER_AGENT']));

				QApplication::$BrowserType = 0;

				// INTERNET EXPLORER (versions 6 through 10)
				if (strpos($strUserAgent, 'msie') !== false) {
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::InternetExplorer;

					// just major version number. Will not see IE 10.6.
					$matches = array();
					preg_match ('#msie\s(.\d)#', $strUserAgent, $matches);
					if ($matches) {
						QApplication::$BrowserVersion = (int)$matches[1];
					}
				}
				else if (strpos($strUserAgent, 'trident') !== false) {
					// IE 11 significantly changes the user agent, and no longer includes 'MSIE'
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::InternetExplorer;

					$matches = array();
					preg_match ('/rv:(.+)\)/', $strUserAgent, $matches);
					if ($matches) {
						QApplication::$BrowserVersion = (float)$matches[1];
					}
				// FIREFOX
				} else if ((strpos($strUserAgent, 'firefox') !== false) || (strpos($strUserAgent, 'iceweasel') !== false)) {
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Firefox;
					$strUserAgent = str_replace('iceweasel/', 'firefox/', $strUserAgent);

					$matches = array();
					preg_match ('#firefox/(.+)#', $strUserAgent, $matches);
					if ($matches) {
						QApplication::$BrowserVersion = (float)$matches[1];
					}
				}
				// CHROME, must come before safari because it also includes a safari string
				elseif (strpos($strUserAgent, 'chrome') !== false) {
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Chrome;

					// find major version number only
					$matches = array();
					preg_match ('#chrome/(\d+)#', $strUserAgent, $matches);
					if ($matches) {
						QApplication::$BrowserVersion = (int)$matches[1];
					}
				}
				// SAFARI
				elseif (strpos($strUserAgent, 'safari') !== false) {
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Safari;

					$matches = array();
					preg_match ('#version/(.+)\s#', $strUserAgent, $matches);
					if ($matches) {
						QApplication::$BrowserVersion = (float)$matches[1];
					}
				}
				// KONQUEROR
				elseif (strpos($strUserAgent, 'konqueror') !== false) {
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Konqueror;

					// only looking at major version number on this one
					$matches = array();
					preg_match ('#konqueror/(\d+)#', $strUserAgent, $matches);
					if ($matches) {
						QApplication::$BrowserVersion = (int)$matches[1];
					}
				}

				// OPERA
				elseif (strpos($strUserAgent, 'opera') !== false) {
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Opera;

					// two different patterns;
					$matches = array();
					preg_match ('#version/(\d+)#', $strUserAgent, $matches);
					if ($matches) {
						QApplication::$BrowserVersion = (int)$matches[1];
					} else {
						preg_match ('#opera\s(.+)#', $strUserAgent, $matches);
						if ($matches) {
							QApplication::$BrowserVersion = (float)$matches[1];
						}
					}
				}

				// Unknown
				if (QApplication::$BrowserType == 0)
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Unsupported;

				// OS (supporting Windows, Linux and Mac)
				if (strpos($strUserAgent, 'windows') !== false)
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Windows;
				elseif (strpos($strUserAgent, 'linux') !== false)
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Linux;
				elseif (strpos($strUserAgent, 'macintosh') !== false)
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Macintosh;

				// Mobile version of one of the above browsers, or some other unknown browser
				if (strpos($strUserAgent, 'mobi') !== false) // opera is just 'mobi', everyone else uses 'mobile'
					QApplication::$BrowserType = QApplication::$BrowserType | QBrowserType::Mobile;
			}

			// Preload Class Files
			foreach (QApplication::$PreloadedClassFile as $strClassFile) {
				require($strClassFile);
			}

			// Initialize any classes that might call into the autoloader
			$strCacheProviderClass = 'QCacheProviderNoCache';
			if (defined('CACHE_PROVIDER_CLASS')) {
				$strCacheProviderClass = CACHE_PROVIDER_CLASS;
			}
			if ($strCacheProviderClass) {
				if (defined('CACHE_PROVIDER_OPTIONS')) {
					QApplicationBase::$objCacheProvider = new $strCacheProviderClass(unserialize(CACHE_PROVIDER_OPTIONS));
				} else {
					QApplicationBase::$objCacheProvider = new $strCacheProviderClass();
				}
			}

			if (defined('__MINIMIZE__') && __MINIMIZE__) {
				QApplicationBase::$Minimize = true;
			}
		}

		/**
		 * Checks for the type of browser in use by the client.
		 * @static
		 * @param int $intBrowserType
		 * @return int
		 */
		public static function IsBrowser($intBrowserType) {
			return ($intBrowserType & QApplication::$BrowserType);
		}

		/**
		 * This call will initialize the database connection(s) as defined by
		 * the constants DB_CONNECTION_X, where "X" is the index number of a
		 * particular database connection.
		 *
		 * @throws Exception
		 * @return void
		 */
		public static function InitializeDatabaseConnections() {
			// for backward compatibility, don't use MAX_DB_CONNECTION_INDEX directly,
			// but check if MAX_DB_CONNECTION_INDEX is defined
			$intMaxIndex = defined('MAX_DB_CONNECTION_INDEX') ? constant('MAX_DB_CONNECTION_INDEX') : 9;

			if (defined('DB_CONNECTION_0')) {
				// This causes a conflict with how DbBackedSessionHandler works.
				throw new Exception('Do not define DB_CONNECTION_0. Start at DB_CONNECTION_1');
			}

			for ($intIndex = 1; $intIndex <= $intMaxIndex; $intIndex++) {
				$strConstantName = sprintf('DB_CONNECTION_%s', $intIndex);

				if (defined($strConstantName)) {
					// Expected Keys to be Set
					$strExpectedKeys = array(
						'adapter', 'server', 'port', 'database',
						'username', 'password', 'profiling', 'dateformat'
					);

					// Lookup the Serialized Array from the DB_CONFIG constants and unserialize it
					$strSerialArray = constant($strConstantName);
					$objConfigArray = unserialize($strSerialArray);

					// Set All Expected Keys
					foreach ($strExpectedKeys as $strExpectedKey)
						if (!array_key_exists($strExpectedKey, $objConfigArray))
							$objConfigArray[$strExpectedKey] = null;

					if (!$objConfigArray['adapter'])
						throw new Exception('No Adapter Defined for ' . $strConstantName . ': ' . var_export($objConfigArray, true));

					if (!$objConfigArray['server'])
						throw new Exception('No Server Defined for ' . $strConstantName . ': ' . constant($strConstantName));

					$strDatabaseType = 'Q' . $objConfigArray['adapter'] . 'Database';
					if (!class_exists($strDatabaseType)) {
						$strDatabaseAdapter = sprintf('%s/database/%s.class.php', __QCUBED_CORE__, $strDatabaseType);
						if (!file_exists($strDatabaseAdapter))
							throw new Exception('Database Type is not valid: ' . $objConfigArray['adapter']);
						require($strDatabaseAdapter);
					}

					QApplication::$Database[$intIndex] = new $strDatabaseType($intIndex, $objConfigArray);
				}
			}
		}

		public static function SessionOverride() {
			// Are we using QDbBackedSessionHandler?
			if (defined("DB_BACKED_SESSION_HANDLER_DB_INDEX") &&
					constant("DB_BACKED_SESSION_HANDLER_DB_INDEX") != 0 && defined("DB_BACKED_SESSION_HANDLER_TABLE_NAME")) {
				// Yes we are going to override PHP's default file based handlers.
				QDbBackedSessionHandler::Initialize(DB_BACKED_SESSION_HANDLER_DB_INDEX, DB_BACKED_SESSION_HANDLER_TABLE_NAME);
			}
		}

		/**
		 * This is called by the PHP5 Autoloader.  This static method can be overridden.
		 *
		 * @param $strClassName
		 * @return boolean whether or not a class was found / included
		 */
		public static function Autoload($strClassName) {
			if (isset(QApplication::$ClassFile[strtolower($strClassName)])) {
				require_once (QApplication::$ClassFile[strtolower($strClassName)]);
				return true;
			} else if (file_exists($strFilePath = sprintf('%s/%s.class.php', __INCLUDES__, $strClassName))) {
				require_once ($strFilePath);
				return true;
			} else if (file_exists($strFilePath = sprintf('%s/controls/%s.class.php', __INCLUDES__, $strClassName))) {
				require_once ($strFilePath);
				return true;
			} else if (file_exists($strFilePath = sprintf('%s/plugins/%s.php', __INCLUDES__, $strClassName))) {
				require_once ($strFilePath);
				return true;
			} else if (false !== ($intStart = strpos($strClassName, 'QCubed\\Plugin\\'))) {
                $strClassName = substr($strClassName, $intStart + 14);
                if (file_exists($strFilePath = sprintf('%s/plugins/%s.php', __INCLUDES__, $strClassName))) {
				    require_once ($strFilePath);
				    return true;
				}
			}

			return false;
		}

		/**
		 * Temprorarily overrides the default error handling mechanism.  Remember to call
		 * RestoreErrorHandler to restore the error handler back to the default.
		 *
		 * @param string  $strName  the name of the new error handler function, or NULL if none
		 * @param integer $intLevel if a error handler function is defined, then the new error reporting level (if any)
		 *
		 * @throws QCallerException
		 */
		public static function SetErrorHandler($strName, $intLevel = null) {
			if (!is_null(QApplicationBase::$intStoredErrorLevel))
				throw new QCallerException('Error handler is already currently overridden.  Cannot override twice.  Call RestoreErrorHandler before calling SetErrorHandler again.');
			if (!$strName) {
				// No Error Handling is wanted -- simulate a "On Error, Resume" type of functionality
				set_error_handler('QcubedHandleError', 0);
				QApplicationBase::$intStoredErrorLevel = error_reporting(0);
			} else {
				set_error_handler($strName, $intLevel);
				QApplicationBase::$intStoredErrorLevel = -1;
			}
		}

		/**
		 * Restores the temporarily overridden default error handling mechanism back to the default.
		 */
		public static function RestoreErrorHandler() {
			if (is_null(QApplicationBase::$intStoredErrorLevel))
				throw new QCallerException('Error handler is not currently overridden.  Cannot reset something that was never overridden.');
			if (QApplicationBase::$intStoredErrorLevel != -1)
				error_reporting(QApplicationBase::$intStoredErrorLevel);
			restore_error_handler();
			QApplicationBase::$intStoredErrorLevel = null;
		}

		/** @var null|int Stored Error Level (used for Settings and Restoring error handler) */
		private static $intStoredErrorLevel = null;

		/**
		 * Create a directory on file system
		 *
		 * @param string   $strPath Path of the directory to be created
		 * @param null|int $intMode Octal representation of permissions ('0755' style)
		 *
		 * @return bool
		 */
		public static function MakeDirectory($strPath, $intMode = null) {
			return QFolder::MakeDirectory($strPath, $intMode);
		}


		/**
		 * This will redirect the user to a new web location.  This can be a relative or absolute web path, or it
		 * can be an entire URL.
		 *
		 * TODO: break this into two routines, since the resulting UI behavior is really different. Redirect and LoadPage??
		 *
		 * @param string $strLocation target patch
		 * @param bool $blnAbortCurrentScript Whether to abort the current script, or finish it out so data gets saved.
		 * @return void
		 */
		public static function Redirect($strLocation, $blnAbortCurrentScript = true) {

			if (!$blnAbortCurrentScript) {
				// Use the javascript command mechanism
				QApplication::$JavascriptCommandArray[QAjaxResponse::Location] = $strLocation;
			}
			else {
				global $_FORM;

				if ($_FORM) {
					$_FORM->SaveControlState();
				}

				// Clear the output buffer (if any)
				ob_clean();

				if ((QApplication::$RequestMode == QRequestMode::Ajax) ||
					(array_key_exists('Qform__FormCallType', $_POST) &&
						($_POST['Qform__FormCallType'] == QCallType::Ajax))) {
					QApplication::SendAjaxResponse(array(QAjaxResponse::Location => $strLocation));
				} else {
					// Was "DOCUMENT_ROOT" set?
					if (array_key_exists('DOCUMENT_ROOT', $_SERVER) && ($_SERVER['DOCUMENT_ROOT'])) {
						// If so, we're likely using PHP as a Plugin/Module
						// Use 'header' to redirect
						header(sprintf('Location: %s', $strLocation));
					} else {
						// We're likely using this as a CGI
						// Use JavaScript to redirect
						printf('<script type="text/javascript">document.location = "%s";</script>', $strLocation);
					}
				}

				// End the Response Script
				session_write_close();
				exit();
			}
		}


		/**
		 * This will close the window.
		 *
		 * @param bool $blnAbortCurrentScript Whether to abort the current script, or finish it out so data gets saved.
		 * @return void
		 */
		public static function CloseWindow($blnAbortCurrentScript = false) {
			if (!$blnAbortCurrentScript) {
				// Use the javascript command mechanism
				QApplication::$JavascriptCommandArray[QAjaxResponse::Close] = true;
			}
			else {
				// Clear the output buffer (if any)
				ob_clean();

				if (QApplication::$RequestMode == QRequestMode::Ajax) {
					// AJAX-based Response
					$aResponse[QAjaxResponse::Close] = 1;
					QApplication::SendAjaxResponse($aResponse);
				} else {
					// Use JavaScript to close
					_p('<script type="text/javascript">window.close();</script>', false);
				}

				// End the Response Script
				exit();
			}
		}

		/**
		 * Set a cookie. Allows setting of cookies in responses to ajax requests.
		 *
		 * @param string $strName
		 * @param sring $strValue
		 * @param QDatTime $dttTimeout
		 * @param string $strPath
		 * @param null|string $strDomain
		 * @param bool $blnSecure
		 */
		public static function SetCookie($strName, $strValue, QDateTime $dttTimeout, $strPath = '/', $strDomain = null, $blnSecure = false) {
			if (QApplication::$RequestMode == QRequestMode::Ajax) {
				self::ExecuteJsFunction ('qcubed.setCookie', $strName, $strValue, $dttTimeout, $strPath, $strDomain, $blnSecure);
			}
			else {
				setcookie($strName, $strValue, $dttTimeout->Timestamp, $strPath, $strDomain, $blnSecure);
			}
		}

		/**
		 * Delete's the given cookie IF its set. In other words, you cannot set a cookie and then delete a cookie right away before the
		 * cookie gets sent to the browser.
		 *
		 * @param $strName
		 */
		public static function DeleteCookie($strName) {
			if (isset($_COOKIE[$strName])) { // don't post a cookie if its not set
				$dttTimeout = QDateTime::Now();
				$dttTimeout->AddYears(-5);

				self::SetCookie($strName, "", $dttTimeout);
			}
		}

		/**
		 * Gets the value of the QueryString item $strItem.  Will return NULL if it doesn't exist.
		 *
		 * @param string $strItem the parameter name
		 *
		 * @return string value of the parameter
		 */
		public static function QueryString($strItem) {
			if (array_key_exists($strItem, $_GET))
				return $_GET[$strItem];
			else
				return null;
		}

		/**
		 * Generates a valid URL Query String based on values in the provided array. If no array is provided, it uses the global $_GET
		 * @param array $arr
		 * @return string
		 */
		public static function GenerateQueryString($arr = null) {
			if(null === $arr)
				$arr = $_GET;
			if (count($arr)) {
				$strToReturn = '';
				foreach ($arr as $strKey => $mixValue)
					$strToReturn .= QApplication::GenerateQueryStringHelper(urlencode($strKey), $mixValue);
				return '?' . substr($strToReturn, 1);
			} else
				return '';
		}

		/**
		 * Generates part of query string (helps in generating the complete query string)
		 * @param string $strKey Key for the query string
		 * @param string|integer|array $mixValue Value we have to put as the value of the key
		 *
		 * @return null|string
		 */
		protected static function GenerateQueryStringHelper($strKey, $mixValue) {
			if (is_array($mixValue)) {
				$strToReturn = null;
				foreach ($mixValue as $strSubKey => $mixSubValue) {
					$strToReturn .= QApplication::GenerateQueryStringHelper($strKey . '[' . $strSubKey . ']', $mixSubValue);
				}
				return $strToReturn;
			} else
				return '&' . $strKey . '=' . urlencode($mixValue);
		}

		/**
		 * By default, this is used by the codegen and form drafts to do a quick check
		 * on the ALLOW_REMOTE_ADMIN constant (as defined in configuration.inc.php).  If enabled,
		 * then anyone can access the page.  If disabled, only "localhost" can access the page.
		 * If you want to run a script that should be accessible regardless of
		 * ALLOW_REMOTE_ADMIN, simply remove the CheckRemoteAdmin() method call from that script.
		 *
		 * @throws QRemoteAdminDeniedException
		 * @return void
		 */
		public static function CheckRemoteAdmin() {
			if (!QApplication::IsRemoteAdminSession()) {
				return;
			}

			// If we're here -- then we're not allowed to access.  Present the Error/Issue.
			header($_SERVER['SERVER_PROTOCOL'] . ' 401 Access Denied');
			header('Status: 401 Access Denied', true);

			throw new QRemoteAdminDeniedException();
		}

		/**
		 * Checks whether the current request was made by an ADMIN
		 * This does not refer to your Database admin or an Admin user defined in your application but an IP address
		 * (or IP address range) defined in configuration.inc.php.
		 *
		 * The function can be used to restrict access to sensitive pages to a list of IPs (or IP ranges), such as the LAN to which
		 * the server hosting the QCubed application is connected.
		 * @static
		 * @return bool
		 */
		public static function IsRemoteAdminSession() {
			// Allow Remote?
			if (ALLOW_REMOTE_ADMIN === true)
				return false;

			// Are we localhost?
			if (substr($_SERVER['REMOTE_ADDR'],0,4) == '127.' || $_SERVER['REMOTE_ADDR'] == '::1')
				return false;

			// Are we the correct IP?
			if (is_string(ALLOW_REMOTE_ADMIN))
				foreach (explode(',', ALLOW_REMOTE_ADMIN) as $strIpAddress) {
					if (QApplication::IsIPInRange($_SERVER['REMOTE_ADDR'], $strIpAddress) ||
						(array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) && (QApplication::IsIPInRange($_SERVER['HTTP_X_FORWARDED_FOR'], $strIpAddress)))) {
						return false;
					}
				}
					
			return true;
		}

		/**
		 * Checks whether the given IP falls into the given IP range
		 * @static
		 * @param string $ip the IP number to check
		 * @param string $range the IP number range. The range could be in 'IP/mask' or 'IP - IP' format. mask could be a simple
		 * integer or a dotted netmask.
		 * @return bool
		 */
		public static function IsIPInRange($ip, $range) {
			$ip = trim($ip);
			if (strpos($range, '/') !== false) {
				// we are given a IP/mask
				list($net, $mask) = explode('/', $range);
				$net = ip2long(trim($net));
				$mask = trim($mask);
				//$ip_net = ip2long($net);
				if (strpos($mask, '.') !== false) {
					// mask has the dotted notation
					$ip_mask = ip2long($mask);
				} else {
					// mask is an integer
					$ip_mask = ~((1 << (32 - $mask)) - 1);
				}
				$ip = ip2long($ip);
				return ($net & $ip_mask) == ($ip & $ip_mask);
			}
			if (strpos($range, '-') !== false) {
				// we are given an IP - IP range
				list($first, $last) = explode('-', $range);
				$first = ip2long(trim($first));
				$last = ip2long(trim($last));
				$ip = ip2long($ip);
				return $first <= $ip && $ip <= $last;
			}

			// $range is a simple IP
			return $ip == trim($range);
		}

		/**
		 * Gets the value of the PathInfo item at index $intIndex.  Will return NULL if it doesn't exist.
		 *
		 * The way PathInfo index is determined is, for example, given a URL '/folder/page.php/id/15/blue',
		 * QApplication::PathInfo(0) will return 'id'
		 * QApplication::PathInfo(1) will return '15'
		 * QApplication::PathInfo(2) will return 'blue'
		 *
		 * @param int $intIndex index
		 * @return string|null
		 */
		public static function PathInfo($intIndex) {
			// TODO: Cache PathInfo
			$strPathInfo = urldecode(QApplication::$PathInfo);
			
			// Remove Starting '/'
			if (QString::FirstCharacter($strPathInfo) == '/')			
				$strPathInfo = substr($strPathInfo, 1);
			
			$strPathInfoArray = explode('/', $strPathInfo);

			if (array_key_exists($intIndex, $strPathInfoArray))
				return $strPathInfoArray[$intIndex];
			else
				return null;
		}

		/**
		 * If this particular item is set, we ensure that this command, and only this command will get invoked on the
		 * next response. The rest of the commands will wait until the next response.
		 *
		 * @var null|array;
		 */
		public static $JavascriptExclusiveCommand = null;

		/** @var array A structured array of commands to be sent to either the ajax response, or page output.
		 * Replaces the AlertMessageArray, JavaScriptArray, JavaScriptArrayHighPriority, and JavaScriptArrayLowPriority.
		 */
		protected static $JavascriptCommandArray = array();

		/** @var array JS files to be added to the list of files in front of the javascript commands. Should include jquery, etc. */
		protected static $JavascriptFileArray = array();

		/*
				public static $AlertMessageArray = array();
				public static $JavaScriptArray = array();
				public static $JavaScriptArrayHighPriority = array();
				public static $JavaScriptArrayLowPriority = array();
				public static $ControlCommands = array();*/

		/** @var bool Used to determine if an error has occurred */
		public static $ErrorFlag = false;

		/**
		 * Causes the browser to display a JavaScript alert() box with supplied message
		 * @param string $strMessage Message to be displayed
		 */
		public static function DisplayAlert($strMessage) {
			QApplication::$JavascriptCommandArray[QAjaxResponse::Alert][] = $strMessage;
		}

		/**
		 * This class can be used to call a Javascript function in the client browser from the server side.
		 * Can be used inside event handlers to do something after verification  on server side.
		 *
		 * TODO: Since this is implemented with an "eval" on the client side in ajax, we should phase this out in favor
		 * of specific commands sent to the client.
		 *
		 * @static
		 * @deprecated Will be eventually removed. If you need to do something in javascript, add it to QAjaxResponse.
		 * @param string $strJavaScript the javascript to execute
		 * @param string $strPriority
		 * @throws QCallerException
		 */
		public static function ExecuteJavaScript($strJavaScript, $strPriority = QJsPriority::Standard) {
			if (is_bool($strPriority)) {
				//we keep this codepath for backward compatibility
				if ($strPriority === true) {
					throw new QCallerException('Please specify a correct priority value');
				}
			} else {
				switch ($strPriority) {
					case QJsPriority::High:
						QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsHigh][] = ['script'=>$strJavaScript];
						break;
					case QJsPriority::Low:
						QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsLow][] = ['script'=>$strJavaScript];
						break;
					case QJsPriority::Exclusive:
						QApplication::$JavascriptExclusiveCommand = ['script'=>$strJavaScript];
						break;
					default:
						QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsMedium][] = ['script'=>$strJavaScript];
						break;
				}
			}
		}

		/**
		 * Execute a function on a particular control. Many javascript widgets are structured this way, and this gives us
		 * a general purpose way of sending commands to widgets without an 'eval' on the client side.
		 *
		 * Commands will be executed in the order received, along with ExecuteJavaScript commands and ExecuteObjectCommands.
		 * If you want to force a command to execute first, give it high priority, or last, give it low priority.
		 *
		 * @param string $strControlId			Id of control to direct the command to.
		 * @param string $strFunctionName		Function name to call. For jQueryUI, this would be the widget name
		 * @param string $strFunctionName,...   Unlimited OPTIONAL parameters to use as a parameter list to the function. List can
		 * 										end with a QJsPriority to prioritize the command.
		 */
		public static function ExecuteControlCommand ($strControlId, $strFunctionName /*, ..., QJsPriority */) {
			$args = func_get_args();
			$args[0] = '#' . $strControlId;
			call_user_func_array('QApplication::ExecuteSelectorFunction', $args);
		}

		/**
		 * Call a function on a jQuery selector. The selector can be a single string, or an array where the first
		 * item is a selector specifying the items within the context of the second selector.
		 *
		 * @param array|string $mixSelector
		 * @param string $strFunctionName
		 * @param string $strFunctionName,...   Unlimited OPTIONAL parameters to use as a parameter list to the function. List can
		 * 										end with a QJsPriority to prioritize the command.
		 * @throws QCallerException
		 */
		public static function ExecuteSelectorFunction ($mixSelector, $strFunctionName /*, ..., QJsPriority */) {
			if (!(is_string($mixSelector) || (is_array($mixSelector) && count($mixSelector) == 2))) {
				throw new QCallerException ('Selector must be a string or an array of two items');
			}
			$args = func_get_args();
			array_shift ($args);
			array_shift ($args);
			if ($args && end($args) === QJsPriority::High) {
				$code = QAjaxResponse::CommandsHigh;
				array_pop($args);
			}
			elseif ($args && end($args) === QJsPriority::Low) {
				$code = QAjaxResponse::CommandsLow;
				array_pop($args);
			}
			elseif ($args && end($args) === QJsPriority::Exclusive) {
				array_pop($args);
				QApplication::$JavascriptExclusiveCommand = ['selector'=>$mixSelector, 'func'=>$strFunctionName, 'params'=>$args];
				return;
			}
			elseif ($args && end($args) === QJsPriority::Last) {
				array_pop($args);
				QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsFinal][] = ['selector'=>$mixSelector, 'func'=>$strFunctionName, 'params'=>$args, 'final'=>true];
				return;
			}
			else {
				$code = QAjaxResponse::CommandsMedium;
			}
			if (empty($args)) {
				$args = null;
			}

			QApplication::$JavascriptCommandArray[$code][] = ['selector'=>$mixSelector, 'func'=>$strFunctionName, 'params'=>$args];
		}


		/**
		 * Call the given function with the given arguments. If just a function name, then the window object is searched.
		 * The function can be inside an object accessible from the global namespace by separating with periods.
		 * @param string $strFunctionName Can be namespaced, as in "qcubed.func".
		 * @param string $strFunctionName,...   Unlimited OPTIONAL parameters to use as a parameter list to the function. List can
		 * 										end with a QJsPriority to prioritize the command.
		 */
		public static function ExecuteJsFunction($strFunctionName /*, ... */) {
			$args = func_get_args();
			array_shift ($args);
			if ($args && end($args) === QJsPriority::High) {
				$code = QAjaxResponse::CommandsHigh;
				array_pop($args);
			}
			elseif ($args && end($args) === QJsPriority::Low) {
				$code = QAjaxResponse::CommandsLow;
				array_pop($args);
			}
			elseif ($args && end($args) === QJsPriority::Exclusive) {
				array_pop($args);
				QApplication::$JavascriptExclusiveCommand = ['func'=>$strFunctionName, 'params'=>$args];
				return;
			}
			else {
				$code = QAjaxResponse::CommandsMedium;
			}
			if (empty($args)) {
				$args = null;
			}

			QApplication::$JavascriptCommandArray[$code][] = ['func'=>$strFunctionName, 'params'=>$args];
		}

		/**
		 * One time add of style sheets, to be used by QForm only for last minute style sheet injection.
		 * @param string[] $strStyleSheetArray
		 */
		public static function AddStyleSheets (array $strStyleSheetArray) {
			if (empty(QApplication::$JavascriptCommandArray[QAjaxResponse::StyleSheets])) {
				QApplication::$JavascriptCommandArray[QAjaxResponse::StyleSheets] = $strStyleSheetArray;
			}
			else {
				QApplication::$JavascriptCommandArray[QAjaxResponse::StyleSheets] =
					array_merge (QApplication::$JavascriptCommandArray[QAjaxResponse::StyleSheets], $strStyleSheetArray);
			}
		}

		/**
		 * Add an array of javascript files for one-time inclusion. Called by QForm. Do not call.
		 * @param string[] $strJavaScriptFileArray
		 */
		public static function AddJavaScriptFiles ($strJavaScriptFileArray) {
			if (empty(QApplication::$JavascriptFileArray[QAjaxResponse::JavaScripts])) {
				QApplication::$JavascriptFileArray[QAjaxResponse::JavaScripts] = $strJavaScriptFileArray;
			}
			else {
				QApplication::$JavascriptFileArray[QAjaxResponse::JavaScripts] =
					array_merge (QApplication::$JavascriptFileArray[QAjaxResponse::JavaScripts], $strJavaScriptFileArray);
			}
		}

		/**
		 * Outputs the current page with the buffer data
		 * @param string $strBuffer Buffer data
		 *
		 * @return string
		 */
		public static function OutputPage($strBuffer) {
			// If the ProcessOutput flag is set to false, simply return the buffer
			// without processing anything.
			if (!QApplication::$ProcessOutput)
				return $strBuffer;

			if (QApplication::$ErrorFlag) {
				return $strBuffer;
			} else {
				if (QApplication::$RequestMode == QRequestMode::Ajax) {
					return trim($strBuffer);
				} else {
					// Update Cache-Control setting
					header('Cache-Control: ' . QApplication::$CacheControl);
					// make sure the server does not override the character encoding value by explicitly sending it out as a header.
					// some servers will use an internal default if not specified in the header, and that will override the "encoding" value sent in the text.
					header(sprintf('Content-Type: %s; charset=%s', strtolower(QApplication::$ContentType), strtolower(QApplication::$EncodingType)));

					/*
					 * Normally, FormBase->RenderEnd will render the javascripts. In the unusual case
					 * of not rendering with a QForm object, this will still output embedded javascript commands.
					 */
					$strScript = QApplicationBase::RenderJavascript();
					if ($strScript) {
						return $strBuffer . '<script type="text/javascript">' . $strScript . '</script>';
					}

					return $strBuffer;
				}
			}
		}

		public static function StartOutputBuffering() {
			if (php_sapi_name() !== 'cli' &&	// Do not buffer the command line interface
				!defined('__NO_OUTPUT_BUFFER__')) {

				ob_start('QApplicationBase::EndOutputBuffering');
			}
		}

		public static function EndOutputBuffering($strBuffer) {
			return QApplication::OutputPage($strBuffer);
		}


		/**
		 * Render scripts for injecting files into the html output. This is for server only, not ajax.
		 * This list will appear ahead of the javascript commands rendered below.
		 *
		 * @static
		 * @return string
		 */
		public static function RenderFiles() {
			$strScript = '';

			// Javascript files should get processed before the commands.
			if (!empty(QApplication::$JavascriptFileArray[QAjaxResponse::JavaScripts])) {
				foreach (QApplication::$JavascriptFileArray[QAjaxResponse::JavaScripts] as $js) {
					$strScript .= sprintf('<script type="text/javascript" src="%s"></script>', QApplication::GetJsFileUri($js)) . "\n";
				}
			}

			QApplication::$JavascriptFileArray = array();

			return $strScript;
		}

		/**
		 * Function renders all the Javascript commands as output to the client browser. This is a mirror of what
		 * occurs in the success function in the qcubed.js ajax code.
		 *
		 * @param $blnBeforeControls	True to only render the javascripts that need to come before the controls are defined.
		 * 								This is used to break the commands issued into two groups.
		 * @static
		 * @return string
		 */
		public static function RenderJavascript($blnBeforeControls = false) {
			$strScript = '';

			// Style sheet injection by a control. Not very common, as other ways of adding style sheets would normally be done first.
			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::StyleSheets])) {
				$str = '';
				foreach (QApplication::$JavascriptCommandArray[QAjaxResponse::StyleSheets] as $ss) {
					$str .= 'qc.loadStyleSheetFile("' . $ss . '", "all"); ';
				}
				QApplication::$JavascriptCommandArray[QAjaxResponse::StyleSheets] = null;
			}

			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::Alert])) {
				foreach (QApplication::$JavascriptCommandArray[QAjaxResponse::Alert] as $strAlert) {
					$strAlert = json_encode($strAlert);
					$strScript .= sprintf('alert(%s); ', $strAlert);
				}
				QApplication::$JavascriptCommandArray[QAjaxResponse::Alert] = null;
			}

			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsHigh])) {
				$strScript .= self::RenderCommandArray(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsHigh]);
				QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsHigh] = null;
			}

			if ($blnBeforeControls) return $strScript;	// When we call again, everything above here will be skipped since we are emptying the arrays

			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsMedium])) {
				$strScript .= self::RenderCommandArray(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsMedium]);
				QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsMedium] = null;
			}

			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsLow])) {
				$strScript .= self::RenderCommandArray(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsLow]);
			}

			// A QApplication::Redirect
			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::Location])) {
				$strLocation = QApplication::$JavascriptCommandArray[QAjaxResponse::Location];
				$strScript .= sprintf('document.location = "%s";', $strLocation);
			}
			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::Close])) {
				$strScript .= 'window.close();';
			}

			QApplication::$JavascriptCommandArray = array();

			return $strScript;
		}

		private static function RenderCommandArray(array $commandArray) {
			$strScript = '';
			foreach ($commandArray as $command) {
				if (isset($command['script'])) {	// a script to use eval on
					$strScript .= sprintf('%s;', $command['script']) . _nl();
				}
				elseif (isset($command['selector'])) {	// a control function
					if (is_array($command['selector'])) {
						$strSelector = sprintf('"%s", "%s"', $command['selector'][0], $command['selector'][1]);
					}
					else {
						$strSelector = '"' . $command['selector'] . '"';
					}

					if ($params = $command['params']) {
						$objParams = new QJsParameterList($params);
						$strParams = $objParams->toJsObject();
					} else {
						$strParams = '';
					}
					$strScript .= sprintf ('jQuery(%s).%s(%s);', $strSelector, $command['func'], $strParams) . _nl();
				}
				elseif (isset($command['func'])) {	// a function call
					if ($params = $command['params']) {
						$objParams = new QJsParameterList($params);
						$strParams = $objParams->toJsObject();
					}
					else {
						$strParams = '';
					}
					$strScript .= sprintf ('%s(%s);', $command['func'], $strParams)  . _nl();
				}
			}
			return $strScript;
		}

		/**
		 * Return the javascript command array, for use by form ajax response. Will erase the command array, so
		 * the form better use it.
		 * @static
		 * @return array
		 */
		public static function GetJavascriptCommandArray() {

			if (QApplication::$JavascriptExclusiveCommand) {
				// only render this one;
				$a[QAjaxResponse::CommandsMedium] = [QApplication::$JavascriptExclusiveCommand];
				QApplication::$JavascriptExclusiveCommand = null;
				return $a;
			}

			// Combine the javascripts into one array item
			$scripts = array();
			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsMedium])) {
				$scripts = QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsMedium];
			}
			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsHigh])) {
				$scripts = array_merge (QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsHigh], $scripts);
				unset (QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsHigh]);
			}
			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsLow])) {
				$scripts = array_merge ($scripts, QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsLow]);
				unset (QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsLow]);
			}
			if (!empty(QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsFinal])) {
				$scripts = array_merge ($scripts, QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsFinal]);
				unset (QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsFinal]);
			}
			if ($scripts) {
				QApplication::$JavascriptCommandArray[QAjaxResponse::CommandsMedium] = $scripts;
			}

			// add the file inclusion array onto the front of the command array
			$a = array_merge(QApplication::$JavascriptFileArray, QApplication::$JavascriptCommandArray);
			QApplication::$JavascriptFileArray = array();
			QApplication::$JavascriptCommandArray = array();
			return $a;
		}


  		/**
		 * If LanguageCode is specified and QI18n::Initialize() has been called, then this
		 * will perform a translation of the given token for the specified Language Code and optional
		 * Country code.
		 *
		 * Otherwise, this will simply return the token as is.
		 * This method is also used by the global print-translated "_t" function.
		 *
		 * @static
		 * @param string $strToken
		 * @return string the Translated token (if applicable)
		 */
		public static function Translate($strToken) {
			if (QApplication::$LanguageObject)
				return QApplication::$LanguageObject->TranslateToken($strToken);
			else
				return $strToken;
		}

		/**
		 * Global/Central HtmlEntities command to perform the PHP equivalent of htmlentities.
		 * Feel free to override to specify encoding/quoting specific preferences (e.g. ENT_QUOTES/ENT_NOQUOTES, etc.)
		 *
		 * Be careful of one thing though. This now uses ENT_HTML5, to correspond with the default page DOCTYPE. This
		 * has the added benefit of encoding newlines in data sent to controls. In particular, a multi-line textbox
		 * needs to have newlines encoded to prevent problems when the output is formatted using _indent().
		 * 
		 * This method is also used by the global print "_p" function.
		 *
		 * @param string $strText text string to perform html escaping
		 * @return string the html escaped string
		 */
		public static function HtmlEntities($strText) {
			return htmlentities($strText, ENT_COMPAT | ENT_HTML5, QApplication::$EncodingType);
		}

		/**
		 * Print an ajax response to the browser.
		 *
		 * @param array $strResponseArray An array keyed with QAjaxResponse items. These items will be read by the qcubed.js
		 * ajax success function and operated on. The goals is to eventually have all possible response types represented
		 * in the QAjaxResponse so that we can remove the "eval" in qcubed.js.
		 */
		public static function SendAjaxResponse(array $strResponseArray) {
			header('Content-Type: text/json'); // not application/json, as IE reportedly blows up on that, but jQuery knows what to do.
			$strJSON = JavascriptHelper::toJSON($strResponseArray);
			if (QApplication::$EncodingType && QApplication::$EncodingType != 'UTF-8') {
				$strJSON = iconv(QApplication::$EncodingType, 'UTF-8', $strJSON); // json must be UTF-8 encoded
			}
			print ($strJSON);
		}


		/**
		 * Utility function to get the JS file URI, given a string input
		 * @param string $strFile File name to be tested
		 *
		 * @return string the final JS file URI
		 */
		public static function GetJsFileUri($strFile) {
			if ((strpos($strFile, "http") === 0) || (strpos($strFile, "https") === 0))
				return $strFile;
			if (strpos($strFile, "/") === 0)
				return __VIRTUAL_DIRECTORY__ . $strFile;
			return __VIRTUAL_DIRECTORY__ . __JS_ASSETS__ . '/' . $strFile;
		}

		/**
		 * Utility function to get the CSS file URI, given a string input
		 * @param string $strFile File name to be tested
		 *
		 * @return string the final CSS URI
		 */
		public static function GetCssFileUri($strFile) {
			if ((strpos($strFile, "http") === 0) || (strpos($strFile, "https") === 0))
				return $strFile;
			if (strpos($strFile, "/") === 0)
				return __VIRTUAL_DIRECTORY__ . $strFile;
			return __VIRTUAL_DIRECTORY__ . __CSS_ASSETS__ . '/' . $strFile;
		}

		/**
		 * For development purposes, this static method outputs all the Application static variables
		 *
		 * @return void
		 */
		public static function VarDump() {
			_p('<div class="var-dump"><strong>QCubed Settings</strong><ul>', false);
			$arrValidationErrors = QInstallationValidator::Validate();
			foreach ($arrValidationErrors as $objResult) {
				printf('<li><strong class="warning">WARNING:</strong> %s</li>', $objResult->strMessage);
			}

			printf('<li>QCUBED_VERSION = "%s"</li>', QCUBED_VERSION);
			printf('<li>jQuery version = "%s"</li>', __JQUERY_CORE_VERSION__);
			printf('<li>jQuery UI version = "%s"</li>', __JQUERY_UI_VERSION__);
			printf('<li>__SUBDIRECTORY__ = "%s"</li>', __SUBDIRECTORY__);
			printf('<li>__VIRTUAL_DIRECTORY__ = "%s"</li>', __VIRTUAL_DIRECTORY__);
			printf('<li>__INCLUDES__ = "%s"</li>', __INCLUDES__);
			printf('<li>__QCUBED_CORE__ = "%s"</li>', __QCUBED_CORE__);
			printf('<li>ERROR_PAGE_PATH = "%s"</li>', ERROR_PAGE_PATH);
			printf('<li>PHP Include Path = "%s"</li>', get_include_path());
			printf('<li>QApplication::$DocumentRoot = "%s"</li>', QApplication::$DocumentRoot);
			printf('<li>QApplication::$EncodingType = "%s"</li>', QApplication::$EncodingType);
			printf('<li>QApplication::$PathInfo = "%s"</li>', QApplication::$PathInfo);
			printf('<li>QApplication::$QueryString = "%s"</li>', QApplication::$QueryString);
			printf('<li>QApplication::$RequestUri = "%s"</li>', QApplication::$RequestUri);
			printf('<li>QApplication::$ScriptFilename = "%s"</li>', QApplication::$ScriptFilename);
			printf('<li>QApplication::$ScriptName = "%s"</li>', QApplication::$ScriptName);
			printf('<li>QApplication::$ServerAddress = "%s"</li>', QApplication::$ServerAddress);

			if (QApplication::$Database) foreach (QApplication::$Database as $intKey => $objObject) {
				printf('<li>QApplication::$Database[%s] settings:</li>', $intKey);
				_p("<ul>", false);				
				foreach (unserialize(constant('DB_CONNECTION_' . $intKey)) as $key => $value) {
					if ($key == "password") {
						$value = "hidden for security purposes";
					}
					
					_p("<li>" . $key. " = " . var_export($value, true). "</li>", false);
				}
				_p("</ul>", false);
					
			}
			_p('</ul></div>', false);
		}		
	}

	/**
	 * Class for enumerating Javascript priority.
	 * These are taken out of a parameter list, and so are very unlikely strings to include normally.
	 */
	class QJsPriority {
		/** Standard Priority */
		const Standard = '*jsMed*';
		/** High prioriy JS */
		const High = '*jsHigh*';
		/** Low Priority JS */
		const Low = '*jsLow*';
		/** Execute ONLY this command and exclude all others */
		const Exclusive = '*jsExclusive*';
		/** Execute this command after all ajax commands have been completely flushed */
		const Last = '*jsFinal*';
	}

	/**
	 * This is an enumerator class for listing Request Modes
	 */
	class QRequestMode {
		/** Normal request (initial request) */
		const Standard = 'Standard';
		/** Ajax Request (mostly calls triggered by events) */
		const Ajax = 'Ajax';
	}

	/**
	 * Class QBrowserType: Type of browsers we can identify
	 */
	class QBrowserType {
		/** IE */
		const InternetExplorer = 1;

		/* Deprecated. See QApplication::BrowserVersion **
		const InternetExplorer_6_0 = 2;
		const InternetExplorer_7_0 = 4;
		const InternetExplorer_8_0 = 8;*/

		/** Firefox  */
		const Firefox = 	0x10;

		/* Deprecated. See QApplication::BrowserVersion **
		const Firefox_1_0 = 0x20;
		const Firefox_1_5 = 0x40;
		const Firefox_2_0 = 0x80;
		const Firefox_3_0 = 0x100;*/

		/** Apple's Safari */
		const Safari = 		0x200;
		/* Deprecated. See QApplication::BrowserVersion **
		const Safari_2_0 = 	0x400;
		const Safari_3_0 = 	0x800;
		const Safari_4_0 = 	0x1000;*/

		/** Browser */
		const Opera = 		0x2000;
		/* Deprecated. See QApplication::BrowserVersion **
		const Opera_7 = 	0x4000;
		const Opera_8 = 	0x8000;
		const Opera_9 = 	0x10000;*/

		/** KDE's failed rocket that never took off */
		const Konqueror = 	0x20000;
		/* Deprecated. See QApplication::BrowserVersion **
		const Konqueror_3 = 0x40000;
		const Konqueror_4 = 0x80000;*/

		/** Google Chrome (and chromium) */
		const Chrome = 		0x100000;
		/* Deprecated. See QApplication::BrowserVersion **
		const Chrome_0 = 	0x200000;
		const Chrome_1 = 	0x400000;*/

		/** Windows OS */
		const Windows = 	0x800000;
		/** Linux based OS */
		const Linux = 		0x1000000;
		/** Apple's OS X */
		const Macintosh = 	0x2000000;
		/** Some kind of Mobile browser */
		const Mobile = 		0x4000000;	// some kind of mobile browser

		/** We don't know this gentleman...err...gentlebrowser */
		const Unsupported = 0x8000000;
	}
