Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/example.php
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/example.php	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/example.php	(revision 2375)
@@ -0,0 +1,22 @@
+<html>
+<head>
+<title>AJAX-Spell</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+<link rel="stylesheet" type="text/css" href="css/spell_checker.css">
+<script src="cpaint/cpaint2.inc.compressed.js" type="text/javascript"></script>
+<!-- You can use either one of the files below, but the compressed one
+     will be faster and a lot smaller to download -->
+<!--script src="js/spell_checker_compressed.js" type="text/javascript"></script-->
+<script src="js/spell_checker.js" type="text/javascript"></script>
+
+</head>
+<body>
+<form action="#">
+<textarea title="spellcheck_icons" accesskey="spell_checker.php" id="spell_checker1" name="comment1" style="width: 400px; height: 150px;" />This is a tï¿œst of non-ascii characters<br /><br />andd a testt of the spelll checker. Javascript was an unregonized wordd but it's now in the custom dictionary!!</textarea>
+<br />
+<textarea id="spell_checker2" name="comment2" style="width: 400px; height: 150px;" />This text area does not have a spell checker.</textarea>
+<br />
+<textarea title="spellcheck" accesskey="spell_checker.php" id="spell_checker3" name="comment3" style="width: 400px; height: 150px;" />This is anotherr testt wiht anoother spell checkker!!</textarea>
+</form>
+</body>
+</html>
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/pspell_comp.php
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/pspell_comp.php	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/pspell_comp.php	(revision 2375)
@@ -0,0 +1,236 @@
+ <?
+/**
+ * pspell compatibility wrapper
+ *
+ * This library provides some of the functions from the pspell PHP extension
+ * by wrapping them to calls to the aspell binary
+ *
+ * It can be simply dropped into code written for the pspell extension like
+ * the following
+ *
+ * if(!function_exists('pspell_suggest')){
+ *   require_once ("pspell_comp.php");
+ * }
+ *
+ * Define the path to the aspell binary like this if needed:
+ *
+ * define('ASPELL_BIN','/path/to/aspell');
+ *
+ * @author   Andreas Gohr <andi@splitbrain.org>
+ *
+ * Copyright (c) 2005, Andreas Gohr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice, 
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright notice, 
+ *     this list of conditions and the following disclaimer in the documentation 
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ */
+
+if(!defined('ASPELL_BIN')) define('ASPELL_BIN','aspell');
+
+define(PSPELL_FAST,1);         # Fast mode (least number of suggestions)
+define(PSPELL_NORMAL,2);       # Normal mode (more suggestions)
+define(PSPELL_BAD_SPELLERS,3); # Slow mode (a lot of suggestions) )
+
+function pspell_config_create($language, $spelling=null, $jargon=null, $encoding='iso8859-1'){
+    return new Pspell($language, $spelling, $jargon, $encoding);
+}
+
+function pspell_config_mode(&$config, $mode){
+    return $config->setMode($mode);
+}
+
+function pspell_new_config(&$config){
+    return $config;
+}
+
+function pspell_check(&$dict,$word){
+    return $dict->check($word);
+}
+
+function pspell_suggest(&$dict, $word){
+    return $dict->suggest($word);
+}
+
+/**
+ * Class to provide pspell functionality through aspell
+ *
+ * Needs PHP >= 4.3.0
+ */
+class Pspell{
+    var $language;
+    var $spelling;
+    var $jargon;
+    var $encoding;
+    var $mode=PSPELL_NORMAL;
+    
+    var $args='';
+
+    /**
+     * Constructor. Works like pspell_config_create()
+     *
+     * @author   Andreas Gohr <andi@splitbrain.org>
+     * @todo     $spelling isn't used
+     */
+    function Pspell($language, $spelling=null, $jargon=null, $encoding='iso8859-1'){
+        $this->language = $language;
+        $this->spelling = $spelling;
+        $this->jargon   = $jargon;
+        $this->encoding = $encoding;
+        $this->_prepareArgs();
+    } 
+
+    /**
+     * Set the spelling mode like pspell_config_mode()
+     *
+     * Mode can be PSPELL_FAST, PSPELL_NORMAL or PSPELL_BAD_SPELLER
+     *
+     * @author   Andreas Gohr <andi@splitbrain.org>
+     * @todo     check for valid mode
+     */
+    function setMode($mode){
+        $this->mode = $mode;
+        $this->_prepareArgs();
+        return $mode;
+    }
+
+    /**
+     * Prepares the needed arguments for the call to the aspell binary
+     *
+     * No need to call this directly
+     *
+     * @author   Andreas Gohr <andi@splitbrain.org>
+     */
+    function _prepareArgs(){
+        $this->args = '';
+
+        if($this->language){
+            $this->args .= ' --lang='.escapeshellarg($this->language);
+        }else{
+            return false; // no lang no spell
+        }
+
+        #FIXME how to support spelling?
+
+        if($this->$jargon){
+            $this->args .= ' --jargon='.escapeshellarg($jargon);
+        }
+
+        if($this->$encoding){
+            $this->args .= ' --encoding='.escapeshellarg($encoding);
+        }
+
+        switch ($this->mode){
+            case PSPELL_FAST:
+                $this->args .= ' --sug-mode=fast';
+                break;
+            case PSPELL_BAD_SPELLERS:
+                $this->args .= ' --sug-mode=bad-spellers';
+                break;
+            default:
+                $this->args .= ' --sug-mode=normal';
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks a word for correctness
+     *
+     * This opens a bidirectional pipe to the aspell binary, writes
+     * the given word to STDIN and reads STDOUT for the result
+     *
+     * @returns array of suggestions on wrong spelling, or true on no spellerror
+     *
+     * @author   Andreas Gohr <andi@splitbrain.org>
+     */
+    function suggest($word){
+        $out = '';
+        $err = '';
+
+        $word = trim($word);
+
+        if(empty($word)) return true;
+
+        //prepare file descriptors
+        $descspec = array(
+               0 => array('pipe', 'r'),  // stdin is a pipe that the child will read from
+               1 => array('pipe', 'w'),  // stdout is a pipe that the child will write to
+               2 => array('pipe', 'w')    // stderr is a file to write to
+        );
+
+        $process = proc_open(ASPELL_BIN.' -a'.$this->args, $descspec, $pipes);
+        if (is_resource($process)) {
+            //write to stdin
+            fwrite($pipes[0],$word);
+            fclose($pipes[0]);
+
+            //read stdout
+            while (!feof($pipes[1])) {
+                $out .= fread($pipes[1], 8192);
+            }
+            fclose($pipes[1]);
+
+            //read stderr
+            while (!feof($pipes[2])) {
+                $err .= fread($pipes[2], 8192);
+            }
+            fclose($pipes[2]);
+
+            if(proc_close($process) != 0){
+                //something went wrong
+                trigger_error("aspell returned an error: $err", E_USER_WARNING);
+                return array();
+            }
+
+            //parse output
+            $lines = split("\n",$out);
+            foreach ($lines as $line){
+                $line = trim($line);
+                if(empty($line))    continue;       // empty line
+                if($line[0] == '@') continue;       // comment
+                if($line[0] == '*') return true;    // no mistakes made
+                if($line[0] == '#') return array(); // mistake but no suggestions
+                if($line[0] == '&'){
+                    $line = preg_replace('/&.*?: /','',$line);
+                    return split(', ',$line);
+                }
+            }
+            return true; // shouldn't be reached
+        }
+        //opening failed
+        trigger_error("Could not run aspell '".ASPELL_BIN."'", E_USER_WARNING);
+        return array();
+    }
+
+    /**
+     * Check if a word is misspelled like pspell_check
+     *
+     * @author   Andreas Gohr <andi@splitbrain.org>
+     */
+    function check($word){
+        if(is_array($this->suggest($word))){
+            return false;
+        }else{
+            return true;
+        }
+    }
+}
+
+?>
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/nbproject/project.properties
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/nbproject/project.properties	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/nbproject/project.properties	(revision 2375)
@@ -0,0 +1,7 @@
+include.path=${php.global.include.path}
+php.version=PHP_5
+source.encoding=UTF-8
+src.dir=.
+tags.asp=false
+tags.short=true
+web.root=.
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/nbproject/project.xml
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/nbproject/project.xml	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/nbproject/project.xml	(revision 2375)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.php.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/php-project/1">
+            <name>spell_checker</name>
+        </data>
+    </configuration>
+</project>
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/css/spell_checker.css
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/css/spell_checker.css	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/css/spell_checker.css	(revision 2375)
@@ -0,0 +1,75 @@
+.status { 
+	color: #FF0000;
+	padding-left: 150px;
+	white-space: nowrap;
+}
+
+.spell_container {
+
+}
+
+.suggestion_box {
+	background-color: #E8F1FF;
+	border: 1px solid #000000;
+	position: absolute;
+	padding: 2px 2px 2px 2px;
+	text-align: left;
+}
+.suggestion {
+	cursor: pointer;
+	text-decoration: underline;
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+	font-size: 11px;
+	color: #000000;
+}
+
+.corrected_word
+{
+	color: #005500;
+}
+
+.addtoDictionary {
+	background-color:  #E8F1FF;
+	cursor: pointer;
+	text-decoration: underline;
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+	font-size: 11px;
+	color: #000000;
+}
+
+.edit_box {
+	background-color: #FFFFFF;
+	color: #000000;
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+	font-size: 11px;
+	border: 1px inset #000000;
+	padding-top: 2px;
+	padding-left: 2px;
+	overflow: auto;
+}
+.text_box {
+	background-color: #FFFFFF;
+	color: #000000;
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+	font-size: 11px;
+}
+.highlight {
+	color: #FF0000;
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+	font-size: 11px;
+	text-decoration: underline;
+	cursor: pointer;
+}
+.no_misspellings {
+	color: #009933;
+}
+.check_spelling {
+	color: #0066CC;
+	text-decoration: underline;
+	cursor: pointer;
+}
+.resume_editing {
+	color: #009933;
+	text-decoration: underline;
+	cursor: pointer;
+}
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/personal_dictionary/personal_dictionary.txt
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/personal_dictionary/personal_dictionary.txt	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/personal_dictionary/personal_dictionary.txt	(revision 2375)
@@ -0,0 +1,1 @@
+personal_ws-1.1 en 0
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/spell_checker.php
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/spell_checker.php	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/spell_checker.php	(revision 2375)
@@ -0,0 +1,434 @@
+<?php
+/**********************************************************************************************
+ * AJAX Spell Checker - Version 2.8
+ * (C) 2005 - Garrison Locke
+ * 
+ * This spell checker is built in the style of the Gmail spell
+ * checker.  It uses AJAX to communicate with the backend without
+ * requiring the page be reloaded.  If you use this code, please
+ * give me credit and a link to my site would be nice.
+ * http://www.broken-notebook.com.
+ *
+ * Copyright (c) 2005, Garrison Locke
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice, 
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright notice, 
+ *     this list of conditions and the following disclaimer in the documentation 
+ *     and/or other materials provided with the distribution.
+ *   * Neither the name of the http://www.broken-notebook.com nor the names of its 
+ *     contributors may be used to endorse or promote products derived from this 
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ ***********************************************************************************************/
+
+// User-configurable list of allowed HTML tags and attributes.
+// Thanks to Jake Olefsky for this little addition
+$allowed_html = '<strong><small><p><br><a><b><u><i><img><code><ul><ol><li>';
+
+// Set the max number of suggestions to return at a time.
+define('MAX_SUGGESTIONS', 10);
+
+// Set whether to use a personal dictionary.
+$usePersonalDict = true;
+
+//Set whether users are allowed to update the personal dictionary.
+$editablePersonalDict = true;
+
+// If using a personal dictionary, set the path to it.  Default is in the
+// personal_dictionary subdirectory of the location of spell_checker.php.
+$path_to_personal_dictionary = dirname(__FILE__) . "/personal_dictionary/personal_dictionary.txt";
+
+//If pspell doesn't exist, then include the pspell wrapper for aspell.
+if(!function_exists('pspell_suggest'))
+{
+	// Set the path to aspell if you need to use it.
+	define('ASPELL_BIN','/usr/bin/aspell');
+	require_once ("pspell_comp.php");
+}
+
+// Create and configure a link to the pspell module.
+
+//$pspell_config = pspell_config_create("en");
+pspell_config_mode($pspell_config, PSPELL_FAST);
+
+if($usePersonalDict)
+{
+	// Allows the use of a custom dictionary (Thanks to Dylan Thurston for this addition).
+	pspell_config_personal($pspell_config, $path_to_personal_dictionary);
+}
+
+//$pspell_link = pspell_new_config($pspell_config);
+$pspell_link = pspell_new("pt_BR");
+
+require_once("cpaint/cpaint2.inc.php"); //AJAX library file
+
+$cp = new cpaint();
+$cp->register('showSuggestions');
+$cp->register('spellCheck');
+$cp->register('switchText');
+$cp->register('addWord');
+$cp->start();
+$cp->return_data();
+
+
+/*************************************************************
+ * showSuggestions($word, $id)
+ *
+ * The showSuggestions function creates the list of up to 10
+ * suggestions to return for the given misspelled word.
+ *
+ * $word - The misspelled word that was clicked on
+ * $id - The id of the span containing the misspelled word.
+ *
+ *************************************************************/
+function showSuggestions($word, $id)
+{
+	global $editablePersonalDict; //bool to set editability of personal dictionary
+	global $pspell_link; //the global link to the pspell module
+	global $cp; //the CPAINT object
+	
+	$retVal = "";
+	
+	$suggestions = pspell_suggest($pspell_link, $word);  //an array of all the suggestions that psepll returns for $word.
+	
+	// If the number of suggestions returned by pspell is less than the maximum
+	// number, just use the number of suggestions returned.
+	$numSuggestions = count($suggestions);
+	$tmpNum = min($numSuggestions, MAX_SUGGESTIONS);
+			
+	if($tmpNum > 0)
+	{
+		//this creates the table of suggestions.
+		//in the onclick event it has a call to the replaceWord javascript function which does the actual replacing on the page
+		for($i=0; $i<$tmpNum; $i++)
+		{
+			$retVal .= "<div class=\"suggestion\" onclick=\"replaceWord('" . addslashes_custom($id) . "', '" . addslashes(utf8_encode($suggestions[$i])) . "'); return false;\">" . utf8_encode($suggestions[$i]) . "</div>";
+		}
+	
+		if($editablePersonalDict)
+		{
+			$retVal .= "<div class=\"addtoDictionary\" onclick=\"addWord('" . addslashes_custom($id) . "'); return false;\">Add To Dictionary</div>";
+		}
+	}
+	else
+	{
+		$retVal .= "No Suggestions";
+	}
+	
+	$cp->set_data($retVal);  //the return value - a string containing the table of suggestions.
+	
+} // end showSuggestions
+
+
+/*************************************************************
+ * spellCheck($string)
+ *
+ * The spellCheck function takes the string of text entered
+ * in the text box and spell checks it.  It splits the text
+ * on anything inside of < > in order to prevent html from being
+ * spell checked.  Then any text is split on spaces so that only
+ * one word is spell checked at a time.  This creates a multidimensional
+ * array.  The array is flattened.  The array is looped through
+ * ignoring the html lines and spell checking the others.  If a word
+ * is misspelled, code is wrapped around it to highlight it and to
+ * make it clickable to show the user the suggestions for that
+ * misspelled word.
+ *
+ * $string - The string of text from the text box that is to be
+ *           spell checked.
+ *
+ *************************************************************/
+function spellCheck($string, $varName)
+{
+	global $pspell_link; //the global link to the pspell module
+	global $cp; //the CPAINT object
+	$retVal = "";
+
+   	//$string = stripslashes_custom($string); //we only need to strip slashes if magic quotes are on
+
+   	$string = remove_word_junk($string);        
+
+   	//make all the returns in the text look the same
+	$string = preg_replace("/\r?\n/", "\n", $string);
+   
+   	//splits the string on any html tags, preserving the tags and putting them in the $words array
+   	$words = preg_split("/(<[^<>]*>)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
+   
+   	$numResults = count($words); //the number of elements in the array.
+
+	$misspelledCount = 0;	
+   
+	//this loop looks through the words array and splits any lines of text that aren't html tags on space, preserving the spaces.
+	for($i=0; $i<$numResults; $i++){
+		// Words alternate between real words and html tags, starting with words.
+		if(($i & 1) == 0) // Even-numbered entries are word sets.
+		{
+			$words[$i] = preg_split("/(\s+)/", $words[$i], -1, PREG_SPLIT_DELIM_CAPTURE); //then split it on the spaces
+
+			// Now go through each word and link up the misspelled ones.
+			$numWords = count($words[$i]);
+			for($j=0; $j<$numWords; $j++)
+			{
+				//preg_match("/[A-Z']{1,16}/i", $words[$i][$j], $tmp); //get the word that is in the array slot $i
+                                $tmp = split(" ", $words[$i][$j]);
+                                $tmpWord = utf8_encode($tmp[0]); //should only have one element in the array anyway, so it's just assign it to $tmpWord
+				
+				//And we replace the word in the array with the span that highlights it and gives it an onClick parameter to show the suggestions.
+				if(!pspell_check($pspell_link, $tmpWord))
+				{
+					$onClick = "onclick=\"setCurrentObject(" . $varName . "); showSuggestions('" . addslashes($tmpWord) . "', '" . $varName . "_" . $misspelledCount . "_" . addslashes($tmpWord) . "'); return false;\"";
+					$words[$i][$j] = str_replace($tmpWord, "<span " . $onClick . " id=\"" . $varName . "_" . $misspelledCount . "_" . $tmpWord . "\" class=\"highlight\">" . stripslashes($tmpWord) . "</span>", $words[$i][$j]); 
+					$misspelledCount++;
+				}
+				
+				$words[$i][$j] = str_replace("\n", "<br />", $words[$i][$j]); //replace any breaks with <br />'s, for html display
+			}//end for $j
+		}//end if
+		
+		else //otherwise, we wrap all the html tags in comments to make them not displayed
+		{
+			$words[$i] = str_replace("<", "<!--<", $words[$i]);
+			$words[$i] = str_replace(">", ">-->", $words[$i]);
+		}
+	}//end for $i
+
+	$words = flattenArray($words); //flatten the array to be one dimensional.
+	$numResults = count($words); //the number of elements in the array after it's been flattened.
+  	
+	$string = ""; //return string  
+   
+	//if there were no misspellings, start the string with a 0.
+	if($misspelledCount == 0)
+	{
+		$string = "0";
+	}
+   	
+	else //else, there were misspellings, start the string with a 1.
+	{
+   		$string = "1";
+   	}
+	
+	// Concatenate all the words/tags/etc. back into a string and append it to the result.
+	$string .= implode('', $words);
+	
+	//remove comments from around all html tags except for <a> because we don't want the links to be clickable
+	//but we want the html to be rendered in the div for preview purposes.
+	$string = preg_replace("/<!--<br( [^>]*)?>-->/i", "<br />", $string);
+	$string = preg_replace("/<!--<p( [^>]*)?>-->/i", "<p>", $string);
+	$string = preg_replace("/<!--<\/p>-->/i", "</p>", $string);
+	$string = preg_replace("/<!--<b( [^>]*)?>-->/i", "<b>", $string);
+	$string = preg_replace("/<!--<\/b>-->/i", "</b>", $string);
+	$string = preg_replace("/<!--<strong( [^>]*)?>-->/i", "<strong>", $string);
+	$string = preg_replace("/<!--<\/strong>-->/i", "</strong>", $string);
+	$string = preg_replace("/<!--<i( [^>]*)?>-->/i", "<i>", $string);
+	$string = preg_replace("/<!--<\/i>-->/i", "</i>", $string);
+	$string = preg_replace("/<!--<small( [^>]*)?>-->/i", "<small>", $string);
+	$string = preg_replace("/<!--<\/small>-->/i", "</small>", $string);
+	$string = preg_replace("/<!--<ul( [^>]*)?>-->/i", "<ul>", $string);
+	$string = preg_replace("/<!--<\/ul>-->/i", "</ul>", $string);
+	$string = preg_replace("/<!--<li( [^>]*)?>-->/i", "<li>", $string);
+	$string = preg_replace("/<!--<\/li>-->/i", "</li>", $string);
+	$string = preg_replace("/<!--<img (?:[^>]+ )?src=\"?([^\"]*)\"?[^>]*>-->/i", "<img src=\"\\1\" />", $string);
+		
+	$cp->set_data($string);  //return value - string containing all the markup for the misspelled words.
+
+} // end spellCheck
+
+
+/*************************************************************
+ * addWord($str)
+ *
+ * This function adds a word to the custom dictionary
+ *
+ * @param $str The word to be added
+ *************************************************************/
+function addWord($str)
+{
+	global $editablePersonalDict;
+	global $pspell_link; //the global link to the pspell module
+	global $cp; //the CPAINT object
+	$retVal = "";
+	pspell_add_to_personal($pspell_link, $str);
+	if($editablePersonalDict && pspell_save_wordlist($pspell_link))
+	{
+		$retVal = "Save successful!";
+	}
+	
+	else
+	{
+		$retVal = "Save Failed!";
+	}
+	
+	$cp->set_data($retVal);
+} // end addWord
+
+
+
+/*************************************************************
+ * flattenArray($array)
+ *
+ * The flattenArray function is a recursive function that takes a
+ * multidimensional array and flattens it to be a one-dimensional
+ * array.  The one-dimensional flattened array is returned.
+ *
+ * $array - The array to be flattened.
+ *
+ *************************************************************/
+function flattenArray($array)
+{
+	$flatArray = array();
+	foreach($array as $subElement)
+	{
+    	if(is_array($subElement))
+		{
+			$flatArray = array_merge($flatArray, flattenArray($subElement));
+		}
+		else
+		{
+			$flatArray[] = $subElement;
+		}
+	}
+	
+	return $flatArray;
+} // end flattenArray
+
+
+/*************************************************************
+ * stripslashes_custom($string)
+ *
+ * This is a custom stripslashes function that only strips
+ * the slashes if magic quotes are on.  This is written for
+ * compatibility with other servers in the event someone doesn't
+ * have magic quotes on.
+ *
+ * $string - The string that might need the slashes stripped.
+ *
+ *************************************************************/
+function stripslashes_custom($string)
+{
+	if(get_magic_quotes_gpc())
+	{
+		return stripslashes($string);
+	}
+	else
+	{
+		return $string;
+	}
+} // end stripslashes_custom
+
+/*************************************************************
+ * addslashes_custom($string)
+ *
+ * This is a custom addslashes function that only adds
+ * the slashes if magic quotes are off.  This is written for
+ * compatibility with other servers in the event someone doesn't
+ * have magic quotes on.
+ *
+ * $string - The string that might need the slashes added.
+ *
+ *************************************************************/
+function addslashes_custom($string)
+{
+	if(!get_magic_quotes_gpc())
+	{
+		return addslashes($string);
+	}
+	else
+	{
+		return $string;
+	}
+} // end addslashes_custom
+
+
+/*************************************************************
+ * remove_word_junk($t)
+ *
+ * This function strips out all the crap that Word tries to
+ * add to it's text in the even someone pastes in code from
+ * Word.
+ *
+ * $t - The text to be cleaned
+ *
+ *************************************************************/
+function remove_word_junk($t)
+{
+	$a=array(
+	"\xe2\x80\x9c"=>'"',
+	"\xe2\x80\x9d"=>'"',
+	"\xe2\x80\x99"=>"'",
+	"\xe2\x80\xa6"=>"...",
+	"\xe2\x80\x98"=>"'",
+	"\xe2\x80\x94"=>"---",
+	"\xe2\x80\x93"=>"--",
+	"\x85"=>"...",
+	"\221"=>"'",
+	"\222"=>"'",
+	"\223"=>'"',
+	"\224"=>'"',
+	"\x97"=>"---",
+	"\x96"=>"--"
+	);
+
+	foreach($a as $k=>$v){
+		$oa[]=$k;
+		$ra[]=$v;
+	}
+	
+	$t=trim(str_replace($oa,$ra,$t));
+	return $t;
+
+} // end remove_word_junk
+
+
+/*************************************************************
+ * switchText($string)
+ *
+ * This function prepares the text to be sent back to the text
+ * box from the div.  The comments are removed and breaks are
+ * converted back into \n's.  All the html tags that the user
+ * might have entered that aren't on the approved list:
+ * <p><br><a><b><strong><i><small><ul><li> are stripped out.
+ * The user-entered returns have already been replaced with
+ * $u2026 so that they can be preserved.  I replace all the 
+ * \n's that might have been added by the browser (Firefox does
+ * this in trying to pretty up the HTML) with " " so that 
+ * everything will look the way it did when the user typed it
+ * in the box the first time.
+ *
+ * $string - The string of html from the div that will be sent
+ *           back to the text box.
+ *
+ *************************************************************/
+function switchText($string)
+{
+	global $allowed_html;
+	global $cp; //the CPAINT object
+	$string = remove_word_junk($string);
+	$string = preg_replace("/<!--/", "", $string);
+	$string = preg_replace("/-->/", "", $string);	
+	$string = preg_replace("/\r?\n/", " ", $string);
+	$string = stripslashes_custom($string); //we only need to strip slashes if magic quotes are on
+	$string = strip_tags($string, $allowed_html);
+	$string = preg_replace('{&lt;/?span.*?&gt;}i', '', $string);
+	$string = html_entity_decode($string);
+	$cp->set_data($string); //the return value
+	
+} // end switchText
+
+?>
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/Readme.txt
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/Readme.txt	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/Readme.txt	(revision 2375)
@@ -0,0 +1,46 @@
+Version 2.8
+
+If you use this code, please give me credit.  I'd also like to see where you're using it.  So send me urls!
+
+
+See http://www.broken-notebook.com/spell_checker for the change log and further documentation.
+
+
+==============================HOW TO USE==================================
+
+Just unzip all the contents to the same directory.  You also need to make 
+sure the personal_dictionary.txt file in the personal_dictionary directory 
+is chmodded to 646.
+
+Just include cpaint2.inc.compressed.js, spell_checker.js, and spell_checker.css
+in the head of your page.
+
+The spell checker can be added to any text area on the page.  I chose to use
+the title and accesskey attributes instead of custom attributes for two reasons. 
+One, they're not used very frequently, and two, so that the code would still be 
+valid XHTML.
+
+Just add the title attribute to your text area and make it equal to "spellcheck" 
+and set the accesskey attribute equal to the location of the spell_checker.php 
+file and the Javascript will do the rest.
+
+Make sure you include a width and a height as well as a name and an id.
+Name and id should be unique.
+
+<textarea title="spellcheck" accesskey="spell_checker.php" id="spell_checker1" type="text" name="comment1" style="width: 400px; height: 200px;" />Text of box check</textarea>
+
+==========================================================================
+
+
+Note - This code requires php and the pspell module to work correctly.
+It has only been tested on Firefox and Internet Explorer so far, and I'm
+told that it works fine in Safari and some versions of Opera too.
+
+
+If you find any bugs or anything please email me and let me know.
+
+Garrison Locke
+
+gplocke@broken-notebook.com
+
+http://www.broken-notebook.com
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/js/spell_checker_compressed.js
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/js/spell_checker_compressed.js	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/js/spell_checker_compressed.js	(revision 2375)
@@ -0,0 +1,150 @@
+var cp = new cpaint(); cp.set_transfer_mode('post'); cp.set_response_type('text'); var currObj; var spellingSuggestionsDiv = null; if(document.onclick)
+{ var old_onclick = document.onclick; document.onclick = function(e)
+{ checkClickLocation(e); old_onclick(e);}
+}
+else
+{ document.onclick = checkClickLocation;}
+if(window.onload)
+{ var old_onload = window.onload; window.onload = function(e)
+{ setupSpellCheckers(e); old_onload(e);}
+}
+else
+{ window.onload = setupSpellCheckers;}
+function setupSpellCheckers()
+{ var textareas = document.getElementsByTagName('textarea'); var numSpellCheckers = 0; var tempSpellCheckers = Array(); for(var i=0; i < textareas.length; i++)
+{ if(textareas[i].getAttribute("title") == "spellcheck" || textareas[i].getAttribute("title") == "spellcheck_icons")
+{ tempSpellCheckers[numSpellCheckers] = textareas[i]; var tempWidth = tempSpellCheckers[numSpellCheckers].offsetWidth + 'px'; var tempHeight = tempSpellCheckers[numSpellCheckers].offsetHeight + 'px'; eval('spellCheckers' + numSpellCheckers + '= new ajaxSpell("spellCheckers' + numSpellCheckers + '", tempWidth, tempHeight, tempSpellCheckers[' + numSpellCheckers + '].getAttribute("accesskey"), "spellCheckDiv' + numSpellCheckers + '", tempSpellCheckers[' + numSpellCheckers + '].getAttribute("name"), tempSpellCheckers[' + numSpellCheckers + '].id, tempSpellCheckers[' + numSpellCheckers + '].title, tempSpellCheckers[' + numSpellCheckers + '].value);'); numSpellCheckers++;}
+}
+}; function ajaxSpell(varName, width, height, spellUrl, divId, name, id, title, value)
+{ currObj = this; currObj.config = new Array(); currObj.config['varName'] = varName; currObj.config['width'] = width; currObj.config['height'] = height; currObj.config['spellUrl'] = spellUrl; currObj.config['divId'] = divId; currObj.config['name'] = name; currObj.config['id'] = id; currObj.config['title'] = title; currObj.config['value'] = value; currObj.config['value'] = currObj.config['value'].replace(/<br *\/?>/gi, "\n"); currObj.config['useIcons'] = false; if(currObj.config['title'] == "spellcheck_icons")
+{ currObj.config['useIcons'] = true;}
+spellContainer = document.createElement('DIV'); spellContainer.id = currObj.config['divId']; spellContainer.className = 'spell_container'; spellContainer.style.width = currObj.config['width']; oldElement = document.getElementById(currObj.config['id']); oldElement.parentNode.replaceChild(spellContainer, oldElement); currObj.controlPanelDiv = document.createElement('DIV'); currObj.controlPanelDiv.className = 'control_panel'; document.getElementById(currObj.config['divId']).appendChild(currObj.controlPanelDiv); currObj.actionSpan = document.createElement('SPAN'); currObj.actionSpan.className = "action"; currObj.actionSpan.id = "action"; if(currObj.config['useIcons'])
+{ currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\"><img src=\"images/spellcheck.png\" width=\"16\" height=\"16\" title=\"Check Spelling &amp; Preview\" alt=\"Check Spelling &amp; Preview\" border=\"0\" /></a>";}
+else
+{ currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\">Check Spelling &amp; Preview</a>";}
+currObj.controlPanelDiv.appendChild(currObj.actionSpan); currObj.statusSpan = document.createElement('SPAN'); currObj.statusSpan.className = "status"; currObj.statusSpan.id = "status"; currObj.statusSpan.innerHTML = ""; currObj.controlPanelDiv.appendChild(currObj.statusSpan); oldElement.value = currObj.config['value']; document.getElementById(currObj.config['divId']).appendChild(oldElement); currObj.objToCheck = document.getElementById(currObj.config['id']); currObj.spellingResultsDiv = null; ajaxSpell.prototype.spellCheck = spellCheck; ajaxSpell.prototype.spellCheck_cb = spellCheck_cb; ajaxSpell.prototype.showSuggestions = showSuggestions; ajaxSpell.prototype.showSuggestions_cb = showSuggestions_cb; ajaxSpell.prototype.replaceWord = replaceWord; ajaxSpell.prototype.switchText = switchText; ajaxSpell.prototype.switchText_cb = switchText_cb; ajaxSpell.prototype.resumeEditing = resumeEditing; ajaxSpell.prototype.resetSpellChecker = resetSpellChecker; ajaxSpell.prototype.resetAction = resetAction;}; function setCurrentObject(obj)
+{ currObj = obj;}; function spellCheck_cb(new_data)
+{ with(currObj); new_data = new_data.toString(); var isThereAMisspelling = new_data.charAt(0); new_data = new_data.substring(1); if(currObj.spellingResultsDiv)
+{ currObj.spellingResultsDiv.parentNode.removeChild(spellingResultsDiv);}
+currObj.spellingResultsDiv = document.createElement('DIV'); currObj.spellingResultsDiv.className = 'edit_box'; currObj.spellingResultsDiv.style.width = currObj.objToCheck.style.width; currObj.spellingResultsDiv.style.height = currObj.objToCheck.style.height; currObj.spellingResultsDiv.innerHTML = new_data; currObj.objToCheck.style.display = "none"; currObj.objToCheck.parentNode.insertBefore(currObj.spellingResultsDiv,currObj.objToCheck); currObj.statusSpan.innerHTML = ""; if(currObj.config['useIcons'])
+{ currObj.actionSpan.innerHTML = "<a class=\"resume_editing\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".resumeEditing();\"><img src=\"images/page_white_edit.png\" width=\"16\" height=\"16\" title=\"Resume Editing\" alt=\"Resume Editing\" border=\"0\" /></a>";}
+else
+{ currObj.actionSpan.innerHTML = "<a class=\"resume_editing\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".resumeEditing();\">Resume Editing</a>";}
+if(isThereAMisspelling != "1")
+{ if(currObj.config['useIcons'])
+{ currObj.statusSpan.innerHTML = "<img src=\"images/accept.png\" width=\"16\" height=\"16\" title=\"No Misspellings Found\" alt=\"No Misspellings Found\" border=\"0\" />";}
+else
+{ currObj.statusSpan.innerHTML = "No Misspellings Found";}
+currObj.objToCheck.disabled = false;}
+}; function spellCheck() { with(currObj); var query; if(currObj.spellingResultsDiv)
+{ currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv); currObj.spellingResultsDiv = null;}
+if(currObj.config['useIcons'])
+{ currObj.actionSpan.innerHTML = "<img src=\"images/spellcheck.png\" width=\"16\" height=\"16\" title=\"Check Spelling &amp; Preview\" alt=\"Check Spelling &amp; Preview\" border=\"0\" />";}
+else
+{ currObj.actionSpan.innerHTML = "<a class=\"check_spelling\">Check Spelling &amp; Preview</a>";}
+if(currObj.config['useIcons'])
+{ currObj.statusSpan.innerHTML = "<img src=\"images/working.gif\" width=\"16\" height=\"16\" title=\"Checking...\" alt=\"Checking...\" border=\"0\" />";}
+else
+{ currObj.statusSpan.innerHTML = "Checking...";}
+query = currObj.objToCheck.value; query = query.replace(/\r?\n/gi, "<br />"); cp.call(currObj.config['spellUrl'], 'spellCheck', spellCheck_cb, query, currObj.config['varName']);}; function addWord(id)
+{ var wordToAdd = document.getElementById(id).innerHTML; with(currObj); if(spellingSuggestionsDiv)
+{ spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv); spellingSuggestionsDiv = null;}
+if(currObj.config['useIcons'])
+{ currObj.statusSpan.innerHTML = "<img src=\"images/working.gif\" width=\"16\" height=\"16\" title=\"Adding Word...\" alt=\"Adding Word...\" border=\"0\" />";}
+else
+{ currObj.statusSpan.innerHTML = "Adding Word...";}
+cp.call(currObj.config['spellUrl'], 'addWord', addWord_cb, wordToAdd);}; function addWord_cb(returnedData)
+{ alert(returnedData); with(currObj); currObj.statusSpan.innerHTML = ""; resumeEditing(); spellCheck();}; function checkClickLocation(e)
+{ if(spellingSuggestionsDiv)
+{ if(spellingSuggestionsDiv.ignoreNextClick){ spellingSuggestionsDiv.ignoreNextClick = false;}
+else
+{ var theTarget = getTarget(e); if(theTarget != spellingSuggestionsDiv)
+{ spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv); spellingSuggestionsDiv = null;}
+}
+}
+return true;}; function getTarget(e)
+{ var value; if(checkBrowser() == "ie")
+{ value = window.event.srcElement;}
+else
+{ value = e.target;}
+return value;}; function checkBrowser()
+{ var theAgent = navigator.userAgent.toLowerCase(); if(theAgent.indexOf("msie") != -1)
+{ if(theAgent.indexOf("opera") != -1)
+{ return "opera";}
+else
+{ return "ie";}
+}
+else if(theAgent.indexOf("netscape") != -1)
+{ return "netscape";}
+else if(theAgent.indexOf("firefox") != -1)
+{ return "firefox";}
+else if(theAgent.indexOf("mozilla/5.0") != -1)
+{ return "mozilla";}
+else if(theAgent.indexOf("\/") != -1)
+{ if(theAgent.substr(0,theAgent.indexOf('\/')) != 'mozilla')
+{ return navigator.userAgent.substr(0,theAgent.indexOf('\/'));}
+else
+{ return "netscape";}
+}
+else if(theAgent.indexOf(' ') != -1)
+{ return navigator.userAgent.substr(0,theAgent.indexOf(' '));}
+else
+{ return navigator.userAgent;}
+}; function showSuggestions_cb(new_data)
+{ with(currObj); spellingSuggestionsDiv.innerHTML = new_data; spellingSuggestionsDiv.style.display = 'block'; currObj.statusSpan.innerHTML = "";}; function showSuggestions(word, id)
+{ with(currObj); if(currObj.config['useIcons'])
+{ currObj.statusSpan.innerHTML = "<img src=\"images/working.gif\" width=\"16\" height=\"16\" title=\"Searching...\" alt=\"Searching...\" border=\"0\" />";}
+else
+{ currObj.statusSpan.innerHTML = "Searching...";}
+var x = findPosXById(id); var y = findPosYById(id); var scrollPos = 0; if(checkBrowser() != "ie")
+{ scrollPos = currObj.spellingResultsDiv.scrollTop;}
+if(spellingSuggestionsDiv)
+{ spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);}
+spellingSuggestionsDiv = document.createElement('DIV'); spellingSuggestionsDiv.style.display = "none"; spellingSuggestionsDiv.className = 'suggestion_box'; spellingSuggestionsDiv.style.position = 'absolute'; spellingSuggestionsDiv.style.left = x + 'px'; spellingSuggestionsDiv.style.top = (y+16-scrollPos) + 'px'; spellingSuggestionsDiv.ignoreNextClick = true; document.body.appendChild(spellingSuggestionsDiv); cp.call(currObj.config['spellUrl'], 'showSuggestions', showSuggestions_cb, word, id);}; function replaceWord(id, newWord)
+{ document.getElementById(id).innerHTML = trim(newWord); if(spellingSuggestionsDiv)
+{ spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv); spellingSuggestionsDiv = null;}
+document.getElementById(id).className = "corrected_word";}; function switchText()
+{ with(currObj); var text = currObj.spellingResultsDiv.innerHTML; text = text.replace(/<br *\/?>/gi, "~~~"); text = '*' + text; cp.call(currObj.config['spellUrl'], 'switchText', switchText_cb, text);}; function switchText_cb(new_string)
+{ with(currObj); new_string = new_string.replace(/~~~/gi, "\n"); new_string = new_string.substr(1); currObj.objToCheck.style.display = "none"; currObj.objToCheck.value = new_string; currObj.objToCheck.disabled = false; if(currObj.spellingResultsDiv)
+{ currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv); currObj.spellingResultsDiv = null;}
+currObj.objToCheck.style.display = "block"; currObj.resetAction();}; function resumeEditing()
+{ with(currObj); if(currObj.config['useIcons'])
+{ currObj.actionSpan.innerHTML = "<a class=\"resume_editing\"><img src=\"images/page_white_edit.png\" width=\"16\" height=\"16\" title=\"Resume Editing\" alt=\"Resume Editing\" border=\"0\" /></a>";}
+else
+{ currObj.actionSpan.innerHTML = "<a class=\"resume_editing\">Resume Editing</a>";}
+if(currObj.config['useIcons'])
+{ currObj.statusSpan.innerHTML = "<img src=\"images/working.gif\" width=\"16\" height=\"16\" title=\"Working...\" alt=\"Working...\" border=\"0\" />";}
+else
+{ currObj.statusSpan.innerHTML = "Working...";}
+if(spellingSuggestionsDiv)
+{ spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv); spellingSuggestionsDiv = null;}
+currObj.switchText();}; function resetAction()
+{ with(currObj); if(currObj.config['useIcons'])
+{ currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\"><img src=\"images/spellcheck.png\" width=\"16\" height=\"16\" title=\"Check Spelling &amp; Preview\" alt=\"Check Spelling &amp; Preview\" border=\"0\" /></a>";}
+else
+{ currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\">Check Spelling &amp; Preview</a>";}
+currObj.statusSpan.innerHTML = "";}; function resetSpellChecker()
+{ with(currObj); currObj.resetAction(); currObj.objToCheck.value = ""; currObj.objToCheck.style.display = "block"; currObj.objToCheck.disabled = false; if(currObj.spellingResultsDiv)
+{ currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv); currObj.spellingResultsDiv = null;}
+if(spellingSuggestionsDiv)
+{ spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv); spellingSuggestionsDiv = null;}
+currObj.statusSpan.style.display = "none";}; function findPosXById(object)
+{ var curleft = 0; var obj = document.getElementById(object); if(obj.offsetParent)
+{ while(obj.offsetParent)
+{ curleft += obj.offsetLeft - obj.scrollLeft; obj = obj.offsetParent;}
+}
+else if(obj.x)
+{ curleft += obj.x;}
+return curleft;}; function findPosYById(object)
+{ var curtop = 0;var curtop = 0; var obj = document.getElementById(object); if(obj.offsetParent)
+{ while(obj.offsetParent)
+{ curtop += obj.offsetTop - obj.scrollTop; obj = obj.offsetParent;}
+}
+else if(obj.y)
+{ curtop += obj.y;}
+return curtop;}; function trim(s)
+{ while(s.substring(0,1) == ' ')
+{ s = s.substring(1,s.length);}
+while(s.substring(s.length-1,s.length) == ' ')
+{ s = s.substring(0,s.length-1);}
+return s;}; 
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/js/spell_checker.js
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/js/spell_checker.js	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/js/spell_checker.js	(revision 2375)
@@ -0,0 +1,913 @@
+/*************************************************************
+ * AJAX Spell Checker - Version 2.8
+ * (C) 2005 - Garrison Locke
+ * 
+ * This spell checker is built in the style of the Gmail spell
+ * checker.  It uses AJAX to communicate with the backend without
+ * requiring the page be reloaded.  If you use this code, please
+ * give me credit and a link to my site would be nice.
+ * http://www.broken-notebook.com.
+ *
+ * Copyright (c) 2005, Garrison Locke
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice, 
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright notice, 
+ *     this list of conditions and the following disclaimer in the documentation 
+ *     and/or other materials provided with the distribution.
+ *   * Neither the name of the http://www.broken-notebook.com nor the names of its 
+ *     contributors may be used to endorse or promote products derived from this 
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ *************************************************************/
+
+var cp;
+var currObj; //the current spell checker being used
+var spellingSuggestionsDiv;
+var old_onclick
+cp = new cpaint();
+cp.set_transfer_mode('post');
+cp.set_response_type('text');
+function beginSpellCheck(){
+
+    //var cp = new cpaint();
+   // alert("Seta globals");
+    //cp = new cpaint();
+    //cp.set_transfer_mode('post');
+    //cp.set_response_type('text');
+    //cp.set_debug(1);
+
+    //var currObj; //the current spell checker being used
+    //var spellingSuggestionsDiv = null;  // Auto-generated suggestions div
+
+    
+    spellingSuggestionsDiv = null;
+
+    //alert("Seta globals");
+
+
+// If there are already any onclick handlers loaded in the page, we'll add
+// our onclick handler first and then call the old one, rather than completely
+// overriding it.  The checkClickLocation is used to hide the suggestions div
+// when the user clicks outside it.
+    /*if(document.onclick)
+    {
+	old_onclick = document.onclick;
+       
+	document.onclick = function(e)
+	{
+		checkClickLocation(e);
+		old_onclick(e);
+	}
+}
+else
+{
+	//document.onclick = checkClickLocation;
+}*/
+
+//alert("Inicio");
+// If there are already any onload handlers loaded in the page, we'll add our onload
+// handler first and then call the old one, rather than completely overriding it.
+if(window.onload)
+{
+        window.onload = new setupSpellCheckers();
+	
+        /*var old_onload = window.onload;
+	window.onload = function(e)
+	{
+		var test = new setupSpellCheckers(e);
+		old_onload(e);
+	}*/
+}
+else
+{
+	window.onload = new setupSpellCheckers();
+}
+
+//window.onload =new setupSpellCheckers();
+//alert("Fim");
+} // fecha funï¿œï¿œo
+
+
+
+
+
+/*************************************************************
+ * function setupSpellCheckers()
+ *
+ * This function goes through the page and finds all the 
+ * textareas.  It then checks the title attribute for either
+ * spellcheck or spellcheck_icons to determine whether or not
+ * it should add a spellchecker to that textarea.
+ *************************************************************/
+function setupSpellCheckers()
+{
+	var ifr = document.getElementsByTagName('iframe')[0];
+	var numSpellCheckers = 0;
+	var tempSpellCheckers = Array();
+      //  alert("NOT OK");
+
+         //novo
+         //if (textareas){
+        if(ifr.contentWindow)
+           ifr=ifr.contentWindow.document;
+        else
+           ifr=ifr.contentDocument;
+       //  alert(ifr.body.innerHTML);
+         var texto = ifr.body.innerHTML;
+
+       //  alert("valor textarea");
+       //  alert(texto);
+        // //tempSpellCheckers[numSpellCheckers] = textareas;
+         var tempWidth = "300";
+         var tempHeight = "300";
+         
+        // alert("Antes AjaxSpell");
+         eval('spellCheckers' + numSpellCheckers + '= new ajaxSpell("spellCheckers' + numSpellCheckers + '", tempWidth, tempHeight, "../spell_checker/spell_checker.php", "body_position_1", "Nome", "body_1", "Titulo", texto);');
+
+        
+         //new  ajaxSpell('spellTexto', tempWidth, tempHeight, 'spell_checker.php' ,'body_position_1', 'Nome','body_1', 'Titulo', texto);
+         
+         //ajaxSpell("", "", "", "" ,"", "","", "", "");
+
+        //}
+
+	/*for(var i=0; i < textareas.length; i++)
+	{
+		alert("ok iframe");
+                if(textareas[i].getAttribute("title") == "spellcheck" || textareas[i].getAttribute("title") == "spellcheck_icons")
+		{
+			tempSpellCheckers[numSpellCheckers] = textareas[i];
+			
+			//create a new spellchecker for this textarea
+			var tempWidth = tempSpellCheckers[numSpellCheckers].offsetWidth + 'px';
+			var tempHeight = tempSpellCheckers[numSpellCheckers].offsetHeight + 'px';
+			eval('spellCheckers' + numSpellCheckers + '= new ajaxSpell("spellCheckers' + numSpellCheckers + '", tempWidth, tempHeight, tempSpellCheckers[' + numSpellCheckers + '].getAttribute("accesskey"), "spellCheckDiv' + numSpellCheckers + '", tempSpellCheckers[' + numSpellCheckers + '].getAttribute("name"), tempSpellCheckers[' + numSpellCheckers + '].id, tempSpellCheckers[' + numSpellCheckers + '].title, tempSpellCheckers[' + numSpellCheckers + '].value);');
+			numSpellCheckers++;
+		}
+	}*/
+	
+}; // end setInit
+
+
+/*************************************************************
+ * ajaxSpell(varName, width, height, spellUrl, divId, name, id)
+ *
+ * This is the constructor that creates a new ajaxSpell object.
+ * All of it is dynamically generated so the user doesn't have
+ * to add a bunch of crap to their site.
+ *
+ * @param varName The name of the variable that the object is
+ *                assigned to (must be unique and the same as the variable)
+ * @param width The width of the spell checker
+ * @param height The height of the spell checker
+ * @param spellUrl The url of the spell_checker.php code
+ * @param divId The id of the div that the spell checker is 
+ *              contained in (must be unique)
+ * @param name The name of the textarea form element
+ * @param id The id of the spell checker textarea (must be unique)
+ *************************************************************/
+function ajaxSpell(varName, width, height, spellUrl, divId, name, id, title, value)
+{
+	//alert("ajaxSpell");
+
+        currObj = this;
+
+	currObj.config               = new Array();         //the array of configuration options
+	currObj.config['varName']    = varName;             //the name of the variable that this instance is stored in
+	currObj.config['width']      = width;               //the width of the textarea
+	currObj.config['height']     = height;              //the height of the textarea
+	currObj.config['spellUrl']   = spellUrl;            //url to spell checker php code (spell_checker.php by default);
+	currObj.config['divId']      = divId;               //the id of the div that the spell checker element is in
+	currObj.config['name']       = name;                //what you want the form element's name to be
+	currObj.config['id']         = id;                  //the unique id of the spell_checker textarea
+	currObj.config['title']      = title;               //the title (specifies whether to use icons or not);
+	currObj.config['value']      = value;               //the value of the text box when the page was loaded
+
+	currObj.config['value']      = currObj.config['value'].replace(/<br *\/?>/gi, "\n");
+	
+	currObj.config['useIcons'] = false;
+	
+	if(currObj.config['title'] == "spellcheck_icons")
+	{
+		currObj.config['useIcons'] = true;
+	}
+	
+	spellContainer = document.createElement('DIV');
+	spellContainer.id = currObj.config['divId'];
+	spellContainer.className = 'spell_container';
+	spellContainer.style.width = currObj.config['width'];
+
+	oldElement = document.getElementById(currObj.config['id']);
+
+	oldElement.parentNode.replaceChild(spellContainer, oldElement);
+	
+	//generate the div to hold the spell checker controls
+	currObj.controlPanelDiv = document.createElement('DIV');
+	currObj.controlPanelDiv.className = 'control_panel';
+	document.getElementById(currObj.config['divId']).appendChild(currObj.controlPanelDiv);
+	
+	//the span that toggles between spell checking and editing
+	currObj.actionSpan = document.createElement('SPAN');
+	currObj.actionSpan.className = "action";
+	currObj.actionSpan.id = "action";
+	if(currObj.config['useIcons'])
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\"><img src=\"images/spellcheck.png\" width=\"16\" height=\"16\" title=\"Check Spelling &amp; Preview\" alt=\"Check Spelling &amp; Preview\" border=\"0\" /></a>";
+	}
+	else
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\">Check Spelling &amp; Preview</a>";
+	}
+	currObj.controlPanelDiv.appendChild(currObj.actionSpan);
+	
+	//the span that lets the user know of the status of the spell checker
+	currObj.statusSpan = document.createElement('SPAN');
+	currObj.statusSpan.className = "status";
+	currObj.statusSpan.id = "status";
+	currObj.statusSpan.innerHTML = "";
+	currObj.controlPanelDiv.appendChild(currObj.statusSpan);
+	
+	//the textarea to be spell checked
+	oldElement.value = currObj.config['value'];
+	document.getElementById(currObj.config['divId']).appendChild(oldElement);
+	
+	currObj.objToCheck              = document.getElementById(currObj.config['id']);      //the actual object we're spell checking
+	currObj.spellingResultsDiv      = null;                                               // Auto-generated results div
+		
+	//prototypes for the ajaxSpell objects
+	ajaxSpell.prototype.spellCheck           = spellCheck;
+	ajaxSpell.prototype.spellCheck_cb        = spellCheck_cb;
+	ajaxSpell.prototype.showSuggestions      = showSuggestions;
+	ajaxSpell.prototype.showSuggestions_cb   = showSuggestions_cb;
+	ajaxSpell.prototype.replaceWord          = replaceWord;
+	ajaxSpell.prototype.switchText           = switchText;
+	ajaxSpell.prototype.switchText_cb        = switchText_cb;
+	ajaxSpell.prototype.resumeEditing        = resumeEditing;
+	ajaxSpell.prototype.resetSpellChecker    = resetSpellChecker;
+	ajaxSpell.prototype.resetAction          = resetAction;
+}; // end ajaxSpell
+
+
+/*************************************************************
+ * setCurrentObject
+ *
+ * This sets the current object to be the spell checker that
+ * the user is currently using.
+ *
+ * @param obj The spell checker currently being used
+ *************************************************************/
+function setCurrentObject(obj)
+{
+	currObj = obj;
+        
+}; // end setCurrentObject
+
+
+/*************************************************************
+ * spellCheck_cb
+ *
+ * This is the callback function that the spellCheck php function
+ * returns the spell checked data to.  It sets the results div
+ * to contain the markedup misspelled data and changes the status
+ * message.  It also sets the width and height of the results
+ * div to match the element that's being checked.
+ * If there are no misspellings then new_data is the empty 
+ * string and the status is set to "No Misspellings Found".
+ *
+ * @param new_data The marked up misspelled data returned from php.
+ *************************************************************/
+function spellCheck_cb(new_data)
+{
+	with(currObj);
+	new_data = new_data.toString();
+	var isThereAMisspelling = new_data.charAt(0);
+	new_data = new_data.substring(1);
+		
+	if(currObj.spellingResultsDiv)
+	{
+		currObj.spellingResultsDiv.parentNode.removeChild(spellingResultsDiv);
+	}
+	
+	currObj.spellingResultsDiv = document.createElement('DIV');
+	currObj.spellingResultsDiv.className = 'edit_box';
+	currObj.spellingResultsDiv.style.width = currObj.objToCheck.style.width;
+	currObj.spellingResultsDiv.style.height = currObj.objToCheck.style.height;
+	currObj.spellingResultsDiv.innerHTML = new_data;
+	
+	currObj.objToCheck.style.display = "none";
+	currObj.objToCheck.parentNode.insertBefore(currObj.spellingResultsDiv,currObj.objToCheck);
+	currObj.statusSpan.innerHTML = "";
+	
+	if(currObj.config['useIcons'])
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"resume_editing\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".resumeEditing();\"><img src=\"images/page_white_edit.png\" width=\"16\" height=\"16\" title=\"Resume Editing\" alt=\"Resume Editing\" border=\"0\" /></a>";
+	}
+	else
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"resume_editing\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".resumeEditing();\">Resume Editing</a>";
+	}
+		
+	if(isThereAMisspelling != "1")
+	{
+		if(currObj.config['useIcons'])
+		{
+			currObj.statusSpan.innerHTML = "<img src=\"images/accept.png\" width=\"16\" height=\"16\" title=\"No Misspellings Found\" alt=\"No Misspellings Found\" border=\"0\" />";
+		}
+		else
+		{
+			currObj.statusSpan.innerHTML = "No Misspellings Found";
+		}
+		currObj.objToCheck.disabled = false;
+	}
+}; // end spellCheck_cb
+
+
+/*************************************************************
+ * spellCheck()
+ *
+ * The spellCheck javascript function sends the text entered by
+ * the user in the text box to php to be spell checked.  It also
+ * sets the status message to "Checking..." because it's currently
+ * checking the spelling.
+ *************************************************************/
+function spellCheck() {
+       	with(currObj);
+	var query;
+	
+	if(currObj.spellingResultsDiv)
+	{
+		currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv);
+		currObj.spellingResultsDiv = null;
+	}
+	
+	if(currObj.config['useIcons'])
+	{
+		currObj.actionSpan.innerHTML = "<img src=\"images/spellcheck.png\" width=\"16\" height=\"16\" title=\"Check Spelling &amp; Preview\" alt=\"Check Spelling &amp; Preview\" border=\"0\" />";
+	}
+	else
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\">Check Spelling &amp; Preview</a>";
+	}
+	
+	if(currObj.config['useIcons'])
+	{
+		currObj.statusSpan.innerHTML = "<img src=\"images/working.gif\" width=\"16\" height=\"16\" title=\"Checking...\" alt=\"Checking...\" border=\"0\" />";
+	}
+	else
+	{
+		currObj.statusSpan.innerHTML = "Checking...";
+	}
+	
+        
+        //Adicionado por Nathalie
+         var ifr= currObj.objToCheck;
+         if(ifr.contentWindow)
+            ifr=ifr.contentWindow.document;
+         else
+            ifr=ifr.contentDocument;
+            alert(ifr.body.innerHTML);
+
+         var texto = ifr.body.innerHTML;
+        
+        
+        query = texto;
+        //query = currObj.objToCheck.value;
+        
+	query = query.replace(/\r?\n/gi, "<br />");
+	
+	cp.call(currObj.config['spellUrl'], 'spellCheck', spellCheck_cb, query, currObj.config['varName']);
+}; // end spellcheck
+
+
+
+/*************************************************************
+ * addWord
+ *
+ * The addWord function adds a word to the custom dictionary
+ * file.
+ *
+ * @param id The id of the span that contains the word to be added
+ *************************************************************/
+function addWord(id)
+{
+	var wordToAdd = document.getElementById(id).innerHTML;
+	
+	with(currObj);
+	
+	if(spellingSuggestionsDiv)
+	{
+		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
+		spellingSuggestionsDiv = null;
+	}
+	
+	if(currObj.config['useIcons'])
+	{
+		currObj.statusSpan.innerHTML = "<img src=\"images/working.gif\" width=\"16\" height=\"16\" title=\"Adding Word...\" alt=\"Adding Word...\" border=\"0\" />";
+	}
+	else
+	{
+		currObj.statusSpan.innerHTML = "Adding Word...";
+	}
+	
+	cp.call(currObj.config['spellUrl'], 'addWord', addWord_cb, wordToAdd);
+
+}; // end addWord
+
+/*************************************************************
+ * addWord_cb
+ *
+ * The addWord_cb function is a callback function that
+ * PHP's addWord function returns to.  It recieves the
+ * return status of the add to word to personal dictionary call.
+ * It hides the status item.
+ *
+ * @param returnedData The return code from PHP.
+ *************************************************************/
+function addWord_cb(returnedData)
+{
+	//alert(returnedData);
+	with(currObj);
+	currObj.statusSpan.innerHTML = "";
+	resumeEditing();
+	spellCheck();
+}; // end addWord_cb
+
+
+
+/*************************************************************
+ * checkClickLocation(e)
+ *
+ * This function is called by the event listener when the user
+ * clicks on anything.  It is used to close the suggestion div
+ * if the user clicks anywhere that's not inside the suggestion
+ * div.  It just checks to see if the name of what the user clicks
+ * on is not "suggestions" then hides the div if it's not.
+ *
+ * @param e The event, in this case the user clicking somewhere on
+ *          the page.
+ *************************************************************/
+function checkClickLocation(e)
+{
+
+   // alert("checkClickLocation");
+    if(spellingSuggestionsDiv)
+	{
+		// Bah.  There's got to be a better way to deal with this, but the click
+		// on a word to get suggestions starts up a race condition between
+		// showing and hiding the suggestion box, so we'll ignore the first
+		// click.
+		if(spellingSuggestionsDiv.ignoreNextClick){
+			spellingSuggestionsDiv.ignoreNextClick = false;
+		}
+		else
+		{
+			var theTarget = getTarget(e);
+			
+			if(theTarget != spellingSuggestionsDiv)
+			{
+				spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
+				spellingSuggestionsDiv = null;
+			}
+		}
+        }
+	
+	return true; // Allow other handlers to continue.
+}; //end checkClickLocation
+
+
+/*************************************************************
+ * getTarget
+ *
+ * The get target function gets the correct target of the event.
+ * This function is required because IE handles the events in
+ * a different (wrong) manner than the rest of the browsers.
+ *
+ * @param e The target, in this case the user clicking somewhere on
+ *     the page.
+ *
+ *************************************************************/
+function getTarget(e)
+{
+	var value;
+	if(checkBrowser() == "ie")
+	{
+		value = window.event.srcElement;
+	}
+	else
+	{
+		value = e.target;
+	}
+	return value;
+}; //end getTarget
+
+
+/*************************************************************
+ * checkBrowser()
+ *
+ * The checkBrowser function simply checks to see what browser
+ * the user is using and returns a string containing the browser
+ * type.
+ *
+ * @return string The browser type
+ *************************************************************/
+function checkBrowser()
+{
+	var theAgent = navigator.userAgent.toLowerCase();
+	if(theAgent.indexOf("msie") != -1)
+	{
+		if(theAgent.indexOf("opera") != -1)
+		{
+			return "opera";
+		}
+		else
+		{
+			return "ie";
+		}
+	}
+	else if(theAgent.indexOf("netscape") != -1)
+	{
+		return "netscape";
+	}
+	else if(theAgent.indexOf("firefox") != -1)
+	{
+		return "firefox";
+	}
+	else if(theAgent.indexOf("mozilla/5.0") != -1)
+	{
+		return "mozilla";
+	}
+	else if(theAgent.indexOf("\/") != -1)
+	{
+		if(theAgent.substr(0,theAgent.indexOf('\/')) != 'mozilla')
+		{
+			return navigator.userAgent.substr(0,theAgent.indexOf('\/'));
+		}
+		else
+		{
+			return "netscape";
+		} 
+	}
+	else if(theAgent.indexOf(' ') != -1)
+	{
+		return navigator.userAgent.substr(0,theAgent.indexOf(' '));
+	}
+	else
+	{ 
+		return navigator.userAgent;
+	}
+}; // end checkBrowser
+
+
+/*************************************************************
+ * showSuggestions_cb
+ *
+ * The showSuggestions_cb function is a callback function that
+ * php's showSuggestions function returns to.  It sets the 
+ * suggestions table to contain the new data and then displays
+ * the suggestions div.  It also clears the status message.
+ *
+ * @param new_data The suggestions table returned from php.
+ *************************************************************/
+function showSuggestions_cb(new_data)
+{
+	with(currObj);
+	spellingSuggestionsDiv.innerHTML = new_data;
+	spellingSuggestionsDiv.style.display = 'block';
+	currObj.statusSpan.innerHTML = "";
+}; //end showSuggestions_cb
+
+
+/*************************************************************
+ * showSuggestions
+ *
+ * The showSuggestions function calls the showSuggestions php
+ * function to get suggestions for the misspelled word that the
+ * user has clicked on.  It sets the status to "Searching...",
+ * hides the suggestions div, finds the x and y position of the
+ * span containing the misspelled word that user clicked on so 
+ * the div can be displayed in the correct location, and then
+ * calls the showSuggestions php function with the misspelled word
+ * and the id of the span containing it.
+ *
+ * @param word The misspelled word that the user clicked on
+ * @param id The id of the span that contains the misspelled word
+ *************************************************************/
+function showSuggestions(word, id)
+{
+	with(currObj);
+	if(currObj.config['useIcons'])
+	{
+		currObj.statusSpan.innerHTML = "<img src=\"images/working.gif\" width=\"16\" height=\"16\" title=\"Searching...\" alt=\"Searching...\" border=\"0\" />";
+	}
+	else
+	{
+		currObj.statusSpan.innerHTML = "Searching...";
+	}
+	var x = findPosXById(id);
+	var y = findPosYById(id);
+	
+	var scrollPos = 0;
+	if(checkBrowser() != "ie")
+	{
+		scrollPos = currObj.spellingResultsDiv.scrollTop;
+	}
+
+	if(spellingSuggestionsDiv)
+	{
+		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
+	}
+	spellingSuggestionsDiv = document.createElement('DIV');
+	spellingSuggestionsDiv.style.display = "none";
+	spellingSuggestionsDiv.className = 'suggestion_box';
+	spellingSuggestionsDiv.style.position = 'absolute';
+	spellingSuggestionsDiv.style.left = x + 'px';
+	spellingSuggestionsDiv.style.top = (y+16-scrollPos) + 'px';
+	
+	// Bah. There's got to be a better way to deal with this, but the click
+	// on a word to get suggestions starts up a race condition between
+	// showing and hiding the suggestion box, so we'll ignore the first
+	// click.
+	spellingSuggestionsDiv.ignoreNextClick = true;
+	
+	document.body.appendChild(spellingSuggestionsDiv);
+	
+	cp.call(currObj.config['spellUrl'], 'showSuggestions', showSuggestions_cb, word, id);
+}; // end showSuggestions
+
+
+/*************************************************************
+ * replaceWord
+ *
+ * The replaceWord function takes the id of the misspelled word
+ * that the user clicked on and replaces the innerHTML of that
+ * span with the new word that the user selects from the suggestion
+ * div.  It hides the suggestions div and changes the color of
+ * the previously misspelled word to green to let the user know
+ * it has been changed.  It then calls the switchText php function
+ * with the innerHTML of the div to update the text of the text box.
+ *
+ * @param id The id of the span that contains the word to be replaced
+ * @param newWord The word the user selected from the suggestions div
+ *                to replace the misspelled word.
+ *************************************************************/
+function replaceWord(id, newWord)
+{
+	document.getElementById(id).innerHTML = trim(newWord);
+	if(spellingSuggestionsDiv)
+	{
+		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
+		spellingSuggestionsDiv = null;
+	}
+	document.getElementById(id).className = "corrected_word";
+}; // end replaceWord
+
+
+/*************************************************************
+ * switchText
+ *
+ * The switchText function is a funtion is called when the user
+ * clicks on resume editing (or submits the form).  It calls the
+ * php function to switchText and uncomments the html and replaces
+ * breaks and everything.  Here all the breaks that the user has
+ * typed are replaced with %u2026.  Firefox does this goofy thing
+ * where it cleans up the display of your html, which adds in \n's
+ * where you don't want them.  So I replace the user-entered returns
+ * with something unique so that I can rip out all the breaks that
+ * the browser might add and we don't want.
+ *************************************************************/
+function switchText()
+{
+	with(currObj);
+	var text = currObj.spellingResultsDiv.innerHTML;
+	text = text.replace(/<br *\/?>/gi, "~~~");
+	// Work around a cpaint/safari bug by prefixing an asterisk to the text so that the text is never completely empty
+	text = '*' + text;
+	cp.call(currObj.config['spellUrl'], 'switchText', switchText_cb, text);
+}; // end switchText
+
+
+/*************************************************************
+ * switchText_cb
+ *
+ * The switchText_cb function is a call back funtion that the
+ * switchText php function returns to.  I replace all the %u2026's
+ * with returns.  It then replaces the text in the text box with 
+ * the corrected text fromt he div.
+ *
+ * @param new_string The corrected text from the div.
+ *
+ *************************************************************/
+function switchText_cb(new_string)
+{
+	with(currObj);
+	new_string = new_string.replace(/~~~/gi, "\n");
+
+	// Remove the prefixed asterisk that was added in switchText().
+	new_string = new_string.substr(1);
+	currObj.objToCheck.style.display = "none";
+	 var ifr= currObj.objToCheck;
+         if(ifr.contentWindow)
+            ifr=ifr.contentWindow.document;
+         else
+            ifr=ifr.contentDocument;
+            alert(ifr.body.innerHTML);
+         var texto = ifr.body.innerHTML;
+         ifr.body.innerHTML = new_string;
+	currObj.objToCheck.disabled = false;
+	if(currObj.spellingResultsDiv)
+	{
+		currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv);
+		currObj.spellingResultsDiv = null;
+	}
+	currObj.objToCheck.style.display = "block";
+	currObj.resetAction();
+}; // end switchText_cb
+
+
+/*************************************************************
+ * resumeEditing
+ *
+ * The resumeEditing function is called when the user is in the
+ * correction mode and wants to return to the editing mode.  It
+ * hides the results div and the suggestions div, then enables
+ * the text box and unhides the text box.  It also calls
+ * resetAction() to reset the status message.
+ *************************************************************/
+function resumeEditing()
+{
+	with(currObj);
+	if(currObj.config['useIcons'])
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"resume_editing\"><img src=\"images/page_white_edit.png\" width=\"16\" height=\"16\" title=\"Resume Editing\" alt=\"Resume Editing\" border=\"0\" /></a>";
+	}
+	else
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"resume_editing\">Resume Editing</a>";
+	}
+	if(currObj.config['useIcons'])
+	{
+		currObj.statusSpan.innerHTML = "<img src=\"images/working.gif\" width=\"16\" height=\"16\" title=\"Working...\" alt=\"Working...\" border=\"0\" />";
+	}
+	else
+	{
+		currObj.statusSpan.innerHTML = "Working...";
+	}
+	
+	if(spellingSuggestionsDiv)
+	{
+		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
+		spellingSuggestionsDiv = null;
+	}
+	
+	currObj.switchText();
+}; // end resumeEditing
+
+
+/*************************************************************
+ * resetAction
+ *
+ * The resetAction function just resets the status message to
+ * the default action of "Check Spelling".
+ *************************************************************/
+function resetAction()
+{
+	with(currObj);
+	if(currObj.config['useIcons'])
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\"><img src=\"images/spellcheck.png\" width=\"16\" height=\"16\" title=\"Check Spelling &amp; Preview\" alt=\"Check Spelling &amp; Preview\" border=\"0\" /></a>";
+	}
+	else
+	{
+		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\">Check Spelling &amp; Preview</a>";
+	}
+
+	currObj.statusSpan.innerHTML = "";
+}; // end resetAction
+
+
+/*************************************************************
+ * resetSpellChecker
+ *
+ * The resetSpellChecker function resets the entire spell checker
+ * to the defaults.
+ *************************************************************/
+function resetSpellChecker()
+{
+	with(currObj);
+	currObj.resetAction();
+	
+	currObj.objToCheck.value = "";
+	currObj.objToCheck.style.display = "block";
+	currObj.objToCheck.disabled = false;
+	
+	if(currObj.spellingResultsDiv)
+	{
+		currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv);
+		currObj.spellingResultsDiv = null;
+	}
+	if(spellingSuggestionsDiv)
+	{
+		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
+		spellingSuggestionsDiv = null;
+	}
+	currObj.statusSpan.style.display = "none";
+	
+}; // end resetSpellChecker
+
+
+/*************************************************************
+ * findPosX
+ *
+ * The findPosX function just finds the X offset of the top left
+ * corner of the object id it's given.
+ *
+ * @param object The id of the object that you want to find the 
+ *               upper left X coordinate of.
+ * @return int The X coordinate of the object
+ *************************************************************/
+function findPosXById(object)
+{
+	var curleft = 0;
+	var obj = document.getElementById(object);
+	if(obj.offsetParent)
+	{
+		while(obj.offsetParent)
+		{
+			curleft += obj.offsetLeft - obj.scrollLeft;
+			obj = obj.offsetParent;
+		}
+	}
+	else if(obj.x)
+	{
+		curleft += obj.x;
+	}
+	return curleft;
+}; // end findPosX
+
+
+/*************************************************************
+ * findPosY
+ *
+ * The findPosY function just finds the Y offset of the top left
+ * corner of the object id it's given.
+ *
+ * @param object The id of the object that you want to find the 
+ *               upper left Y coordinate of.
+ * @return int The Y coordinate of the object
+ *************************************************************/
+function findPosYById(object)
+{
+	var curtop = 0;var curtop = 0;
+	var obj = document.getElementById(object);
+	if(obj.offsetParent)
+	{
+		while(obj.offsetParent)
+		{
+			curtop += obj.offsetTop - obj.scrollTop;
+			obj = obj.offsetParent;
+		}
+	}
+	else if(obj.y)
+	{
+		curtop += obj.y;
+	}
+	return curtop;
+}; // end findPosY
+
+
+/*************************************************************
+ * trim
+ *
+ * Trims white space from a string.
+ *
+ * @param s The string you want to trim.
+ * @return string The trimmed string.
+ *************************************************************/
+function trim(s)
+{
+	while(s.substring(0,1) == ' ')
+	{
+    	s = s.substring(1,s.length);
+	}
+	while(s.substring(s.length-1,s.length) == ' ')
+	{
+    	s = s.substring(0,s.length-1);
+	}
+	return s;
+}; // end trim
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/json.php
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/json.php	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/json.php	(revision 2375)
@@ -0,0 +1,580 @@
+<?php
+/** 
+* The content of this file is (c) 2003-2005 digital media center GmbH
+* All rights reserved
+*
+* a JSON parser / generator.
+* implemented as 100% compatible port of the original JSON parser
+* by Douglas Crockford on http://www.JSON.org.
+*
+* @package      JSON
+* @access       public
+* @copyright    Copyright (c) 2005-2006 Dominique Stender - http://sf.net/projects/cpaint
+* @license      http://www.crockford.com/JSON/license.html
+* @author       Dominique Stender <dstender@st-webdevelopment.de>
+* @version      $Id$
+*/
+
+  
+  
+  /**
+  * a JSON parser / generator
+  * 
+  * @access public
+  * @author Dominique Stender <dst@dmc.de>
+  * @version 1.0.1
+  */
+  class JSON {
+    /**
+    * line counter
+    * 
+    * @access   private
+    * @var      integer    $at
+    */
+    var $at = 0;
+    
+    /**
+    * 
+    * @access   private
+    * @var      string    $ch
+    */
+    var $ch = ' ';
+    
+    /**
+    * JSON representation
+    * 
+    * @access   private
+    * @var      string    $text
+    */
+    var $text = '';
+    
+    /**
+    * takes an arbitrary PHP data structure and generates a valid JSON representation from it
+    * 
+    * @param    mixed    $arg    an arbitrary PHP data structure
+    * @access   public
+    * @return   string
+    * @version  1.0.1   fixed tests whether $s has content by using strlen(). this enables the user to use 0 as very first character
+    */
+    function stringify($arg) {
+      $returnValue  = '';
+      $c            = '';
+      $i            = '';
+      $l            = '';
+      $s            = '';
+      $v            = '';
+      $numeric      = true;
+
+      switch (gettype($arg)) {
+
+        case 'array':
+          // do a test whether all array keys are numeric
+          foreach ($arg as $i => $v) {
+            if (!is_numeric($i)) {
+              $numeric = false;
+              break;
+            }
+          }
+          
+          if ($numeric) {
+            
+            foreach ($arg as $i => $v) {
+              if (strlen($s) > 0) {
+                $s .= ',';
+              }
+                          
+              $s .= $this->stringify($arg[$i]);
+            } // end: foreach
+                      
+            $returnValue = '[' . $s . ']';
+          
+          } else {
+            // associative array
+            foreach ($arg as $i => $v) {
+              if (strlen($s) > 0) {
+                $s .= ',';
+              }
+              $s .= $this->stringify($i) . ':' . $this->stringify($arg[$i]);
+            }
+            // return as object
+            $returnValue = '{' . $s . '}';
+          }
+          break;
+          
+        case 'object':
+          
+          foreach (get_object_vars($arg) as $i => $v) {
+            $v = $this->stringify($v);
+                           
+            if (strlen($s) > 0) {
+              $s .= ',';
+            }
+                           
+            $s .= $this->stringify($i) . ':' . $v;
+          }
+                    
+          $returnValue = '{' . $s . '}';
+          break;
+            
+        case 'integer':
+        case 'double':
+          $returnValue = is_numeric($arg) ? (string) $arg : 'null';
+          break;
+            
+        case 'string':
+          $l = strlen($arg);
+          $s = '"';
+                
+          for ($i = 0; $i < $l; $i++) {
+            $c = $arg{$i};
+                    
+            if (ord($c) >= ord(' ')) {
+                        
+              if ($c == '\\' 
+                || $c == '"') {
+                            
+                $s .= '\\';
+              }
+              $s .= $c;
+                    
+            } else {
+                        
+              switch ($c) {
+                            
+                case '\b':
+                  $s .= '\\b';
+                  break;
+                            
+                case '\f':
+                  $s .= '\\f';
+                  break;
+                            
+                case '\n':
+                  $s .= '\\n';
+                  break;
+                            
+                case '\r':
+                  $s .= '\\r';
+                  break;
+                            
+                case '\t':
+                  $s .= '\\t';
+                  break;
+                            
+                default:
+                  $s .= '\u00' . sprintf('%02x', ord($c));
+              }
+            }
+          }
+          $returnValue = $s . '"';
+          break;
+          
+        case 'boolean':
+          $returnValue = (string) $arg;
+          break;
+          
+        default:
+          $returnValue = 'null';
+      }
+      
+      return $returnValue;
+    }
+    
+    
+    /**
+    * parses the given string into a PHP data structure
+    * 
+    * @param    string    $text    JSON data representation
+    * @access   public
+    * @return   mixed
+    */
+    function parse($text) {
+      $this->at  = 0;
+      $this->ch  = ' ';
+      $this->text  = $text;
+      
+      return $this->val();
+    }
+    
+    /**
+    * triggers a PHP_ERROR
+    * 
+    * @access   private
+    * @param    string    $m    error message
+    * @return   void
+    */
+    function error($m) {
+      trigger_error($m . ' at offset ' . $this->at . ': ' . $this->text, E_USER_ERROR);
+    }
+
+    /**
+    * returns the next character of a JSON string
+    * 
+    * @access  private
+    * @return  string
+    */
+    function next() {
+      $this->ch = $this->text{$this->at};
+      $this->at++;
+      return $this->ch;
+    }
+
+    /**
+    * handles whitespace and comments
+    * 
+    * @access  private
+    * @return  void
+    */
+    function white() {
+    
+      while ($this->ch != '' 
+        && ord($this->ch) <= ord(' ')) {
+
+        $this->next();
+      }
+    }
+
+    /**
+    * handles strings
+    * 
+    * @access  private
+    * @return  void
+    */
+    function str() {
+      $i = '';
+      $s = '';
+      $t = '';
+      $u = '';
+
+      if ($this->ch == '"') {
+
+        while ($this->next() !== null) {
+                    
+          if ($this->ch == '"') {
+            $this->next();
+            return $s;
+                    
+          } elseif ($this->ch == '\\') {
+                        
+            switch ($this->next()) {
+              case 'b':
+                $s .= '\b';
+                break;
+                          
+              case 'f':
+                $s .= '\f';
+                break;
+                          
+              case 'n':
+                $s .= '\n';
+                break;
+                          
+              case 'r':
+                $s .= '\r';
+                break;
+                          
+              case 't':
+                $s .= '\t';
+                break;
+                          
+              case 'u':
+                $u = 0;
+                              
+                for ($i = 0; $i < 4; $i += 1) {
+                  $t = (integer) sprintf('%01c', hexdec($this->next()));
+                                  
+                  if (!is_numeric($t)) {
+                    break 2;
+                  }
+                  $u = $u * 16 + $t;
+                }
+                              
+                $s .= chr($u);
+                break;
+                          
+              default:
+                $s .= $this->ch;
+            }
+          } else {
+            $s .= $this->ch;
+          }
+        }
+      }
+      
+      $this->error('Bad string');
+    }
+
+    /**
+    * handless arrays
+    * 
+    * @access  private
+    * @return  void
+    */
+    function arr() {
+      $a = array();
+
+      if ($this->ch == '[') {
+        $this->next();
+        $this->white();
+                
+        if ($this->ch == ']') {
+          $this->next();
+          return $a;
+        }
+                
+        while ($this->ch) {
+          array_push($a, $this->val());
+          $this->white();
+                    
+          if ($this->ch == ']') {
+            $this->next();
+            return $a;
+                    
+          } elseif ($this->ch != ',') {
+            break;
+          }
+                    
+          $this->next();
+          $this->white();
+        }
+            
+        $this->error('Bad array');
+      }
+    }
+    
+    /**
+    * handles objects
+    * 
+    * @access  public
+    * @return  void
+    */
+    function obj() {
+      $k = '';
+      $o = new stdClass();
+
+      if ($this->ch == '{') {
+        $this->next();
+        $this->white();
+                
+        if ($this->ch == '}') {
+          $this->next();
+          return $o;
+        }
+                
+        while ($this->ch) {
+          $k = $this->str();
+          $this->white();
+                    
+          if ($this->ch != ':') {
+            break;
+          }
+                    
+          $this->next();
+          $o->$k = $this->val();
+          $this->white();
+                    
+          if ($this->ch == '}') {
+            $this->next();
+            return $o;
+                    
+          } elseif ($this->ch != ',') {
+            break;
+          }
+                    
+          $this->next();
+          $this->white();
+        }
+      }
+      
+      $this->error('Bad object');
+    }
+
+    /**
+    * handles objects
+    * 
+    * @access  public
+    * @return  void
+    */
+    function assoc() {
+      $k = '';
+      $a = array();
+
+      if ($this->ch == '<') {
+        $this->next();
+        $this->white();
+                
+        if ($this->ch == '>') {
+          $this->next();
+          return $a;
+        }
+                
+        while ($this->ch) {
+          $k = $this->str();
+          $this->white();
+                    
+          if ($this->ch != ':') {
+            break;
+          }
+                    
+          $this->next();
+          $a[$k] = $this->val();
+          $this->white();
+                    
+          if ($this->ch == '>') {
+            $this->next();
+            return $a;
+                    
+          } elseif ($this->ch != ',') {
+            break;
+          }
+                    
+          $this->next();
+          $this->white();
+        }
+      }
+      
+      $this->error('Bad associative array');
+    }
+
+    /**
+    * handles numbers
+    * 
+    * @access  private
+    * @return  void
+    */
+    function num() {
+      $n = '';
+      $v = '';
+            
+      if ($this->ch == '-') {
+        $n = '-';
+        $this->next();
+      }
+            
+      while ($this->ch >= '0' 
+        && $this->ch <= '9') {
+        
+        $n .= $this->ch;
+        $this->next();
+      }
+            
+      if ($this->ch == '.') {
+        $n .= '.';
+                
+        while ($this->next() 
+          && $this->ch >= '0' 
+          && $this->ch <= '9') {
+          
+            $n .= $this->ch;
+          }
+        }
+            
+      if ($this->ch == 'e' 
+        || $this->ch == 'E') {
+                
+        $n .= 'e';
+        $this->next();
+                
+        if ($this->ch == '-' 
+          || $this->ch == '+') {
+          
+          $n .= $this->ch;
+          $this->next();
+        }
+
+        while ($this->ch >= '0' 
+          && $this->ch <= '9') {
+
+          $n .= $this->ch;
+          $this->next();
+        }
+      }
+            
+      $v += $n;
+      
+      if (!is_numeric($v)) {
+        $this->error('Bad number');
+      
+      } else {
+        return $v;
+      } // end: if
+    }
+
+    /**
+    * handles words
+    * 
+    * @access  private
+    * @return  mixed
+    */
+    function word() {
+      switch ($this->ch) {
+              
+        case 't':
+                    
+          if ($this->next() == 'r' 
+            && $this->next() == 'u' 
+            && $this->next() == 'e') {
+                        
+            $this->next();
+            return true;
+          }
+          break;
+                
+        case 'f':
+          if ($this->next() == 'a' 
+            && $this->next() == 'l' 
+            && $this->next() == 's' 
+            && $this->next() == 'e') {
+                        
+            $this->next();
+            return false;
+          }
+          break;
+                
+        case 'n':
+          if ($this->next() == 'u' 
+            && $this->next() == 'l' 
+            && $this->next() == 'l') {
+                        
+            $this->next();
+            return null;
+          }
+          break;
+      }
+            
+      $this->error('Syntax error');
+    }
+
+    /**
+    * generic value handler
+    * 
+    * @access  private
+    * @return  mixed
+    */
+    function val() {
+      $this->white();
+            
+      switch ($this->ch) {
+                
+        case '{':
+          return $this->obj();
+                
+        case '[':
+          return $this->arr();
+                
+        case '<':
+          return $this->assoc();
+                
+        case '"':
+          return $this->str();
+                
+        case '-':
+          return $this->num();
+                
+        default:
+          return ($this->ch >= '0' && $this->ch <= '9') ? $this->num() : $this->word();
+      }
+    }
+  }
+  
+?>
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.js
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.js	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.js	(revision 2375)
@@ -0,0 +1,1425 @@
+/**
+* CPAINT - Cross-Platform Asynchronous INterface Toolkit
+*
+* http://sf.net/projects/cpaint
+* 
+* released under the terms of the LGPL
+* see http://www.fsf.org/licensing/licenses/lgpl.txt for details
+*
+* @package      CPAINT
+* @access       public
+* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+* @author       Paul Sullivan <wiley14@gmail.com>
+* @author       Dominique Stender <dstender@st-webdevelopment.de>
+* @author		Stephan Tijink <stijink@googlemail.com>
+* @version      $Id$
+*/
+function cpaint() {
+  /**
+  * CPAINT version
+  * 
+  * @access     protected
+  * @var        string      version
+  */
+  this.version = '2.0.2';
+  
+  /**
+  * configuration options both for this class but also for  the cpaint_call() objects.
+  *
+  * @access     protected
+  * @var        array       config
+  */
+  var config                      = new Array();
+  config['debugging']             = -1;
+  config['proxy_url']             = '';
+  config['transfer_mode']         = 'GET';
+  config['async']                 = true;
+  config['response_type']         = 'OBJECT';
+  config['persistent_connection'] = false;
+  config['use_cpaint_api']        = true;
+  
+  /**
+  * maintains the next free index in the stack
+  *
+  * @access   protected
+  * @var      integer   stack_count
+  */
+  var stack_count = 0;
+
+  /**
+  * property returns whether or not the browser is AJAX capable
+  * 
+  * @access		public
+  * @return		boolean
+  */
+  this.capable = test_ajax_capability();
+  
+  /**
+  * switches debug mode on/off.
+  *
+  * @access   public
+  * @param    boolean    debug    debug flag
+  * @return   void
+  */
+  this.set_debug = function() {
+    
+    if (typeof arguments[0] == 'boolean') {
+      if (arguments[0] === true) {
+        config['debugging'] = 1;
+
+      } else {
+        config['debugging'] = 0;
+      }
+      
+    } else if (typeof arguments[0] == 'number') {
+      config['debugging'] = Math.round(arguments[0]);
+    }
+  }
+
+  /**
+  * defines the URL of the proxy script.
+  *
+  * @access   public
+  * @param    string    proxy_url    URL of the proxyscript to connect
+  * @return   void
+  */
+  this.set_proxy_url = function() {
+    
+    if (typeof arguments[0] == 'string') {
+
+      config['proxy_url'] = arguments[0];
+    }
+  }
+
+  /**
+  * sets the transfer_mode (GET|POST).
+  *
+  * @access   public
+  * @param    string    transfer_mode    transfer_mode
+  * @return   void
+  */
+  this.set_transfer_mode = function() {
+    
+    if (arguments[0].toUpperCase() == 'GET'
+      || arguments[0].toUpperCase() == 'POST') {
+
+      config['transfer_mode'] = arguments[0].toUpperCase();
+    }
+  }
+
+  /**
+  * sets the flag whether or not to use asynchronous calls.
+  *
+  * @access   public
+  * @param    boolean    async    syncronization flag
+  * @return   void
+  */
+  this.set_async = function() {
+    
+    if (typeof arguments[0] == 'boolean') {
+      config['async'] = arguments[0];
+    }
+  }
+
+  /**
+  * defines the response type.
+  *
+  * allowed values are:
+  *   TEXT    = raw text response
+  *   XML     = raw XMLHttpObject
+  *   OBJECT  = parsed JavaScript object structure from XMLHttpObject
+  *
+  * the default is OBJECT.
+  *
+  * @access   public
+  * @param    string    response_type    response type
+  * @return   void
+  */
+  this.set_response_type = function() {
+    
+    if (arguments[0].toUpperCase() == 'TEXT'
+      || arguments[0].toUpperCase() == 'XML'
+      || arguments[0].toUpperCase() == 'OBJECT'
+      || arguments[0].toUpperCase() == 'E4X'
+      || arguments[0].toUpperCase() == 'JSON') {
+
+      config['response_type'] = arguments[0].toUpperCase();
+    }
+  }
+
+  /**
+  * sets the flag whether or not to use a persistent connection.
+  *
+  * @access   public
+  * @param    boolean    persistent_connection    persistance flag
+  * @return   void
+  */
+  this.set_persistent_connection = function() {
+    
+    if (typeof arguments[0] == 'boolean') {
+      config['persistent_connection'] = arguments[0];
+    }
+  }
+  
+  
+  /**
+  * sets the flag whether or not to use the cpaint api on the backend.
+  *
+  * @access    public
+  * @param     boolean    cpaint_api      api_flag
+  * @return    void
+  */
+  this.set_use_cpaint_api = function() {
+    if (typeof arguments[0] == 'boolean') {
+      config['use_cpaint_api'] = arguments[0];
+    }
+  }
+  
+  /**
+  * tests whether one of the necessary implementations
+  * of the XMLHttpRequest class are available
+  *
+  * @access     protected
+  * @return     boolean
+  */
+  function test_ajax_capability() {
+    var cpc = new cpaint_call(0, config, this.version);
+    return cpc.test_ajax_capability();
+  }
+
+  /**
+  * takes the arguments supplied and triggers a call to the CPAINT backend
+  * based on the settings.
+  *
+  * upon response cpaint_call.callback() will automatically be called
+  * to perform post-processing operations.
+  *
+  * @access   public
+  * @param    string    url                 remote URL to call
+  * @param    string    remote_method       remote method to call
+  * @param    object    client_callback     client side callback method to deliver the remote response to. do NOT supply a string!
+  * @param    mixed     argN                remote parameters from now on
+  * @return   void
+  */
+  this.call = function() {
+    var use_stack = -1;
+    
+    if (config['persistent_connection'] == true
+      && __cpaint_stack[0] != null) {
+
+      switch (__cpaint_stack[0].get_http_state()) {
+        case -1:
+          // no XMLHttpObject object has already been instanciated
+          // create new object and configure it
+          use_stack = 0;
+          debug('no XMLHttpObject object to re-use for persistence, creating new one later', 2);
+          break;
+          
+        case 4:
+          // object is ready for a new request, no need to do anything
+          use_stack = 0
+          debug('re-using the persistent connection', 2);
+          break;
+          
+        default:
+          // connection is currently in use, don't do anything
+          debug('the persistent connection is in use - skipping this request', 2);
+      }
+      
+    } else if (config['persistent_connection'] == true) {
+      // persistent connection is active, but no object has been instanciated
+      use_stack = 0;
+      __cpaint_stack[use_stack] = new cpaint_call(use_stack, config, this.version);
+      debug('no cpaint_call object available for re-use, created new one', 2);
+    
+    } else {
+      // no connection persistance
+      use_stack = stack_count;
+      __cpaint_stack[use_stack] = new cpaint_call(use_stack, config, this.version);
+      debug('no cpaint_call object created new one', 2);
+    }
+
+    // configure cpaint_call if allowed to
+    if (use_stack != -1) {
+      __cpaint_stack[use_stack].set_client_callback(arguments[2]);
+      
+      // distribute according to proxy use
+      if (config['proxy_url'] != '') {
+        __cpaint_stack[use_stack].call_proxy(arguments);
+      
+      } else {
+        __cpaint_stack[use_stack].call_direct(arguments);
+      }
+
+      // increase stack counter
+      stack_count++;
+      debug('stack size: ' + __cpaint_stack.length, 2);
+    }
+  }
+
+  /**
+  * debug method
+  *
+  * @access  protected
+  * @param   string       message         the message to debug
+  * @param   integer      debug_level     debug level at which the message appears
+  * @return  void
+  */
+  var debug  = function(message, debug_level) {
+    var prefix = '[CPAINT Debug] ';
+    
+    if (debug_level < 1) {
+      prefix = '[CPAINT Error] ';
+    }
+    
+    if (config['debugging'] >= debug_level) {
+      alert(prefix + message);
+    }
+  }
+}
+
+/**
+* internal FIFO stack of cpaint_call() objects.
+*
+* @access   protected
+* @var      array    __cpaint_stack
+*/
+var __cpaint_stack = new Array();
+
+/**
+* local instance of cpaint_transformer
+* MSIE is unable to handle static classes... sheesh.
+*
+* @access   public
+* @var      object    __cpaint_transformer
+*/
+var __cpaint_transformer = new cpaint_transformer();
+
+/**
+* transport agent class
+*
+* creates the request object, takes care of the response, handles the 
+* client callback. Is configured by the cpaint() object.
+*
+* @package      CPAINT
+* @access       public
+* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+* @author       Dominique Stender <dstender@st-webdevelopment.de>
+* @author       Paul Sullivan <wiley14@gmail.com>
+* @param        integer     stack_id      stack Id in cpaint
+* @param        array       config        configuration array for this call
+* @param        string      version       CPAINT API version
+*/
+function cpaint_call() {
+  /**
+  * CPAINT version
+  * 
+  * @access     protected
+  * @var        string      version
+  */
+  var version = arguments[2];
+  
+  /**
+  * configuration options both for this class objects.
+  *
+  * @access     protected
+  * @var        array       config
+  */
+  var config                      = new Array();
+  config['debugging']             = arguments[1]['debugging'];
+  config['proxy_url']             = arguments[1]['proxy_url'];
+  config['transfer_mode']         = arguments[1]['transfer_mode'];
+  config['async']                 = arguments[1]['async'];
+  config['response_type']         = arguments[1]['response_type'];
+  config['persistent_connection'] = arguments[1]['persistent_connection'];
+  config['use_cpaint_api']        = arguments[1]['use_cpaint_api'];
+
+  /**
+  * XMLHttpObject used for this request.
+  *
+  * @access   protected
+  * @var      object     httpobj
+  */
+  var httpobj    = false;
+
+  /**
+  * client callback function.
+  *
+  * @access   public
+  * @var      function    client_callback
+  */
+  var client_callback;
+
+  /**
+  * stores the stack Id within the cpaint object
+  *
+  * @access   protected
+  * @var      stack_id
+  */
+  var stack_id = arguments[0];
+  
+  /**
+  * sets the client callback function.
+  *
+  * @access   public
+  * @param    function    client_callback     the client callback function
+  * @return   void
+  */
+  this.set_client_callback = function() {
+    
+    if (typeof arguments[0] == 'function') {
+      client_callback = arguments[0];
+    }
+  }
+
+  /**
+  * returns the ready state of the internal XMLHttpObject
+  *
+  * if no such object was set up already, -1 is returned
+  * 
+  * @access     public
+  * @return     integer
+  */
+  this.get_http_state = function() {
+    var return_value = -1;
+    
+    if (typeof httpobj == 'object') {
+      return_value = httpobj.readyState;
+    }
+    
+    return return_value;
+  }
+  
+  /**
+  * internal method for remote calls to the local server without use of the proxy script.
+  *
+  * @access   public
+  * @param    array    call_arguments    array of arguments initially passed to cpaint.call()
+  * @return   void
+  */
+  this.call_direct = function(call_arguments) {
+    var url             = call_arguments[0];
+    var remote_method   = call_arguments[1];
+    var querystring     = '';
+    var i               = 0;
+    
+    // correct link to self
+    if (url == 'SELF') {
+      url = document.location.href;
+    }
+  
+    if (config['use_cpaint_api'] == true) {
+      // backend uses cpaint api
+      // pass parameters to remote method
+      for (i = 3; i < call_arguments.length; i++) {
+
+        if ((typeof call_arguments[i] == 'string'
+              && call_arguments[i] != ''
+              && call_arguments[i].search(/^\s+$/g) == -1)
+          && !isNaN(call_arguments[i])
+          && isFinite(call_arguments[i])) {
+          // numerical value, convert it first
+          querystring += '&cpaint_argument[]=' + encodeURIComponent(JSON.stringify(Number(call_arguments[i])));
+        
+        } else {
+          querystring += '&cpaint_argument[]=' + encodeURIComponent(JSON.stringify(call_arguments[i]));
+        }
+      }
+    
+      // add response type to querystring
+      querystring += '&cpaint_response_type=' + config['response_type'];
+    
+      // build header
+      if (config['transfer_mode'] == 'GET') {
+				
+        if(url.indexOf('?') != -1) {
+					url = url + '&cpaint_function=' + remote_method +	querystring;
+				
+        } else {
+					url = url + '?cpaint_function=' + remote_method +	querystring; 
+				}
+      
+      } else {
+        querystring = 'cpaint_function=' + remote_method + querystring;
+      }
+      
+    } else {
+      // backend does not use cpaint api
+      // pass parameters to remote method
+      for (i = 3; i < call_arguments.length; i++) {
+        
+        if (i == 3) {
+          querystring += encodeURIComponent(call_arguments[i]);
+        
+        } else {
+          querystring += '&' + encodeURIComponent(call_arguments[i]);
+        }
+      }
+    
+      // build header
+      if (config['transfer_mode'] == 'GET') {
+        url = url + querystring;
+      } 
+    }
+  
+    // open connection 
+    get_connection_object();
+
+    // open connection to remote target
+    debug('opening connection to "' + url + '"', 1);
+    httpobj.open(config['transfer_mode'], url, config['async']);
+
+    // send "urlencoded" header if necessary (if POST)
+    if (config['transfer_mode'] == 'POST') {
+
+      try {
+        httpobj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+
+      } catch (cp_err) {
+        debug('POST cannot be completed due to incompatible browser.  Use GET as your request method.', 0);
+      }
+    }
+
+    // make ourselves known
+    httpobj.setRequestHeader('X-Powered-By', 'CPAINT v' + version + ' :: http://sf.net/projects/cpaint');
+
+    // callback handling for asynchronous calls
+    httpobj.onreadystatechange = callback;
+
+    // send content
+    if (config['transfer_mode'] == 'GET') {
+      httpobj.send(null);
+
+    } else {
+      debug('sending query: ' + querystring, 1);
+      httpobj.send(querystring);
+    }
+
+    if (config['async'] == true) {
+      // manual callback handling for synchronized calls
+      callback();
+    }
+  }
+    
+  /**
+  * internal method for calls to remote servers through the proxy script.
+  *
+  * @access   public
+  * @param    array    call_arguments    array of arguments passed to cpaint.call()
+  * @return   void
+  */
+  this.call_proxy = function(call_arguments) {
+    var proxyscript     = config['proxy_url'];
+    var url             = call_arguments[0];
+    var remote_method   = call_arguments[1];
+    var querystring     = '';
+    var i               = 0;
+    
+    var querystring_argument_prefix = 'cpaint_argument[]=';
+
+    // pass parameters to remote method
+    if (config['use_cpaint_api'] == false) {
+      // when not talking to a CPAINT backend, don't prefix arguments
+      querystring_argument_prefix = '';
+    }
+
+    for (i = 3; i < call_arguments.length; i++) {
+
+      if (config['use_cpaint_api'] == true) {
+      
+        if ((typeof call_arguments[i] == 'string'
+              && call_arguments[i] != ''
+              && call_arguments[i].search(/^\s+$/g) == -1)
+          && !isNaN(call_arguments[i])
+          && isFinite(call_arguments[i])) {
+          // numerical value, convert it first
+          querystring += encodeURIComponent(querystring_argument_prefix + JSON.stringify(Number(call_arguments[i])) + '&');
+
+        } else {
+          querystring += encodeURIComponent(querystring_argument_prefix + JSON.stringify(call_arguments[i]) + '&');
+        }
+        
+      } else {
+        // no CPAINT in the backend
+        querystring += encodeURIComponent(querystring_argument_prefix + call_arguments[i] + '&');
+      }
+    }
+
+    if (config['use_cpaint_api'] == true) {
+      // add remote function name to querystring
+      querystring += encodeURIComponent('&cpaint_function=' + remote_method);
+  
+      // add response type to querystring
+      querystring += encodeURIComponent('&cpaint_responsetype=' + config['response_type']);
+    }
+    
+    // build header
+    if (config['transfer_mode'] == 'GET') {
+      proxyscript += '?cpaint_remote_url=' + encodeURIComponent(url) 
+        + '&cpaint_remote_query=' + querystring
+        + '&cpaint_remote_method=' + config['transfer_mode'] 
+        + '&cpaint_response_type=' + config['response_type'];
+
+    } else {
+      querystring = 'cpaint_remote_url=' + encodeURIComponent(url)
+        + '&cpaint_remote_query=' + querystring
+        + '&cpaint_remote_method=' + config['transfer_mode'] 
+        + '&cpaint_response_type=' + config['response_type'];
+    }
+
+    // open connection
+    get_connection_object();
+
+    // open connection to remote target
+    debug('opening connection to proxy "' + proxyscript + '"', 1);
+    httpobj.open(config['transfer_mode'], proxyscript, config['async']);
+
+    // send "urlencoded" header if necessary (if POST)
+    if (config['transfer_mode'] == 'POST') {
+
+      try {
+        httpobj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+
+      } catch (cp_err) {
+        debug('POST cannot be completed due to incompatible browser.  Use GET as your request method.', 0);
+      }
+    }
+
+    httpobj.setRequestHeader('X-Powered-By', 'CPAINT v' + version);
+
+    // callback handling for asynchronous calls
+    httpobj.onreadystatechange = callback;
+
+    // send content
+    if (config['transfer_mode'] == 'GET') {
+      httpobj.send(null);
+
+    } else {
+      debug('sending query: ' + querystring, 1);
+      httpobj.send(querystring);
+    }
+
+    if (config['async'] == false) {
+      // manual callback handling for synchronized calls
+      callback();
+    }
+  }
+
+  this.test_ajax_capability = function() {
+    return get_connection_object();
+  }
+  
+  /**
+  * creates a new connection object.
+  *
+  * @access   protected
+  * @return   boolean
+  */
+  var get_connection_object = function() {
+    var return_value    = false;
+    var new_connection  = false;
+
+    // open new connection only if necessary
+    if (config['persistent_connection'] == false) {
+      // no persistance, create a new object every time
+      debug('Using new connection object', 1);
+      new_connection = true;
+
+    } else {
+      // persistent connection object, only open one if no object exists
+      debug('Using shared connection object.', 1);
+
+      if (typeof httpobj != 'object') {
+        debug('Getting new persistent connection object.', 1);
+        new_connection = true;
+      }
+    }
+
+    if (new_connection == true) {
+      try {
+        httpobj = new ActiveXObject('Msxml2.XMLHTTP');
+  
+      } catch (e) {
+        
+        try {  
+          httpobj = new ActiveXObject('Microsoft.XMLHTTP');
+  
+        } catch (oc) {
+          httpobj = null;
+        } 
+      }
+  
+      if (!httpobj && typeof XMLHttpRequest != 'undefined') {
+        httpobj = new XMLHttpRequest();
+      }
+  
+      if (!httpobj) {
+        debug('Could not create connection object', 0);
+      
+      } else {
+        return_value = true;
+      }
+    }
+
+    if (httpobj.readyState != 4) {
+      httpobj.abort();
+    }
+
+    return return_value;
+  }
+
+  /**
+  * internal callback function.
+  *
+  * will perform some consistency checks (response code, NULL value testing)
+  * and if response_type = 'OBJECT' it will automatically call
+  * cpaint_call.parse_ajax_xml() to have a JavaScript object structure generated.
+  *
+  * after all that is done the client side callback function will be called 
+  * with the generated response as single value.
+  *
+  * @access   protected
+  * @return   void
+  */
+  var callback = function() {
+    var response = null;
+
+    if (httpobj.readyState == 4
+      && httpobj.status == 200) {
+      
+      debug(httpobj.responseText, 1);
+      debug('using response type ' + config['response_type'], 2);
+      
+      // fetch correct response
+      switch (config['response_type']) {
+        case 'XML':
+          debug(httpobj.responseXML, 2);
+          response = __cpaint_transformer.xml_conversion(httpobj.responseXML);
+          break;
+          
+        case 'OBJECT':
+          response = __cpaint_transformer.object_conversion(httpobj.responseXML);
+          break;
+        
+        case 'TEXT':
+          response = __cpaint_transformer.text_conversion(httpobj.responseText);
+          break;
+          
+        case 'E4X':
+          response = __cpaint_transformer.e4x_conversion(httpobj.responseText);
+          break;
+          
+        case 'JSON':
+          response = __cpaint_transformer.json_conversion(httpobj.responseText);
+          break;
+          
+        default:
+          debug('invalid response type \'' + response_type + '\'', 0);
+      }
+      
+      // call client side callback
+      if (response != null 
+        && typeof client_callback == 'function') {
+        client_callback(response, httpobj.responseText);
+      }
+      
+      // remove ourselves from the stack
+      remove_from_stack();
+    
+    } else if (httpobj.readyState == 4
+      && httpobj.status != 200) {
+      // HTTP error of some kind
+      debug('invalid HTTP response code \'' + Number(httpobj.status) + '\'', 0);
+    }
+  }
+
+  /**
+  * removes an entry from the stack
+  *
+  * @access     protected
+  * @return     void
+  */
+  var remove_from_stack = function() {
+    // remove only if everything is okay and we're not configured as persistent connection
+    if (typeof stack_id == 'number'
+      && __cpaint_stack[stack_id]
+      && config['persistent_connection'] == false) {
+      
+      __cpaint_stack[stack_id] = null;
+    }
+  }
+
+  /**
+  * debug method
+  *
+  * @access  protected
+  * @param   string       message         the message to debug
+  * @param   integer      debug_level     debug level at which the message appears
+  * @return  void
+  */
+  var debug  = function(message, debug_level) {
+    var prefix = '[CPAINT Debug] ';
+    
+    if (config['debugging'] < 1) {
+      prefix = '[CPAINT Error] ';
+    }
+    
+    if (config['debugging'] >= debug_level) {
+      alert(prefix + message);
+    }
+  }
+}
+
+/**
+* CPAINT transformation object
+*
+* @package      CPAINT
+* @access       public
+* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+* @author       Paul Sullivan <wiley14@gmail.com>
+* @author       Dominique Stender <dstender@st-webdevelopment.de>
+*/
+function cpaint_transformer() {
+
+  /**
+  * will take a XMLHttpObject and generate a JavaScript
+  * object structure from it.
+  *
+  * is internally called by cpaint_call.callback() if necessary.
+  * will call cpaint_call.create_object_structure() to create nested object structures.
+  *
+  * @access   public
+  * @param    object    xml_document  a XMLHttpObject
+  * @return   object
+  */
+  this.object_conversion = function(xml_document) {
+    var return_value  = new cpaint_result_object();
+    var i             = 0;
+    var firstNodeName = '';
+    
+    if (typeof xml_document == 'object'
+      && xml_document != null) {
+
+      // find the first element node - for MSIE the <?xml?> node is the very first...
+      for (i = 0; i < xml_document.childNodes.length; i++) {
+
+        if (xml_document.childNodes[i].nodeType == 1) {
+          firstNodeName = xml_document.childNodes[i].nodeName;
+          break;
+        }
+      }
+      
+      var ajax_response = xml_document.getElementsByTagName(firstNodeName);
+
+      return_value[firstNodeName] = new Array();
+    
+      for (i = 0; i < ajax_response.length; i++) {
+        var tmp_node = create_object_structure(ajax_response[i]);
+        tmp_node.id  = ajax_response[i].getAttribute('id')
+        return_value[firstNodeName].push(tmp_node);
+      }
+
+    } else {
+      debug('received invalid XML response', 0);
+    }
+
+    return return_value;
+  }
+
+  /**
+  * performs the necessary conversions for the XML response type
+  *
+  * @access   public
+  * @param    object    xml_document  a XMLHttpObject
+  * @return   object
+  */
+  this.xml_conversion = function(xml_document) {
+    return xml_document;
+  }
+  
+  /**
+  * performs the necessary conversions for the TEXT response type
+  *
+  * @access   public
+  * @param    string    text  the response text
+  * @return   string
+  */
+  this.text_conversion = function(text) {
+    return decode(text);
+  }
+  
+  /**
+  * performs the necessary conversions for the E4X response type
+  *
+  * @access   public
+  * @param    string    text  the response text
+  * @return   string
+  */
+  this.e4x_conversion = function(text) {
+    // remove <?xml ?>tag
+    text = text.replace(/^\<\?xml[^>]+\>/, '');
+    return new XML(text);
+  }
+  
+  /**
+  * performs the necessary conversions for the JSON response type
+  *
+  * @access   public
+  * @param    string    text  the response text
+  * @return   string
+  */
+  this.json_conversion = function(text) {
+    return JSON.parse(text);
+  }
+  
+  /**
+  * this method takes a HTML / XML node object and creates a
+  * JavaScript object structure from it.
+  *
+  * @access   public
+  * @param    object    stream    a node in the XML structure
+  * @return   object
+  */
+  var create_object_structure = function(stream) {
+    var return_value = new cpaint_result_object();
+    var node_name = '';
+    var i         = 0;
+    var attrib    = 0;
+    
+    if (stream.hasChildNodes() == true) {
+      for (i = 0; i < stream.childNodes.length; i++) {
+  
+        node_name = stream.childNodes[i].nodeName;
+        node_name = node_name.replace(/[^a-zA-Z0-9_]*/g, '');
+        
+        // reset / create subnode
+        if (typeof return_value[node_name] != 'object') {
+          return_value[node_name] = new Array();
+        }
+        
+        if (stream.childNodes[i].nodeType == 1) {
+          var tmp_node  = create_object_structure(stream.childNodes[i]);
+
+          for (attrib = 0; attrib < stream.childNodes[i].attributes.length; attrib++) {
+            tmp_node.set_attribute(stream.childNodes[i].attributes[attrib].nodeName, stream.childNodes[i].attributes[attrib].nodeValue);
+          }
+          
+          return_value[node_name].push(tmp_node);
+        
+        } else if (stream.childNodes[i].nodeType == 3) {
+          return_value.data  = decode(String(stream.firstChild.data));
+        }
+      }
+    }
+    
+    return return_value;
+  }
+
+  /**
+  * converts an encoded text back to viewable characters.
+  *
+  * @access     public
+  * @param      string      rawtext     raw text as provided by the backend
+  * @return     mixed
+  */
+  var decode = function(rawtext) {
+    var plaintext = ''; 
+    var i         = 0; 
+    var c1        = 0;
+    var c2        = 0;
+    var c3        = 0;
+    var u         = 0;
+    var t         = 0;
+
+    // remove special JavaScript encoded non-printable characters
+    while (i < rawtext.length) {
+      if (rawtext.charAt(i) == '\\'
+        && rawtext.charAt(i + 1) == 'u') {
+        
+        u = 0;
+        
+        for (j = 2; j < 6; j += 1) {
+          t = parseInt(rawtext.charAt(i + j), 16);
+          
+          if (!isFinite(t)) {
+            break;
+          }
+          u = u * 16 + t;
+        }
+
+        plaintext += String.fromCharCode(u);
+        i       += 6;
+      
+      } else {
+        plaintext += rawtext.charAt(i);
+        i++;
+      }
+    }
+
+    // convert numeric data to number type
+    if (plaintext != ''
+      && plaintext.search(/^\s+$/g) == -1
+      && !isNaN(plaintext) 
+      && isFinite(plaintext)) {
+      
+      plaintext = Number(plaintext);
+    }
+  
+    return plaintext;
+  }
+}
+
+/**
+* this is the basic prototype for a cpaint node object
+* as used in cpaint_call.parse_ajax_xml()
+*
+* @package      CPAINT
+* @access       public
+* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+* @author       Paul Sullivan <wiley14@gmail.com>
+* @author       Dominique Stender <dstender@st-webdevelopment.de>
+*/
+function cpaint_result_object() {
+  this.id           = 0;
+  this.data         = '';
+  var __attributes  = new Array();
+  
+  /**
+  * Returns a subnode with the given type and id.
+  *
+  * @access     public
+  * @param      string    type    The type of the subnode. Equivalent to the XML tag name.
+  * @param      string    id      The id of the subnode. Equivalent to the XML tag names id attribute.
+  * @return     object
+  */
+  this.find_item_by_id = function() {
+    var return_value  = null;
+    var type    = arguments[0];
+    var id      = arguments[1];
+    var i       = 0;
+    
+    if (this[type]) {
+
+      for (i = 0; i < this[type].length; i++) {
+
+        if (this[type][i].get_attribute('id') == id) {
+          return_value = this[type][i];
+          break;
+        }
+      }
+    }
+
+    return return_value;
+  }
+  
+  /**
+  * retrieves the value of an attribute.
+  *
+  * @access   public
+  * @param    string    name    name of the attribute
+  * @return   mixed
+  */
+  this.get_attribute = function() {
+    var return_value  = null;
+    var id            = arguments[0];
+    
+    if (typeof __attributes[id] != 'undefined') {
+      return_value = __attributes[id];
+    }
+    
+    return return_value;
+  }
+  
+  /**
+  * assigns a value to an attribute.
+  *
+  * if that attribute does not exist it will be created.
+  *
+  * @access     public
+  * @param      string    name    name of the attribute
+  * @param      string    value   value of the attribute
+  * @return     void
+  */
+  this.set_attribute = function() {
+    __attributes[arguments[0]] = arguments[1];
+  }
+}
+
+
+/*
+Copyright (c) 2005 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+Array.prototype.______array = '______array';
+
+var JSON = {
+  org: 'http://www.JSON.org',
+  copyright: '(c)2005 JSON.org',
+  license: 'http://www.crockford.com/JSON/license.html',
+
+  stringify: function (arg) {
+    var c, i, l, s = '', v;
+    var numeric = true;
+    
+    switch (typeof arg) {
+    case 'object':
+      if (arg) {
+        if (arg.______array == '______array') {
+          // do a test whether all array keys are numeric
+          for (i in arg) {
+            if (i != '______array'
+              && (isNaN(i) 
+                || !isFinite(i))) {
+              numeric = false;
+              break;
+            }
+          }
+          
+          if (numeric == true) {
+            for (i = 0; i < arg.length; ++i) {
+              if (typeof arg[i] != 'undefined') {
+                v = this.stringify(arg[i]);
+                if (s) {
+                  s += ',';
+                }
+                s += v;
+              } else {
+                s += ',null';
+              }
+            }
+            return '[' + s + ']';
+          } else {
+            for (i in arg) {
+              if (i != '______array') {
+                v = arg[i];
+                if (typeof v != 'undefined' && typeof v != 'function') {
+                  v = this.stringify(v);
+                  if (s) {
+                    s += ',';
+                  }
+                  s += this.stringify(i) + ':' + v;
+                }
+              }
+            }
+            // return as object
+            return '{' + s + '}';
+          }
+        } else if (typeof arg.toString != 'undefined') {
+          for (i in arg) {
+            v = arg[i];
+            if (typeof v != 'undefined' && typeof v != 'function') {
+              v = this.stringify(v);
+              if (s) {
+                s += ',';
+              }
+              s += this.stringify(i) + ':' + v;
+            }
+          }
+          return '{' + s + '}';
+        }
+      }
+      return 'null';
+    case 'number':
+      return isFinite(arg) ? String(arg) : 'null';
+    case 'string':
+      l = arg.length;
+      s = '"';
+      for (i = 0; i < l; i += 1) {
+        c = arg.charAt(i);
+        if (c >= ' ') {
+          if (c == '\\' || c == '"') {
+            s += '\\';
+          }
+          s += c;
+        } else {
+          switch (c) {
+            case '\b':
+              s += '\\b';
+              break;
+            case '\f':
+              s += '\\f';
+              break;
+            case '\n':
+              s += '\\n';
+              break;
+            case '\r':
+              s += '\\r';
+              break;
+            case '\t':
+              s += '\\t';
+              break;
+            default:
+              c = c.charCodeAt();
+              s += '\\u00' + Math.floor(c / 16).toString(16) +
+                (c % 16).toString(16);
+          }
+        }
+      }
+      return s + '"';
+    case 'boolean':
+      return String(arg);
+    default:
+      return 'null';
+    }
+  },
+  parse: function (text) {
+    var at = 0;
+    var ch = ' ';
+
+    function error(m) {
+      throw {
+        name: 'JSONError',
+        message: m,
+        at: at - 1,
+        text: text
+      };
+    }
+
+    function next() {
+      ch = text.charAt(at);
+      at += 1;
+      return ch;
+    }
+
+    function white() {
+      while (ch != '' && ch <= ' ') {
+        next();
+      }
+    }
+
+    function str() {
+      var i, s = '', t, u;
+
+      if (ch == '"') {
+outer:      while (next()) {
+          if (ch == '"') {
+            next();
+            return s;
+          } else if (ch == '\\') {
+            switch (next()) {
+            case 'b':
+              s += '\b';
+              break;
+            case 'f':
+              s += '\f';
+              break;
+            case 'n':
+              s += '\n';
+              break;
+            case 'r':
+              s += '\r';
+              break;
+            case 't':
+              s += '\t';
+              break;
+            case 'u':
+              u = 0;
+              for (i = 0; i < 4; i += 1) {
+                t = parseInt(next(), 16);
+                if (!isFinite(t)) {
+                  break outer;
+                }
+                u = u * 16 + t;
+              }
+              s += String.fromCharCode(u);
+              break;
+            default:
+              s += ch;
+            }
+          } else {
+            s += ch;
+          }
+        }
+      }
+      error("Bad string");
+    }
+
+    function arr() {
+      var a = [];
+
+      if (ch == '[') {
+        next();
+        white();
+        if (ch == ']') {
+          next();
+          return a;
+        }
+        while (ch) {
+          a.push(val());
+          white();
+          if (ch == ']') {
+            next();
+            return a;
+          } else if (ch != ',') {
+            break;
+          }
+          next();
+          white();
+        }
+      }
+      error("Bad array");
+    }
+
+    function obj() {
+      var k, o = {};
+
+      if (ch == '{') {
+        next();
+        white();
+        if (ch == '}') {
+          next();
+          return o;
+        }
+        while (ch) {
+          k = str();
+          white();
+          if (ch != ':') {
+            break;
+          }
+          next();
+          o[k] = val();
+          white();
+          if (ch == '}') {
+            next();
+            return o;
+          } else if (ch != ',') {
+            break;
+          }
+          next();
+          white();
+        }
+      }
+      error("Bad object");
+    }
+
+    function assoc() {
+      var k, a = [];
+
+      if (ch == '<') {
+        next();
+        white();
+        if (ch == '>') {
+          next();
+          return a;
+        }
+        while (ch) {
+          k = str();
+          white();
+          if (ch != ':') {
+            break;
+          }
+          next();
+          a[k] = val();
+          white();
+          if (ch == '>') {
+            next();
+            return a;
+          } else if (ch != ',') {
+            break;
+          }
+          next();
+          white();
+        }
+      }
+      error("Bad associative array");
+    }
+
+    function num() {
+      var n = '', v;
+      if (ch == '-') {
+        n = '-';
+        next();
+      }
+      while (ch >= '0' && ch <= '9') {
+        n += ch;
+        next();
+      }
+      if (ch == '.') {
+        n += '.';
+        while (next() && ch >= '0' && ch <= '9') {
+          n += ch;
+        }
+      }
+      if (ch == 'e' || ch == 'E') {
+        n += 'e';
+        next();
+        if (ch == '-' || ch == '+') {
+          n += ch;
+          next();
+        }
+        while (ch >= '0' && ch <= '9') {
+          n += ch;
+          next();
+        }
+      }
+      v = +n;
+      if (!isFinite(v)) {
+        error("Bad number");
+      } else {
+        return v;
+      }
+    }
+
+    function word() {
+      switch (ch) {
+        case 't':
+          if (next() == 'r' && next() == 'u' && next() == 'e') {
+            next();
+            return true;
+          }
+          break;
+        case 'f':
+          if (next() == 'a' && next() == 'l' && next() == 's' &&
+              next() == 'e') {
+            next();
+            return false;
+          }
+          break;
+        case 'n':
+          if (next() == 'u' && next() == 'l' && next() == 'l') {
+            next();
+            return null;
+          }
+          break;
+      }
+      error("Syntax error");
+    }
+
+    function val() {
+      white();
+      switch (ch) {
+        case '{':
+          return obj();
+        case '[':
+          return arr();
+        case '<':
+          return assoc();
+        case '"':
+          return str();
+        case '-':
+          return num();
+        default:
+          return ch >= '0' && ch <= '9' ? num() : word();
+      }
+    }
+
+    return val();
+  }
+};
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.compressed.js
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.compressed.js	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.compressed.js	(revision 2375)
@@ -0,0 +1,132 @@
+/**
+* CPAINT - Cross-Platform Asynchronous INterface Toolkit
+*
+* http://sf.net/projects/cpaint
+* 
+* released under the terms of the LGPL
+* see http://www.fsf.org/licensing/licenses/lgpl.txt for details
+*
+* @package      CPAINT
+* @access       public
+* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+* @author       Paul Sullivan <wiley14@gmail.com>
+* @author       Dominique Stender <dstender@st-webdevelopment.de>
+* @version      $Id$
+*/
+function cpaint(){this.version='2.0.2';var config=new Array();config['debugging']=0;config['proxy_url']='';config['transfer_mode']='GET';config['async']=true;config['response_type']='OBJECT';config['persistent_connection']=false;config['use_cpaint_api']=true;var stack_count=0;this.capable=test_ajax_capability();this.set_debug=function(){if(typeof arguments[0]=='boolean'){if(arguments[0]===true){config['debugging']=1;}else{config['debugging']=0;}}else if(typeof arguments[0]=='number'){config['debugging']=Math.round(arguments[0]);}}
+this.set_proxy_url=function(){if(typeof arguments[0]=='string'){config['proxy_url']=arguments[0];}}
+this.set_transfer_mode=function(){if(arguments[0].toUpperCase()=='GET'||arguments[0].toUpperCase()=='POST'){config['transfer_mode']=arguments[0].toUpperCase();}}
+this.set_async=function(){if(typeof arguments[0]=='boolean'){config['async']=arguments[0];}}
+this.set_response_type=function(){if(arguments[0].toUpperCase()=='TEXT'||arguments[0].toUpperCase()=='XML'||arguments[0].toUpperCase()=='OBJECT'||arguments[0].toUpperCase()=='E4X'||arguments[0].toUpperCase()=='JSON'){config['response_type']=arguments[0].toUpperCase();}}
+this.set_persistent_connection=function(){if(typeof arguments[0]=='boolean'){config['persistent_connection']=arguments[0];}}
+this.set_use_cpaint_api=function(){if(typeof arguments[0]=='boolean'){config['use_cpaint_api']=arguments[0];}}
+function test_ajax_capability(){var cpc=new cpaint_call(0,config,this.version);return cpc.test_ajax_capability();}
+this.call=function(){var use_stack=-1;if(config['persistent_connection']==true&&__cpaint_stack[0]!=null){switch(__cpaint_stack[0].get_http_state()){case-1:use_stack=0;debug('no XMLHttpObject object to re-use for persistence, creating new one later',2);break;case 4:use_stack=0
+debug('re-using the persistent connection',2);break;default:debug('the persistent connection is in use - skipping this request',2);}}else if(config['persistent_connection']==true){use_stack=0;__cpaint_stack[use_stack]=new cpaint_call(use_stack,config,this.version);debug('no cpaint_call object available for re-use, created new one',2);}else{use_stack=stack_count;__cpaint_stack[use_stack]=new cpaint_call(use_stack,config,this.version);debug('no cpaint_call object created new one',2);}
+if(use_stack!=-1){__cpaint_stack[use_stack].set_client_callback(arguments[2]);if(config['proxy_url']!=''){__cpaint_stack[use_stack].call_proxy(arguments);}else{__cpaint_stack[use_stack].call_direct(arguments);}
+stack_count++;debug('stack size: '+__cpaint_stack.length,2);}}
+var debug=function(message,debug_level){var prefix='[CPAINT Debug] ';if(debug_level<1){prefix='[CPAINT Error] ';}
+if(config['debugging']>=debug_level){alert(prefix+message);}}}
+var __cpaint_stack=new Array();var __cpaint_transformer=new cpaint_transformer();function cpaint_call(){var version=arguments[2];var config=new Array();config['debugging']=arguments[1]['debugging'];config['proxy_url']=arguments[1]['proxy_url'];config['transfer_mode']=arguments[1]['transfer_mode'];config['async']=arguments[1]['async'];config['response_type']=arguments[1]['response_type'];config['persistent_connection']=arguments[1]['persistent_connection'];config['use_cpaint_api']=arguments[1]['use_cpaint_api'];var httpobj=false;var client_callback;var stack_id=arguments[0];this.set_client_callback=function(){if(typeof arguments[0]=='function'){client_callback=arguments[0];}}
+this.get_http_state=function(){var return_value=-1;if(typeof httpobj=='object'){return_value=httpobj.readyState;}
+return return_value;}
+this.call_direct=function(call_arguments){var url=call_arguments[0];var remote_method=call_arguments[1];var querystring='';var i=0;if(url=='SELF'){url=document.location.href;}
+if(config['use_cpaint_api']==true){for(i=3;i<call_arguments.length;i++){if((typeof call_arguments[i]=='string'&&call_arguments[i]!=''&&call_arguments[i].search(/^\s+$/g)==-1)&&!isNaN(call_arguments[i])&&isFinite(call_arguments[i])){querystring+='&cpaint_argument[]='+encodeURIComponent(JSON.stringify(Number(call_arguments[i])));}else{querystring+='&cpaint_argument[]='+encodeURIComponent(JSON.stringify(call_arguments[i]));}}
+querystring+='&cpaint_response_type='+config['response_type'];if(config['transfer_mode']=='GET'){if(url.indexOf('?')!=-1){url=url+'&cpaint_function='+remote_method+querystring;}else{url=url+'?cpaint_function='+remote_method+querystring;}}else{querystring='cpaint_function='+remote_method+querystring;}}else{for(i=3;i<call_arguments.length;i++){if(i==3){querystring+=encodeURIComponent(call_arguments[i]);}else{querystring+='&'+encodeURIComponent(call_arguments[i]);}}
+if(config['transfer_mode']=='GET'){url=url+querystring;}}
+get_connection_object();debug('opening connection to "'+url+'"',1);httpobj.open(config['transfer_mode'],url,config['async']);if(config['transfer_mode']=='POST'){try{httpobj.setRequestHeader('Content-Type','application/x-www-form-urlencoded');}catch(cp_err){debug('POST cannot be completed due to incompatible browser.  Use GET as your request method.',0);}}
+httpobj.setRequestHeader('X-Powered-By','CPAINT v'+version+' :: http://sf.net/projects/cpaint');httpobj.onreadystatechange=callback;if(config['transfer_mode']=='GET'){httpobj.send(null);}else{debug('sending query: '+querystring,1);httpobj.send(querystring);}
+if(config['async']==true){callback();}}
+this.call_proxy=function(call_arguments){var proxyscript=config['proxy_url'];var url=call_arguments[0];var remote_method=call_arguments[1];var querystring='';var i=0;var querystring_argument_prefix='cpaint_argument[]=';if(config['use_cpaint_api']==false){querystring_argument_prefix='';}
+for(i=3;i<call_arguments.length;i++){if(config['use_cpaint_api']==true){if((typeof call_arguments[i]=='string'&&call_arguments[i]!=''&&call_arguments[i].search(/^\s+$/g)==-1)&&!isNaN(call_arguments[i])&&isFinite(call_arguments[i])){querystring+=encodeURIComponent(querystring_argument_prefix+JSON.stringify(Number(call_arguments[i]))+'&');}else{querystring+=encodeURIComponent(querystring_argument_prefix+JSON.stringify(call_arguments[i])+'&');}}else{querystring+=encodeURIComponent(querystring_argument_prefix+call_arguments[i]+'&');}}
+if(config['use_cpaint_api']==true){querystring+=encodeURIComponent('&cpaint_function='+remote_method);querystring+=encodeURIComponent('&cpaint_responsetype='+config['response_type']);}
+if(config['transfer_mode']=='GET'){proxyscript+='?cpaint_remote_url='+encodeURIComponent(url)
++'&cpaint_remote_query='+querystring
++'&cpaint_remote_method='+config['transfer_mode']
++'&cpaint_response_type='+config['response_type'];}else{querystring='cpaint_remote_url='+encodeURIComponent(url)
++'&cpaint_remote_query='+querystring
++'&cpaint_remote_method='+config['transfer_mode']
++'&cpaint_response_type='+config['response_type'];}
+get_connection_object();debug('opening connection to proxy "'+proxyscript+'"',1);httpobj.open(config['transfer_mode'],proxyscript,config['async']);if(config['transfer_mode']=='POST'){try{httpobj.setRequestHeader('Content-Type','application/x-www-form-urlencoded');}catch(cp_err){debug('POST cannot be completed due to incompatible browser.  Use GET as your request method.',0);}}
+httpobj.setRequestHeader('X-Powered-By','CPAINT v'+version);httpobj.onreadystatechange=callback;if(config['transfer_mode']=='GET'){httpobj.send(null);}else{debug('sending query: '+querystring,1);httpobj.send(querystring);}
+if(config['async']==false){callback();}}
+this.test_ajax_capability=function(){return get_connection_object();}
+var get_connection_object=function(){var return_value=false;var new_connection=false;if(config['persistent_connection']==false){debug('Using new connection object',1);new_connection=true;}else{debug('Using shared connection object.',1);if(typeof httpobj!='object'){debug('Getting new persistent connection object.',1);new_connection=true;}}
+if(new_connection==true){try{httpobj=new ActiveXObject('Msxml2.XMLHTTP');}catch(e){try{httpobj=new ActiveXObject('Microsoft.XMLHTTP');}catch(oc){httpobj=null;}}
+if(!httpobj&&typeof XMLHttpRequest!='undefined'){httpobj=new XMLHttpRequest();}
+if(!httpobj){debug('Could not create connection object',0);}else{return_value=true;}}
+if(httpobj.readyState!=4){httpobj.abort();}
+return return_value;}
+var callback=function(){var response=null;if(httpobj.readyState==4&&httpobj.status==200){debug(httpobj.responseText,1);debug('using response type '+config['response_type'],2);switch(config['response_type']){case'XML':debug(httpobj.responseXML,2);response=__cpaint_transformer.xml_conversion(httpobj.responseXML);break;case'OBJECT':response=__cpaint_transformer.object_conversion(httpobj.responseXML);break;case'TEXT':response=__cpaint_transformer.text_conversion(httpobj.responseText);break;case'E4X':response=__cpaint_transformer.e4x_conversion(httpobj.responseText);break;case'JSON':response=__cpaint_transformer.json_conversion(httpobj.responseText);break;default:debug('invalid response type \''+response_type+'\'',0);}
+if(response!=null&&typeof client_callback=='function'){client_callback(response,httpobj.responseText);}
+remove_from_stack();}else if(httpobj.readyState==4&&httpobj.status!=200){debug('invalid HTTP response code \''+Number(httpobj.status)+'\'',0);}}
+var remove_from_stack=function(){if(typeof stack_id=='number'&&__cpaint_stack[stack_id]&&config['persistent_connection']==false){__cpaint_stack[stack_id]=null;}}
+var debug=function(message,debug_level){var prefix='[CPAINT Debug] ';if(config['debugging']<1){prefix='[CPAINT Error] ';}
+if(config['debugging']>=debug_level){alert(prefix+message);}}}
+function cpaint_transformer(){this.object_conversion=function(xml_document){var return_value=new cpaint_result_object();var i=0;var firstNodeName='';if(typeof xml_document=='object'&&xml_document!=null){for(i=0;i<xml_document.childNodes.length;i++){if(xml_document.childNodes[i].nodeType==1){firstNodeName=xml_document.childNodes[i].nodeName;break;}}
+var ajax_response=xml_document.getElementsByTagName(firstNodeName);return_value[firstNodeName]=new Array();for(i=0;i<ajax_response.length;i++){var tmp_node=create_object_structure(ajax_response[i]);tmp_node.id=ajax_response[i].getAttribute('id')
+return_value[firstNodeName].push(tmp_node);}}else{debug('received invalid XML response',0);}
+return return_value;}
+this.xml_conversion=function(xml_document){return xml_document;}
+this.text_conversion=function(text){return decode(text);}
+this.e4x_conversion=function(text){text=text.replace(/^\<\?xml[^>]+\>/,'');return new XML(text);}
+this.json_conversion=function(text){return JSON.parse(text);}
+var create_object_structure=function(stream){var return_value=new cpaint_result_object();var node_name='';var i=0;var attrib=0;if(stream.hasChildNodes()==true){for(i=0;i<stream.childNodes.length;i++){node_name=stream.childNodes[i].nodeName;node_name=node_name.replace(/[^a-zA-Z0-9_]*/g,'');if(typeof return_value[node_name]!='object'){return_value[node_name]=new Array();}
+if(stream.childNodes[i].nodeType==1){var tmp_node=create_object_structure(stream.childNodes[i]);for(attrib=0;attrib<stream.childNodes[i].attributes.length;attrib++){tmp_node.set_attribute(stream.childNodes[i].attributes[attrib].nodeName,stream.childNodes[i].attributes[attrib].nodeValue);}
+return_value[node_name].push(tmp_node);}else if(stream.childNodes[i].nodeType==3){return_value.data=decode(String(stream.firstChild.data));}}}
+return return_value;}
+var decode=function(rawtext){var plaintext='';var i=0;var c1=0;var c2=0;var c3=0;var u=0;var t=0;while(i<rawtext.length){if(rawtext.charAt(i)=='\\'&&rawtext.charAt(i+1)=='u'){u=0;for(j=2;j<6;j+=1){t=parseInt(rawtext.charAt(i+j),16);if(!isFinite(t)){break;}
+u=u*16+t;}
+plaintext+=String.fromCharCode(u);i+=6;}else{plaintext+=rawtext.charAt(i);i++;}}
+if(plaintext!=''&&plaintext.search(/^\s+$/g)==-1&&!isNaN(plaintext)&&isFinite(plaintext)){plaintext=Number(plaintext);}
+return plaintext;}}
+function cpaint_result_object(){this.id=0;this.data='';var __attributes=new Array();this.find_item_by_id=function(){var return_value=null;var type=arguments[0];var id=arguments[1];var i=0;if(this[type]){for(i=0;i<this[type].length;i++){if(this[type][i].get_attribute('id')==id){return_value=this[type][i];break;}}}
+return return_value;}
+this.get_attribute=function(){var return_value=null;var id=arguments[0];if(typeof __attributes[id]!='undefined'){return_value=__attributes[id];}
+return return_value;}
+this.set_attribute=function(){__attributes[arguments[0]]=arguments[1];}}
+Array.prototype.______array='______array';var JSON={org:'http://www.JSON.org',copyright:'(c)2005 JSON.org',license:'http://www.crockford.com/JSON/license.html',stringify:function(arg){var c,i,l,s='',v;var numeric=true;switch(typeof arg){case'object':if(arg){if(arg.______array=='______array'){for(i in arg){if(i!='______array'&&(isNaN(i)||!isFinite(i))){numeric=false;break;}}
+if(numeric==true){for(i=0;i<arg.length;++i){if(typeof arg[i]!='undefined'){v=this.stringify(arg[i]);if(s){s+=',';}
+s+=v;}else{s+=',null';}}
+return'['+s+']';}else{for(i in arg){if(i!='______array'){v=arg[i];if(typeof v!='undefined'&&typeof v!='function'){v=this.stringify(v);if(s){s+=',';}
+s+=this.stringify(i)+':'+v;}}}
+return'{'+s+'}';}}else if(typeof arg.toString!='undefined'){for(i in arg){v=arg[i];if(typeof v!='undefined'&&typeof v!='function'){v=this.stringify(v);if(s){s+=',';}
+s+=this.stringify(i)+':'+v;}}
+return'{'+s+'}';}}
+return'null';case'number':return isFinite(arg)?String(arg):'null';case'string':l=arg.length;s='"';for(i=0;i<l;i+=1){c=arg.charAt(i);if(c>=' '){if(c=='\\'||c=='"'){s+='\\';}
+s+=c;}else{switch(c){case'\b':s+='\\b';break;case'\f':s+='\\f';break;case'\n':s+='\\n';break;case'\r':s+='\\r';break;case'\t':s+='\\t';break;default:c=c.charCodeAt();s+='\\u00'+Math.floor(c/16).toString(16)+
+(c%16).toString(16);}}}
+return s+'"';case'boolean':return String(arg);default:return'null';}},parse:function(text){var at=0;var ch=' ';function error(m){throw{name:'JSONError',message:m,at:at-1,text:text};}
+function next(){ch=text.charAt(at);at+=1;return ch;}
+function white(){while(ch!=''&&ch<=' '){next();}}
+function str(){var i,s='',t,u;if(ch=='"'){outer:while(next()){if(ch=='"'){next();return s;}else if(ch=='\\'){switch(next()){case'b':s+='\b';break;case'f':s+='\f';break;case'n':s+='\n';break;case'r':s+='\r';break;case't':s+='\t';break;case'u':u=0;for(i=0;i<4;i+=1){t=parseInt(next(),16);if(!isFinite(t)){break outer;}
+u=u*16+t;}
+s+=String.fromCharCode(u);break;default:s+=ch;}}else{s+=ch;}}}
+error("Bad string");}
+function arr(){var a=[];if(ch=='['){next();white();if(ch==']'){next();return a;}
+while(ch){a.push(val());white();if(ch==']'){next();return a;}else if(ch!=','){break;}
+next();white();}}
+error("Bad array");}
+function obj(){var k,o={};if(ch=='{'){next();white();if(ch=='}'){next();return o;}
+while(ch){k=str();white();if(ch!=':'){break;}
+next();o[k]=val();white();if(ch=='}'){next();return o;}else if(ch!=','){break;}
+next();white();}}
+error("Bad object");}
+function assoc(){var k,a=[];if(ch=='<'){next();white();if(ch=='>'){next();return a;}
+while(ch){k=str();white();if(ch!=':'){break;}
+next();a[k]=val();white();if(ch=='>'){next();return a;}else if(ch!=','){break;}
+next();white();}}
+error("Bad associative array");}
+function num(){var n='',v;if(ch=='-'){n='-';next();}
+while(ch>='0'&&ch<='9'){n+=ch;next();}
+if(ch=='.'){n+='.';while(next()&&ch>='0'&&ch<='9'){n+=ch;}}
+if(ch=='e'||ch=='E'){n+='e';next();if(ch=='-'||ch=='+'){n+=ch;next();}
+while(ch>='0'&&ch<='9'){n+=ch;next();}}
+v=+n;if(!isFinite(v)){error("Bad number");}else{return v;}}
+function word(){switch(ch){case't':if(next()=='r'&&next()=='u'&&next()=='e'){next();return true;}
+break;case'f':if(next()=='a'&&next()=='l'&&next()=='s'&&next()=='e'){next();return false;}
+break;case'n':if(next()=='u'&&next()=='l'&&next()=='l'){next();return null;}
+break;}
+error("Syntax error");}
+function val(){white();switch(ch){case'{':return obj();case'[':return arr();case'<':return assoc();case'"':return str();case'-':return num();default:return ch>='0'&&ch<='9'?num():word();}}
+return val();}};
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.proxy.php
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.proxy.php	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.proxy.php	(revision 2375)
@@ -0,0 +1,134 @@
+<?php
+/**
+* CPAINT (Cross-Platform Asynchronous INterface Toolkit)
+*
+* http://sf.net/projects/cpaint
+* 
+* released under the terms of the GPL
+* see http://www.fsf.org/licensing/licenses/gpl.txt for details
+* 
+* $Id$
+* $Log$
+* Revision 1.5  2005/07/20 17:25:14  saloon12yrd
+* updated file headers to contain proper licensing information as well as @package info
+*
+* Revision 1.3  2005/07/14 04:43:20  saloon12yrd
+* - changed parameter cpaint_returnxml=true to cpaint_response_type=(OBJECT|TEXT|XML) to be futureproof when JSON or other formats get implemented
+* - added toXML() method in cpaint2.inc.php
+* not sure it response-type TEXT worked in earlier version. now it does
+*
+*
+* proxy script to pass request on to remote servers
+*
+* @package    CPAINT
+* @author     Paul Sullivan <wiley14@gmail.com>
+* @author     Dominique Stender <dstender@st-webdevelopment.de>
+* @copyright  Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+* @version 		2.0.2
+*/
+
+//---- includes ----------------------------------------------------------------
+	/**
+	*	@include config
+	*/
+	require_once("cpaint2.config.php");
+	
+//---- main code ---------------------------------------------------------------
+
+  error_reporting (E_ALL ^ E_NOTICE ^ E_WARNING); 
+  set_time_limit(0);
+  
+  if ($_GET['cpaint_remote_url'] != "") {
+    $cp_remote_url      = urldecode($_GET['cpaint_remote_url']);
+    $cp_remote_method   = urldecode($_GET['cpaint_remote_method']);
+    $cp_remote_query    = urldecode($_GET['cpaint_remote_query']);
+    $cp_response_type   = strtoupper($_GET['cpaint_response_type']);
+  }
+
+  if ($_POST['cpaint_remote_url'] != "") {
+    $cp_remote_url      = urldecode($_POST['cpaint_remote_url']);
+    $cp_remote_method   = urldecode($_POST['cpaint_remote_method']);
+    $cp_remote_query    = urldecode($_POST['cpaint_remote_query']);
+    $cp_response_type   = strtoupper($_POST['cpaint_response_type']);
+  }
+
+  // propagate XML header if necessary
+  if ($cp_response_type == 'XML'
+    || $cp_response_type == 'OBJECT') {
+    header("Content-type:  text/xml");
+  }
+
+  // transfer mode specifics
+  if ($cp_remote_method == 'GET') {
+    $cp_remote_url    .= '?' . $cp_remote_query;
+    $cp_request_body  = '';
+
+    // prepare parameters
+    $url_parts  = parse_url($cp_remote_url);
+  
+    // build basic header
+    $cp_request_header  = 'GET ' . $url_parts['path'] . '?' . str_replace(' ', '+', $url_parts['query']) . " HTTP/1.0\r\n"
+                        . "Host: " . $url_parts['host'] . "\r\n";
+  
+  } elseif ($cp_remote_method == 'POST') {
+    $cp_request_body  = '&' . $cp_remote_query;
+
+    // prepare parameters
+    $url_parts  = parse_url($cp_remote_url);
+		
+		// check against whitelist
+		if ($cpaint2_config["proxy.security.use_whitelist"] == true) {
+			$url_allowed = false;
+			foreach($cpaint2_proxy_whitelist as $whitelistURL) {
+				$whiteList_parts = parse_url("http://" . $whitelistURL);
+				$url_parts_temp = parse_url("http://" . $cp_remote_url);
+				if (array_key_exists("path", $whiteList_parts)) {
+					if ((strtolower($whiteList_parts["path"]) == strtolower($url_parts_temp["path"])) && (strtolower($whiteList_parts["host"]) == strtolower($url_parts_temp["host"]))) $url_allowed = true;					
+				} else {	// no path, check only host
+					if (strtolower($whiteList_parts["host"]) == strtolower($url_parts_temp["host"]))	$url_allowed = true;
+				}
+			}
+			if ($url_allowed == false) die("[CPAINT] The host or script cannot be accessed through this proxy.");
+		}
+    
+    // build basic header
+    $cp_request_header  = 'POST ' . $url_parts['path']  . " HTTP/1.0\r\n"
+                        . "Host: " . $url_parts['host'] . "\r\n"
+                        . "Content-Type:  application/x-www-form-urlencoded\r\n";
+  }
+
+  // add port if none exists
+  if (!isset($url_parts['port'])) {
+    $url_parts['port'] = 80;
+  }
+
+  // add content-length header
+  $cp_request_header .= "Content-Length: " . strlen($cp_request_body) . "\r\n";
+
+  // add authentication to header if necessary
+  if ($url_parts['user'] != '') {
+    $cp_request_header .= 'Authorization: Basic ' . base64_encode($url_parts['user'] . ':' . $url_parts['pass']) . "\r\n";
+  }
+
+  // open connection
+  $cp_socket = @fsockopen($url_parts['host'], $url_parts['port'], $error, $errstr, 10);
+  
+  if ($cp_socket !== false) {
+    // send headers
+    @fwrite($cp_socket, $cp_request_header . "\r\n\r\n");
+    
+    // send body if necessary
+    if ($cp_request_body != '') {
+      @fwrite($cp_socket, $cp_request_body . "\r\n");
+    }
+    
+    while (!feof($cp_socket)) {
+      $http_data = $http_data . fgets($cp_socket);
+    }
+
+    list($http_headers, $http_body) = split("\r\n\r\n", $http_data, 2);
+    echo($http_body);
+    @fclose($cp_socket);
+  }
+
+?>
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.config.php
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.config.php	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.config.php	(revision 2375)
@@ -0,0 +1,35 @@
+<?
+/**
+* CPAINT (Cross-Platform Asynchronous INterface Toolkit)
+*
+* http://sf.net/projects/cpaint
+* 
+* released under the terms of the GPL
+* see http://www.fsf.org/licensing/licenses/gpl.txt for details
+* 
+* $Id$
+* $Log$
+* 
+* Configuration file for backend scripts, including proxy
+*
+* @package    CPAINT
+* @author     Paul Sullivan <wiley14@gmail.com>
+* @author     Dominique Stender <dstender@st-webdevelopment.de>
+* @copyright  Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+* @version 		2.0.2
+*/
+
+//---- proxy settings ----------------------------------------------------------
+	$cpaint2_config["proxy.security.use_whitelist"] = true;		
+				// Use the whitelist for allowed URLs?
+					
+//---- proxy security whitelist ------------------------------------------------
+	/* 	whitelist data should be added to the variable $cpaint2_proxy_whitelist[]
+			example: $cpaint2_proxy_whitelist[] = "example.com/test.php";
+				- or -
+			example: $cpaint2_proxy_whitelist[] = "example.com";
+			** Omit http:// and https:// from the URL **
+	*/
+	$cpaint2_proxy_whitelist[] = $_SERVER['HTTP_HOST']; 	// this server	
+
+?>
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.backend-debugger.php
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.backend-debugger.php	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.backend-debugger.php	(revision 2375)
@@ -0,0 +1,104 @@
+<? 
+/* 
+ CPAINT Backend Debug Interface
+
+ released under the terms of the GPL
+ see http://www.fsf.org/licensing/licenses/gpl.txt for details
+ 
+ @package    CPAINT
+ @access     public
+ @author     Paul Sullivan  <wiley14@gmail.com>
+ @author     Stephan Tijink <stijink@googlemail.com>
+ @copyright  Copyright (c) 2005-2006 Paul Sullivan - http://sf.net/projects/cpaint 
+ @version    2.0.2
+*/
+
+if (!(isset($_GET["cpaint_function"]) || isset($_POST["cpaint_function"]))) {
+	$debug_html_start = '
+	<html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>CPAINT Debug Interface</title>
+	<style type="text/css">body { background-color: #9999CC; margin-top: 0px; }
+		.style3 { font-family: Arial, Helvetica, sans-serif; font-size: 12px; }
+		.style4 { font-size: 20px; color: #FFFFFF; font-family: Arial, Helvetica, sans-serif;} 
+		.style5 { font-size: 16px;	color: #FFFFFF;	font-family: Arial, Helvetica, sans-serif;	font-weight: bold; } 
+		.style6 { font-size: 12px;	color: #FFFFFF;	font-family: Arial, Helvetica, sans-serif;	font-weight: bold; } 
+		div, iframe {	margin: 0px; border: 1px solid #9999CC; }
+	</style>
+	<script type="text/javascript">
+		function showForm(divId) {
+			if (document.getElementById(divId).style.display == "block") { 
+				document.getElementById(divId).style.display = "none"; 
+			} else { 
+				document.getElementById(divId).style.display = "block";
+			}
+		}
+	</script>
+	</head>
+	<body>
+		<table width="100%"  border="0" cellspacing="0" cellpadding="0">
+		<tr><td bgcolor="#9999CC"><div align="justify"><span class="style4">CPAINT Debug Interface</span></div></td></tr>
+		<tr><td bgcolor="#9999CC"><div align="justify"><span class="style6" style="color:#FFFFFF;">backend filename: '.$_SERVER["SCRIPT_NAME"].'</span></div></td></tr>
+		<tr><td height="10" bgcolor="#9999CC" class="style3"></td></tr>
+		<tr><td height="10" bgcolor="#9999CC" class="style3"></td></tr>
+		<tr><td bgcolor="#FFFFFF" class="style3"><blockquote>';
+	$debug_html_end = "</blockquote></td></tr></table><br><iframe name=\"results\" class=\"style3\" width=\"100%\" height=\"100%\" scrolling=\"yes\" allowtransparency=\"false\" style=\"background-color:  #FFFFFF\"></iframe></body></html>";
+	
+	// get function names and function variables/values
+	$functionArray = getCallableCode();
+	
+	$debug_body = "";
+	if (count($functionArray) > 0) {
+		foreach ($functionArray as $func_name=>$func_variables) {
+			$debug_body = $debug_body . "<form method=\"get\" target=\"results\"><a href=\"javascript:showForm('" . $func_name . "_form');\">" . $func_name . "</a><div id=\"" . $func_name . "_form\" style=\"display:  none;\">";
+			
+			$debug_body = $debug_body . '<table border="0">';
+			if ( count($func_variables) > 0) {
+				foreach ($func_variables as $var_name=>$var_preset) {
+					$debug_body = $debug_body . '<tr><td class="style3">Parameter (<strong>$'.$var_name.'</strong>):</td><td><input type="text" name="cpaint_argument[]"></td>';
+					if (strlen($var_preset) > 0) { $debug_body = $debug_body . "<td class=\"style3\"> &nbsp; default value is <strong>".$var_preset." &nbsp;</strong></td>"; }
+					$debug_body = $debug_body . '</tr>';
+				}
+			}
+			$debug_body = $debug_body . "<tr><td class=\"style3\">Response Type:</td><td><select name=\"cpaint_response_type\"><option>TEXT</option><option>XML</option><option>OBJECT</option></select></td></tr>";
+			$debug_body = $debug_body . "<tr><td colspan=\"3\"><input type=\"hidden\" name=\"cpaint_function\" value=\"" . $func_name . "\"><input type=\"submit\"></td></tr></table></div></form>";
+		}
+	}
+	
+	print($debug_html_start . $debug_body . $debug_html_end);
+	die();
+}
+
+function getCallableCode() {
+	$scriptName = $_SERVER['SCRIPT_FILENAME'];
+	$fileLines = file($scriptName);
+	for ($i=0; $i < sizeof($fileLines); $i++) {
+		$line = trim($fileLines[$i]);
+		if (substr($line, 0, 9) == "FUNCTION " || substr($line,0,9) == "function ") {
+			$match[] = $line;
+		}		
+	}
+	for ($i = 0; $i < sizeof($match); $i++) {
+		$line = str_replace("function ", "", $match[$i]);
+		$line = str_replace("FUNCTION ", "", $line);
+		$line = str_replace("{", "", $line);
+		$parts = explode("(", $line);
+		$func_name = trim($parts[0]);
+		
+		$Tempargs = explode(")", $parts[1]);
+		$args = explode(",", $Tempargs[0]);
+		$argSize = sizeof($args);
+		
+		// check args for preset values
+		if ($argSize > 0) {
+			foreach ($args as $arg) {
+				$arg 		= trim ($arg);
+				$varArray 	= explode ("=", $arg);
+				$var_name 	= trim (str_replace ("$", "", $varArray["0"]));
+				$var_value 	= trim ($varArray["1"]);
+				$resultArray[$func_name][$var_name] = $var_value;
+			}
+		}
+	}
+	return $resultArray;
+}
+?>
Index: sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.php
===================================================================
--- sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.php	(revision 2375)
+++ sandbox/expressoMail1_2/corretor_ortografico/spell_checker/cpaint/cpaint2.inc.php	(revision 2375)
@@ -0,0 +1,939 @@
+<?php
+/**
+* CPAINT - Cross-Platform Asynchronous INterface Toolkit
+*
+* http://sf.net/projects/cpaint
+* 
+* released under the terms of the LGPL
+* see http://www.fsf.org/licensing/licenses/lgpl.txt for details
+*
+* @package    CPAINT
+* @author     Paul Sullivan <wiley14@gmail.com>
+* @author     Dominique Stender <dstender@st-webdevelopment.de>
+* @copyright  Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+* @version    $Id$
+*/
+  
+//---- includes ----------------------------------------------------------------
+  /**
+  * @include JSON
+  */
+  require_once(dirname(__FILE__) . '/json.php');
+	
+  /**
+  *	@include config
+  */
+  require_once("cpaint2.config.php");
+
+//---- variables ---------------------------------------------------------------
+  $GLOBALS['__cpaint_json'] = new JSON();
+
+//---- error reporting ---------------------------------------------------------
+	error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
+
+//---- classes -----------------------------------------------------------------
+  /**
+  * cpaint base class.
+  *
+  * @package    CPAINT
+  * @access     public
+  * @author     Paul Sullivan <wiley14@gmail.com>
+  * @author     Dominique Stender <dstender@st-webdevelopment.de>
+  * @copyright  Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
+  * @version    2.0.2
+  */
+  class cpaint {
+    /**
+    * version number
+    *
+    * @access private
+    * @var    string $version
+    */
+    var $version = '2.0.2';
+    
+    /**
+    * response type.
+    *
+    * @access   protected
+    * @var      string    $response_type
+    */
+    var $response_type;
+    
+    /**
+    * the basenode ajaxResponse.
+    *
+    * @access   protected
+    * @var      object    $basenode
+    */
+    var $basenode;
+    
+    /**
+    * list of registered methods available through the CPAINT API
+    * 
+    * @access   protected
+    * @var      array     $api_functions
+    */
+    var $api_functions;
+    
+    /**
+    * list of registered complex types used in the CPAINT API
+    *
+    * @access protected
+    * @var    array $api_datatypes
+    */
+    var $api_datatypes;
+    
+    /**
+    * whether or not the CPAINT API generates a WSDL when called with ?wsdl querystring
+    *
+    * @access private
+    * @var    boolean $use_wsdl
+    */
+    var $use_wsdl;
+    
+    /**
+    * PHP4 constructor.
+    *
+    * @access   public
+    * @return   void
+    */
+    function cpaint() {
+      $this->__construct();
+    }
+    
+    /**
+    * PHP 5 constructor.
+    *
+    * @access   public
+    * @return   void
+    * @todo -o"Dominique Stender" -ccpaint implement a better debugging
+    */
+    function __construct() {
+      // initialize properties
+      $this->basenode       = new cpaint_node();
+      $this->basenode->set_name('ajaxResponse');
+      $this->basenode->set_attribute('id', '');
+      $this->basenode->set_encoding('UTF-8');
+      
+      $this->response_type  = 'TEXT';
+      $this->api_functions  = array();
+      $this->api_datatypes  = array();
+      $this->use_wsdl       = true;
+      
+      $this->complex_type(array(
+          'name'      => 'cpaintResponseType',
+          'type'      => 'restriction',   // (restriction|complex|list)
+          'base_type' => 'string',        // scalar type of all values, e.g. 'string', for type = (restriction|list) only
+          'values'    => array(           // for type = 'restriction' only: list of allowed values
+            'XML', 'TEXT', 'OBJECT', 'E4X', 'JSON',
+          ),
+        )
+      );
+      $this->complex_type(array(
+          'name'      => 'cpaintDebugLevel',
+          'type'      => 'restriction',
+          'base_type' => 'long',
+          'values'    => array(
+            -1, 0, 1, 2
+          ),
+        )
+      );
+      $this->complex_type(array(
+          'name'      => 'cpaintDebugMessage',
+          'type'      => 'list',
+          'base_type' => 'string',
+        )
+      );
+      
+      $this->complex_type(array(
+          'name'      => 'cpaintRequestHead',
+          'type'      => 'complex',
+          'struct'    => array(
+            0 => array('name' => 'functionName',  'type' => 'string'),
+            1 => array('name' => 'responseType',  'type' => 'cpaintResponseType'),
+            2 => array('name' => 'debugLevel',    'type' => 'cpaintDebugLevel'),
+          ),
+        )
+      );
+      
+      $this->complex_type(array(
+          'name'      => 'cpaintResponseHead',
+          'type'      => 'complex',
+          'struct'    => array(
+            0 => array('name' => 'success',  'type' => 'boolean'),
+            1 => array('name' => 'debugger', 'type' => 'cpaintDebugMessage'),
+          ),
+        )
+      );
+      
+      // determine response type
+      if (isset($_REQUEST['cpaint_response_type'])) {
+        $this->response_type = strtoupper((string) $_REQUEST['cpaint_response_type']);
+      } // end: if
+    }
+
+    /**
+    * calls the user function responsible for this specific call.
+    *
+    * @access   public
+    * @param    string      $input_encoding         input data character encoding, default is UTF-8
+    * @return   void
+    */
+    function start($input_encoding = 'UTF-8') {
+      $user_function  = '';
+      $arguments      = array();
+      
+      // work only if there is no API version request
+      if (!isset($_REQUEST['api_query'])
+        && !isset($_REQUEST['wsdl'])) {
+        $this->basenode->set_encoding($input_encoding);
+        
+        if ($_REQUEST['cpaint_function'] != '') {
+          $user_function  = $_REQUEST['cpaint_function'];
+          $arguments      = $_REQUEST['cpaint_argument'];
+        }
+  
+        // perform character conversion on every argument
+        foreach ($arguments as $key => $value) {
+          
+          if (get_magic_quotes_gpc() == true) {
+            $value = stripslashes($value);
+          } // end: if
+          
+          // convert from JSON string
+          $arguments[$key] = $GLOBALS['__cpaint_json']->parse($value);
+        } // end: foreach
+        
+        $arguments = cpaint_transformer::decode_array($arguments, $this->basenode->get_encoding());
+  
+        if (is_array($this->api_functions[$user_function])
+          && is_callable($this->api_functions[$user_function]['call'])) {
+          // a valid API function is to be called
+          call_user_func_array($this->api_functions[$user_function]['call'], $arguments);
+        
+        } else if ($user_function != '') {
+          // desired function is not registered as API function
+          $this->basenode->set_data('[CPAINT] A function name was passed that is not allowed to execute on this server.');
+        }
+      } // end: if
+    }
+
+    /**
+    * generates and prints the response based on response type supplied by the frontend.
+    *
+    * @access  public
+    * @return  void
+    */
+    function return_data() {
+      // send appropriate headers to avoid caching
+      header('Expires: Fri, 14 Mar 1980 20:53:00 GMT');
+      header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+      header('Cache-Control: no-cache, must-revalidate');
+      header('Pragma: no-cache'); 
+      header('X-Powered-By: CPAINT v' . $this->version . '/PHP v' . phpversion());
+      
+      // work only if there is no API version request
+      
+      if (!isset($_REQUEST['api_query']) 
+        && !isset($_REQUEST['wsdl'])) {
+        // trigger generation of response
+        switch (trim($this->response_type)) {
+  
+          case 'TEXT':
+            header('Content-type: text/plain; charset=' . cpaint_transformer::find_output_charset($this->basenode->get_encoding()));
+            echo cpaint_transformer::toString($this->basenode);
+            break;
+          
+          case 'JSON':
+            header('Content-type: text/plain; charset=' . cpaint_transformer::find_output_charset($this->basenode->get_encoding()));
+            echo cpaint_transformer::toJSON($this->basenode);
+            break;
+               
+          case 'OBJECT':
+          case 'E4X':
+          case 'XML':
+            header('Content-type:  text/xml; charset=' . cpaint_transformer::find_output_charset($this->basenode->get_encoding()));
+            echo '<?xml version="1.0" encoding="' . cpaint_transformer::find_output_charset($this->basenode->get_encoding()) . '"?>' 
+                . cpaint_transformer::toXML($this->basenode);
+            break;
+  
+          default:
+            echo 'ERROR: invalid response type \'' . $this->response_type . '\'';
+        } // end: switch
+      
+      } elseif (isset($_REQUEST['api_query'])) {
+        // API version request
+        header('Content-type: text/plain; charset=ISO-8859-1');
+        echo 'CPAINT v' . $this->version . '/PHP v' . phpversion();
+      
+      } elseif ($this->use_wsdl == true
+        && isset($_REQUEST['wsdl'])) {
+        
+        if (is_file(dirname(__FILE__) . '/cpaint2.wsdl.php')
+          && is_readable(dirname(__FILE__) . '/cpaint2.wsdl.php')) {
+          
+          require_once(dirname(__FILE__) . '/cpaint2.wsdl.php');
+          
+          if (class_exists('cpaint_wsdl')) {
+            // create new instance of WSDL library
+            $wsdl = new cpaint_wsdl();
+
+            // build WSDL info
+            header('Content-type: text/xml; charset=UTF-8');
+            echo $wsdl->generate($this->api_functions, $this->api_datatypes);
+
+          } else {
+            header('Content-type: text/plain; charset=ISO-8859-1');
+            echo 'WSDL generator is unavailable';
+          } // end: if
+        
+        } else {
+          header('Content-type: text/plain; charset=ISO-8859-1');
+          echo 'WSDL generator is unavailable';
+        } // end: if
+      } // end: if
+    }
+    
+    /**
+    * registers a new function or method as part of the CPAINT API
+    *
+    * @access public
+    * @param  mixed     $func     function name, array(&$object, 'function_name') or array('class', 'function_name')
+    * @param  array     $input    function input parameters (not yet used by CPAINT and subject to change)
+    * @param  array     $output   function output format (not yed used by CPAINT and subject to change)
+    * @param  string    $comment  description of the functionality
+    * @return boolean
+    */
+    function register($func, $input = array(), $output = array(), $comment = '') {
+      $return_value = false;
+      $input        = (array)   $input;
+      $output       = (array)   $output;
+      $comment      = (string)  $comment;
+      
+      if (is_array($func)
+        && (is_object($func[0]) || is_string($func[0]))
+        && is_string($func[1])
+        && is_callable($func)) {
+        
+        // calling a method of an object
+        $this->api_functions[$func[1]] = array(
+          'call'    => $func,
+          'input'   => $input,
+          'output'  => $output,
+          'comment' => $comment,
+        );
+        $return_value = true;
+        
+      } elseif (is_string($func)) {
+        // calling a standalone function
+        $this->api_functions[$func] = array(
+          'call'    => $func,
+          'input'   => $input,
+          'output'  => $output,
+          'comment' => $comment,
+        );
+        $return_value = true;
+      } // end: if
+      
+      return $return_value;
+    }
+    
+    
+    
+    /**
+    * unregisters a function that is currently part of the CPAINT API.
+    *     
+    * proves useful when the same set of functions is to be used in the
+    * frontend and in some kind of administration environment. you might
+    * want to unregister a few (admin) functions for the frontend in this
+    * case.
+    *
+    * @access public
+    * @param  string     $func     function name
+    * @return boolean
+    */
+    function unregister($func) {
+      $retval = false;
+      
+      if (is_array($this->api_functions[$func])) {
+        unset($this->api_functions[$func]);
+      } // end: if
+    
+      return $retval;
+    }
+    
+    
+    
+    /**
+    * registers a complex data type
+    *
+    * @access public
+    * @param  array     $schema     schema definition for the complex type
+    * @return boolean
+    */
+    function complex_type($schema) {
+      $return_value = false;
+      $schema       = (array)   $schema;
+      
+      if ($schema['name'] != ''
+        && in_array($schema['type'], array('restriction', 'complex', 'list'))) {
+        
+        $this->api_datatypes[] = $schema;
+        $return_value          = true;
+      } // end: if
+    
+      return $return_value;
+    }
+    
+    /**
+    * switches the generation of WSDL on/off. default is on
+    *
+    * @access public
+    * @param  boolean   $state      state of WSDL generation
+    * @return void
+    */
+    function use_wsdl($state) {
+      $this->use_wsdl = (boolean) $state;
+    }
+    
+    /**
+    * adds a new subnode to the basenode.
+    *
+    * will return a reference to it for further processing.
+    *
+    * @access   public
+    * @param    string    $nodename     name of the new node
+    * @param    string    $id           id of the new node
+    * @return   object
+    */
+    function &add_node($nodename, $id = '') {
+      return $this->basenode->add_node($nodename, $id);
+    }
+
+    /**
+    * assigns textual data to the basenode.
+    *
+    * @access   public
+    * @param    mixed    $data    data to assign to this node
+    * @return   void
+    */
+    function set_data($data) {
+      $this->basenode->set_data($data);
+    }
+    
+    /**
+    * returns the data assigned to the basenode.
+    *
+    * @access   public
+    * @return   mixed
+    */
+    function get_data() {
+      return $this->basenode->get_data();
+    }
+    
+    /**
+    * sets the id property of the basenode.
+    *
+    * @deprecated   deprecated since version 2.0.0
+    * @access       public
+    * @param        string    $id      the id
+    * @return       void
+    */
+    function set_id($id) {
+      $this->basenode->set_attribute('id', $id);
+    }
+    
+    /**
+    * gets the id property of the basenode.
+    *
+    * @deprecated   deprecated since version 2.0.0
+    * @access       public
+    * @return       string
+    */
+    function get_id() {
+      return $this->basenode->get_attribute('id');
+    }
+    
+    /**
+    * adds a new attribute to the basenode.
+    *
+    * @access   public
+    * @param    string    $name       attribute name
+    * @param    mixed     $value      attribute value
+    * @return   void
+    */
+    function set_attribute($name, $value) {
+      $this->basenode->set_attribute($name, $value);
+    }
+    
+    /**
+    * retrieves an attribute of the basenode by name.
+    *
+    * @access   public
+    * @param    string    $name       attribute name
+    * @return   string
+    */
+    function get_attribute($name) {
+      return $this->basenode->get_attributes($name);
+    }
+    
+    /**
+    * set name property of the basenode.
+    *
+    * @access   public
+    * @param    string    $name   the name
+    * @return   void
+    */
+    function set_name($name) {
+      $this->basenode->set_name($name);
+    }
+
+    /**
+    * get name property of the basenode.
+    *
+    * @access   public
+    * @return   string
+    */
+    function get_name() {
+      return $this->basenode->get_name();
+    }
+    
+    
+    
+    /**
+    * returns the response type as requested by the client
+    *
+    * @access public
+    * @return string
+    */
+    function get_response_type() {
+      return $this->response_type;
+    }
+    
+  }
+  
+  /**
+  * a cpaint data node. Data nodes are used to build up the response.
+  *
+  * @package   CPAINT
+  * @access    public
+  * @author    Dominique Stender <dstender@st-webdevelopment.de>
+  * @copyright 2005 (Dominique Stender); All rights reserved
+  * @version   2.0.2
+  */
+  class cpaint_node {
+    /**
+    * array of subnodes.
+    *
+    * @access   public
+    * @var      array     $composites
+    */
+    var $composites;
+    
+    /**
+    * node attributes.
+    *
+    * @access   public
+    * @var      array     $attributes
+    */
+    var $attributes;
+
+    /**
+    * name of this node.
+    *
+    * @access   public
+    * @var      string    $nodename
+    */
+    var $nodename;
+
+    /**
+    * textual data of this node.
+    *
+    * @access   public
+    * @var      string    $data
+    */
+    var $data;
+
+    /**
+    * character encoding for input data
+    * 
+    * @access   private
+    * @var      $input_encoding
+    */
+    var $input_encoding;
+
+    /**
+    * PHP4 constructor.
+    *
+    * @package  CPAINT
+    * @access   public
+    * @return   void
+    */
+    function cpaint_node() {
+      $this->__construct();
+    }
+    
+    /**
+    * PHP 5 constructor.
+    *
+    * @access   public
+    * @return   void
+    */
+    function __construct() {
+      // initialize properties
+      $this->composites     = array();
+      $this->attributes     = array();
+      $this->data           = '';
+      
+      $this->set_encoding('UTF-8');
+      $this->set_name('');
+      $this->set_attribute('id', '');
+    }
+
+    /**
+    * adds a new subnode to this node.
+    *
+    * will return a reference to it for further processing.
+    *
+    * @access   public
+    * @param    string    $nodename     name of the new node
+    * @param    string    $id           id of the new node
+    * @return   object
+    */
+    function &add_node($nodename, $id = '') {
+      $composites = count($this->composites);
+
+      // create new node
+      $this->composites[$composites] =& new cpaint_node();
+      $this->composites[$composites]->set_name($nodename);
+      $this->composites[$composites]->set_attribute('id', $id);
+      $this->composites[$composites]->set_encoding($this->input_encoding);
+
+      return $this->composites[$composites];
+    }
+
+    /**
+    * assigns textual data to this node.
+    *
+    * @access   public
+    * @param    mixed    $data    data to assign to this node
+    * @return   void
+    */
+    function set_data($data) {
+      $this->data = $data;
+    }
+    
+    /**
+    * returns the textual data assigned to this node.
+    *
+    * @access   public
+    * @return   mixed
+    */
+    function get_data() {
+      return $this->data;
+    }
+    
+    /**
+    * sets the id property of this node.
+    *
+    * @deprecated   deprecated since version 2.0.0
+    * @access       public
+    * @param        string    id      the id
+    * @return       void
+    */
+    function set_id($id) {
+      if ($id != '') {
+        $this->set_attribute('id', $id);
+      } // end: if
+    }
+    
+    /**
+    * returns the id property if this node.
+    *
+    * @deprecated   deprecated since version 2.0.0
+    * @access       public
+    * @return       string
+    */
+    function get_id() {
+      return $this->get_attribute('id');
+    }
+    
+    /**
+    * adds a new attribute to this node.
+    *
+    * @access   public
+    * @param    string    $name       attribute name
+    * @param    mixed     $value      attribute value
+    * @return   void
+    */
+    function set_attribute($name, $value) {
+      $this->attributes[$name] = (string) $value;
+    }
+    
+    /**
+    * retrieves an attribute by name.
+    *
+    * @access   public
+    * @param    string    $name       attribute name
+    * @return   string
+    */
+    function get_attribute($name) {
+      return $this->attributes[$name];
+    }
+    
+    /**
+    * set name property.
+    *
+    * @access   public
+    * @param    string    $name   the name
+    * @return   void
+    */
+    function set_name($name) {
+      $this->nodename = (string) $name;
+    }
+
+    /**
+    * get name property.
+    *
+    * @access   public
+    * @return   string
+    */
+    function get_name() {
+      return $this->nodename;
+    }
+  
+    /**
+    * sets the character encoding for this node
+    * 
+    * @access   public
+    * @param    string      $encoding     character encoding
+    * @return   void
+    */
+    function set_encoding($encoding) {
+      $this->input_encoding = strtoupper((string) $encoding);
+    }
+  
+    /**
+    * returns the character encoding for this node
+    * 
+    * @access   public
+    * @return   string
+    */
+    function get_encoding() {
+      return $this->input_encoding;
+    }
+  }
+  
+  /**
+  * static class of output transformers.
+  *
+  * @package   CPAINT
+  * @access    public
+  * @author    Dominique Stender <dstender@st-webdevelopment.de>
+  * @copyright 2002-2005 (Dominique Stender); All rights reserved
+  * @version   2.0.2
+  */
+  class cpaint_transformer {
+    /**
+    * toString method, used to generate response of type TEXT.
+    * will perform character transformation according to parameters.
+    *
+    * @access   public
+    * @param    object    $node               a cpaint_node object
+    * @return   string
+    */
+    function toString(&$node) {
+      $return_value = '';
+
+      foreach ($node->composites as $composite) {
+        $return_value .= cpaint_transformer::toString($composite);
+      }
+
+      $return_value .= cpaint_transformer::encode($node->get_data(), $node->get_encoding());
+
+      return $return_value;
+    }
+    
+    /**
+    * XML response generator.
+    * will perform character transformation according to parameters.
+    *
+    * @access   public
+    * @param    object    $node               a cpaint_node object
+    * @return   string
+    */
+    function toXML(&$node) {
+      $return_value = '<' . $node->get_name();
+      
+      // handle attributes
+      foreach ($node->attributes as $name => $value) {
+        if ($value != '') {
+          $return_value .= ' ' . $name . '="' . $node->get_attribute($name) . '"';
+        }
+      } // end: foreach
+      
+      $return_value .= '>';
+
+      // handle subnodes    
+      foreach ($node->composites as $composite) {
+        $return_value .= cpaint_transformer::toXML($composite);
+      }
+
+      $return_value .= cpaint_transformer::encode($node->get_data(), $node->get_encoding())
+                    . '</' . $node->get_name() . '>';
+
+      return $return_value;
+    }
+    
+    /**
+    * JSON response generator.
+    * will perform character transformation according to parameters.
+    *
+    * @access   public
+    * @param    object    $node               a cpaint_node object
+    * @return   string
+    */
+    function toJSON($node) {
+      $return_value = '';
+      $JSON_node    = new stdClass();
+
+      // handle attributes
+      $JSON_node->attributes = $node->attributes;      
+
+      // handle subnodes    
+      foreach ($node->composites as $composite) {
+        
+        if (!is_array($JSON_node->{$composite->nodename})) {
+          $JSON_node->{$composite->nodename} = array();
+        } // end: if
+        
+        // we need to parse the JSON object again to avoid multiple encoding
+        $JSON_node->{$composite->nodename}[] = $GLOBALS['__cpaint_json']->parse(cpaint_transformer::toJSON($composite));
+      }
+
+      // handle data
+      $JSON_node->data = $node->data;
+      
+      return $GLOBALS['__cpaint_json']->stringify($JSON_node);
+    }
+    
+    /**
+    * performs conversion to JavaScript-safe UTF-8 characters
+    *
+    * @access   public
+    * @param    string    $data         data to convert
+    * @param    string    $encoding     character encoding
+    * @return   string
+    */
+    function encode($data, $encoding) {
+      // convert string
+      if (function_exists('iconv')) {
+        // iconv is by far the most flexible approach, try this first
+        $return_value = iconv($encoding, 'UTF-8', $data);
+
+      } elseif ($encoding == 'ISO-8859-1') {
+        // for ISO-8859-1 we can use utf8-encode()
+        $return_value = utf8_encode($data);
+
+      } else {
+        // give up. if UTF-8 data was supplied everything is fine!
+        $return_value = $data;
+      } /* end: if */
+      
+      // now encode non-printable characters
+      for ($i = 0; $i < 32; $i++) {
+        $return_value = str_replace(chr($i), '\u00' . sprintf('%02x', $i), $return_value);
+      } // end: for
+      
+      // encode <, >, and & respectively for XML sanity
+      $return_value = str_replace(chr(0x26), '\u0026', $return_value);
+      $return_value = str_replace(chr(0x3c), '\u003c', $return_value);
+      $return_value = str_replace(chr(0x3e), '\u003e', $return_value);
+      
+      return $return_value;
+    }
+  
+    /**
+    * performs conversion from JavaScript encodeURIComponent() string (UTF-8) to 
+    * the charset in use.
+    * 
+    * @access   public
+    * @param    string    $data         data to convert
+    * @param    string    $encoding     character encoding
+    * @return   string
+    */
+    function decode($data, $encoding) {
+      // convert string
+      
+      if (is_string($data)) {
+        if (function_exists('iconv')) {
+          // iconv is by far the most flexible approach, try this first
+          $return_value = iconv('UTF-8', $encoding, $data);
+  
+        } elseif ($encoding == 'ISO-8859-1') {
+          // for ISO-8859-1 we can use utf8-decode()
+          $return_value = utf8_decode($data);
+  
+        } else {
+          // give up. if data was supplied in the correct format everything is fine!
+          $return_value = $data;
+        } // end: if
+      
+      } else {
+        // non-string value
+        $return_value = $data;
+      } // end: if
+      
+      return $return_value;
+    }
+
+    /**
+    * decodes a (nested) array of data from UTF-8 into the configured character set
+    * 
+    * @access   public
+    * @param    array     $data         data to convert
+    * @param    string    $encoding     character encoding
+    * @return   array
+    */
+    function decode_array($data, $encoding) {
+      $return_value = array();
+    
+      foreach ($data as $key => $value) {
+
+        if (!is_array($value)) {
+          $return_value[$key] = cpaint_transformer::decode($value, $encoding);
+
+        } else {
+          $return_value[$key] = cpaint_transformer::decode_array($value, $encoding);
+        }
+      }
+
+      return $return_value;
+    }
+    
+    /**
+    * determines the output character set
+    * based on input character set
+    *
+    * @access   public
+    * @param    string    $encoding     character encoding
+    * @return   string
+    */
+    function find_output_charset($encoding) {
+      $return_value = 'UTF-8';
+    
+      if (function_exists('iconv')
+        || $encoding == 'UTF-8'
+        || $encoding == 'ISO-8859-1') {
+        
+        $return_value = 'UTF-8';
+
+      } else {
+        $return_value = $encoding;
+      } // end: if
+      
+      return $return_value;
+    }
+  }
+ 
+?>
