/**
  * Make an AJAX request.
  *
  * @param array See $options http://docs.jquery.com/Ajax/jQuery.ajax#toptions
  * Additional options are:
  * 'document' - document for global events, @see phpQuery::getDocumentID()
  * 'referer' - implemented
  * 'requested_with' - TODO; not implemented (X-Requested-With)
  * @return Zend_Http_Client
  * @link http://docs.jquery.com/Ajax/jQuery.ajax
  *
  * @TODO $options['cache']
  * @TODO $options['processData']
  * @TODO $options['xhr']
  * @TODO $options['data'] as string
  * @TODO XHR interface
  */
 public static function ajax($options = array(), $xhr = null)
 {
     $options = array_merge(self::$ajaxSettings, $options);
     $documentID = isset($options['document']) ? self::getDocumentID($options['document']) : null;
     if ($xhr) {
         // reuse existing XHR object, but clean it up
         $client = $xhr;
         //			$client->setParameterPost(null);
         //			$client->setParameterGet(null);
         $client->setAuth(false);
         $client->setHeaders("If-Modified-Since", null);
         $client->setHeaders("Referer", null);
         $client->resetParameters();
     } else {
         // create new XHR object
         require_once 'Zend/Http/Client.php';
         $client = new Zend_Http_Client();
         $client->setCookieJar();
     }
     if (isset($options['timeout'])) {
         $client->setConfig(array('timeout' => $options['timeout']));
     }
     //			'maxredirects' => 0,
     foreach (self::$ajaxAllowedHosts as $k => $host) {
         if ($host == '.' && isset($_SERVER['HTTP_HOST'])) {
             self::$ajaxAllowedHosts[$k] = $_SERVER['HTTP_HOST'];
         }
     }
     $host = parse_url($options['url'], PHP_URL_HOST);
     if (!in_array($host, self::$ajaxAllowedHosts)) {
         throw new Exception("Request not permitted, host '{$host}' not present in " . "phpQuery::\$ajaxAllowedHosts");
     }
     // JSONP
     $jsre = "/=\\?(&|\$)/";
     if (isset($options['dataType']) && $options['dataType'] == 'jsonp') {
         $jsonpCallbackParam = $options['jsonp'] ? $options['jsonp'] : 'callback';
         if (strtolower($options['type']) == 'get') {
             if (!preg_match($jsre, $options['url'])) {
                 $sep = strpos($options['url'], '?') ? '&' : '?';
                 $options['url'] .= "{$sep}{$jsonpCallbackParam}=?";
             }
         } else {
             if ($options['data']) {
                 $jsonp = false;
                 foreach ($options['data'] as $n => $v) {
                     if ($v == '?') {
                         $jsonp = true;
                     }
                 }
                 if (!$jsonp) {
                     $options['data'][$jsonpCallbackParam] = '?';
                 }
             }
         }
         $options['dataType'] = 'json';
     }
     if (isset($options['dataType']) && $options['dataType'] == 'json') {
         $jsonpCallback = 'json_' . md5(microtime());
         $jsonpData = $jsonpUrl = false;
         if ($options['data']) {
             foreach ($options['data'] as $n => $v) {
                 if ($v == '?') {
                     $jsonpData = $n;
                 }
             }
         }
         if (preg_match($jsre, $options['url'])) {
             $jsonpUrl = true;
         }
         if ($jsonpData !== false || $jsonpUrl) {
             // remember callback name for httpData()
             $options['_jsonp'] = $jsonpCallback;
             if ($jsonpData !== false) {
                 $options['data'][$jsonpData] = $jsonpCallback;
             }
             if ($jsonpUrl) {
                 $options['url'] = preg_replace($jsre, "={$jsonpCallback}\\1", $options['url']);
             }
         }
     }
     $client->setUri($options['url']);
     $client->setMethod(strtoupper($options['type']));
     if (isset($options['referer']) && $options['referer']) {
         $client->setHeaders('Referer', $options['referer']);
     }
     $client->setHeaders(array('User-Agent' => 'Mozilla/5.0 (X11; U; Linux x86; en-US; rv:1.9.0.5) Gecko' . '/2008122010 Firefox/3.0.5', 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'Accept-Language' => 'en-us,en;q=0.5'));
     if ($options['username']) {
         $client->setAuth($options['username'], $options['password']);
     }
     if (isset($options['ifModified']) && $options['ifModified']) {
         $client->setHeaders("If-Modified-Since", self::$lastModified ? self::$lastModified : "Thu, 01 Jan 1970 00:00:00 GMT");
     }
     $client->setHeaders("Accept", isset($options['dataType']) && isset(self::$ajaxSettings['accepts'][$options['dataType']]) ? self::$ajaxSettings['accepts'][$options['dataType']] . ", */*" : self::$ajaxSettings['accepts']['_default']);
     // TODO $options['processData']
     if ($options['data'] instanceof phpQueryObject) {
         $serialized = $options['data']->serializeArray($options['data']);
         $options['data'] = array();
         foreach ($serialized as $r) {
             $options['data'][$r['name']] = $r['value'];
         }
     }
     if (strtolower($options['type']) == 'get') {
         $client->setParameterGet($options['data']);
     } else {
         if (strtolower($options['type']) == 'post') {
             $client->setEncType($options['contentType']);
             $client->setParameterPost($options['data']);
         }
     }
     if (self::$active == 0 && $options['global']) {
         phpQueryEvents::trigger($documentID, 'ajaxStart');
     }
     self::$active++;
     // beforeSend callback
     if (isset($options['beforeSend']) && $options['beforeSend']) {
         phpQuery::callbackRun($options['beforeSend'], array($client));
     }
     // ajaxSend event
     if ($options['global']) {
         phpQueryEvents::trigger($documentID, 'ajaxSend', array($client, $options));
     }
     if (phpQuery::$debug) {
         self::debug("{$options['type']}: {$options['url']}\n");
         self::debug("Options: <pre>" . var_export($options, true) . "</pre>\n");
         //			if ($client->getCookieJar())
         //				self::debug("Cookies: <pre>".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."</pre>\n");
     }
     // request
     $response = $client->request();
     if (phpQuery::$debug) {
         self::debug('Status: ' . $response->getStatus() . ' / ' . $response->getMessage());
         self::debug($client->getLastRequest());
         self::debug($response->getHeaders());
     }
     if ($response->isSuccessful()) {
         // XXX tempolary
         self::$lastModified = $response->getHeader('Last-Modified');
         $data = self::httpData($response->getBody(), $options['dataType'], $options);
         if (isset($options['success']) && $options['success']) {
             phpQuery::callbackRun($options['success'], array($data, $response->getStatus(), $options));
         }
         if ($options['global']) {
             phpQueryEvents::trigger($documentID, 'ajaxSuccess', array($client, $options));
         }
     } else {
         if (isset($options['error']) && $options['error']) {
             phpQuery::callbackRun($options['error'], array($client, $response->getStatus(), $response->getMessage()));
         }
         if ($options['global']) {
             phpQueryEvents::trigger($documentID, 'ajaxError', array($client, $response->getMessage(), $options));
         }
     }
     if (isset($options['complete']) && $options['complete']) {
         phpQuery::callbackRun($options['complete'], array($client, $response->getStatus()));
     }
     if ($options['global']) {
         phpQueryEvents::trigger($documentID, 'ajaxComplete', array($client, $options));
     }
     if ($options['global'] && !--self::$active) {
         phpQueryEvents::trigger($documentID, 'ajaxStop');
     }
     return $client;
     //		if (is_null($domId))
     //			$domId = self::$defaultDocumentID ? self::$defaultDocumentID : false;
     //		return new phpQueryAjaxResponse($response, $domId);
 }