]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-16780 Merging from MOODLE_19_STABLE
authornicolasconnault <nicolasconnault>
Fri, 3 Oct 2008 07:13:16 +0000 (07:13 +0000)
committernicolasconnault <nicolasconnault>
Fri, 3 Oct 2008 07:13:16 +0000 (07:13 +0000)
14 files changed:
group/index.php
lib/pear/HTML/AJAX/Action.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Debug.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Helper.php [new file with mode: 0755]
lib/pear/HTML/AJAX/JSON.php [moved from lib/json/JSON.php with 92% similarity, mode: 0755]
lib/pear/HTML/AJAX/Response.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Serializer/Error.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Serializer/JSON.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Serializer/Null.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Serializer/PHP.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Serializer/Urlencoded.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Serializer/XML.php [new file with mode: 0755]
lib/pear/HTML/AJAX/Server.php [new file with mode: 0755]
lib/pear/README.txt

index 62b66f528d8391091cca57d99e97e668c71751a2..de4039c7b09db2c615038b0271165ff64bd6ffad 100644 (file)
@@ -10,7 +10,7 @@
  */
 require_once('../config.php');
 require_once('lib.php');
-require_once($CFG->libdir.'/json/JSON.php');
+require_once($CFG->libdir.'/pear/HTML/AJAX/JSON.php');
 
 require_js(array('yui_yahoo', 'yui_dom', 'yui_utilities', 'yui_connection'));
 require_js('group/clientlib.js');
@@ -58,8 +58,7 @@ switch ($action) {
                 $roles[]=$shortroledata;
             }
         }
-        $json = new Services_JSON();
-        echo $json->encode($roles);
+        echo json_encode($roles);
         die;  // Client side JavaScript takes it from here.
 
     case 'deletegroup':
@@ -205,11 +204,11 @@ if ($sel_groupid) {
                 echo '<option value="'.$member->id.'">'.fullname($member, true).'</option>';
                 $atleastonemember = true;
             }
-            echo '</optgroup>';        
+            echo '</optgroup>';
         }
-    } 
+    }
 }
-    
+
 if (!$atleastonemember) {
     // Print an empty option to avoid the XHTML error of having an empty select element
     echo '<option>&nbsp;</option>';
diff --git a/lib/pear/HTML/AJAX/Action.php b/lib/pear/HTML/AJAX/Action.php
new file mode 100755 (executable)
index 0000000..77ef9f5
--- /dev/null
@@ -0,0 +1,357 @@
+<?php
+/**
+ * OO AJAX Implementation for PHP, contains HTML_AJAX_Action
+ *
+ * SVN Rev: $Id$
+ *
+ * @category  HTML
+ * @package   AJAX
+ * @author    Elizabeth Smith <auroraeosrose@gmail.com>
+ * @copyright 2005-2008 Elizabeth Smith
+ * @license   http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version   Release: 0.5.6
+ * @link      http://htmlajax.org/HTML_AJAX/Using%20haSerializer
+ */
+
+/**
+ * Require the response class and json serializer
+ */
+require_once 'HTML/AJAX/Response.php';
+require_once 'HTML/AJAX/Serializer/JSON.php';
+
+/**
+ * Helper class to eliminate the need to write javascript functions to deal with data
+ *
+ * This class creates information that can be properly serialized and used by
+ * the haaction serializer which eliminates the need for php users to write 
+ * javascript for dealing with the information returned by an ajax method - 
+ * instead the javascript is basically created for them
+ *
+ * @category  HTML
+ * @package   AJAX
+ * @author    Elizabeth Smith <auroraeosrose@gmail.com>
+ * @copyright 2005-2008 Elizabeth Smith
+ * @license   http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version   Release: 0.5.6
+ * @link      http://htmlajax.org/HTML_AJAX/Using%20haSerializer
+ */
+class HTML_AJAX_Action extends HTML_AJAX_Response
+{
+
+    /**
+     * Content type for the HAA response
+     *
+     * goofy but unique content type to tell the javascript which deserializer to use
+     * overrides HTML_AJAX_Response
+     *
+     * @var string
+     * @access public
+     */
+    var $contentType = 'application/html_ajax_action';
+
+    /**
+     * An array holding all the actions for the class
+     *
+     * these have numeric keys and each new action is added on the end, remember
+     * these are executed in the order added
+     *
+     * @var array
+     * @access private
+     */
+    var $_actions = array();
+
+    /**
+     * Prepends data to the attribute identified by id
+     *
+     * The data will be added to the beginning of the attribute identified by the id
+     * sent, id must be unique
+     *
+     * $response->prependAttr('myid', 'class', 'red');
+     * $response->prependAttr('myid', array('class' => 'red', 'innerHTML' => 'this is an error'));
+     *
+     * @param string       $id        id for a specific item on the page <div id="myid"></div>
+     * @param string|array $attribute either an array of attribute/data pairs or a string attribute name
+     * @param mixed        $data      should be NULL if attribute is an array, otherwise data you wish to set the attribute to
+     *
+     * @return void
+     * @access public
+     */
+    function prependAttr($id, $attribute, $data = null)
+    {
+        if (!is_null($data)) {
+            $attribute = array($attribute => $data);
+        }
+        $this->_actions[] = array(
+            'action' => 'prepend',
+            'id' => $id,
+            'attributes' => $attribute,
+            'data' => $data,
+        );
+        return;
+    }
+
+    /**
+     * Appends data to the attribute identified by id
+     *
+     * The data will be added to the end of the attribute identified by the id
+     * sent, id must be unique
+     *
+     * $response->appendAttr('myid', 'class', 'red');
+     * $response->appendAttr('myid', array('class' => 'red', 'innerHTML' => 'this is an error'));
+     *
+     * @param string       $id        id for a specific item on the page <div id="myid"></div>
+     * @param string|array $attribute either an array of attribute/data pairs or a string attribute name
+     * @param mixed        $data      should be NULL if attribute is an array, otherwise data you wish to set the attribute to
+     *
+     * @return void
+     * @access public
+     */
+    function appendAttr($id, $attribute, $data = null)
+    {
+        if (!is_null($data)) {
+            $attribute = array($attribute => $data);
+        }
+        $this->_actions[] = array(
+            'action' => 'append',
+            'id' => $id,
+            'attributes' => $attribute,
+        );
+        return;
+    }
+
+    /**
+     * Assigns data to the attribute identified by id overwriting any previous values
+     *
+     * The data will be assigned to the attribute identified by the id
+     * sent, id must be unique
+     *
+     * $response->assignAttr('myid', 'class', 'red');
+     * $response->assignAttr('myid', array('class' => 'red', 'innerHTML' => 'this is an error'));
+     *
+     * @param string       $id        id for a specific item on the page <div id="myid"></div>
+     * @param string|array $attribute either an array of attribute/data pairs or a string attribute name
+     * @param mixed        $data      should be NULL if attribute is an array, otherwise data you wish to set the attribute to
+     *
+     * @return void
+     * @access public
+     */
+    function assignAttr($id, $attribute, $data = null)
+    {
+        if (!is_null($data)) {
+            $attribute = array($attribute => $data);
+        }
+        $this->_actions[] = array(
+            'action' => 'assign',
+            'id' => $id,
+            'attributes' => $attribute,
+        );
+        return;
+    }
+
+    /**
+     * Deletes or assigns a value of an empty string to an attribute
+     *
+     * You may send either a single attribute or an array of attributes to clear
+     *
+     * $response->clearAttr('myid', 'class');
+     * $response->clearAttr('myid', array('class', 'innerHTML'));
+     *
+     * @param string       $id        id for a specific item on the page <div id="myid"></div>
+     * @param string|array $attribute either an array of attribute/data pairs or a string attribute name
+     *
+     * @return void
+     * @access public
+     */
+    function clearAttr($id, $attribute)
+    {
+        if (!is_array($attribute)) {
+            $attribute = array($attribute);
+        }
+        $this->_actions[] = array(
+            'action' => 'clear',
+            'id' => $id,
+            'attributes' => $attribute,
+        );
+        return;
+    }
+
+    /**
+     * create a dom node via javascript
+     *
+     * higher level dom manipulation - creates a new node to insert into the dom
+     * You can control where the new node is inserted with two things, the insertion
+     * type and the id/  The type should be append, prepend, insertBefore, or insertAfter
+     *
+     * The id is a sibling node - like a div in the same div you want to add more to
+     * If you choose to append or prepend a node it will be placed at the beginning
+     * or end of the node with the id you send. If you choose insertBefore or
+     * InsertAfter it will be put right before or right after the node you specified.
+     * You can send an array of attributes to apply to the new node as well,
+     * so you don't have to create it and then assign Attributes.
+     *
+     * <code>
+     * $response->createNode('myid', 'div');
+     * $response->createNode('submit', 'input',
+     *   array('id' => 'key',
+     *         'name' => 'key',
+     *         'type' => 'hidden',
+     *         'value' => $id),
+     *   'insertBefore');
+     * <code>
+     *
+     * @param string $id         id for a specific item on the page <div id="myid"></div>
+     * @param string $tag        html node to create
+     * @param array  $attributes array of attribute -> data to fill the node with
+     * @param string $type       append|prepend|insertBefore|insertAfter default is append
+     *
+     * @return void
+     * @access public
+     */
+    function createNode($id, $tag, $attributes, $type = 'append')
+    {
+        $types = array('append', 'prepend', 'insertBefore', 'insertAfter');
+        if (!in_array($type, $types)) {
+            $type = 'append';
+        }
+        settype($attributes, 'array');
+        $this->_actions[] = array(
+            'action' => 'create',
+            'id' => $id,
+            'tag' => $tag,
+            'attributes' => $attributes,
+            'type' => $type,
+        );
+        return;
+    }
+
+    /**
+     * Replace a dom node via javascript
+     *
+     * higher level dom manipulation - replaces one node with another
+     * This can be used to replace a div with a form for inline editing
+     * use innerHtml attribute to change inside text
+     *
+     * $response->replaceNode('myid', 'div', array('innerHTML' => 'loading complete'));
+     * $response->replaceNode('mydiv', 'form', array('innerHTML' => $form));
+     *
+     * @param string $id         id for a specific item on the page <div id="myid"></div>
+     * @param string $tag        html node to create
+     * @param array  $attributes array of attribute -> data to fill the node with
+     *
+     * @return void
+     * @access public
+     */
+    function replaceNode($id, $tag, $attributes)
+    {
+        settype($attributes, 'array');
+        $this->_actions[] = array(
+            'action' => 'replace',
+            'id' => $id,
+            'tag' => $tag,
+            'attributes' => $attributes,
+        );
+        return;
+    }
+
+    /**
+     * Delete a dom node via javascript
+     *
+     * $response->removeNode('myid');
+     * $response->removeNode(array('mydiv', 'myform'));
+     *
+     * @param string $id id for a specific item on the page <div id="myid"></div>
+     *
+     * @return void
+     * @access public
+     */
+    function removeNode($id)
+    {
+        $this->_actions[] = array(
+            'action' => 'remove',
+            'id' => $id,
+        );
+        return;
+    }
+
+    /**
+     * Send a string to a javascript eval
+     *
+     * This will send the data right to the eval javascript function, it will NOT
+     * allow you to dynamically add a javascript function for use later on because
+     * it is constrined by the eval function
+     *
+     * @param string $data string to pass to the alert javascript function
+     *
+     * @return void
+     * @access public
+     */
+    function insertScript($data)
+    {
+        $this->_actions[] = array(
+            'action' => 'script',
+            'data' => $data,
+        );
+        return;
+    }
+
+    /**
+     * Send a string to a javascript alert
+     *
+     * This will send the data right to the alert javascript function
+     *
+     * @param string $data string to pass to the alert javascript function
+     *
+     * @return void
+     * @access public
+     */
+    function insertAlert($data)
+    {
+        $this->_actions[] = array(
+            'action' => 'alert',
+            'data' => $data,
+        );
+        return;
+    }
+
+    /**
+     * Returns the serialized content of the response class
+     *
+     * we actually use the json serializer underneath, so we send the actions array
+     * to the json serializer and return the data
+     *
+     * @return string serialized response content
+     * @access public
+     */
+    function getPayload()
+    {
+        $serializer = new HTML_AJAX_Serializer_JSON();
+        return $serializer->serialize($this->_actions);
+    }
+
+    /**
+     * Adds all the actions from one response object to another, feature request
+     * #6635 at pear.php.net
+     *
+     * @param object &$instance referenced HTML_AJAX_Action object
+     *
+     * @return array
+     * @access public
+     */
+    function combineActions(&$instance)
+    {
+        $this->_actions = array_merge($this->_actions, $instance->retrieveActions());
+    }
+
+    /**
+     * to follow proper property access we need a way to retrieve the private
+     * actions array
+     *
+     * @return  array
+     * @access public
+     */
+    function retrieveActions()
+    {
+        return $this->_actions;
+    }
+}
+?>
diff --git a/lib/pear/HTML/AJAX/Debug.php b/lib/pear/HTML/AJAX/Debug.php
new file mode 100755 (executable)
index 0000000..7e11f7d
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+/**
+ * AJAX Debugging implementation
+ *
+ * SVN Rev: $Id$
+ */
+
+/**
+ * Newline to use
+ */
+define ("HTML_AJAX_NEWLINE", "\n");
+
+// {{{ class HTML_AJAX_Debug
+/**
+ * AJAX Debugging implementation
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     David Coallier <davidc@php.net>
+ * @copyright  2005 David Coallier 
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ */
+class HTML_AJAX_Debug {
+    // {{{ properties
+    /**
+     * This is the error message.
+     *
+     * @access private
+     */
+    var $errorMsg;
+
+    /**
+     * The line where the error occured.
+     *
+     * @access private
+     */
+    var $errorLine;
+
+    /**
+     * The error code.
+     *
+     * @access private
+     */
+    var $errorCode;
+    
+    /**
+     * The file where the error occured.
+     *
+     * @access private
+     */
+    var $errorFile;
+
+    /**
+     * Time the error occured
+     *
+     * @access private
+     */
+    var $_timeOccured;
+
+    /**
+     * The whole error itself
+     *
+     * @access private
+     * @see errorMsg
+     * @see errorLine
+     * @see errorFile
+     * @see errorCode
+     */
+    var $error;
+
+    /**
+     * The file to save the error to.
+     *
+     * @access private
+     * @default ajaxErrLog.xml
+     */
+    var $file = 'ajaxErrLog.xml';
+    // }}}
+    // {{{ constructor
+    /**
+     * The constructor.
+     *
+     * @param string $errorMsg   The error message.
+     * @param string $errLine    The line where error occured.
+     * @param string $errCode    The error Code.
+     * @param string $errFile    The file where error occured.
+     */
+    function HTML_AJAX_Debug($errMsg, $errLine, $errCode, $errFile)
+    {
+        $this->errorMsg    = $errMsg;
+        $this->errorLine   = $errLine;
+        $this->errorCode   = $errCode;
+        $this->errorFile   = $errFile;
+        $this->_timeOccured = date("Y-m-d H:i:s", time());
+        $this->xmlError();
+    }
+    // }}}
+    // {{{ xmlError
+    /**
+     * This functions formats the error to xml format then we can save it.
+     *
+     * @access protected
+     * @return $this->error   the main error.
+     */
+    function xmlError()
+    {
+        $error  = " <when>{$this->_timeOccured}</when>" . HTML_AJAX_NEWLINE;
+        $error .= " <msg>{$this->errorMsg}</msg>"       . HTML_AJAX_NEWLINE;
+        $error .= " <code>{$this->errorCode}</code>"    . HTML_AJAX_NEWLINE;
+        $error .= " <line>{$this->errorLine}</line>"    . HTML_AJAX_NEWLINE;
+        $error .= " <file>{$this->errorFile}</file>"    . HTML_AJAX_NEWLINE . HTML_AJAX_NEWLINE;
+        return $this->error = $error; 
+    }
+    // }}}
+    // {{{ sessionError
+    /**
+     * This function pushes the array $_SESSION['html_ajax_debug']['time'][]
+     * with the values inside of $this->error
+     *
+     * @access public
+     */
+    function sessionError() 
+    {
+        $_SESSION['html_ajax_debug']['time'][] = $this->error;
+    }
+    // }}}
+    // {{{ _saveError
+    /**
+     * This function saves the error to a file
+     * appending to this file.
+     *
+     * @access private.
+     */
+    function _saveError()
+    {
+        if ($handle = fopen($this->file, 'a')) {
+            fwrite($handle, $this->error);
+        }
+    }
+    // }}}
+}
+// }}}
+?>
diff --git a/lib/pear/HTML/AJAX/Helper.php b/lib/pear/HTML/AJAX/Helper.php
new file mode 100755 (executable)
index 0000000..ecf5497
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+/**
+ * HTML/JavaScript Generation Helper
+ *
+ * SVN Rev: $Id$
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Joshua Eichorn <josh@bluga.net>
+ * @copyright  2005 Joshua Eichorn
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ */
+
+/**
+ * HTML/JavaScript Generation Helper
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Joshua Eichorn <josh@bluga.net>
+ * @copyright  2005 Joshua Eichorn
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ * @link       http://pear.php.net/package/HTML_AJAX
+ */
+class HTML_AJAX_Helper 
+{
+       /**
+        * URL where an HTML_AJAX_Server instance is serving up clients and taking ajax requests
+        */
+       var $serverUrl = 'server.php';
+
+       /**
+        * JS libraries to include
+        *
+        * @var array
+        */
+       var $jsLibraries = array('Util','Main','Request','HttpClient','Dispatcher','Behavior','Loading','JSON','iframe');
+
+       /**
+        * Remote class stubs to include
+        */
+       var $stubs = array();
+
+    /**
+     *  Combine jsLibraries into a single require and remove duplicates
+     */
+    var $combineJsIncludes = false;
+
+       /**
+        * Include all needed libraries, stubs, and set defaultServer
+        *
+        * @return      string
+        */
+       function setupAJAX() 
+       {
+               $libs = array(0=>array());
+        $combinedLibs = array();
+
+        $this->jsLibraries = array_unique($this->jsLibraries);
+               foreach($this->jsLibraries as $library) {
+                       if (is_array($library)) {
+                $library = array_unique($library);
+                $combinedLibs = array_merge($combinedLibs,$library);
+                               $libs[] = implode(',',$library);
+                       }
+                       else {
+                               $libs[0][] = $library;
+                $combinedLibs[] = $library;
+                       }
+               }
+               $libs[0] = implode(',',$libs[0]);
+
+        $sep = '?';
+        if (strstr($this->serverUrl,'?')) {
+            $sep = '&';
+        }
+
+               $ret = '';
+        if ($this->combineJsIncludes == true) {
+            $list = implode(',',$combinedLibs);
+            $ret .= "<script type='text/javascript' src='{$this->serverUrl}{$sep}client={$list}'></script>\n";
+        } 
+        else {
+            foreach($libs as $list) {
+                $ret .= "<script type='text/javascript' src='{$this->serverUrl}{$sep}client={$list}'></script>\n";
+            }
+        }
+
+               if (count($this->stubs) > 0) {
+                       $stubs = implode(',',$this->stubs);
+                       $ret .= "<script type='text/javascript' src='{$this->serverUrl}{$sep}stub={$stubs}'></script>\n";
+               }
+               $ret .= $this->encloseInScript('HTML_AJAX.defaultServerUrl = '.$this->escape($this->serverUrl));
+               return $ret;
+       }
+
+       /**
+        * Create a custom Loading message
+        *
+        * @param string        $body   HTML body of the loading div
+        * @param string        $class  CSS class of the div
+        * @param string        $style  style tag of the loading div
+        */
+       function loadingMessage($body, $class = 'HTML_AJAX_Loading', 
+                       $style = 'position: absolute; top: 0; right: 0; background-color: red; width: 80px; padding: 4px; display: none') 
+       {
+               return "<div id='HTML_AJAX_LOADING' class='{$class}' style=\"{$style}\">{$body}</div>\n";
+       }
+
+       /**
+        * Update the contents of an element using ajax
+        *
+        * @param string        $id     id of the element to update
+        * @param string|array  $update Either a url to update with or a array like array('class','method')
+        * @param string        $type   replace or append
+        * @param boolean       $enclose
+        */
+       function updateElement($id, $update, $type, $enclose = false) {
+               if (is_array($update)) {
+                       $updateStr = "";
+                       $comma = '';
+                       foreach($update as $item) {
+                               $updateStr .= $comma.$this->escape($item);
+                               $comma = ',';
+                       }
+               }
+               else {
+                       $updateStr = $this->escape($update);
+               }
+
+               $ret = "HTML_AJAX.{$type}(".$this->escape($id).",{$updateStr});\n";
+               if ($enclose) {
+                       $ret = $this->encloseInScript($ret);
+               }
+               return $ret;
+       }
+
+       /**
+        * Escape a string and add quotes allowing it to be a javascript paramater
+        *
+        * @param string        $input
+        * @return string
+        * @todo do something here besides a quick hack
+        */
+       function escape($input) {
+               return "'".addslashes($input)."'";
+       }
+
+       /**
+        * Enclose a string in a script block
+        *
+        * @param string        $input
+        * @return string
+        */
+       function encloseInScript($input) {
+               return '<script type="text/javascript">'.$input."</script>\n";
+       }
+
+       /**
+        * Generate a JSON String
+        *
+        * @param string        $input
+        * @return string
+        */
+       function jsonEncode($input) {
+               require_once 'HTML/AJAX/Serializer/JSON.php';
+
+               $s = new HTML_AJAX_Serializer_JSON();
+               return $s->serialize($input);
+       }
+
+       /**
+        * Check the request headers to see if this is an AJAX request
+     *
+     * @return boolean
+     */
+    function isAJAX() {
+        if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
+            return true;
+        }
+        return false;
+    }
+}
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+?>
old mode 100644 (file)
new mode 100755 (executable)
similarity index 92%
rename from lib/json/JSON.php
rename to lib/pear/HTML/AJAX/JSON.php
index 8283e7e..afdf03b
@@ -1,5 +1,17 @@
 <?php
-/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+/**
+ * This is an embedded version of HTML_AJAX_JSON since it has yet to have
+ * a PEAR release it has been renamed to HTML_AJAX_JSON so no problems
+ * will be caused by an eventual release
+ * Feel free to report bugs against it to HTML_AJAX
+ *
+ * SVN Rev: $Id$
+ */
+
+/**
+ * Needed for compat functions
+ */
+require_once 'HTML/AJAX.php';
 
 /**
  * Converts to and from JSON format.
  * DAMAGE.
  *
  * @category
- * @package     Services_JSON
+ * @package     HTML_AJAX_JSON
  * @author      Michal Migurski <mike-json@teczno.com>
  * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
  * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
  * @copyright   2005 Michal Migurski
- * @version     CVS: $Id$
  * @license     http://www.opensource.org/licenses/bsd-license.php
  * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
  */
 
 /**
- * Marker constant for Services_JSON::decode(), used to flag stack state
+ * Marker constant for HTML_AJAX_JSON::decode(), used to flag stack state
  */
 define('SERVICES_JSON_SLICE',   1);
 
 /**
- * Marker constant for Services_JSON::decode(), used to flag stack state
+ * Marker constant for HTML_AJAX_JSON::decode(), used to flag stack state
  */
 define('SERVICES_JSON_IN_STR',  2);
 
 /**
- * Marker constant for Services_JSON::decode(), used to flag stack state
+ * Marker constant for HTML_AJAX_JSON::decode(), used to flag stack state
  */
 define('SERVICES_JSON_IN_ARR',  3);
 
 /**
- * Marker constant for Services_JSON::decode(), used to flag stack state
+ * Marker constant for HTML_AJAX_JSON::decode(), used to flag stack state
  */
 define('SERVICES_JSON_IN_OBJ',  4);
 
 /**
- * Marker constant for Services_JSON::decode(), used to flag stack state
+ * Marker constant for HTML_AJAX_JSON::decode(), used to flag stack state
  */
 define('SERVICES_JSON_IN_CMT', 5);
 
 /**
- * Behavior switch for Services_JSON::decode()
+ * Behavior switch for HTML_AJAX_JSON::decode()
  */
 define('SERVICES_JSON_LOOSE_TYPE', 16);
 
 /**
- * Behavior switch for Services_JSON::decode()
+ * Behavior switch for HTML_AJAX_JSON::decode()
  */
 define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
 
@@ -97,8 +108,8 @@ define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
  * Brief example of use:
  *
  * <code>
- * // create a new instance of Services_JSON
- * $json = new Services_JSON();
+ * // create a new instance of HTML_AJAX_JSON
+ * $json = new HTML_AJAX_JSON();
  *
  * // convert a complexe value to JSON notation, and send it to the browser
  * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
@@ -112,7 +123,7 @@ define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
  * $value = $json->decode($input);
  * </code>
  */
-class Services_JSON
+class HTML_AJAX_JSON
 {
    /**
     * constructs a new JSON instance
@@ -130,7 +141,7 @@ class Services_JSON
     *                                   bubble up with an error, so all return values
     *                                   from encode() should be checked with isError()
     */
-    function Services_JSON($use = 0)
+    function HTML_AJAX_JSON($use = 0)
     {
         $this->use = $use;
     }
@@ -149,9 +160,8 @@ class Services_JSON
     function utf162utf8($utf16)
     {
         // oh please oh please oh please oh please oh please
-        if(function_exists('mb_convert_encoding')) {
+        if(function_exists('mb_convert_encoding'))
             return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
-        }
 
         $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
 
@@ -193,9 +203,8 @@ class Services_JSON
     function utf82utf16($utf8)
     {
         // oh please oh please oh please oh please oh please
-        if(function_exists('mb_convert_encoding')) {
+        if(function_exists('mb_convert_encoding'))
             return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
-        }
 
         switch(strlen($utf8)) {
             case 1:
@@ -227,7 +236,7 @@ class Services_JSON
     * encodes an arbitrary variable into JSON format
     *
     * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
-    *                           see argument 1 to Services_JSON() above for array-parsing behavior.
+    *                           see argument 1 to HTML_AJAX_JSON() above for array-parsing behavior.
     *                           if var is a strng, note that encode() always expects it
     *                           to be in ASCII or UTF-8 format!
     *
@@ -380,11 +389,9 @@ class Services_JSON
                                             array_keys($var),
                                             array_values($var));
 
-                    foreach($properties as $property) {
-                        if(Services_JSON::isError($property)) {
+                    foreach($properties as $property)
+                        if(HTML_AJAX_JSON::isError($property))
                             return $property;
-                        }
-                    }
 
                     return '{' . join(',', $properties) . '}';
                 }
@@ -392,11 +399,9 @@ class Services_JSON
                 // treat it like a regular array
                 $elements = array_map(array($this, 'encode'), $var);
 
-                foreach($elements as $element) {
-                    if(Services_JSON::isError($element)) {
+                foreach($elements as $element)
+                    if(HTML_AJAX_JSON::isError($element))
                         return $element;
-                    }
-                }
 
                 return '[' . join(',', $elements) . ']';
 
@@ -407,18 +412,16 @@ class Services_JSON
                                         array_keys($vars),
                                         array_values($vars));
 
-                foreach($properties as $property) {
-                    if(Services_JSON::isError($property)) {
+                foreach($properties as $property)
+                    if(HTML_AJAX_JSON::isError($property))
                         return $property;
-                    }
-                }
 
                 return '{' . join(',', $properties) . '}';
 
             default:
                 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
                     ? 'null'
-                    : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
+                    : new HTML_AJAX_JSON_Error(gettype($var)." can not be encoded as JSON string");
         }
     }
 
@@ -435,9 +438,8 @@ class Services_JSON
     {
         $encoded_value = $this->encode($value);
 
-        if(Services_JSON::isError($encoded_value)) {
+        if(HTML_AJAX_JSON::isError($encoded_value))
             return $encoded_value;
-        }
 
         return $this->encode(strval($name)) . ':' . $encoded_value;
     }
@@ -476,7 +478,7 @@ class Services_JSON
     *
     * @return   mixed   number, boolean, string, array, or object
     *                   corresponding to given JSON input string.
-    *                   See argument 1 to Services_JSON() above for object-output behavior.
+    *                   See argument 1 to HTML_AJAX_JSON() above for object-output behavior.
     *                   Note that decode() always returns strings
     *                   in ASCII or UTF-8 format!
     * @access   public
@@ -496,8 +498,6 @@ class Services_JSON
                 return null;
 
             default:
-                $m = array();
-
                 if (is_numeric($str)) {
                     // Lookie-loo, it's a number
 
@@ -665,8 +665,6 @@ class Services_JSON
                                 // out the property name and set an
                                 // element in an associative array,
                                 // for now
-                                $parts = array();
-                                
                                 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
                                     // "name":value pair
                                     $key = $this->decode($parts[1]);
@@ -698,10 +696,9 @@ class Services_JSON
 
                         } elseif (($chrs{$c} == $top['delim']) &&
                                  ($top['what'] == SERVICES_JSON_IN_STR) &&
-                                 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
+                                 (($chrs{$c - 1} != '\\') ||
+                                 ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) {
                             // found a quote, we're in a string, and it's not escaped
-                            // we know that it's not escaped becase there is _not_ an
-                            // odd number of backslashes at the end of the string so far
                             array_pop($stk);
                             //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
 
@@ -765,7 +762,7 @@ class Services_JSON
      */
     function isError($data, $code = null)
     {
-        if (class_exists('pear')) {
+        if (HTML_AJAX_class_exists('pear', false)) {
             return PEAR::isError($data, $code);
         } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
                                  is_subclass_of($data, 'services_json_error'))) {
@@ -776,11 +773,11 @@ class Services_JSON
     }
 }
 
-if (class_exists('PEAR_Error')) {
+if (HTML_AJAX_class_exists('pear_error', false)) {
 
-    class Services_JSON_Error extends PEAR_Error
+    class HTML_AJAX_JSON_Error extends PEAR_Error
     {
-        function Services_JSON_Error($message = 'unknown error', $code = null,
+        function HTML_AJAX_JSON_Error($message = 'unknown error', $code = null,
                                      $mode = null, $options = null, $userinfo = null)
         {
             parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
@@ -792,9 +789,9 @@ if (class_exists('PEAR_Error')) {
     /**
      * @todo Ultimately, this class shall be descended from PEAR_Error
      */
-    class Services_JSON_Error
+    class HTML_AJAX_JSON_Error
     {
-        function Services_JSON_Error($message = 'unknown error', $code = null,
+        function HTML_AJAX_JSON_Error($message = 'unknown error', $code = null,
                                      $mode = null, $options = null, $userinfo = null)
         {
 
@@ -802,5 +799,21 @@ if (class_exists('PEAR_Error')) {
     }
 
 }
-    
+
+// Future-friendly json_encode
+if( !function_exists('json_encode') ) {
+       function json_encode($data) {
+        $json = new HTML_AJAX_JSON();
+        return( $json->encode($data) );
+    }
+}
+
+// Future-friendly json_decode
+if( !function_exists('json_decode') ) {
+       function json_decode($data) {
+        $json = new HTML_AJAX_JSON();
+        return( $json->decode($data) );
+    }
+}
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 ?>
diff --git a/lib/pear/HTML/AJAX/Response.php b/lib/pear/HTML/AJAX/Response.php
new file mode 100755 (executable)
index 0000000..d3deb08
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/**
+ * OO AJAX Implementation for PHP, contains HTML_AJAX_Response
+ *
+ * SVN Rev: $Id$ 
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Elizabeth Smith <auroraeosrose@gmail.com>
+ * @copyright  2005-2006 Elizabeth Smith
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ */
+
+/**
+ * Require the main AJAX library
+ */
+require_once 'HTML/AJAX.php';
+
+/**
+ * Simple base class for a response object to use as an ajax callback
+ *
+ * This is the base response class, more interesting response classes can be
+ * built off of this, simply give it a unique content type and override the
+ * getPayload method or fill the payload property with your extended classes's
+ * serialized content
+ *
+ * @version   $Id$
+ */
+class HTML_AJAX_Response
+{
+
+    /**
+     * The base response class uses plain text so use that content type
+     *
+     * @var string
+     * @access public
+     */
+    var $contentType = 'text/plain';
+
+    /**
+     * Assign a string to this variable to use the bare response class
+     *
+     * @var string
+     * @access public
+     */
+    var $payload = '';
+
+    /**
+     * Returns the appropriate content type
+     *
+     * This normally simply returns the contentType property but can be overridden
+     * by an extending class if the content-type is variable
+     *
+     * @return  string   appropriate content type
+     * @access public
+     */
+    function getContentType()
+    {
+        return $this->contentType;
+    }
+
+    /**
+     * Returns the serialized content of the response class
+     *
+     * You can either fill the payload elsewhere in an extending class and leave
+     * this method alone, or you can override it if you have a different type
+     * of payload that needs special treatment
+     *
+     * @return  string   serialized response content
+     * @access public
+     */
+    function getPayload()
+    {
+        return $this->payload;
+    }
+}
+?>
diff --git a/lib/pear/HTML/AJAX/Serializer/Error.php b/lib/pear/HTML/AJAX/Serializer/Error.php
new file mode 100755 (executable)
index 0000000..736a3e4
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+require_once 'HTML/AJAX/Serializer/JSON.php';
+// $Id
+/**
+ * Error Serializer, for now just uses JSON
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Joshua Eichorn <josh@bluga.net>
+ * @copyright  2005 Joshua Eichorn
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: 0.5.6
+ * @link       http://pear.php.net/package/HTML_AJAX
+ */
+class HTML_AJAX_Serializer_Error extends HTML_AJAX_Serializer_JSON 
+{
+
+}
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+?>
diff --git a/lib/pear/HTML/AJAX/Serializer/JSON.php b/lib/pear/HTML/AJAX/Serializer/JSON.php
new file mode 100755 (executable)
index 0000000..62a7004
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+require_once 'HTML/AJAX/JSON.php';
+// $Id$
+/**
+ * JSON Serializer
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Joshua Eichorn <josh@bluga.net>
+ * @copyright  2005 Joshua Eichorn
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ * @link       http://pear.php.net/package/PackageName
+ */
+// {{{ class HTMLA_AJAX_Serialize_JSON
+class HTML_AJAX_Serializer_JSON 
+{
+    // {{{ variables-properties
+    /**
+     * JSON instance
+     * @var HTML_AJAX_JSON
+     * @access private
+     */
+    var $_json;
+
+    /**
+     * use json php extension http://www.aurore.net/projects/php-json/
+     * @access private
+     */
+    var $_jsonext;
+
+    /**
+     * use loose typing to decode js objects into php associative arrays
+     * @access public
+     */
+    var $loose_type;
+    
+    // }}}
+    // {{{ constructor
+    function HTML_AJAX_Serializer_JSON($use_loose_type = true) 
+    {
+        $this->loose_type = (bool) $use_loose_type;
+        $this->_jsonext = $this->_detect();
+        if(!$this->_jsonext) {
+            $use_loose_type = ($this->loose_type) ? SERVICES_JSON_LOOSE_TYPE : 0;
+            $this->_json = new HTML_AJAX_JSON($use_loose_type);
+        }
+    }
+    // }}}
+    // {{{ serialize
+    /**
+     * This function serializes and input passed to it.
+     *
+     * @access public
+     * @param  string $input   The input to serialize.
+     * @return string $input   The serialized input.
+     */
+    function serialize($input) 
+    {
+        if($this->_jsonext) {
+            return json_encode($input);
+        } else {
+            return $this->_json->encode($input);
+        }
+    }
+    // }}}
+    // {{{ unserialize
+    /**
+     * this function unserializes the input passed to it.
+     *
+     * @access public
+     * @param  string $input   The input to unserialize
+     * @return string $input   The unserialized input.
+     */
+    function unserialize($input) 
+    {
+        if($this->_jsonext) {
+            return json_decode($input, $this->loose_type);
+        } else {
+            return $this->_json->decode($input);
+        }
+    }
+    // }}}
+    // {{{ _detect
+    /**
+     * detects the loaded extension
+     */
+    function _detect()
+    {
+        return extension_loaded('json');
+    }
+    // }}}
+}
+// }}}
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+?>
diff --git a/lib/pear/HTML/AJAX/Serializer/Null.php b/lib/pear/HTML/AJAX/Serializer/Null.php
new file mode 100755 (executable)
index 0000000..026bd58
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+// $Id$
+/**
+ * Null Serializer
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Joshua Eichorn <josh@bluga.net>
+ * @copyright  2005 Joshua Eichorn
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ * @link       http://pear.php.net/package/PackageName
+ */
+class HTML_AJAX_Serializer_Null 
+{
+    
+    function serialize($input) 
+    {
+        return $input;
+    }
+
+    function unserialize($input) 
+    {
+        return $input;
+    }
+}
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+?>
diff --git a/lib/pear/HTML/AJAX/Serializer/PHP.php b/lib/pear/HTML/AJAX/Serializer/PHP.php
new file mode 100755 (executable)
index 0000000..a3000ad
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+// $Id$
+/**
+ * PHP Serializer
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Arpad Ray <arpad@php.net>
+ * @copyright  2005 Arpad Ray
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ * @link       http://pear.php.net/package/HTML_AJAX
+ */
+class HTML_AJAX_Serializer_PHP 
+{    
+    function serialize($input) 
+    {
+        return serialize($input);
+    }
+
+    /**
+     * Unserializes the given string
+     *
+     * Triggers an error if a class is found which is not
+     * in the provided array of allowed class names.
+     *
+     * @param   string  $input
+     *  the serialized string to process
+     * @param   array   $allowedClasses
+     *  an array of class names to check objects against
+     *  before instantion
+     * @return  mixed
+     *  the unserialized variable on success, or false on
+     *  failure. If this method fails it will also trigger
+     *  a warning.
+     */
+    function unserialize($input, $allowedClasses) 
+    {
+        if (version_compare(PHP_VERSION, '4.3.10', '<')
+             || (substr(PHP_VERSION, 0, 1) == '5' && version_compare(PHP_VERSION, '5.0.3', '<'))) {
+            trigger_error('Unsafe version of PHP for native unserialization');
+            return false;
+        }
+        $classes = $this->_getSerializedClassNames($input);
+        if ($classes === false) {
+            trigger_error('Invalidly serialized string');
+            return false;
+        }
+        $diff = array_diff($classes, $allowedClasses);
+        if (!empty($diff)) {
+            trigger_error('Class(es) not allowed to be serialized');
+            return false;
+        }
+        return unserialize($input);
+    }
+    
+    /**
+     * Extract class names from serialized string
+     *
+     * Adapted from code by Harry Fuecks
+     *
+     * @param   string  $string
+     *  the serialized string to process
+     * @return  mixed
+     *  an array of class names found, or false if the input
+     *  is invalidly formed
+     */
+    function _getSerializedClassNames($string) {
+        // Strip any string representations (which might contain object syntax)
+        while (($pos = strpos($string, 's:')) !== false) {
+            $pos2 = strpos($string, ':', $pos + 2);
+            if ($pos2 === false) {
+                // invalidly serialized string
+                return false;    
+            }
+            $end = $pos + 2 + substr($string, $pos + 2, $pos2) + 1;
+            $string = substr($string, 0, $pos) . substr($string, $end);
+        }
+        
+        // Pull out the class names
+        preg_match_all('/O:[0-9]+:"(.*)"/U', $string, $matches);
+        
+        // Make sure names are unique (same object serialized twice)
+        return array_unique($matches[1]);
+    }
+}
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+?>
diff --git a/lib/pear/HTML/AJAX/Serializer/Urlencoded.php b/lib/pear/HTML/AJAX/Serializer/Urlencoded.php
new file mode 100755 (executable)
index 0000000..a26ff1e
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+// $Id$
+
+// {{{ http_build_query
+/**
+ * Replacement for http_build_query()
+ *
+ * @link   http://php.net/function.http-build-query
+ * @author vlad_mustafin@ukr.net
+ * @author Arpad Ray <arpad@php.net>
+ */
+if (!function_exists('http_build_query')) {
+    function http_build_query($formdata, $numeric_prefix = null, $key = null) 
+    {
+        $res = array();
+        foreach ((array)$formdata as $k => $v) {
+            if (is_resource($v)) {
+                return null;
+            }
+            $tmp_key = urlencode(is_int($k) ? $numeric_prefix . $k : $k);
+            if (!is_null($key)) {
+                $tmp_key = $key . '[' . $tmp_key . ']';
+            }
+            $res[] = (is_scalar($v))
+                ? $tmp_key . '=' . urlencode($v)
+                : http_build_query($v, null , $tmp_key);
+        }
+        $separator = ini_get('arg_separator.output');
+        if (strlen($separator) == 0) {
+            $separator = '&';
+        }
+        return implode($separator, $res);
+    }
+}
+// }}}
+// {{{ class HTML_AJAX_Serialize_Urlencoded
+/**
+ * URL Encoding Serializer
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Arpad Ray <arpad@php.net>
+ * @author     David Coallier <davidc@php.net>
+ * @copyright  2005 Arpad Ray
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ * @link       http://pear.php.net/package/HTML_AJAX
+ */
+class HTML_AJAX_Serializer_Urlencoded
+{
+    // {{{ serialize
+    function serialize($input) 
+    {
+        return http_build_query(array('_HTML_AJAX' => $input));
+    }
+    // }}}
+    // {{{ unserialize
+    function unserialize($input) 
+    {
+        parse_str($input, $ret);
+        return (isset($ret['_HTML_AJAX']) ? $ret['_HTML_AJAX'] : $ret);
+    }
+    // }}}
+}
+// }}}
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+?>
diff --git a/lib/pear/HTML/AJAX/Serializer/XML.php b/lib/pear/HTML/AJAX/Serializer/XML.php
new file mode 100755 (executable)
index 0000000..6856f72
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+// $Id$
+/**
+ * XML Serializer - does NOT need a js serializer, use responseXML property in XmlHttpRequest
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Elizabeth Smith <auroraeosrose@gmail.com>
+ * @copyright  2005-2006 Elizabeth Smith
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: 0.5.6
+ * @link       http://pear.php.net/package/PackageName
+ */
+class HTML_AJAX_Serializer_XML
+{
+
+    /**
+     * Serializes a domdocument into an xml string
+     *
+     * Uses dom or domxml to dump a string from a DomDocument instance
+     * remember dom is always the default and this will die horribly without
+     * a domdocument instance
+     *
+     * @access public
+     * @param  object $input instanceof DomDocument
+     * @return string xml string of DomDocument
+     */
+    function serialize($input) 
+    {
+        if(empty($input))
+        {
+            return $input;
+        }
+        // we check for the dom extension
+        elseif (extension_loaded('Dom'))
+        {
+            return $input->saveXml();
+        }
+        // then will check for domxml
+        elseif (extension_loaded('Domxml')) 
+       {
+            return $input->dump_mem();
+        }
+       // will throw an error
+       else {
+               $error = new HTML_AJAX_Serializer_Error();      
+               $this->serializerNewType = 'Error';
+               return $error->serialize(array('errStr'=>"Missing PHP Dom extension direct XML won't work"));
+       }
+    }
+
+    /**
+     * Unserializes the xml string sent from the document
+     *
+     * Uses dom or domxml to pump a string into a DomDocument instance
+     * remember dom is always the default and this will die horribly without
+     * one or the other, and will throw warnings if you have bad xml
+     *
+     * @access public
+     * @param  string $input   The input to serialize.
+     * @return object instanceofDomDocument
+     */
+    function unserialize($input) 
+    {
+        if(empty($input))
+        {
+            return $input;
+        }
+        // we check for the dom extension
+        elseif (extension_loaded('Dom'))
+        {
+            $doc = new DOMDocument();
+            $doc->loadXML($input);
+            return $doc;
+        }
+        // then we check for the domxml extensions
+        elseif (extension_loaded('Domxml'))
+       {
+            return domxml_open_mem($input);
+       }
+       // we give up and just return the xml directly
+        else
+        {
+               return $input;
+        }
+    }
+}
+?>
diff --git a/lib/pear/HTML/AJAX/Server.php b/lib/pear/HTML/AJAX/Server.php
new file mode 100755 (executable)
index 0000000..b5fc982
--- /dev/null
@@ -0,0 +1,725 @@
+<?php
+/**
+ * OO AJAX Implementation for PHP
+ *
+ * SVN Rev: $Id$
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Joshua Eichorn <josh@bluga.net>
+ * @copyright  2005 Joshua Eichorn
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: @package_version@
+ */
+
+/**
+ * Require the main AJAX library
+ */
+require_once 'HTML/AJAX.php';
+
+/**
+ * Class for creating an external AJAX server
+ *
+ * Can be used in 2 different modes, registerClass mode where you create an instance of the server and add the classes that will be registered
+ * and then run handle request
+ *
+ * Or you can extend it and add init{className} methods for each class you want to export
+ *
+ * Client js generation is exposed through 2 _GET params client and stub
+ *  Setting the _GET param client to `all` will give you all the js classes needed
+ *  Setting the _GET param stub to `all` will give you stubs of all registered classes, you can also set it too just 1 class
+ *
+ * @category   HTML
+ * @package    AJAX
+ * @author     Joshua Eichorn <josh@bluga.net>
+ * @copyright  2005 Joshua Eichorn
+ * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/PackageName
+ */
+class HTML_AJAX_Server 
+{
+
+    /**
+     * Client options array if set to true the code looks at _GET
+     * @var bool|array
+     */
+    var $options = true;
+
+    /**
+     * HTML_AJAX instance
+     * @var HTML_AJAX
+     */
+    var $ajax;
+
+    /**
+     * Set to true if your extending the server to add init{className methods}
+     * @var boolean
+     * @access  public
+     */
+    var $initMethods = false;
+
+    /**
+     * Location on filesystem of client javascript library
+     * @var false|string if false the default pear data dir location is used
+     */
+    var $clientJsLocation = false;
+
+    /** 
+     * An array of options that tell the server howto Cache output
+     *
+     * The rules are functions that make etag hash used to see if the client needs to download updated content
+     * If you extend this class you can make your own rule function the naming convention is _cacheRule{RuleName}
+     *
+     * <code>
+     * array(
+     *  'httpCacheClient' => true,   // send 304 headers for responses to ?client=* requests
+     *  'ClientCacheRule' => 'File', // create a hash from file names and modified times, options: file|content
+     *  'ClientCacheExpects'=> 'files', // what type of content to send to the hash function, options: files|classes|content
+     *  'httpCacheStub'   => true,   // send 304 headers for responses to ?stub=* requests
+     *  'StubCacheRule'   => 'Api',  // create a hash from the exposed api, options: api|content
+     *  'StubCacheExpects'=> 'classes', // what type of content to send to the hash function, options: files|classes|content
+     * )
+     * </code>
+     *
+     * @var array
+     * @access  public
+     */
+    var $cacheOptions = array(
+        'httpCacheClient'       => true, 
+        'ClientCacheRule'       => 'file',
+        'ClientCacheExpects'    => 'files',
+        'httpCacheStub'         => true, 
+        'StubCacheRule'         => 'api', 
+        'StubCacheExpects'      => 'classes', 
+        );
+
+    /**
+     * Compression Options
+     *
+     * <code>
+     * array(
+     *  'enabled'   => false,   // enable compression
+     *  'type'      => 'gzip'   // the type of compression to do, options: gzip
+     * )
+     * </code>
+     *
+     * @var array
+     * @access public
+     */
+    var $compression = array(
+        'enabled'       => false,
+        'type'          => 'gzip'
+    );
+
+    /**
+     * Javascript library names and there path 
+     *
+     * the return of $this->clientJsLocation(), is prepended before running readfile on them
+     *
+     * @access  public
+     * @var array
+     */
+    var $javascriptLibraries = array(
+        'all'           =>  'HTML_AJAX.js',
+        'html_ajax'     =>  'HTML_AJAX.js',
+        'html_ajax_lite'=>  'HTML_AJAX_lite.js',
+        'json'          =>  'serializer/JSON.js',
+        'request'       =>  'Request.js',
+        'main'          =>  array('Compat.js','Main.js','clientPool.js'),
+        'httpclient'    =>  'HttpClient.js',
+        'dispatcher'    =>  'Dispatcher.js',
+        'util'          =>  'util.js',
+        'loading'       =>  'Loading.js',
+        'phpserializer' =>  'serializer/phpSerializer.js',
+        'urlserializer' =>  'serializer/UrlSerializer.js',
+        'haserializer'  =>  'serializer/haSerializer.js',
+        'clientpool'    =>  'clientPool.js',
+        'iframe'        =>  'IframeXHR.js',
+        'alias'         =>  'Alias.js',
+        'queues'        =>  'Queue.js',
+        'behavior'      =>  array('behavior/behavior.js','behavior/cssQuery-p.js'),
+
+        // rules to help you use a minimal library set
+        'standard'      =>  array('Compat.js','clientPool.js','util.js','Main.js','HttpClient.js','Request.js','serializer/JSON.js',
+                                    'Loading.js','serializer/UrlSerializer.js','Alias.js','behavior/behavior.js','behavior/cssQuery-p.js'),
+        'jsonrpc'       =>  array('Compat.js','util.js','Main.js','clientPool.js','HttpClient.js','Request.js','serializer/JSON.js'),
+        'proxyobjects'  =>  array('Compat.js','util.js','Main.js','clientPool.js','Request.js','serializer/JSON.js','Dispatcher.js'),
+
+        // BC rules
+        'priorityqueue' =>  'Queue.js',
+        'orderedqueue'  =>  'Queue.js',
+    );
+
+    /**
+     * Custom paths to use for javascript libraries, if not set {@link clientJsLocation} is used to find the system path
+     *
+     * @access public
+     * @var array
+     * @see registerJsLibrary
+     */
+    var $javascriptLibraryPaths = array();
+
+    /**
+     * Array of className => init methods to call, generated from constructor from initClassName methods
+     *
+     * @access protected
+     */
+    var $_initLookup = array();
+    
+
+    /**
+     * Constructor creates the HTML_AJAX instance
+     *
+     * @param string $serverUrl (Optional) the url the client should be making a request too
+     */
+    function HTML_AJAX_Server($serverUrl = false) 
+    {
+        $this->ajax = new HTML_AJAX();
+
+        // parameters for HTML::AJAX
+        $parameters = array('stub', 'client');
+
+        // keep in the query string all the parameters that don't belong to AJAX
+        // we remove all string like "parameter=something&". Final '&' can also
+        // be '&amp;' (to be sure) and is optional. '=something' is optional too.
+        $querystring = '';
+        if (isset($_SERVER['QUERY_STRING'])) {
+            $querystring = preg_replace('/(' . join('|', $parameters) . ')(?:=[^&]*(?:&(?:amp;)?|$))?/', '', $this->ajax->_getServer('QUERY_STRING'));
+        }
+
+        // call the server with this query string
+        if ($serverUrl === false) {
+            $serverUrl = htmlentities($this->ajax->_getServer('PHP_SELF'));
+        }
+
+        if (substr($serverUrl,-1) != '?') {
+            $serverUrl .= '?';
+        }
+        $this->ajax->serverUrl =  $serverUrl . $querystring;
+        
+        $methods = get_class_methods($this);
+        foreach($methods as $method) {
+            if (preg_match('/^init([a-zA-Z0-9_]+)$/',$method,$match)) {
+                $this->_initLookup[strtolower($match[1])] = $method;
+            }
+        }
+    }
+
+    /**
+     * Handle a client request, either generating a client or having HTML_AJAX handle the request
+     *
+     * @return boolean true if request was handled, false otherwise
+     */
+    function handleRequest() 
+    {
+        if ($this->options == true) {
+            $this->_loadOptions();
+        }
+        //basically a hook for iframe but allows processing of data earlier
+        $this->ajax->populatePayload();
+        if (!isset($_GET['c']) && (count($this->options['client']) > 0 || count($this->options['stub']) > 0) ) {
+            $this->generateClient();
+            return true;
+        } else {
+            if (!empty($_GET['c'])) {
+                $this->_init($this->_cleanIdentifier($this->ajax->_getVar('c')));
+            }
+            return $this->ajax->handleRequest();
+        }
+    }
+
+    /**
+     * Register method passthrough to HTML_AJAX
+     *
+     * @see HTML_AJAX::registerClass for docs
+     */
+    function registerClass(&$instance, $exportedName = false, $exportedMethods = false) 
+    {
+        $this->ajax->registerClass($instance,$exportedName,$exportedMethods);
+    }
+
+    /**
+     * Change default serialization - important for exporting classes
+     *
+     * I wanted this for the xml serializer :)
+     */
+    function setSerializer($type) 
+    {
+        $this->ajax->serializer = $type;
+        $this->ajax->unserializer = $type;
+    }
+
+    /**
+     * Register a new js client library
+     *
+     * @param string          $libraryName name you'll reference the library as
+     * @param string|array    $fileName   actual filename with no path, for example customLib.js
+     * @param string|false    $path   Optional, if not set the result from jsClientLocation is used
+     */
+    function registerJSLibrary($libraryName,$fileName,$path = false) {
+        $libraryName = strtolower($libraryName);
+        $this->javascriptLibraries[$libraryName] = $fileName;
+
+        if ($path !== false) {
+            $this->javascriptLibraryPaths[$libraryName] = $path;
+        }
+    }
+
+    /**
+     * Register init methods from an external class
+     *
+     * @param object    $instance an external class with initClassName methods
+     */
+    function registerInitObject(&$instance) {
+        $instance->server =& $this;
+        $methods = get_class_methods($instance);
+        foreach($methods as $method) {
+            if (preg_match('/^init([a-zA-Z0-9_]+)$/',$method,$match)) {
+                $this->_initLookup[strtolower($match[1])] = array(&$instance,$method);
+            }
+        }
+    }
+
+    /**
+     * Register a callback to be exported to the client
+     *
+     * This function uses the PHP callback pseudo-type
+     *
+     */
+    function registerPhpCallback($callback)
+    {
+        if (!is_callable($callback)) {
+            // invalid callback
+            return false;
+        }
+        
+        if (is_array($callback) && is_object($callback[0])) {
+            // object method
+            $this->registerClass($callback[0], strtolower(get_class($callback[0])), array($callback[1]));
+            return true;
+        }
+        
+        // static callback
+        $this->ajax->registerPhpCallback($callback);
+    }
+
+    /**
+     * Generate client js
+     *
+     * @todo    this is going to need tests to cover all the options
+     */
+    function generateClient() 
+    {
+        $headers = array();
+
+        ob_start();
+
+        // create a list list of js files were going to need to output
+        // index is the full file and so is the value, this keeps duplicates out of $fileList
+        $fileList = array();
+
+        if(!is_array($this->options['client'])) {
+            $this->options['client'] = array();
+        }
+        foreach($this->options['client'] as $library) {
+            if (isset($this->javascriptLibraries[$library])) {
+                $lib = (array)$this->javascriptLibraries[$library];
+                foreach($lib as $file) {
+                    if (isset($this->javascriptLibraryPaths[$library])) {
+                        $fileList[$this->javascriptLibraryPaths[$library].$file] = $this->javascriptLibraryPaths[$library].$file;
+                    }
+                    else {
+                        $fileList[$this->clientJsLocation().$file] = $this->clientJsLocation().$file;
+                    }
+                }
+            }
+        }
+
+        // do needed class init if were running an init server
+        if(!is_array($this->options['stub'])) {
+            $this->options['stub'] = array();
+        }
+        $classList = $this->options['stub'];
+        if ($this->initMethods) {
+            if (isset($this->options['stub'][0]) && $this->options['stub'][0] === 'all') {
+                    $this->_initAll();
+            } else {
+                foreach($this->options['stub'] as $stub) {
+                    $this->_init($stub);
+                }
+            }
+        }
+        if (isset($this->options['stub'][0]) && $this->options['stub'][0] === 'all') {
+            $classList = array_keys($this->ajax->_exportedInstances);
+        }
+
+        // if were doing stub and client we have to wait for both ETags before we can compare with the client
+        $combinedOutput = false;
+        if ($classList != false && count($classList) > 0 && count($fileList) > 0) {
+            $combinedOutput = true;
+        }
+
+
+        if ($classList != false && count($classList) > 0) {
+
+            // were setup enough to make a stubETag if the input it wants is a class list
+            if ($this->cacheOptions['httpCacheStub'] && 
+                $this->cacheOptions['StubCacheExpects'] == 'classes') 
+            {
+                $stubETag = $this->_callCacheRule('Stub',$classList);
+            }
+
+            // if were not in combined output compare etags, if method returns true were done
+            if (!$combinedOutput && isset($stubETag)) {
+                if ($this->_compareEtags($stubETag)) {
+                    ob_end_clean();
+                    return;
+                }
+            }
+
+            // output the stubs for all the classes in our list
+            foreach($classList as $class) {
+                    echo $this->ajax->generateClassStub($class);
+            }
+
+            // if were cacheing and the rule expects content make a tag and check it, if the check is true were done
+            if ($this->cacheOptions['httpCacheStub'] && 
+                $this->cacheOptions['StubCacheExpects'] == 'content') 
+            {
+                $stubETag = $this->_callCacheRule('Stub',ob_get_contents());
+            }
+
+            // if were not in combined output compare etags, if method returns true were done
+            if (!$combinedOutput && isset($stubETag)) {
+                if ($this->_compareEtags($stubETag)) {
+                    ob_end_clean();
+                    return;
+                }
+            }
+        }
+
+        if (count($fileList) > 0) {
+            // if were caching and need a file list build our jsETag
+            if ($this->cacheOptions['httpCacheClient'] && 
+                $this->cacheOptions['ClientCacheExpects'] === 'files') 
+            {
+                $jsETag = $this->_callCacheRule('Client',$fileList);
+
+            }
+
+            // if were not in combined output compare etags, if method returns true were done
+            if (!$combinedOutput && isset($jsETag)) {
+                if ($this->_compareEtags($jsETag)) {
+                    ob_end_clean();
+                    return;
+                }
+            }
+
+            // output the needed client js files
+            foreach($fileList as $file) {
+                $this->_readFile($file);
+            }
+
+            // if were caching and need content build the etag
+            if ($this->cacheOptions['httpCacheClient'] && 
+                $this->cacheOptions['ClientCacheExpects'] === 'content') 
+            {
+                $jsETag = $this->_callCacheRule('Client',ob_get_contents());
+            }
+
+            // if were not in combined output compare etags, if method returns true were done
+            if (!$combinedOutput && isset($jsETag)) {
+                if ($this->_compareEtags($jsETag)) {
+                    ob_end_clean();
+                    return;
+                }
+            }
+            // were in combined output, merge the 2 ETags and compare
+            else if (isset($jsETag) && isset($stubETag)) {
+                if ($this->_compareEtags(md5($stubETag.$jsETag))) {
+                    ob_end_clean();
+                    return;
+                }
+            }
+        }
+
+
+        // were outputting content, add our length header and send the output
+        $length = ob_get_length();
+        $output = ob_get_contents();
+        ob_end_clean();
+
+        if ($this->ajax->packJavaScript) {
+            $output = $this->ajax->packJavaScript($output);
+            $length = strlen($output);
+        }
+
+        if ($this->compression['enabled'] && $this->compression['type'] == 'gzip' && strpos($_SERVER["HTTP_ACCEPT_ENCODING"], "gzip") !== false) {
+            $output = gzencode($output,9);
+            $length = strlen($output);
+            $headers['Content-Encoding'] = 'gzip';
+        }
+
+        if ($length > 0 && $this->ajax->_sendContentLength()) { 
+            $headers['Content-Length'] = $length;
+        }
+        $headers['Content-Type'] = 'text/javascript; charset=utf-8';
+        $this->ajax->_sendHeaders($headers);
+        echo($output);
+    }
+
+    /**
+     * Run readfile on input with basic error checking
+     *
+     * @param   string  $file   file to read
+     * @access  private
+     * @todo    is addslashes enough encoding for js?
+     */
+    function _readFile($file) 
+    {
+        if (file_exists($file)) {
+            readfile($file);
+        } else {
+            $file = addslashes($file);
+            echo "alert('Unable to find javascript file: $file');";
+        }
+    }
+
+    /**
+     * Get the location of the client js
+     * To override the default pear datadir location set $this->clientJsLocation
+     *
+     * @return  string
+     */
+    function clientJsLocation() 
+    {
+        if (!$this->clientJsLocation) {
+            $path = '@data-dir@'.DIRECTORY_SEPARATOR.'HTML_AJAX'.DIRECTORY_SEPARATOR.'js'.DIRECTORY_SEPARATOR;
+            if(strpos($path, '@'.'data-dir@') === 0)
+            {
+                $path = realpath(dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'js').DIRECTORY_SEPARATOR;
+            }
+            return $path;
+        } else {
+            return $this->clientJsLocation;
+        }
+    }
+
+    /**
+     * Set the location of the client js
+     *
+     * @access  public
+     * @param   string  $location   Location
+     * @return  void
+     */
+    function setClientJsLocation($location) 
+    {
+        $this->clientJsLocation = $location;
+    }
+
+    /**
+     * Set the path to a Javascript libraries
+     *
+     * @access  public
+     * @param   string  $library    Library name
+     * @param   string  $path       Path
+     * @return  void
+     */
+    function setJavascriptLibraryPath($library, $path) 
+    {
+        $this->javascriptLibraryPaths[$library] = $path;
+    }
+
+    /**
+     * Set the path to more than one Javascript libraries at once
+     *
+     * @access  public
+     * @param   array   $paths  Paths
+     * @return  void
+     */
+    function setJavascriptLibraryPaths($paths) 
+    {
+        if (is_array($paths)) {
+            $this->javascriptLibraryPaths = array_merge($this->javascriptLibraryPaths, $paths);
+        }
+    }
+
+    /**
+     * Load options from _GET
+     *
+     * @access private
+     */
+    function _loadOptions() 
+    {
+        $this->options = array('client'=>array(),'stub'=>array());
+        if (isset($_GET['client'])) {
+            $clients = explode(',',$this->ajax->_getVar('client'));
+            $client = array();
+            foreach($clients as $val) {
+                $cleanVal = $this->_cleanIdentifier($val);
+                if (!empty($cleanVal)) {
+                    $client[] = strtolower($cleanVal);
+                }
+            }
+
+            if (count($client) > 0) {
+                $this->options['client'] = $client;
+            }
+        }
+        if (isset($_GET['stub'])) {
+            $stubs = explode(',',$this->ajax->_getVar('stub'));
+            $stub = array();
+            foreach($stubs as $val) {
+                $cleanVal = $this->_cleanIdentifier($val);
+                if (!empty($cleanVal)) {
+                    $stub[] = strtolower($cleanVal);
+                }
+            }
+
+            if (count($stub) > 0) {
+                $this->options['stub'] = $stub;
+            }
+        }
+    }
+
+    /**
+     * Clean an identifier like a class name making it safe to use
+     *
+     * @param   string  $input
+     * @return  string
+     * @access  private
+     */
+    function _cleanIdentifier($input) {
+            return trim(preg_replace('/[^A-Za-z_0-9]/','',$input));
+    }
+
+    /**
+     * Run every init method on the class
+     *
+     * @access private
+     */
+    function _initAll() 
+    {
+        if ($this->initMethods) {
+            foreach($this->_initLookup as $class => $method) {
+                $this->_init($class);
+            }
+        }
+    }
+
+    /**
+     * Init one class
+     *
+     * @param   string  $className
+     * @access private
+     */
+    function _init($className) 
+    {
+        $className = strtolower($className);
+        if ($this->initMethods) {
+            if (isset($this->_initLookup[$className])) {
+                $method =& $this->_initLookup[$className];
+                if (is_array($method)) {
+                    call_user_func($method);
+                }
+                else {
+                    $this->$method();
+                }
+            } else {
+                trigger_error("Could find an init method for class: " . $className);
+            }
+        }
+    }
+
+    /**
+     * Generate a hash from a list of files
+     *
+     * @param   array   $files  file list
+     * @return  string  a hash that can be used as an etag
+     * @access  private
+     */
+    function _cacheRuleFile($files) {
+        $signature = "";
+        foreach($files as $file) {
+            if (file_exists($file)) {
+                $signature .= $file.filemtime($file);
+            }
+        }
+        return md5($signature);
+    }
+
+    /**
+     * Generate a hash from the api of registered classes
+     *
+     * @param   array   $classes class list
+     * @return  string  a hash that can be used as an etag
+     * @access  private
+     */
+    function _cacheRuleApi($classes) {
+        $signature = "";
+        foreach($classes as $class) {
+            if (isset($this->ajax->_exportedInstances[$class])) {
+                $signature .= $class.implode(',',$this->ajax->_exportedInstances[$class]['exportedMethods']);
+            }
+        }
+        return md5($signature);
+    }
+
+    /**
+     * Generate a hash from the raw content
+     *
+     * @param   array   $content
+     * @return  string  a hash that can be used as an etag
+     * @access  private
+     */
+    function _cacheRuleContent($content) {
+        return md5($content);
+    }
+
+    /**
+     * Send cache control headers
+     * @access  private
+     */
+    function _sendCacheHeaders($etag,$notModified) {
+        header('Cache-Control: must-revalidate');
+        header('ETag: '.$etag);
+        if ($notModified) {
+            header('HTTP/1.0 304 Not Modified',false,304);
+        }
+    }
+
+    /**
+     * Compare eTags
+     *
+     * @param   string  $serverETag server eTag
+     * @return  boolean
+     * @access  private
+     */
+    function _compareEtags($serverETag) {
+        if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
+               if (strcmp($this->ajax->_getServer('HTTP_IF_NONE_MATCH'),$serverETag) == 0) {
+                $this->_sendCacheHeaders($serverETag,true);
+                return true;
+            }
+       }
+        $this->_sendCacheHeaders($serverETag,false);
+        return false;
+    }
+
+    /**
+     * Call a cache rule and return its retusn
+     *
+     * @param   string  $rule Stub|Client
+     * @param   mixed   $payload
+     * @return  boolean
+     * @access  private
+     * @todo    decide if error checking is needed
+     */
+    function _callCacheRule($rule,$payload) {
+        $method = '_cacheRule'.$this->cacheOptions[$rule.'CacheRule'];
+        return call_user_func(array(&$this,$method),$payload);
+    }
+}
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+?>
index c348a678071653144d37b92e4b991a879e9959be..3b1f4a477cef3868485500fddbddbe8e317aaf0b 100644 (file)
@@ -2,13 +2,13 @@ PEAR Libraries
 ====================================================================
 
 
-This directory (lib/pear) contains unmodified copies of some 
+This directory (lib/pear) contains unmodified copies of some
 libraries from the standard PEAR distribution (http://pear.php.net).
 
-We include these in Moodle solely for the convenience of sites that 
+We include these in Moodle solely for the convenience of sites that
 may not have PEAR installed.
 
-If this directory is DELETED from Moodle then Moodle will search 
+If this directory is DELETED from Moodle then Moodle will search
 the standard PHP directories and use the PEAR libraries there instead.
 
 
@@ -46,7 +46,7 @@ In detail, the libraries added here are:
     - http://pear.php.net/package/HTML
 - PEAR main class:
     - Current version: 1.4.5
-    - by Stig Bakken, Thomas V.V.Cox, Pierre-Alain Joye, 
+    - by Stig Bakken, Thomas V.V.Cox, Pierre-Alain Joye,
       Greg Beaver and Martin Jansen
     - License: PHP
     - http://pear.php.net/package/PEAR
@@ -60,23 +60,28 @@ In detail, the libraries added here are:
     - by Hartmut Holzgraefe and Christian Stocker
     - License: BSD
     - http://pear.php.net/package/HTTP_WebDAV_Server
+- PEAR HTML_AJAX:
+    - Current version: 0.5.6
+    - by Elizabeth Smith, Arpad Ray, Joshua Eichorn, David Coallier and Laurent Yaish
+    - License: LGPL
+    - http://pear.php.net/package/HTML_AJAX/
 
 
 ----------------------------------------------------------------
 A NOTE TO DEVELOPERS
 ================================================================
 
-We must not use these classes directly ever. Instead we must build 
+We must not use these classes directly ever. Instead we must build
 and use wrapper classes to isolate Moodle code from internal PEAR
-implementations, allowing us to migrate if needed to other 
-libraries in the future. For an example of wrapped classes, 
-see the excel.class.lib file, that includes code to build 
+implementations, allowing us to migrate if needed to other
+libraries in the future. For an example of wrapped classes,
+see the excel.class.lib file, that includes code to build
 Excel files using the cool library inside PEAR, but using
 the old calls used before Moodle 1.6 to maintain compatibility.
 
 Please, don't forget it! Always use wrapper classes/functions!
 
-Ciao, 
+Ciao,
 Eloy Lafuente, 2005-12-17 :-)
 
 
@@ -88,28 +93,28 @@ A NOTE ON THE PHP LICENSE AND MOODLE
 Everything in Moodle in pure GPL.  This pear directory is the only
 part of the distribution that is not.
 
-There is some question about how PHP-licensed software can be 
+There is some question about how PHP-licensed software can be
 included within a GPL-licensed distribution like Moodle, specifically
-the clause that annoyingly says no derivative of the software can 
-include the name PHP.  
+the clause that annoyingly says no derivative of the software can
+include the name PHP.
 
-We don't intend to rename Moodle to anything of the sort, obviously, 
-but to help people downstream who could possibly want to do so, 
-we have sought special permission from the authors of these classes 
-to allow us an exemption on this point so that we don't need to 
+We don't intend to rename Moodle to anything of the sort, obviously,
+but to help people downstream who could possibly want to do so,
+we have sought special permission from the authors of these classes
+to allow us an exemption on this point so that we don't need to
 change our nice clean GPL license.
 
-Several authors have given Moodle explicit permission to distribute 
-their PHP-licensed PEAR classes in the Moodle distribution, allowing 
+Several authors have given Moodle explicit permission to distribute
+their PHP-licensed PEAR classes in the Moodle distribution, allowing
 anybody using these classes ONLY as part of the Moodle distribution
 exemption from clauses of the PHP license that could cause
 conflict with the main GNU Public License that Moodle uses.
 
-We are still waiting to hear back from the others but we assume 
+We are still waiting to hear back from the others but we assume
 for now that it will likewise be OK.
 
-If you are at all worried about this situation you can simply delete 
-this directory from Moodle and it will use your installed PEAR 
+If you are at all worried about this situation you can simply delete
+this directory from Moodle and it will use your installed PEAR
 libraries instead.
 
 Cheers,