--- /dev/null
+<?PHP // $Id$
+
+// Manage all uploaded files in a course file area
+
+// This file is a hack to files/index.php that removes
+// the headers and adds some controls so that images
+// can be selected within the Richtext editor.
+
+// All the Moodle-specific stuff is in this top section
+// Configuration and access control occurs here.
+// Must define: USER, basedir, baseweb, html_header and html_footer
+// USER is a persistent variable using sessions
+
+ require("../../config.php");
+ require("../../files/mimetypes.php");
+
+ require_variable($id);
+ optional_variable($file, "");
+ optional_variable($wdir, "");
+ optional_variable($action, "");
+
+ if (! $course = get_record("course", "id", $id) ) {
+ error("That's an invalid course id");
+ }
+
+ require_login($course->id);
+
+ if (! isteacher($course->id) ) {
+ error("Only teachers can edit files");
+ }
+
+ function html_footer() {
+ echo "</td></tr></table></body></html>";
+ }
+
+ function html_header($course, $wdir, $formfield=""){
+
+ global $CFG;
+
+ if (! $site = get_site()) {
+ error("Invalid site!");
+ }
+
+ if ($course->id == $site->id) {
+ $strfiles = get_string("sitefiles");
+ } else {
+ $strfiles = get_string("files");
+ }
+
+ if ($wdir == "/") {
+ $fullnav = "$strfiles";
+ } else {
+ $dirs = explode("/", $wdir);
+ $numdirs = count($dirs);
+ $link = "";
+ $navigation = "";
+ for ($i=1; $i<$numdirs; $i++) {
+ $navigation .= " -> ";
+ $link .= "/".urlencode($dirs[$i]);
+ $navigation .= "<a href=\"".$_SERVER['PHP_SELF']."?id=$course->id&wdir=$link\">".$dirs[$i]."</a>";
+ }
+ $fullnav = "<a href=\"".$_SERVER['PHP_SELF']."?id=$course->id&wdir=/\">$strfiles</a> $navigation";
+ }
+
+ print_header();
+ ?>
+ <script language="javscript" type="text/javascript">
+ <!--
+ function set_value(url) {
+ var str_url = url;
+ //if(document.all) {
+ // window.returnValue = str_url;
+ // window.close();
+ //}
+ //else {
+ window.opener.document.forms[0].url.value = str_url;
+ window.close();
+ //}
+ }
+ -->
+ </script>
+ <?php
+
+ echo '<table border="0" cellpadding="3" cellspacing="0" width="100%">';
+ echo '<tr>';
+ echo '<td bgcolor="'.$THEME->cellheading.'" class="navbar">';
+ echo '<font size="2"><b>'."$course->shortname -> $fullnav".'</b></font>';
+ echo '</td>';
+ echo '</tr>';
+ echo '</table>';
+
+ if ($course->id == $site->id) {
+ print_heading(get_string("publicsitefileswarning"), "center", 2);
+ }
+
+ echo "<table border=0 align=center cellspacing=3 cellpadding=3 width=640>";
+ echo "<tr>";
+ echo "<td colspan=\"2\">";
+ }
+
+ if (! $basedir = make_upload_directory("$course->id")) {
+ error("The site administrator needs to fix the file permissions");
+ }
+
+ $baseweb = $CFG->wwwroot;
+
+// End of configuration and access control
+
+
+ $regexp="\\.\\.";
+ if (ereg( $regexp, $file, $regs )| ereg( $regexp, $wdir,$regs )) {
+ $message = "Error: Directories can not contain \"..\"";
+ $wdir = "/";
+ $action = "";
+ }
+
+ if (!$wdir) {
+ $wdir="/";
+ }
+
+
+ switch ($action) {
+
+ case "upload":
+ html_header($course, $wdir);
+
+ if (!empty($_FILES['userfile'])) {
+ $userfile = $_FILES['userfile'];
+ } else {
+ $save = false;
+ }
+ if (!empty($save)) {
+ if (!is_uploaded_file($userfile['tmp_name']) or $userfile['size'] == 0) {
+ notify(get_string("uploadnofilefound"));
+ } else {
+ $userfile_name = clean_filename($userfile['name']);
+ if ($userfile_name) {
+ $newfile = "$basedir$wdir/$userfile_name";
+ if (move_uploaded_file($userfile['tmp_name'], $newfile)) {
+ chmod($newfile, 0666);
+ $a = NULL;
+ $a->file = "$userfile_name (".$userfile['type'].")";
+ $a->directory = $wdir;
+ print_string("uploadedfileto", "", $a);
+ } else {
+ notify(get_string("uploadproblem", "", $userfile_name));
+ }
+ }
+ }
+ displaydir($wdir);
+
+ } else {
+ $upload_max_filesize = get_max_upload_file_size();
+ $filesize = display_size($upload_max_filesize);
+
+ $struploadafile = get_string("uploadafile");
+ $struploadthisfile = get_string("uploadthisfile");
+ $strmaxsize = get_string("maxsize", "", $filesize);
+ $strcancel = get_string("cancel");
+
+ echo "<P>$struploadafile ($strmaxsize) --> <B>$wdir</B>";
+ echo "<TABLE><TR><TD COLSPAN=2>";
+ echo "<FORM ENCTYPE=\"multipart/form-data\" METHOD=\"post\" ACTION=\"".$_SERVER['PHP_SELF']."\">";
+ echo " <INPUT TYPE=hidden NAME=MAX_FILE_SIZE value=\"$upload_max_filesize\">";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=upload>";
+ echo " <INPUT NAME=\"userfile\" TYPE=\"file\" size=\"60\">";
+ echo " </TD><TR><TD WIDTH=10>";
+ echo " <INPUT TYPE=submit NAME=save VALUE=\"$struploadthisfile\">";
+ echo "</FORM>";
+ echo "</TD><TD WIDTH=100%>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=\"get\">";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=cancel>";
+ echo " <INPUT TYPE=submit VALUE=\"$strcancel\">";
+ echo "</FORM>";
+ echo "</TD></TR></TABLE>";
+ }
+ html_footer();
+ break;
+
+ case "delete":
+ if (!empty($confirm)) {
+ html_header($course, $wdir);
+ foreach ($USER->filelist as $file) {
+ $fullfile = $basedir.$file;
+ if (! fulldelete($fullfile)) {
+ echo "<BR>Error: Could not delete: $fullfile";
+ }
+ }
+ clearfilelist();
+ displaydir($wdir);
+ html_footer();
+
+ } else {
+ html_header($course, $wdir);
+ if (setfilelist($_POST)) {
+ echo "<p align=center>".get_string("deletecheckwarning").":</p>";
+ print_simple_box_start("center");
+ printfilelist($USER->filelist);
+ print_simple_box_end();
+ echo "<br />";
+ notice_yesno (get_string("deletecheckfiles"),
+ "".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&action=delete&confirm=1",
+ "".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&action=cancel");
+ } else {
+ displaydir($wdir);
+ }
+ html_footer();
+ }
+ break;
+
+ case "move":
+ html_header($course, $wdir);
+ if ($count = setfilelist($_POST)) {
+ $USER->fileop = $action;
+ $USER->filesource = $wdir;
+ echo "<p align=center>";
+ print_string("selectednowmove", "moodle", $count);
+ echo "</p>";
+ }
+ displaydir($wdir);
+ html_footer();
+ break;
+
+ case "paste":
+ html_header($course, $wdir);
+ if (isset($USER->fileop) and $USER->fileop == "move") {
+ foreach ($USER->filelist as $file) {
+ $shortfile = basename($file);
+ $oldfile = $basedir.$file;
+ $newfile = $basedir.$wdir."/".$shortfile;
+ if (!rename($oldfile, $newfile)) {
+ echo "<P>Error: $shortfile not moved";
+ }
+ }
+ }
+ clearfilelist();
+ displaydir($wdir);
+ html_footer();
+ break;
+
+ case "rename":
+ if (!empty($name)) {
+ html_header($course, $wdir);
+ $name = clean_filename($name);
+ if (file_exists($basedir.$wdir."/".$name)) {
+ echo "Error: $name already exists!";
+ } else if (!rename($basedir.$wdir."/".$oldname, $basedir.$wdir."/".$name)) {
+ echo "Error: could not rename $oldname to $name";
+ }
+ displaydir($wdir);
+
+ } else {
+ $strrename = get_string("rename");
+ $strcancel = get_string("cancel");
+ $strrenamefileto = get_string("renamefileto", "moodle", $file);
+ html_header($course, $wdir, "form.name");
+ echo "<P>$strrenamefileto:";
+ echo "<TABLE><TR><TD>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=\"post\" NAME=\"form\">";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=rename>";
+ echo " <INPUT TYPE=hidden NAME=oldname VALUE=\"$file\">";
+ echo " <INPUT TYPE=text NAME=name SIZE=35 VALUE=\"$file\">";
+ echo " <INPUT TYPE=submit VALUE=\"$strrename\">";
+ echo "</FORM>";
+ echo "</TD><TD>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=cancel>";
+ echo " <INPUT TYPE=submit VALUE=\"$strcancel\">";
+ echo "</FORM>";
+ echo "</TD></TR></TABLE>";
+ }
+ html_footer();
+ break;
+
+ case "mkdir":
+ if (!empty($name)) {
+ html_header($course, $wdir);
+ $name = clean_filename($name);
+ if (file_exists("$basedir$wdir/$name")) {
+ echo "Error: $name already exists!";
+ } else if (! make_upload_directory("$course->id/$wdir/$name")) {
+ echo "Error: could not create $name";
+ }
+ displaydir($wdir);
+
+ } else {
+ $strcreate = get_string("create");
+ $strcancel = get_string("cancel");
+ $strcreatefolder = get_string("createfolder", "moodle", $wdir);
+ html_header($course, $wdir, "form.name");
+ echo "<P>$strcreatefolder:";
+ echo "<TABLE><TR><TD>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=post NAME=form>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=mkdir>";
+ echo " <INPUT TYPE=text NAME=name SIZE=35>";
+ echo " <INPUT TYPE=submit VALUE=\"$strcreate\">";
+ echo "</FORM>";
+ echo "</TD><TD>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=cancel>";
+ echo " <INPUT TYPE=submit VALUE=\"$strcancel\">";
+ echo "</FORM>";
+ echo "</TD></TR></TABLE>";
+ }
+ html_footer();
+ break;
+
+ case "edit":
+ html_header($course, $wdir);
+ if (isset($text)) {
+ $fileptr = fopen($basedir.$file,"w");
+ fputs($fileptr, stripslashes($text));
+ fclose($fileptr);
+ displaydir($wdir);
+
+ } else {
+ $streditfile = get_string("edit", "", "<B>$file</B>");
+ $fileptr = fopen($basedir.$file, "r");
+ $contents = fread($fileptr, filesize($basedir.$file));
+ fclose($fileptr);
+
+ if (mimeinfo("type", $file) == "text/html") {
+ if ($usehtmleditor = can_use_richtext_editor()) {
+ $onsubmit = "onsubmit=\"copyrichtext(document.form.text);\"";
+ } else {
+ $onsubmit = "";
+ }
+ } else {
+ $usehtmleditor = false;
+ $onsubmit = "";
+ }
+
+ print_heading("$streditfile");
+
+ echo "<TABLE><TR><TD COLSPAN=2>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=\"post\" NAME=\"form\" $onsubmit>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=\"$wdir\">";
+ echo " <INPUT TYPE=hidden NAME=file VALUE=\"$file\">";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=edit>";
+ print_textarea($usehtmleditor, 25, 80, 680, 400, "text", $contents);
+ echo "</TD></TR><TR><TD>";
+ echo " <INPUT TYPE=submit VALUE=\"".get_string("savechanges")."\">";
+ echo "</FORM>";
+ echo "</TD><TD>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=cancel>";
+ echo " <INPUT TYPE=submit VALUE=\"".get_string("cancel")."\">";
+ echo "</FORM>";
+ echo "</TD></TR></TABLE>";
+
+ if ($usehtmleditor) {
+ print_richedit_javascript("form", "text", "yes");
+ }
+
+
+ }
+ html_footer();
+ break;
+
+ case "zip":
+ if (!empty($name)) {
+ html_header($course, $wdir);
+ $name = clean_filename($name);
+ if (empty($CFG->zip)) { // Use built-in php-based zip function
+ $files = array();
+ foreach ($USER->filelist as $file) {
+ $files[] = "$basedir/$file";
+ }
+ include_once('../pclzip/pclzip.lib.php');
+ $archive = new PclZip("$basedir/$wdir/$name");
+ if (($list = $archive->create($files,'',"$basedir/$wdir/")) == 0) {
+ error($archive->errorInfo(true));
+ }
+ } else { // Use external zip program
+ $files = "";
+ foreach ($USER->filelist as $file) {
+ $files .= basename($file);
+ $files .= " ";
+ }
+ $command = "cd $basedir/$wdir ; $CFG->zip -r $name $files";
+ Exec($command);
+ }
+ clearfilelist();
+ displaydir($wdir);
+
+ } else {
+ html_header($course, $wdir, "form.name");
+
+ if (setfilelist($_POST)) {
+ echo "<P ALIGN=CENTER>".get_string("youareabouttocreatezip").":</P>";
+ print_simple_box_start("center");
+ printfilelist($USER->filelist);
+ print_simple_box_end();
+ echo "<BR>";
+ echo "<P ALIGN=CENTER>".get_string("whattocallzip");
+ echo "<TABLE><TR><TD>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=post NAME=form>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=\"$wdir\">";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=zip>";
+ echo " <INPUT TYPE=text NAME=name SIZE=35 VALUE=\"new.zip\">";
+ echo " <INPUT TYPE=submit VALUE=\"".get_string("createziparchive")."\">";
+ echo "</FORM>";
+ echo "</TD><TD>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=cancel>";
+ echo " <INPUT TYPE=submit VALUE=\"".get_string("cancel")."\">";
+ echo "</FORM>";
+ echo "</TD></TR></TABLE>";
+ } else {
+ displaydir($wdir);
+ clearfilelist();
+ }
+ }
+ html_footer();
+ break;
+
+ case "unzip":
+ html_header($course, $wdir);
+ if (!empty($file)) {
+ $strname = get_string("name");
+ $strsize = get_string("size");
+ $strmodified = get_string("modified");
+ $strstatus = get_string("status");
+ $strok = get_string("ok");
+ $strunpacking = get_string("unpacking", "", $file);
+
+ echo "<P ALIGN=CENTER>$strunpacking:</P>";
+
+ $file = basename($file);
+
+ if (empty($CFG->unzip)) { // Use built-in php-based unzip function
+ include_once('../pclzip/pclzip.lib.php');
+ $archive = new PclZip("$basedir/$wdir/$file");
+ if (!$list = $archive->extract("$basedir/$wdir")) {
+ error($archive->errorInfo(true));
+ } else { // print some output
+ echo "<table cellpadding=\"4\" cellspacing=\"2\" border=\"0\" width=640>";
+ echo "<tr><th align=left>$strname</th>";
+ echo "<th align=right>$strsize</th>";
+ echo "<th align=right>$strmodified</th>";
+ echo "<th align=right>$strstatus</th></tr>";
+ foreach ($list as $item) {
+ echo "<tr>";
+ $item['filename'] = str_replace("$basedir/$wdir/", "", $item['filename']);
+ print_cell("left", $item['filename']);
+ if (! $item['folder']) {
+ print_cell("right", display_size($item['size']));
+ } else {
+ echo "<td> </td>";
+ }
+ $filedate = userdate($item['mtime'], get_string("strftimedatetime"));
+ print_cell("right", $filedate);
+ print_cell("right", $item['status']);
+ echo "</tr>";
+ }
+ echo "</table>";
+ }
+
+ } else { // Use external unzip program
+ print_simple_box_start("center");
+ echo "<PRE>";
+ $command = "cd $basedir/$wdir ; $CFG->unzip -o $file 2>&1";
+ passthru($command);
+ echo "</PRE>";
+ print_simple_box_end();
+ }
+
+ echo "<CENTER><FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=cancel>";
+ echo " <INPUT TYPE=submit VALUE=\"$strok\">";
+ echo "</FORM>";
+ echo "</CENTER>";
+ } else {
+ displaydir($wdir);
+ }
+ html_footer();
+ break;
+
+ case "listzip":
+ html_header($course, $wdir);
+ if (!empty($file)) {
+ $strname = get_string("name");
+ $strsize = get_string("size");
+ $strmodified = get_string("modified");
+ $strok = get_string("ok");
+ $strlistfiles = get_string("listfiles", "", $file);
+
+ echo "<P ALIGN=CENTER>$strlistfiles:</P>";
+ $file = basename($file);
+
+ include_once('../lib/pclzip/pclzip.lib.php');
+ $archive = new PclZip("$basedir/$wdir/$file");
+ if (!$list = $archive->listContent("$basedir/$wdir")) {
+ notify($archive->errorInfo(true));
+
+ } else {
+ echo "<table cellpadding=\"4\" cellspacing=\"2\" border=\"0\" width=640>";
+ echo "<tr><th align=left>$strname</th><th align=right>$strsize</th><th align=right>$strmodified</th></tr>";
+ foreach ($list as $item) {
+ echo "<tr>";
+ print_cell("left", $item['filename']);
+ if (! $item['folder']) {
+ print_cell("right", display_size($item['size']));
+ } else {
+ echo "<td> </td>";
+ }
+ $filedate = userdate($item['mtime'], get_string("strftimedatetime"));
+ print_cell("right", $filedate);
+ echo "</tr>";
+ }
+ echo "</table>";
+ }
+ echo "<br><center><form action=\"".$_SERVER['PHP_SELF']."\" method=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=$wdir>";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=cancel>";
+ echo " <INPUT TYPE=submit VALUE=\"$strok\">";
+ echo "</FORM>";
+ echo "</CENTER>";
+ } else {
+ displaydir($wdir);
+ }
+ html_footer();
+ break;
+
+ case "torte":
+ if($_POST)
+ {
+ while(list($key, $val) = each($_POST))
+ {
+ if(ereg("file([0-9]+)", $key, $regs))
+ {
+ $file = $val;
+ }
+ }
+ if(@filetype($CFG->dataroot ."/". $course->id . $file) == "file")
+ {
+ if(mimeinfo("icon", $file) == "image.gif")
+ {
+ $url = $CFG->wwwroot ."/file.php?file=/" .$course->id . $file;
+ runjavascript($url);
+ }
+ else
+ {
+ print "File is not a image!";
+ }
+ }
+ else
+ {
+ print "You cannot insert FOLDER into richtext editor!!!";
+ }
+ }
+ break;
+ case "cancel";
+ clearfilelist();
+
+ default:
+ html_header($course, $wdir);
+ displaydir($wdir);
+ html_footer();
+ break;
+}
+
+
+/// FILE FUNCTIONS ///////////////////////////////////////////////////////////
+
+
+function fulldelete($location) {
+ if (is_dir($location)) {
+ $currdir = opendir($location);
+ while ($file = readdir($currdir)) {
+ if ($file <> ".." && $file <> ".") {
+ $fullfile = $location."/".$file;
+ if (is_dir($fullfile)) {
+ if (!fulldelete($fullfile)) {
+ return false;
+ }
+ } else {
+ if (!unlink($fullfile)) {
+ return false;
+ }
+ }
+ }
+ }
+ closedir($currdir);
+ if (! rmdir($location)) {
+ return false;
+ }
+
+ } else {
+ if (!unlink($location)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+function setfilelist($VARS) {
+ global $USER;
+
+ $USER->filelist = array ();
+ $USER->fileop = "";
+
+ $count = 0;
+ foreach ($VARS as $key => $val) {
+ if (substr($key,0,4) == "file") {
+ $count++;
+ $USER->filelist[] = rawurldecode($val);
+ }
+ }
+ return $count;
+}
+
+function clearfilelist() {
+ global $USER;
+
+ $USER->filelist = array ();
+ $USER->fileop = "";
+}
+
+
+function printfilelist($filelist) {
+ global $basedir, $CFG;
+
+ foreach ($filelist as $file) {
+ if (is_dir($basedir.$file)) {
+ echo "<IMG SRC=\"$CFG->wwwroot/files/pix/folder.gif\" HEIGHT=16 WIDTH=16> $file<BR>";
+ $subfilelist = array();
+ $currdir = opendir($basedir.$file);
+ while ($subfile = readdir($currdir)) {
+ if ($subfile <> ".." && $subfile <> ".") {
+ $subfilelist[] = $file."/".$subfile;
+ }
+ }
+ printfilelist($subfilelist);
+
+ } else {
+ $icon = mimeinfo("icon", $file);
+ echo "<IMG SRC=\"$CFG->wwwroot/files/pix/$icon\" HEIGHT=16 WIDTH=16> $file<BR>";
+ }
+ }
+}
+
+
+function print_cell($alignment="center", $text=" ") {
+ echo "<TD ALIGN=\"$alignment\" NOWRAP>";
+ echo "<FONT SIZE=\"-1\" FACE=\"Arial, Helvetica\">";
+ echo "$text";
+ echo "</FONT>";
+ echo "</TD>\n";
+}
+
+function displaydir ($wdir) {
+// $wdir == / or /a or /a/b/c/d etc
+
+ global $basedir;
+ global $id;
+ global $USER, $CFG;
+
+ $fullpath = $basedir.$wdir;
+
+ $directory = opendir($fullpath); // Find all files
+ while ($file = readdir($directory)) {
+ if ($file == "." || $file == "..") {
+ continue;
+ }
+
+ if (is_dir($fullpath."/".$file)) {
+ $dirlist[] = $file;
+ } else {
+ $filelist[] = $file;
+ }
+ }
+ closedir($directory);
+
+ $strname = get_string("name");
+ $strsize = get_string("size");
+ $strmodified = get_string("modified");
+ $straction = get_string("action");
+ $strmakeafolder = get_string("makeafolder");
+ $struploadafile = get_string("uploadafile");
+ $strwithchosenfiles = get_string("withchosenfiles");
+ $strmovetoanotherfolder = get_string("movetoanotherfolder");
+ $strmovefilestohere = get_string("movefilestohere");
+ $strdeletecompletely = get_string("deletecompletely");
+ $strcreateziparchive = get_string("createziparchive");
+ $strrename = get_string("rename");
+ $stredit = get_string("edit");
+ $strunzip = get_string("unzip");
+ $strlist = get_string("list");
+ $strchoose = get_string("choose");
+
+
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=post NAME=dirform>";
+ echo "<TABLE BORDER=0 cellspacing=2 cellpadding=2 width=640>";
+ echo "<TR>";
+ echo "<TH WIDTH=5></TH>";
+ echo "<TH ALIGN=left>$strname</TH>";
+ echo "<TH ALIGN=right>$strsize</TH>";
+ echo "<TH ALIGN=right>$strmodified</TH>";
+ echo "<TH ALIGN=right>$straction</TH>";
+ echo "</TR>\n";
+
+ if ($wdir == "/") {
+ $wdir = "";
+ }
+
+ $count = 0;
+
+ if (!empty($dirlist)) {
+ asort($dirlist);
+ foreach ($dirlist as $dir) {
+
+ $count++;
+
+ $filename = $fullpath."/".$dir;
+ $fileurl = rawurlencode($wdir."/".$dir);
+ $filesafe = rawurlencode($dir);
+ $filedate = userdate(filectime($filename), "%d %b %Y, %I:%M %p");
+
+ echo "<TR>";
+
+ print_cell("center", "<INPUT TYPE=checkbox NAME=\"file$count\" VALUE=\"$fileurl\">");
+ print_cell("left", "<A HREF=\"".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$fileurl\"><IMG SRC=\"$CFG->wwwroot/files/pix/folder.gif\" HEIGHT=16 WIDTH=16 BORDER=0 ALT=\"Folder\"></A> <A HREF=\"".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$fileurl\">".htmlspecialchars($dir)."</A>");
+ print_cell("right", "-");
+ print_cell("right", $filedate);
+ print_cell("right", "<A HREF=\"".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&file=$filesafe&action=rename\">$strrename</A>");
+
+ echo "</TR>";
+ }
+ }
+
+
+ if (!empty($filelist)) {
+ asort($filelist);
+ foreach ($filelist as $file) {
+
+ $icon = mimeinfo("icon", $file);
+
+ $count++;
+ $filename = $fullpath."/".$file;
+ $fileurl = "$wdir/$file";
+ $filesafe = rawurlencode($file);
+ $fileurlsafe = rawurlencode($fileurl);
+ $filedate = userdate(filectime($filename), "%d %b %Y, %I:%M %p");
+
+ echo "<tr>";
+
+ print_cell("center", "<INPUT TYPE=\"checkbox\" NAME=\"file$count\" VALUE=\"$fileurl\">");
+ echo "<td align=left nowrap>";
+ if ($CFG->slasharguments) {
+ $ffurl = "/file.php/$id$fileurl";
+ } else {
+ $ffurl = "/file.php?file=/$id$fileurl";
+ }
+ link_to_popup_window ($ffurl, "display",
+ "<IMG SRC=\"$CFG->wwwroot/files/pix/$icon\" HEIGHT=16 WIDTH=16 BORDER=0 ALT=\"File\">",
+ 480, 640);
+ echo "<font size=\"-1\" face=\"Arial, Helvetica\">";
+ link_to_popup_window ($ffurl, "display",
+ htmlspecialchars($file),
+ 480, 640);
+ echo "</font></td>";
+
+ $file_size = filesize($filename);
+ print_cell("right", display_size($file_size));
+ print_cell("right", $filedate);
+
+ if ($icon == "text.gif" || $icon == "html.gif") {
+ $edittext = "<a href=\"".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&file=$fileurl&action=edit\">$stredit</a>";
+ } else if ($icon == "zip.gif") {
+ $edittext = "<a href=\"".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&file=$fileurl&action=unzip\">$strunzip</a> ";
+ $edittext .= "<a href=\"".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&file=$fileurl&action=listzip\">$strlist</a> ";
+ } else if ($icon == "image.gif") {
+ $edittext = "<b><a onMouseDown=\"return set_value('$CFG->wwwroot$ffurl')\" href=\"\">$strchoose</a></b>";
+ } else {
+ $edittext = "";
+ }
+ print_cell("right", "$edittext <A HREF=\"".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&file=$filesafe&action=rename\">$strrename</A>");
+
+ echo "</TR>";
+ }
+ }
+ echo "</TABLE>";
+ echo "<hr width=640 align=center noshade size=1>";
+
+ if (empty($wdir)) {
+ $wdir = "/";
+ }
+
+ echo "<TABLE BORDER=0 cellspacing=2 cellpadding=2 width=640>";
+ echo "<TR><TD>";
+ echo "<INPUT TYPE=hidden NAME=id VALUE=\"$id\">";
+ echo "<INPUT TYPE=hidden NAME=wdir VALUE=\"$wdir\"> ";
+ $options = array (
+ "move" => "$strmovetoanotherfolder",
+ "delete" => "$strdeletecompletely",
+ "zip" => "$strcreateziparchive"
+ );
+ if (!empty($count)) {
+ choose_from_menu ($options, "action", "", "$strwithchosenfiles...", "javascript:document.dirform.submit()");
+ }
+
+ echo "</FORM>";
+ echo "<TD ALIGN=center>";
+ if (!empty($USER->fileop) and ($USER->fileop == "move") and ($USER->filesource <> $wdir)) {
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=\"$wdir\">";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=paste>";
+ echo " <INPUT TYPE=submit VALUE=\"$strmovefilestohere\">";
+ echo "</FORM>";
+ }
+ echo "<TD ALIGN=right>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=\"$wdir\">";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=mkdir>";
+ echo " <INPUT TYPE=submit VALUE=\"$strmakeafolder\">";
+ echo "</FORM>";
+ echo "</TD>";
+ echo "<TD ALIGN=right>";
+ echo "<FORM ACTION=\"".$_SERVER['PHP_SELF']."\" METHOD=get>";
+ echo " <INPUT TYPE=hidden NAME=id VALUE=$id>";
+ echo " <INPUT TYPE=hidden NAME=wdir VALUE=\"$wdir\">";
+ echo " <INPUT TYPE=hidden NAME=action VALUE=upload>";
+ echo " <INPUT TYPE=submit VALUE=\"$struploadafile\">";
+ echo "</FORM>";
+ echo "</TD></TR>";
+ echo "</TABLE>";
+ echo "<HR WIDTH=640 ALIGN=CENTER NOSHADE SIZE=1>";
+
+}
+
+?>
--- /dev/null
+// Though "Dialog" looks like an object, it isn't really an object. Instead
+// it's just namespace for protecting global symbols.
+
+function Dialog(url, action, init) {
+ if (typeof init == "undefined") {
+ init = window; // pass this window object by default
+ }
+ if (document.all) { // here we hope that Mozilla will never support document.all
+ var value =
+ showModalDialog(url, init,
+ //window.open(url, '_blank',
+ "resizable: no; help: no; status: no; scroll: no");
+ if (action) {
+ action(value);
+ }
+ } else {
+ return Dialog._geckoOpenModal(url, action, init);
+ }
+};
+
+Dialog._parentEvent = function(ev) {
+ if (Dialog._modal && !Dialog._modal.closed) {
+ Dialog._modal.focus();
+ // we get here in Mozilla only, anyway, so we can safely use
+ // the DOM version.
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+};
+
+// should be a function, the return handler of the currently opened dialog.
+Dialog._return = null;
+
+// constant, the currently opened dialog
+Dialog._modal = null;
+
+// the dialog will read it's args from this variable
+Dialog._arguments = null;
+
+Dialog._geckoOpenModal = function(url, action, init) {
+ var dlg = window.open(url, "ha_dialog",
+ "toolbar=no,menubar=no,personalbar=no,width=10,height=10," +
+ "scrollbars=no,resizable=no");
+ Dialog._modal = dlg;
+ Dialog._arguments = init;
+
+ // capture some window's events
+ function capwin(w) {
+ w.addEventListener("click", Dialog._parentEvent, true);
+ w.addEventListener("mousedown", Dialog._parentEvent, true);
+ w.addEventListener("focus", Dialog._parentEvent, true);
+ };
+ // release the captured events
+ function relwin(w) {
+ w.removeEventListener("focus", Dialog._parentEvent, true);
+ w.removeEventListener("mousedown", Dialog._parentEvent, true);
+ w.removeEventListener("click", Dialog._parentEvent, true);
+ };
+ capwin(window);
+ // capture other frames
+ //for (var i = 0; i < window.frames.length; capwin(window.frames[i++]));
+ // make up a function to be called when the Dialog ends.
+ Dialog._return = function (val) {
+ if (val && action) {
+ action(val);
+ }
+ relwin(window);
+ // capture other frames
+ //for (var i = 0; i < window.frames.length; relwin(window.frames[i++]));
+ Dialog._modal = null;
+ };
+};
--- /dev/null
+.htmlarea { background: #fff; }
+
+.htmlarea .toolbar {
+ cursor: default;
+ background: ButtonFace;
+ padding: 1px 1px 2px 1px;
+ border: 1px solid;
+ border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+.htmlarea .toolbar table { font-family: tahoma,verdana,sans-serif; font-size: 11px; }
+.htmlarea .toolbar img { border: none; }
+.htmlarea .toolbar .label { padding: 0px 3px; }
+
+.htmlarea .toolbar .button {
+ background: ButtonFace;
+ color: ButtonText;
+ border: 1px solid ButtonFace;
+ padding: 1px;
+ margin: 0px;
+}
+.htmlarea .toolbar .buttonHover {
+ border: 1px solid;
+ border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+.htmlarea .toolbar .buttonActive, .htmlarea .toolbar .buttonPressed {
+ padding: 2px 0px 0px 2px;
+ border: 1px solid;
+ border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+}
+.htmlarea .toolbar .buttonPressed {
+ background: ButtonHighlight;
+}
+.htmlarea .toolbar .indicator {
+ padding: 0px 3px;
+ overflow: hidden;
+ width: 20px;
+ text-align: center;
+ cursor: default;
+ border: 1px solid ButtonShadow;
+}
+
+.htmlarea .toolbar .buttonDisabled { background-color: #aaa; }
+
+.htmlarea .toolbar .buttonDisabled img {
+ filter: alpha(opacity = 25);
+ -moz-opacity: 25%;
+}
+
+.htmlarea .toolbar .separator {
+ position: relative;
+ margin: 3px;
+ border-left: 1px solid ButtonShadow;
+ border-right: 1px solid ButtonHighlight;
+ width: 0px;
+ height: 16px;
+ padding: 0px;
+}
+
+.htmlarea .toolbar .space { width: 5px; }
+
+.htmlarea .toolbar select { font: 11px Tahoma,Verdana,sans-serif; }
+
+.htmlarea .toolbar select,
+.htmlarea .toolbar select:hover,
+.htmlarea .toolbar select:active { background: FieldFace; color: ButtonText; }
+
+.htmlarea .statusBar {
+ border: 1px solid;
+ border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+ padding: 2px 4px;
+ background-color: ButtonFace;
+ color: ButtonText;
+ font: 11px Tahoma,Verdana,sans-serif;
+}
+
+.htmlarea .statusBar .statusBarTree a {
+ padding: 2px 5px;
+ color: #00f;
+}
+
+.htmlarea .statusBar .statusBarTree a:visited { color: #00f; }
+.htmlarea .statusBar .statusBarTree a:hover {
+ background-color: Highlight;
+ color: HighlightText;
+ padding: 1px 4px;
+ border: 1px solid HighlightText;
+}
+
+
+/* Hidden DIV popup dialogs (PopupDiv) */
+
+.dialog {
+ color: ButtonText;
+ background: ButtonFace;
+}
+
+.dialog .content { padding: 2px; }
+
+.dialog, .dialog button, .dialog input, .dialog select, .dialog textarea, .dialog table {
+ font: 11px Tahoma,Verdana,sans-serif;
+}
+
+.dialog table { border-collapse: collapse; }
+
+.dialog .title {
+ background: #008;
+ color: #ff8;
+ border-bottom: 1px solid #000;
+ padding: 1px 0px 2px 5px;
+ font-size: 12px;
+ font-weight: bold;
+ cursor: default;
+}
+
+.dialog .title .button {
+ float: right;
+ border: 1px solid #66a;
+ padding: 0px 1px 0px 2px;
+ margin-right: 1px;
+ color: #fff;
+ text-align: center;
+}
+
+.dialog .title .button-hilite { border-color: #88f; background: #44c; }
+
+.dialog button {
+ width: 5em;
+ padding: 0px;
+}
+
+.dialog .buttonColor {
+ padding: 1px;
+ cursor: default;
+ border: 1px solid;
+ border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+
+.dialog .buttonColor-hilite {
+ border-color: #000;
+}
+
+.dialog .buttonColor .chooser, .dialog .buttonColor .nocolor {
+ height: 0.6em;
+ border: 1px solid;
+ padding: 0px 1em;
+ border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+}
+
+.dialog .buttonColor .nocolor { padding: 0px; }
+.dialog .buttonColor .nocolor-hilite { background-color: #fff; color: #f00; }
+
+.dialog .label { text-align: right; width: 6em; }
+.dialog .value input { width: 100%; }
+.dialog .buttons { text-align: right; padding: 2px 4px 0px 4px; }
+
+.dialog legend { font-weight: bold; }
+.dialog fieldset table { margin: 2px 0px; }
+
+.popupdiv {
+ border: 2px solid;
+ border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+
+.popupwin {
+ padding: 0px;
+ margin: 0px;
+}
+
+.popupwin .title {
+ background: #fff;
+ color: #000;
+ font-weight: bold;
+ font-size: 120%;
+ padding: 3px 10px;
+ margin-bottom: 10px;
+ border-bottom: 1px solid black;
+ letter-spacing: 2px;
+}
+
+form { margin: 0px; border: none; }
--- /dev/null
+//
+// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+// This copyright notice MUST stay intact for use (see license.txt).
+//
+// A free WYSIWYG editor replacement for <textarea> fields.
+// For full source code and docs, visit http://www.interactivetools.com/
+//
+// Version 3.0 developed by Mihai Bazon for InteractiveTools.
+// http://dynarch.com/mishoo
+//
+// $Id$
+
+// Creates a new HTMLArea object. Tries to replace the textarea with the given
+// ID with it.
+function HTMLArea(textarea, config) {
+ if (HTMLArea.checkSupportedBrowser()) {
+ if (typeof config == "undefined") {
+ this.config = new HTMLArea.Config();
+ } else {
+ this.config = config;
+ }
+ this._htmlArea = null;
+ this._textArea = textarea;
+ this._editMode = "wysiwyg";
+ this.plugins = {};
+ this._timerToolbar = null;
+ this._timerUndo = null;
+ this._undoQueue = new Array(this.config.undoSteps);
+ this._undoPos = -1;
+ this._mdoc = document; // cache the document, we need it in plugins
+ this.doctype = '';
+ }
+};
+
+// cache some regexps
+HTMLArea.RE_tagName = /(<\/|<)\s*([^ \t\n>]+)/ig;
+HTMLArea.RE_doctype = /(<!doctype((.|\n)*?)>)\n?/i;
+HTMLArea.RE_head = /<head>((.|\n)*?)<\/head>/i;
+HTMLArea.RE_body = /<body>((.|\n)*?)<\/body>/i;
+
+HTMLArea.Config = function () {
+ this.version = "3.0";
+
+ this.width = "auto";
+ this.height = "auto";
+
+ // enable creation of a status bar?
+ this.statusBar = true;
+
+ // maximum size of the undo queue
+ this.undoSteps = 20;
+
+ // the time interval at which undo samples are taken
+ this.undoTimeout = 500; // 1/2 sec.
+
+ // the next parameter specifies whether the toolbar should be included
+ // in the size or not.
+ this.sizeIncludesToolbar = true;
+
+ // if true then HTMLArea will retrieve the full HTML, starting with the
+ // <HTML> tag.
+ this.fullPage = false;
+
+ // style included in the iframe document
+ this.pageStyle = "body { background-color: #fff; font-family: verdana,sans-serif; }";
+ if (typeof _editor_url != "undefined") {
+ this.editorURL = _editor_url;
+ } else {
+ this.editorURL = "";
+ }
+
+ // URL-s
+ this.imgURL = "images/";
+ this.popupURL = "popups/";
+
+ /** CUSTOMIZING THE TOOLBAR
+ * -------------------------
+ *
+ * It is recommended that you customize the toolbar contents in an
+ * external file (i.e. the one calling HTMLArea) and leave this one
+ * unchanged. That's because when we (InteractiveTools.com) release a
+ * new official version, it's less likely that you will have problems
+ * upgrading HTMLArea.
+ */
+ this.toolbar = [
+ [ "fontname", "space",
+ "fontsize", "space",
+ "formatblock", "space",
+ "bold", "italic", "underline", "strikethrough", "separator",
+ "subscript", "superscript", "separator",
+ "copy", "cut", "paste", "space", "undo", "redo" ],
+
+ [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",
+ "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator",
+ "forecolor", "hilitecolor", "separator",
+ "inserthorizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator",
+ "popupeditor", "separator", "showhelp", "about" ]
+ ];
+
+ this.fontname = {
+ "Arial": 'arial,helvetica,sans-serif',
+ "Courier New": 'courier new,courier,monospace',
+ "Georgia": 'georgia,times new roman,times,serif',
+ "Tahoma": 'tahoma,arial,helvetica,sans-serif',
+ "Times New Roman": 'times new roman,times,serif',
+ "Verdana": 'verdana,arial,helvetica,sans-serif',
+ "impact": 'impact',
+ "WingDings": 'wingdings'
+ };
+
+ this.fontsize = {
+ "1 (8 pt)": "1",
+ "2 (10 pt)": "2",
+ "3 (12 pt)": "3",
+ "4 (14 pt)": "4",
+ "5 (18 pt)": "5",
+ "6 (24 pt)": "6",
+ "7 (36 pt)": "7"
+ };
+
+ this.formatblock = {
+ "Heading 1": "h1",
+ "Heading 2": "h2",
+ "Heading 3": "h3",
+ "Heading 4": "h4",
+ "Heading 5": "h5",
+ "Heading 6": "h6",
+ "Normal": "p",
+ "Address": "address",
+ "Formatted": "pre"
+ };
+
+ this.customSelects = {};
+
+ function cut_copy_paste(e, cmd, obj) {
+ try {
+ e.execCommand(cmd);
+ } catch (e) {
+ if (HTMLArea.is_gecko) {
+ alert("Some revisions of Mozilla/Gecko do not support programatic " +
+ "access to cut/copy/paste functions, for security reasons. " +
+ "Your browser is one of them. Please use the standard key combinations:\n" +
+ "CTRL-X for cut, CTRL-C for copy, CTRL-V for paste.");
+ obj.element.style.display = "none";
+ }
+ }
+ };
+
+ // ADDING CUSTOM BUTTONS: please read below!
+ // format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]"
+ // - ID: unique ID for the button. If the button calls document.execCommand
+ // it's wise to give it the same name as the called command.
+ // - ACTION: function that gets called when the button is clicked.
+ // it has the following prototype:
+ // function(editor, buttonName)
+ // - editor is the HTMLArea object that triggered the call
+ // - buttonName is the ID of the clicked button
+ // These 2 parameters makes it possible for you to use the same
+ // handler for more HTMLArea objects or for more different buttons.
+ // - ToolTip: default tooltip, for cases when it is not defined in the -lang- file (HTMLArea.I18N)
+ // - Icon: path to an icon image file for the button (TODO: use one image for all buttons!)
+ // - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.
+ this.btnList = {
+ bold: [ "Bold", "ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ],
+ italic: [ "Italic", "ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ],
+ underline: [ "Underline", "ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ],
+ strikethrough: [ "Strikethrough", "ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ],
+ subscript: [ "Subscript", "ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ],
+ superscript: [ "Superscript", "ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ],
+ justifyleft: [ "Justify Left", "ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ],
+ justifycenter: [ "Justify Center", "ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ],
+ justifyright: [ "Justify Right", "ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ],
+ justifyfull: [ "Justify Full", "ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ],
+ insertorderedlist: [ "Ordered List", "ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ],
+ insertunorderedlist: [ "Bulleted List", "ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ],
+ outdent: [ "Decrease Indent", "ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ],
+ indent: [ "Increase Indent", "ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ],
+ forecolor: [ "Font Color", "ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ],
+ hilitecolor: [ "Background Color", "ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ],
+ inserthorizontalrule: [ "Horizontal Rule", "ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ],
+ createlink: [ "Insert Web Link", "ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ],
+ insertimage: [ "Insert Image", "ed_image.gif", false, function(e) {e.execCommand("insertimage");} ],
+ inserttable: [ "Insert Table", "insert_table.gif", false, function(e) {e.execCommand("inserttable");} ],
+ htmlmode: [ "Toggle HTML Source", "ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ],
+ popupeditor: [ "Enlarge Editor", "fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ],
+ about: [ "About this editor", "ed_about.gif", true, function(e) {e.execCommand("about");} ],
+ showhelp: [ "Help using editor", "ed_help.gif", true, function(e) {e.execCommand("showhelp");} ],
+ undo: [ "Undoes your last action", "ed_undo.gif", false, function(e) {e.execCommand("undo");} ],
+ redo: [ "Redoes your last action", "ed_redo.gif", false, function(e) {e.execCommand("redo");} ],
+ cut: [ "Cut selection", "ed_cut.gif", false, cut_copy_paste ],
+ copy: [ "Copy selection", "ed_copy.gif", false, cut_copy_paste ],
+ paste: [ "Paste from clipboard", "ed_paste.gif", false, cut_copy_paste ]
+ };
+ /* ADDING CUSTOM BUTTONS
+ * ---------------------
+ *
+ * It is recommended that you add the custom buttons in an external
+ * file and leave this one unchanged. That's because when we
+ * (InteractiveTools.com) release a new official version, it's less
+ * likely that you will have problems upgrading HTMLArea.
+ *
+ * Example on how to add a custom button when you construct the HTMLArea:
+ *
+ * var editor = new HTMLArea("your_text_area_id");
+ * var cfg = editor.config; // this is the default configuration
+ * cfg.btnList["my-hilite"] =
+ * [ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action
+ * "Highlight selection", // tooltip
+ * "my_hilite.gif", // image
+ * false // disabled in text mode
+ * ];
+ * cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar
+ *
+ * An alternate (also more convenient and recommended) way to
+ * accomplish this is to use the registerButton function below.
+ */
+ // initialize tooltips from the I18N module and generate correct image path
+ for (var i in this.btnList) {
+ var btn = this.btnList[i];
+ btn[1] = this.editorURL + this.imgURL + btn[1];
+ if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {
+ btn[0] = HTMLArea.I18N.tooltips[i];
+ }
+ }
+};
+
+/** Helper function: register a new button with the configuration. It can be
+ * called with all 5 arguments, or with only one (first one). When called with
+ * only one argument it must be an object with the following properties: id,
+ * tooltip, image, textMode, action. Examples:
+ *
+ * 1. config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...});
+ * 2. config.registerButton({
+ * id : "my-hilite", // the ID of your button
+ * tooltip : "Hilite text", // the tooltip
+ * image : "my-hilite.gif", // image to be displayed in the toolbar
+ * textMode : false, // disabled in text mode
+ * action : function(editor) { // called when the button is clicked
+ * editor.surroundHTML('<span class="hilite">', '</span>');
+ * },
+ * context : "p" // will be disabled if outside a <p> element
+ * });
+ */
+HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) {
+ var the_id;
+ if (typeof id == "string") {
+ the_id = id;
+ } else if (typeof id == "object") {
+ the_id = id.id;
+ } else {
+ alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments");
+ return false;
+ }
+ // check for existing id
+ if (typeof this.customSelects[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ switch (typeof id) {
+ case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break;
+ case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break;
+ }
+};
+
+/** The following helper function registers a dropdown box with the editor
+ * configuration. You still have to add it to the toolbar, same as with the
+ * buttons. Call it like this:
+ *
+ * FIXME: add example
+ */
+HTMLArea.Config.prototype.registerDropdown = function(object) {
+ // check for existing id
+ if (typeof this.customSelects[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ this.customSelects[object.id] = object;
+};
+
+/** Call this function to remove some buttons/drop-down boxes from the toolbar.
+ * Pass as the only parameter a string containing button/drop-down names
+ * delimited by spaces. Note that the string should also begin with a space
+ * and end with a space. Example:
+ *
+ * config.hideSomeButtons(" fontname fontsize textindicator ");
+ *
+ * It's useful because it's easier to remove stuff from the defaul toolbar than
+ * create a brand new toolbar ;-)
+ */
+HTMLArea.Config.prototype.hideSomeButtons = function(remove) {
+ var toolbar = this.toolbar;
+ for (var i in toolbar) {
+ var line = toolbar[i];
+ for (var j = line.length; --j >= 0; ) {
+ if (remove.indexOf(" " + line[j] + " ") >= 0) {
+ var len = 1;
+ if (/separator|space/.test(line[j + 1])) {
+ len = 2;
+ }
+ line.splice(j, len);
+ }
+ }
+ }
+};
+
+/** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */
+HTMLArea.replaceAll = function(config) {
+ var tas = document.getElementsByTagName("textarea");
+ for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate());
+};
+
+/** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */
+HTMLArea.replace = function(id, config) {
+ var ta = document.getElementById(id);
+ return ta ? (new HTMLArea(ta, config)).generate() : null;
+};
+
+// Creates the toolbar and appends it to the _htmlarea
+HTMLArea.prototype._createToolbar = function () {
+ var editor = this; // to access this in nested functions
+
+ var toolbar = document.createElement("div");
+ this._toolbar = toolbar;
+ toolbar.className = "toolbar";
+ toolbar.unselectable = "1";
+ var tb_row = null;
+ var tb_objects = new Object();
+ this._toolbarObjects = tb_objects;
+
+ // creates a new line in the toolbar
+ function newLine() {
+ var table = document.createElement("table");
+ table.border = "0px";
+ table.cellSpacing = "0px";
+ table.cellPadding = "0px";
+ toolbar.appendChild(table);
+ // TBODY is required for IE, otherwise you don't see anything
+ // in the TABLE.
+ var tb_body = document.createElement("tbody");
+ table.appendChild(tb_body);
+ tb_row = document.createElement("tr");
+ tb_body.appendChild(tb_row);
+ }; // END of function: newLine
+ // init first line
+ newLine();
+
+ // updates the state of a toolbar element. This function is member of
+ // a toolbar element object (unnamed objects created by createButton or
+ // createSelect functions below).
+ function setButtonStatus(id, newval) {
+ var oldval = this[id];
+ var el = this.element;
+ if (oldval != newval) {
+ switch (id) {
+ case "enabled":
+ if (newval) {
+ HTMLArea._removeClass(el, "buttonDisabled");
+ el.disabled = false;
+ } else {
+ HTMLArea._addClass(el, "buttonDisabled");
+ el.disabled = true;
+ }
+ break;
+ case "active":
+ if (newval) {
+ HTMLArea._addClass(el, "buttonPressed");
+ } else {
+ HTMLArea._removeClass(el, "buttonPressed");
+ }
+ break;
+ }
+ this[id] = newval;
+ }
+ }; // END of function: setButtonStatus
+
+ // this function will handle creation of combo boxes. Receives as
+ // parameter the name of a button as defined in the toolBar config.
+ // This function is called from createButton, above, if the given "txt"
+ // doesn't match a button.
+ function createSelect(txt) {
+ var options = null;
+ var el = null;
+ var cmd = null;
+ var customSelects = editor.config.customSelects;
+ var context = null;
+ switch (txt) {
+ case "fontsize":
+ case "fontname":
+ case "formatblock":
+ // the following line retrieves the correct
+ // configuration option because the variable name
+ // inside the Config object is named the same as the
+ // button/select in the toolbar. For instance, if txt
+ // == "formatblock" we retrieve config.formatblock (or
+ // a different way to write it in JS is
+ // config["formatblock"].
+ options = editor.config[txt];
+ cmd = txt;
+ break;
+ default:
+ // try to fetch it from the list of registered selects
+ cmd = txt;
+ var dropdown = customSelects[cmd];
+ if (typeof dropdown != "undefined") {
+ options = dropdown.options;
+ context = dropdown.context;
+ } else {
+ alert("ERROR [createSelect]:\nCan't find the requested dropdown definition");
+ }
+ break;
+ }
+ if (options) {
+ el = document.createElement("select");
+ var obj = {
+ name : txt, // field name
+ element : el, // the UI element (SELECT)
+ enabled : true, // is it enabled?
+ text : false, // enabled in text mode?
+ cmd : cmd, // command ID
+ state : setButtonStatus, // for changing state
+ context : context
+ };
+ tb_objects[txt] = obj;
+ for (var i in options) {
+ var op = document.createElement("option");
+ op.appendChild(document.createTextNode(i));
+ op.value = options[i];
+ el.appendChild(op);
+ }
+ HTMLArea._addEvent(el, "change", function () {
+ editor._comboSelected(el, txt);
+ });
+ }
+ return el;
+ }; // END of function: createSelect
+
+ // appends a new button to toolbar
+ function createButton(txt) {
+ // the element that will be created
+ var el = null;
+ var btn = null;
+ switch (txt) {
+ case "separator":
+ el = document.createElement("div");
+ el.className = "separator";
+ break;
+ case "space":
+ el = document.createElement("div");
+ el.className = "space";
+ break;
+ case "linebreak":
+ newLine();
+ return false;
+ case "textindicator":
+ el = document.createElement("div");
+ el.appendChild(document.createTextNode("A"));
+ el.className = "indicator";
+ el.title = HTMLArea.I18N.tooltips.textindicator;
+ var obj = {
+ name : txt, // the button name (i.e. 'bold')
+ element : el, // the UI element (DIV)
+ enabled : true, // is it enabled?
+ active : false, // is it pressed?
+ text : false, // enabled in text mode?
+ cmd : "textindicator", // the command ID
+ state : setButtonStatus // for changing state
+ };
+ tb_objects[txt] = obj;
+ break;
+ default:
+ btn = editor.config.btnList[txt];
+ }
+ if (!el && btn) {
+ el = document.createElement("div");
+ el.title = btn[0];
+ el.className = "button";
+ // let's just pretend we have a button object, and
+ // assign all the needed information to it.
+ var obj = {
+ name : txt, // the button name (i.e. 'bold')
+ element : el, // the UI element (DIV)
+ enabled : true, // is it enabled?
+ active : false, // is it pressed?
+ text : btn[2], // enabled in text mode?
+ cmd : btn[3], // the command ID
+ state : setButtonStatus, // for changing state
+ context : btn[4] || null // enabled in a certain context?
+ };
+ tb_objects[txt] = obj;
+ // handlers to emulate nice flat toolbar buttons
+ HTMLArea._addEvent(el, "mouseover", function () {
+ if (obj.enabled) {
+ HTMLArea._addClass(el, "buttonHover");
+ }
+ });
+ HTMLArea._addEvent(el, "mouseout", function () {
+ if (obj.enabled) with (HTMLArea) {
+ _removeClass(el, "buttonHover");
+ _removeClass(el, "buttonActive");
+ (obj.active) && _addClass(el, "buttonPressed");
+ }
+ });
+ HTMLArea._addEvent(el, "mousedown", function (ev) {
+ if (obj.enabled) with (HTMLArea) {
+ _addClass(el, "buttonActive");
+ _removeClass(el, "buttonPressed");
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ // when clicked, do the following:
+ HTMLArea._addEvent(el, "click", function (ev) {
+ if (obj.enabled) with (HTMLArea) {
+ _removeClass(el, "buttonActive");
+ _removeClass(el, "buttonHover");
+ obj.cmd(editor, obj.name, obj);
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ var img = document.createElement("img");
+ img.src = btn[1];
+ img.style.width = "18px";
+ img.style.height = "18px";
+ el.appendChild(img);
+ } else if (!el) {
+ el = createSelect(txt);
+ }
+ if (el) {
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.appendChild(el);
+ } else {
+ alert("FIXME: Unknown toolbar item: " + txt);
+ }
+ return el;
+ };
+
+ var first = true;
+ for (var i in this.config.toolbar) {
+ if (!first) {
+ createButton("linebreak");
+ } else {
+ first = false;
+ }
+ var group = this.config.toolbar[i];
+ for (var j in group) {
+ var code = group[j];
+ if (/^([IT])\[(.*?)\]/.test(code)) {
+ // special case, create text label
+ var l7ed = RegExp.$1 == "I"; // localized?
+ var label = RegExp.$2;
+ if (l7ed) {
+ label = HTMLArea.I18N.custom[label];
+ }
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.className = "label";
+ tb_cell.innerHTML = label;
+ } else {
+ createButton(code);
+ }
+ }
+ }
+
+ this._htmlArea.appendChild(toolbar);
+};
+
+HTMLArea.prototype._createStatusBar = function() {
+ var statusbar = document.createElement("div");
+ statusbar.className = "statusBar";
+ this._htmlArea.appendChild(statusbar);
+ this._statusBar = statusbar;
+ statusbar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ // creates a holder for the path view
+ div = document.createElement("span");
+ div.className = "statusBarTree";
+ this._statusBarTree = div;
+ this._statusBar.appendChild(div);
+ if (!this.config.statusBar) {
+ // disable it...
+ statusbar.style.display = "none";
+ }
+};
+
+// Creates the HTMLArea object and replaces the textarea with it.
+HTMLArea.prototype.generate = function () {
+ var editor = this; // we'll need "this" in some nested functions
+ // get the textarea
+ var textarea = this._textArea;
+ if (typeof textarea == "string") {
+ // it's not element but ID
+ this._textArea = textarea = document.getElementById(textarea);
+ }
+ this._ta_size = {
+ w: textarea.offsetWidth,
+ h: textarea.offsetHeight
+ };
+ textarea.style.display = "none";
+
+ // create the editor framework
+ var htmlarea = document.createElement("div");
+ htmlarea.className = "htmlarea";
+ this._htmlArea = htmlarea;
+
+ // insert the editor before the textarea.
+ textarea.parentNode.insertBefore(htmlarea, textarea);
+
+ if (textarea.form) {
+ // we have a form, on submit get the HTMLArea content and
+ // update original textarea.
+ var f = textarea.form;
+ if (typeof f.onsubmit == "function") {
+ var funcref = f.onsubmit;
+ if (typeof f.__msh_prevOnSubmit == "undefined") {
+ f.__msh_prevOnSubmit = [];
+ }
+ f.__msh_prevOnSubmit.push(funcref);
+ }
+ f.onsubmit = function() {
+ editor._textArea.value = editor.getHTML();
+ var a = this.__msh_prevOnSubmit;
+ // call previous submit methods if they were there.
+ if (typeof a != "undefined") {
+ for (var i in a) {
+ a[i]();
+ }
+ }
+ };
+ }
+
+ // add a handler for the "back/forward" case -- on body.unload we save
+ // the HTML content into the original textarea.
+ window.onunload = function() {
+ editor._textArea.value = editor.getHTML();
+ };
+
+ // creates & appends the toolbar
+ this._createToolbar();
+
+ // create the IFRAME
+ var iframe = document.createElement("iframe");
+ htmlarea.appendChild(iframe);
+
+ this._iframe = iframe;
+
+ // creates & appends the status bar, if the case
+ this._createStatusBar();
+
+ // remove the default border as it keeps us from computing correctly
+ // the sizes. (somebody tell me why doesn't this work in IE)
+
+ if (!HTMLArea.is_ie) {
+ iframe.style.borderWidth = "1px";
+ // iframe.frameBorder = "1";
+ // iframe.marginHeight = "0";
+ // iframe.marginWidth = "0";
+ }
+
+ // size the IFRAME according to user's prefs or initial textarea
+ var height = (this.config.height == "auto" ? (this._ta_size.h + "px") : this.config.height);
+ height = parseInt(height);
+ var width = (this.config.width == "auto" ? (this._ta_size.w + "px") : this.config.width);
+ width = parseInt(width);
+
+ if (!HTMLArea.is_ie) {
+ height -= 2;
+ width -= 2;
+ }
+
+ iframe.style.width = width + "px";
+ if (this.config.sizeIncludesToolbar) {
+ // substract toolbar height
+ height -= this._toolbar.offsetHeight;
+ height -= this._statusBar.offsetHeight;
+ }
+ if (height < 0) {
+ height = 0;
+ }
+ iframe.style.height = height + "px";
+
+ // the editor including the toolbar now have the same size as the
+ // original textarea.. which means that we need to reduce that a bit.
+ textarea.style.width = iframe.style.width;
+ textarea.style.height = iframe.style.height;
+
+ // IMPORTANT: we have to allow Mozilla a short time to recognize the
+ // new frame. Otherwise we get a stupid exception.
+ function initIframe() {
+ var doc = editor._iframe.contentWindow.document;
+ if (!doc) {
+ // Try again..
+ // FIXME: don't know what else to do here. Normally
+ // we'll never reach this point.
+ if (HTMLArea.is_gecko) {
+ setTimeout(initIframe, 10);
+ return false;
+ } else {
+ alert("ERROR: IFRAME can't be initialized.");
+ }
+ }
+ if (HTMLArea.is_gecko) {
+ // enable editable mode for Mozilla
+ doc.designMode = "on";
+ }
+ editor._doc = doc;
+ if (!editor.config.fullPage) {
+ doc.open();
+ var html = "<html>\n";
+ html += "<head>\n";
+ html += "<style>" + editor.config.pageStyle + "</style>\n";
+ html += "</head>\n";
+ html += "<body>\n";
+ html += editor._textArea.value;
+ html += "</body>\n";
+ html += "</html>";
+ doc.write(html);
+ doc.close();
+ } else {
+ var html = editor._textArea.value;
+ if (html.match(HTMLArea.RE_doctype)) {
+ editor.setDoctype(RegExp.$1);
+ html = html.replace(HTMLArea.RE_doctype, "");
+ }
+ doc.open();
+ doc.write(html);
+ doc.close();
+ }
+
+ if (HTMLArea.is_ie) {
+ // enable editable mode for IE. For some reason this
+ // doesn't work if done in the same place as for Gecko
+ // (above).
+ doc.body.contentEditable = true;
+ }
+
+ editor.focusEditor();
+ // intercept some events; for updating the toolbar & keyboard handlers
+ HTMLArea._addEvents
+ (doc, ["keydown", "keypress", "mousedown", "mouseup", "drag"],
+ function (event) {
+ return editor._editorEvent(HTMLArea.is_ie ? editor._iframe.contentWindow.event : event);
+ });
+ editor.updateToolbar();
+ };
+ setTimeout(initIframe, HTMLArea.is_gecko ? 10 : 0);
+};
+
+// Switches editor mode; parameter can be "textmode" or "wysiwyg". If no
+// parameter was passed this function toggles between modes.
+HTMLArea.prototype.setMode = function(mode) {
+ if (typeof mode == "undefined") {
+ mode = ((this._editMode == "textmode") ? "wysiwyg" : "textmode");
+ }
+ switch (mode) {
+ case "textmode":
+ this._textArea.value = this.getHTML();
+ this._iframe.style.display = "none";
+ this._textArea.style.display = "block";
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = HTMLArea.I18N.msg["TEXT_MODE"];
+ }
+ break;
+ case "wysiwyg":
+ if (HTMLArea.is_gecko) {
+ // disable design mode before changing innerHTML
+ this._doc.designMode = "off";
+ }
+ if (!this.config.fullPage)
+ this._doc.body.innerHTML = this.getHTML();
+ else
+ this.setFullHTML(this.getHTML());
+ this._iframe.style.display = "block";
+ this._textArea.style.display = "none";
+ if (HTMLArea.is_gecko) {
+ // we need to refresh that info for Moz-1.3a
+ this._doc.designMode = "on";
+ }
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = '';
+ this._statusBar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ this._statusBar.appendChild(this._statusBarTree);
+ }
+ break;
+ default:
+ alert("Mode <" + mode + "> not defined!");
+ return false;
+ }
+ this._editMode = mode;
+ this.focusEditor();
+};
+
+HTMLArea.prototype.setFullHTML = function(html) {
+ var save_multiline = RegExp.multiline;
+ RegExp.multiline = true;
+ if (html.match(HTMLArea.RE_doctype)) {
+ this.setDoctype(RegExp.$1);
+ html = html.replace(HTMLArea.RE_doctype, "");
+ }
+ RegExp.multiline = save_multiline;
+ if (!HTMLArea.is_ie) {
+ if (html.match(HTMLArea.RE_head))
+ this._doc.getElementsByTagName("head")[0].innerHTML = RegExp.$1;
+ if (html.match(HTMLArea.RE_body))
+ this._doc.getElementsByTagName("body")[0].innerHTML = RegExp.$1;
+ } else {
+ var html_re = /<html>((.|\n)*?)<\/html>/i;
+ html = html.replace(html_re, "$1");
+ this._doc.open();
+ this._doc.write(html);
+ this._doc.close();
+ this._doc.body.contentEditable = true;
+ return true;
+ }
+};
+
+/***************************************************
+ * Category: PLUGINS
+ ***************************************************/
+
+// Create the specified plugin and register it with this HTMLArea
+HTMLArea.prototype.registerPlugin = function() {
+ var plugin = arguments[0];
+ if (typeof plugin == "string")
+ plugin = eval(plugin);
+ var args = [];
+ for (var i = 1; i < arguments.length; ++i)
+ args.push(arguments[i]);
+ var obj = new plugin(this, args);
+ if (obj) {
+ var clone = {};
+ var info = plugin._pluginInfo;
+ for (var i in info)
+ clone[i] = info[i];
+ clone.instance = obj;
+ this.plugins[plugin._pluginInfo.name] = clone;
+ } else
+ alert("Can't register plugin " + plugin.toString() + ".");
+};
+
+// static function that loads the required plugin and lang file, based on the
+// language loaded already for HTMLArea. You better make sure that the plugin
+// _has_ that language, otherwise shit might happen ;-)
+HTMLArea.loadPlugin = function(pluginName) {
+ var editorurl = '';
+ if (typeof _editor_url != "undefined") {
+ editorurl = _editor_url + "/";
+ }
+ var dir = editorurl + "plugins/" + pluginName;
+ var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g,
+ function (str, l1, l2, l3) {
+ return l1 + "-" + l2.toLowerCase() + l3;
+ }).toLowerCase() + ".js";
+ document.write("<script type='text/javascript' src='" + dir + "/" + plugin + "'></script>");
+ document.write("<script type='text/javascript' src='" + dir + "/lang/" + HTMLArea.I18N.lang + ".js'></script>");
+};
+
+/***************************************************
+ * Category: EDITOR UTILITIES
+ ***************************************************/
+
+HTMLArea.prototype.forceRedraw = function() {
+ this._doc.body.style.visibility = "hidden";
+ this._doc.body.style.visibility = "visible";
+ // this._doc.body.innerHTML = this.getInnerHTML();
+};
+
+// focuses the iframe window. returns a reference to the editor document.
+HTMLArea.prototype.focusEditor = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : this._iframe.contentWindow.focus(); break;
+ case "textmode": this._textArea.focus(); break;
+ default : alert("ERROR: mode " + this._editMode + " is not defined");
+ }
+ return this._doc;
+};
+
+// takes a snapshot of the current text (for undo)
+HTMLArea.prototype._undoTakeSnapshot = function() {
+ ++this._undoPos;
+ if (this._undoPos >= this.config.undoSteps) {
+ // remove the first element
+ this._undoQueue.shift();
+ --this._undoPos;
+ }
+ // use the fasted method (getInnerHTML);
+ var take = true;
+ var txt = this.getInnerHTML();
+ if (this._undoPos > 0)
+ take = (this._undoQueue[this._undoPos - 1] != txt);
+ if (take) {
+ this._undoQueue[this._undoPos] = txt;
+ } else {
+ this._undoPos--;
+ }
+};
+
+HTMLArea.prototype.undo = function() {
+ if (this._undoPos > 0) {
+ var txt = this._undoQueue[--this._undoPos];
+ if (txt) this.setHTML(txt);
+ else ++this._undoPos;
+ }
+};
+
+HTMLArea.prototype.redo = function() {
+ if (this._undoPos < this._undoQueue.length - 1) {
+ var txt = this._undoQueue[++this._undoPos];
+ if (txt) this.setHTML(txt);
+ else --this._undoPos;
+ }
+};
+
+// updates enabled/disable/active state of the toolbar elements
+HTMLArea.prototype.updateToolbar = function(noStatus) {
+ var doc = this._doc;
+ var text = (this._editMode == "textmode");
+ var ancestors = null;
+ if (!text) {
+ ancestors = this.getAllAncestors();
+ if (this.config.statusBar && !noStatus) {
+ this._statusBarTree.innerHTML = ''; // clear
+ for (var i = ancestors.length; --i >= 0;) {
+ var el = ancestors[i];
+ if (!el) {
+ // hell knows why we get here; this
+ // could be a classic example of why
+ // it's good to check for conditions
+ // that are impossible to happen ;-)
+ continue;
+ }
+ var a = document.createElement("a");
+ a.href = "#";
+ a.el = el;
+ a.editor = this;
+ a.onclick = function() {
+ this.blur();
+ this.editor.selectNodeContents(this.el);
+ this.editor.updateToolbar(true);
+ return false;
+ };
+ a.oncontextmenu = function() {
+ // TODO: add context menu here
+ this.blur();
+ var info = "Inline style:\n\n";
+ info += this.el.style.cssText.split(/;\s*/).join(";\n");
+ alert(info);
+ return false;
+ };
+ var txt = el.tagName.toLowerCase();
+ a.title = el.style.cssText;
+ if (el.id) {
+ txt += "#" + el.id;
+ }
+ if (el.className) {
+ txt += "." + el.className;
+ }
+ a.appendChild(document.createTextNode(txt));
+ this._statusBarTree.appendChild(a);
+ if (i != 0) {
+ this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
+ }
+ }
+ }
+ }
+ for (var i in this._toolbarObjects) {
+ var btn = this._toolbarObjects[i];
+ var cmd = i;
+ var inContext = true;
+ if (btn.context && !text) {
+ inContext = false;
+ var context = btn.context;
+ var attrs = [];
+ if (/(.*)\[(.*?)\]/.test(context)) {
+ context = RegExp.$1;
+ attrs = RegExp.$2.split(",");
+ }
+ context = context.toLowerCase();
+ var match = (context == "*");
+ for (var k in ancestors) {
+ if (!ancestors[k]) {
+ // the impossible really happens.
+ continue;
+ }
+ if (match || (ancestors[k].tagName.toLowerCase() == context)) {
+ inContext = true;
+ for (var ka in attrs) {
+ if (!eval("ancestors[k]." + attrs[ka])) {
+ inContext = false;
+ break;
+ }
+ }
+ if (inContext) {
+ break;
+ }
+ }
+ }
+ }
+ btn.state("enabled", (!text || btn.text) && inContext);
+ if (typeof cmd == "function") {
+ continue;
+ }
+ // look-it-up in the custom dropdown boxes
+ var dropdown = this.config.customSelects[cmd];
+ if ((!text || btn.text) && (typeof dropdown != "undefined")) {
+ dropdown.refresh(this);
+ continue;
+ }
+ switch (cmd) {
+ case "fontname":
+ case "fontsize":
+ case "formatblock":
+ if (!text) {
+ var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
+ if (!value) {
+ // FIXME: what do we do here?
+ break;
+ }
+ // HACK -- retrieve the config option for this
+ // combo box. We rely on the fact that the
+ // variable in config has the same name as
+ // button name in the toolbar.
+ var options = this.config[cmd];
+ var k = 0;
+ // btn.element.selectedIndex = 0;
+ for (var j in options) {
+ // FIXME: the following line is scary.
+ if ((j.toLowerCase() == value) ||
+ (options[j].substr(0, value.length).toLowerCase() == value)) {
+ btn.element.selectedIndex = k;
+ break;
+ }
+ ++k;
+ }
+ }
+ break;
+ case "textindicator":
+ if (!text) {
+ try {with (btn.element.style) {
+ backgroundColor = HTMLArea._makeColor(
+ doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor"));
+ if (/transparent/i.test(backgroundColor)) {
+ // Mozilla
+ backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor"));
+ }
+ color = HTMLArea._makeColor(doc.queryCommandValue("forecolor"));
+ fontFamily = doc.queryCommandValue("fontname");
+ fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
+ fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
+ }} catch (e) {
+ // alert(e + "\n\n" + cmd);
+ }
+ }
+ break;
+ case "htmlmode": btn.state("active", text); break;
+ default:
+ try {
+ btn.state("active", (!text && doc.queryCommandState(cmd)));
+ } catch (e) {}
+ }
+ }
+ // take undo snapshots
+ if (!this._timerUndo) {
+ this._undoTakeSnapshot();
+ var editor = this;
+ this._timerUndo = setTimeout(function() {
+ editor._timerUndo = null;
+ }, this.config.undoTimeout);
+ }
+ // check if any plugins have registered refresh handlers
+ for (var i in this.plugins) {
+ var plugin = this.plugins[i].instance;
+ if (typeof plugin.onUpdateToolbar == "function")
+ plugin.onUpdateToolbar();
+ }
+};
+
+/** Returns a node after which we can insert other nodes, in the current
+ * selection. The selection is removed. It splits a text node, if needed.
+ */
+HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted) {
+ if (!HTMLArea.is_ie) {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ // remove the current selection
+ sel.removeAllRanges();
+ range.deleteContents();
+ var node = range.startContainer;
+ var pos = range.startOffset;
+ switch (node.nodeType) {
+ case 3: // Node.TEXT_NODE
+ // we have to split it at the caret position.
+ if (toBeInserted.nodeType == 3) {
+ // do optimized insertion
+ node.insertData(pos, toBeInserted.data);
+ range = this._createRange();
+ range.setEnd(node, pos + toBeInserted.length);
+ range.setStart(node, pos + toBeInserted.length);
+ sel.addRange(range);
+ } else {
+ node = node.splitText(pos);
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.parentNode.insertBefore(toBeInserted, node);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ }
+ break;
+ case 1: // Node.ELEMENT_NODE
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.insertBefore(toBeInserted, node.childNodes[pos]);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ break;
+ }
+ } else {
+ return null; // this function not yet used for IE <FIXME>
+ }
+};
+
+// Returns the deepest node that contains both endpoints of the selection.
+HTMLArea.prototype.getParentElement = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ return range.parentElement ? range.parentElement() : this._doc.body;
+ } else {
+ var p = range.commonAncestorContainer;
+ while (p.nodeType == 3) {
+ p = p.parentNode;
+ }
+ return p;
+ }
+};
+
+// Returns an array with all the ancestor nodes of the selection.
+HTMLArea.prototype.getAllAncestors = function() {
+ var p = this.getParentElement();
+ var a = [];
+ while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
+ a.push(p);
+ p = p.parentNode;
+ }
+ a.push(this._doc.body);
+ return a;
+};
+
+// Selects the contents inside the given node
+HTMLArea.prototype.selectNodeContents = function(node, pos) {
+ this.focusEditor();
+ this.forceRedraw();
+ var range;
+ var collapsed = (typeof pos != "undefined");
+ if (HTMLArea.is_ie) {
+ range = this._doc.body.createTextRange();
+ range.moveToElementText(node);
+ (collapsed) && range.collapse(pos);
+ range.select();
+ } else {
+ var sel = this._getSelection();
+ range = this._doc.createRange();
+ range.selectNodeContents(node);
+ (collapsed) && range.collapse(pos);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+};
+
+/** Call this function to insert HTML code at the current position. It deletes
+ * the selection, if any.
+ */
+HTMLArea.prototype.insertHTML = function(html) {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(html);
+ } else {
+ // construct a new document fragment with the given HTML
+ var fragment = this._doc.createDocumentFragment();
+ var div = this._doc.createElement("div");
+ div.innerHTML = html;
+ while (div.firstChild) {
+ // the following call also removes the node from div
+ fragment.appendChild(div.firstChild);
+ }
+ // this also removes the selection
+ var node = this.insertNodeAtSelection(fragment);
+ }
+};
+
+/**
+ * Call this function to surround the existing HTML code in the selection with
+ * your tags. FIXME: buggy! This function will be deprecated "soon".
+ */
+HTMLArea.prototype.surroundHTML = function(startTag, endTag) {
+ var html = this.getSelectedHTML();
+ // the following also deletes the selection
+ this.insertHTML(startTag + html + endTag);
+};
+
+/// Retrieve the selected block
+HTMLArea.prototype.getSelectedHTML = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var existing = null;
+ if (HTMLArea.is_ie) {
+ existing = range.htmlText;
+ } else {
+ existing = HTMLArea.getHTML(range.cloneContents(), false);
+ }
+ return existing;
+};
+
+// Called when the user clicks on "InsertImage" button
+HTMLArea.prototype._insertImage = function() {
+ var editor = this; // for nested functions
+ this._popupDialog("insert_image.html", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var sel = editor._getSelection();
+ var range = editor._createRange(sel);
+ editor._doc.execCommand("insertimage", false, param["f_url"]);
+ var img = null;
+ if (HTMLArea.is_ie) {
+ img = range.parentElement();
+ // wonder if this works...
+ if (img.tagName.toLowerCase() != "img") {
+ img = img.previousSibling;
+ }
+ } else {
+ img = range.startContainer.previousSibling;
+ }
+ for (field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_alt" : img.alt = value; break;
+ case "f_border" : img.border = parseInt(value); break;
+ case "f_align" : img.align = value; break;
+ case "f_vert" : img.vspace = parseInt(value); break;
+ case "f_horiz" : img.hspace = parseInt(value); break;
+ }
+ }
+ }, null);
+};
+
+// Called when the user clicks the Insert Table button
+HTMLArea.prototype._insertTable = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var editor = this; // for nested functions
+ this._popupDialog("insert_table.html", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var doc = editor._doc;
+ // create the table element
+ var table = doc.createElement("table");
+ // assign the given arguments
+ for (var field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_width" : table.style.width = value + param["f_unit"]; break;
+ case "f_align" : table.align = value; break;
+ case "f_border" : table.border = parseInt(value); break;
+ case "f_spacing" : table.cellspacing = parseInt(value); break;
+ case "f_padding" : table.cellpadding = parseInt(value); break;
+ }
+ }
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+ for (var i = 0; i < param["f_rows"]; ++i) {
+ var tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ for (var j = 0; j < param["f_cols"]; ++j) {
+ var td = doc.createElement("td");
+ tr.appendChild(td);
+ // Mozilla likes to see something inside the cell.
+ (HTMLArea.is_gecko) && td.appendChild(doc.createElement("br"));
+ }
+ }
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(table.outerHTML);
+ } else {
+ // insert the table
+ editor.insertNodeAtSelection(table);
+ }
+ return true;
+ }, null);
+};
+
+/***************************************************
+ * Category: EVENT HANDLERS
+ ***************************************************/
+
+// el is reference to the SELECT object
+// txt is the name of the select field, as in config.toolbar
+HTMLArea.prototype._comboSelected = function(el, txt) {
+ this.focusEditor();
+ var value = el.options[el.selectedIndex].value;
+ switch (txt) {
+ case "fontname":
+ case "fontsize": this.execCommand(txt, false, value); break;
+ case "formatblock":
+ (HTMLArea.is_ie) && (value = "<" + value + ">");
+ this.execCommand(txt, false, value);
+ break;
+ default:
+ // try to look it up in the registered dropdowns
+ var dropdown = this.config.customSelects[txt];
+ if (typeof dropdown != "undefined") {
+ dropdown.action(this);
+ } else {
+ alert("FIXME: combo box " + txt + " not implemented");
+ }
+ }
+};
+
+// the execCommand function (intercepts some commands and replaces them with
+// our own implementation)
+HTMLArea.prototype.execCommand = function(cmdID, UI, param) {
+ var editor = this; // for nested functions
+ this.focusEditor();
+ switch (cmdID.toLowerCase()) {
+ case "htmlmode" : this.setMode(); break;
+ case "hilitecolor":
+ (HTMLArea.is_ie) && (cmdID = "backcolor");
+ case "forecolor":
+ this._popupDialog("select_color.html", function(color) {
+ if (color) { // selection not canceled
+ editor._doc.execCommand(cmdID, false, "#" + color);
+ }
+ }, HTMLArea._colorToRgb(this._doc.queryCommandValue(cmdID)));
+ break;
+ case "createlink":
+ if (HTMLArea.is_ie || !UI) {
+ this._doc.execCommand(cmdID, UI, param);
+ } else {
+ // browser is Mozilla & wants UI
+ var param;
+ if ((param = prompt("Enter URL"))) {
+ this._doc.execCommand(cmdID, false, param);
+ }
+ }
+ break;
+ case "popupeditor":
+ if (HTMLArea.is_ie) {
+ window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
+ "toolbar=no,location=no,directories=no,status=no,menubar=no," +
+ "scrollbars=no,resizable=yes,width=640,height=480");
+ } else {
+ window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
+ "toolbar=no,menubar=no,personalbar=no,width=640,height=480," +
+ "scrollbars=no,resizable=yes");
+ }
+ // pass this object to the newly opened window
+ HTMLArea._object = this;
+ break;
+ case "undo": this.undo(); break;
+ case "redo": this.redo(); break;
+ case "inserttable": this._insertTable(); break;
+ case "insertimage": this._insertImage(); break;
+ case "about" : this._popupDialog("about.html", null, this); break;
+ case "showhelp" : window.open(this.config.editorURL + "reference.html", "ha_help"); break;
+ default: this._doc.execCommand(cmdID, UI, param);
+ }
+ this.updateToolbar();
+ return false;
+};
+
+/** A generic event handler for things that happen in the IFRAME's document.
+ * This function also handles key bindings. */
+HTMLArea.prototype._editorEvent = function(ev) {
+ var editor = this;
+ var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");
+ if (keyEvent && ev.ctrlKey) {
+ var sel = null;
+ var range = null;
+ var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
+ var cmd = null;
+ var value = null;
+ switch (key) {
+ case 'a':
+ if (!HTMLArea.is_ie) {
+ // KEY select all
+ sel = this._getSelection();
+ sel.removeAllRanges();
+ range = this._createRange();
+ range.selectNodeContents(this._doc.body);
+ sel.addRange(range);
+ HTMLArea._stopEvent(ev);
+ }
+ break;
+
+ // simple key commands follow
+
+ case 'b': cmd = "bold"; break;
+ case 'i': cmd = "italic"; break;
+ case 'u': cmd = "underline"; break;
+ case 's': cmd = "strikethrough"; break;
+ case 'l': cmd = "justifyleft"; break;
+ case 'e': cmd = "justifycenter"; break;
+ case 'r': cmd = "justifyright"; break;
+ case 'j': cmd = "justifyfull"; break;
+ case 'z': cmd = "undo"; break;
+ case 'y': cmd = "redo"; break;
+
+ // headings
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ cmd = "formatblock";
+ value = "h" + key;
+ if (HTMLArea.is_ie) {
+ value = "<" + value + ">";
+ }
+ break;
+ }
+ if (cmd) {
+ // execute simple command
+ this.execCommand(cmd, false, value);
+ HTMLArea._stopEvent(ev);
+ }
+ }
+ /*
+ else if (keyEvent) {
+ // other keys here
+ switch (ev.keyCode) {
+ case 13: // KEY enter
+ // if (HTMLArea.is_ie) {
+ this.insertHTML("<br />");
+ HTMLArea._stopEvent(ev);
+ // }
+ break;
+ }
+ }
+ */
+ // update the toolbar state after some time
+ if (editor._timerToolbar) {
+ clearTimeout(editor._timerToolbar);
+ }
+ editor._timerToolbar = setTimeout(function() {
+ editor.updateToolbar();
+ editor._timerToolbar = null;
+ }, 50);
+};
+
+// retrieve the HTML
+HTMLArea.prototype.getHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ return HTMLArea.getHTML(this._doc.body, false);
+ else
+ return this.doctype + "\n" + HTMLArea.getHTML(this._doc.documentElement, true);
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// retrieve the HTML (fastest version, but uses innerHTML)
+HTMLArea.prototype.getInnerHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ return this._doc.body.innerHTML;
+ else
+ return this.doctype + "\n" + this._doc.documentElement.innerHTML;
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// completely change the HTML inside
+HTMLArea.prototype.setHTML = function(html) {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ this._doc.body.innerHTML = html;
+ else
+ // this._doc.documentElement.innerHTML = html;
+ this._doc.body.innerHTML = html;
+ break;
+ case "textmode" : this._textArea.value = html; break;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// sets the given doctype (useful when config.fullPage is true)
+HTMLArea.prototype.setDoctype = function(doctype) {
+ this.doctype = doctype;
+};
+
+/***************************************************
+ * Category: UTILITY FUNCTIONS
+ ***************************************************/
+
+// browser identification
+
+HTMLArea.agt = navigator.userAgent.toLowerCase();
+HTMLArea.is_ie = ((HTMLArea.agt.indexOf("msie") != -1) && (HTMLArea.agt.indexOf("opera") == -1));
+HTMLArea.is_opera = (HTMLArea.agt.indexOf("opera") != -1);
+HTMLArea.is_mac = (HTMLArea.agt.indexOf("mac") != -1);
+HTMLArea.is_mac_ie = (HTMLArea.is_ie && HTMLArea.is_mac);
+HTMLArea.is_win_ie = (HTMLArea.is_ie && !HTMLArea.is_mac);
+HTMLArea.is_gecko = (navigator.product == "Gecko");
+
+// variable used to pass the object to the popup editor window.
+HTMLArea._object = null;
+
+// FIXME!!! this should return false for IE < 5.5
+HTMLArea.checkSupportedBrowser = function() {
+ if (HTMLArea.is_gecko) {
+ if (navigator.productSub < 20021201) {
+ alert("You need at least Mozilla-1.3 Alpha.\n" +
+ "Sorry, your Gecko is not supported.");
+ return false;
+ }
+ if (navigator.productSub < 20030210) {
+ alert("Mozilla < 1.3 Beta is not supported!\n" +
+ "I'll try, though, but it might not work.");
+ }
+ }
+ return HTMLArea.is_gecko || HTMLArea.is_ie;
+};
+
+// selection & ranges
+
+// returns the current selection object
+HTMLArea.prototype._getSelection = function() {
+ if (HTMLArea.is_ie) {
+ return this._doc.selection;
+ } else {
+ return this._iframe.contentWindow.getSelection();
+ }
+};
+
+// returns a range for the current selection
+HTMLArea.prototype._createRange = function(sel) {
+ if (HTMLArea.is_ie) {
+ return sel.createRange();
+ } else {
+ this.focusEditor();
+ if (typeof sel != "undefined") {
+ return sel.getRangeAt(0);
+ } else {
+ return this._doc.createRange();
+ }
+ }
+};
+
+// event handling
+
+HTMLArea._addEvent = function(el, evname, func) {
+ if (HTMLArea.is_ie) {
+ el.attachEvent("on" + evname, func);
+ } else {
+ el.addEventListener(evname, func, true);
+ }
+};
+
+HTMLArea._addEvents = function(el, evs, func) {
+ for (var i in evs) {
+ HTMLArea._addEvent(el, evs[i], func);
+ }
+};
+
+HTMLArea._removeEvent = function(el, evname, func) {
+ if (HTMLArea.is_ie) {
+ el.detachEvent("on" + evname, func);
+ } else {
+ el.removeEventListener(evname, func, true);
+ }
+};
+
+HTMLArea._removeEvents = function(el, evs, func) {
+ for (var i in evs) {
+ HTMLArea._removeEvent(el, evs[i], func);
+ }
+};
+
+HTMLArea._stopEvent = function(ev) {
+ if (HTMLArea.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+};
+
+HTMLArea._removeClass = function(el, className) {
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
+};
+
+HTMLArea._addClass = function(el, className) {
+ // remove the class first, if already there
+ HTMLArea._removeClass(el, className);
+ el.className += " " + className;
+};
+
+HTMLArea._hasClass = function(el, className) {
+ if (!(el && el.className)) {
+ return false;
+ }
+ var cls = el.className.split(" ");
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] == className) {
+ return true;
+ }
+ }
+ return false;
+};
+
+HTMLArea.isBlockElement = function(el) {
+ var blockTags = " body form textarea fieldset ul ol dl li div " +
+ "p h1 h2 h3 h4 h5 h6 quote pre table thead " +
+ "tbody tfoot tr td iframe address ";
+ return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+HTMLArea.needsClosingTag = function(el) {
+ var closingTags = " head script style div span tr td tbody table em strong font a title ";
+ return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+// performs HTML encoding of some given string
+HTMLArea.htmlEncode = function(str) {
+ // we don't need regexp for that, but.. so be it for now.
+ str = str.replace(/&/ig, "&");
+ str = str.replace(/</ig, "<");
+ str = str.replace(/>/ig, ">");
+ str = str.replace(/\x22/ig, """);
+ // \x22 means '"' -- we use hex reprezentation so that we don't disturb
+ // JS compressors (well, at least mine fails.. ;)
+ return str;
+};
+
+// Retrieves the HTML code from the given node. This is a replacement for
+// getting innerHTML, using standard DOM calls.
+HTMLArea.getHTML = function(root, outputRoot) {
+ var html = "";
+ switch (root.nodeType) {
+ case 1: // Node.ELEMENT_NODE
+ case 11: // Node.DOCUMENT_FRAGMENT_NODE
+ var closed;
+ var i;
+ var root_tag = root.tagName.toLowerCase();
+ if (HTMLArea.is_ie && root_tag == "head") {
+ if (outputRoot)
+ html += "<head>";
+ // lowercasize
+ var save_multiline = RegExp.multiline;
+ RegExp.multiline = true;
+ var txt = root.innerHTML.replace(HTMLArea.RE_tagName, function(str, p1, p2) {
+ return p1 + p2.toLowerCase();
+ });
+ RegExp.multiline = save_multiline;
+ html += txt;
+ if (outputRoot)
+ html += "</head>";
+ break;
+ } else if (outputRoot) {
+ closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));
+ html = "<" + root.tagName.toLowerCase();
+ var attrs = root.attributes;
+ for (i = 0; i < attrs.length; ++i) {
+ var a = attrs.item(i);
+ if (!a.specified) {
+ continue;
+ }
+ var name = a.nodeName.toLowerCase();
+ if (/_moz|contenteditable/.test(name)) {
+ // avoid certain attributes
+ continue;
+ }
+ var value;
+ if (name != "style") {
+ // IE5.5 reports 25 when cellSpacing is
+ // 1; other values might be doomed too.
+ // For this reason we extract the
+ // values directly from the root node.
+ // I'm starting to HATE JavaScript
+ // development. Browser differences
+ // suck.
+ if (typeof root[a.nodeName] != "undefined") {
+ value = root[a.nodeName];
+ } else {
+ value = a.nodeValue;
+ }
+ } else { // IE fails to put style in attributes list
+ // FIXME: cssText reported by IE is UPPERCASE
+ value = root.style.cssText;
+ }
+ if (/_moz/.test(value)) {
+ // Mozilla reports some special tags
+ // here; we don't need them.
+ continue;
+ }
+ html += " " + name + '="' + value + '"';
+ }
+ html += closed ? " />" : ">";
+ }
+ for (i = root.firstChild; i; i = i.nextSibling) {
+ html += HTMLArea.getHTML(i, true);
+ }
+ if (outputRoot && !closed) {
+ html += "</" + root.tagName.toLowerCase() + ">";
+ }
+ break;
+ case 3: // Node.TEXT_NODE
+ html = HTMLArea.htmlEncode(root.data);
+ break;
+ case 8: // Node.COMMENT_NODE
+ html = "<!--" + root.data + "-->";
+ break; // skip comments, for now.
+ }
+ return html;
+};
+
+// creates a rgb-style color from a number
+HTMLArea._makeColor = function(v) {
+ if (typeof v != "number") {
+ // already in rgb (hopefully); IE doesn't get here.
+ return v;
+ }
+ // IE sends number; convert to rgb.
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "rgb(" + r + "," + g + "," + b + ")";
+};
+
+// returns hexadecimal color representation from a number or a rgb-style color.
+HTMLArea._colorToRgb = function(v) {
+ if (!v)
+ return '';
+
+ // returns the hex representation of one byte (2 digits)
+ function hex(d) {
+ return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
+ };
+
+ if (typeof v == "number") {
+ // we're talking to IE here
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+
+ if (v.substr(0, 3) == "rgb") {
+ // in rgb(...) form -- Mozilla
+ var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
+ if (v.match(re)) {
+ var r = parseInt(RegExp.$1);
+ var g = parseInt(RegExp.$2);
+ var b = parseInt(RegExp.$3);
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+ // doesn't match RE?! maybe uses percentages or float numbers
+ // -- FIXME: not yet implemented.
+ return null;
+ }
+
+ if (v.substr(0, 1) == "#") {
+ // already hex rgb (hopefully :D )
+ return v;
+ }
+
+ // if everything else fails ;)
+ return null;
+};
+
+// modal dialogs for Mozilla (for IE we're using the showModalDialog() call).
+
+// receives an URL to the popup dialog and a function that receives one value;
+// this function will get called after the dialog is closed, with the return
+// value of the dialog.
+HTMLArea.prototype._popupDialog = function(url, action, init) {
+ Dialog(this.popupURL(url), action, init);
+};
+
+// paths
+
+HTMLArea.prototype.imgURL = function(file, plugin) {
+ if (typeof plugin == "undefined")
+ return this.config.editorURL + file;
+ else
+ return this.config.editorURL + "plugins/" + plugin + "/img/" + file;
+};
+
+HTMLArea.prototype.popupURL = function(file) {
+ var url = "";
+ if (file.match(/^plugin:\/\/(.*?)\/(.*)/)) {
+ var plugin = RegExp.$1;
+ var popup = RegExp.$2;
+ if (!/\.html$/.test(popup))
+ popup += ".html";
+ url = this.config.editorURL + "plugins/" + plugin + "/popups/" + popup;
+ } else
+ url = this.config.editorURL + this.config.popupURL + file;
+ return url;
+};
+
+// EOF
+// Local variables: //
+// c-basic-offset:8 //
+// indent-tabs-mode:t //
+// End: //
--- /dev/null
+//
+// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+// This copyright notice MUST stay intact for use (see license.txt).
+//
+// A free WYSIWYG editor replacement for <textarea> fields.
+// For full source code and docs, visit http://www.interactivetools.com/
+//
+// Version 3.0 developed by Mihai Bazon for InteractiveTools.
+// http://dynarch.com/mishoo
+//
+// $Id$
+
+// Creates a new HTMLArea object. Tries to replace the textarea with the given
+// ID with it.
+<?php
+include("../../config.php");
+
+ $lang = current_language();
+
+ if (empty($lang)) {
+ $lang = "en";
+ }
+
+?>
+function HTMLArea(textarea, config) {
+ if (HTMLArea.checkSupportedBrowser()) {
+ if (typeof config == "undefined") {
+ this.config = new HTMLArea.Config();
+ } else {
+ this.config = config;
+ }
+ this._htmlArea = null;
+ this._textArea = textarea;
+ this._editMode = "wysiwyg";
+ this.plugins = {};
+ this._timerToolbar = null;
+ this._timerUndo = null;
+ this._undoQueue = new Array(this.config.undoSteps);
+ this._undoPos = -1;
+ this._mdoc = document; // cache the document, we need it in plugins
+ this.doctype = '';
+ }
+};
+
+// cache some regexps
+HTMLArea.RE_tagName = /(<\/|<)\s*([^ \t\n>]+)/ig;
+HTMLArea.RE_doctype = /(<!doctype((.|\n)*?)>)\n?/i;
+HTMLArea.RE_head = /<head>((.|\n)*?)<\/head>/i;
+HTMLArea.RE_body = /<body>((.|\n)*?)<\/body>/i;
+
+HTMLArea.Config = function () {
+ this.version = "3.0";
+
+ this.width = "auto";
+ this.height = "auto";
+
+ // enable creation of a status bar?
+ this.statusBar = true;
+
+ // maximum size of the undo queue
+ this.undoSteps = 20;
+
+ // the time interval at which undo samples are taken
+ this.undoTimeout = 500; // 1/2 sec.
+
+ // the next parameter specifies whether the toolbar should be included
+ // in the size or not.
+ this.sizeIncludesToolbar = true;
+
+ // if true then HTMLArea will retrieve the full HTML, starting with the
+ // <HTML> tag.
+ this.fullPage = false;
+
+ // style included in the iframe document
+ this.pageStyle = "body { background-color: #fff; font-family: 'Times New Roman', Times; }";
+ if (typeof _editor_url != "undefined") {
+ this.editorURL = _editor_url;
+ } else {
+ this.editorURL = "<?php echo $CFG->wwwroot ?>/lib/editor/";
+ }
+
+ // URL-s
+ this.imgURL = "images/";
+ this.popupURL = "popups/";
+
+ /** CUSTOMIZING THE TOOLBAR
+ * -------------------------
+ *
+ * It is recommended that you customize the toolbar contents in an
+ * external file (i.e. the one calling HTMLArea) and leave this one
+ * unchanged. That's because when we (InteractiveTools.com) release a
+ * new official version, it's less likely that you will have problems
+ * upgrading HTMLArea.
+ */
+ this.toolbar = [
+ [ "fontname", "space",
+ "fontsize", "space",
+ "formatblock", "space",
+ "bold", "italic", "underline", "strikethrough", "separator",
+ "subscript", "superscript", "separator",
+ "copy", "cut", "paste", "space", "undo", "redo" ],
+
+ [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",
+ "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator",
+ "forecolor", "hilitecolor", "separator",
+ "inserthorizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator",
+ "popupeditor", "separator", "showhelp", "about", "insertsmile", "insertchar" ]
+ ];
+
+ this.fontname = {
+ "Arial": 'arial,helvetica,sans-serif',
+ "Courier New": 'courier new,courier,monospace',
+ "Georgia": 'georgia,times new roman,times,serif',
+ "Tahoma": 'tahoma,arial,helvetica,sans-serif',
+ "Times New Roman": 'times new roman,times,serif',
+ "Verdana": 'verdana,arial,helvetica,sans-serif',
+ "impact": 'impact',
+ "WingDings": 'wingdings'
+ };
+
+ this.fontsize = {
+ "1 (8 pt)": "1",
+ "2 (10 pt)": "2",
+ "3 (12 pt)": "3",
+ "4 (14 pt)": "4",
+ "5 (18 pt)": "5",
+ "6 (24 pt)": "6",
+ "7 (36 pt)": "7"
+ };
+
+ this.formatblock = {
+ "Heading 1": "h1",
+ "Heading 2": "h2",
+ "Heading 3": "h3",
+ "Heading 4": "h4",
+ "Heading 5": "h5",
+ "Heading 6": "h6",
+ "Normal": "p",
+ "Address": "address",
+ "Formatted": "pre"
+ };
+
+ this.customSelects = {};
+
+ function cut_copy_paste(e, cmd, obj) {
+ try {
+ e.execCommand(cmd);
+ } catch (e) {
+ if (HTMLArea.is_gecko) {
+ alert("Some revisions of Mozilla/Gecko do not support programatic " +
+ "access to cut/copy/paste functions, for security reasons. " +
+ "Your browser is one of them. Please use the standard key combinations:\n" +
+ "CTRL-X for cut, CTRL-C for copy, CTRL-V for paste.");
+ obj.element.style.display = "none";
+ }
+ }
+ };
+
+ // ADDING CUSTOM BUTTONS: please read below!
+ // format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]"
+ // - ID: unique ID for the button. If the button calls document.execCommand
+ // it's wise to give it the same name as the called command.
+ // - ACTION: function that gets called when the button is clicked.
+ // it has the following prototype:
+ // function(editor, buttonName)
+ // - editor is the HTMLArea object that triggered the call
+ // - buttonName is the ID of the clicked button
+ // These 2 parameters makes it possible for you to use the same
+ // handler for more HTMLArea objects or for more different buttons.
+ // - ToolTip: default tooltip, for cases when it is not defined in the -lang- file (HTMLArea.I18N)
+ // - Icon: path to an icon image file for the button (TODO: use one image for all buttons!)
+ // - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.
+ this.btnList = {
+ bold: [ "Bold", "ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ],
+ italic: [ "Italic", "ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ],
+ underline: [ "Underline", "ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ],
+ strikethrough: [ "Strikethrough", "ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ],
+ subscript: [ "Subscript", "ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ],
+ superscript: [ "Superscript", "ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ],
+ justifyleft: [ "Justify Left", "ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ],
+ justifycenter: [ "Justify Center", "ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ],
+ justifyright: [ "Justify Right", "ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ],
+ justifyfull: [ "Justify Full", "ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ],
+ insertorderedlist: [ "Ordered List", "ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ],
+ insertunorderedlist: [ "Bulleted List", "ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ],
+ outdent: [ "Decrease Indent", "ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ],
+ indent: [ "Increase Indent", "ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ],
+ forecolor: [ "Font Color", "ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ],
+ hilitecolor: [ "Background Color", "ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ],
+ inserthorizontalrule: [ "Horizontal Rule", "ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ],
+ createlink: [ "Insert Web Link", "ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ],
+ insertimage: [ "Insert Image", "ed_image.gif", false, function(e) {e.execCommand("insertimage");} ],
+ inserttable: [ "Insert Table", "insert_table.gif", false, function(e) {e.execCommand("inserttable");} ],
+ htmlmode: [ "Toggle HTML Source", "ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ],
+ popupeditor: [ "Enlarge Editor", "fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ],
+ about: [ "About this editor", "ed_about.gif", true, function(e) {e.execCommand("about");} ],
+ showhelp: [ "Help using editor", "ed_help.gif", true, function(e) {e.execCommand("showhelp");} ],
+ undo: [ "Undoes your last action", "ed_undo.gif", false, function(e) {e.execCommand("undo");} ],
+ redo: [ "Redoes your last action", "ed_redo.gif", false, function(e) {e.execCommand("redo");} ],
+ cut: [ "Cut selection", "ed_cut.gif", false, cut_copy_paste ],
+ copy: [ "Copy selection", "ed_copy.gif", false, cut_copy_paste ],
+ paste: [ "Paste from clipboard", "ed_paste.gif", false, cut_copy_paste ],
+ insertsmile: ["Insert Smiley", "em.icon.smile.gif", false, function(e) {e.execCommand("insertsmile");} ],
+ insertchar: [ "Insert Char", "icon_ins_char.gif", false, function(e) {e.execCommand("insertchar");} ]
+ };
+ /* ADDING CUSTOM BUTTONS
+ * ---------------------
+ *
+ * It is recommended that you add the custom buttons in an external
+ * file and leave this one unchanged. That's because when we
+ * (InteractiveTools.com) release a new official version, it's less
+ * likely that you will have problems upgrading HTMLArea.
+ *
+ * Example on how to add a custom button when you construct the HTMLArea:
+ *
+ * var editor = new HTMLArea("your_text_area_id");
+ * var cfg = editor.config; // this is the default configuration
+ * cfg.btnList["my-hilite"] =
+ * [ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action
+ * "Highlight selection", // tooltip
+ * "my_hilite.gif", // image
+ * false // disabled in text mode
+ * ];
+ * cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar
+ *
+ * An alternate (also more convenient and recommended) way to
+ * accomplish this is to use the registerButton function below.
+ */
+ // initialize tooltips from the I18N module and generate correct image path
+ for (var i in this.btnList) {
+ var btn = this.btnList[i];
+ btn[1] = this.editorURL + this.imgURL + btn[1];
+ if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {
+ btn[0] = HTMLArea.I18N.tooltips[i];
+ }
+ }
+};
+
+/** Helper function: register a new button with the configuration. It can be
+ * called with all 5 arguments, or with only one (first one). When called with
+ * only one argument it must be an object with the following properties: id,
+ * tooltip, image, textMode, action. Examples:
+ *
+ * 1. config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...});
+ * 2. config.registerButton({
+ * id : "my-hilite", // the ID of your button
+ * tooltip : "Hilite text", // the tooltip
+ * image : "my-hilite.gif", // image to be displayed in the toolbar
+ * textMode : false, // disabled in text mode
+ * action : function(editor) { // called when the button is clicked
+ * editor.surroundHTML('<span class="hilite">', '</span>');
+ * },
+ * context : "p" // will be disabled if outside a <p> element
+ * });
+ */
+HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) {
+ var the_id;
+ if (typeof id == "string") {
+ the_id = id;
+ } else if (typeof id == "object") {
+ the_id = id.id;
+ } else {
+ alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments");
+ return false;
+ }
+ // check for existing id
+ if (typeof this.customSelects[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ switch (typeof id) {
+ case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break;
+ case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break;
+ }
+};
+
+/** The following helper function registers a dropdown box with the editor
+ * configuration. You still have to add it to the toolbar, same as with the
+ * buttons. Call it like this:
+ *
+ * FIXME: add example
+ */
+HTMLArea.Config.prototype.registerDropdown = function(object) {
+ // check for existing id
+ if (typeof this.customSelects[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ this.customSelects[object.id] = object;
+};
+
+/** Call this function to remove some buttons/drop-down boxes from the toolbar.
+ * Pass as the only parameter a string containing button/drop-down names
+ * delimited by spaces. Note that the string should also begin with a space
+ * and end with a space. Example:
+ *
+ * config.hideSomeButtons(" fontname fontsize textindicator ");
+ *
+ * It's useful because it's easier to remove stuff from the defaul toolbar than
+ * create a brand new toolbar ;-)
+ */
+HTMLArea.Config.prototype.hideSomeButtons = function(remove) {
+ var toolbar = this.toolbar;
+ for (var i in toolbar) {
+ var line = toolbar[i];
+ for (var j = line.length; --j >= 0; ) {
+ if (remove.indexOf(" " + line[j] + " ") >= 0) {
+ var len = 1;
+ if (/separator|space/.test(line[j + 1])) {
+ len = 2;
+ }
+ line.splice(j, len);
+ }
+ }
+ }
+};
+
+/** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */
+HTMLArea.replaceAll = function(config) {
+ var tas = document.getElementsByTagName("textarea");
+ for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate());
+};
+
+/** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */
+HTMLArea.replace = function(id, config) {
+ var ta = document.getElementById(id);
+ return ta ? (new HTMLArea(ta, config)).generate() : null;
+};
+
+// Creates the toolbar and appends it to the _htmlarea
+HTMLArea.prototype._createToolbar = function () {
+ var editor = this; // to access this in nested functions
+
+ var toolbar = document.createElement("div");
+ this._toolbar = toolbar;
+ toolbar.className = "toolbar";
+ toolbar.unselectable = "1";
+ var tb_row = null;
+ var tb_objects = new Object();
+ this._toolbarObjects = tb_objects;
+
+ // creates a new line in the toolbar
+ function newLine() {
+ var table = document.createElement("table");
+ table.border = "0px";
+ table.cellSpacing = "0px";
+ table.cellPadding = "0px";
+ toolbar.appendChild(table);
+ // TBODY is required for IE, otherwise you don't see anything
+ // in the TABLE.
+ var tb_body = document.createElement("tbody");
+ table.appendChild(tb_body);
+ tb_row = document.createElement("tr");
+ tb_body.appendChild(tb_row);
+ }; // END of function: newLine
+ // init first line
+ newLine();
+
+ // updates the state of a toolbar element. This function is member of
+ // a toolbar element object (unnamed objects created by createButton or
+ // createSelect functions below).
+ function setButtonStatus(id, newval) {
+ var oldval = this[id];
+ var el = this.element;
+ if (oldval != newval) {
+ switch (id) {
+ case "enabled":
+ if (newval) {
+ HTMLArea._removeClass(el, "buttonDisabled");
+ el.disabled = false;
+ } else {
+ HTMLArea._addClass(el, "buttonDisabled");
+ el.disabled = true;
+ }
+ break;
+ case "active":
+ if (newval) {
+ HTMLArea._addClass(el, "buttonPressed");
+ } else {
+ HTMLArea._removeClass(el, "buttonPressed");
+ }
+ break;
+ }
+ this[id] = newval;
+ }
+ }; // END of function: setButtonStatus
+
+ // this function will handle creation of combo boxes. Receives as
+ // parameter the name of a button as defined in the toolBar config.
+ // This function is called from createButton, above, if the given "txt"
+ // doesn't match a button.
+ function createSelect(txt) {
+ var options = null;
+ var el = null;
+ var cmd = null;
+ var customSelects = editor.config.customSelects;
+ var context = null;
+ switch (txt) {
+ case "fontsize":
+ case "fontname":
+ case "formatblock":
+ // the following line retrieves the correct
+ // configuration option because the variable name
+ // inside the Config object is named the same as the
+ // button/select in the toolbar. For instance, if txt
+ // == "formatblock" we retrieve config.formatblock (or
+ // a different way to write it in JS is
+ // config["formatblock"].
+ options = editor.config[txt];
+ cmd = txt;
+ break;
+ default:
+ // try to fetch it from the list of registered selects
+ cmd = txt;
+ var dropdown = customSelects[cmd];
+ if (typeof dropdown != "undefined") {
+ options = dropdown.options;
+ context = dropdown.context;
+ } else {
+ alert("ERROR [createSelect]:\nCan't find the requested dropdown definition");
+ }
+ break;
+ }
+ if (options) {
+ el = document.createElement("select");
+ var obj = {
+ name : txt, // field name
+ element : el, // the UI element (SELECT)
+ enabled : true, // is it enabled?
+ text : false, // enabled in text mode?
+ cmd : cmd, // command ID
+ state : setButtonStatus, // for changing state
+ context : context
+ };
+ tb_objects[txt] = obj;
+ for (var i in options) {
+ var op = document.createElement("option");
+ op.appendChild(document.createTextNode(i));
+ op.value = options[i];
+ el.appendChild(op);
+ }
+ HTMLArea._addEvent(el, "change", function () {
+ editor._comboSelected(el, txt);
+ });
+ }
+ return el;
+ }; // END of function: createSelect
+
+ // appends a new button to toolbar
+ function createButton(txt) {
+ // the element that will be created
+ var el = null;
+ var btn = null;
+ switch (txt) {
+ case "separator":
+ el = document.createElement("div");
+ el.className = "separator";
+ break;
+ case "space":
+ el = document.createElement("div");
+ el.className = "space";
+ break;
+ case "linebreak":
+ newLine();
+ return false;
+ case "textindicator":
+ el = document.createElement("div");
+ el.appendChild(document.createTextNode("A"));
+ el.className = "indicator";
+ el.title = HTMLArea.I18N.tooltips.textindicator;
+ var obj = {
+ name : txt, // the button name (i.e. 'bold')
+ element : el, // the UI element (DIV)
+ enabled : true, // is it enabled?
+ active : false, // is it pressed?
+ text : false, // enabled in text mode?
+ cmd : "textindicator", // the command ID
+ state : setButtonStatus // for changing state
+ };
+ tb_objects[txt] = obj;
+ break;
+ default:
+ btn = editor.config.btnList[txt];
+ }
+ if (!el && btn) {
+ el = document.createElement("div");
+ el.title = btn[0];
+ el.className = "button";
+ // let's just pretend we have a button object, and
+ // assign all the needed information to it.
+ var obj = {
+ name : txt, // the button name (i.e. 'bold')
+ element : el, // the UI element (DIV)
+ enabled : true, // is it enabled?
+ active : false, // is it pressed?
+ text : btn[2], // enabled in text mode?
+ cmd : btn[3], // the command ID
+ state : setButtonStatus, // for changing state
+ context : btn[4] || null // enabled in a certain context?
+ };
+ tb_objects[txt] = obj;
+ // handlers to emulate nice flat toolbar buttons
+ HTMLArea._addEvent(el, "mouseover", function () {
+ if (obj.enabled) {
+ HTMLArea._addClass(el, "buttonHover");
+ }
+ });
+ HTMLArea._addEvent(el, "mouseout", function () {
+ if (obj.enabled) with (HTMLArea) {
+ _removeClass(el, "buttonHover");
+ _removeClass(el, "buttonActive");
+ (obj.active) && _addClass(el, "buttonPressed");
+ }
+ });
+ HTMLArea._addEvent(el, "mousedown", function (ev) {
+ if (obj.enabled) with (HTMLArea) {
+ _addClass(el, "buttonActive");
+ _removeClass(el, "buttonPressed");
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ // when clicked, do the following:
+ HTMLArea._addEvent(el, "click", function (ev) {
+ if (obj.enabled) with (HTMLArea) {
+ _removeClass(el, "buttonActive");
+ _removeClass(el, "buttonHover");
+ obj.cmd(editor, obj.name, obj);
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ var img = document.createElement("img");
+ img.src = btn[1];
+ img.style.width = "18px";
+ img.style.height = "18px";
+ el.appendChild(img);
+ } else if (!el) {
+ el = createSelect(txt);
+ }
+ if (el) {
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.appendChild(el);
+ } else {
+ alert("FIXME: Unknown toolbar item: " + txt);
+ }
+ return el;
+ };
+
+ var first = true;
+ for (var i in this.config.toolbar) {
+ if (!first) {
+ createButton("linebreak");
+ } else {
+ first = false;
+ }
+ var group = this.config.toolbar[i];
+ for (var j in group) {
+ var code = group[j];
+ if (/^([IT])\[(.*?)\]/.test(code)) {
+ // special case, create text label
+ var l7ed = RegExp.$1 == "I"; // localized?
+ var label = RegExp.$2;
+ if (l7ed) {
+ label = HTMLArea.I18N.custom[label];
+ }
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.className = "label";
+ tb_cell.innerHTML = label;
+ } else {
+ createButton(code);
+ }
+ }
+ }
+
+ this._htmlArea.appendChild(toolbar);
+};
+
+HTMLArea.prototype._createStatusBar = function() {
+ var statusbar = document.createElement("div");
+ statusbar.className = "statusBar";
+ this._htmlArea.appendChild(statusbar);
+ this._statusBar = statusbar;
+ statusbar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ // creates a holder for the path view
+ div = document.createElement("span");
+ div.className = "statusBarTree";
+ this._statusBarTree = div;
+ this._statusBar.appendChild(div);
+ if (!this.config.statusBar) {
+ // disable it...
+ statusbar.style.display = "none";
+ }
+};
+
+// Creates the HTMLArea object and replaces the textarea with it.
+HTMLArea.prototype.generate = function () {
+ var editor = this; // we'll need "this" in some nested functions
+ // get the textarea
+ var textarea = this._textArea;
+ if (typeof textarea == "string") {
+ // it's not element but ID
+ this._textArea = textarea = document.getElementById(textarea);
+ }
+ this._ta_size = {
+ w: textarea.offsetWidth,
+ h: textarea.offsetHeight
+ };
+ textarea.style.display = "none";
+
+ // create the editor framework
+ var htmlarea = document.createElement("div");
+ htmlarea.className = "htmlarea";
+ this._htmlArea = htmlarea;
+
+ // insert the editor before the textarea.
+ textarea.parentNode.insertBefore(htmlarea, textarea);
+
+ if (textarea.form) {
+ // we have a form, on submit get the HTMLArea content and
+ // update original textarea.
+ var f = textarea.form;
+ if (typeof f.onsubmit == "function") {
+ var funcref = f.onsubmit;
+ if (typeof f.__msh_prevOnSubmit == "undefined") {
+ f.__msh_prevOnSubmit = [];
+ }
+ f.__msh_prevOnSubmit.push(funcref);
+ }
+ f.onsubmit = function() {
+ editor._textArea.value = editor.getHTML();
+ var a = this.__msh_prevOnSubmit;
+ // call previous submit methods if they were there.
+ if (typeof a != "undefined") {
+ for (var i in a) {
+ a[i]();
+ }
+ }
+ };
+ }
+
+ // add a handler for the "back/forward" case -- on body.unload we save
+ // the HTML content into the original textarea.
+ window.onunload = function() {
+ editor._textArea.value = editor.getHTML();
+ };
+
+ // creates & appends the toolbar
+ this._createToolbar();
+
+ // create the IFRAME
+ var iframe = document.createElement("iframe");
+ htmlarea.appendChild(iframe);
+
+ this._iframe = iframe;
+
+ // creates & appends the status bar, if the case
+ this._createStatusBar();
+
+ // remove the default border as it keeps us from computing correctly
+ // the sizes. (somebody tell me why doesn't this work in IE)
+
+ if (!HTMLArea.is_ie) {
+ iframe.style.borderWidth = "1px";
+ // iframe.frameBorder = "1";
+ // iframe.marginHeight = "0";
+ // iframe.marginWidth = "0";
+ }
+
+ // size the IFRAME according to user's prefs or initial textarea
+ var height = (this.config.height == "auto" ? (this._ta_size.h + "px") : this.config.height);
+ height = parseInt(height);
+ var width = (this.config.width == "auto" ? (this._ta_size.w + "px") : this.config.width);
+ width = parseInt(width);
+
+ if (!HTMLArea.is_ie) {
+ height -= 2;
+ width -= 2;
+ }
+
+ iframe.style.width = width + "px";
+ if (this.config.sizeIncludesToolbar) {
+ // substract toolbar height
+ height -= this._toolbar.offsetHeight;
+ height -= this._statusBar.offsetHeight;
+ }
+ if (height < 0) {
+ height = 0;
+ }
+ iframe.style.height = height + "px";
+
+ // the editor including the toolbar now have the same size as the
+ // original textarea.. which means that we need to reduce that a bit.
+ textarea.style.width = iframe.style.width;
+ textarea.style.height = iframe.style.height;
+
+ // IMPORTANT: we have to allow Mozilla a short time to recognize the
+ // new frame. Otherwise we get a stupid exception.
+ function initIframe() {
+ var doc = editor._iframe.contentWindow.document;
+ if (!doc) {
+ // Try again..
+ // FIXME: don't know what else to do here. Normally
+ // we'll never reach this point.
+ if (HTMLArea.is_gecko) {
+ setTimeout(initIframe, 10);
+ return false;
+ } else {
+ alert("ERROR: IFRAME can't be initialized.");
+ }
+ }
+ if (HTMLArea.is_gecko) {
+ // enable editable mode for Mozilla
+ doc.designMode = "on";
+ }
+ editor._doc = doc;
+ if (!editor.config.fullPage) {
+ doc.open();
+ var html = "<html>\n";
+ html += "<head>\n";
+ html += "<style>" + editor.config.pageStyle + "</style>\n";
+ html += "</head>\n";
+ html += "<body>\n";
+ html += editor._textArea.value;
+ html += "</body>\n";
+ html += "</html>";
+ doc.write(html);
+ doc.close();
+ } else {
+ var html = editor._textArea.value;
+ if (html.match(HTMLArea.RE_doctype)) {
+ editor.setDoctype(RegExp.$1);
+ html = html.replace(HTMLArea.RE_doctype, "");
+ }
+ doc.open();
+ doc.write(html);
+ doc.close();
+ }
+
+ if (HTMLArea.is_ie) {
+ // enable editable mode for IE. For some reason this
+ // doesn't work if done in the same place as for Gecko
+ // (above).
+ doc.body.contentEditable = true;
+ }
+
+ editor.focusEditor();
+ // intercept some events; for updating the toolbar & keyboard handlers
+ HTMLArea._addEvents
+ (doc, ["keydown", "keypress", "mousedown", "mouseup", "drag"],
+ function (event) {
+ return editor._editorEvent(HTMLArea.is_ie ? editor._iframe.contentWindow.event : event);
+ });
+ editor.updateToolbar();
+ };
+ setTimeout(initIframe, HTMLArea.is_gecko ? 10 : 0);
+};
+
+// Switches editor mode; parameter can be "textmode" or "wysiwyg". If no
+// parameter was passed this function toggles between modes.
+HTMLArea.prototype.setMode = function(mode) {
+ if (typeof mode == "undefined") {
+ mode = ((this._editMode == "textmode") ? "wysiwyg" : "textmode");
+ }
+ switch (mode) {
+ case "textmode":
+ this._textArea.value = this.getHTML();
+ this._iframe.style.display = "none";
+ this._textArea.style.display = "block";
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = HTMLArea.I18N.msg["TEXT_MODE"];
+ }
+ break;
+ case "wysiwyg":
+ if (HTMLArea.is_gecko) {
+ // disable design mode before changing innerHTML
+ this._doc.designMode = "off";
+ }
+ if (!this.config.fullPage)
+ this._doc.body.innerHTML = this.getHTML();
+ else
+ this.setFullHTML(this.getHTML());
+ this._iframe.style.display = "block";
+ this._textArea.style.display = "none";
+ if (HTMLArea.is_gecko) {
+ // we need to refresh that info for Moz-1.3a
+ this._doc.designMode = "on";
+ }
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = '';
+ this._statusBar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ this._statusBar.appendChild(this._statusBarTree);
+ }
+ break;
+ default:
+ alert("Mode <" + mode + "> not defined!");
+ return false;
+ }
+ this._editMode = mode;
+ this.focusEditor();
+};
+
+HTMLArea.prototype.setFullHTML = function(html) {
+ var save_multiline = RegExp.multiline;
+ RegExp.multiline = true;
+ if (html.match(HTMLArea.RE_doctype)) {
+ this.setDoctype(RegExp.$1);
+ html = html.replace(HTMLArea.RE_doctype, "");
+ }
+ RegExp.multiline = save_multiline;
+ if (!HTMLArea.is_ie) {
+ if (html.match(HTMLArea.RE_head))
+ this._doc.getElementsByTagName("head")[0].innerHTML = RegExp.$1;
+ if (html.match(HTMLArea.RE_body))
+ this._doc.getElementsByTagName("body")[0].innerHTML = RegExp.$1;
+ } else {
+ var html_re = /<html>((.|\n)*?)<\/html>/i;
+ html = html.replace(html_re, "$1");
+ this._doc.open();
+ this._doc.write(html);
+ this._doc.close();
+ this._doc.body.contentEditable = true;
+ return true;
+ }
+};
+
+/***************************************************
+ * Category: PLUGINS
+ ***************************************************/
+
+// Create the specified plugin and register it with this HTMLArea
+HTMLArea.prototype.registerPlugin = function() {
+ var plugin = arguments[0];
+ if (typeof plugin == "string")
+ plugin = eval(plugin);
+ var args = [];
+ for (var i = 1; i < arguments.length; ++i)
+ args.push(arguments[i]);
+ var obj = new plugin(this, args);
+ if (obj) {
+ var clone = {};
+ var info = plugin._pluginInfo;
+ for (var i in info)
+ clone[i] = info[i];
+ clone.instance = obj;
+ this.plugins[plugin._pluginInfo.name] = clone;
+ } else
+ alert("Can't register plugin " + plugin.toString() + ".");
+};
+
+// static function that loads the required plugin and lang file, based on the
+// language loaded already for HTMLArea. You better make sure that the plugin
+// _has_ that language, otherwise shit might happen ;-)
+HTMLArea.loadPlugin = function(pluginName) {
+ var editorurl = '';
+ if (typeof _editor_url != "undefined") {
+ editorurl = _editor_url + "/";
+ }
+ var dir = editorurl + "plugins/" + pluginName;
+ var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g,
+ function (str, l1, l2, l3) {
+ return l1 + "-" + l2.toLowerCase() + l3;
+ }).toLowerCase() + ".js";
+ document.write("<script type='text/javascript' src='" + dir + "/" + plugin + "'></script>");
+ document.write("<script type='text/javascript' src='" + dir + "/lang/" + HTMLArea.I18N.lang + ".js'></script>");
+};
+
+/***************************************************
+ * Category: EDITOR UTILITIES
+ ***************************************************/
+
+HTMLArea.prototype.forceRedraw = function() {
+ this._doc.body.style.visibility = "hidden";
+ this._doc.body.style.visibility = "visible";
+ // this._doc.body.innerHTML = this.getInnerHTML();
+};
+
+// focuses the iframe window. returns a reference to the editor document.
+HTMLArea.prototype.focusEditor = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : this._iframe.contentWindow.focus(); break;
+ case "textmode": this._textArea.focus(); break;
+ default : alert("ERROR: mode " + this._editMode + " is not defined");
+ }
+ return this._doc;
+};
+
+// takes a snapshot of the current text (for undo)
+HTMLArea.prototype._undoTakeSnapshot = function() {
+ ++this._undoPos;
+ if (this._undoPos >= this.config.undoSteps) {
+ // remove the first element
+ this._undoQueue.shift();
+ --this._undoPos;
+ }
+ // use the fasted method (getInnerHTML);
+ var take = true;
+ var txt = this.getInnerHTML();
+ if (this._undoPos > 0)
+ take = (this._undoQueue[this._undoPos - 1] != txt);
+ if (take) {
+ this._undoQueue[this._undoPos] = txt;
+ } else {
+ this._undoPos--;
+ }
+};
+
+HTMLArea.prototype.undo = function() {
+ if (this._undoPos > 0) {
+ var txt = this._undoQueue[--this._undoPos];
+ if (txt) this.setHTML(txt);
+ else ++this._undoPos;
+ }
+};
+
+HTMLArea.prototype.redo = function() {
+ if (this._undoPos < this._undoQueue.length - 1) {
+ var txt = this._undoQueue[++this._undoPos];
+ if (txt) this.setHTML(txt);
+ else --this._undoPos;
+ }
+};
+
+// updates enabled/disable/active state of the toolbar elements
+HTMLArea.prototype.updateToolbar = function(noStatus) {
+ var doc = this._doc;
+ var text = (this._editMode == "textmode");
+ var ancestors = null;
+ if (!text) {
+ ancestors = this.getAllAncestors();
+ if (this.config.statusBar && !noStatus) {
+ this._statusBarTree.innerHTML = ''; // clear
+ for (var i = ancestors.length; --i >= 0;) {
+ var el = ancestors[i];
+ if (!el) {
+ // hell knows why we get here; this
+ // could be a classic example of why
+ // it's good to check for conditions
+ // that are impossible to happen ;-)
+ continue;
+ }
+ var a = document.createElement("a");
+ a.href = "#";
+ a.el = el;
+ a.editor = this;
+ a.onclick = function() {
+ this.blur();
+ this.editor.selectNodeContents(this.el);
+ this.editor.updateToolbar(true);
+ return false;
+ };
+ a.oncontextmenu = function() {
+ // TODO: add context menu here
+ this.blur();
+ var info = "Inline style:\n\n";
+ info += this.el.style.cssText.split(/;\s*/).join(";\n");
+ alert(info);
+ return false;
+ };
+ var txt = el.tagName.toLowerCase();
+ a.title = el.style.cssText;
+ if (el.id) {
+ txt += "#" + el.id;
+ }
+ if (el.className) {
+ txt += "." + el.className;
+ }
+ a.appendChild(document.createTextNode(txt));
+ this._statusBarTree.appendChild(a);
+ if (i != 0) {
+ this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
+ }
+ }
+ }
+ }
+ for (var i in this._toolbarObjects) {
+ var btn = this._toolbarObjects[i];
+ var cmd = i;
+ var inContext = true;
+ if (btn.context && !text) {
+ inContext = false;
+ var context = btn.context;
+ var attrs = [];
+ if (/(.*)\[(.*?)\]/.test(context)) {
+ context = RegExp.$1;
+ attrs = RegExp.$2.split(",");
+ }
+ context = context.toLowerCase();
+ var match = (context == "*");
+ for (var k in ancestors) {
+ if (!ancestors[k]) {
+ // the impossible really happens.
+ continue;
+ }
+ if (match || (ancestors[k].tagName.toLowerCase() == context)) {
+ inContext = true;
+ for (var ka in attrs) {
+ if (!eval("ancestors[k]." + attrs[ka])) {
+ inContext = false;
+ break;
+ }
+ }
+ if (inContext) {
+ break;
+ }
+ }
+ }
+ }
+ btn.state("enabled", (!text || btn.text) && inContext);
+ if (typeof cmd == "function") {
+ continue;
+ }
+ // look-it-up in the custom dropdown boxes
+ var dropdown = this.config.customSelects[cmd];
+ if ((!text || btn.text) && (typeof dropdown != "undefined")) {
+ dropdown.refresh(this);
+ continue;
+ }
+ switch (cmd) {
+ case "fontname":
+ case "fontsize":
+ case "formatblock":
+ if (!text) {
+ var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
+ if (!value) {
+ // FIXME: what do we do here?
+ break;
+ }
+ // HACK -- retrieve the config option for this
+ // combo box. We rely on the fact that the
+ // variable in config has the same name as
+ // button name in the toolbar.
+ var options = this.config[cmd];
+ var k = 0;
+ // btn.element.selectedIndex = 0;
+ for (var j in options) {
+ // FIXME: the following line is scary.
+ if ((j.toLowerCase() == value) ||
+ (options[j].substr(0, value.length).toLowerCase() == value)) {
+ btn.element.selectedIndex = k;
+ break;
+ }
+ ++k;
+ }
+ }
+ break;
+ case "textindicator":
+ if (!text) {
+ try {with (btn.element.style) {
+ backgroundColor = HTMLArea._makeColor(
+ doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor"));
+ if (/transparent/i.test(backgroundColor)) {
+ // Mozilla
+ backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor"));
+ }
+ color = HTMLArea._makeColor(doc.queryCommandValue("forecolor"));
+ fontFamily = doc.queryCommandValue("fontname");
+ fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
+ fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
+ }} catch (e) {
+ // alert(e + "\n\n" + cmd);
+ }
+ }
+ break;
+ case "htmlmode": btn.state("active", text); break;
+ default:
+ try {
+ btn.state("active", (!text && doc.queryCommandState(cmd)));
+ } catch (e) {}
+ }
+ }
+ // take undo snapshots
+ if (!this._timerUndo) {
+ this._undoTakeSnapshot();
+ var editor = this;
+ this._timerUndo = setTimeout(function() {
+ editor._timerUndo = null;
+ }, this.config.undoTimeout);
+ }
+ // check if any plugins have registered refresh handlers
+ for (var i in this.plugins) {
+ var plugin = this.plugins[i].instance;
+ if (typeof plugin.onUpdateToolbar == "function")
+ plugin.onUpdateToolbar();
+ }
+};
+
+/** Returns a node after which we can insert other nodes, in the current
+ * selection. The selection is removed. It splits a text node, if needed.
+ */
+HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted) {
+ if (!HTMLArea.is_ie) {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ // remove the current selection
+ sel.removeAllRanges();
+ range.deleteContents();
+ var node = range.startContainer;
+ var pos = range.startOffset;
+ switch (node.nodeType) {
+ case 3: // Node.TEXT_NODE
+ // we have to split it at the caret position.
+ if (toBeInserted.nodeType == 3) {
+ // do optimized insertion
+ node.insertData(pos, toBeInserted.data);
+ range = this._createRange();
+ range.setEnd(node, pos + toBeInserted.length);
+ range.setStart(node, pos + toBeInserted.length);
+ sel.addRange(range);
+ } else {
+ node = node.splitText(pos);
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.parentNode.insertBefore(toBeInserted, node);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ }
+ break;
+ case 1: // Node.ELEMENT_NODE
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.insertBefore(toBeInserted, node.childNodes[pos]);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ break;
+ }
+ } else {
+ return null; // this function not yet used for IE <FIXME>
+ }
+};
+
+// Returns the deepest node that contains both endpoints of the selection.
+HTMLArea.prototype.getParentElement = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ return range.parentElement ? range.parentElement() : this._doc.body;
+ } else {
+ var p = range.commonAncestorContainer;
+ while (p.nodeType == 3) {
+ p = p.parentNode;
+ }
+ return p;
+ }
+};
+
+// Returns an array with all the ancestor nodes of the selection.
+HTMLArea.prototype.getAllAncestors = function() {
+ var p = this.getParentElement();
+ var a = [];
+ while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
+ a.push(p);
+ p = p.parentNode;
+ }
+ a.push(this._doc.body);
+ return a;
+};
+
+// Selects the contents inside the given node
+HTMLArea.prototype.selectNodeContents = function(node, pos) {
+ this.focusEditor();
+ this.forceRedraw();
+ var range;
+ var collapsed = (typeof pos != "undefined");
+ if (HTMLArea.is_ie) {
+ range = this._doc.body.createTextRange();
+ range.moveToElementText(node);
+ (collapsed) && range.collapse(pos);
+ range.select();
+ } else {
+ var sel = this._getSelection();
+ range = this._doc.createRange();
+ range.selectNodeContents(node);
+ (collapsed) && range.collapse(pos);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+};
+
+/** Call this function to insert HTML code at the current position. It deletes
+ * the selection, if any.
+ */
+HTMLArea.prototype.insertHTML = function(html) {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(html);
+ } else {
+ // construct a new document fragment with the given HTML
+ var fragment = this._doc.createDocumentFragment();
+ var div = this._doc.createElement("div");
+ div.innerHTML = html;
+ while (div.firstChild) {
+ // the following call also removes the node from div
+ fragment.appendChild(div.firstChild);
+ }
+ // this also removes the selection
+ var node = this.insertNodeAtSelection(fragment);
+ }
+};
+
+/**
+ * Call this function to surround the existing HTML code in the selection with
+ * your tags. FIXME: buggy! This function will be deprecated "soon".
+ */
+HTMLArea.prototype.surroundHTML = function(startTag, endTag) {
+ var html = this.getSelectedHTML();
+ // the following also deletes the selection
+ this.insertHTML(startTag + html + endTag);
+};
+
+/// Retrieve the selected block
+HTMLArea.prototype.getSelectedHTML = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var existing = null;
+ if (HTMLArea.is_ie) {
+ existing = range.htmlText;
+ } else {
+ existing = HTMLArea.getHTML(range.cloneContents(), false);
+ }
+ return existing;
+};
+
+// Called when the user clicks on "InsertImage" button
+HTMLArea.prototype._insertImage = function() {
+ var editor = this; // for nested functions
+ this._popupDialog("insert_image.php?id=<?php echo $id ?>", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var sel = editor._getSelection();
+ var range = editor._createRange(sel);
+ editor._doc.execCommand("insertimage", false, param["f_url"]);
+ var img = null;
+ if (HTMLArea.is_ie) {
+ img = range.parentElement();
+ // wonder if this works...
+ if (img.tagName.toLowerCase() != "img") {
+ img = img.previousSibling;
+ }
+ } else {
+ img = range.startContainer.previousSibling;
+ }
+ for (field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_alt" : img.alt = value; break;
+ case "f_border" : img.border = parseInt(value); break;
+ case "f_align" : img.align = value; break;
+ case "f_vert" : img.vspace = parseInt(value); break;
+ case "f_horiz" : img.hspace = parseInt(value); break;
+ }
+ }
+ }, null);
+};
+
+// Called when the user clicks the Insert Table button
+HTMLArea.prototype._insertTable = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var editor = this; // for nested functions
+ this._popupDialog("insert_table.php", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var doc = editor._doc;
+ // create the table element
+ var table = doc.createElement("table");
+ // assign the given arguments
+ for (var field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_width" : table.style.width = value + param["f_unit"]; break;
+ case "f_align" : table.align = value; break;
+ case "f_border" : table.border = parseInt(value); break;
+ case "f_spacing" : table.cellspacing = parseInt(value); break;
+ case "f_padding" : table.cellpadding = parseInt(value); break;
+ }
+ }
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+ for (var i = 0; i < param["f_rows"]; ++i) {
+ var tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ for (var j = 0; j < param["f_cols"]; ++j) {
+ var td = doc.createElement("td");
+ tr.appendChild(td);
+ // Mozilla likes to see something inside the cell.
+ (HTMLArea.is_gecko) && td.appendChild(doc.createElement("br"));
+ }
+ }
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(table.outerHTML);
+ } else {
+ // insert the table
+ editor.insertNodeAtSelection(table);
+ }
+ return true;
+ }, null);
+};
+
+/******************************************************************
+* Moodle hack - insertSmile
+******************************************************************/
+HTMLArea.prototype._insertSmile = function() {
+ var editor = this; // for nested functions
+ this._popupDialog("dlg_ins_smile.php", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var sel = editor._getSelection();
+ var range = editor._createRange(sel);
+ editor._doc.execCommand("insertimage", false, param["f_url"]);
+ var img = null;
+ if (HTMLArea.is_ie) {
+ img = range.parentElement();
+ // wonder if this works...
+ if (img.tagName.toLowerCase() != "img") {
+ img = img.previousSibling;
+ }
+ } else {
+ img = range.startContainer.previousSibling;
+ }
+ for (field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_alt" : img.alt = value; break;
+ case "f_border" : img.border = parseInt(value); break;
+ case "f_align" : img.align = value; break;
+ case "f_vert" : img.vspace = parseInt(value); break;
+ case "f_horiz" : img.hspace = parseInt(value); break;
+ }
+ }
+ }, null);
+};
+HTMLArea.prototype._insertChar = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var editor = this; // for nested functions
+ this._popupDialog("dlg_ins_char.php", function(sChar) {
+ if(!sChar) {
+ return false;
+ }
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(sChar);
+ } else {
+ // insert the table
+ editor.insertHTML(sChar);
+ }
+ return true;
+ }, null);
+};
+/************************************************************************
+* Moodle hack's ends
+************************************************************************/
+
+/***************************************************
+ * Category: EVENT HANDLERS
+ ***************************************************/
+
+// el is reference to the SELECT object
+// txt is the name of the select field, as in config.toolbar
+HTMLArea.prototype._comboSelected = function(el, txt) {
+ this.focusEditor();
+ var value = el.options[el.selectedIndex].value;
+ switch (txt) {
+ case "fontname":
+ case "fontsize": this.execCommand(txt, false, value); break;
+ case "formatblock":
+ (HTMLArea.is_ie) && (value = "<" + value + ">");
+ this.execCommand(txt, false, value);
+ break;
+ default:
+ // try to look it up in the registered dropdowns
+ var dropdown = this.config.customSelects[txt];
+ if (typeof dropdown != "undefined") {
+ dropdown.action(this);
+ } else {
+ alert("FIXME: combo box " + txt + " not implemented");
+ }
+ }
+};
+
+// the execCommand function (intercepts some commands and replaces them with
+// our own implementation)
+HTMLArea.prototype.execCommand = function(cmdID, UI, param) {
+ var editor = this; // for nested functions
+ this.focusEditor();
+ switch (cmdID.toLowerCase()) {
+ case "htmlmode" : this.setMode(); break;
+ case "hilitecolor":
+ (HTMLArea.is_ie) && (cmdID = "backcolor");
+ case "forecolor":
+ this._popupDialog("select_color.html", function(color) {
+ if (color) { // selection not canceled
+ editor._doc.execCommand(cmdID, false, "#" + color);
+ }
+ }, HTMLArea._colorToRgb(this._doc.queryCommandValue(cmdID)));
+ break;
+ case "createlink":
+ if (HTMLArea.is_ie || !UI) {
+ this._doc.execCommand(cmdID, UI, param);
+ } else {
+ // browser is Mozilla & wants UI
+ var param;
+ if ((param = prompt("Enter URL"))) {
+ this._doc.execCommand(cmdID, false, param);
+ }
+ }
+ break;
+ case "popupeditor":
+ if (HTMLArea.is_ie) {
+ window.open(this.popupURL("fullscreen.php?id=<?php echo $id ?>"), "ha_fullscreen",
+ "toolbar=no,location=no,directories=no,status=no,menubar=no," +
+ "scrollbars=no,resizable=yes,width=640,height=480");
+ } else {
+ window.open(this.popupURL("fullscreen.php?id=<?php echo $id ?>"), "ha_fullscreen",
+ "toolbar=no,menubar=no,personalbar=no,width=640,height=480," +
+ "scrollbars=no,resizable=yes");
+ }
+ // pass this object to the newly opened window
+ HTMLArea._object = this;
+ break;
+ case "undo": this.undo(); break;
+ case "redo": this.redo(); break;
+ case "inserttable": this._insertTable(); break;
+ case "insertimage": this._insertImage(); break;
+ case "insertsmile": this._insertSmile(); break;
+ case "insertchar": this._insertChar(); break;
+ case "about" : this._popupDialog("about.html", null, this); break;
+ case "showhelp" : window.open(this.config.editorURL + "reference.html", "ha_help"); break;
+ default: this._doc.execCommand(cmdID, UI, param);
+ }
+ this.updateToolbar();
+ return false;
+};
+
+/** A generic event handler for things that happen in the IFRAME's document.
+ * This function also handles key bindings. */
+HTMLArea.prototype._editorEvent = function(ev) {
+ var editor = this;
+ var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");
+ if (keyEvent && ev.ctrlKey) {
+ var sel = null;
+ var range = null;
+ var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
+ var cmd = null;
+ var value = null;
+ switch (key) {
+ case 'a':
+ if (!HTMLArea.is_ie) {
+ // KEY select all
+ sel = this._getSelection();
+ sel.removeAllRanges();
+ range = this._createRange();
+ range.selectNodeContents(this._doc.body);
+ sel.addRange(range);
+ HTMLArea._stopEvent(ev);
+ }
+ break;
+
+ // simple key commands follow
+
+ case 'b': cmd = "bold"; break;
+ case 'i': cmd = "italic"; break;
+ case 'u': cmd = "underline"; break;
+ case 's': cmd = "strikethrough"; break;
+ case 'l': cmd = "justifyleft"; break;
+ case 'e': cmd = "justifycenter"; break;
+ case 'r': cmd = "justifyright"; break;
+ case 'j': cmd = "justifyfull"; break;
+ case 'z': cmd = "undo"; break;
+ case 'y': cmd = "redo"; break;
+
+ // headings
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ cmd = "formatblock";
+ value = "h" + key;
+ if (HTMLArea.is_ie) {
+ value = "<" + value + ">";
+ }
+ break;
+ }
+ if (cmd) {
+ // execute simple command
+ this.execCommand(cmd, false, value);
+ HTMLArea._stopEvent(ev);
+ }
+ }
+ /*
+ else if (keyEvent) {
+ // other keys here
+ switch (ev.keyCode) {
+ case 13: // KEY enter
+ // if (HTMLArea.is_ie) {
+ this.insertHTML("<br />");
+ HTMLArea._stopEvent(ev);
+ // }
+ break;
+ }
+ }
+ */
+ // update the toolbar state after some time
+ if (editor._timerToolbar) {
+ clearTimeout(editor._timerToolbar);
+ }
+ editor._timerToolbar = setTimeout(function() {
+ editor.updateToolbar();
+ editor._timerToolbar = null;
+ }, 50);
+};
+
+// retrieve the HTML
+HTMLArea.prototype.getHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ return HTMLArea.getHTML(this._doc.body, false);
+ else
+ return this.doctype + "\n" + HTMLArea.getHTML(this._doc.documentElement, true);
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// retrieve the HTML (fastest version, but uses innerHTML)
+HTMLArea.prototype.getInnerHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ return this._doc.body.innerHTML;
+ else
+ return this.doctype + "\n" + this._doc.documentElement.innerHTML;
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// completely change the HTML inside
+HTMLArea.prototype.setHTML = function(html) {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ this._doc.body.innerHTML = html;
+ else
+ // this._doc.documentElement.innerHTML = html;
+ this._doc.body.innerHTML = html;
+ break;
+ case "textmode" : this._textArea.value = html; break;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// sets the given doctype (useful when config.fullPage is true)
+HTMLArea.prototype.setDoctype = function(doctype) {
+ this.doctype = doctype;
+};
+
+/***************************************************
+ * Category: UTILITY FUNCTIONS
+ ***************************************************/
+
+// browser identification
+
+HTMLArea.agt = navigator.userAgent.toLowerCase();
+HTMLArea.is_ie = ((HTMLArea.agt.indexOf("msie") != -1) && (HTMLArea.agt.indexOf("opera") == -1));
+HTMLArea.is_opera = (HTMLArea.agt.indexOf("opera") != -1);
+HTMLArea.is_mac = (HTMLArea.agt.indexOf("mac") != -1);
+HTMLArea.is_mac_ie = (HTMLArea.is_ie && HTMLArea.is_mac);
+HTMLArea.is_win_ie = (HTMLArea.is_ie && !HTMLArea.is_mac);
+HTMLArea.is_gecko = (navigator.product == "Gecko");
+
+// variable used to pass the object to the popup editor window.
+HTMLArea._object = null;
+
+// FIXME!!! this should return false for IE < 5.5
+HTMLArea.checkSupportedBrowser = function() {
+ if (HTMLArea.is_gecko) {
+ if (navigator.productSub < 20021201) {
+ alert("You need at least Mozilla-1.3 Alpha.\n" +
+ "Sorry, your Gecko is not supported.");
+ return false;
+ }
+ if (navigator.productSub < 20030210) {
+ alert("Mozilla < 1.3 Beta is not supported!\n" +
+ "I'll try, though, but it might not work.");
+ }
+ }
+ return HTMLArea.is_gecko || HTMLArea.is_ie;
+};
+
+// selection & ranges
+
+// returns the current selection object
+HTMLArea.prototype._getSelection = function() {
+ if (HTMLArea.is_ie) {
+ return this._doc.selection;
+ } else {
+ return this._iframe.contentWindow.getSelection();
+ }
+};
+
+// returns a range for the current selection
+HTMLArea.prototype._createRange = function(sel) {
+ if (HTMLArea.is_ie) {
+ return sel.createRange();
+ } else {
+ this.focusEditor();
+ if (typeof sel != "undefined") {
+ return sel.getRangeAt(0);
+ } else {
+ return this._doc.createRange();
+ }
+ }
+};
+
+// event handling
+
+HTMLArea._addEvent = function(el, evname, func) {
+ if (HTMLArea.is_ie) {
+ el.attachEvent("on" + evname, func);
+ } else {
+ el.addEventListener(evname, func, true);
+ }
+};
+
+HTMLArea._addEvents = function(el, evs, func) {
+ for (var i in evs) {
+ HTMLArea._addEvent(el, evs[i], func);
+ }
+};
+
+HTMLArea._removeEvent = function(el, evname, func) {
+ if (HTMLArea.is_ie) {
+ el.detachEvent("on" + evname, func);
+ } else {
+ el.removeEventListener(evname, func, true);
+ }
+};
+
+HTMLArea._removeEvents = function(el, evs, func) {
+ for (var i in evs) {
+ HTMLArea._removeEvent(el, evs[i], func);
+ }
+};
+
+HTMLArea._stopEvent = function(ev) {
+ if (HTMLArea.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+};
+
+HTMLArea._removeClass = function(el, className) {
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
+};
+
+HTMLArea._addClass = function(el, className) {
+ // remove the class first, if already there
+ HTMLArea._removeClass(el, className);
+ el.className += " " + className;
+};
+
+HTMLArea._hasClass = function(el, className) {
+ if (!(el && el.className)) {
+ return false;
+ }
+ var cls = el.className.split(" ");
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] == className) {
+ return true;
+ }
+ }
+ return false;
+};
+
+HTMLArea.isBlockElement = function(el) {
+ var blockTags = " body form textarea fieldset ul ol dl li div " +
+ "p h1 h2 h3 h4 h5 h6 quote pre table thead " +
+ "tbody tfoot tr td iframe address ";
+ return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+HTMLArea.needsClosingTag = function(el) {
+ var closingTags = " head script style div span tr td tbody table em strong font a title ";
+ return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+// performs HTML encoding of some given string
+HTMLArea.htmlEncode = function(str) {
+ // we don't need regexp for that, but.. so be it for now.
+ str = str.replace(/&/ig, "&");
+ str = str.replace(/</ig, "<");
+ str = str.replace(/>/ig, ">");
+ str = str.replace(/\x22/ig, """);
+ // \x22 means '"' -- we use hex reprezentation so that we don't disturb
+ // JS compressors (well, at least mine fails.. ;)
+ return str;
+};
+
+// Retrieves the HTML code from the given node. This is a replacement for
+// getting innerHTML, using standard DOM calls.
+HTMLArea.getHTML = function(root, outputRoot) {
+ var html = "";
+ switch (root.nodeType) {
+ case 1: // Node.ELEMENT_NODE
+ case 11: // Node.DOCUMENT_FRAGMENT_NODE
+ var closed;
+ var i;
+ var root_tag = root.tagName.toLowerCase();
+ if (HTMLArea.is_ie && root_tag == "head") {
+ if (outputRoot)
+ html += "<head>";
+ // lowercasize
+ var save_multiline = RegExp.multiline;
+ RegExp.multiline = true;
+ var txt = root.innerHTML.replace(HTMLArea.RE_tagName, function(str, p1, p2) {
+ return p1 + p2.toLowerCase();
+ });
+ RegExp.multiline = save_multiline;
+ html += txt;
+ if (outputRoot)
+ html += "</head>";
+ break;
+ } else if (outputRoot) {
+ closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));
+ html = "<" + root.tagName.toLowerCase();
+ var attrs = root.attributes;
+ for (i = 0; i < attrs.length; ++i) {
+ var a = attrs.item(i);
+ if (!a.specified) {
+ continue;
+ }
+ var name = a.nodeName.toLowerCase();
+ if (/_moz|contenteditable/.test(name)) {
+ // avoid certain attributes
+ continue;
+ }
+ var value;
+ if (name != "style") {
+ // IE5.5 reports 25 when cellSpacing is
+ // 1; other values might be doomed too.
+ // For this reason we extract the
+ // values directly from the root node.
+ // I'm starting to HATE JavaScript
+ // development. Browser differences
+ // suck.
+ if (typeof root[a.nodeName] != "undefined") {
+ value = root[a.nodeName];
+ } else {
+ value = a.nodeValue;
+ }
+ } else { // IE fails to put style in attributes list
+ // FIXME: cssText reported by IE is UPPERCASE
+ value = root.style.cssText;
+ }
+ if (/_moz/.test(value)) {
+ // Mozilla reports some special tags
+ // here; we don't need them.
+ continue;
+ }
+ html += " " + name + '="' + value + '"';
+ }
+ html += closed ? " />" : ">";
+ }
+ for (i = root.firstChild; i; i = i.nextSibling) {
+ html += HTMLArea.getHTML(i, true);
+ }
+ if (outputRoot && !closed) {
+ html += "</" + root.tagName.toLowerCase() + ">";
+ }
+ break;
+ case 3: // Node.TEXT_NODE
+ html = HTMLArea.htmlEncode(root.data);
+ break;
+ case 8: // Node.COMMENT_NODE
+ html = "<!--" + root.data + "-->";
+ break; // skip comments, for now.
+ }
+ return html;
+};
+
+// creates a rgb-style color from a number
+HTMLArea._makeColor = function(v) {
+ if (typeof v != "number") {
+ // already in rgb (hopefully); IE doesn't get here.
+ return v;
+ }
+ // IE sends number; convert to rgb.
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "rgb(" + r + "," + g + "," + b + ")";
+};
+
+// returns hexadecimal color representation from a number or a rgb-style color.
+HTMLArea._colorToRgb = function(v) {
+ if (!v)
+ return '';
+
+ // returns the hex representation of one byte (2 digits)
+ function hex(d) {
+ return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
+ };
+
+ if (typeof v == "number") {
+ // we're talking to IE here
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+
+ if (v.substr(0, 3) == "rgb") {
+ // in rgb(...) form -- Mozilla
+ var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
+ if (v.match(re)) {
+ var r = parseInt(RegExp.$1);
+ var g = parseInt(RegExp.$2);
+ var b = parseInt(RegExp.$3);
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+ // doesn't match RE?! maybe uses percentages or float numbers
+ // -- FIXME: not yet implemented.
+ return null;
+ }
+
+ if (v.substr(0, 1) == "#") {
+ // already hex rgb (hopefully :D )
+ return v;
+ }
+
+ // if everything else fails ;)
+ return null;
+};
+
+// modal dialogs for Mozilla (for IE we're using the showModalDialog() call).
+
+// receives an URL to the popup dialog and a function that receives one value;
+// this function will get called after the dialog is closed, with the return
+// value of the dialog.
+HTMLArea.prototype._popupDialog = function(url, action, init) {
+ Dialog(this.popupURL(url), action, init);
+};
+
+// paths
+
+HTMLArea.prototype.imgURL = function(file, plugin) {
+ if (typeof plugin == "undefined")
+ return this.config.editorURL + file;
+ else
+ return this.config.editorURL + "plugins/" + plugin + "/img/" + file;
+};
+
+HTMLArea.prototype.popupURL = function(file) {
+ var url = "";
+ if (file.match(/^plugin:\/\/(.*?)\/(.*)/)) {
+ var plugin = RegExp.$1;
+ var popup = RegExp.$2;
+ if (!/\.html$/.test(popup))
+ popup += ".html";
+ url = this.config.editorURL + "plugins/" + plugin + "/popups/" + popup;
+ } else
+ url = this.config.editorURL + this.config.popupURL + file;
+ return url;
+};
+
+// EOF
+// Local variables: //
+// c-basic-offset:8 //
+// indent-tabs-mode:t //
+// End: //
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 3.2//EN">
+<html>
+ <head>
+ <title>HTMLArea -- the free, customizable online editor</title>
+
+ <style type="text/css">
+ html, body { font-family: georgia,"times new roman",serif; background-color: #fff; color: #000; }
+ .label { text-align: right; padding-right: 0.3em; }
+ .bline { border-bottom: 1px solid #aaa; }
+ </style>
+ </head>
+
+ <body>
+ <div style="float: right; border: 1px solid #aaa; background-color: #eee; padding: 3px; margin-left: 10px; margin-bottom: 10px;">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td class="label">Version:</td><td>3.0</td>
+ </tr>
+ <tr>
+ <td class="label">Release:</td><td>beta (<a href="release-notes.html">release notes</a>)</td>
+ </tr>
+ <tr>
+ <td class="label bline">Compiled at:</td><td class="bline">Aug 11, 2003 [21:30] GMT</td>
+ </tr>
+ <tr>
+ <td class="label">SourceForge page:</td><td><a href="http://sf.net/projects/itools-htmlarea/">http://sf.net/projects/itools-htmlarea/</a></td>
+ </table>
+ </div>
+ <h1>HTMLArea -- the free<br/>customizable online editor</h1>
+
+ <p>
+ HTMLArea is a free, customizable online editor. It works inside your
+ browser. It uses a non-standard feature implemented in Internet
+ Explorer 5.5 or better for Windows and Mozilla 1.3 or better (any
+ platform), therefore it will only work in one of these browsers.
+ </p>
+
+ <p>
+ HTMLArea is copyright <a
+ href="http://interactivetools.com">InteractiveTools.com</a> and
+ released under a BSD-style license. HTMLArea is created and developed
+ upto version 2.03 by InteractiveTools.com. Version 3.0 developed by
+ <a href="http://students.infoiasi.ro/~mishoo/">Mihai Bazon</a> for
+ InteractiveTools. It contains code sponsored by other companies as
+ well.
+ </p>
+
+ <h2>Online demos</h2>
+
+ <ul>
+
+ <li><a href="example.html">HTMLArea standard</a> -- contains the core
+ editor.</li>
+
+ <li><a href="example-table-operations.html">HTMLArea + tables</a> --
+ loads the <tt>TableOperations</tt> plugin, sponsored by <a
+ href="http://bloki.com">Zapatec Inc.</a></li>
+
+ <li><a href="example-spell-checker.html">HTMLArea + spell checher</a>
+ -- loads the <tt>SpellChecker</tt> plugin, sponsored by <a
+ href="http://americanbible.org">American Bible Society</a>.</li>
+
+ <li><a href="example-fully-loaded.html">HTMLArea fully loaded</a> ;-)</li>
+
+ </ul>
+
+ <h2>Installation</h2>
+
+ <p>
+ Installation is (or should be) easy. You need to unpack the ZIP file
+ in a directory accessible through your webserver. Supposing you
+ unpack in your <tt>DocumentRoot</tt> and your <tt>DocumentRoot</tt> is
+ <tt>/var/www/html</tt> as in a standard RedHat installation, you need
+ to acomplish the following steps: (the example is for a Unix-like
+ operating system)
+ </p>
+
+ <pre style="margin-left: 2em"
+>
+cd /var/www/html
+unzip /path/to/archive/HTMLArea-3.0-beta.zip
+mv HTMLArea-3.0-beta htmlarea
+find htmlarea/ -type f -exec chmod 644 {} \;
+find htmlarea/ -type d -exec chmod 755 {} \;
+find htmlarea/ -name "*.cgi" -exec chmod 755 {} \;</pre>
+
+ <p>
+ <strong>Notes.</strong> You may chose to symlink "htmlarea" to "HTMLArea-3.0-beta", in which case your server needs to be configured to
+ "<tt>FollowSymLinks</tt>". You need to make sure that *.cgi files are
+ interpreted as CGI scripts. If you want to use the SpellChecker
+ plugin you need to have a recent version of Perl installed (I
+ recommend 5.8.0) on the server, and the module Text::Aspell, available
+ from CPAN. More info in "<a
+ href="plugins/SpellChecker/readme-tech.html">plugins/SpellChecker/readme-tech.html</a>".
+ </p>
+
+ <p>About how to setup your pages to use the editor, please read the
+ [outdated yet generally valid] <a
+ href="reference.html">documentation</a>.</p>
+
+ <h2>Status and links</h2>
+
+ <p>HTMLArea has reached version 3.0. As of this version, it
+ supports:</p>
+
+ <ul>
+
+ <li>Customizable toolbar</li>
+
+ <li>Easy internationalization</li>
+
+ <li>Plugin-based infrastructure</li>
+
+ <li>Delivers W3-compliant HTML (with few exceptions)</li>
+
+ <li>Has a subset of Microsoft Word's keyboard shortcuts</li>
+
+ <li>Full-screen editor</li>
+
+ <li>Advanced table operations (by external plugin
+ "TableOperations")</li>
+
+ <li>Spell checker (by external plugin "SpellChecker")</li>
+
+ <li>probably more... ;-)</li>
+
+ </ul>
+
+ <p>We have a <a
+ href="http://sourceforge.net/projects/itools-htmlarea/">project page</a>
+ at <a href="http://sourceforge.net">SourceForge.net</a>. There you can
+ also find out <a href="http://sourceforge.net/cvs/?group_id=69750">how
+ to retrieve the code from CVS</a>, or you can <a
+ href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/itools-htmlarea">browse
+ the CVS online</a>. We also have a <a
+ href="http://sourceforge.net/tracker/?atid=525656&group_id=69750&func=browse">bug
+ system</a>, a <a
+ href="http://sourceforge.net/tracker/?atid=525658&group_id=69750&func=browse">patch
+ tracking system</a> and a <a
+ href="http://sourceforge.net/tracker/?atid=525659&group_id=69750&func=browse">feature
+ request page</a>.</p>
+
+ <p>We invite you to say everything you want about HTMLArea <a
+ href="http://www.interactivetools.com/forum/gforum.cgi?forum=14;">on the
+ forums</a> at InteractiveTools.com. There you should also find the
+ latest news.</p>
+
+ <p>Sometimes I post news about the latest developments on <a
+ href="http://students.infoiasi.ro/~mishoo/">my personal homepage</a>.</p>
+
+ <h2>"It doesn't work, what's wrong?"</h2>
+
+ <p>If it doesn't work, you have several options:</p>
+
+ <ul>
+
+ <li>Post a message to the forum. Describe your problem in as much
+ detail as possible. Include errors you might find in the JavaScript
+ console (if you are a Mozilla user), or errors displayed by IE (though
+ they're most of the times useless).</li>
+
+ <li>If you're positive that you discovered a bug in HTMLArea then feel
+ free to fill a bug report in our bug system. If you have the time you
+ should check to see if a similar bug was reported or not; it might be
+ fixed already in the CVS ;-) If you're positive that a similar bug was
+ not yet reported, do fill a bug report and please include as much
+ detail as possible, such as your browser, OS, errors from JavaScript
+ console, etc.</li>
+
+ <li>If you want a new feature to be implemented, post it on the
+ features request and someone will hopefully take care of it.</li>
+
+ </ul>
+
+ <p>You can <a href="mailto:mishoo@infoiasi.ro">contact me directly</a>
+ <em>only</em> if you want to pay me for implementing custom features to
+ HTMLArea. If you want to sponsor these features (that is, allow them to
+ get back into the public HTMLArea distribution) I'll be cheaper. ;-)</p>
+
+ <hr />
+ <address><a href="http://students.infoiasi.ro/~mishoo/">Mihai Bazon</a></address>
+<!-- Created: Sun Aug 3 14:11:26 EEST 2003 -->
+<!-- hhmts start -->
+Last modified on Tue Aug 12 00:23:26 2003
+<!-- hhmts end -->
+<!-- doc-lang: English -->
+ </body>
+</html>
+
+
--- /dev/null
+// I18N constants -- Chinese Big-5
+// by Dave Lo -- dlo@interactivetools.com
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "b5",
+
+ tooltips: {
+ bold: "²ÊÅé",
+ italic: "±×Åé",
+ underline: "©³½u",
+ strikethrough: "§R°£½u",
+ subscript: "¤U¼Ð",
+ superscript: "¤W¼Ð",
+ justifyleft: "¦ì¸m¾a¥ª",
+ justifycenter: "¦ì¸m©~¤¤",
+ justifyright: "¦ì¸m¾a¥k",
+ justifyfull: "¦ì¸m¥ª¥k¥µ¥",
+ orderedlist: "¶¶§Ç²M³æ",
+ unorderedlist: "µL§Ç²M³æ",
+ outdent: "´î¤p¦æ«eªÅ¥Õ",
+ indent: "¥[¼e¦æ«eªÅ¥Õ",
+ forecolor: "¤å¦rÃC¦â",
+ backcolor: "I´ºÃC¦â",
+ horizontalrule: "¤ô¥½u",
+ createlink: "´¡¤J³sµ²",
+ insertimage: "´¡¤J¹Ï§Î",
+ inserttable: "´¡¤Jªí®æ",
+ htmlmode: "¤Á´«HTMLì©l½X",
+ popupeditor: "©ñ¤j",
+ about: "Ãö©ó HTMLArea",
+ help: "»¡©ú",
+ textindicator: "¦rÅé¨Ò¤l"
+ }
+};
--- /dev/null
+// danish version for htmlArea v3.0 - Alpha Release
+// - translated by rene<rene@laerke.net>
+// term´s and licenses are equal to htmlarea!
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "da",
+
+ tooltips: {
+ bold: "Fed",
+ italic: "Kursiv",
+ underline: "Understregning",
+ strikethrough: "Overstregning ",
+ subscript: "Sænket skrift",
+ superscript: "Hævet skrift",
+ justifyleft: "Venstrejuster",
+ justifycenter: "Centrer",
+ justifyright: "Højrejuster",
+ justifyfull: "Lige margener",
+ orderedlist: "Opstilling med tal",
+ unorderedlist: "Opstilling med punkttegn",
+ outdent: "Formindsk indrykning",
+ indent: "Forøg indrykning",
+ forecolor: "Skriftfarve",
+ backcolor: "Baggrundsfarve",
+ horizontalrule: "Horisontal linie",
+ createlink: "Indsæt hyperlink",
+ insertimage: "Indsæt billede",
+ inserttable: "Indsæt tabel",
+ htmlmode: "HTML visning",
+ popupeditor: "Vis editor i popup",
+ about: "Om htmlarea",
+ help: "Hjælp",
+ textindicator: "Anvendt stil"
+ }
+};
--- /dev/null
+// german version for htmlArea v3.0 - Alpha Release
+// - translated by AtK<atk@chello.at>
+// term´s and licenses are equal to htmlarea!
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "de",
+
+ tooltips: {
+ bold: "Fett",
+ italic: "Kursiv",
+ underline: "Unterstrichen",
+ strikethrough: "Durchgestrichen",
+ subscript: "hochgestellt",
+ superscript: "tiefgestellt",
+ justifyleft: "Links ausrichten",
+ justifycenter: "Zentrieren",
+ justifyright: "Rechts ausrichten",
+ justifyfull: "Blocksatz",
+ orderedlist: "Nummerierung",
+ unorderedlist: "Aufzählungszeichen",
+ outdent: "Einzug verkleinern",
+ indent: "Einzug vergrössern",
+ forecolor: "Text Farbe",
+ backcolor: "Hintergrund Farbe",
+ horizontalrule: "Horizontale Linie",
+ createlink: "Hyperlink einfügen",
+ insertimage: "Bild einfügen",
+ inserttable: "Tabelle einfügen",
+ htmlmode: "HTML Modus",
+ popupeditor: "Editor im Popup öffnen",
+ about: "Über htmlarea",
+ help: "Hilfe",
+ textindicator: "derzeitiger Stil"
+ }
+};
--- /dev/null
+// I18N constants
+
+// LANG: "en", ENCODING: UTF-8 | ISO-8859-1
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+
+// FOR TRANSLATORS:
+//
+// 1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+// (at least a valid email address)
+//
+// 2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+// (if this is not possible, please include a comment
+// that states what encoding is necessary.)
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "en",
+
+ tooltips: {
+ bold: "Bold",
+ italic: "Italic",
+ underline: "Underline",
+ strikethrough: "Strikethrough",
+ subscript: "Subscript",
+ superscript: "Superscript",
+ justifyleft: "Justify Left",
+ justifycenter: "Justify Center",
+ justifyright: "Justify Right",
+ justifyfull: "Justify Full",
+ orderedlist: "Ordered List",
+ unorderedlist: "Bulleted List",
+ outdent: "Decrease Indent",
+ indent: "Increase Indent",
+ forecolor: "Font Color",
+ hilitecolor: "Background Color",
+ horizontalrule: "Horizontal Rule",
+ createlink: "Insert Web Link",
+ insertimage: "Insert Image",
+ inserttable: "Insert Table",
+ htmlmode: "Toggle HTML Source",
+ popupeditor: "Enlarge Editor",
+ about: "About this editor",
+ showhelp: "Help using editor",
+ textindicator: "Current style",
+ undo: "Undoes your last action",
+ redo: "Redoes your last action",
+ cut: "Cut selection",
+ copy: "Copy selection",
+ paste: "Paste from clipboard"
+ },
+
+ buttons: {
+ "ok": "OK",
+ "cancel": "Cancel"
+ },
+
+ msg: {
+ "Path": "Path",
+ "TEXT_MODE": "You are in TEXT MODE. Use the [<>] button to switch back to WYSIWIG."
+ }
+};
--- /dev/null
+// I18N constants
+
+// LANG: "en", ENCODING: UTF-8 | ISO-8859-1
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+
+// FOR TRANSLATORS:
+//
+// 1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+// (at least a valid email address)
+//
+// 2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+// (if this is not possible, please include a comment
+// that states what encoding is necessary.)
+<?php
+ include("../../../config.php");
+
+?>
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "en",
+
+ tooltips: {
+ bold: "<?php print(get_string("bold","htmlarea"));?>",
+ italic: "<?php print(get_string("italic","htmlarea"));?>",
+ underline: "<?php print(get_string("underline","htmlarea"));?>",
+ strikethrough: "<?php print(get_string("strikethrough","htmlarea"));?>",
+ subscript: "<?php print(get_string("subscript","htmlarea"));?>",
+ superscript: "<?php print(get_string("superscript","htmlarea"));?>",
+ justifyleft: "<?php print(get_string("justifyleft","htmlarea"));?>",
+ justifycenter: "<?php print(get_string("justifycenter","htmlarea"));?>",
+ justifyright: "<?php print(get_string("justifyright","htmlarea"));?>",
+ justifyfull: "<?php print(get_string("justifyfull","htmlarea"));?>",
+ insertorderedlist: "<?php print(get_string("orderedlist","htmlarea"));?>",
+ insertunorderedlist: "<?php print(get_string("unorderedlist","htmlarea"));?>",
+ outdent: "<?php print(get_string("outdent","htmlarea"));?>",
+ indent: "<?php print(get_string("indent","htmlarea"));?>",
+ forecolor: "<?php print(get_string("forecolor","htmlarea"));?>",
+ hilitecolor: "<?php print(get_string("hilitecolor","htmlarea"));?>",
+ inserthorizontalrule: "<?php print(get_string("horizontalrule","htmlarea"));?>",
+ createlink: "<?php print(get_string("createlink","htmlarea"));?>",
+ insertimage: "<?php print(get_string("insertimage","htmlarea"));?>",
+ inserttable: "<?php print(get_string("inserttable","htmlarea"));?>",
+ htmlmode: "<?php print(get_string("htmlmode","htmlarea"));?>",
+ popupeditor: "<?php print(get_string("popupeditor","htmlarea"));?>",
+ about: "<?php print(get_string("about","htmlarea"));?>",
+ showhelp: "<?php print(get_string("showhelp","htmlarea"));?>",
+ textindicator: "<?php print(get_string("textindicator","htmlarea"));?>",
+ undo: "<?php print(get_string("undo","htmlarea"));?>",
+ redo: "<?php print(get_string("redo","htmlarea"));?>",
+ cut: "<?php print(get_string("cut","htmlarea"));?>",
+ copy: "<?php print(get_string("copy","htmlarea"));?>",
+ paste: "<?php print(get_string("paste","htmlarea"));?>",
+ insertsmile: "<?php print(get_string("insertsmile","htmlarea"));?>",
+ insertchar: "<?php print(get_string("insertchar","htmlarea"));?>"
+ },
+
+ buttons: {
+ "ok": "<?php print(get_string("ok","htmlarea"));?>",
+ "cancel": "<?php print(get_string("cancel","htmlarea"));?>",
+ "browse": "<?php print(get_string("browse","htmlarea"));?>"
+ },
+
+ msg: {
+ "Path": "<?php print(get_string("Path","htmlarea"));?>",
+ "TEXT_MODE": "<?php print(get_string("TEXT_MODE","htmlarea"));?>"
+ }
+};
--- /dev/null
+// I18N constants\r
+\r
+HTMLArea.I18N = {\r
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "es",
+
+ tooltips: {\r
+ bold: "Negritas",\r
+ italic: "Cursiva",\r
+ underline: "Subrayado",\r
+ strikethrough: "Texto Cruzado",\r
+ subscript: "Subscript",\r
+ superscript: "Superscript",\r
+ justifyleft: "Alinear a la Izquierda",\r
+ justifycenter: "Centrar",\r
+ justifyright: "Alinear a la Derecha",\r
+ justifyfull: "Justificar",\r
+ orderedlist: "Lista Ordenada",\r
+ unorderedlist: "Lista No Ordenada",\r
+ outdent: "Aumentar Sangría",\r
+ indent: "Disminuir Sangría",\r
+ forecolor: "Color del Texto",\r
+ backcolor: "Color del Fondo",\r
+ horizontalrule: "Línea Horizontal",\r
+ createlink: "Insertar Enlace",\r
+ insertimage: "Insertar Imagen",\r
+ inserttable: "Insertar Tabla",\r
+ htmlmode: "Ver Documento en HTML",\r
+ popupeditor: "Ampliar Editor",\r
+ about: "Acerca del Editor",\r
+ help: "Ayuda",\r
+ textindicator: "Estilo Actual"\r
+ }\r
+};\r
--- /dev/null
+// I18N constants
+
+// LANG: "en", ENCODING: UTF-8 | ISO-8859-1
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+
+// FOR TRANSLATORS:
+//
+// 1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+// (at least a valid email address)
+//
+// 2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+// (if this is not possible, please include a comment
+// that states what encoding is necessary.)
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "fi",
+
+ tooltips: {
+ bold: "Lihavointi",
+ italic: "Kursivointi",
+ underline: "Alleviivaus",
+ strikethrough: "Päälleviivaus",
+ subscript: "Alaindeksi",
+ superscript: "Yläindeksi",
+ justifyleft: "Tasaa vasemmat reunat",
+ justifycenter: "Keskitä",
+ justifyright: "Tasaa oikeat reunat",
+ justifyfull: "Tasaa molemmat reunat",
+ orderedlist: "Numerointi",
+ unorderedlist: "Luettelomerkit",
+ outdent: "Lisää sisennystä",
+ indent: "Vähennä sisennystä",
+ forecolor: "Tekstin väri",
+ hilitecolor: "Taustan väri",
+ horizontalrule: "Vaakaviiva",
+ createlink: "Lisää linkki",
+ insertimage: "Lisää kuva",
+ inserttable: "Lisää taulukko",
+ htmlmode: "Näytä HTML koodi",
+ popupeditor: "Suurenna editori",
+ about: "Tietoja editorista",
+ showhelp: "Ohje",
+ textindicator: "Nykyinen tyyli",
+ undo: "Peruuta viimeinen toiminto",
+ redo: "Palauta viimeinen toiminto",
+ cut: "Leikkaa",
+ copy: "Kopioi",
+ paste: "Liitä"
+ },
+
+ buttons: {
+ "ok": "OK",
+ "cancel": "Peruuta",
+ },
+
+ msg: {
+ "Path": "Polku",
+ "TEXT_MODE": "Olet tekstitilassa. Käytä [<>] painiketta palataksesi WYSIWIG -tilaan."
+ }
+};
--- /dev/null
+// I18N constants
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "fr",
+
+ tooltips: {
+ bold: "Gras",
+ italic: "Italique",
+ underline: "Souligné",
+ strikethrough: "Barré",
+ subscript: "Subscript",
+ superscript: "Superscript",
+ justifyleft: "Aligné à gauche",
+ justifycenter: "Centré",
+ justifyright: "Aligné à droite",
+ justifyfull: "Justifié",
+ orderedlist: "Numérotation",
+ unorderedlist: "Puces",
+ outdent: "Augmenter le retrait",
+ indent: "Diminuer le retrait",
+ forecolor: "Couleur du texte",
+ backcolor: "Couleur du fond",
+ horizontalrule: "Ligne horizontale",
+ createlink: "Insérer un lien",
+ insertimage: "Insérer une image",
+ inserttable: "Insérer un tableau",
+ htmlmode: "Passer au code source HTML",
+ popupeditor: "Agrandir l'éditeur",
+ about: "A propos de cet éditeur",
+ help: "Aide sur l'éditeur",
+ textindicator: "Style courant"
+ }
+};
--- /dev/null
+// I18N constants -- Chinese GB
+// by Dave Lo -- dlo@interactivetools.com
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "gb",
+
+ tooltips: {
+ bold: "´ÖÌå",
+ italic: "бÌå",
+ underline: "µ×Ïß",
+ strikethrough: "ɾ³ýÏß",
+ subscript: "챐",
+ superscript: "Éϱê",
+ justifyleft: "λÖÿ¿×ó",
+ justifycenter: "λÖþÓÖÐ",
+ justifyright: "λÖÿ¿ÓÒ",
+ justifyfull: "λÖÃ×óÓÒÆ½µÈ",
+ orderedlist: "˳ÐòÇåµ¥",
+ unorderedlist: "ÎÞÐòÇåµ¥",
+ outdent: "¼õСÐÐǰ¿Õ°×",
+ indent: "¼Ó¿íÐÐǰ¿Õ°×",
+ forecolor: "ÎÄ×ÖÑÕÉ«",
+ backcolor: "±³¾°ÑÕÉ«",
+ horizontalrule: "ˮƽÏß",
+ createlink: "²åÈëÁ¬½á",
+ insertimage: "²åÈëͼÐÎ",
+ inserttable: "²åÈë±í¸ñ",
+ htmlmode: "Çл»HTMLÔʼÂë",
+ popupeditor: "·Å´ó",
+ about: "¹Øì¶ HTMLArea",
+ help: "˵Ã÷",
+ textindicator: "×ÖÌåÀý×Ó"
+ }
+};
--- /dev/null
+// I18N constants
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "it",
+
+ tooltips: {
+ bold: "Grassetto",
+ italic: "Corsivo",
+ underline: "Sottolineato",
+ strikethrough: "Barrato",
+ subscript: "Pedice",
+ superscript: "Apice",
+ justifyleft: "Allinea a sinistra",
+ justifycenter: "Centra",
+ justifyright: "Allinea a destra",
+ justifyfull: "Giustifica",
+ orderedlist: "Elenco numerato",
+ unorderedlist: "Elenco puntato",
+ outdent: "Riduci rientro",
+ indent: "Aumenta rientro",
+ forecolor: "Colore carattere",
+ backcolor: "Colore di sfondo",
+ horizontalrule: "Linea orizzontale",
+ createlink: "Inserisci collegamento ipertestuale",
+ insertimage: "Inserisci immagine",
+ inserttable: "Inserisci tabella",
+ htmlmode: "Passa alla visualizzazione HTML",
+ popupeditor: "Ingrandisci editor",
+ about: "Info",
+ help: "Aiuto",
+ textindicator: "Stile utilizzato"
+ }
+};
--- /dev/null
+// I18N constants -- Japanese EUC
+// by Manabu Onoue -- tmocsys@tmocsys.com
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "ja-euc",
+
+ tooltips: {
+ bold: "ÂÀ»ú",
+ italic: "¼ÐÂÎ",
+ underline: "²¼Àþ",
+ strikethrough: "ÂǤÁ¾Ã¤·Àþ",
+ subscript: "²¼Éդꤍ»ú",
+ superscript: "¾åÉդꤍ»ú",
+ justifyleft: "º¸´ó¤»",
+ justifycenter: "Ãæ±û´ó¤»",
+ justifyright: "±¦´ó¤»",
+ justifyfull: "¶ÑÅù³äÉÕ",
+ orderedlist: "ÈÖ¹æÉÕ¤²Õ¾ò½ñ¤",
+ unorderedlist: "µ¹æÉÕ¤²Õ¾ò½ñ¤",
+ outdent: "¥¤¥ó¥Ç¥ó¥È²ò½ü",
+ indent: "¥¤¥ó¥Ç¥ó¥ÈÀßÄê",
+ forecolor: "ʸ»ú¿§",
+ backcolor: "ÇØ·Ê¿§",
+ horizontalrule: "¿åÊ¿Àþ",
+ createlink: "¥ê¥ó¥¯ºîÀ®",
+ insertimage: "²èÁüÁÞÆþ",
+ inserttable: "¥Æ¡¼¥Ö¥ëÁÞÆþ",
+ htmlmode: "HTMLɽ¼¨ÀÚÂØ",
+ popupeditor: "¥¨¥Ç¥£¥¿³ÈÂç",
+ about: "¥Ð¡¼¥¸¥ç¥ó¾ðÊó",
+ help: "¥Ø¥ë¥×",
+ textindicator: "¸½ºß¤Î¥¹¥¿¥¤¥ë"
+ }
+};
--- /dev/null
+// I18N constants -- Japanese JIS
+// by Manabu Onoue -- tmocsys@tmocsys.com
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "ja-jis",
+
+ tooltips: {
+ bold: "\e$BB@;z\e(B",
+ italic: "\e$B<PBN\e(B",
+ underline: "\e$B2<@~\e(B",
+ strikethrough: "\e$BBG$A>C$7@~\e(B",
+ subscript: "\e$B2<IU$-E:$(;z\e(B",
+ superscript: "\e$B>eIU$-E:$(;z\e(B",
+ justifyleft: "\e$B:84s$;\e(B",
+ justifycenter: "\e$BCf1{4s$;\e(B",
+ justifyright: "\e$B1&4s$;\e(B",
+ justifyfull: "\e$B6QEy3dIU\e(B",
+ orderedlist: "\e$BHV9fIU$-2U>r=q$-\e(B",
+ unorderedlist: "\e$B5-9fIU$-2U>r=q$-\e(B",
+ outdent: "\e$B%$%s%G%s%H2r=|\e(B",
+ indent: "\e$B%$%s%G%s%H@_Dj\e(B",
+ forecolor: "\e$BJ8;z?'\e(B",
+ backcolor: "\e$BGX7J?'\e(B",
+ horizontalrule: "\e$B?eJ?@~\e(B",
+ createlink: "\e$B%j%s%/:n@.\e(B",
+ insertimage: "\e$B2hA|A^F~\e(B",
+ inserttable: "\e$B%F!<%V%kA^F~\e(B",
+ htmlmode: "HTML\e$BI=<(@ZBX\e(B",
+ popupeditor: "\e$B%(%G%#%?3HBg\e(B",
+ about: "\e$B%P!<%8%g%s>pJs\e(B",
+ help: "\e$B%X%k%W\e(B",
+ textindicator: "\e$B8=:_$N%9%?%$%k\e(B"
+ }
+};
--- /dev/null
+// I18N constants -- Japanese Shift-JIS
+// by Manabu Onoue -- tmocsys@tmocsys.com
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "ja-sjis",
+
+ tooltips: {
+ bold: "\91¾\8e\9a",
+ italic: "\8eÎ\91Ì",
+ underline: "\89º\90ü",
+ strikethrough: "\91Å\82¿\8fÁ\82µ\90ü",
+ subscript: "\89º\95t\82«\93Y\82¦\8e\9a",
+ superscript: "\8fã\95t\82«\93Y\82¦\8e\9a",
+ justifyleft: "\8d¶\8añ\82¹",
+ justifycenter: "\92\86\89\9b\8añ\82¹",
+ justifyright: "\89E\8añ\82¹",
+ justifyfull: "\8bÏ\93\99\8a\84\95t",
+ orderedlist: "\94Ô\8d\86\95t\82«\89Ó\8fð\8f\91\82«",
+ unorderedlist: "\8bL\8d\86\95t\82«\89Ó\8fð\8f\91\82«",
+ outdent: "\83C\83\93\83f\83\93\83g\89ð\8f\9c",
+ indent: "\83C\83\93\83f\83\93\83g\90Ý\92è",
+ forecolor: "\95¶\8e\9a\90F",
+ backcolor: "\94w\8ci\90F",
+ horizontalrule: "\90\85\95½\90ü",
+ createlink: "\83\8a\83\93\83N\8dì\90¬",
+ insertimage: "\89æ\91\9c\91}\93ü",
+ inserttable: "\83e\81[\83u\83\8b\91}\93ü",
+ htmlmode: "HTML\95\\8e¦\90Ø\91Ö",
+ popupeditor: "\83G\83f\83B\83^\8ag\91å",
+ about: "\83o\81[\83W\83\87\83\93\8fî\95ñ",
+ help: "\83w\83\8b\83v",
+ textindicator: "\8c»\8dÝ\82Ì\83X\83^\83C\83\8b"
+ }
+};
--- /dev/null
+// I18N constants -- Japanese UTF-8
+// by Manabu Onoue -- tmocsys@tmocsys.com
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "ja-utf8",
+
+ tooltips: {
+ bold: "太字",
+ italic: "斜体",
+ underline: "下線",
+ strikethrough: "打ち消し線",
+ subscript: "下付き添え字",
+ superscript: "上付き添え字",
+ justifyleft: "左寄せ",
+ justifycenter: "中央寄せ",
+ justifyright: "右寄せ",
+ justifyfull: "均等割付",
+ orderedlist: "番号付き箇条書き",
+ unorderedlist: "記号付き箇条書き",
+ outdent: "インデント解除",
+ indent: "インデント設定",
+ forecolor: "文字色",
+ backcolor: "背景色",
+ horizontalrule: "水平線",
+ createlink: "リンク作成",
+ insertimage: "画像挿入",
+ inserttable: "テーブル挿入",
+ htmlmode: "HTML表示切替",
+ popupeditor: "エディタ拡大",
+ about: "バージョン情報",
+ help: "ヘルプ",
+ textindicator: "現在のスタイル"
+ }
+};
--- /dev/null
+// I18N constants
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "nb",
+
+ tooltips: {
+ bold: "Fet",
+ italic: "Kursiv",
+ underline: "Understreket",
+ strikethrough: "Gjennomstreket",
+ subscript: "Senket",
+ superscript: "Hevet",
+ justifyleft: "Venstrejuster",
+ justifycenter: "Midtjuster",
+ justifyright: "Høyrejuster",
+ justifyfull: "Blokkjuster",
+ orderedlist: "Nummerert liste",
+ unorderedlist: "Punktmerket liste",
+ outdent: "Øke innrykk",
+ indent: "Reduser innrykk",
+ forecolor: "Skriftfarge",
+ backcolor: "Bakgrunnsfarge",
+ horizontalrule: "Horisontal linje",
+ createlink: "Sett inn lenke",
+ insertimage: "Sett inn bilde",
+ inserttable: "Sett inn tabell",
+ htmlmode: "Vis HTML kode",
+ popupeditor: "Forstørr redigeringsvindu",
+ about: "Om..",
+ help: "Hjelp",
+ textindicator: "Gjeldende stil"
+ }
+};
--- /dev/null
+// Dutch version
+// Author: Wouter Meeus alias Redspider <webmaster@tielt.be>
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "nl",
+
+ tooltips: {
+ bold: "Vet",
+ italic: "Cursief",
+ underline: "Onderlijnen",
+ strikethrough: "Doorstrepen",
+ subscript: "Subscript",
+ superscript: "Superscript",
+ justifyleft: "Links Uitlijnen",
+ justifycenter: "Centreren",
+ justifyright: "Rechts Uitlijnen",
+ justifyfull: "Uitvullen",
+ orderedlist: "Nummering",
+ unorderedlist: "Opsomming",
+ outdent: "Verklein insprong",
+ indent: "Vergroot insprong",
+ forecolor: "Tekst Kleur",
+ backcolor: "Achtergrond Kleur",
+ horizontalrule: "Horizontale lijn",
+ createlink: "Hyperlink invoegen",
+ insertimage: "Afbeelding invoegen",
+ inserttable: "Tabel invoegen",
+ htmlmode: "HTML broncode",
+ popupeditor: "Vergroot Editor",
+ about: "Over deze editor",
+ help: "Help",
+ textindicator: "Huidige stijl"
+ }
+};
--- /dev/null
+// I18N constants
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "fi",
+
+ tooltips: {
+ bold: "Lihavoitu",
+ italic: "Kursivoitu",
+ underline: "Alleviivattu",
+ strikethrough: "Yliviivattu",
+ subscript: "Alaindeksi",
+ superscript: "Yläindeksi",
+ justifyleft: "Tasaa vasemmat reunat",
+ justifycenter: "Keskitä",
+ justifyright: "Tasaa oikeat reunat",
+ justifyfull: "Tasaa molemmat reunat",
+ insertorderedlist: "Numerointi",
+ insertunorderedlist: "Luettelomerkit",
+ outdent: "Lisää sisennystä",
+ indent: "Pienennä sisennystä",
+ forecolor: "Fontin väri",
+ hilitecolor: "Taustaväri",
+ inserthorizontalrule: "Vaakaviiva",
+ createlink: "Lisää Linkki",
+ insertimage: "Lisää Kuva",
+ inserttable: "Lisää Taulu",
+ htmlmode: "HTML Lähdekoodi vs WYSIWYG",
+ popupeditor: "Suurenna Editori",
+ about: "Tietoja Editorista",
+ showhelp: "Näytä Ohje",
+ textindicator: "Nykyinen tyyli",
+ undo: "Peruuta viimeinen toiminto",
+ redo: "Palauta viimeinen toiminto",
+ cut: "Leikkaa maalattu",
+ copy: "Kopioi maalattu",
+ paste: "Liitä leikepyödältä"
+ },
+
+ buttons: {
+ "ok": "Hyväksy",
+ "cancel": "Peruuta"
+ },
+
+ msg: {
+ "Path": "Polku",
+ "TEXT_MODE": "You are in TEXT MODE. Use the [<>] button to switch back to WYSIWIG."
+ }
+};
--- /dev/null
+// I18N constants\r
+\r
+HTMLArea.I18N = {\r
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "pl",
+
+ tooltips: {\r
+ bold: "Pogrubienie",\r
+ italic: "Pochylenie",\r
+ underline: "Podkre\9clenie",\r
+ strikethrough: "Przekre\9clenie",\r
+ subscript: "Indeks dolny",\r
+ superscript: "Indeks górny",\r
+ justifyleft: "Wyrównaj do lewej",\r
+ justifycenter: "Wy\9crodkuj",\r
+ justifyright: "Wyrównaj do prawej",\r
+ justifyfull: "Wyjustuj",\r
+ orderedlist: "Numerowanie",\r
+ unorderedlist: "Wypunktowanie",\r
+ outdent: "Zmniejsz wciêcie",\r
+ indent: "Zwiêksz wciêcie",\r
+ forecolor: "Kolor czcionki",\r
+ backcolor: "Kolor t³a",\r
+ horizontalrule: "Linia pozioma",\r
+ createlink: "Wstaw adres sieci Web",\r
+ insertimage: "Wstaw obraz",\r
+ inserttable: "Wstaw tabelê",\r
+ htmlmode: "Edycja WYSIWYG/w \9fródle strony",\r
+ popupeditor: "Pe³ny ekran",\r
+ about: "Informacje o tym edytorze",\r
+ help: "Pomoc",\r
+ textindicator: "Obecny styl"\r
+ }\r
+};\r
--- /dev/null
+// I18N constants\r
+// Brazilian Portuguese Translation by Alex Piaz <webmaster@globalmap.com>\r
+\r
+HTMLArea.I18N = {\r
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "pt_br",
+
+ tooltips: {\r
+ bold: "Negrito",\r
+ italic: "Itálico",\r
+ underline: "Sublinhado",\r
+ strikethrough: "Tachado",\r
+ subscript: "Subescrito",\r
+ superscript: "Sobrescrito",\r
+ justifyleft: "Alinhar à Esquerda",\r
+ justifycenter: "Centralizar",\r
+ justifyright: "Alinhar à Direita",\r
+ justifyfull: "Justificar",\r
+ orderedlist: "Lista Numerada",\r
+ unorderedlist: "Lista Marcadores",\r
+ outdent: "Diminuir Indentação",\r
+ indent: "Aumentar Indentação",\r
+ forecolor: "Cor da Fonte",\r
+ backcolor: "Cor do Fundo",\r
+ horizontalrule: "Linha Horizontal",\r
+ createlink: "Inserir Link",\r
+ insertimage: "Inserir Imagem",\r
+ inserttable: "Inserir Tabela",\r
+ htmlmode: "Ver Código-Fonte",\r
+ popupeditor: "Expandir Editor",\r
+ about: "Sobre",\r
+ help: "Ajuda",\r
+ textindicator: "Estilo Atual"\r
+ }\r
+};\r
--- /dev/null
+// I18N constants
+
+// LANG: "ro", ENCODING: UTF-8
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+
+// FOR TRANSLATORS:
+//
+// 1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+// (at least a valid email address)
+//
+// 2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+// (if this is not possible, please include a comment
+// that states what encoding is necessary.)
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "ro",
+
+ tooltips: {
+ bold: "Îngroşat",
+ italic: "Italic",
+ underline: "Subliniat",
+ strikethrough: "Tăiat",
+ subscript: "Subscript",
+ superscript: "Superscript",
+ justifyleft: "Aliniere la stânga",
+ justifycenter: "Aliniere pe centru",
+ justifyright: "Aliniere la dreapta",
+ justifyfull: "Aliniere în ambele părţi",
+ orderedlist: "Listă ordonată",
+ unorderedlist: "Listă marcată",
+ outdent: "Micşorează alineatul",
+ indent: "Măreşte alineatul",
+ forecolor: "Culoarea textului",
+ hilitecolor: "Culoare de fundal",
+ horizontalrule: "Linie orizontală",
+ createlink: "Inserează link",
+ insertimage: "Inserează o imagine",
+ inserttable: "Inserează un tabel",
+ htmlmode: "Sursa HTML / WYSIWYG",
+ popupeditor: "Maximizează editorul",
+ about: "Despre editor",
+ showhelp: "Documentaţie (devel)",
+ textindicator: "Stilul curent",
+ undo: "Anulează ultima acţiune",
+ redo: "Reface ultima acţiune anulată",
+ cut: "Taie în clipboard",
+ copy: "Copie în clipboard",
+ paste: "Aduce din clipboard"
+ },
+
+ buttons: {
+ "ok": "OK",
+ "cancel": "Anulează"
+ },
+
+ msg: {
+ "Path": "Calea",
+ "TEXT_MODE": "Eşti în modul TEXT. Apasă butonul [<>] pentru a te întoarce în modul WYSIWYG."
+ }
+};
--- /dev/null
+// I18N constants
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "ru",
+
+ tooltips: {
+ bold: "Æèðíûé",
+ italic: "Íàêëîííûé",
+ underline: "Ïîä÷åðêíóòûé",
+ strikethrough: "Ïåðå÷åðêíóòûé",
+ subscript: "Íèæíèé èíäåêñ",
+ superscript: "Âåðõíèé èíäåêñ",
+ justifyleft: "Âûðàâíèâàíèå ïî ëåâîìó êðàþ",
+ justifycenter: "Âûðàâíèâàíèå ïî öåíòðó",
+ justifyright: "Âûðàâíèâàíèå ïî ïðàâîìó êðàþ",
+ justifyfull: "Ðàñòÿíóòûé òåêñò",
+ orderedlist: "Íóìåðîâàííûé ñïèñîê",
+ unorderedlist: "Ìàðêèðîâàííûé ñïèñîê",
+ outdent: "Ñäâèã â ëåâî",
+ indent: "Ñäâèã â ïðàâî",
+ forecolor: "Öâåò øðèôòà",
+ backcolor: "Öâåò ôîíà",
+ horizontalrule: "Ãîðèçîíòàëüíàÿ ëèíèÿ",
+ createlink: "Âñòàâèòü ññûëêó",
+ insertimage: "Âñòàâèòü êàðòèíêó",
+ inserttable: "Âñòàâèòü òàáëèöó",
+ htmlmode: "Âèäåòü HTML êîä",
+ popupeditor: "Óâåëè÷èòü ðåäàêòîð",
+ about: "Î ðåäàêòîðå",
+ help: "Ïîìîùü â èñïîëüçîâàíèè",
+ textindicator: "Äàííûé ñòèëü"
+ }
+};
--- /dev/null
+// Swedish version for htmlArea v3.0 - Alpha Release\r
+// - translated by pat<pat@engvall.nu>\r
+// term´s and licenses are equal to htmlarea!\r
+\r
+HTMLArea.I18N = {\r
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "se",
+
+ tooltips: {\r
+ bold: "Fet",\r
+ italic: "Kursiv",\r
+ underline: "Understruken",\r
+ strikethrough: "Genomstruken",\r
+ subscript: "Nedsänkt",\r
+ superscript: "Upphöjd",\r
+ justifyleft: "Vänsterjustera",\r
+ justifycenter: "Centrera",\r
+ justifyright: "Högerjustera",\r
+ justifyfull: "Marginaljustera",\r
+ orderedlist: "Numrerad lista",\r
+ unorderedlist: "Punktlista",\r
+ outdent: "Minska indrag",\r
+ indent: "Öka indrag",\r
+ forecolor: "Textfärg",\r
+ backcolor: "Bakgrundsfärg",\r
+ horizontalrule: "Vågrät linje",\r
+ createlink: "Infoga länk",\r
+ insertimage: "Infoga bild",\r
+ inserttable: "Infoga tabell",\r
+ htmlmode: "Visa källkod",\r
+ popupeditor: "Visa i eget fönster",\r
+ about: "Om denna editor",\r
+ help: "Hjälp",\r
+ textindicator: "Nuvarande stil"\r
+ }\r
+};\r
--- /dev/null
+// I18N constants : Vietnamese
+// mviet: download the free Vietnamese script addon for htmlArea at: www.mviet.org
+// email: mviet@socal.rr.com
+
+HTMLArea.I18N = {
+
+ // the following should be the filename without .js extension
+ // it will be used for automatically load plugin language.
+ lang: "vn",
+
+ tooltips: {
+ bold: "Đậm",
+ italic: "Nghiêng",
+ underline: "Gạch Đít",
+ strikethrough: "Gạch Xóa",
+ subscript: "Viết Xuống Dưới",
+ superscript: "Viết Lên Trên ",
+ justifyleft: "Ngay Hàng Bên Trái ",
+ justifycenter: "Ngay Hàng Giữa",
+ justifyright: "Ngay Hàng Lên Phải",
+ justifyfull: "Ngay Hàng Trái & Phải",
+ orderedlist: "Chuỗi Thứ Tự 123",
+ unorderedlist: "Chuỗi Nút",
+ outdent: "Giảm Vào Hàng",
+ indent: "Tăng Vào Hàng",
+ forecolor: "Màu Chữ",
+ backcolor: "Màu Nền",
+ horizontalrule: "Thước Ngang",
+ createlink: "Tạo Nối",
+ insertimage: "Mang Hình Vô",
+ inserttable: "Mang Khuôn Vô",
+ htmlmode: "Bật / Tắt Nguồn HTML",
+ popupeditor: "Póp Lớn Khung Viết",
+ about: "Nói Về Chương Trình",
+ help: "Giúp Đỡ",
+ textindicator: "Loại Kiểu Viết"
+ }
+};
--- /dev/null
+htmlArea License (based on BSD license)\r
+Copyright (c) 2002, interactivetools.com, inc.\r
+All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\r
+\r
+1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \r
+\r
+2) 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. \r
+\r
+3) Neither the name of interactivetools.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \r
+\r
+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.\r
--- /dev/null
+// Though "Dialog" looks like an object, it isn't really an object. Instead
+// it's just namespace for protecting global symbols.
+
+function Dialog(url, action, init) {
+ if (typeof init == "undefined") {
+ init = window; // pass this window object by default
+ }
+ if (document.all) { // here we hope that Mozilla will never support document.all
+ var value =
+ showModalDialog(url, init,
+ //window.open(url, '_blank',
+ "resizable: no; help: no; status: no; scroll: no");
+ if (action) {
+ action(value);
+ }
+ } else {
+ return Dialog._geckoOpenModal(url, action, init);
+ }
+};
+
+Dialog._parentEvent = function(ev) {
+ if (Dialog._modal && !Dialog._modal.closed) {
+ //Dialog._modal.focus();
+ // we get here in Mozilla only, anyway, so we can safely use
+ // the DOM version.
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+};
+
+// should be a function, the return handler of the currently opened dialog.
+Dialog._return = null;
+
+// constant, the currently opened dialog
+Dialog._modal = null;
+
+// the dialog will read it's args from this variable
+Dialog._arguments = null;
+
+Dialog._geckoOpenModal = function(url, action, init) {
+ var dlg = window.open(url, "ha_dialog",
+ "toolbar=no,menubar=no,personalbar=no,width=10,height=10," +
+ "scrollbars=no,resizable=no");
+ Dialog._modal = dlg;
+ Dialog._arguments = init;
+
+ // capture some window's events
+ function capwin(w) {
+ w.addEventListener("click", Dialog._parentEvent, true);
+ w.addEventListener("mousedown", Dialog._parentEvent, true);
+ w.addEventListener("focus", Dialog._parentEvent, true);
+ };
+ // release the captured events
+ function relwin(w) {
+ w.removeEventListener("focus", Dialog._parentEvent, true);
+ w.removeEventListener("mousedown", Dialog._parentEvent, true);
+ w.removeEventListener("click", Dialog._parentEvent, true);
+ };
+ capwin(window);
+ // capture other frames
+ for (var i = 0; i < window.frames.length; capwin(window.frames[i++]));
+ // make up a function to be called when the Dialog ends.
+ Dialog._return = function (val) {
+ if (val && action) {
+ action(val);
+ }
+ relwin(window);
+ // capture other frames
+ for (var i = 0; i < window.frames.length; relwin(window.frames[i++]));
+ Dialog._modal = null;
+ };
+};
--- /dev/null
+//
+// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+// This copyright notice MUST stay intact for use (see license.txt).
+//
+// A free WYSIWYG editor replacement for <textarea> fields.
+// For full source code and docs, visit http://www.interactivetools.com/
+//
+// Version 3.0 developed by Mihai Bazon for InteractiveTools.
+// http://dynarch.com/mishoo
+//
+// $Id$
+
+// Creates a new HTMLArea object. Tries to replace the textarea with the given
+// ID with it.
+function HTMLArea(textarea, config) {
+ if (HTMLArea.checkSupportedBrowser()) {
+ if (typeof config == "undefined") {
+ this.config = new HTMLArea.Config();
+ } else {
+ this.config = config;
+ }
+ this._htmlArea = null;
+ this._textArea = textarea;
+ this._editMode = "wysiwyg";
+ this.plugins = {};
+ this._timerToolbar = null;
+ this._timerUndo = null;
+ this._undoQueue = new Array(this.config.undoSteps);
+ this._undoPos = -1;
+ this._mdoc = document; // cache the document, we need it in plugins
+ this.doctype = '';
+ }
+};
+
+// cache some regexps
+HTMLArea.RE_tagName = /(<\/|<)\s*([^ \t\n>]+)/ig;
+HTMLArea.RE_doctype = /(<!doctype((.|\n)*?)>)\n?/i;
+HTMLArea.RE_head = /<head>((.|\n)*?)<\/head>/i;
+HTMLArea.RE_body = /<body>((.|\n)*?)<\/body>/i;
+
+HTMLArea.Config = function () {
+ this.version = "3.0";
+
+ this.width = "auto";
+ this.height = "auto";
+
+ // enable creation of a status bar?
+ this.statusBar = true;
+
+ // maximum size of the undo queue
+ this.undoSteps = 20;
+
+ // the time interval at which undo samples are taken
+ this.undoTimeout = 500; // 1/2 sec.
+
+ // the next parameter specifies whether the toolbar should be included
+ // in the size or not.
+ this.sizeIncludesToolbar = true;
+
+ // if true then HTMLArea will retrieve the full HTML, starting with the
+ // <HTML> tag.
+ this.fullPage = false;
+
+ // style included in the iframe document
+ this.pageStyle = "body { background-color: #fff; font-family: verdana,sans-serif; }";
+ if (typeof _editor_url != "undefined") {
+ this.editorURL = _editor_url;
+ } else {
+ this.editorURL = "";
+ }
+
+ // URL-s
+ this.imgURL = "images/";
+ this.popupURL = "popups/";
+
+ /** CUSTOMIZING THE TOOLBAR
+ * -------------------------
+ *
+ * It is recommended that you customize the toolbar contents in an
+ * external file (i.e. the one calling HTMLArea) and leave this one
+ * unchanged. That's because when we (InteractiveTools.com) release a
+ * new official version, it's less likely that you will have problems
+ * upgrading HTMLArea.
+ */
+ this.toolbar = [
+ [ "fontname", "space",
+ "fontsize", "space",
+ "formatblock", "space",
+ "bold", "italic", "underline", "strikethrough", "separator",
+ "subscript", "superscript", "separator",
+ "copy", "cut", "paste", "space", "undo", "redo" ],
+
+ [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",
+ "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator",
+ "forecolor", "hilitecolor", "separator",
+ "inserthorizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator",
+ "popupeditor", "separator", "showhelp", "about" ]
+ ];
+
+ this.fontname = {
+ "Arial": 'arial,helvetica,sans-serif',
+ "Courier New": 'courier new,courier,monospace',
+ "Georgia": 'georgia,times new roman,times,serif',
+ "Tahoma": 'tahoma,arial,helvetica,sans-serif',
+ "Times New Roman": 'times new roman,times,serif',
+ "Verdana": 'verdana,arial,helvetica,sans-serif',
+ "impact": 'impact',
+ "WingDings": 'wingdings'
+ };
+
+ this.fontsize = {
+ "1 (8 pt)": "1",
+ "2 (10 pt)": "2",
+ "3 (12 pt)": "3",
+ "4 (14 pt)": "4",
+ "5 (18 pt)": "5",
+ "6 (24 pt)": "6",
+ "7 (36 pt)": "7"
+ };
+
+ this.formatblock = {
+ "Heading 1": "h1",
+ "Heading 2": "h2",
+ "Heading 3": "h3",
+ "Heading 4": "h4",
+ "Heading 5": "h5",
+ "Heading 6": "h6",
+ "Normal": "p",
+ "Address": "address",
+ "Formatted": "pre"
+ };
+
+ this.customSelects = {};
+
+ function cut_copy_paste(e, cmd, obj) {
+ try {
+ e.execCommand(cmd);
+ } catch (e) {
+ if (HTMLArea.is_gecko) {
+ alert("Some revisions of Mozilla/Gecko do not support programatic " +
+ "access to cut/copy/paste functions, for security reasons. " +
+ "Your browser is one of them. Please use the standard key combinations:\n" +
+ "CTRL-X for cut, CTRL-C for copy, CTRL-V for paste.");
+ obj.element.style.display = "none";
+ }
+ }
+ };
+
+ // ADDING CUSTOM BUTTONS: please read below!
+ // format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]"
+ // - ID: unique ID for the button. If the button calls document.execCommand
+ // it's wise to give it the same name as the called command.
+ // - ACTION: function that gets called when the button is clicked.
+ // it has the following prototype:
+ // function(editor, buttonName)
+ // - editor is the HTMLArea object that triggered the call
+ // - buttonName is the ID of the clicked button
+ // These 2 parameters makes it possible for you to use the same
+ // handler for more HTMLArea objects or for more different buttons.
+ // - ToolTip: default tooltip, for cases when it is not defined in the -lang- file (HTMLArea.I18N)
+ // - Icon: path to an icon image file for the button (TODO: use one image for all buttons!)
+ // - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.
+ this.btnList = {
+ bold: [ "Bold", "ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ],
+ italic: [ "Italic", "ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ],
+ underline: [ "Underline", "ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ],
+ strikethrough: [ "Strikethrough", "ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ],
+ subscript: [ "Subscript", "ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ],
+ superscript: [ "Superscript", "ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ],
+ justifyleft: [ "Justify Left", "ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ],
+ justifycenter: [ "Justify Center", "ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ],
+ justifyright: [ "Justify Right", "ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ],
+ justifyfull: [ "Justify Full", "ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ],
+ insertorderedlist: [ "Ordered List", "ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ],
+ insertunorderedlist: [ "Bulleted List", "ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ],
+ outdent: [ "Decrease Indent", "ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ],
+ indent: [ "Increase Indent", "ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ],
+ forecolor: [ "Font Color", "ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ],
+ hilitecolor: [ "Background Color", "ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ],
+ inserthorizontalrule: [ "Horizontal Rule", "ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ],
+ createlink: [ "Insert Web Link", "ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ],
+ insertimage: [ "Insert Image", "ed_image.gif", false, function(e) {e.execCommand("insertimage");} ],
+ inserttable: [ "Insert Table", "insert_table.gif", false, function(e) {e.execCommand("inserttable");} ],
+ htmlmode: [ "Toggle HTML Source", "ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ],
+ popupeditor: [ "Enlarge Editor", "fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ],
+ about: [ "About this editor", "ed_about.gif", true, function(e) {e.execCommand("about");} ],
+ showhelp: [ "Help using editor", "ed_help.gif", true, function(e) {e.execCommand("showhelp");} ],
+ undo: [ "Undoes your last action", "ed_undo.gif", false, function(e) {e.execCommand("undo");} ],
+ redo: [ "Redoes your last action", "ed_redo.gif", false, function(e) {e.execCommand("redo");} ],
+ cut: [ "Cut selection", "ed_cut.gif", false, cut_copy_paste ],
+ copy: [ "Copy selection", "ed_copy.gif", false, cut_copy_paste ],
+ paste: [ "Paste from clipboard", "ed_paste.gif", false, cut_copy_paste ]
+ };
+ /* ADDING CUSTOM BUTTONS
+ * ---------------------
+ *
+ * It is recommended that you add the custom buttons in an external
+ * file and leave this one unchanged. That's because when we
+ * (InteractiveTools.com) release a new official version, it's less
+ * likely that you will have problems upgrading HTMLArea.
+ *
+ * Example on how to add a custom button when you construct the HTMLArea:
+ *
+ * var editor = new HTMLArea("your_text_area_id");
+ * var cfg = editor.config; // this is the default configuration
+ * cfg.btnList["my-hilite"] =
+ * [ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action
+ * "Highlight selection", // tooltip
+ * "my_hilite.gif", // image
+ * false // disabled in text mode
+ * ];
+ * cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar
+ *
+ * An alternate (also more convenient and recommended) way to
+ * accomplish this is to use the registerButton function below.
+ */
+ // initialize tooltips from the I18N module and generate correct image path
+ for (var i in this.btnList) {
+ var btn = this.btnList[i];
+ btn[1] = this.editorURL + this.imgURL + btn[1];
+ if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {
+ btn[0] = HTMLArea.I18N.tooltips[i];
+ }
+ }
+};
+
+/** Helper function: register a new button with the configuration. It can be
+ * called with all 5 arguments, or with only one (first one). When called with
+ * only one argument it must be an object with the following properties: id,
+ * tooltip, image, textMode, action. Examples:
+ *
+ * 1. config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...});
+ * 2. config.registerButton({
+ * id : "my-hilite", // the ID of your button
+ * tooltip : "Hilite text", // the tooltip
+ * image : "my-hilite.gif", // image to be displayed in the toolbar
+ * textMode : false, // disabled in text mode
+ * action : function(editor) { // called when the button is clicked
+ * editor.surroundHTML('<span class="hilite">', '</span>');
+ * },
+ * context : "p" // will be disabled if outside a <p> element
+ * });
+ */
+HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) {
+ var the_id;
+ if (typeof id == "string") {
+ the_id = id;
+ } else if (typeof id == "object") {
+ the_id = id.id;
+ } else {
+ alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments");
+ return false;
+ }
+ // check for existing id
+ if (typeof this.customSelects[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ switch (typeof id) {
+ case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break;
+ case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break;
+ }
+};
+
+/** The following helper function registers a dropdown box with the editor
+ * configuration. You still have to add it to the toolbar, same as with the
+ * buttons. Call it like this:
+ *
+ * FIXME: add example
+ */
+HTMLArea.Config.prototype.registerDropdown = function(object) {
+ // check for existing id
+ if (typeof this.customSelects[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ this.customSelects[object.id] = object;
+};
+
+/** Call this function to remove some buttons/drop-down boxes from the toolbar.
+ * Pass as the only parameter a string containing button/drop-down names
+ * delimited by spaces. Note that the string should also begin with a space
+ * and end with a space. Example:
+ *
+ * config.hideSomeButtons(" fontname fontsize textindicator ");
+ *
+ * It's useful because it's easier to remove stuff from the defaul toolbar than
+ * create a brand new toolbar ;-)
+ */
+HTMLArea.Config.prototype.hideSomeButtons = function(remove) {
+ var toolbar = this.toolbar;
+ for (var i in toolbar) {
+ var line = toolbar[i];
+ for (var j = line.length; --j >= 0; ) {
+ if (remove.indexOf(" " + line[j] + " ") >= 0) {
+ var len = 1;
+ if (/separator|space/.test(line[j + 1])) {
+ len = 2;
+ }
+ line.splice(j, len);
+ }
+ }
+ }
+};
+
+/** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */
+HTMLArea.replaceAll = function(config) {
+ var tas = document.getElementsByTagName("textarea");
+ for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate());
+};
+
+/** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */
+HTMLArea.replace = function(id, config) {
+ var ta = document.getElementById(id);
+ return ta ? (new HTMLArea(ta, config)).generate() : null;
+};
+
+// Creates the toolbar and appends it to the _htmlarea
+HTMLArea.prototype._createToolbar = function () {
+ var editor = this; // to access this in nested functions
+
+ var toolbar = document.createElement("div");
+ this._toolbar = toolbar;
+ toolbar.className = "toolbar";
+ toolbar.unselectable = "1";
+ var tb_row = null;
+ var tb_objects = new Object();
+ this._toolbarObjects = tb_objects;
+
+ // creates a new line in the toolbar
+ function newLine() {
+ var table = document.createElement("table");
+ table.border = "0px";
+ table.cellSpacing = "0px";
+ table.cellPadding = "0px";
+ toolbar.appendChild(table);
+ // TBODY is required for IE, otherwise you don't see anything
+ // in the TABLE.
+ var tb_body = document.createElement("tbody");
+ table.appendChild(tb_body);
+ tb_row = document.createElement("tr");
+ tb_body.appendChild(tb_row);
+ }; // END of function: newLine
+ // init first line
+ newLine();
+
+ // updates the state of a toolbar element. This function is member of
+ // a toolbar element object (unnamed objects created by createButton or
+ // createSelect functions below).
+ function setButtonStatus(id, newval) {
+ var oldval = this[id];
+ var el = this.element;
+ if (oldval != newval) {
+ switch (id) {
+ case "enabled":
+ if (newval) {
+ HTMLArea._removeClass(el, "buttonDisabled");
+ el.disabled = false;
+ } else {
+ HTMLArea._addClass(el, "buttonDisabled");
+ el.disabled = true;
+ }
+ break;
+ case "active":
+ if (newval) {
+ HTMLArea._addClass(el, "buttonPressed");
+ } else {
+ HTMLArea._removeClass(el, "buttonPressed");
+ }
+ break;
+ }
+ this[id] = newval;
+ }
+ }; // END of function: setButtonStatus
+
+ // this function will handle creation of combo boxes. Receives as
+ // parameter the name of a button as defined in the toolBar config.
+ // This function is called from createButton, above, if the given "txt"
+ // doesn't match a button.
+ function createSelect(txt) {
+ var options = null;
+ var el = null;
+ var cmd = null;
+ var customSelects = editor.config.customSelects;
+ var context = null;
+ switch (txt) {
+ case "fontsize":
+ case "fontname":
+ case "formatblock":
+ // the following line retrieves the correct
+ // configuration option because the variable name
+ // inside the Config object is named the same as the
+ // button/select in the toolbar. For instance, if txt
+ // == "formatblock" we retrieve config.formatblock (or
+ // a different way to write it in JS is
+ // config["formatblock"].
+ options = editor.config[txt];
+ cmd = txt;
+ break;
+ default:
+ // try to fetch it from the list of registered selects
+ cmd = txt;
+ var dropdown = customSelects[cmd];
+ if (typeof dropdown != "undefined") {
+ options = dropdown.options;
+ context = dropdown.context;
+ } else {
+ alert("ERROR [createSelect]:\nCan't find the requested dropdown definition");
+ }
+ break;
+ }
+ if (options) {
+ el = document.createElement("select");
+ var obj = {
+ name : txt, // field name
+ element : el, // the UI element (SELECT)
+ enabled : true, // is it enabled?
+ text : false, // enabled in text mode?
+ cmd : cmd, // command ID
+ state : setButtonStatus, // for changing state
+ context : context
+ };
+ tb_objects[txt] = obj;
+ for (var i in options) {
+ var op = document.createElement("option");
+ op.appendChild(document.createTextNode(i));
+ op.value = options[i];
+ el.appendChild(op);
+ }
+ HTMLArea._addEvent(el, "change", function () {
+ editor._comboSelected(el, txt);
+ });
+ }
+ return el;
+ }; // END of function: createSelect
+
+ // appends a new button to toolbar
+ function createButton(txt) {
+ // the element that will be created
+ var el = null;
+ var btn = null;
+ switch (txt) {
+ case "separator":
+ el = document.createElement("div");
+ el.className = "separator";
+ break;
+ case "space":
+ el = document.createElement("div");
+ el.className = "space";
+ break;
+ case "linebreak":
+ newLine();
+ return false;
+ case "textindicator":
+ el = document.createElement("div");
+ el.appendChild(document.createTextNode("A"));
+ el.className = "indicator";
+ el.title = HTMLArea.I18N.tooltips.textindicator;
+ var obj = {
+ name : txt, // the button name (i.e. 'bold')
+ element : el, // the UI element (DIV)
+ enabled : true, // is it enabled?
+ active : false, // is it pressed?
+ text : false, // enabled in text mode?
+ cmd : "textindicator", // the command ID
+ state : setButtonStatus // for changing state
+ };
+ tb_objects[txt] = obj;
+ break;
+ default:
+ btn = editor.config.btnList[txt];
+ }
+ if (!el && btn) {
+ el = document.createElement("div");
+ el.title = btn[0];
+ el.className = "button";
+ // let's just pretend we have a button object, and
+ // assign all the needed information to it.
+ var obj = {
+ name : txt, // the button name (i.e. 'bold')
+ element : el, // the UI element (DIV)
+ enabled : true, // is it enabled?
+ active : false, // is it pressed?
+ text : btn[2], // enabled in text mode?
+ cmd : btn[3], // the command ID
+ state : setButtonStatus, // for changing state
+ context : btn[4] || null // enabled in a certain context?
+ };
+ tb_objects[txt] = obj;
+ // handlers to emulate nice flat toolbar buttons
+ HTMLArea._addEvent(el, "mouseover", function () {
+ if (obj.enabled) {
+ HTMLArea._addClass(el, "buttonHover");
+ }
+ });
+ HTMLArea._addEvent(el, "mouseout", function () {
+ if (obj.enabled) with (HTMLArea) {
+ _removeClass(el, "buttonHover");
+ _removeClass(el, "buttonActive");
+ (obj.active) && _addClass(el, "buttonPressed");
+ }
+ });
+ HTMLArea._addEvent(el, "mousedown", function (ev) {
+ if (obj.enabled) with (HTMLArea) {
+ _addClass(el, "buttonActive");
+ _removeClass(el, "buttonPressed");
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ // when clicked, do the following:
+ HTMLArea._addEvent(el, "click", function (ev) {
+ if (obj.enabled) with (HTMLArea) {
+ _removeClass(el, "buttonActive");
+ _removeClass(el, "buttonHover");
+ obj.cmd(editor, obj.name, obj);
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ var img = document.createElement("img");
+ img.src = btn[1];
+ img.style.width = "18px";
+ img.style.height = "18px";
+ el.appendChild(img);
+ } else if (!el) {
+ el = createSelect(txt);
+ }
+ if (el) {
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.appendChild(el);
+ } else {
+ alert("FIXME: Unknown toolbar item: " + txt);
+ }
+ return el;
+ };
+
+ var first = true;
+ for (var i in this.config.toolbar) {
+ if (!first) {
+ createButton("linebreak");
+ } else {
+ first = false;
+ }
+ var group = this.config.toolbar[i];
+ for (var j in group) {
+ var code = group[j];
+ if (/^([IT])\[(.*?)\]/.test(code)) {
+ // special case, create text label
+ var l7ed = RegExp.$1 == "I"; // localized?
+ var label = RegExp.$2;
+ if (l7ed) {
+ label = HTMLArea.I18N.custom[label];
+ }
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.className = "label";
+ tb_cell.innerHTML = label;
+ } else {
+ createButton(code);
+ }
+ }
+ }
+
+ this._htmlArea.appendChild(toolbar);
+};
+
+HTMLArea.prototype._createStatusBar = function() {
+ var statusbar = document.createElement("div");
+ statusbar.className = "statusBar";
+ this._htmlArea.appendChild(statusbar);
+ this._statusBar = statusbar;
+ statusbar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ // creates a holder for the path view
+ div = document.createElement("span");
+ div.className = "statusBarTree";
+ this._statusBarTree = div;
+ this._statusBar.appendChild(div);
+ if (!this.config.statusBar) {
+ // disable it...
+ statusbar.style.display = "none";
+ }
+};
+
+// Creates the HTMLArea object and replaces the textarea with it.
+HTMLArea.prototype.generate = function () {
+ var editor = this; // we'll need "this" in some nested functions
+ // get the textarea
+ var textarea = this._textArea;
+ if (typeof textarea == "string") {
+ // it's not element but ID
+ this._textArea = textarea = document.getElementById(textarea);
+ }
+ this._ta_size = {
+ w: textarea.offsetWidth,
+ h: textarea.offsetHeight
+ };
+ textarea.style.display = "none";
+
+ // create the editor framework
+ var htmlarea = document.createElement("div");
+ htmlarea.className = "htmlarea";
+ this._htmlArea = htmlarea;
+
+ // insert the editor before the textarea.
+ textarea.parentNode.insertBefore(htmlarea, textarea);
+
+ if (textarea.form) {
+ // we have a form, on submit get the HTMLArea content and
+ // update original textarea.
+ var f = textarea.form;
+ if (typeof f.onsubmit == "function") {
+ var funcref = f.onsubmit;
+ if (typeof f.__msh_prevOnSubmit == "undefined") {
+ f.__msh_prevOnSubmit = [];
+ }
+ f.__msh_prevOnSubmit.push(funcref);
+ }
+ f.onsubmit = function() {
+ editor._textArea.value = editor.getHTML();
+ var a = this.__msh_prevOnSubmit;
+ // call previous submit methods if they were there.
+ if (typeof a != "undefined") {
+ for (var i in a) {
+ a[i]();
+ }
+ }
+ };
+ }
+
+ // add a handler for the "back/forward" case -- on body.unload we save
+ // the HTML content into the original textarea.
+ window.onunload = function() {
+ editor._textArea.value = editor.getHTML();
+ };
+
+ // creates & appends the toolbar
+ this._createToolbar();
+
+ // create the IFRAME
+ var iframe = document.createElement("iframe");
+ htmlarea.appendChild(iframe);
+
+ this._iframe = iframe;
+
+ // creates & appends the status bar, if the case
+ this._createStatusBar();
+
+ // remove the default border as it keeps us from computing correctly
+ // the sizes. (somebody tell me why doesn't this work in IE)
+
+ if (!HTMLArea.is_ie) {
+ iframe.style.borderWidth = "1px";
+ // iframe.frameBorder = "1";
+ // iframe.marginHeight = "0";
+ // iframe.marginWidth = "0";
+ }
+
+ // size the IFRAME according to user's prefs or initial textarea
+ var height = (this.config.height == "auto" ? (this._ta_size.h + "px") : this.config.height);
+ height = parseInt(height);
+ var width = (this.config.width == "auto" ? (this._ta_size.w + "px") : this.config.width);
+ width = parseInt(width);
+
+ if (!HTMLArea.is_ie) {
+ height -= 2;
+ width -= 2;
+ }
+
+ iframe.style.width = width + "px";
+ if (this.config.sizeIncludesToolbar) {
+ // substract toolbar height
+ height -= this._toolbar.offsetHeight;
+ height -= this._statusBar.offsetHeight;
+ }
+ if (height < 0) {
+ height = 0;
+ }
+ iframe.style.height = height + "px";
+
+ // the editor including the toolbar now have the same size as the
+ // original textarea.. which means that we need to reduce that a bit.
+ textarea.style.width = iframe.style.width;
+ textarea.style.height = iframe.style.height;
+
+ // IMPORTANT: we have to allow Mozilla a short time to recognize the
+ // new frame. Otherwise we get a stupid exception.
+ function initIframe() {
+ var doc = editor._iframe.contentWindow.document;
+ if (!doc) {
+ // Try again..
+ // FIXME: don't know what else to do here. Normally
+ // we'll never reach this point.
+ if (HTMLArea.is_gecko) {
+ setTimeout(initIframe, 10);
+ return false;
+ } else {
+ alert("ERROR: IFRAME can't be initialized.");
+ }
+ }
+ if (HTMLArea.is_gecko) {
+ // enable editable mode for Mozilla
+ doc.designMode = "on";
+ }
+ editor._doc = doc;
+ if (!editor.config.fullPage) {
+ doc.open();
+ var html = "<html>\n";
+ html += "<head>\n";
+ html += "<style>" + editor.config.pageStyle + "</style>\n";
+ html += "</head>\n";
+ html += "<body>\n";
+ html += editor._textArea.value;
+ html += "</body>\n";
+ html += "</html>";
+ doc.write(html);
+ doc.close();
+ } else {
+ var html = editor._textArea.value;
+ if (html.match(HTMLArea.RE_doctype)) {
+ editor.setDoctype(RegExp.$1);
+ html = html.replace(HTMLArea.RE_doctype, "");
+ }
+ doc.open();
+ doc.write(html);
+ doc.close();
+ }
+
+ if (HTMLArea.is_ie) {
+ // enable editable mode for IE. For some reason this
+ // doesn't work if done in the same place as for Gecko
+ // (above).
+ doc.body.contentEditable = true;
+ }
+
+ editor.focusEditor();
+ // intercept some events; for updating the toolbar & keyboard handlers
+ HTMLArea._addEvents
+ (doc, ["keydown", "keypress", "mousedown", "mouseup", "drag"],
+ function (event) {
+ return editor._editorEvent(HTMLArea.is_ie ? editor._iframe.contentWindow.event : event);
+ });
+ editor.updateToolbar();
+ };
+ setTimeout(initIframe, HTMLArea.is_gecko ? 10 : 0);
+};
+
+// Switches editor mode; parameter can be "textmode" or "wysiwyg". If no
+// parameter was passed this function toggles between modes.
+HTMLArea.prototype.setMode = function(mode) {
+ if (typeof mode == "undefined") {
+ mode = ((this._editMode == "textmode") ? "wysiwyg" : "textmode");
+ }
+ switch (mode) {
+ case "textmode":
+ this._textArea.value = this.getHTML();
+ this._iframe.style.display = "none";
+ this._textArea.style.display = "block";
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = HTMLArea.I18N.msg["TEXT_MODE"];
+ }
+ break;
+ case "wysiwyg":
+ if (HTMLArea.is_gecko) {
+ // disable design mode before changing innerHTML
+ this._doc.designMode = "off";
+ }
+ if (!this.config.fullPage)
+ this._doc.body.innerHTML = this.getHTML();
+ else
+ this.setFullHTML(this.getHTML());
+ this._iframe.style.display = "block";
+ this._textArea.style.display = "none";
+ if (HTMLArea.is_gecko) {
+ // we need to refresh that info for Moz-1.3a
+ this._doc.designMode = "on";
+ }
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = '';
+ this._statusBar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ this._statusBar.appendChild(this._statusBarTree);
+ }
+ break;
+ default:
+ alert("Mode <" + mode + "> not defined!");
+ return false;
+ }
+ this._editMode = mode;
+ this.focusEditor();
+};
+
+HTMLArea.prototype.setFullHTML = function(html) {
+ var save_multiline = RegExp.multiline;
+ RegExp.multiline = true;
+ if (html.match(HTMLArea.RE_doctype)) {
+ this.setDoctype(RegExp.$1);
+ html = html.replace(HTMLArea.RE_doctype, "");
+ }
+ RegExp.multiline = save_multiline;
+ if (!HTMLArea.is_ie) {
+ if (html.match(HTMLArea.RE_head))
+ this._doc.getElementsByTagName("head")[0].innerHTML = RegExp.$1;
+ if (html.match(HTMLArea.RE_body))
+ this._doc.getElementsByTagName("body")[0].innerHTML = RegExp.$1;
+ } else {
+ var html_re = /<html>((.|\n)*?)<\/html>/i;
+ html = html.replace(html_re, "$1");
+ this._doc.open();
+ this._doc.write(html);
+ this._doc.close();
+ this._doc.body.contentEditable = true;
+ return true;
+ }
+};
+
+/***************************************************
+ * Category: PLUGINS
+ ***************************************************/
+
+// Create the specified plugin and register it with this HTMLArea
+HTMLArea.prototype.registerPlugin = function() {
+ var plugin = arguments[0];
+ if (typeof plugin == "string")
+ plugin = eval(plugin);
+ var args = [];
+ for (var i = 1; i < arguments.length; ++i)
+ args.push(arguments[i]);
+ var obj = new plugin(this, args);
+ if (obj) {
+ var clone = {};
+ var info = plugin._pluginInfo;
+ for (var i in info)
+ clone[i] = info[i];
+ clone.instance = obj;
+ this.plugins[plugin._pluginInfo.name] = clone;
+ } else
+ alert("Can't register plugin " + plugin.toString() + ".");
+};
+
+// static function that loads the required plugin and lang file, based on the
+// language loaded already for HTMLArea. You better make sure that the plugin
+// _has_ that language, otherwise shit might happen ;-)
+HTMLArea.loadPlugin = function(pluginName) {
+ var editorurl = '';
+ if (typeof _editor_url != "undefined") {
+ editorurl = _editor_url + "/";
+ }
+ var dir = editorurl + "plugins/" + pluginName;
+ var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g,
+ function (str, l1, l2, l3) {
+ return l1 + "-" + l2.toLowerCase() + l3;
+ }).toLowerCase() + ".js";
+ document.write("<script type='text/javascript' src='" + dir + "/" + plugin + "'></script>");
+ document.write("<script type='text/javascript' src='" + dir + "/lang/" + HTMLArea.I18N.lang + ".js'></script>");
+};
+
+/***************************************************
+ * Category: EDITOR UTILITIES
+ ***************************************************/
+
+HTMLArea.prototype.forceRedraw = function() {
+ this._doc.body.style.visibility = "hidden";
+ this._doc.body.style.visibility = "visible";
+ // this._doc.body.innerHTML = this.getInnerHTML();
+};
+
+// focuses the iframe window. returns a reference to the editor document.
+HTMLArea.prototype.focusEditor = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : this._iframe.contentWindow.focus(); break;
+ case "textmode": this._textArea.focus(); break;
+ default : alert("ERROR: mode " + this._editMode + " is not defined");
+ }
+ return this._doc;
+};
+
+// takes a snapshot of the current text (for undo)
+HTMLArea.prototype._undoTakeSnapshot = function() {
+ ++this._undoPos;
+ if (this._undoPos >= this.config.undoSteps) {
+ // remove the first element
+ this._undoQueue.shift();
+ --this._undoPos;
+ }
+ // use the fasted method (getInnerHTML);
+ var take = true;
+ var txt = this.getInnerHTML();
+ if (this._undoPos > 0)
+ take = (this._undoQueue[this._undoPos - 1] != txt);
+ if (take) {
+ this._undoQueue[this._undoPos] = txt;
+ } else {
+ this._undoPos--;
+ }
+};
+
+HTMLArea.prototype.undo = function() {
+ if (this._undoPos > 0) {
+ var txt = this._undoQueue[--this._undoPos];
+ if (txt) this.setHTML(txt);
+ else ++this._undoPos;
+ }
+};
+
+HTMLArea.prototype.redo = function() {
+ if (this._undoPos < this._undoQueue.length - 1) {
+ var txt = this._undoQueue[++this._undoPos];
+ if (txt) this.setHTML(txt);
+ else --this._undoPos;
+ }
+};
+
+// updates enabled/disable/active state of the toolbar elements
+HTMLArea.prototype.updateToolbar = function(noStatus) {
+ var doc = this._doc;
+ var text = (this._editMode == "textmode");
+ var ancestors = null;
+ if (!text) {
+ ancestors = this.getAllAncestors();
+ if (this.config.statusBar && !noStatus) {
+ this._statusBarTree.innerHTML = ''; // clear
+ for (var i = ancestors.length; --i >= 0;) {
+ var el = ancestors[i];
+ if (!el) {
+ // hell knows why we get here; this
+ // could be a classic example of why
+ // it's good to check for conditions
+ // that are impossible to happen ;-)
+ continue;
+ }
+ var a = document.createElement("a");
+ a.href = "#";
+ a.el = el;
+ a.editor = this;
+ a.onclick = function() {
+ this.blur();
+ this.editor.selectNodeContents(this.el);
+ this.editor.updateToolbar(true);
+ return false;
+ };
+ a.oncontextmenu = function() {
+ // TODO: add context menu here
+ this.blur();
+ var info = "Inline style:\n\n";
+ info += this.el.style.cssText.split(/;\s*/).join(";\n");
+ alert(info);
+ return false;
+ };
+ var txt = el.tagName.toLowerCase();
+ a.title = el.style.cssText;
+ if (el.id) {
+ txt += "#" + el.id;
+ }
+ if (el.className) {
+ txt += "." + el.className;
+ }
+ a.appendChild(document.createTextNode(txt));
+ this._statusBarTree.appendChild(a);
+ if (i != 0) {
+ this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
+ }
+ }
+ }
+ }
+ for (var i in this._toolbarObjects) {
+ var btn = this._toolbarObjects[i];
+ var cmd = i;
+ var inContext = true;
+ if (btn.context && !text) {
+ inContext = false;
+ var context = btn.context;
+ var attrs = [];
+ if (/(.*)\[(.*?)\]/.test(context)) {
+ context = RegExp.$1;
+ attrs = RegExp.$2.split(",");
+ }
+ context = context.toLowerCase();
+ var match = (context == "*");
+ for (var k in ancestors) {
+ if (!ancestors[k]) {
+ // the impossible really happens.
+ continue;
+ }
+ if (match || (ancestors[k].tagName.toLowerCase() == context)) {
+ inContext = true;
+ for (var ka in attrs) {
+ if (!eval("ancestors[k]." + attrs[ka])) {
+ inContext = false;
+ break;
+ }
+ }
+ if (inContext) {
+ break;
+ }
+ }
+ }
+ }
+ btn.state("enabled", (!text || btn.text) && inContext);
+ if (typeof cmd == "function") {
+ continue;
+ }
+ // look-it-up in the custom dropdown boxes
+ var dropdown = this.config.customSelects[cmd];
+ if ((!text || btn.text) && (typeof dropdown != "undefined")) {
+ dropdown.refresh(this);
+ continue;
+ }
+ switch (cmd) {
+ case "fontname":
+ case "fontsize":
+ case "formatblock":
+ if (!text) {
+ var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
+ if (!value) {
+ // FIXME: what do we do here?
+ break;
+ }
+ // HACK -- retrieve the config option for this
+ // combo box. We rely on the fact that the
+ // variable in config has the same name as
+ // button name in the toolbar.
+ var options = this.config[cmd];
+ var k = 0;
+ // btn.element.selectedIndex = 0;
+ for (var j in options) {
+ // FIXME: the following line is scary.
+ if ((j.toLowerCase() == value) ||
+ (options[j].substr(0, value.length).toLowerCase() == value)) {
+ btn.element.selectedIndex = k;
+ break;
+ }
+ ++k;
+ }
+ }
+ break;
+ case "textindicator":
+ if (!text) {
+ try {with (btn.element.style) {
+ backgroundColor = HTMLArea._makeColor(
+ doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor"));
+ if (/transparent/i.test(backgroundColor)) {
+ // Mozilla
+ backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor"));
+ }
+ color = HTMLArea._makeColor(doc.queryCommandValue("forecolor"));
+ fontFamily = doc.queryCommandValue("fontname");
+ fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
+ fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
+ }} catch (e) {
+ // alert(e + "\n\n" + cmd);
+ }
+ }
+ break;
+ case "htmlmode": btn.state("active", text); break;
+ default:
+ try {
+ btn.state("active", (!text && doc.queryCommandState(cmd)));
+ } catch (e) {}
+ }
+ }
+ // take undo snapshots
+ if (!this._timerUndo) {
+ this._undoTakeSnapshot();
+ var editor = this;
+ this._timerUndo = setTimeout(function() {
+ editor._timerUndo = null;
+ }, this.config.undoTimeout);
+ }
+ // check if any plugins have registered refresh handlers
+ for (var i in this.plugins) {
+ var plugin = this.plugins[i].instance;
+ if (typeof plugin.onUpdateToolbar == "function")
+ plugin.onUpdateToolbar();
+ }
+};
+
+/** Returns a node after which we can insert other nodes, in the current
+ * selection. The selection is removed. It splits a text node, if needed.
+ */
+HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted) {
+ if (!HTMLArea.is_ie) {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ // remove the current selection
+ sel.removeAllRanges();
+ range.deleteContents();
+ var node = range.startContainer;
+ var pos = range.startOffset;
+ switch (node.nodeType) {
+ case 3: // Node.TEXT_NODE
+ // we have to split it at the caret position.
+ if (toBeInserted.nodeType == 3) {
+ // do optimized insertion
+ node.insertData(pos, toBeInserted.data);
+ range = this._createRange();
+ range.setEnd(node, pos + toBeInserted.length);
+ range.setStart(node, pos + toBeInserted.length);
+ sel.addRange(range);
+ } else {
+ node = node.splitText(pos);
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.parentNode.insertBefore(toBeInserted, node);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ }
+ break;
+ case 1: // Node.ELEMENT_NODE
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.insertBefore(toBeInserted, node.childNodes[pos]);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ break;
+ }
+ } else {
+ return null; // this function not yet used for IE <FIXME>
+ }
+};
+
+// Returns the deepest node that contains both endpoints of the selection.
+HTMLArea.prototype.getParentElement = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ return range.parentElement ? range.parentElement() : this._doc.body;
+ } else {
+ var p = range.commonAncestorContainer;
+ while (p.nodeType == 3) {
+ p = p.parentNode;
+ }
+ return p;
+ }
+};
+
+// Returns an array with all the ancestor nodes of the selection.
+HTMLArea.prototype.getAllAncestors = function() {
+ var p = this.getParentElement();
+ var a = [];
+ while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
+ a.push(p);
+ p = p.parentNode;
+ }
+ a.push(this._doc.body);
+ return a;
+};
+
+// Selects the contents inside the given node
+HTMLArea.prototype.selectNodeContents = function(node, pos) {
+ this.focusEditor();
+ this.forceRedraw();
+ var range;
+ var collapsed = (typeof pos != "undefined");
+ if (HTMLArea.is_ie) {
+ range = this._doc.body.createTextRange();
+ range.moveToElementText(node);
+ (collapsed) && range.collapse(pos);
+ range.select();
+ } else {
+ var sel = this._getSelection();
+ range = this._doc.createRange();
+ range.selectNodeContents(node);
+ (collapsed) && range.collapse(pos);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+};
+
+/** Call this function to insert HTML code at the current position. It deletes
+ * the selection, if any.
+ */
+HTMLArea.prototype.insertHTML = function(html) {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(html);
+ } else {
+ // construct a new document fragment with the given HTML
+ var fragment = this._doc.createDocumentFragment();
+ var div = this._doc.createElement("div");
+ div.innerHTML = html;
+ while (div.firstChild) {
+ // the following call also removes the node from div
+ fragment.appendChild(div.firstChild);
+ }
+ // this also removes the selection
+ var node = this.insertNodeAtSelection(fragment);
+ }
+};
+
+/**
+ * Call this function to surround the existing HTML code in the selection with
+ * your tags. FIXME: buggy! This function will be deprecated "soon".
+ */
+HTMLArea.prototype.surroundHTML = function(startTag, endTag) {
+ var html = this.getSelectedHTML();
+ // the following also deletes the selection
+ this.insertHTML(startTag + html + endTag);
+};
+
+/// Retrieve the selected block
+HTMLArea.prototype.getSelectedHTML = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var existing = null;
+ if (HTMLArea.is_ie) {
+ existing = range.htmlText;
+ } else {
+ existing = HTMLArea.getHTML(range.cloneContents(), false);
+ }
+ return existing;
+};
+
+// Called when the user clicks on "InsertImage" button
+HTMLArea.prototype._insertImage = function() {
+ var editor = this; // for nested functions
+ this._popupDialog("insert_image.html", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var sel = editor._getSelection();
+ var range = editor._createRange(sel);
+ editor._doc.execCommand("insertimage", false, param["f_url"]);
+ var img = null;
+ if (HTMLArea.is_ie) {
+ img = range.parentElement();
+ // wonder if this works...
+ if (img.tagName.toLowerCase() != "img") {
+ img = img.previousSibling;
+ }
+ } else {
+ img = range.startContainer.previousSibling;
+ }
+ for (field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_alt" : img.alt = value; break;
+ case "f_border" : img.border = parseInt(value); break;
+ case "f_align" : img.align = value; break;
+ case "f_vert" : img.vspace = parseInt(value); break;
+ case "f_horiz" : img.hspace = parseInt(value); break;
+ }
+ }
+ }, null);
+};
+
+// Called when the user clicks the Insert Table button
+HTMLArea.prototype._insertTable = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var editor = this; // for nested functions
+ this._popupDialog("insert_table.html", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var doc = editor._doc;
+ // create the table element
+ var table = doc.createElement("table");
+ // assign the given arguments
+ for (var field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_width" : table.style.width = value + param["f_unit"]; break;
+ case "f_align" : table.align = value; break;
+ case "f_border" : table.border = parseInt(value); break;
+ case "f_spacing" : table.cellspacing = parseInt(value); break;
+ case "f_padding" : table.cellpadding = parseInt(value); break;
+ }
+ }
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+ for (var i = 0; i < param["f_rows"]; ++i) {
+ var tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ for (var j = 0; j < param["f_cols"]; ++j) {
+ var td = doc.createElement("td");
+ tr.appendChild(td);
+ // Mozilla likes to see something inside the cell.
+ (HTMLArea.is_gecko) && td.appendChild(doc.createElement("br"));
+ }
+ }
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(table.outerHTML);
+ } else {
+ // insert the table
+ editor.insertNodeAtSelection(table);
+ }
+ return true;
+ }, null);
+};
+
+/***************************************************
+ * Category: EVENT HANDLERS
+ ***************************************************/
+
+// el is reference to the SELECT object
+// txt is the name of the select field, as in config.toolbar
+HTMLArea.prototype._comboSelected = function(el, txt) {
+ this.focusEditor();
+ var value = el.options[el.selectedIndex].value;
+ switch (txt) {
+ case "fontname":
+ case "fontsize": this.execCommand(txt, false, value); break;
+ case "formatblock":
+ (HTMLArea.is_ie) && (value = "<" + value + ">");
+ this.execCommand(txt, false, value);
+ break;
+ default:
+ // try to look it up in the registered dropdowns
+ var dropdown = this.config.customSelects[txt];
+ if (typeof dropdown != "undefined") {
+ dropdown.action(this);
+ } else {
+ alert("FIXME: combo box " + txt + " not implemented");
+ }
+ }
+};
+
+// the execCommand function (intercepts some commands and replaces them with
+// our own implementation)
+HTMLArea.prototype.execCommand = function(cmdID, UI, param) {
+ var editor = this; // for nested functions
+ this.focusEditor();
+ switch (cmdID.toLowerCase()) {
+ case "htmlmode" : this.setMode(); break;
+ case "hilitecolor":
+ (HTMLArea.is_ie) && (cmdID = "backcolor");
+ case "forecolor":
+ this._popupDialog("select_color.html", function(color) {
+ if (color) { // selection not canceled
+ editor._doc.execCommand(cmdID, false, "#" + color);
+ }
+ }, HTMLArea._colorToRgb(this._doc.queryCommandValue(cmdID)));
+ break;
+ case "createlink":
+ if (HTMLArea.is_ie || !UI) {
+ this._doc.execCommand(cmdID, UI, param);
+ } else {
+ // browser is Mozilla & wants UI
+ var param;
+ if ((param = prompt("Enter URL"))) {
+ this._doc.execCommand(cmdID, false, param);
+ }
+ }
+ break;
+ case "popupeditor":
+ if (HTMLArea.is_ie) {
+ window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
+ "toolbar=no,location=no,directories=no,status=no,menubar=no," +
+ "scrollbars=no,resizable=yes,width=640,height=480");
+ } else {
+ window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
+ "toolbar=no,menubar=no,personalbar=no,width=640,height=480," +
+ "scrollbars=no,resizable=yes");
+ }
+ // pass this object to the newly opened window
+ HTMLArea._object = this;
+ break;
+ case "undo": this.undo(); break;
+ case "redo": this.redo(); break;
+ case "inserttable": this._insertTable(); break;
+ case "insertimage": this._insertImage(); break;
+ case "about" : this._popupDialog("about.html", null, this); break;
+ case "showhelp" : window.open(this.config.editorURL + "reference.html", "ha_help"); break;
+ default: this._doc.execCommand(cmdID, UI, param);
+ }
+ this.updateToolbar();
+ return false;
+};
+
+/** A generic event handler for things that happen in the IFRAME's document.
+ * This function also handles key bindings. */
+HTMLArea.prototype._editorEvent = function(ev) {
+ var editor = this;
+ var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");
+ if (keyEvent && ev.ctrlKey) {
+ var sel = null;
+ var range = null;
+ var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
+ var cmd = null;
+ var value = null;
+ switch (key) {
+ case 'a':
+ if (!HTMLArea.is_ie) {
+ // KEY select all
+ sel = this._getSelection();
+ sel.removeAllRanges();
+ range = this._createRange();
+ range.selectNodeContents(this._doc.body);
+ sel.addRange(range);
+ HTMLArea._stopEvent(ev);
+ }
+ break;
+
+ // simple key commands follow
+
+ case 'b': cmd = "bold"; break;
+ case 'i': cmd = "italic"; break;
+ case 'u': cmd = "underline"; break;
+ case 's': cmd = "strikethrough"; break;
+ case 'l': cmd = "justifyleft"; break;
+ case 'e': cmd = "justifycenter"; break;
+ case 'r': cmd = "justifyright"; break;
+ case 'j': cmd = "justifyfull"; break;
+ case 'z': cmd = "undo"; break;
+ case 'y': cmd = "redo"; break;
+
+ // headings
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ cmd = "formatblock";
+ value = "h" + key;
+ if (HTMLArea.is_ie) {
+ value = "<" + value + ">";
+ }
+ break;
+ }
+ if (cmd) {
+ // execute simple command
+ this.execCommand(cmd, false, value);
+ HTMLArea._stopEvent(ev);
+ }
+ }
+ /*
+ else if (keyEvent) {
+ // other keys here
+ switch (ev.keyCode) {
+ case 13: // KEY enter
+ // if (HTMLArea.is_ie) {
+ this.insertHTML("<br />");
+ HTMLArea._stopEvent(ev);
+ // }
+ break;
+ }
+ }
+ */
+ // update the toolbar state after some time
+ if (editor._timerToolbar) {
+ clearTimeout(editor._timerToolbar);
+ }
+ editor._timerToolbar = setTimeout(function() {
+ editor.updateToolbar();
+ editor._timerToolbar = null;
+ }, 50);
+};
+
+// retrieve the HTML
+HTMLArea.prototype.getHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ return HTMLArea.getHTML(this._doc.body, false);
+ else
+ return this.doctype + "\n" + HTMLArea.getHTML(this._doc.documentElement, true);
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// retrieve the HTML (fastest version, but uses innerHTML)
+HTMLArea.prototype.getInnerHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ return this._doc.body.innerHTML;
+ else
+ return this.doctype + "\n" + this._doc.documentElement.innerHTML;
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// completely change the HTML inside
+HTMLArea.prototype.setHTML = function(html) {
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ this._doc.body.innerHTML = html;
+ else
+ // this._doc.documentElement.innerHTML = html;
+ this._doc.body.innerHTML = html;
+ break;
+ case "textmode" : this._textArea.value = html; break;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// sets the given doctype (useful when config.fullPage is true)
+HTMLArea.prototype.setDoctype = function(doctype) {
+ this.doctype = doctype;
+};
+
+/***************************************************
+ * Category: UTILITY FUNCTIONS
+ ***************************************************/
+
+// browser identification
+
+HTMLArea.agt = navigator.userAgent.toLowerCase();
+HTMLArea.is_ie = ((HTMLArea.agt.indexOf("msie") != -1) && (HTMLArea.agt.indexOf("opera") == -1));
+HTMLArea.is_opera = (HTMLArea.agt.indexOf("opera") != -1);
+HTMLArea.is_mac = (HTMLArea.agt.indexOf("mac") != -1);
+HTMLArea.is_mac_ie = (HTMLArea.is_ie && HTMLArea.is_mac);
+HTMLArea.is_win_ie = (HTMLArea.is_ie && !HTMLArea.is_mac);
+HTMLArea.is_gecko = (navigator.product == "Gecko");
+
+// variable used to pass the object to the popup editor window.
+HTMLArea._object = null;
+
+// FIXME!!! this should return false for IE < 5.5
+HTMLArea.checkSupportedBrowser = function() {
+ if (HTMLArea.is_gecko) {
+ if (navigator.productSub < 20021201) {
+ alert("You need at least Mozilla-1.3 Alpha.\n" +
+ "Sorry, your Gecko is not supported.");
+ return false;
+ }
+ if (navigator.productSub < 20030210) {
+ alert("Mozilla < 1.3 Beta is not supported!\n" +
+ "I'll try, though, but it might not work.");
+ }
+ }
+ return HTMLArea.is_gecko || HTMLArea.is_ie;
+};
+
+// selection & ranges
+
+// returns the current selection object
+HTMLArea.prototype._getSelection = function() {
+ if (HTMLArea.is_ie) {
+ return this._doc.selection;
+ } else {
+ return this._iframe.contentWindow.getSelection();
+ }
+};
+
+// returns a range for the current selection
+HTMLArea.prototype._createRange = function(sel) {
+ if (HTMLArea.is_ie) {
+ return sel.createRange();
+ } else {
+ this.focusEditor();
+ if (typeof sel != "undefined") {
+ return sel.getRangeAt(0);
+ } else {
+ return this._doc.createRange();
+ }
+ }
+};
+
+// event handling
+
+HTMLArea._addEvent = function(el, evname, func) {
+ if (HTMLArea.is_ie) {
+ el.attachEvent("on" + evname, func);
+ } else {
+ el.addEventListener(evname, func, true);
+ }
+};
+
+HTMLArea._addEvents = function(el, evs, func) {
+ for (var i in evs) {
+ HTMLArea._addEvent(el, evs[i], func);
+ }
+};
+
+HTMLArea._removeEvent = function(el, evname, func) {
+ if (HTMLArea.is_ie) {
+ el.detachEvent("on" + evname, func);
+ } else {
+ el.removeEventListener(evname, func, true);
+ }
+};
+
+HTMLArea._removeEvents = function(el, evs, func) {
+ for (var i in evs) {
+ HTMLArea._removeEvent(el, evs[i], func);
+ }
+};
+
+HTMLArea._stopEvent = function(ev) {
+ if (HTMLArea.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+};
+
+HTMLArea._removeClass = function(el, className) {
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
+};
+
+HTMLArea._addClass = function(el, className) {
+ // remove the class first, if already there
+ HTMLArea._removeClass(el, className);
+ el.className += " " + className;
+};
+
+HTMLArea._hasClass = function(el, className) {
+ if (!(el && el.className)) {
+ return false;
+ }
+ var cls = el.className.split(" ");
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] == className) {
+ return true;
+ }
+ }
+ return false;
+};
+
+HTMLArea.isBlockElement = function(el) {
+ var blockTags = " body form textarea fieldset ul ol dl li div " +
+ "p h1 h2 h3 h4 h5 h6 quote pre table thead " +
+ "tbody tfoot tr td iframe address ";
+ return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+HTMLArea.needsClosingTag = function(el) {
+ var closingTags = " head script style div span tr td tbody table em strong font a title ";
+ return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+// performs HTML encoding of some given string
+HTMLArea.htmlEncode = function(str) {
+ // we don't need regexp for that, but.. so be it for now.
+ str = str.replace(/&/ig, "&");
+ str = str.replace(/</ig, "<");
+ str = str.replace(/>/ig, ">");
+ str = str.replace(/\x22/ig, """);
+ // \x22 means '"' -- we use hex reprezentation so that we don't disturb
+ // JS compressors (well, at least mine fails.. ;)
+ return str;
+};
+
+// Retrieves the HTML code from the given node. This is a replacement for
+// getting innerHTML, using standard DOM calls.
+HTMLArea.getHTML = function(root, outputRoot) {
+ var html = "";
+ switch (root.nodeType) {
+ case 1: // Node.ELEMENT_NODE
+ case 11: // Node.DOCUMENT_FRAGMENT_NODE
+ var closed;
+ var i;
+ var root_tag = root.tagName.toLowerCase();
+ if (HTMLArea.is_ie && root_tag == "head") {
+ if (outputRoot)
+ html += "<head>";
+ // lowercasize
+ var save_multiline = RegExp.multiline;
+ RegExp.multiline = true;
+ var txt = root.innerHTML.replace(HTMLArea.RE_tagName, function(str, p1, p2) {
+ return p1 + p2.toLowerCase();
+ });
+ RegExp.multiline = save_multiline;
+ html += txt;
+ if (outputRoot)
+ html += "</head>";
+ break;
+ } else if (outputRoot) {
+ closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));
+ html = "<" + root.tagName.toLowerCase();
+ var attrs = root.attributes;
+ for (i = 0; i < attrs.length; ++i) {
+ var a = attrs.item(i);
+ if (!a.specified) {
+ continue;
+ }
+ var name = a.nodeName.toLowerCase();
+ if (/_moz|contenteditable/.test(name)) {
+ // avoid certain attributes
+ continue;
+ }
+ var value;
+ if (name != "style") {
+ // IE5.5 reports 25 when cellSpacing is
+ // 1; other values might be doomed too.
+ // For this reason we extract the
+ // values directly from the root node.
+ // I'm starting to HATE JavaScript
+ // development. Browser differences
+ // suck.
+ if (typeof root[a.nodeName] != "undefined") {
+ value = root[a.nodeName];
+ } else {
+ value = a.nodeValue;
+ }
+ } else { // IE fails to put style in attributes list
+ // FIXME: cssText reported by IE is UPPERCASE
+ value = root.style.cssText;
+ }
+ if (/_moz/.test(value)) {
+ // Mozilla reports some special tags
+ // here; we don't need them.
+ continue;
+ }
+ html += " " + name + '="' + value + '"';
+ }
+ html += closed ? " />" : ">";
+ }
+ for (i = root.firstChild; i; i = i.nextSibling) {
+ html += HTMLArea.getHTML(i, true);
+ }
+ if (outputRoot && !closed) {
+ html += "</" + root.tagName.toLowerCase() + ">";
+ }
+ break;
+ case 3: // Node.TEXT_NODE
+ html = HTMLArea.htmlEncode(root.data);
+ break;
+ case 8: // Node.COMMENT_NODE
+ html = "<!--" + root.data + "-->";
+ break; // skip comments, for now.
+ }
+ return html;
+};
+
+// creates a rgb-style color from a number
+HTMLArea._makeColor = function(v) {
+ if (typeof v != "number") {
+ // already in rgb (hopefully); IE doesn't get here.
+ return v;
+ }
+ // IE sends number; convert to rgb.
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "rgb(" + r + "," + g + "," + b + ")";
+};
+
+// returns hexadecimal color representation from a number or a rgb-style color.
+HTMLArea._colorToRgb = function(v) {
+ if (!v)
+ return '';
+
+ // returns the hex representation of one byte (2 digits)
+ function hex(d) {
+ return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
+ };
+
+ if (typeof v == "number") {
+ // we're talking to IE here
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+
+ if (v.substr(0, 3) == "rgb") {
+ // in rgb(...) form -- Mozilla
+ var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
+ if (v.match(re)) {
+ var r = parseInt(RegExp.$1);
+ var g = parseInt(RegExp.$2);
+ var b = parseInt(RegExp.$3);
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+ // doesn't match RE?! maybe uses percentages or float numbers
+ // -- FIXME: not yet implemented.
+ return null;
+ }
+
+ if (v.substr(0, 1) == "#") {
+ // already hex rgb (hopefully :D )
+ return v;
+ }
+
+ // if everything else fails ;)
+ return null;
+};
+
+// modal dialogs for Mozilla (for IE we're using the showModalDialog() call).
+
+// receives an URL to the popup dialog and a function that receives one value;
+// this function will get called after the dialog is closed, with the return
+// value of the dialog.
+HTMLArea.prototype._popupDialog = function(url, action, init) {
+ Dialog(this.popupURL(url), action, init);
+};
+
+// paths
+
+HTMLArea.prototype.imgURL = function(file, plugin) {
+ if (typeof plugin == "undefined")
+ return this.config.editorURL + file;
+ else
+ return this.config.editorURL + "plugins/" + plugin + "/img/" + file;
+};
+
+HTMLArea.prototype.popupURL = function(file) {
+ var url = "";
+ if (file.match(/^plugin:\/\/(.*?)\/(.*)/)) {
+ var plugin = RegExp.$1;
+ var popup = RegExp.$2;
+ if (!/\.html$/.test(popup))
+ popup += ".html";
+ url = this.config.editorURL + "plugins/" + plugin + "/popups/" + popup;
+ } else
+ url = this.config.editorURL + this.config.popupURL + file;
+ return url;
+};
+
+// EOF
+// Local variables: //
+// c-basic-offset:8 //
+// indent-tabs-mode:t //
+// End: //
--- /dev/null
+//
+// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+// This copyright notice MUST stay intact for use (see license.txt).
+//
+// A free WYSIWYG editor replacement for <textarea> fields.
+// For full source code and docs, visit http://www.interactivetools.com/
+//
+// Version 3.0 developed by Mihai Bazon for InteractiveTools.
+// http://students.infoiasi.ro/~mishoo
+//
+// $Id$
+
+// Creates a new HTMLArea object. Tries to replace the textarea with the given
+// ID with it.
+<?php
+include("../../config.php");
+
+ $lang = current_language();
+ if(empty($lang)) {
+ $lang = "en";
+ }
+
+?>
+function HTMLArea(textarea, config) {
+ if (HTMLArea.checkSupportedBrowser()) {
+ if (typeof config == "undefined") {
+ this.config = new HTMLArea.Config();
+ } else {
+ this.config = config;
+ }
+ this._htmlArea = null;
+ this._textArea = textarea;
+ this._editMode = "wysiwyg";
+ this.plugins = {};
+ this._timerToolbar = null;
+ this._mdoc = document; // cache the document, we need it in plugins
+ }
+};
+
+HTMLArea.Config = function () {
+ this.version = "3.0";
+
+ this.width = "auto";
+ this.height = "auto";
+
+ // enable creation of a status bar?
+ this.statusBar = true;
+
+ // the next parameter specifies whether the toolbar should be included
+ // in the size or not.
+ this.sizeIncludesToolbar = true;
+
+ // style included in the iframe document
+ this.pageStyle = "body { background-color: #fff; font-family: 'Times New Roman', Times, sans-serif; font-size: 12pt; }";
+ if (typeof _editor_url != "undefined") {
+ this.editorURL = _editor_url;
+ } else {
+ this.editorURL = "<?php echo $CFG->wwwroot ?>/lib/editor/";
+ }
+
+ // URL-s
+ this.imgURL = "images/";
+ this.popupURL = "popups/";
+
+ // configuration for plugins
+ this.plugins = {};
+
+ /** CUSTOMIZING THE TOOLBAR
+ * -------------------------
+ *
+ * It is recommended that you customize the toolbar contents in an
+ * external file (i.e. the one calling HTMLArea) and leave this one
+ * unchanged. That's because when we (InteractiveTools.com) release a
+ * new official version, it's less likely that you will have problems
+ * upgrading HTMLArea.
+ */
+ this.toolbar = [
+ [ "fontname", "space",
+ "fontsize", "space",
+ "formatblock", "space",
+ "bold", "italic", "underline", "separator",
+ "strikethrough", "forecolor", "hilitecolor", "separator", "insertsmile", "insertchar" ],
+
+ [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",
+ "insertorderedlist", "insertunorderedlist","separator",
+ "textindicator", "separator",
+ "inserthorizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator",
+ "popupeditor", "separator", "showhelp", "about" ]
+ ];
+
+ this.fontname = {
+ "Arial": 'arial,helvetica,sans-serif',
+ "Courier New": 'courier new,courier,monospace',
+ "Georgia": 'georgia,times new roman,times,serif',
+ "Tahoma": 'tahoma,arial,helvetica,sans-serif',
+ "Times New Roman": 'times new roman,times,serif',
+ "Verdana": 'verdana,arial,helvetica,sans-serif',
+ "impact": 'impact',
+ "WingDings": 'wingdings'
+ };
+
+ this.fontsize = {
+ "1 (8 pt)": "1",
+ "2 (10 pt)": "2",
+ "3 (12 pt)": "3",
+ "4 (14 pt)": "4",
+ "5 (18 pt)": "5",
+ "6 (24 pt)": "6",
+ "7 (36 pt)": "7"
+ };
+
+ this.formatblock = {
+ "Heading 1": "h1",
+ "Heading 2": "h2",
+ "Heading 3": "h3",
+ "Heading 4": "h4",
+ "Heading 5": "h5",
+ "Heading 6": "h6",
+ "Normal": "p",
+ "Address": "address",
+ "Formatted": "pre"
+ };
+
+ this.customSelects = {};
+
+ function cut_copy_paste(e, cmd, obj) {
+ try {
+ e.execCommand(cmd);
+ } catch (e) {
+ if (HTMLArea.is_gecko) {
+ alert("Some revisions of Mozilla/Gecko do not support programatic " +
+ "access to cut/copy/paste functions, for security reasons. " +
+ "Your browser is one of them. Please use the standard key combinations:\n" +
+ "CTRL-X for cut, CTRL-C for copy, CTRL-V for paste.");
+ obj.element.style.display = "none";
+ }
+ }
+ };
+
+ // ADDING CUSTOM BUTTONS: please read below!
+ // format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]"
+ // - ID: unique ID for the button. If the button calls document.execCommand
+ // it's wise to give it the same name as the called command.
+ // - ACTION: function that gets called when the button is clicked.
+ // it has the following prototype:
+ // function(editor, buttonName)
+ // - editor is the HTMLArea object that triggered the call
+ // - buttonName is the ID of the clicked button
+ // These 2 parameters makes it possible for you to use the same
+ // handler for more HTMLArea objects or for more different buttons.
+ // - ToolTip: default tooltip, for cases when it is not defined in the -lang- file (HTMLArea.I18N)
+ // - Icon: path to an icon image file for the button (TODO: use one image for all buttons!)
+ // - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.
+ this.btnList = {
+ bold: [ "Bold", "images/ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ],
+ italic: [ "Italic", "images/ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ],
+ underline: [ "Underline", "images/ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ],
+ strikethrough: [ "Strikethrough", "images/ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ],
+ subscript: [ "Subscript", "images/ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ],
+ superscript: [ "Superscript", "images/ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ],
+ justifyleft: [ "Justify Left", "images/ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ],
+ justifycenter: [ "Justify Center", "images/ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ],
+ justifyright: [ "Justify Right", "images/ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ],
+ justifyfull: [ "Justify Full", "images/ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ],
+ insertorderedlist: [ "Ordered List", "images/ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ],
+ insertunorderedlist: [ "Bulleted List", "images/ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ],
+ outdent: [ "Decrease Indent", "images/ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ],
+ indent: [ "Increase Indent", "images/ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ],
+ forecolor: [ "Font Color", "images/ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ],
+ hilitecolor: [ "Background Color", "images/ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ],
+ inserthorizontalrule: [ "Horizontal Rule", "images/ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ],
+ createlink: [ "Insert Web Link", "images/ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ],
+ insertimage: [ "Insert Image", "images/ed_image.gif", false, function(e) {e.execCommand("insertimage");} ],
+ inserttable: [ "Insert Table", "images/insert_table.gif", false, function(e) {e.execCommand("inserttable");} ],
+ htmlmode: [ "Toggle HTML Source", "images/ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ],
+ popupeditor: [ "Enlarge Editor", "images/fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ],
+ about: [ "About this editor", "images/ed_about.gif", true, function(e) {e.execCommand("about");} ],
+ showhelp: [ "Help using editor", "images/ed_help.gif", true, function(e) {e.execCommand("showhelp");} ],
+ undo: [ "Undoes your last action", "images/ed_undo.gif", false, function(e) {e.execCommand("undo");} ],
+ redo: [ "Redoes your last action", "images/ed_redo.gif", false, function(e) {e.execCommand("redo");} ],
+ cut: [ "Cut selection", "images/ed_cut.gif", false, cut_copy_paste ],
+ copy: [ "Copy selection", "images/ed_copy.gif", false, cut_copy_paste ],
+ paste: [ "Paste from clipboard", "images/ed_paste.gif", false, cut_copy_paste ],
+ insertsmile: ["Insert Smiley", "images/em.icon.smile.gif", false, function(e) {e.execCommand("insertsmile");} ],
+ insertchar: [ "Insert Char", "images/icon_ins_char.gif", false, function(e) {e.execCommand("insertchar");} ]
+ };
+ /* ADDING CUSTOM BUTTONS
+ * ---------------------
+ *
+ * It is recommended that you add the custom buttons in an external
+ * file and leave this one unchanged. That's because when we
+ * (InteractiveTools.com) release a new official version, it's less
+ * likely that you will have problems upgrading HTMLArea.
+ *
+ * Example on how to add a custom button when you construct the HTMLArea:
+ *
+ * var editor = new HTMLArea("your_text_area_id");
+ * var cfg = editor.config; // this is the default configuration
+ * cfg.btnList["my-hilite"] =
+ * [ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action
+ * "Highlight selection", // tooltip
+ * "my_hilite.gif", // image
+ * false // disabled in text mode
+ * ];
+ * cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar
+ *
+ * An alternate (also more convenient and recommended) way to
+ * accomplish this is to use the registerButton function below.
+ */
+ // initialize tooltips from the I18N module
+ for (var i in this.btnList) {
+ var btn = this.btnList[i];
+ if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {
+ btn[0] = HTMLArea.I18N.tooltips[i];
+ }
+ }
+};
+
+/** Helper function: register a new button with the configuration. It can be
+ * called with all 5 arguments, or with only one (first one). When called with
+ * only one argument it must be an object with the following properties: id,
+ * tooltip, image, textMode, action. Examples:
+ *
+ * 1. config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...});
+ * 2. config.registerButton({
+ * id : "my-hilite", // the ID of your button
+ * tooltip : "Hilite text", // the tooltip
+ * image : "my-hilite.gif", // image to be displayed in the toolbar
+ * textMode : false, // disabled in text mode
+ * action : function(editor) { // called when the button is clicked
+ * editor.surroundHTML('<span class="hilite">', '</span>');
+ * },
+ * context : "p" // will be disabled if outside a <p> element
+ * });
+ */
+HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) {
+ var the_id;
+ if (typeof id == "string") {
+ the_id = id;
+ } else if (typeof id == "object") {
+ the_id = id.id;
+ } else {
+ alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments");
+ return false;
+ }
+ // check for existing id
+ if (typeof this.customSelects[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ switch (typeof id) {
+ case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break;
+ case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break;
+ }
+};
+
+/** The following helper function registers a dropdown box with the editor
+ * configuration. You still have to add it to the toolbar, same as with the
+ * buttons. Call it like this:
+ *
+ * FIXME: add example
+ */
+HTMLArea.Config.prototype.registerDropdown = function(object) {
+ // check for existing id
+ if (typeof this.customSelects[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ this.customSelects[object.id] = object;
+};
+
+/** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */
+HTMLArea.replaceAll = function(config) {
+ var tas = document.getElementsByTagName("textarea");
+ for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate());
+};
+
+/** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */
+HTMLArea.replace = function(id, config) {
+ var ta = document.getElementById(id);
+ return ta ? (new HTMLArea(ta, config)).generate() : null;
+};
+
+// Creates the toolbar and appends it to the _htmlarea
+HTMLArea.prototype._createToolbar = function () {
+ var editor = this; // to access this in nested functions
+
+ var toolbar = document.createElement("div");
+ this._toolbar = toolbar;
+ toolbar.className = "toolbar";
+ toolbar.unselectable = "1";
+ var tb_row = null;
+ var tb_objects = new Object();
+ this._toolbarObjects = tb_objects;
+
+ // creates a new line in the toolbar
+ function newLine() {
+ var table = document.createElement("table");
+ table.border = "0px";
+ table.cellSpacing = "0px";
+ table.cellPadding = "0px";
+ toolbar.appendChild(table);
+ // TBODY is required for IE, otherwise you don't see anything
+ // in the TABLE.
+ var tb_body = document.createElement("tbody");
+ table.appendChild(tb_body);
+ tb_row = document.createElement("tr");
+ tb_body.appendChild(tb_row);
+ }; // END of function: newLine
+ // init first line
+ newLine();
+
+ // updates the state of a toolbar element. This function is member of
+ // a toolbar element object (unnamed objects created by createButton or
+ // createSelect functions below).
+ function setButtonStatus(id, newval) {
+ var oldval = this[id];
+ var el = this.element;
+ if (oldval != newval) {
+ switch (id) {
+ case "enabled":
+ if (newval) {
+ HTMLArea._removeClass(el, "buttonDisabled");
+ el.disabled = false;
+ } else {
+ HTMLArea._addClass(el, "buttonDisabled");
+ el.disabled = true;
+ }
+ break;
+ case "active":
+ if (newval) {
+ HTMLArea._addClass(el, "buttonPressed");
+ } else {
+ HTMLArea._removeClass(el, "buttonPressed");
+ }
+ break;
+ }
+ this[id] = newval;
+ }
+ }; // END of function: setButtonStatus
+
+ // this function will handle creation of combo boxes. Receives as
+ // parameter the name of a button as defined in the toolBar config.
+ // This function is called from createButton, above, if the given "txt"
+ // doesn't match a button.
+ function createSelect(txt) {
+ var options = null;
+ var el = null;
+ var cmd = null;
+ var customSelects = editor.config.customSelects;
+ var context = null;
+ switch (txt) {
+ case "fontsize":
+ case "fontname":
+ case "formatblock":
+ // the following line retrieves the correct
+ // configuration option because the variable name
+ // inside the Config object is named the same as the
+ // button/select in the toolbar. For instance, if txt
+ // == "formatblock" we retrieve config.formatblock (or
+ // a different way to write it in JS is
+ // config["formatblock"].
+ options = editor.config[txt];
+ cmd = txt;
+ break;
+ default:
+ // try to fetch it from the list of registered selects
+ cmd = txt;
+ var dropdown = customSelects[cmd];
+ if (typeof dropdown != "undefined") {
+ options = dropdown.options;
+ context = dropdown.context;
+ } else {
+ alert("ERROR [createSelect]:\nCan't find the requested dropdown definition");
+ }
+ break;
+ }
+ if (options) {
+ el = document.createElement("select");
+ var obj = {
+ name : txt, // field name
+ element : el, // the UI element (SELECT)
+ enabled : true, // is it enabled?
+ text : false, // enabled in text mode?
+ cmd : cmd, // command ID
+ state : setButtonStatus, // for changing state
+ context : context
+ };
+ tb_objects[txt] = obj;
+ for (var i in options) {
+ var op = document.createElement("option");
+ op.appendChild(document.createTextNode(i));
+ op.value = options[i];
+ el.appendChild(op);
+ }
+ HTMLArea._addEvent(el, "change", function () {
+ editor._comboSelected(el, txt);
+ });
+ }
+ return el;
+ }; // END of function: createSelect
+
+ // appends a new button to toolbar
+ function createButton(txt) {
+ // the element that will be created
+ var el = null;
+ var btn = null;
+ switch (txt) {
+ case "separator":
+ el = document.createElement("div");
+ el.className = "separator";
+ break;
+ case "space":
+ el = document.createElement("div");
+ el.className = "space";
+ break;
+ case "linebreak":
+ newLine();
+ return false;
+ case "textindicator":
+ el = document.createElement("div");
+ el.appendChild(document.createTextNode("A"));
+ el.className = "indicator";
+ el.title = HTMLArea.I18N.tooltips.textindicator;
+ var obj = {
+ name : txt, // the button name (i.e. 'bold')
+ element : el, // the UI element (DIV)
+ enabled : true, // is it enabled?
+ active : false, // is it pressed?
+ text : false, // enabled in text mode?
+ cmd : "textindicator", // the command ID
+ state : setButtonStatus // for changing state
+ };
+ tb_objects[txt] = obj;
+ break;
+ default:
+ btn = editor.config.btnList[txt];
+ }
+ if (!el && btn) {
+ el = document.createElement("div");
+ el.title = btn[0];
+ el.className = "button";
+ // let's just pretend we have a button object, and
+ // assign all the needed information to it.
+ var obj = {
+ name : txt, // the button name (i.e. 'bold')
+ element : el, // the UI element (DIV)
+ enabled : true, // is it enabled?
+ active : false, // is it pressed?
+ text : btn[2], // enabled in text mode?
+ cmd : btn[3], // the command ID
+ state : setButtonStatus, // for changing state
+ context : btn[4] || null // enabled in a certain context?
+ };
+ tb_objects[txt] = obj;
+ // handlers to emulate nice flat toolbar buttons
+ HTMLArea._addEvent(el, "mouseover", function () {
+ if (obj.enabled) {
+ HTMLArea._addClass(el, "buttonHover");
+ }
+ });
+ HTMLArea._addEvent(el, "mouseout", function () {
+ if (obj.enabled) with (HTMLArea) {
+ _removeClass(el, "buttonHover");
+ _removeClass(el, "buttonActive");
+ (obj.active) && _addClass(el, "buttonPressed");
+ }
+ });
+ HTMLArea._addEvent(el, "mousedown", function (ev) {
+ if (obj.enabled) with (HTMLArea) {
+ _addClass(el, "buttonActive");
+ _removeClass(el, "buttonPressed");
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ // when clicked, do the following:
+ HTMLArea._addEvent(el, "click", function (ev) {
+ if (obj.enabled) with (HTMLArea) {
+ _removeClass(el, "buttonActive");
+ _removeClass(el, "buttonHover");
+ obj.cmd(editor, obj.name, obj);
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ var img = document.createElement("img");
+ img.src = editor.imgURL(btn[1]);
+ img.style.width = "18px";
+ img.style.height = "18px";
+ el.appendChild(img);
+ } else if (!el) {
+ el = createSelect(txt);
+ }
+ if (el) {
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.appendChild(el);
+ } else {
+ alert("FIXME: Unknown toolbar item: " + txt);
+ }
+ return el;
+ };
+
+ var first = true;
+ for (var i in this.config.toolbar) {
+ if (!first) {
+ createButton("linebreak");
+ } else {
+ first = false;
+ }
+ var group = this.config.toolbar[i];
+ for (var j in group) {
+ var code = group[j];
+ if (/^([IT])\[(.*?)\]/.test(code)) {
+ // special case, create text label
+ var l7ed = RegExp.$1 == "I"; // localized?
+ var label = RegExp.$2;
+ if (l7ed) {
+ label = HTMLArea.I18N.custom[label];
+ }
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.className = "label";
+ tb_cell.innerHTML = label;
+ } else {
+ createButton(code);
+ }
+ }
+ }
+
+ this._htmlArea.appendChild(toolbar);
+};
+
+HTMLArea.prototype._createStatusBar = function() {
+ var div = document.createElement("div");
+ div.className = "statusBar";
+ this._htmlArea.appendChild(div);
+ this._statusBar = div;
+ div.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ // creates a holder for the path view
+ div = document.createElement("span");
+ div.className = "statusBarTree";
+ this._statusBarTree = div;
+ this._statusBar.appendChild(div);
+ if (!this.config.statusBar) {
+ // disable it...
+ div.style.display = "none";
+ }
+};
+
+// Creates the HTMLArea object and replaces the textarea with it.
+HTMLArea.prototype.generate = function () {
+ var editor = this; // we'll need "this" in some nested functions
+ // get the textarea
+ var textarea = this._textArea;
+ if (typeof textarea == "string") {
+ // it's not element but ID
+ this._textArea = textarea = document.getElementById(textarea);
+ }
+ this._ta_size = {
+ w: textarea.offsetWidth,
+ h: textarea.offsetHeight
+ };
+ textarea.style.display = "none";
+
+ // create the editor framework
+ var htmlarea = document.createElement("div");
+ htmlarea.className = "htmlarea";
+ this._htmlArea = htmlarea;
+
+ // insert the editor before the textarea.
+ textarea.parentNode.insertBefore(htmlarea, textarea);
+
+ if (textarea.form) {
+ // we have a form, on submit get the HTMLArea content and
+ // update original textarea.
+ textarea.form.onsubmit = function() {
+ editor._textArea.value = editor.getHTML();
+ };
+ }
+
+ // add a handler for the "back/forward" case -- on body.unload we save
+ // the HTML content into the original textarea.
+ window.onunload = function() {
+ editor._textArea.value = editor.getHTML();
+ };
+
+ // creates & appends the toolbar
+ this._createToolbar();
+
+ // create the IFRAME
+ var iframe = document.createElement("iframe");
+ htmlarea.appendChild(iframe);
+
+ this._iframe = iframe;
+
+ // creates & appends the status bar, if the case
+ this._createStatusBar();
+
+ // remove the default border as it keeps us from computing correctly
+ // the sizes. (somebody tell me why doesn't this work in IE)
+
+ if (!HTMLArea.is_ie) {
+ iframe.style.borderWidth = "1px";
+ // iframe.frameBorder = "1";
+ // iframe.marginHeight = "0";
+ // iframe.marginWidth = "0";
+ }
+
+ // size the IFRAME according to user's prefs or initial textarea
+ var height = (this.config.height == "auto" ? (this._ta_size.h + "px") : this.config.height);
+ height = parseInt(height);
+ var width = (this.config.width == "auto" ? (this._ta_size.w + "px") : this.config.width);
+ width = parseInt(width);
+
+ if (!HTMLArea.is_ie) {
+ height -= 2;
+ width -= 2;
+ }
+
+ iframe.style.width = width + "px";
+ if (this.config.sizeIncludesToolbar) {
+ // substract toolbar height
+ height -= this._toolbar.offsetHeight;
+ height -= this._statusBar.offsetHeight;
+ }
+ if (height < 0) {
+ height = 0;
+ }
+ iframe.style.height = height + "px";
+
+ // the editor including the toolbar now have the same size as the
+ // original textarea.. which means that we need to reduce that a bit.
+ textarea.style.width = iframe.style.width;
+ textarea.style.height = iframe.style.height;
+
+ // IMPORTANT: we have to allow Mozilla a short time to recognize the
+ // new frame. Otherwise we get a stupid exception.
+ function initIframe() {
+ var doc = editor._iframe.contentWindow.document;
+ if (!doc) {
+ // Try again..
+ // FIXME: don't know what else to do here. Normally
+ // we'll never reach this point.
+ if (HTMLArea.is_gecko) {
+ setTimeout(initIframe, 10);
+ return false;
+ } else {
+ alert("ERROR: IFRAME can't be initialized.");
+ }
+ }
+ if (HTMLArea.is_gecko) {
+ // enable editable mode for Mozilla
+ doc.designMode = "on";
+ }
+ editor._doc = doc;
+ doc.open();
+ var html = "<html>\n";
+ html += "<head>\n";
+ html += "<style>" + editor.config.pageStyle + "</style>\n";
+ html += "</head>\n";
+ html += "<body>\n";
+ html += editor._textArea.value;
+ html += "</body>\n";
+ html += "</html>";
+ doc.write(html);
+ doc.close();
+
+ if (HTMLArea.is_ie) {
+ // enable editable mode for IE. For some reason this
+ // doesn't work if done in the same place as for Gecko
+ // (above).
+ doc.body.contentEditable = true;
+ }
+
+ editor.focusEditor();
+ // intercept some events; for updating the toolbar & keyboard handlers
+ HTMLArea._addEvents
+ (doc, ["keydown", "keypress", "mousedown", "mouseup", "drag"],
+ function (event) {
+ return editor._editorEvent(HTMLArea.is_ie ? editor._iframe.contentWindow.event : event);
+ });
+ editor.updateToolbar();
+ };
+ setTimeout(initIframe, HTMLArea.is_gecko ? 10 : 0);
+};
+
+// Switches editor mode; parameter can be "textmode" or "wysiwyg". If no
+// parameter was passed this function toggles between modes.
+HTMLArea.prototype.setMode = function(mode) {
+ if (typeof mode == "undefined") {
+ mode = ((this._editMode == "textmode") ? "wysiwyg" : "textmode");
+ }
+ switch (mode) {
+ case "textmode":
+ this._textArea.value = this.getHTML();
+ this._iframe.style.display = "none";
+ this._textArea.style.display = "block";
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = HTMLArea.I18N.msg["TEXT_MODE"];
+ }
+ break;
+ case "wysiwyg":
+ if (HTMLArea.is_gecko) {
+ // disable design mode before changing innerHTML
+ this._doc.designMode = "off";
+ }
+ this._doc.body.innerHTML = this.getHTML();
+ this._iframe.style.display = "block";
+ this._textArea.style.display = "none";
+ if (HTMLArea.is_gecko) {
+ // we need to refresh that info for Moz-1.3a
+ this._doc.designMode = "on";
+ }
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = '';
+ this._statusBar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ this._statusBar.appendChild(this._statusBarTree);
+ }
+ break;
+ default:
+ alert("Mode <" + mode + "> not defined!");
+ return false;
+ }
+ this._editMode = mode;
+ this.focusEditor();
+};
+
+/***************************************************
+ * Category: PLUGINS
+ ***************************************************/
+
+// Create the specified plugin and register it with this HTMLArea
+HTMLArea.prototype.registerPlugin = function(pluginName) {
+ this.plugins[pluginName] = eval("new " + pluginName + "(this);");
+};
+
+// static function that loads the required plugin and lang file, based on the
+// language loaded already for HTMLArea. You better make sure that the plugin
+// _has_ that language, otherwise shit might happen ;-)
+HTMLArea.loadPlugin = function(pluginName) {
+ var editorurl = '';
+ if (typeof _editor_url != "undefined") {
+ editorurl = _editor_url + "/";
+ }
+ var dir = editorurl + "plugins/" + pluginName;
+ var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g,
+ function (str, l1, l2, l3) {
+ return l1 + "-" + l2.toLowerCase() + l3;
+ }).toLowerCase() + ".js";
+ document.write("<script type='text/javascript' src='" + dir + "/" + plugin + "'></script>");
+ document.write("<script type='text/javascript' src='" + dir + "/lang/" + HTMLArea.I18N.lang + ".js'></script>");
+};
+
+/***************************************************
+ * Category: EDITOR UTILITIES
+ ***************************************************/
+
+HTMLArea.prototype.forceRedraw = function() {
+ this._doc.body.style.visibility = "hidden";
+ this._doc.body.style.visibility = "visible";
+ // this._doc.body.innerHTML = this.getInnerHTML();
+};
+
+// focuses the iframe window. returns a reference to the editor document.
+HTMLArea.prototype.focusEditor = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : this._iframe.contentWindow.focus(); break;
+ case "textmode": this._textArea.focus(); break;
+ default : alert("ERROR: mode " + this._editMode + " is not defined");
+ }
+ return this._doc;
+};
+
+// updates enabled/disable/active state of the toolbar elements
+HTMLArea.prototype.updateToolbar = function(noStatus) {
+ var doc = this._doc;
+ var text = (this._editMode == "textmode");
+ var ancestors = null;
+ if (!text) {
+ ancestors = this.getAllAncestors();
+ if (this.config.statusBar && !noStatus) {
+ this._statusBarTree.innerHTML = ''; // clear
+ for (var i = ancestors.length; --i >= 0;) {
+ var el = ancestors[i];
+ if (!el) {
+ // hell knows why we get here; this
+ // could be a classic example of why
+ // it's good to check for conditions
+ // that are impossible to happen ;-)
+ continue;
+ }
+ var a = document.createElement("a");
+ a.href = "#";
+ a.el = el;
+ a.editor = this;
+ a.onclick = function() {
+ this.blur();
+ this.editor.selectNodeContents(this.el);
+ this.editor.updateToolbar(true);
+ return false;
+ };
+ a.oncontextmenu = function() {
+ // TODO: add context menu here
+ this.blur();
+ var info = "Inline style:\n\n";
+ info += this.el.style.cssText.split(/;\s*/).join(";\n");
+ alert(info);
+ return false;
+ };
+ var txt = el.tagName.toLowerCase();
+ a.title = el.style.cssText;
+ if (el.id) {
+ txt += "#" + el.id;
+ }
+ if (el.className) {
+ txt += "." + el.className;
+ }
+ a.appendChild(document.createTextNode(txt));
+ this._statusBarTree.appendChild(a);
+ if (i != 0) {
+ this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
+ }
+ }
+ }
+ }
+ for (var i in this._toolbarObjects) {
+ var btn = this._toolbarObjects[i];
+ var cmd = i;
+ var inContext = true;
+ if (btn.context && !text) {
+ inContext = false;
+ var context = btn.context;
+ var attrs = [];
+ if (/(.*)\[(.*?)\]/.test(context)) {
+ context = RegExp.$1;
+ attrs = RegExp.$2.split(",");
+ }
+ context = context.toLowerCase();
+ var match = (context == "*");
+ for (var k in ancestors) {
+ if (!ancestors[k]) {
+ // the impossible really happens.
+ continue;
+ }
+ if (match || (ancestors[k].tagName.toLowerCase() == context)) {
+ inContext = true;
+ for (var ka in attrs) {
+ if (!eval("ancestors[k]." + attrs[ka])) {
+ inContext = false;
+ break;
+ }
+ }
+ if (inContext) {
+ break;
+ }
+ }
+ }
+ }
+ btn.state("enabled", (!text || btn.text) && inContext);
+ if (typeof cmd == "function") {
+ continue;
+ }
+ // look-it-up in the custom dropdown boxes
+ var dropdown = this.config.customSelects[cmd];
+ if ((!text || btn.text) && (typeof dropdown != "undefined")) {
+ dropdown.refresh(this);
+ continue;
+ }
+ switch (cmd) {
+ case "fontname":
+ case "fontsize":
+ case "formatblock":
+ if (!text) {
+ var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
+ if (!value) {
+ // FIXME: what do we do here?
+ break;
+ }
+ // HACK -- retrieve the config option for this
+ // combo box. We rely on the fact that the
+ // variable in config has the same name as
+ // button name in the toolbar.
+ var options = this.config[cmd];
+ var k = 0;
+ // btn.element.selectedIndex = 0;
+ for (var j in options) {
+ // FIXME: the following line is scary.
+ if ((j.toLowerCase() == value) ||
+ (options[j].substr(0, value.length).toLowerCase() == value)) {
+ btn.element.selectedIndex = k;
+ break;
+ }
+ ++k;
+ }
+ }
+ break;
+ case "textindicator":
+ if (!text) {
+ try {with (btn.element.style) {
+ backgroundColor = HTMLArea._makeColor(
+ doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor"));
+ if (/transparent/i.test(backgroundColor)) {
+ // Mozilla
+ backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor"));
+ }
+ color = HTMLArea._makeColor(doc.queryCommandValue("forecolor"));
+ fontFamily = doc.queryCommandValue("fontname");
+ fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
+ fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
+ }} catch (e) {
+ // alert(e + "\n\n" + cmd);
+ }
+ }
+ break;
+ case "htmlmode": btn.state("active", text); break;
+ default:
+ try {
+ btn.state("active", (!text && doc.queryCommandState(cmd)));
+ } catch (e) {}
+ }
+ }
+};
+
+/** Returns a node after which we can insert other nodes, in the current
+ * selection. The selection is removed. It splits a text node, if needed.
+ */
+HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted) {
+ if (!HTMLArea.is_ie) {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ // remove the current selection
+ sel.removeAllRanges();
+ range.deleteContents();
+ var node = range.startContainer;
+ var pos = range.startOffset;
+ switch (node.nodeType) {
+ case 3: // Node.TEXT_NODE
+ // we have to split it at the caret position.
+ if (toBeInserted.nodeType == 3) {
+ // do optimized insertion
+ node.insertData(pos, toBeInserted.data);
+ range = this._createRange();
+ range.setEnd(node, pos + toBeInserted.length);
+ range.setStart(node, pos + toBeInserted.length);
+ sel.addRange(range);
+ } else {
+ node = node.splitText(pos);
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.parentNode.insertBefore(toBeInserted, node);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ }
+ break;
+ case 1: // Node.ELEMENT_NODE
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.insertBefore(toBeInserted, node.childNodes[pos]);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ break;
+ }
+ } else {
+ return null; // this function not yet used for IE <FIXME>
+ }
+};
+
+// Returns the deepest node that contains both endpoints of the selection.
+HTMLArea.prototype.getParentElement = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ return range.parentElement ? range.parentElement() : this._doc.body;
+ } else {
+ var p = range.commonAncestorContainer;
+ while (p.nodeType == 3) {
+ p = p.parentNode;
+ }
+ return p;
+ }
+};
+
+// Returns an array with all the ancestor nodes of the selection.
+HTMLArea.prototype.getAllAncestors = function() {
+ var p = this.getParentElement();
+ var a = [];
+ while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
+ a.push(p);
+ p = p.parentNode;
+ }
+ a.push(this._doc.body);
+ return a;
+};
+
+// Selects the contents inside the given node
+HTMLArea.prototype.selectNodeContents = function(node, pos) {
+ this.focusEditor();
+ this.forceRedraw();
+ var range;
+ var collapsed = (typeof pos != "undefined");
+ if (HTMLArea.is_ie) {
+ range = this._doc.body.createTextRange();
+ range.moveToElementText(node);
+ (collapsed) && range.collapse(pos);
+ range.select();
+ } else {
+ var sel = this._getSelection();
+ range = this._doc.createRange();
+ range.selectNodeContents(node);
+ (collapsed) && range.collapse(pos);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+};
+
+/** Call this function to insert HTML code at the current position. It deletes
+ * the selection, if any.
+ */
+HTMLArea.prototype.insertHTML = function(html) {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(html);
+ } else {
+ // construct a new document fragment with the given HTML
+ var fragment = this._doc.createDocumentFragment();
+ var div = this._doc.createElement("div");
+ div.innerHTML = html;
+ while (div.firstChild) {
+ // the following call also removes the node from div
+ fragment.appendChild(div.firstChild);
+ }
+ // this also removes the selection
+ var node = this.insertNodeAtSelection(fragment);
+ }
+};
+
+/**
+ * Call this function to surround the existing HTML code in the selection with
+ * your tags. FIXME: buggy! This function will be deprecated "soon".
+ */
+HTMLArea.prototype.surroundHTML = function(startTag, endTag) {
+ var html = this.getSelectedHTML();
+ // the following also deletes the selection
+ this.insertHTML(startTag + html + endTag);
+};
+
+/// Retrieve the selected block
+HTMLArea.prototype.getSelectedHTML = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var existing = null;
+ if (HTMLArea.is_ie) {
+ existing = range.htmlText;
+ } else {
+ existing = HTMLArea.getHTML(range.cloneContents(), false);
+ }
+ return existing;
+};
+
+// Called when the user clicks on "InsertImage" button
+HTMLArea.prototype._insertImage = function() {
+ var editor = this; // for nested functions
+ this._popupDialog("insert_image.php?id=<?php echo $id ?>", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var sel = editor._getSelection();
+ var range = editor._createRange(sel);
+ editor._doc.execCommand("insertimage", false, param["f_url"]);
+ var img = null;
+ if (HTMLArea.is_ie) {
+ img = range.parentElement();
+ // wonder if this works...
+ if (img.tagName.toLowerCase() != "img") {
+ img = img.previousSibling;
+ }
+ } else {
+ img = range.startContainer.previousSibling;
+ }
+ for (field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_alt" : img.alt = value; break;
+ case "f_border" : img.border = parseInt(value); break;
+ case "f_align" : img.align = value; break;
+ case "f_vert" : img.vspace = parseInt(value); break;
+ case "f_horiz" : img.hspace = parseInt(value); break;
+ }
+ }
+ }, null);
+};
+/******************************************************************
+* Moodle hack - insertSmile
+******************************************************************/
+HTMLArea.prototype._insertSmile = function() {
+ var editor = this; // for nested functions
+ this._popupDialog("dlg_ins_smile.php", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var sel = editor._getSelection();
+ var range = editor._createRange(sel);
+ editor._doc.execCommand("insertimage", false, param["f_url"]);
+ var img = null;
+ if (HTMLArea.is_ie) {
+ img = range.parentElement();
+ // wonder if this works...
+ if (img.tagName.toLowerCase() != "img") {
+ img = img.previousSibling;
+ }
+ } else {
+ img = range.startContainer.previousSibling;
+ }
+ for (field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_alt" : img.alt = value; break;
+ case "f_border" : img.border = parseInt(value); break;
+ case "f_align" : img.align = value; break;
+ case "f_vert" : img.vspace = parseInt(value); break;
+ case "f_horiz" : img.hspace = parseInt(value); break;
+ }
+ }
+ }, null);
+};
+HTMLArea.prototype._insertChar = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var editor = this; // for nested functions
+ this._popupDialog("dlg_ins_char.html", function(sChar) {
+ if(!sChar) {
+ return false;
+ }
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(sChar);
+ } else {
+ // insert the table
+ editor.insertHTML(sChar);
+ }
+ return true;
+ }, null);
+};
+/************************************************************************
+* Moodle hack's ends
+************************************************************************/
+// Called when the user clicks the Insert Table button
+HTMLArea.prototype._insertTable = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ var editor = this; // for nested functions
+ this._popupDialog("insert_table.html", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var doc = editor._doc;
+ // create the table element
+ var table = doc.createElement("table");
+ // assign the given arguments
+ for (var field in param) {
+ var value = param[field];
+ if (!value) {
+ continue;
+ }
+ switch (field) {
+ case "f_width" : table.style.width = value + param["f_unit"]; break;
+ case "f_align" : table.align = value; break;
+ case "f_border" : table.border = parseInt(value); break;
+ case "f_spacing" : table.cellspacing = parseInt(value); break;
+ case "f_padding" : table.cellpadding = parseInt(value); break;
+ }
+ }
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+ for (var i = 0; i < param["f_rows"]; ++i) {
+ var tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ for (var j = 0; j < param["f_cols"]; ++j) {
+ var td = doc.createElement("td");
+ tr.appendChild(td);
+ // Mozilla likes to see something inside the cell.
+ (HTMLArea.is_gecko) && td.appendChild(doc.createElement("br"));
+ }
+ }
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(table.outerHTML);
+ } else {
+ // insert the table
+ editor.insertNodeAtSelection(table);
+ }
+ return true;
+ }, null);
+};
+
+/***************************************************
+ * Category: EVENT HANDLERS
+ ***************************************************/
+
+// el is reference to the SELECT object
+// txt is the name of the select field, as in config.toolbar
+HTMLArea.prototype._comboSelected = function(el, txt) {
+ this.focusEditor();
+ var value = el.options[el.selectedIndex].value;
+ switch (txt) {
+ case "fontname":
+ case "fontsize": this.execCommand(txt, false, value); break;
+ case "formatblock":
+ (HTMLArea.is_ie) && (value = "<" + value + ">");
+ this.execCommand(txt, false, value);
+ break;
+ default:
+ // try to look it up in the registered dropdowns
+ var dropdown = this.config.customSelects[txt];
+ if (typeof dropdown != "undefined") {
+ dropdown.action(this);
+ } else {
+ alert("FIXME: combo box " + txt + " not implemented");
+ }
+ }
+};
+
+// the execCommand function (intercepts some commands and replaces them with
+// our own implementation)
+HTMLArea.prototype.execCommand = function(cmdID, UI, param) {
+ var editor = this; // for nested functions
+ this.focusEditor();
+ switch (cmdID.toLowerCase()) {
+ case "htmlmode" : this.setMode(); break;
+ case "hilitecolor":
+ (HTMLArea.is_ie) && (cmdID = "backcolor");
+ case "forecolor":
+ this._popupDialog("select_color.html", function(color) {
+ if (color) { // selection not canceled
+ editor._doc.execCommand(cmdID, false, "#" + color);
+ }
+ }, HTMLArea._colorToRgb(this._doc.queryCommandValue(cmdID)));
+ break;
+ case "createlink":
+ if (HTMLArea.is_ie || !UI) {
+ this._doc.execCommand(cmdID, UI, param);
+ } else {
+ // browser is Mozilla & wants UI
+ var param;
+ if ((param = prompt("Enter URL"))) {
+ this._doc.execCommand(cmdID, false, param);
+ }
+ }
+ break;
+ case "popupeditor":
+ if (HTMLArea.is_ie) {
+ window.open(this.popupURL("fullscreen.php?id=<?php echo $id ?>"), "ha_fullscreen",
+ "toolbar=no,location=no,directories=no,status=no,menubar=no," +
+ "scrollbars=no,resizable=yes,width=640,height=480");
+ } else {
+ window.open(this.popupURL("fullscreen.php?id=<?php echo $id ?>"), "ha_fullscreen",
+ "toolbar=no,menubar=no,personalbar=no,width=640,height=480," +
+ "scrollbars=no,resizable=yes");
+ }
+ // pass this object to the newly opened window
+ HTMLArea._object = this;
+ break;
+ case "inserttable": this._insertTable(); break;
+ case "insertimage": this._insertImage(); break;
+ case "insertsmile": this._insertSmile(); break;
+ case "insertchar": this._insertChar(); break;
+ case "about" : this._popupDialog("about.html", null, null); break;
+ case "showhelp" : window.open("<?php echo "$CFG->wwwroot/lang/$lang/docs/htmlarea_editor.html" ?>", "ha_help"); break;
+ default: this._doc.execCommand(cmdID, UI, param);
+ }
+ this.updateToolbar();
+ return false;
+};
+
+/** A generic event handler for things that happen in the IFRAME's document.
+ * This function also handles key bindings. */
+HTMLArea.prototype._editorEvent = function(ev) {
+ var editor = this;
+ var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");
+ if (keyEvent && ev.ctrlKey) {
+ var sel = null;
+ var range = null;
+ var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
+ var cmd = null;
+ var value = null;
+ switch (key) {
+ case 'a':
+ if (!HTMLArea.is_ie) {
+ // KEY select all
+ sel = this._getSelection();
+ sel.removeAllRanges();
+ range = this._createRange();
+ range.selectNodeContents(this._doc.body);
+ sel.addRange(range);
+ HTMLArea._stopEvent(ev);
+ }
+ break;
+
+ // simple key commands follow
+
+ case 'b': cmd = "bold"; break;
+ case 'i': cmd = "italic"; break;
+ case 'u': cmd = "underline"; break;
+ case 's': cmd = "strikethrough"; break;
+ case 'l': cmd = "justifyleft"; break;
+ case 'e': cmd = "justifycenter"; break;
+ case 'r': cmd = "justifyright"; break;
+ case 'j': cmd = "justifyfull"; break;
+
+ // headings
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ cmd = "formatblock";
+ value = "h" + key;
+ if (HTMLArea.is_ie) {
+ value = "<" + value + ">";
+ }
+ break;
+ }
+ if (cmd) {
+ // execute simple command
+ this.execCommand(cmd, false, value);
+ HTMLArea._stopEvent(ev);
+ }
+ }
+ /*
+ else if (keyEvent) {
+ // other keys here
+ switch (ev.keyCode) {
+ case 13: // KEY enter
+ // if (HTMLArea.is_ie) {
+ this.insertHTML("<br />");
+ HTMLArea._stopEvent(ev);
+ // }
+ break;
+ }
+ }
+ */
+ // update the toolbar state after some time
+ if (editor._timerToolbar) {
+ clearTimeout(editor._timerToolbar);
+ }
+ editor._timerToolbar = setTimeout(function() {
+ editor.updateToolbar();
+ editor._timerToolbar = null;
+ }, 50);
+};
+
+// retrieve the HTML
+HTMLArea.prototype.getHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : return HTMLArea.getHTML(this._doc.body, false);
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// retrieve the HTML (fastest version, but uses innerHTML)
+HTMLArea.prototype.getInnerHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : return this._doc.body.innerHTML;
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// completely change the HTML inside
+HTMLArea.prototype.setHTML = function(html) {
+ switch (this._editMode) {
+ case "wysiwyg" : this._doc.body.innerHTML = html; break;
+ case "textmode" : this._textArea.value = html; break;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+/***************************************************
+ * Category: UTILITY FUNCTIONS
+ ***************************************************/
+
+// browser identification
+
+HTMLArea.agt = navigator.userAgent.toLowerCase();
+HTMLArea.is_ie = ((HTMLArea.agt.indexOf("msie") != -1) && (HTMLArea.agt.indexOf("opera") == -1));
+HTMLArea.is_opera = (HTMLArea.agt.indexOf("opera") != -1);
+HTMLArea.is_mac = (HTMLArea.agt.indexOf("mac") != -1);
+HTMLArea.is_mac_ie = (HTMLArea.is_ie && HTMLArea.is_mac);
+HTMLArea.is_win_ie = (HTMLArea.is_ie && !HTMLArea.is_mac);
+HTMLArea.is_gecko = (navigator.product == "Gecko");
+
+// variable used to pass the object to the popup editor window.
+HTMLArea._object = null;
+
+// FIXME!!! this should return false for IE < 5.5
+HTMLArea.checkSupportedBrowser = function() {
+ if (HTMLArea.is_gecko) {
+ if (navigator.productSub < 20021201) {
+ alert("You need at least Mozilla-1.3 Alpha.\n" +
+ "Sorry, your Gecko is not supported.");
+ return false;
+ }
+ if (navigator.productSub < 20030210) {
+ alert("Mozilla < 1.3 Beta is not supported!\n" +
+ "I'll try, though, but it might not work.");
+ }
+ }
+ return HTMLArea.is_gecko || HTMLArea.is_ie;
+};
+
+// selection & ranges
+
+// returns the current selection object
+HTMLArea.prototype._getSelection = function() {
+ if (HTMLArea.is_ie) {
+ return this._doc.selection;
+ } else {
+ return this._iframe.contentWindow.getSelection();
+ }
+};
+
+// returns a range for the current selection
+HTMLArea.prototype._createRange = function(sel) {
+ if (HTMLArea.is_ie) {
+ return sel.createRange();
+ } else {
+ this.focusEditor();
+ if (typeof sel != "undefined") {
+ return sel.getRangeAt(0);
+ } else {
+ return this._doc.createRange();
+ }
+ }
+};
+
+// event handling
+
+HTMLArea._addEvent = function(el, evname, func) {
+ if (HTMLArea.is_ie) {
+ el.attachEvent("on" + evname, func);
+ } else {
+ el.addEventListener(evname, func, true);
+ }
+};
+
+HTMLArea._addEvents = function(el, evs, func) {
+ for (var i in evs) {
+ HTMLArea._addEvent(el, evs[i], func);
+ }
+};
+
+HTMLArea._removeEvent = function(el, evname, func) {
+ if (HTMLArea.is_ie) {
+ el.detachEvent("on" + evname, func);
+ } else {
+ el.removeEventListener(evname, func, true);
+ }
+};
+
+HTMLArea._removeEvents = function(el, evs, func) {
+ for (var i in evs) {
+ HTMLArea._removeEvent(el, evs[i], func);
+ }
+};
+
+HTMLArea._stopEvent = function(ev) {
+ if (HTMLArea.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+};
+
+HTMLArea._removeClass = function(el, className) {
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
+};
+
+HTMLArea._addClass = function(el, className) {
+ // remove the class first, if already there
+ HTMLArea._removeClass(el, className);
+ el.className += " " + className;
+};
+
+HTMLArea._hasClass = function(el, className) {
+ if (!(el && el.className)) {
+ return false;
+ }
+ var cls = el.className.split(" ");
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] == className) {
+ return true;
+ }
+ }
+ return false;
+};
+
+HTMLArea.isBlockElement = function(el) {
+ var blockTags = " body form textarea fieldset ul ol dl li div " +
+ "p h1 h2 h3 h4 h5 h6 quote pre table thead " +
+ "tbody tfoot tr td iframe address ";
+ return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+HTMLArea.needsClosingTag = function(el) {
+ var closingTags = " script style div span tr td tbody table em strong font a ";
+ return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+// performs HTML encoding of some given string
+HTMLArea.htmlEncode = function(str) {
+ // we don't need regexp for that, but.. so be it for now.
+ str = str.replace(/&/ig, "&");
+ str = str.replace(/</ig, "<");
+ str = str.replace(/>/ig, ">");
+ str = str.replace(/\x22/ig, """);
+ // \x22 means '"' -- we use hex reprezentation so that we don't disturb
+ // JS compressors (well, at least mine fails.. ;)
+ return str;
+};
+
+// Retrieves the HTML code from the given node. This is a replacement for
+// getting innerHTML, using standard DOM calls.
+HTMLArea.getHTML = function(root, outputRoot) {
+ var html = "";
+ switch (root.nodeType) {
+ case 1: // Node.ELEMENT_NODE
+ case 11: // Node.DOCUMENT_FRAGMENT_NODE
+ var closed;
+ var i;
+ if (outputRoot) {
+ closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));
+ html = "<" + root.tagName.toLowerCase();
+ var attrs = root.attributes;
+ for (i = 0; i < attrs.length; ++i) {
+ var a = attrs.item(i);
+ if (!a.specified) {
+ continue;
+ }
+ var name = a.nodeName.toLowerCase();
+ if (/_moz/.test(name)) {
+ // Mozilla reports some special tags
+ // here; we don't need them.
+ continue;
+ }
+ var value;
+ if (name != "style") {
+ // IE5.5 reports 25 when cellSpacing is
+ // 1; other values might be doomed too.
+ // For this reason we extract the
+ // values directly from the root node.
+ // I'm starting to HATE JavaScript
+ // development. Browser differences
+ // suck.
+ if (typeof root[a.nodeName] != "undefined") {
+ value = root[a.nodeName];
+ } else {
+ value = a.nodeValue;
+ }
+ } else { // IE fails to put style in attributes list
+ // FIXME: cssText reported by IE is UPPERCASE
+ value = root.style.cssText;
+ }
+ if (/_moz/.test(value)) {
+ // Mozilla reports some special tags
+ // here; we don't need them.
+ continue;
+ }
+ html += " " + name + '="' + value + '"';
+ }
+ html += closed ? " />" : ">";
+ }
+ for (i = root.firstChild; i; i = i.nextSibling) {
+ html += HTMLArea.getHTML(i, true);
+ }
+ if (outputRoot && !closed) {
+ html += "</" + root.tagName.toLowerCase() + ">";
+ }
+ break;
+ case 3: // Node.TEXT_NODE
+ html = HTMLArea.htmlEncode(root.data);
+ break;
+ case 8: // Node.COMMENT_NODE
+ html = "<!--" + root.data + "-->";
+ break; // skip comments, for now.
+ }
+ return html;
+};
+
+// creates a rgb-style color from a number
+HTMLArea._makeColor = function(v) {
+ if (typeof v != "number") {
+ // already in rgb (hopefully); IE doesn't get here.
+ return v;
+ }
+ // IE sends number; convert to rgb.
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "rgb(" + r + "," + g + "," + b + ")";
+};
+
+// returns hexadecimal color representation from a number or a rgb-style color.
+HTMLArea._colorToRgb = function(v) {
+ // returns the hex representation of one byte (2 digits)
+ function hex(d) {
+ return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
+ };
+
+ if (typeof v == "number") {
+ // we're talking to IE here
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+
+ if (v.substr(0, 3) == "rgb") {
+ // in rgb(...) form -- Mozilla
+ var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
+ if (v.match(re)) {
+ var r = parseInt(RegExp.$1);
+ var g = parseInt(RegExp.$2);
+ var b = parseInt(RegExp.$3);
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+ // doesn't match RE?! maybe uses percentages or float numbers
+ // -- FIXME: not yet implemented.
+ return null;
+ }
+
+ if (v[0] == "#") {
+ // already hex rgb (hopefully :D )
+ return v;
+ }
+
+ // if everything else fails ;)
+ return null;
+};
+
+// modal dialogs for Mozilla (for IE we're using the showModalDialog() call).
+
+// receives an URL to the popup dialog and a function that receives one value;
+// this function will get called after the dialog is closed, with the return
+// value of the dialog.
+HTMLArea.prototype._popupDialog = function(url, action, init) {
+ Dialog(this.popupURL(url), action, init);
+};
+
+// paths
+
+HTMLArea.prototype.imgURL = function(file, plugin) {
+ if (typeof plugin == "undefined") {
+ return this.config.editorURL + file;
+ } else {
+ return this.config.editorURL + "plugins/" + plugin + "/img/" + file;
+ }
+};
+
+HTMLArea.prototype.popupURL = function(file) {
+ return this.config.editorURL + this.config.popupURL + file;
+};
+
+// EOF
+// Local variables: //
+// c-basic-offset:8 //
+// indent-tabs-mode:t //
+// End: //
--- /dev/null
+// I18N constants
+
+// LANG: "en", ENCODING: UTF-8 | ISO-8859-1
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+
+// FOR TRANSLATORS:
+//
+// 1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+// (at least a valid email address)
+//
+// 2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+// (if this is not possible, please include a comment
+// that states what encoding is necessary.)
+
+SpellChecker.I18N = {
+ "CONFIRM_LINK_CLICK" : "Please confirm that you want to open this link",
+ "Cancel" : "Cancel",
+ "Dictionary" : "Dictionary",
+ "Finished list of mispelled words" : "Finished list of mispelled words",
+ "I will open it in a new page." : "I will open it in a new page.",
+ "Ignore all" : "Ignore all",
+ "Ignore" : "Ignore",
+ "NO_ERRORS" : "No mispelled words found with the selected dictionary.",
+ "NO_ERRORS_CLOSING" : "Spell check complete, didn't find any mispelled words. Closing now...",
+ "OK" : "OK",
+ "Original word" : "Original word",
+ "Please wait. Calling spell checker." : "Please wait. Calling spell checker.",
+ "Please wait: changing dictionary to" : "Please wait: changing dictionary to",
+ "QUIT_CONFIRMATION" : "This will drop changes and quit spell checker. Please confirm.",
+ "Re-check" : "Re-check",
+ "Replace all" : "Replace all",
+ "Replace with" : "Replace with",
+ "Replace" : "Replace",
+ "SC-spell-check" : "Spell-check",
+ "Suggestions" : "Suggestions",
+ "pliz weit ;-)" : "pliz weit ;-)"
+};
--- /dev/null
+// I18N constants
+
+// LANG: "ro", ENCODING: UTF-8
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+
+// FOR TRANSLATORS:
+//
+// 1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+// (at least a valid email address)
+//
+// 2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+// (if this is not possible, please include a comment
+// that states what encoding is necessary.)
+
+SpellChecker.I18N = {
+ "CONFIRM_LINK_CLICK" : "Vă rog confirmaţi că vreţi să deschideţi acest link",
+ "Cancel" : "Anulează",
+ "Dictionary" : "Dicţionar",
+ "Finished list of mispelled words" : "Am terminat lista de cuvinte greşite",
+ "I will open it in a new page." : "O voi deschide într-o altă fereastră.",
+ "Ignore all" : "Ignoră toate",
+ "Ignore" : "Ignoră",
+ "NO_ERRORS" : "Nu am găsit nici un cuvânt greşit cu acest dicţionar.",
+ "NO_ERRORS_CLOSING" : "Am terminat, nu am detectat nici o greşeală. Acum închid fereastra...",
+ "OK" : "OK",
+ "Original word" : "Cuvântul original",
+ "Please wait. Calling spell checker." : "Vă rog aşteptaţi. Apelez spell-checker-ul.",
+ "Please wait: changing dictionary to" : "Vă rog aşteptaţi. Schimb dicţionarul cu",
+ "QUIT_CONFIRMATION" : "Doriţi să renunţaţi la modificări şi să închid spell-checker-ul?",
+ "Re-check" : "Scanează",
+ "Replace all" : "Înlocuieşte toate",
+ "Replace with" : "Înlocuieşte cu",
+ "Replace" : "Înlocuieşte",
+ "SC-spell-check" : "Detectează greşeli",
+ "Suggestions" : "Sugestii",
+ "pliz weit ;-)" : "va rog ashteptatzi ;-)"
+};
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 3.2//EN">
+<html>
+ <head>
+ <title>HTMLArea Spell Checker</title>
+ </head>
+
+ <body>
+ <h1>HTMLArea Spell Checker</h1>
+
+ <p>The HTMLArea Spell Checker subsystem consists of the following
+ files:</p>
+
+ <ul>
+
+ <li>spell-checker.js — the spell checker plugin interface for
+ HTMLArea</li>
+
+ <li>spell-checker-ui.html — the HTML code for the user
+ interface</li>
+
+ <li>spell-checker-ui.js — functionality of the user
+ interface</li>
+
+ <li>spell-checker-logic.cgi — Perl CGI script that checks a text
+ given through POST for spelling errors</li>
+
+ <li>spell-checker-style.css — style for mispelled words</li>
+
+ <li>lang/en.js — main language file (English).</li>
+
+ </ul>
+
+ <h2>Process overview</h2>
+
+ <p>
+ When an end-user clicks the "spell-check" button in the HTMLArea
+ editor, a new window is opened with the URL of "spell-check-ui.html".
+ This window initializes itself with the text found in the editor (uses
+ <tt>window.opener.SpellChecker.editor</tt> global variable) and it
+ submits the text to the server-side script "spell-check-logic.cgi".
+ The target of the FORM is an inline frame which is used both to
+ display the text and correcting.
+ </p>
+
+ <p>
+ Further, spell-check-logic.cgi calls Aspell for each portion of plain
+ text found in the given HTML. It rebuilds an HTML file that contains
+ clear marks of which words are incorrect, along with suggestions for
+ each of them. This file is then loaded in the inline frame. Upon
+ loading, a JavaScript function from "spell-check-ui.js" is called.
+ This function will retrieve all mispelled words from the HTML of the
+ iframe and will setup the user interface so that it allows correction.
+ </p>
+
+ <h2>The server-side script (spell-check-logic.cgi)</h2>
+
+ <p>
+ <strong>Unicode safety</strong> — the program <em>is</em>
+ Unicode safe. HTML entities are expanded into their corresponding
+ Unicode characters. These characters will be matched as part of the
+ word passed to Aspell. All texts passed to Aspell are in Unicode
+ (when appropriate). However, Aspell seems to not support Unicode
+ yet (<a
+ href="http://mail.gnu.org/archive/html/aspell-user/2000-11/msg00007.html">thread concerning Aspell and Unicode</a>).
+ This mean that words containing Unicode
+ characters that are not in 0..255 are likely to be reported as "mispelled" by Aspell.
+ </p>
+
+ <p>
+ I digged the Net for a couple of hours today and I can't seem to find
+ any open-source spell checker that has Unicode support. For this
+ reason we keep using Aspell, because it also seems to have the
+ best suggestions engine. Unicode support will eventually be
+ implemented in Aspell. <a href="mailto:kevin@atkinson.dhs.org">Email
+ Kevin Atkinson</a> (Aspell author and maintainer) about this ;-)
+ </p>
+
+ <p>
+ The Perl Unicode manual (man perluniintro) states:
+ </p>
+
+ <blockquote>
+ <em>
+ Starting from Perl 5.6.0, Perl has had the capacity to handle Unicode
+ natively. Perl 5.8.0, however, is the first recommended release for
+ serious Unicode work. The maintenance release 5.6.1 fixed many of the
+ problems of the initial Unicode implementation, but for example regular
+ expressions still do not work with Unicode in 5.6.1.
+ </em>
+ </blockquote>
+
+ <p>In other words, do <em>not</em> assume that this script is
+ Unicode-safe on Perl interpreters older than 5.8.0.</p>
+
+ <p>The following Perl modules are required:</p>
+
+ <ul>
+ <li><a href="http://search.cpan.org/search?query=Text%3A%3AAspell&mode=all" target="_blank">Text::Aspell</a></li>
+ <li><a href="http://search.cpan.org/search?query=HTML%3A%3AParser&mode=all" target="_blank">HTML::Parser</a></li>
+ <li><a href="http://search.cpan.org/search?query=HTML%3A%3AEntities&mode=all" target="_blank">HTML::Entities</a></li>
+ <li><a href="http://search.cpan.org/search?query=CGI&mode=all" target="_blank">CGI</a></li>
+ </ul>
+
+ <p>Of these, only Text::Aspell might need to be installed manually. The
+ others are likely to be available by default in most Perl distributions.</p>
+
+ <hr />
+ <address><a href="http://students.infoiasi.ro/~mishoo/">Mihai Bazon</a></address>
+<!-- Created: Thu Jul 17 13:22:27 EEST 2003 -->
+<!-- hhmts start -->
+Last modified on Sun Aug 10 12:28:24 2003
+<!-- hhmts end -->
+<!-- doc-lang: English -->
+ </body>
+</html>
--- /dev/null
+#! /usr/bin/perl -w
+
+# Spell Checker Plugin for HTMLArea-3.0
+# Implementation by Mihai Bazon. Sponsored by www.americanbible.org
+#
+# htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+# This notice MUST stay intact for use (see license.txt).
+#
+# A free WYSIWYG editor replacement for <textarea> fields.
+# For full source code and docs, visit http://www.interactivetools.com/
+#
+# Version 3.0 developed by Mihai Bazon for InteractiveTools.
+# http://students.infoiasi.ro/~mishoo
+#
+# $Id$
+
+use strict;
+use utf8;
+use Encode;
+use Text::Aspell;
+use HTML::Parser;
+use HTML::Entities;
+use CGI;
+
+my $debug = 0;
+
+open (DEBUG, '>:encoding(UTF-8)', '> /tmp/spell-check-debug.log') if $debug;
+
+# use Data::Dumper; # for debug only
+
+my $speller = new Text::Aspell;
+my $cgi = new CGI;
+
+# FIXME: report a nice error...
+die "Can't create speller!" unless $speller;
+
+# add configurable option for this
+my $dict = $cgi->param('dictionary') || 'en_US';
+$speller->set_option('lang', $dict);
+
+# ultra, fast, normal, bad-spellers
+# bad-spellers seems to cause segmentation fault
+$speller->set_option('sug-mode', 'ultra');
+
+my @replacements = ();
+
+sub text_handler {
+ my ($offset, $length, $text, $is_cdata) = @_;
+ if ($is_cdata or $text =~ /^\s*$/) {
+ return 0;
+ }
+ # print STDERR "*** OFFSET: $offset, LENGTH: $length, $text\n";
+ $text = decode_entities($text);
+ $text =~ s/&#([0-9]+);/chr($1)/eg;
+ $text =~ s/&#x([0-9a-fA-F]+);/chr(hex $1)/eg;
+ my $repl = spellcheck($text);
+ if ($repl) {
+ push(@replacements, [ $offset, $length, $repl ]);
+ }
+}
+
+my $p = HTML::Parser->new
+ (api_version => 3,
+ handlers => { start => [ sub {
+ my ($self, $tagname, $attrs) = @_;
+ # print STDERR "\033[1;31m parsing tag: $tagname\033[0m\n";
+ # following we skip words that have already been marked as "fixed".
+ if ($tagname eq "span" and $attrs->{class} =~ /HA-spellcheck-fixed/) {
+ $self->handler(text => undef);
+ }
+ }, "self, tagname, attr"
+ ],
+ end => [ sub {
+ my ($self, $tagname) = @_;
+ # print STDERR "\033[1;32m END tag: $tagname\033[0m\n";
+ $self->handler(text => \&text_handler, 'offset, length, dtext, is_cdata');
+ }, "self, tagname"
+ ]
+ }
+ );
+$p->handler(text => \&text_handler, 'offset, length, dtext, is_cdata');
+$p->case_sensitive(1);
+my $file_content = $cgi->param('content');
+
+if ($debug) {
+ open (FOO, '>:encoding(UTF-8)', '/tmp/spell-check-before');
+ print FOO $file_content, "\n";
+ close(FOO);
+}
+
+$p->parse($file_content);
+$p->eof();
+
+foreach (reverse @replacements) {
+ substr($file_content, $_->[0], $_->[1], $_->[2]);
+}
+
+# we output UTF-8
+binmode(STDOUT, ':encoding(UTF-8)'); # apparently, this sucks.
+print "Content-type: text/html; charset: utf-8\n\n";
+print qq^
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<link rel="stylesheet" type="text/css" media="all" href="spell-check-style.css" />
+</head>
+<body onload="window.parent.finishedSpellChecking();">^;
+
+print $file_content;
+if ($cgi->param('init') eq '1') {
+ my @dicts = $speller->dictionary_info();
+ my $dictionaries = '';
+ foreach my $i (@dicts) {
+ $dictionaries .= ',' . $i->{name} unless $i->{jargon};
+ }
+ $dictionaries =~ s/^,//;
+ print qq^
+<div id="HA-spellcheck-dictionaries"
+>$dictionaries</div>
+^;
+}
+
+if ($debug) {
+ open (FOO, '>:encoding(UTF-8)', '/tmp/spell-check-after');
+ print FOO $file_content, "\n";
+ close(FOO);
+}
+
+print '</body></html>';
+
+# Perl is beautiful.
+sub spellcheck {
+ my $text = shift;
+ sub check { # called for each word in the text
+ # input is in UTF-8
+ my $U_word = shift;
+ my $word = encode($speller->get_option('encoding'), $U_word);
+ print DEBUG "*$U_word* ----> |$word|\n" if $debug;
+ if ($speller->check($word)) {
+ return $U_word; # we return the word in UTF-8
+ } else {
+ # we should have suggestions; give them back to browser in UTF-8
+ my $suggestions = decode($speller->get_option('encoding'), join(',', $speller->suggest($word)));
+ my $ret = '<span class="HA-spellcheck-error">'.$U_word.'</span><span class="HA-spellcheck-suggestions">'.$suggestions.'</span>';
+ return $ret;
+ }
+ }
+ $text =~ s/([[:word:]']+)/check($1)/egs;
+ # $text =~ s/(\w+)/check($1)/egs;
+
+ # the following is definitely what we want to use; too bad it sucks most.
+ # $text =~ s/(\p{IsWord}+)/check($1)/egs;
+ return $text;
+}
--- /dev/null
+.HA-spellcheck-error { border-bottom: 2px dotted #f00; cursor: default; }
+.HA-spellcheck-same { background-color: #ff8; color: #000; }
+.HA-spellcheck-hover { background-color: #433; color: white; }
+.HA-spellcheck-fixed { border-bottom: 1px dotted #0b8; }
+.HA-spellcheck-current { background-color: #7be; color: #000; }
+.HA-spellcheck-suggestions { display: none; }
+
+#HA-spellcheck-dictionaries { display: none; }
+
+a:link, a:visited { color: #55e; }
--- /dev/null
+<!--
+
+ Strangely, IE sucks with or without the DOCTYPE switch.
+ I thought it would only suck without it.
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+ Spell Checker Plugin for HTMLArea-3.0
+ Implementation by Mihai Bazon. Sponsored by www.americanbible.org
+
+ htmlArea v3.0 - Copyright (c) 2003 interactivetools.com, inc.
+ This notice MUST stay intact for use (see license.txt).
+
+ A free WYSIWYG editor replacement for <textarea> fields.
+ For full source code and docs, visit http://www.interactivetools.com/
+
+ Version 3.0 developed by Mihai Bazon for InteractiveTools.
+ http://students.infoiasi.ro/~mishoo
+
+ $Id$
+
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+ <head>
+ <title>Spell Checker</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" src="spell-check-ui.js"></script>
+
+ <style type="text/css">
+ html, body { height: 100%; margin: 0px; padding: 0px; background-color: #fff;
+ color: #000; }
+ a:link, a:visited { color: #00f; text-decoration: none; }
+ a:hover { color: #f00; text-decoration: underline; }
+
+ table { background-color: ButtonFace; color: ButtonText;
+ font-family: tahoma,verdana,sans-serif; font-size: 11px; }
+
+ iframe { background-color: #fff; color: #000; }
+
+ .controls { width: 13em; }
+ .controls .sectitle { /* background-color: #736c6c; color: #fff;
+ border-top: 1px solid #000; border-bottom: 1px solid #fff; */
+ text-align: center;
+ font-weight: bold; padding: 2px 4px; }
+ .controls .secbody { margin-bottom: 10px; }
+
+ button, select { font-family: tahoma,verdana,sans-serif; font-size: 11px; }
+ button { width: 6em; padding: 0px; }
+
+ input, select { font-family: fixed,"andale mono",monospace; }
+
+ #v_currentWord { color: #f00; font-weight: bold; font-size: 120%; }
+ #statusbar { padding: 7px 0px 0px 5px; }
+ #status { font-weight: bold; }
+ </style>
+
+ </head>
+
+ <body onload="initDocument()">
+
+ <form style="display: none;" action="spell-check-logic.cgi"
+ method="post" target="framecontent"
+ accept-charset="utf-8"
+ ><input type="hidden" name="content" id="f_content"
+ /><input type="hidden" name="dictionary" id="f_dictionary"
+ /><input type="hidden" name="init" id="f_init" value="1"
+ /></form>
+
+ <table style="height: 100%; width: 100%; border-collapse: collapse;" cellspacing="0" cellpadding="0">
+ <tr>
+ <td colspan="2" style="height: 1em; padding: 2px;">
+ <div style="float: right; padding: 2px;"><span>Dictionary</span>
+ <select id="v_dictionaries" style="width: 10em"></select>
+ <button id="b_recheck">Re-check</button>
+ </div>
+ <span id="status">Please wait. Calling spell checker.</span>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" class="controls">
+ <div class="sectitle">Original word</div>
+ <div class="secbody" id="v_currentWord" style="text-align: center">pliz weit ;-)</div>
+ <div class="sectitle">Replace with</div>
+ <div class="secbody">
+ <input type="text" id="v_replacement" style="width: 94%; margin-left: 3%;" /><br />
+ <div style="text-align: center; margin-top: 2px;">
+ <button id="b_replace">Replace</button><button
+ id="b_replall">Replace all</button><br /><button
+ id="b_ignore">Ignore</button><button
+ id="b_ignall">Ignore all</button>
+ </div>
+ </div>
+ <div class="sectitle">Suggestions</div>
+ <div class="secbody">
+ <select size="11" style="width: 94%; margin-left: 3%;" id="v_suggestions"></select>
+ </div>
+ </td>
+
+ <td>
+ <iframe src="about:blank" width="100%" height="100%"
+ id="i_framecontent" name="framecontent"></iframe>
+ </td>
+ </tr>
+ <tr>
+ <td style="height: 1em;" colspan="2">
+ <div style="padding: 4px 2px 2px 2px; float: right;">
+ <button id="b_ok">OK</button>
+ <button id="b_cancel">Cancel</button>
+ </div>
+ <div id="statusbar"></div>
+ </td>
+ </tr>
+ </table>
+
+ </body>
+
+</html>
--- /dev/null
+// Spell Checker Plugin for HTMLArea-3.0
+// Implementation by Mihai Bazon. Sponsored by www.americanbible.org
+//
+// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+// This notice MUST stay intact for use (see license.txt).
+//
+// A free WYSIWYG editor replacement for <textarea> fields.
+// For full source code and docs, visit http://www.interactivetools.com/
+//
+// Version 3.0 developed by Mihai Bazon for InteractiveTools.
+// http://students.infoiasi.ro/~mishoo
+//
+// $Id$
+
+// internationalization file was already loaded in parent ;-)
+var SpellChecker = window.opener.SpellChecker;
+var i18n = SpellChecker.I18N;
+
+var is_ie = window.opener.HTMLArea.is_ie;
+var editor = SpellChecker.editor;
+var frame = null;
+var currentElement = null;
+var wrongWords = null;
+var modified = false;
+var allWords = {};
+
+function makeCleanDoc(leaveFixed) {
+ // document.getElementById("status").innerHTML = 'Please wait: rendering valid HTML';
+ for (var i in wrongWords) {
+ var el = wrongWords[i];
+ if (!(leaveFixed && /HA-spellcheck-fixed/.test(el.className))) {
+ el.parentNode.insertBefore(el.firstChild, el);
+ el.parentNode.removeChild(el.nextSibling);
+ el.parentNode.removeChild(el);
+ } else {
+ el.className = "HA-spellcheck-fixed";
+ el.parentNode.removeChild(el.nextSibling);
+ }
+ }
+ // we should use innerHTML here, but IE6's implementation fucks up the
+ // HTML to such extent that our poor Perl parser doesn't understand it
+ // anymore.
+ return window.opener.HTMLArea.getHTML(frame.contentWindow.document.body, leaveFixed);
+};
+
+function recheckClicked() {
+ document.getElementById("status").innerHTML = i18n["Please wait: changing dictionary to"] + ': "' + document.getElementById("f_dictionary").value + '".';
+ var field = document.getElementById("f_content");
+ field.value = makeCleanDoc(true);
+ field.form.submit();
+};
+
+function saveClicked() {
+ if (modified) {
+ editor.setHTML(makeCleanDoc(false));
+ }
+ window.close();
+ return false;
+};
+
+function cancelClicked() {
+ var ok = true;
+ if (modified) {
+ ok = confirm(i18n["QUIT_CONFIRMATION"]);
+ }
+ if (ok) {
+ window.close();
+ }
+ return false;
+};
+
+function replaceWord(el) {
+ var replacement = document.getElementById("v_replacement").value;
+ modified = (el.innerHTML != replacement);
+ if (el) {
+ el.className = el.className.replace(/\s*HA-spellcheck-(hover|fixed)\s*/g, " ");
+ }
+ el.className += " HA-spellcheck-fixed";
+ el.__msh_fixed = true;
+ if (!modified) {
+ return false;
+ }
+ el.innerHTML = replacement;
+};
+
+function replaceClicked() {
+ replaceWord(currentElement);
+ var start = currentElement.__msh_id;
+ var index = start;
+ do {
+ ++index;
+ if (index == wrongWords.length) {
+ index = 0;
+ }
+ } while ((index != start) && wrongWords[index].__msh_fixed);
+ if (index == start) {
+ index = 0;
+ alert(i18n["Finished list of mispelled words"]);
+ }
+ wrongWords[index].onclick();
+ return false;
+};
+
+function replaceAllClicked() {
+ var replacement = document.getElementById("v_replacement").value;
+ var ok = true;
+ var spans = allWords[currentElement.__msh_origWord];
+ if (spans.length == 0) {
+ alert("An impossible condition just happened. Call FBI. ;-)");
+ } else if (spans.length == 1) {
+ replaceClicked();
+ return false;
+ }
+ /*
+ var message = "The word \"" + currentElement.__msh_origWord + "\" occurs " + spans.length + " times.\n";
+ if (replacement == currentElement.__msh_origWord) {
+ ok = confirm(message + "Ignore all occurrences?");
+ } else {
+ ok = confirm(message + "Replace all occurrences with \"" + replacement + "\"?");
+ }
+ */
+ if (ok) {
+ for (var i in spans) {
+ if (spans[i] != currentElement) {
+ replaceWord(spans[i]);
+ }
+ }
+ // replace current element the last, so that we jump to the next word ;-)
+ replaceClicked();
+ }
+ return false;
+};
+
+function ignoreClicked() {
+ document.getElementById("v_replacement").value = currentElement.__msh_origWord;
+ replaceClicked();
+ return false;
+};
+
+function ignoreAllClicked() {
+ document.getElementById("v_replacement").value = currentElement.__msh_origWord;
+ replaceAllClicked();
+ return false;
+};
+
+function learnClicked() {
+ alert("Not [yet] implemented");
+ return false;
+};
+
+function internationalizeWindow() {
+ var types = ["div", "span", "button"];
+ for (var i in types) {
+ var tag = types[i];
+ var els = document.getElementsByTagName(tag);
+ for (var j = els.length; --j >= 0;) {
+ var el = els[j];
+ if (el.childNodes.length == 1 && /\S/.test(el.innerHTML)) {
+ var txt = el.innerHTML;
+ if (typeof i18n[txt] != "undefined") {
+ el.innerHTML = i18n[txt];
+ }
+ }
+ }
+ }
+};
+
+function initDocument() {
+ internationalizeWindow();
+ modified = false;
+ frame = document.getElementById("i_framecontent");
+ var field = document.getElementById("f_content");
+ field.value = editor.getHTML();
+ field.form.submit();
+ document.getElementById("f_init").value = "0";
+
+ // assign some global event handlers
+
+ var select = document.getElementById("v_suggestions");
+ select.onchange = function() {
+ document.getElementById("v_replacement").value = this.value;
+ };
+ if (is_ie) {
+ select.attachEvent("ondblclick", replaceClicked);
+ } else {
+ select.addEventListener("dblclick", replaceClicked, true);
+ }
+
+ document.getElementById("b_replace").onclick = replaceClicked;
+ // document.getElementById("b_learn").onclick = learnClicked;
+ document.getElementById("b_replall").onclick = replaceAllClicked;
+ document.getElementById("b_ignore").onclick = ignoreClicked;
+ document.getElementById("b_ignall").onclick = ignoreAllClicked;
+ document.getElementById("b_recheck").onclick = recheckClicked;
+
+ document.getElementById("b_ok").onclick = saveClicked;
+ document.getElementById("b_cancel").onclick = cancelClicked;
+
+ select = document.getElementById("v_dictionaries");
+ select.onchange = function() {
+ document.getElementById("f_dictionary").value = this.value;
+ };
+};
+
+function wordClicked() {
+ if (currentElement) {
+ var a = allWords[currentElement.__msh_origWord];
+ currentElement.className = currentElement.className.replace(/\s*HA-spellcheck-current\s*/g, " ");
+ for (var i in a) {
+ var el = a[i];
+ if (el != currentElement) {
+ el.className = el.className.replace(/\s*HA-spellcheck-same\s*/g, " ");
+ }
+ }
+ }
+ currentElement = this;
+ this.className += " HA-spellcheck-current";
+ var a = allWords[currentElement.__msh_origWord];
+ for (var i in a) {
+ var el = a[i];
+ if (el != currentElement) {
+ el.className += " HA-spellcheck-same";
+ }
+ }
+ document.getElementById("b_replall").disabled = (a.length <= 1);
+ document.getElementById("b_ignall").disabled = (a.length <= 1);
+ var txt;
+ if (a.length == 1) {
+ txt = "one occurrence";
+ } else if (a.length == 2) {
+ txt = "two occurrences";
+ } else {
+ txt = a.length + " occurrences";
+ }
+ document.getElementById("statusbar").innerHTML = "Found " + txt +
+ ' for word "<b>' + currentElement.__msh_origWord + '</b>"';
+ var select = document.getElementById("v_suggestions");
+ for (var i = select.length; --i >= 0;) {
+ select.remove(i);
+ }
+ var suggestions;
+ suggestions = this.nextSibling.firstChild.data.split(/,/);
+ for (var i = 0; i < suggestions.length; ++i) {
+ var txt = suggestions[i];
+ var option = document.createElement("option");
+ option.value = txt;
+ option.appendChild(document.createTextNode(txt));
+ select.appendChild(option);
+ }
+ document.getElementById("v_currentWord").innerHTML = this.__msh_origWord;
+ if (suggestions.length > 0) {
+ select.selectedIndex = 0;
+ select.onchange();
+ } else {
+ document.getElementById("v_replacement").value = this.innerHTML;
+ }
+ return false;
+};
+
+function wordMouseOver() {
+ this.className += " HA-spellcheck-hover";
+};
+
+function wordMouseOut() {
+ this.className = this.className.replace(/\s*HA-spellcheck-hover\s*/g, " ");
+};
+
+function finishedSpellChecking() {
+ // initialization of global variables
+ currentElement = null;
+ wrongWords = null;
+ allWords = {};
+
+ document.getElementById("status").innerHTML = "HTMLArea Spell Checker (<a href='readme-tech.html' target='_blank' title='Technical information'>info</a>)";
+ var doc = frame.contentWindow.document;
+ var spans = doc.getElementsByTagName("span");
+ var sps = [];
+ var id = 0;
+ for (var i = 0; i < spans.length; ++i) {
+ var el = spans[i];
+ if (/HA-spellcheck-error/.test(el.className)) {
+ sps.push(el);
+ el.onclick = wordClicked;
+ el.onmouseover = wordMouseOver;
+ el.onmouseout = wordMouseOut;
+ el.__msh_id = id++;
+ var txt = (el.__msh_origWord = el.firstChild.data);
+ el.__msh_fixed = false;
+ if (typeof allWords[txt] == "undefined") {
+ allWords[txt] = [el];
+ } else {
+ allWords[txt].push(el);
+ }
+ }
+ }
+ wrongWords = sps;
+ if (sps.length == 0) {
+ if (!modified) {
+ alert(i18n["NO_ERRORS_CLOSING"]);
+ window.close();
+ } else {
+ alert(i18n["NO_ERRORS"]);
+ }
+ return false;
+ }
+ (currentElement = sps[0]).onclick();
+ var as = doc.getElementsByTagName("a");
+ for (var i = as.length; --i >= 0;) {
+ var a = as[i];
+ a.onclick = function() {
+ if (confirm(i18n["CONFIRM_LINK_CLICK"] + ":\n" +
+ this.href + "\n" + i18n["I will open it in a new page."])) {
+ window.open(this.href);
+ }
+ return false;
+ };
+ }
+ var dicts = doc.getElementById("HA-spellcheck-dictionaries");
+ if (dicts) {
+ dicts.parentNode.removeChild(dicts);
+ dicts = dicts.innerHTML.split(/,/);
+ var select = document.getElementById("v_dictionaries");
+ for (var i = select.length; --i >= 0;) {
+ select.remove(i);
+ }
+ for (var i = 0; i < dicts.length; ++i) {
+ var txt = dicts[i];
+ var option = document.createElement("option");
+ option.value = txt;
+ option.appendChild(document.createTextNode(txt));
+ select.appendChild(option);
+ }
+ }
+};
--- /dev/null
+// Spell Checker Plugin for HTMLArea-3.0
+// Implementation by Mihai Bazon. Sponsored by www.americanbible.org
+//
+// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+// This notice MUST stay intact for use (see license.txt).
+//
+// A free WYSIWYG editor replacement for <textarea> fields.
+// For full source code and docs, visit http://www.interactivetools.com/
+//
+// Version 3.0 developed by Mihai Bazon for InteractiveTools.
+// http://students.infoiasi.ro/~mishoo
+//
+// $Id$
+
+function SpellChecker(editor) {
+ this.editor = editor;
+
+ var cfg = editor.config;
+ var tt = SpellChecker.I18N;
+ var bl = SpellChecker.btnList;
+ var self = this;
+
+ // register the toolbar buttons provided by this plugin
+ var toolbar = [];
+ for (var i in bl) {
+ var btn = bl[i];
+ if (!btn) {
+ toolbar.push("separator");
+ } else {
+ var id = "SC-" + btn[0];
+ cfg.registerButton(id, tt[id], "plugins/SpellChecker/img/" + btn[0] + ".gif", false,
+ function(editor, id) {
+ // dispatch button press event
+ self.buttonPress(editor, id);
+ }, btn[1]);
+ toolbar.push(id);
+ }
+ }
+
+ for (var i in toolbar) {
+ cfg.toolbar[0].push(toolbar[i]);
+ }
+};
+
+SpellChecker.btnList = [
+ null, // separator
+ ["spell-check"]
+ ];
+
+SpellChecker.prototype.buttonPress = function(editor, id) {
+ switch (id) {
+ case "SC-spell-check":
+ SpellChecker.editor = editor;
+ SpellChecker.init = true;
+ var uiurl = editor.config.editorURL + "plugins/SpellChecker/spell-check-ui.html";
+ var win;
+ if (HTMLArea.is_ie) {
+ win = window.open(uiurl, "SC_spell_checker",
+ "toolbar=no,location=no,directories=no,status=no,menubar=no," +
+ "scrollbars=no,resizable=yes,width=600,height=400");
+ } else {
+ win = window.open(uiurl, "SC_spell_checker",
+ "toolbar=no,menubar=no,personalbar=no,width=600,height=400," +
+ "scrollbars=no,resizable=yes");
+ }
+ win.focus();
+ break;
+ }
+};
+
+// this needs to be global, it's accessed from spell-check-ui.html
+SpellChecker.editor = null;
--- /dev/null
+// I18N constants
+
+// LANG: "en", ENCODING: UTF-8 | ISO-8859-1
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+
+// FOR TRANSLATORS:
+//
+// 1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+// (at least a valid email address)
+//
+// 2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+// (if this is not possible, please include a comment
+// that states what encoding is necessary.)
+
+TableOperations.I18N = {
+ "Align": "Align",
+ "All four sides": "All four sides",
+ "Background": "Background",
+ "Baseline": "Baseline",
+ "Border": "Border",
+ "Borders": "Borders",
+ "Bottom": "Bottom",
+ "CSS Style": "Style [CSS]",
+ "Caption": "Caption",
+ "Cell Properties": "Cell Properties",
+ "Center": "Center",
+ "Char": "Char",
+ "Collapsed borders": "Collapsed borders",
+ "Color": "Color",
+ "Description": "Description",
+ "FG Color": "FG Color",
+ "Float": "Float",
+ "Frames": "Frames",
+ "Height": "Height",
+ "How many columns would you like to merge?": "How many columns would you like to merge?",
+ "How many rows would you like to merge?": "How many rows would you like to merge?",
+ "Image URL": "Image URL",
+ "Justify": "Justify",
+ "Layout": "Layout",
+ "Left": "Left",
+ "Margin": "Margin",
+ "Middle": "Middle",
+ "No rules": "No rules",
+ "No sides": "No sides",
+ "None": "None",
+ "Padding": "Padding",
+ "Please click into some cell": "Please click into some cell",
+ "Right": "Right",
+ "Row Properties": "Row Properties",
+ "Rules will appear between all rows and columns": "Rules will appear between all rows and columns",
+ "Rules will appear between columns only": "Rules will appear between columns only",
+ "Rules will appear between rows only": "Rules will appear between rows only",
+ "Rules": "Rules",
+ "Spacing and padding": "Spacing and padding",
+ "Spacing": "Spacing",
+ "Summary": "Summary",
+ "TO-cell-delete": "Delete cell",
+ "TO-cell-insert-after": "Insert cell after",
+ "TO-cell-insert-before": "Insert cell before",
+ "TO-cell-merge": "Merge cells",
+ "TO-cell-prop": "Cell properties",
+ "TO-cell-split": "Split cell",
+ "TO-col-delete": "Delete column",
+ "TO-col-insert-after": "Insert column after",
+ "TO-col-insert-before": "Insert column before",
+ "TO-col-split": "Split column",
+ "TO-row-delete": "Delete row",
+ "TO-row-insert-above": "Insert row before",
+ "TO-row-insert-under": "Insert row after",
+ "TO-row-prop": "Row properties",
+ "TO-row-split": "Split row",
+ "TO-table-prop": "Table properties",
+ "Table Properties": "Table Properties",
+ "Text align": "Text align",
+ "The bottom side only": "The bottom side only",
+ "The left-hand side only": "The left-hand side only",
+ "The right and left sides only": "The right and left sides only",
+ "The right-hand side only": "The right-hand side only",
+ "The top and bottom sides only": "The top and bottom sides only",
+ "The top side only": "The top side only",
+ "Top": "Top",
+ "Unset color": "Unset color",
+ "Vertical align": "Vertical align",
+ "Width": "Width",
+ "not-del-last-cell": "HTMLArea cowardly refuses to delete the last cell in row.",
+ "not-del-last-col": "HTMLArea cowardly refuses to delete the last column in table.",
+ "not-del-last-row": "HTMLArea cowardly refuses to delete the last row in table.",
+ "percent": "percent",
+ "pixels": "pixels"
+};
--- /dev/null
+TableOperations.I18N = {
+ "Align": "Kohdistus",
+ "All four sides": "Kaikki neljä sivua",
+ "Background": "Tausta",
+ "Baseline": "Takaraja",
+ "Border": "Reuna",
+ "Borders": "Reunat",
+ "Bottom": "Alle",
+ "CSS Style": "Tyyli [CSS]",
+ "Caption": "Otsikko",
+ "Cell Properties": "Solun asetukset",
+ "Center": "Keskelle",
+ "Char": "Merkki",
+ "Collapsed borders": "Luhistetut reunat",
+ "Color": "Väri",
+ "Description": "Kuvaus",
+ "FG Color": "FG Väri",
+ "Frames": "Kehykset",
+ "Image URL": "Kuvan osoite",
+ "Layout": "Sommittelu",
+ "Left": "Vasen",
+ "Margin": "Marginaali",
+ "Middle": "Keskelle",
+ "No rules": "Ei viivoja",
+ "No sides": "Ei sivuja",
+ "Padding": "Palstantäyte",
+ "Right": "Oikea",
+ "Row Properties": "Rivin asetukset",
+ "Rules will appear between all rows and columns": "Viivat jokaisen rivin ja sarakkeen välillä",
+ "Rules will appear between columns only": "Viivat ainoastaan sarakkeiden välillä",
+ "Rules will appear between rows only": "Viivat ainoastaan rivien välillä",
+ "Rules": "Viivat",
+ "Spacing": "Palstatila",
+ "Summary": "Yhteenveto",
+ "TO-cell-delete": "Poista solu",
+ "TO-cell-insert-after": "Lisää solu perään",
+ "TO-cell-insert-before": "Lisää solu ennen",
+ "TO-cell-merge": "Yhdistä solut",
+ "TO-cell-prop": "Solun asetukset",
+ "TO-cell-split": "Jaa solu",
+ "TO-col-delete": "Poista sarake",
+ "TO-col-insert-after": "Lisää sarake perään",
+ "TO-col-insert-before": "Lisää sarake ennen",
+ "TO-col-split": "Jaa sarake",
+ "TO-row-delete": "Poista rivi",
+ "TO-row-insert-above": "Lisää rivi yläpuolelle",
+ "TO-row-insert-under": "Lisää rivi alapuolelle",
+ "TO-row-prop": "Rivin asetukset",
+ "TO-row-split": "Jaa rivi",
+ "TO-table-prop": "Taulukon asetukset",
+ "Top": "Ylös",
+ "Table Properties": "Taulukon asetukset",
+ "The bottom side only": "Ainoastaan alapuolelle",
+ "The left-hand side only": "Ainoastaan vasenreuna",
+ "The right and left sides only": "Oikea- ja vasenreuna",
+ "The right-hand side only": "Ainoastaan oikeareuna",
+ "The top and bottom sides only": "Ylä- ja alapuoli.",
+ "The top side only": "Ainoastaan yläpuoli",
+ "Vertical align": "Vertikaali kohdistus",
+ "Width": "Leveys",
+ "not-del-last-cell": "Ei voida poistaa viimeistä solua rivistä.",
+ "not-del-last-col": "Ei voida poistaa viimeistä saraketta taulusta.",
+ "not-del-last-row": "Ei voida poistaa viimeistä riviä taulusta.",
+ "percent": "prosenttia",
+ "pixels": "pikseliä"
+};
--- /dev/null
+// I18N constants
+
+// LANG: "ro", ENCODING: UTF-8
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+
+// FOR TRANSLATORS:
+//
+// 1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+// (at least a valid email address)
+//
+// 2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+// (if this is not possible, please include a comment
+// that states what encoding is necessary.)
+
+TableOperations.I18N = {
+ "Align": "Aliniere",
+ "All four sides": "Toate părţile",
+ "Background": "Fundal",
+ "Baseline": "Baseline",
+ "Border": "Chenar",
+ "Borders": "Chenare",
+ "Bottom": "Jos",
+ "CSS Style": "Stil [CSS]",
+ "Caption": "Titlu de tabel",
+ "Cell Properties": "Proprietăţile celulei",
+ "Center": "Centru",
+ "Char": "Caracter",
+ "Collapsed borders": "Chenare asimilate",
+ "Color": "Culoare",
+ "Description": "Descriere",
+ "FG Color": "Culoare text",
+ "Float": "Poziţie",
+ "Frames": "Chenare",
+ "Height": "Înălţimea",
+ "How many columns would you like to merge?": "Câte coloane vrei să uneşti?",
+ "How many rows would you like to merge?": "Câte linii vrei să uneşti?",
+ "Image URL": "URL-ul imaginii",
+ "Justify": "Justify",
+ "Layout": "Aranjament",
+ "Left": "Stânga",
+ "Margin": "Margine",
+ "Middle": "Mijloc",
+ "No rules": "Fără linii",
+ "No sides": "Fără părţi",
+ "None": "Nimic",
+ "Padding": "Spaţiere",
+ "Please click into some cell": "Vă rog să daţi click într-o celulă",
+ "Right": "Dreapta",
+ "Row Properties": "Proprietăţile liniei",
+ "Rules will appear between all rows and columns": "Vor apărea linii între toate rândurile şi coloanele",
+ "Rules will appear between columns only": "Vor apărea doar linii verticale",
+ "Rules will appear between rows only": "Vor apărea doar linii orizontale",
+ "Rules": "Linii",
+ "Spacing and padding": "Spaţierea",
+ "Spacing": "Între celule",
+ "Summary": "Sumar",
+ "TO-cell-delete": "Şterge celula",
+ "TO-cell-insert-after": "Inserează o celulă la dreapta",
+ "TO-cell-insert-before": "Inserează o celulă la stânga",
+ "TO-cell-merge": "Uneşte celulele",
+ "TO-cell-prop": "Proprietăţile celulei",
+ "TO-cell-split": "Împarte celula",
+ "TO-col-delete": "Şterge coloana",
+ "TO-col-insert-after": "Inserează o coloană la dreapta",
+ "TO-col-insert-before": "Inserează o coloană la stânga",
+ "TO-col-split": "Împarte coloana",
+ "TO-row-delete": "Şterge rândul",
+ "TO-row-insert-above": "Inserează un rând înainte",
+ "TO-row-insert-under": "Inserează un rând după",
+ "TO-row-prop": "Proprietăţile rândului",
+ "TO-row-split": "Împarte rândul",
+ "TO-table-prop": "Proprietăţile tabelei",
+ "Table Properties": "Proprietăţile tabelei",
+ "Text align": "Aliniere",
+ "The bottom side only": "Doar partea de jos",
+ "The left-hand side only": "Doar partea din stânga",
+ "The right and left sides only": "Partea din stânga şi cea din dreapta",
+ "The right-hand side only": "Doar partea din dreapta",
+ "The top and bottom sides only": "Partea de sus si cea de jos",
+ "The top side only": "Doar partea de sus",
+ "Top": "Sus",
+ "Unset color": "Dezactivează culoarea",
+ "Vertical align": "Aliniere pe verticală",
+ "Width": "Lăţime",
+ "not-del-last-cell": "HTMLArea refuză cu laşitate să şteargă ultima celulă din rând.",
+ "not-del-last-col": "HTMLArea refuză cu laşitate să şteargă ultima coloamă din tabela.",
+ "not-del-last-row": "HTMLArea refuză cu laşitate să şteargă ultimul rând din tabela.",
+ "percent": "procente",
+ "pixels": "pixeli"
+};
--- /dev/null
+// Table Operations Plugin for HTMLArea-3.0
+// Implementation by Mihai Bazon. Sponsored by http://www.bloki.com
+//
+// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+// This notice MUST stay intact for use (see license.txt).
+//
+// A free WYSIWYG editor replacement for <textarea> fields.
+// For full source code and docs, visit http://www.interactivetools.com/
+//
+// Version 3.0 developed by Mihai Bazon for InteractiveTools.
+// http://students.infoiasi.ro/~mishoo
+//
+// $Id$
+
+// Object that will encapsulate all the table operations provided by
+// HTMLArea-3.0 (except "insert table" which is included in the main file)
+function TableOperations(editor) {
+ this.editor = editor;
+
+ var cfg = editor.config;
+ var tt = TableOperations.I18N;
+ var bl = TableOperations.btnList;
+ var self = this;
+
+ // register the toolbar buttons provided by this plugin
+ var toolbar = ["linebreak"];
+ for (var i in bl) {
+ var btn = bl[i];
+ if (!btn) {
+ toolbar.push("separator");
+ } else {
+ var id = "TO-" + btn[0];
+ cfg.registerButton(id, tt[id], "plugins/TableOperations/img/" + btn[0] + ".gif", false,
+ function(editor, id) {
+ // dispatch button press event
+ self.buttonPress(editor, id);
+ }, btn[1]);
+ toolbar.push(id);
+ }
+ }
+
+ // add a new line in the toolbar
+ cfg.toolbar.push(toolbar);
+};
+
+/************************
+ * UTILITIES
+ ************************/
+
+// retrieves the closest element having the specified tagName in the list of
+// ancestors of the current selection/caret.
+TableOperations.prototype.getClosest = function(tagName) {
+ var editor = this.editor;
+ var ancestors = editor.getAllAncestors();
+ var ret = null;
+ tagName = ("" + tagName).toLowerCase();
+ for (var i in ancestors) {
+ var el = ancestors[i];
+ if (el.tagName.toLowerCase() == tagName) {
+ ret = el;
+ break;
+ }
+ }
+ return ret;
+};
+
+// this function requires the file PopupDiv/PopupWin to be loaded from browser
+TableOperations.prototype.dialogTableProperties = function() {
+ var i18n = TableOperations.I18N;
+ // retrieve existing values
+ var table = this.getClosest("table");
+ // this.editor.selectNodeContents(table);
+ // this.editor.updateToolbar();
+
+ var dialog = new PopupWin(this.editor, i18n["Table Properties"], function(dialog, params) {
+ TableOperations.processStyle(params, table);
+ for (var i in params) {
+ var val = params[i];
+ switch (i) {
+ case "f_caption":
+ if (/\S/.test(val)) {
+ // contains non white-space characters
+ var caption = table.getElementsByTagName("caption")[0];
+ if (!caption) {
+ caption = dialog.editor._doc.createElement("caption");
+ table.insertBefore(caption, table.firstChild);
+ }
+ caption.innerHTML = val;
+ } else {
+ // search for caption and delete it if found
+ var caption = table.getElementsByTagName("caption")[0];
+ if (caption) {
+ caption.parentNode.removeChild(caption);
+ }
+ }
+ break;
+ case "f_summary":
+ table.summary = val;
+ break;
+ case "f_width":
+ table.style.width = ("" + val) + params.f_unit;
+ break;
+ case "f_align":
+ table.align = val;
+ break;
+ case "f_spacing":
+ table.cellSpacing = val;
+ break;
+ case "f_padding":
+ table.cellPadding = val;
+ break;
+ case "f_borders":
+ table.border = val;
+ break;
+ case "f_frames":
+ table.frame = val;
+ break;
+ case "f_rules":
+ table.rules = val;
+ break;
+ }
+ }
+ // various workarounds to refresh the table display (Gecko,
+ // what's going on?! do not disappoint me!)
+ dialog.editor.forceRedraw();
+ dialog.editor.focusEditor();
+ dialog.editor.updateToolbar();
+ var save_collapse = table.style.borderCollapse;
+ table.style.borderCollapse = "collapse";
+ table.style.borderCollapse = "separate";
+ table.style.borderCollapse = save_collapse;
+ },
+
+ // this function gets called when the dialog needs to be initialized
+ function (dialog) {
+
+ var f_caption = "";
+ var capel = table.getElementsByTagName("caption")[0];
+ if (capel) {
+ f_caption = capel.innerHTML;
+ }
+ var f_summary = table.summary;
+ var f_width = parseInt(table.style.width);
+ isNaN(f_width) && (f_width = "");
+ var f_unit = /%/.test(table.style.width) ? 'percent' : 'pixels';
+ var f_align = table.align;
+ var f_spacing = table.cellSpacing;
+ var f_padding = table.cellPadding;
+ var f_borders = table.border;
+ var f_frames = table.frame;
+ var f_rules = table.rules;
+
+ function selected(val) {
+ return val ? " selected" : "";
+ };
+
+ // dialog contents
+ dialog.content.style.width = "400px";
+ dialog.content.innerHTML = " \
+<div class='title'\
+ style='background: url(" + dialog.baseURL + dialog.editor.imgURL("table-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n["Table Properties"] + "\
+</div> \
+<table style='width:100%'> \
+ <tr> \
+ <td> \
+ <fieldset><legend>" + i18n["Description"] + "</legend> \
+ <table style='width:100%'> \
+ <tr> \
+ <td class='label'>" + i18n["Caption"] + ":</td> \
+ <td class='value'><input type='text' name='f_caption' value='" + f_caption + "'/></td> \
+ </tr><tr> \
+ <td class='label'>" + i18n["Summary"] + ":</td> \
+ <td class='value'><input type='text' name='f_summary' value='" + f_summary + "'/></td> \
+ </tr> \
+ </table> \
+ </fieldset> \
+ </td> \
+ </tr> \
+ <tr><td id='--HA-layout'></td></tr> \
+ <tr> \
+ <td> \
+ <fieldset><legend>" + i18n["Spacing and padding"] + "</legend> \
+ <table style='width:100%'> \
+"+// <tr> \
+// <td class='label'>" + i18n["Width"] + ":</td> \
+// <td><input type='text' name='f_width' value='" + f_width + "' size='5' /> \
+// <select name='f_unit'> \
+// <option value='%'" + selected(f_unit == "percent") + ">" + i18n["percent"] + "</option> \
+// <option value='px'" + selected(f_unit == "pixels") + ">" + i18n["pixels"] + "</option> \
+// </select> " + i18n["Align"] + ": \
+// <select name='f_align'> \
+// <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
+// <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
+// <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
+// </select> \
+// </td> \
+// </tr> \
+" <tr> \
+ <td class='label'>" + i18n["Spacing"] + ":</td> \
+ <td><input type='text' name='f_spacing' size='5' value='" + f_spacing + "' /> " + i18n["Padding"] + ":\
+ <input type='text' name='f_padding' size='5' value='" + f_padding + "' /> " + i18n["pixels"] + "\
+ </td> \
+ </tr> \
+ </table> \
+ </fieldset> \
+ </td> \
+ </tr> \
+ <tr> \
+ <td> \
+ <fieldset><legend>Frame and borders</legend> \
+ <table width='100%'> \
+ <tr> \
+ <td class='label'>" + i18n["Borders"] + ":</td> \
+ <td><input name='f_borders' type='text' size='5' value='" + f_borders + "' /> " + i18n["pixels"] + "</td> \
+ </tr> \
+ <tr> \
+ <td class='label'>" + i18n["Frames"] + ":</td> \
+ <td> \
+ <select name='f_frames'> \
+ <option value='void'" + selected(f_frames == "void") + ">" + i18n["No sides"] + "</option> \
+ <option value='above'" + selected(f_frames == "above") + ">" + i18n["The top side only"] + "</option> \
+ <option value='below'" + selected(f_frames == "below") + ">" + i18n["The bottom side only"] + "</option> \
+ <option value='hsides'" + selected(f_frames == "hsides") + ">" + i18n["The top and bottom sides only"] + "</option> \
+ <option value='vsides'" + selected(f_frames == "vsides") + ">" + i18n["The right and left sides only"] + "</option> \
+ <option value='lhs'" + selected(f_frames == "lhs") + ">" + i18n["The left-hand side only"] + "</option> \
+ <option value='rhs'" + selected(f_frames == "rhs") + ">" + i18n["The right-hand side only"] + "</option> \
+ <option value='box'" + selected(f_frames == "box") + ">" + i18n["All four sides"] + "</option> \
+ </select> \
+ </td> \
+ </tr> \
+ <tr> \
+ <td class='label'>" + i18n["Rules"] + ":</td> \
+ <td> \
+ <select name='f_rules'> \
+ <option value='none'" + selected(f_rules == "none") + ">" + i18n["No rules"] + "</option> \
+ <option value='rows'" + selected(f_rules == "rows") + ">" + i18n["Rules will appear between rows only"] + "</option> \
+ <option value='cols'" + selected(f_rules == "cols") + ">" + i18n["Rules will appear between columns only"] + "</option> \
+ <option value='all'" + selected(f_rules == "all") + ">" + i18n["Rules will appear between all rows and columns"] + "</option> \
+ </select> \
+ </td> \
+ </tr> \
+ </table> \
+ </fieldset> \
+ </td> \
+ </tr> \
+ <tr> \
+ <td id='--HA-style'></td> \
+ </tr> \
+</table> \
+";
+ var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, table);
+ var p = dialog.doc.getElementById("--HA-style");
+ p.appendChild(st_prop);
+ var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, table);
+ p = dialog.doc.getElementById("--HA-layout");
+ p.appendChild(st_layout);
+ dialog.modal = true;
+ dialog.addButtons("ok", "cancel");
+ dialog.showAtElement(dialog.editor._iframe, "c");
+ });
+};
+
+// this function requires the file PopupDiv/PopupWin to be loaded from browser
+TableOperations.prototype.dialogRowCellProperties = function(cell) {
+ var i18n = TableOperations.I18N;
+ // retrieve existing values
+ var element = this.getClosest(cell ? "td" : "tr");
+ var table = this.getClosest("table");
+ // this.editor.selectNodeContents(element);
+ // this.editor.updateToolbar();
+
+ var dialog = new PopupWin(this.editor, i18n[cell ? "Cell Properties" : "Row Properties"], function(dialog, params) {
+ TableOperations.processStyle(params, element);
+ for (var i in params) {
+ var val = params[i];
+ switch (i) {
+ case "f_align":
+ element.align = val;
+ break;
+ case "f_char":
+ element.ch = val;
+ break;
+ case "f_valign":
+ element.vAlign = val;
+ break;
+ }
+ }
+ // various workarounds to refresh the table display (Gecko,
+ // what's going on?! do not disappoint me!)
+ dialog.editor.forceRedraw();
+ dialog.editor.focusEditor();
+ dialog.editor.updateToolbar();
+ var save_collapse = table.style.borderCollapse;
+ table.style.borderCollapse = "collapse";
+ table.style.borderCollapse = "separate";
+ table.style.borderCollapse = save_collapse;
+ },
+
+ // this function gets called when the dialog needs to be initialized
+ function (dialog) {
+
+ var f_align = element.align;
+ var f_valign = element.vAlign;
+ var f_char = element.ch;
+
+ function selected(val) {
+ return val ? " selected" : "";
+ };
+
+ // dialog contents
+ dialog.content.style.width = "400px";
+ dialog.content.innerHTML = " \
+<div class='title'\
+ style='background: url(" + dialog.baseURL + dialog.editor.imgURL(cell ? "cell-prop.gif" : "row-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n[cell ? "Cell Properties" : "Row Properties"] + "</div> \
+<table style='width:100%'> \
+ <tr> \
+ <td id='--HA-layout'> \
+"+// <fieldset><legend>" + i18n["Layout"] + "</legend> \
+// <table style='width:100%'> \
+// <tr> \
+// <td class='label'>" + i18n["Align"] + ":</td> \
+// <td> \
+// <select name='f_align'> \
+// <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
+// <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
+// <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
+// <option value='char'" + selected(f_align == "char") + ">" + i18n["Char"] + "</option> \
+// </select> \
+// " + i18n["Char"] + ": \
+// <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \
+// </td> \
+// </tr><tr> \
+// <td class='label'>" + i18n["Vertical align"] + ":</td> \
+// <td> \
+// <select name='f_valign'> \
+// <option value='top'" + selected(f_valign == "top") + ">" + i18n["Top"] + "</option> \
+// <option value='middle'" + selected(f_valign == "middle") + ">" + i18n["Middle"] + "</option> \
+// <option value='bottom'" + selected(f_valign == "bottom") + ">" + i18n["Bottom"] + "</option> \
+// <option value='baseline'" + selected(f_valign == "baseline") + ">" + i18n["Baseline"] + "</option> \
+// </select> \
+// </td> \
+// </tr> \
+// </table> \
+// </fieldset> \
+" </td> \
+ </tr> \
+ <tr> \
+ <td id='--HA-style'></td> \
+ </tr> \
+</table> \
+";
+ var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element);
+ var p = dialog.doc.getElementById("--HA-style");
+ p.appendChild(st_prop);
+ var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element);
+ p = dialog.doc.getElementById("--HA-layout");
+ p.appendChild(st_layout);
+ dialog.modal = true;
+ dialog.addButtons("ok", "cancel");
+ dialog.showAtElement(dialog.editor._iframe, "c");
+ });
+};
+
+// this function gets called when some button from the TableOperations toolbar
+// was pressed.
+TableOperations.prototype.buttonPress = function(editor, button_id) {
+ this.editor = editor;
+ var mozbr = HTMLArea.is_gecko ? "<br />" : "";
+ var i18n = TableOperations.I18N;
+
+ // helper function that clears the content in a table row
+ function clearRow(tr) {
+ var tds = tr.getElementsByTagName("td");
+ for (var i = tds.length; --i >= 0;) {
+ var td = tds[i];
+ td.rowSpan = 1;
+ td.innerHTML = mozbr;
+ }
+ };
+
+ function splitRow(td) {
+ var n = parseInt("" + td.rowSpan);
+ var nc = parseInt("" + td.colSpan);
+ td.rowSpan = 1;
+ tr = td.parentNode;
+ var itr = tr.rowIndex;
+ var trs = tr.parentNode.rows;
+ var index = td.cellIndex;
+ while (--n > 0) {
+ tr = trs[++itr];
+ var otd = editor._doc.createElement("td");
+ otd.colSpan = td.colSpan;
+ otd.innerHTML = mozbr;
+ tr.insertBefore(otd, tr.cells[index]);
+ }
+ editor.forceRedraw();
+ editor.updateToolbar();
+ };
+
+ function splitCol(td) {
+ var nc = parseInt("" + td.colSpan);
+ td.colSpan = 1;
+ tr = td.parentNode;
+ var ref = td.nextSibling;
+ while (--nc > 0) {
+ var otd = editor._doc.createElement("td");
+ otd.rowSpan = td.rowSpan;
+ otd.innerHTML = mozbr;
+ tr.insertBefore(otd, ref);
+ }
+ editor.forceRedraw();
+ editor.updateToolbar();
+ };
+
+ function splitCell(td) {
+ var nc = parseInt("" + td.colSpan);
+ splitCol(td);
+ var items = td.parentNode.cells;
+ var index = td.cellIndex;
+ while (nc-- > 0) {
+ splitRow(items[index++]);
+ }
+ };
+
+ function selectNextNode(el) {
+ var node = el.nextSibling;
+ while (node && node.nodeType != 1) {
+ node = node.nextSibling;
+ }
+ if (!node) {
+ node = el.previousSibling;
+ while (node && node.nodeType != 1) {
+ node = node.previousSibling;
+ }
+ }
+ if (!node) {
+ node = el.parentNode;
+ }
+ editor.selectNodeContents(node);
+ };
+
+ switch (button_id) {
+ // ROWS
+
+ case "TO-row-insert-above":
+ case "TO-row-insert-under":
+ var tr = this.getClosest("tr");
+ if (!tr) {
+ break;
+ }
+ var otr = tr.cloneNode(true);
+ clearRow(otr);
+ tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr);
+ editor.forceRedraw();
+ editor.focusEditor();
+ break;
+ case "TO-row-delete":
+ var tr = this.getClosest("tr");
+ if (!tr) {
+ break;
+ }
+ var par = tr.parentNode;
+ if (par.rows.length == 1) {
+ alert(i18n["not-del-last-row"]);
+ break;
+ }
+ // set the caret first to a position that doesn't
+ // disappear.
+ selectNextNode(tr);
+ par.removeChild(tr);
+ editor.forceRedraw();
+ editor.focusEditor();
+ editor.updateToolbar();
+ break;
+ case "TO-row-split":
+ var td = this.getClosest("td");
+ if (!td) {
+ break;
+ }
+ splitRow(td);
+ break;
+
+ // COLUMNS
+
+ case "TO-col-insert-before":
+ case "TO-col-insert-after":
+ var td = this.getClosest("td");
+ if (!td) {
+ break;
+ }
+ var rows = td.parentNode.parentNode.rows;
+ var index = td.cellIndex;
+ for (var i = rows.length; --i >= 0;) {
+ var tr = rows[i];
+ var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)];
+ var otd = editor._doc.createElement("td");
+ otd.innerHTML = mozbr;
+ tr.insertBefore(otd, ref);
+ }
+ editor.focusEditor();
+ break;
+ case "TO-col-split":
+ var td = this.getClosest("td");
+ if (!td) {
+ break;
+ }
+ splitCol(td);
+ break;
+ case "TO-col-delete":
+ var td = this.getClosest("td");
+ if (!td) {
+ break;
+ }
+ var index = td.cellIndex;
+ if (td.parentNode.cells.length == 1) {
+ alert(i18n["not-del-last-col"]);
+ break;
+ }
+ // set the caret first to a position that doesn't disappear
+ selectNextNode(td);
+ var rows = td.parentNode.parentNode.rows;
+ for (var i = rows.length; --i >= 0;) {
+ var tr = rows[i];
+ tr.removeChild(tr.cells[index]);
+ }
+ editor.forceRedraw();
+ editor.focusEditor();
+ editor.updateToolbar();
+ break;
+
+ // CELLS
+
+ case "TO-cell-split":
+ var td = this.getClosest("td");
+ if (!td) {
+ break;
+ }
+ splitCell(td);
+ break;
+ case "TO-cell-insert-before":
+ case "TO-cell-insert-after":
+ var td = this.getClosest("td");
+ if (!td) {
+ break;
+ }
+ var tr = td.parentNode;
+ var otd = editor._doc.createElement("td");
+ otd.innerHTML = mozbr;
+ tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td);
+ editor.forceRedraw();
+ editor.focusEditor();
+ break;
+ case "TO-cell-delete":
+ var td = this.getClosest("td");
+ if (!td) {
+ break;
+ }
+ if (td.parentNode.cells.length == 1) {
+ alert(i18n["not-del-last-cell"]);
+ break;
+ }
+ // set the caret first to a position that doesn't disappear
+ selectNextNode(td);
+ td.parentNode.removeChild(td);
+ editor.forceRedraw();
+ editor.updateToolbar();
+ break;
+ case "TO-cell-merge":
+ // !! FIXME: Mozilla specific !!
+ var sel = editor._getSelection();
+ var range, i = 0;
+ var rows = [];
+ var row = null;
+ var cells = null;
+ if (!HTMLArea.is_ie) {
+ try {
+ while (range = sel.getRangeAt(i++)) {
+ var td = range.startContainer.childNodes[range.startOffset];
+ if (td.parentNode != row) {
+ row = td.parentNode;
+ (cells) && rows.push(cells);
+ cells = [];
+ }
+ cells.push(td);
+ }
+ } catch(e) {/* finished walking through selection */}
+ rows.push(cells);
+ } else {
+ // Internet Explorer "browser"
+ var td = this.getClosest("td");
+ if (!td) {
+ alert(i18n["Please click into some cell"]);
+ break;
+ }
+ var tr = td.parentElement;
+ var no_cols = prompt(i18n["How many columns would you like to merge?"], 2);
+ if (!no_cols) {
+ // cancelled
+ break;
+ }
+ var no_rows = prompt(i18n["How many rows would you like to merge?"], 2);
+ if (!no_rows) {
+ // cancelled
+ break;
+ }
+ var cell_index = td.cellIndex;
+ while (no_rows-- > 0) {
+ td = tr.cells[cell_index];
+ cells = [td];
+ for (var i = 1; i < no_cols; ++i) {
+ td = td.nextSibling;
+ if (!td) {
+ break;
+ }
+ cells.push(td);
+ }
+ rows.push(cells);
+ tr = tr.nextSibling;
+ if (!tr) {
+ break;
+ }
+ }
+ }
+ var HTML = "";
+ for (i = 0; i < rows.length; ++i) {
+ // i && (HTML += "<br />");
+ var cells = rows[i];
+ for (var j = 0; j < cells.length; ++j) {
+ // j && (HTML += " ");
+ var cell = cells[j];
+ HTML += cell.innerHTML;
+ (i || j) && (cell.parentNode.removeChild(cell));
+ }
+ }
+ var td = rows[0][0];
+ td.innerHTML = HTML;
+ td.rowSpan = rows.length;
+ td.colSpan = rows[0].length;
+ editor.selectNodeContents(td);
+ editor.forceRedraw();
+ editor.focusEditor();
+ break;
+
+ // PROPERTIES
+
+ case "TO-table-prop":
+ this.dialogTableProperties();
+ break;
+
+ case "TO-row-prop":
+ this.dialogRowCellProperties(false);
+ break;
+
+ case "TO-cell-prop":
+ this.dialogRowCellProperties(true);
+ break;
+
+ default:
+ alert("Button [" + button_id + "] not yet implemented");
+ }
+};
+
+// the list of buttons added by this plugin
+TableOperations.btnList = [
+ // table properties button
+ ["table-prop", "table"],
+ null, // separator
+
+ // ROWS
+ ["row-prop", "tr"],
+ ["row-insert-above", "tr"],
+ ["row-insert-under", "tr"],
+ ["row-delete", "tr"],
+ ["row-split", "td[rowSpan!=1]"],
+ null,
+
+ // COLS
+ ["col-insert-before", "td"],
+ ["col-insert-after", "td"],
+ ["col-delete", "td"],
+ ["col-split", "td[colSpan!=1]"],
+ null,
+
+ // CELLS
+ ["cell-prop", "td"],
+ ["cell-insert-before", "td"],
+ ["cell-insert-after", "td"],
+ ["cell-delete", "td"],
+ ["cell-merge", "tr"],
+ ["cell-split", "td[colSpan!=1,rowSpan!=1]"]
+ ];
+
+
+
+//// GENERIC CODE [style of any element; this should be moved into a separate
+//// file as it'll be very useful]
+//// BEGIN GENERIC CODE -----------------------------------------------------
+
+TableOperations.getLength = function(value) {
+ var len = parseInt(value);
+ if (isNaN(len)) {
+ len = "";
+ }
+ return len;
+};
+
+// Applies the style found in "params" to the given element.
+TableOperations.processStyle = function(params, element) {
+ var style = element.style;
+ for (var i in params) {
+ var val = params[i];
+ switch (i) {
+ case "f_st_backgroundColor":
+ style.backgroundColor = val;
+ break;
+ case "f_st_color":
+ style.color = val;
+ break;
+ case "f_st_backgroundImage":
+ if (/\S/.test(val)) {
+ style.backgroundImage = "url(" + val + ")";
+ } else {
+ style.backgroundImage = "none";
+ }
+ break;
+ case "f_st_borderWidth":
+ style.borderWidth = val;
+ break;
+ case "f_st_borderStyle":
+ style.borderStyle = val;
+ break;
+ case "f_st_borderColor":
+ style.borderColor = val;
+ break;
+ case "f_st_borderCollapse":
+ style.borderCollapse = val ? "collapse" : "";
+ break;
+ case "f_st_width":
+ if (/\S/.test(val)) {
+ style.width = val + params["f_st_widthUnit"];
+ } else {
+ style.width = "";
+ }
+ break;
+ case "f_st_height":
+ if (/\S/.test(val)) {
+ style.height = val + params["f_st_heightUnit"];
+ } else {
+ style.height = "";
+ }
+ break;
+ case "f_st_textAlign":
+ if (val == "char") {
+ var ch = params["f_st_textAlignChar"];
+ if (ch == '"') {
+ ch = '\\"';
+ }
+ style.textAlign = '"' + ch + '"';
+ } else {
+ style.textAlign = val;
+ }
+ break;
+ case "f_st_verticalAlign":
+ style.verticalAlign = val;
+ break;
+ case "f_st_float":
+ style.cssFloat = val;
+ break;
+// case "f_st_margin":
+// style.margin = val + "px";
+// break;
+// case "f_st_padding":
+// style.padding = val + "px";
+// break;
+ }
+ }
+};
+
+// Returns an HTML element for a widget that allows color selection. That is,
+// a button that contains the given color, if any, and when pressed will popup
+// the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
+// to select some color. If a color is selected, an input field with the name
+// "f_st_"+name will be updated with the color value in #123456 format.
+TableOperations.createColorButton = function(doc, editor, color, name) {
+ if (!color) {
+ color = "";
+ } else if (!/#/.test(color)) {
+ color = HTMLArea._colorToRgb(color);
+ }
+
+ var df = doc.createElement("span");
+ var field = doc.createElement("input");
+ field.type = "hidden";
+ df.appendChild(field);
+ field.name = "f_st_" + name;
+ field.value = color;
+ var button = doc.createElement("span");
+ button.className = "buttonColor";
+ df.appendChild(button);
+ var span = doc.createElement("span");
+ span.className = "chooser";
+ // span.innerHTML = " ";
+ span.style.backgroundColor = color;
+ button.appendChild(span);
+ button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }};
+ button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }};
+ span.onclick = function() {
+ if (this.parentNode.disabled) {
+ return false;
+ }
+ editor._popupDialog("select_color.html", function(color) {
+ if (color) {
+ span.style.backgroundColor = "#" + color;
+ field.value = "#" + color;
+ }
+ }, color);
+ };
+ var span2 = doc.createElement("span");
+ span2.innerHTML = "×";
+ span2.className = "nocolor";
+ span2.title = TableOperations.I18N["Unset color"];
+ button.appendChild(span2);
+ span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }};
+ span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }};
+ span2.onclick = function() {
+ span.style.backgroundColor = "";
+ field.value = "";
+ };
+ return df;
+};
+
+TableOperations.createStyleLayoutFieldset = function(doc, editor, el) {
+ var i18n = TableOperations.I18N;
+ var fieldset = doc.createElement("fieldset");
+ var legend = doc.createElement("legend");
+ fieldset.appendChild(legend);
+ legend.innerHTML = i18n["Layout"];
+ var table = doc.createElement("table");
+ fieldset.appendChild(table);
+ table.style.width = "100%";
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+
+ var tagname = el.tagName.toLowerCase();
+ var tr, td, input, select, option, options, i;
+
+ if (tagname != "td" && tagname != "tr" && tagname != "th") {
+ tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ td = doc.createElement("td");
+ td.className = "label";
+ tr.appendChild(td);
+ td.innerHTML = i18n["Float"] + ":";
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ select = doc.createElement("select");
+ td.appendChild(select);
+ select.name = "f_st_float";
+ options = ["None", "Left", "Right"];
+ for (i in options) {
+ var Val = options[i];
+ var val = options[i].toLowerCase();
+ option = doc.createElement("option");
+ option.innerHTML = i18n[Val];
+ option.value = val;
+ option.selected = (("" + el.style.cssFloat).toLowerCase() == val);
+ select.appendChild(option);
+ }
+ }
+
+ tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ td = doc.createElement("td");
+ td.className = "label";
+ tr.appendChild(td);
+ td.innerHTML = i18n["Width"] + ":";
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ input = doc.createElement("input");
+ input.type = "text";
+ input.value = TableOperations.getLength(el.style.width);
+ input.size = "5";
+ input.name = "f_st_width";
+ input.style.marginRight = "0.5em";
+ td.appendChild(input);
+ select = doc.createElement("select");
+ select.name = "f_st_widthUnit";
+ option = doc.createElement("option");
+ option.innerHTML = i18n["percent"];
+ option.value = "%";
+ option.selected = /%/.test(el.style.width);
+ select.appendChild(option);
+ option = doc.createElement("option");
+ option.innerHTML = i18n["pixels"];
+ option.value = "px";
+ option.selected = /px/.test(el.style.width);
+ select.appendChild(option);
+ td.appendChild(select);
+
+ select.style.marginRight = "0.5em";
+ td.appendChild(doc.createTextNode(i18n["Text align"] + ":"));
+ select = doc.createElement("select");
+ select.style.marginLeft = select.style.marginRight = "0.5em";
+ td.appendChild(select);
+ select.name = "f_st_textAlign";
+ options = ["Left", "Center", "Right", "Justify"];
+ if (tagname == "td") {
+ options.push("Char");
+ }
+ input = doc.createElement("input");
+ input.name = "f_st_textAlignChar";
+ input.size = "1";
+ input.style.fontFamily = "monospace";
+ td.appendChild(input);
+ for (i in options) {
+ var Val = options[i];
+ var val = Val.toLowerCase();
+ option = doc.createElement("option");
+ option.value = val;
+ option.innerHTML = i18n[Val];
+ option.selected = (el.style.textAlign.toLowerCase() == val);
+ select.appendChild(option);
+ }
+ function setCharVisibility(value) {
+ input.style.visibility = value ? "visible" : "hidden";
+ if (value) {
+ input.focus();
+ input.select();
+ }
+ };
+ select.onchange = function() { setCharVisibility(this.value == "char"); };
+ setCharVisibility(select.value == "char");
+
+ tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ td = doc.createElement("td");
+ td.className = "label";
+ tr.appendChild(td);
+ td.innerHTML = i18n["Height"] + ":";
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ input = doc.createElement("input");
+ input.type = "text";
+ input.value = TableOperations.getLength(el.style.height);
+ input.size = "5";
+ input.name = "f_st_height";
+ input.style.marginRight = "0.5em";
+ td.appendChild(input);
+ select = doc.createElement("select");
+ select.name = "f_st_heightUnit";
+ option = doc.createElement("option");
+ option.innerHTML = i18n["percent"];
+ option.value = "%";
+ option.selected = /%/.test(el.style.height);
+ select.appendChild(option);
+ option = doc.createElement("option");
+ option.innerHTML = i18n["pixels"];
+ option.value = "px";
+ option.selected = /px/.test(el.style.height);
+ select.appendChild(option);
+ td.appendChild(select);
+
+ select.style.marginRight = "0.5em";
+ td.appendChild(doc.createTextNode(i18n["Vertical align"] + ":"));
+ select = doc.createElement("select");
+ select.name = "f_st_verticalAlign";
+ select.style.marginLeft = "0.5em";
+ td.appendChild(select);
+ options = ["Top", "Middle", "Bottom", "Baseline"];
+ for (i in options) {
+ var Val = options[i];
+ var val = Val.toLowerCase();
+ option = doc.createElement("option");
+ option.value = val;
+ option.innerHTML = i18n[Val];
+ option.selected = (el.style.verticalAlign.toLowerCase() == val);
+ select.appendChild(option);
+ }
+
+ return fieldset;
+};
+
+// Returns an HTML element containing the style attributes for the given
+// element. This can be easily embedded into any dialog; the functionality is
+// also provided.
+TableOperations.createStyleFieldset = function(doc, editor, el) {
+ var i18n = TableOperations.I18N;
+ var fieldset = doc.createElement("fieldset");
+ var legend = doc.createElement("legend");
+ fieldset.appendChild(legend);
+ legend.innerHTML = i18n["CSS Style"];
+ var table = doc.createElement("table");
+ fieldset.appendChild(table);
+ table.style.width = "100%";
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+
+ var tr, td, input, select, option, options, i;
+
+ tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ td.className = "label";
+ td.innerHTML = i18n["Background"] + ":";
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor");
+ df.firstChild.nextSibling.style.marginRight = "0.5em";
+ td.appendChild(df);
+ td.appendChild(doc.createTextNode(i18n["Image URL"] + ": "));
+ input = doc.createElement("input");
+ input.type = "text";
+ input.name = "f_st_backgroundImage";
+ if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) {
+ input.value = RegExp.$1;
+ }
+ // input.style.width = "100%";
+ td.appendChild(input);
+
+ tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ td.className = "label";
+ td.innerHTML = i18n["FG Color"] + ":";
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color"));
+
+ // for better alignment we include an invisible field.
+ input = doc.createElement("input");
+ input.style.visibility = "hidden";
+ input.type = "text";
+ td.appendChild(input);
+
+ tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ td.className = "label";
+ td.innerHTML = i18n["Border"] + ":";
+ td = doc.createElement("td");
+ tr.appendChild(td);
+
+ var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor");
+ var btn = colorButton.firstChild.nextSibling;
+ td.appendChild(colorButton);
+ // borderFields.push(btn);
+ btn.style.marginRight = "0.5em";
+
+ select = doc.createElement("select");
+ var borderFields = [];
+ td.appendChild(select);
+ select.name = "f_st_borderStyle";
+ options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"];
+ var currentBorderStyle = el.style.borderStyle;
+ // Gecko reports "solid solid solid solid" for "border-style: solid".
+ // That is, "top right bottom left" -- we only consider the first
+ // value.
+ (currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1);
+ for (i in options) {
+ var val = options[i];
+ option = doc.createElement("option");
+ option.value = val;
+ option.innerHTML = val;
+ (val == currentBorderStyle) && (option.selected = true);
+ select.appendChild(option);
+ }
+ select.style.marginRight = "0.5em";
+ function setBorderFieldsStatus(value) {
+ for (i in borderFields) {
+ var el = borderFields[i];
+ el.style.visibility = value ? "hidden" : "visible";
+ if (!value && (el.tagName.toLowerCase() == "input")) {
+ el.focus();
+ el.select();
+ }
+ }
+ };
+ select.onchange = function() { setBorderFieldsStatus(this.value == "none"); };
+
+ input = doc.createElement("input");
+ borderFields.push(input);
+ input.type = "text";
+ input.name = "f_st_borderWidth";
+ input.value = TableOperations.getLength(el.style.borderWidth);
+ input.size = "5";
+ td.appendChild(input);
+ input.style.marginRight = "0.5em";
+ var span = doc.createElement("span");
+ span.innerHTML = i18n["pixels"];
+ td.appendChild(span);
+ borderFields.push(span);
+
+ setBorderFieldsStatus(select.value == "none");
+
+ if (el.tagName.toLowerCase() == "table") {
+ // the border-collapse style is only for tables
+ tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ td = doc.createElement("td");
+ td.className = "label";
+ tr.appendChild(td);
+ input = doc.createElement("input");
+ input.type = "checkbox";
+ input.name = "f_st_borderCollapse";
+ input.id = "f_st_borderCollapse";
+ var val = (/collapse/i.test(el.style.borderCollapse));
+ input.checked = val ? 1 : 0;
+ td.appendChild(input);
+
+ td = doc.createElement("td");
+ tr.appendChild(td);
+ var label = doc.createElement("label");
+ label.htmlFor = "f_st_borderCollapse";
+ label.innerHTML = i18n["Collapsed borders"];
+ td.appendChild(label);
+ }
+
+// tr = doc.createElement("tr");
+// tbody.appendChild(tr);
+// td = doc.createElement("td");
+// td.className = "label";
+// tr.appendChild(td);
+// td.innerHTML = i18n["Margin"] + ":";
+// td = doc.createElement("td");
+// tr.appendChild(td);
+// input = doc.createElement("input");
+// input.type = "text";
+// input.size = "5";
+// input.name = "f_st_margin";
+// td.appendChild(input);
+// input.style.marginRight = "0.5em";
+// td.appendChild(doc.createTextNode(i18n["Padding"] + ":"));
+
+// input = doc.createElement("input");
+// input.type = "text";
+// input.size = "5";
+// input.name = "f_st_padding";
+// td.appendChild(input);
+// input.style.marginLeft = "0.5em";
+// input.style.marginRight = "0.5em";
+// td.appendChild(doc.createTextNode(i18n["pixels"]));
+
+ return fieldset;
+};
+
+//// END GENERIC CODE -------------------------------------------------------
--- /dev/null
+/** This file is derived from PopupDiv, developed by Mihai Bazon for
+ * SamWare.net. Modifications were needed to make it usable in HTMLArea.
+ * HTMLArea is a free WYSIWYG online HTML editor from InteractiveTools.com.
+ *
+ * This file does not function standalone. It is dependent of global functions
+ * defined in HTMLArea-3.0 (htmlarea.js).
+ *
+ * Please see file htmlarea.js for further details.
+ **/
+
+var is_ie = ( (navigator.userAgent.toLowerCase().indexOf("msie") != -1) &&
+ (navigator.userAgent.toLowerCase().indexOf("opera") == -1) );
+var is_compat = (document.compatMode == "BackCompat");
+
+function PopupDiv(editor, titleText, handler, initFunction) {
+ var self = this;
+
+ this.editor = editor;
+ this.doc = editor._mdoc;
+ this.handler = handler;
+
+ var el = this.doc.createElement("div");
+ el.className = "content";
+
+ var popup = this.doc.createElement("div");
+ popup.className = "dialog popupdiv";
+ this.element = popup;
+ var s = popup.style;
+ s.position = "absolute";
+ s.left = "0px";
+ s.top = "0px";
+
+ var title = this.doc.createElement("div");
+ title.className = "title";
+ this.title = title;
+ popup.appendChild(title);
+
+ HTMLArea._addEvent(title, "mousedown", function(ev) {
+ self._dragStart(is_ie ? window.event : ev);
+ });
+
+ var button = this.doc.createElement("div");
+ button.className = "button";
+ title.appendChild(button);
+ button.innerHTML = "×";
+ title.appendChild(this.doc.createTextNode(titleText));
+ this.titleText = titleText;
+
+ button.onmouseover = function() {
+ this.className += " button-hilite";
+ };
+ button.onmouseout = function() {
+ this.className = this.className.replace(/\s*button-hilite\s*/g, " ");
+ };
+ button.onclick = function() {
+ this.className = this.className.replace(/\s*button-hilite\s*/g, " ");
+ self.close();
+ };
+
+ popup.appendChild(el);
+ this.content = el;
+
+ this.doc.body.appendChild(popup);
+
+ this.dragging = false;
+ this.onShow = null;
+ this.onClose = null;
+ this.modal = false;
+
+ initFunction(this);
+};
+
+PopupDiv.currentPopup = null;
+
+PopupDiv.prototype.showAtElement = function(el, mode) {
+ this.defaultSize();
+ var pos, ew, eh;
+ var popup = this.element;
+ popup.style.display = "block";
+ var w = popup.offsetWidth;
+ var h = popup.offsetHeight;
+ popup.style.display = "none";
+ if (el != window) {
+ pos = PopupDiv.getAbsolutePos(el);
+ ew = el.offsetWidth;
+ eh = el.offsetHeight;
+ } else {
+ pos = {x:0, y:0};
+ var size = PopupDiv.getWindowSize();
+ ew = size.x;
+ eh = size.y;
+ }
+ var FX = false, FY = false;
+ if (mode.indexOf("l") != -1) {
+ pos.x -= w;
+ FX = true;
+ }
+ if (mode.indexOf("r") != -1) {
+ pos.x += ew;
+ FX = true;
+ }
+ if (mode.indexOf("t") != -1) {
+ pos.y -= h;
+ FY = true;
+ }
+ if (mode.indexOf("b") != -1) {
+ pos.y += eh;
+ FY = true;
+ }
+ if (mode.indexOf("c") != -1) {
+ FX || (pos.x += Math.round((ew - w) / 2));
+ FY || (pos.y += Math.round((eh - h) / 2));
+ }
+ this.showAt(pos.x, pos.y);
+};
+
+PopupDiv.prototype.defaultSize = function() {
+ var s = this.element.style;
+ var cs = this.element.currentStyle;
+ var addX = (is_ie && is_compat) ? (parseInt(cs.borderLeftWidth) +
+ parseInt(cs.borderRightWidth) +
+ parseInt(cs.paddingLeft) +
+ parseInt(cs.paddingRight)) : 0;
+ var addY = (is_ie && is_compat) ? (parseInt(cs.borderTopWidth) +
+ parseInt(cs.borderBottomWidth) +
+ parseInt(cs.paddingTop) +
+ parseInt(cs.paddingBottom)) : 0;
+ s.display = "block";
+ s.width = (this.content.offsetWidth + addX) + "px";
+ s.height = (this.content.offsetHeight + this.title.offsetHeight) + "px";
+ s.display = "none";
+};
+
+PopupDiv.prototype.showAt = function(x, y) {
+ this.defaultSize();
+ var s = this.element.style;
+ s.display = "block";
+ s.left = x + "px";
+ s.top = y + "px";
+ this.hideShowCovered();
+
+ PopupDiv.currentPopup = this;
+ HTMLArea._addEvents(this.doc.body, ["mousedown", "click"], PopupDiv.checkPopup);
+ HTMLArea._addEvents(this.editor._doc.body, ["mousedown", "click"], PopupDiv.checkPopup);
+ if (is_ie && this.modal) {
+ this.doc.body.setCapture(false);
+ this.doc.body.onlosecapture = function() {
+ (PopupDiv.currentPopup) && (this.doc.body.setCapture(false));
+ };
+ }
+ window.event && HTMLArea._stopEvent(window.event);
+
+ if (typeof this.onShow == "function") {
+ this.onShow();
+ } else if (typeof this.onShow == "string") {
+ eval(this.onShow);
+ }
+
+ var field = this.element.getElementsByTagName("input")[0];
+ if (!field) {
+ field = this.element.getElementsByTagName("select")[0];
+ }
+ if (!field) {
+ field = this.element.getElementsByTagName("textarea")[0];
+ }
+ if (field) {
+ field.focus();
+ }
+};
+
+PopupDiv.prototype.close = function() {
+ this.element.style.display = "none";
+ PopupDiv.currentPopup = null;
+ this.hideShowCovered();
+ HTMLArea._removeEvents(this.doc.body, ["mousedown", "click"], PopupDiv.checkPopup);
+ HTMLArea._removeEvents(this.editor._doc.body, ["mousedown", "click"], PopupDiv.checkPopup);
+ is_ie && this.modal && this.doc.body.releaseCapture();
+ if (typeof this.onClose == "function") {
+ this.onClose();
+ } else if (typeof this.onClose == "string") {
+ eval(this.onClose);
+ }
+ this.element.parentNode.removeChild(this.element);
+};
+
+PopupDiv.prototype.getForm = function() {
+ var forms = this.content.getElementsByTagName("form");
+ return (forms.length > 0) ? forms[0] : null;
+};
+
+PopupDiv.prototype.callHandler = function() {
+ var tags = ["input", "textarea", "select"];
+ var params = new Object();
+ for (var ti in tags) {
+ var tag = tags[ti];
+ var els = this.content.getElementsByTagName(tag);
+ for (var j = 0; j < els.length; ++j) {
+ var el = els[j];
+ params[el.name] = el.value;
+ }
+ }
+ this.handler(this, params);
+ return false;
+};
+
+PopupDiv.getAbsolutePos = function(el) {
+ var r = { x: el.offsetLeft, y: el.offsetTop };
+ if (el.offsetParent) {
+ var tmp = PopupDiv.getAbsolutePos(el.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+};
+
+PopupDiv.getWindowSize = function() {
+ if (window.innerHeight) {
+ return { y: window.innerHeight, x: window.innerWidth };
+ }
+ if (this.doc.body.clientHeight) {
+ return { y: this.doc.body.clientHeight, x: this.doc.body.clientWidth };
+ }
+ return { y: this.doc.documentElement.clientHeight, x: this.doc.documentElement.clientWidth };
+};
+
+PopupDiv.prototype.hideShowCovered = function () {
+ var self = this;
+ function isContained(el) {
+ while (el) {
+ if (el == self.element) {
+ return true;
+ }
+ el = el.parentNode;
+ }
+ return false;
+ };
+ var tags = new Array("applet", "select");
+ var el = this.element;
+
+ var p = PopupDiv.getAbsolutePos(el);
+ var EX1 = p.x;
+ var EX2 = el.offsetWidth + EX1;
+ var EY1 = p.y;
+ var EY2 = el.offsetHeight + EY1;
+
+ if (el.style.display == "none") {
+ EX1 = EX2 = EY1 = EY2 = 0;
+ }
+
+ for (var k = tags.length; k > 0; ) {
+ var ar = this.doc.getElementsByTagName(tags[--k]);
+ var cc = null;
+
+ for (var i = ar.length; i > 0;) {
+ cc = ar[--i];
+ if (isContained(cc)) {
+ cc.style.visibility = "visible";
+ continue;
+ }
+
+ p = PopupDiv.getAbsolutePos(cc);
+ var CX1 = p.x;
+ var CX2 = cc.offsetWidth + CX1;
+ var CY1 = p.y;
+ var CY2 = cc.offsetHeight + CY1;
+
+ if ((CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
+ cc.style.visibility = "visible";
+ } else {
+ cc.style.visibility = "hidden";
+ }
+ }
+ }
+};
+
+PopupDiv.prototype._dragStart = function (ev) {
+ if (this.dragging) {
+ return false;
+ }
+ this.dragging = true;
+ PopupDiv.currentPopup = this;
+ var posX = ev.clientX;
+ var posY = ev.clientY;
+ if (is_ie) {
+ posY += this.doc.body.scrollTop;
+ posX += this.doc.body.scrollLeft;
+ } else {
+ posY += window.scrollY;
+ posX += window.scrollX;
+ }
+ var st = this.element.style;
+ this.xOffs = posX - parseInt(st.left);
+ this.yOffs = posY - parseInt(st.top);
+ HTMLArea._addEvent(this.doc, "mousemove", PopupDiv.dragIt);
+ HTMLArea._addEvent(this.doc, "mouseover", HTMLArea._stopEvent);
+ HTMLArea._addEvent(this.doc, "mouseup", PopupDiv.dragEnd);
+ HTMLArea._stopEvent(ev);
+};
+
+PopupDiv.dragIt = function (ev) {
+ var popup = PopupDiv.currentPopup;
+ if (!(popup && popup.dragging)) {
+ return false;
+ }
+ is_ie && (ev = window.event);
+ var posX = ev.clientX;
+ var posY = ev.clientY;
+ if (is_ie) {
+ posY += this.doc.body.scrollTop;
+ posX += this.doc.body.scrollLeft;
+ } else {
+ posY += window.scrollY;
+ posX += window.scrollX;
+ }
+ popup.hideShowCovered();
+ var st = popup.element.style;
+ st.left = (posX - popup.xOffs) + "px";
+ st.top = (posY - popup.yOffs) + "px";
+ HTMLArea._stopEvent(ev);
+};
+
+PopupDiv.dragEnd = function () {
+ var popup = PopupDiv.currentPopup;
+ if (!popup) {
+ return false;
+ }
+ popup.dragging = false;
+ HTMLArea._removeEvent(popup.doc, "mouseup", PopupDiv.dragEnd);
+ HTMLArea._removeEvent(popup.doc, "mouseover", HTMLArea._stopEvent);
+ HTMLArea._removeEvent(popup.doc, "mousemove", PopupDiv.dragIt);
+ popup.hideShowCovered();
+};
+
+PopupDiv.checkPopup = function (ev) {
+ is_ie && (ev = window.event);
+ var el = is_ie ? ev.srcElement : ev.target;
+ var cp = PopupDiv.currentPopup;
+ for (; (el != null) && (el != cp.element); el = el.parentNode);
+ if (el == null) {
+ cp.modal || ev.type == "mouseover" || cp.close();
+ HTMLArea._stopEvent(ev);
+ }
+};
+
+PopupDiv.prototype.addButtons = function() {
+ var self = this;
+ var div = this.doc.createElement("div");
+ this.content.appendChild(div);
+ div.className = "buttons";
+ for (var i = 0; i < arguments.length; ++i) {
+ var btn = arguments[i];
+ var button = this.doc.createElement("button");
+ div.appendChild(button);
+ button.innerHTML = HTMLArea.I18N.buttons[btn];
+ switch (btn) {
+ case "ok":
+ button.onclick = function() {
+ self.callHandler();
+ self.close();
+ };
+ break;
+ case "cancel":
+ button.onclick = function() {
+ self.close();
+ };
+ break;
+ }
+ }
+};
--- /dev/null
+<html style="width: 380px; height: 250px;">
+<head><title>About HTMLArea</title>
+<script type="text/javascript" src="popup.js"></script>
+<script type="text/javascript">
+function closeAbout() {
+ __dlg_close(null);
+}
+</script>
+<style>
+ html,body,textarea { font-family: tahoma,verdana,arial; font-size: 11px;
+padding: 0px; margin: 0px; }
+ tt { font-size: 120%; }
+ body { padding: 0px; background: ButtonFace; color: ButtonText; }
+ a:link, a:visited { color: #00f; }
+ a:hover { color: #f00; }
+ a:active { color: #f80; }
+ button { font: 11px tahoma,verdana,sans-serif; }
+</style></head>
+<body onload="__dlg_init()">
+
+<div style="background-color: #fff; color: #000; padding: 3px; border-bottom: 1px solid #000;">
+<div style="font-family: 'arial black',arial,sans-serif; font-size: 28px;
+letter-spacing: -1px;">
+<span style="position: relative; top: -0.2em">H</span><span
+style="position: relative; top: 0.1em">T</span><span
+style="position: relative; top: -0.1em">M</span><span
+style="position: relative; top: 0.2em">L</span> Area
+3.0 <span style="position: relative; top: -0.6em; font-size: 50%; font-weight: normal">[ rev. beta ]</span></div>
+
+<div style="text-align: right; font-size: 90%; margin-bottom: 1em">
+Released on Aug 11, 2003 [21:30] GMT
+</div>
+</div>
+
+<div style="margin: 1em">
+
+<p>A free WYSIWYG editor replacement for <tt><textarea></tt> fields.</p>
+
+<p>For full source code and docs, visit:<br />
+<a href="http://www.interactivetools.com/products/htmlarea/" target="_blank"
+>http://www.interactivetools.com/products/htmlarea/</a></p>
+
+<p>Version 3.0 developed and maintained by <a href="http://students.infoiasi.ro/~mishoo/" target="_blank">mishoo</a>.</p>
+
+<p>© 2002, 2003 <a href="http://interactivetools.com" target="_blank">interactivetools.com</a>, inc. All Rights Reserved.</p>
+
+</div>
+
+<div style="text-align: right; padding: 0px 3px 3px 0px;">
+<button type="button" onclick="closeAbout()">I agree it's cool</button>
+</div>
+
+</body></html>
+
+
--- /dev/null
+<html>\r
+</html>
\ No newline at end of file
--- /dev/null
+<html style="width:300px; Height: 60px;">\r
+ <head>\r
+ <title>Select Phrase</title>\r
+<script language="javascript">\r
+\r
+var myTitle = window.dialogArguments;\r
+document.title = myTitle;\r
+\r
+\r
+function returnSelected() {\r
+ var idx = document.all.textPulldown.selectedIndex;\r
+ var text = document.all.textPulldown[idx].text;\r
+\r
+ window.returnValue = text; // set return value\r
+ window.close(); // close dialog\r
+}\r
+\r
+</script>\r
+</head>\r
+<body bgcolor="#FFFFFF" topmargin=15 leftmargin=0>\r
+\r
+<form method=get onSubmit="Set(document.all.ColorHex.value); return false;">\r
+<div align=center>\r
+\r
+<select name="textPulldown">\r
+<option>The quick brown</option>\r
+<option>fox jumps over</option>\r
+<option>the lazy dog.</option>\r
+</select>\r
+\r
+<input type="button" value=" Go " onClick="returnSelected()">\r
+\r
+</div>\r
+</form>\r
+</body></html>
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+**
+** HTML Text Editing Component for hosting in Web Pages
+** Copyright (C) 2001 Ramesys (Contracting Services) Limited
+**
+** This library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Lesser General Public
+** License as published by the Free Software Foundation; either
+** version 2.1 of the License, or (at your option) any later version.
+**
+** This library is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU LesserGeneral Public License
+** along with this program; if not a copy can be obtained from
+**
+** http://www.gnu.org/copyleft/lesser.html
+**
+** or by writing to:
+**
+** Free Software Foundation, Inc.
+** 59 Temple Place - Suite 330,
+** Boston,
+** MA 02111-1307,
+** USA.
+**
+** Original Developer:
+**
+** Austin David France
+** Ramesys (Contracting Services) Limited
+** Mentor House
+** Ainsworth Street
+** Blackburn
+** Lancashire
+** BB1 6AY
+** United Kingdom
+** email: Austin.France@Ramesys.com
+**
+** Home Page: http://richtext.sourceforge.net/
+** Support: http://richtext.sourceforge.net/
+**
+*******************************************************************************/
+
+BODY { background-color: buttonface; }
+TD, INPUT, SELECT { font-family: "MS Sans Serif"; font-size: xx-small; vertical-align: middle; }
+TABLE.dlg { border:0; }
+.dlg TD { align: left; height: 20; }
+.dlg INPUT { border-size: 2px; }
+INPUT.button { border-top: 1px solid white; border-left: 1px solid white;
+border-bottom: 1px solid black; border-right: 1px solid black;
+font-size: x-small; width: 60; }
+SELECT { height: 75%; }
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html style="height:270px;">
+<head>
+<style type="text/css">
+ BODY {
+ background-color: buttonface;
+ }
+
+</style>
+<script type="text/javascript" src="popup.js"></script>
+<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
+<!--
+function Init() {
+ __dlg_init();
+}
+var chars = ["!",""","#","$","%","&","'","(",")","*","+","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","]","^","_","`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~","€","\83","\84","\85","\86","\87","\88","\\89","\8a","\8b","\8c","‘","’","’","“","”","\95","–","—","\98","\99","\9a","\9b","\9c","\9f","¡","¢","£","£","¤","¥","¦","§","¨","©","ª","«","¬","","®","¯","°","±","²","³","´","µ","¶","·","¸","¹","º","»","¼","½","¾","¿","À","Á","Â","Ã","Ä","Å","Æ","Ç","È","É","Ê","Ë","Ì","Í","Î","Ï","Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×","Ø","Ù","Ú","Û","Ü","Ý","Þ","ß","à","á","â","ã","ä","å","æ","ç","è","é","ê","ë","ì","í","î","ï","ð","ñ","ò","ó","ô","õ","ö","÷","ø","ù","ú","û","ü","ü","ý","þ","ÿ"]
+
+function tab(w,h) {
+ var strtab = ["<TABLE border='1' cellspacing='0' cellpadding='0' align='center' bordercolor='#dcdcdc' bgcolor='#C0C0C0'>"]
+ var k = 0;
+ for(var i = 0; i < w; i++) {
+ strtab[strtab.length] = "<TR>";
+ for(var j = 0; j < h; j++) {
+ strtab[strtab.length] = "<TD width='14' align='center' onClick='getchar(this)' onMouseOver='hover(this,true)' onMouseOut='hover(this,false)'>"+(chars[k]||'')+"</TD>";
+ k++;
+ }
+ strtab[strtab.length]="</TR>";
+ }
+ strtab[strtab.length] = "</TABLE>";
+ return strtab.join("\n");
+}
+
+function hover(obj,val) {
+ if (!obj.innerHTML) {
+ obj.style.cursor = "default";
+ return;
+ }
+ obj.style.border = val ? "1px solid black" : "1px solid #dcdcdc";
+ //obj.style.backgroundColor = val ? "black" : "#C0C0C0"
+ //obj.style.color = val ? "white" : "black";
+}
+function getchar(obj) {
+ if(!obj.innerHTML) return;
+ var sChar = obj.innerHTML || "";
+ __dlg_close(sChar);
+ return false;
+}
+function cancel() {
+ __dlg_close(null);
+ return false;
+}
+//-->
+</SCRIPT>
+<title>Insert Character</title>
+</head>
+<body onload="Init()">
+<table class="dlg" cellpadding="0" cellspacing="2">
+<tr><td><table width="100%"><tr><td nowrap>Choose Character</td><td valign="middle" width="100%"><hr width="100%"></td></tr></table></td></tr>
+<tr>
+<td>
+ <table border="0" align="center" cellpadding="5">
+ <tr valign="top">
+ <td>
+
+ <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
+ <!--
+ document.write(tab(7,32))
+ //-->
+ </SCRIPT>
+
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+<tr><td><table width="100%"><tr><td valign="middle" width="90%"><hr width="100%"></td></tr></table></td></tr>
+<tr><td align="right">
+ <input type="button" value="Close" onclick="cancel()"></td></tr>
+</table>
+</body>
+</body>
+</html>
--- /dev/null
+<!--
+################################################################################
+##
+## HTML Text Editing Component for hosting in Web Pages
+## Copyright (C) 2001 Ramesys (Contracting Services) Limited
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU LesserGeneral Public License
+## along with this program; if not a copy can be obtained from
+##
+## http://www.gnu.org/copyleft/lesser.html
+##
+## or by writing to:
+##
+## Free Software Foundation, Inc.
+## 59 Temple Place - Suite 330,
+## Boston,
+## MA 02111-1307,
+## USA.
+##
+## Original Developer:
+##
+## Austin David France
+## Ramesys (Contracting Services) Limited
+## Mentor House
+## Ainsworth Street
+## Blackburn
+## Lancashire
+## BB1 6AY
+## United Kingdom
+## email: Austin.France@Ramesys.com
+##
+## Home Page: http://richtext.sourceforge.net/
+## Support: http://richtext.sourceforge.net/
+##
+################################################################################
+-->
+<?php
+ include("../../../config.php");
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html style="height:270px;">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+<style type="text/css">
+body {
+background-color: buttonface;
+}
+td.hover {
+background-color : Fuchsia;
+}
+.description {
+font-family: "MS Sans-Serif", sans-serif;
+font-size: x-small;
+}
+.dlg td {
+align: left;
+height: 20;
+}
+.dlg input {
+border-top: 1px solid white;
+border-left: 1px solid white;
+border-bottom: 1px solid black;
+border-right: 1px solid black;
+font-size: xx-small;
+width: 60;
+}
+</style>
+<script type="text/javascript" src="popup.js"></script>
+<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
+<!--
+function Init() {
+ __dlg_init();
+}
+var chars = ["!",""","#","$","%","&","'","(",")","*","+","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","]","^","_","`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~","€","\83","\84","\85","\86","\87","\88","\\89","\8a","\8b","\8c","‘","’","’","“","”","\95","–","—","\98","\99","\9a","\9b","\9c","\9f","¡","¢","£","£","¤","¥","¦","§","¨","©","ª","«","¬","","®","¯","°","±","²","³","´","µ","¶","·","¸","¹","º","»","¼","½","¾","¿","À","Á","Â","Ã","Ä","Å","Æ","Ç","È","É","Ê","Ë","Ì","Í","Î","Ï","Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×","Ø","Ù","Ú","Û","Ü","Ý","Þ","ß","à","á","â","ã","ä","å","æ","ç","è","é","ê","ë","ì","í","î","ï","ð","ñ","ò","ó","ô","õ","ö","÷","ø","ù","ú","û","ü","ü","ý","þ","ÿ"]
+
+function tab(w,h) {
+ var strtab = ["<TABLE border='1' cellspacing='0' cellpadding='0' align='center' bordercolor='#dcdcdc' bgcolor='#C0C0C0'>"]
+ var k = 0;
+ for(var i = 0; i < w; i++) {
+ strtab[strtab.length] = "<TR>";
+ for(var j = 0; j < h; j++) {
+ strtab[strtab.length] = "<TD width='14' align='center' onClick='getchar(this)' onMouseOver='hover(this,true)' onMouseOut='hover(this,false)'>"+(chars[k]||'')+"</TD>";
+ k++;
+ }
+ strtab[strtab.length]="</TR>";
+ }
+ strtab[strtab.length] = "</TABLE>";
+ return strtab.join("\n");
+}
+
+function hover(obj,val) {
+ if (!obj.innerHTML) {
+ obj.style.cursor = "default";
+ return;
+ }
+ obj.style.border = val ? "1px solid black" : "1px solid #dcdcdc";
+ //obj.style.backgroundColor = val ? "black" : "#C0C0C0"
+ //obj.style.color = val ? "white" : "black";
+}
+function getchar(obj) {
+ if(!obj.innerHTML) return;
+ var sChar = obj.innerHTML || "";
+ __dlg_close(sChar);
+ return false;
+}
+function cancel() {
+ __dlg_close(null);
+ return false;
+}
+//-->
+</SCRIPT>
+<title>Insert Character</title>
+</head>
+<body onload="Init()">
+<table class="dlg" cellpadding="0" cellspacing="2">
+<tr><td><table width="100%"><tr><td class="description" nowrap><?php print(get_string("choosechar","htmlarea"));?></td><td valign="middle" width="100%"><hr width="100%"></td></tr></table></td></tr>
+<tr>
+<td>
+ <table border="0" align="center" cellpadding="5">
+ <tr valign="top">
+ <td>
+
+ <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
+ <!--
+ document.write(tab(7,32))
+ //-->
+ </SCRIPT>
+
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+<tr><td><table width="100%"><tr><td valign="middle" width="90%"><hr width="100%"></td></tr></table></td></tr>
+<tr><td align="right">
+ <input type="button" value="<?php print(get_string("close","htmlarea"));?>" onclick="cancel()"></td></tr>
+</table>
+</body>
+</body>
+</html>
--- /dev/null
+<!--
+#################################################################################
+##
+## HTML Text Editing Component for hosting in Web Pages
+## Copyright (C) 2001 Ramesys (Contracting Services) Limited
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU LesserGeneral Public License
+## along with this program; if not a copy can be obtained from
+##
+## http://www.gnu.org/copyleft/lesser.html
+##
+## or by writing to:
+##
+## Free Software Foundation, Inc.
+## 59 Temple Place - Suite 330,
+## Boston,
+## MA 02111-1307,
+## USA.
+##
+## Original Developer:
+##
+## Austin David France
+## Ramesys (Contracting Services) Limited
+## Mentor House
+## Ainsworth Street
+## Blackburn
+## Lancashire
+## BB1 6AY
+## United Kingdom
+## email: Austin.France@Ramesys.com
+##
+## Home Page: http://richtext.sourceforge.net/
+## Support: http://richtext.sourceforge.net/
+##
+#################################################################################
+-->
+<?php
+ include("../../../config.php");
+ $pix = $CFG->wwwroot . "/pix/s";
+?>
+<html style="width: 300px; height: 350px;">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<meta name=vs_targetSchema content="HTML 4.0">
+<meta name="GENERATOR" content="Microsoft Visual Studio 7.0">
+<LINK rel="stylesheet" type="text/css" href="dialog.css">
+<title>Insert Smiley Icon </title>
+<script type="text/javascript" src="popup.js"></script>
+
+<script language="JavaScript" type="text/javascript">
+function Init() {
+ __dlg_init();
+}
+function attr(name, value) {
+ if (!value || value == "") return "";
+ return ' ' + name + '="' + value + '"';
+}
+function insert(img) {
+ if (img) {
+ var src = img;
+ }
+ // pass data back to the calling window
+ var fields = ["f_url"];
+ var param = new Object();
+ for (var i in fields) {
+ var id = fields[i];
+ param[id] = src;
+ }
+ __dlg_close(param);
+ return false;
+};
+
+function cancel() {
+ __dlg_close(null);
+ return false;
+};
+</script>
+</head>
+<body onload="Init()">
+<table class="dlg" cellpadding="0" cellspacing="2" width="100%" height="100%">
+<tr><td><table width="100%"><tr><td nowrap><?php print(get_string("chooseicon","htmlarea"));?></td><td valign="middle" width="100%"><hr width="100%"></td></tr></table></td></tr>
+<tr>
+<td>
+ <table border="0" align="center" cellpadding="5">
+ <tr valign="top">
+ <td>
+ <table border="0" align="center">
+ <tr>
+ <td><img border="0" hspace="10" src="<?php echo $pix ?>/smiley.gif" onclick="insert('<?php echo $pix ?>/smiley.gif')" width="15" height="15"></td>
+ <td>smile</td>
+ <td><FONT FACE=Courier>:-)</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/biggrin.gif" onclick="insert('<?php echo $pix ?>/biggrin.gif')" width="15" height="15"></td>
+ <td>big grin</td>
+ <td><FONT FACE=Courier>:-D</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/wink.gif" onclick="insert('<?php echo $pix ?>/wink.gif')" width="15" height="15"></td>
+ <td>wink</td>
+ <td><FONT FACE=Courier>;-)</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/mixed.gif" onclick="insert('<?php echo $pix ?>/mixed.gif')" width="15" height="15"></td>
+ <td>mixed</td>
+ <td><FONT FACE=Courier>:-/</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/thoughtful.gif" onclick="insert('<?php echo $pix ?>/thoughtful.gif')" width="15" height="15"></td>
+ <td>thoughtful</td>
+ <td><FONT FACE=Courier>V-.</FONT></td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/tongueout.gif" onclick="insert('<?php echo $pix ?>/tongueout.gif')" width="15" height="15"></td>
+ <td>tongue out</td>
+ <td><FONT FACE=Courier>:-P</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/cool.gif" onclick="insert('<?php echo $pix ?>/cool.gif')" width="15" height="15"></td>
+ <td>cool</td>
+ <td><FONT FACE=Courier>B-)</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/approve.gif" onclick="insert('<?php echo $pix ?>/approve.gif')" width="15" height="15"></td>
+ <td>approve</td>
+ <td><FONT FACE=Courier>^-)</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/wideeyes.gif" onclick="insert('<?php echo $pix ?>/wideeyes.gif')" width="15" height="15"></td>
+ <td>wide eyes</td>
+ <td><FONT FACE=Courier>8-)</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/surprise.gif" onclick="insert('<?php echo $pix ?>/surprise.gif')" width="15" height="15"></td>
+ <td>surprise</td>
+ <td><FONT FACE=Courier>8-o</td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ <table border="0" align="center">
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/sad.gif" onclick="insert('<?php echo $pix ?>/sad.gif')" width="15" height="15"></td>
+ <td>sad</td>
+ <td><FONT FACE=Courier>:-(</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/shy.gif" onclick="insert('<?php echo $pix ?>/shy.gif')" width="15" height="15"></td>
+ <td>shy</td>
+ <td><FONT FACE=Courier>8-.</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/blush.gif" onclick="insert('<?php echo $pix ?>/blush.gif')" width="15" height="15"></td>
+ <td>blush</td>
+ <td><FONT FACE=Courier>:-I</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/kiss.gif" onclick="insert('<?php echo $pix ?>/kiss.gif')" width="15" height="15"></td>
+ <td>kisses</td>
+ <td><FONT FACE=Courier>:-X</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/clown.gif" onclick="insert('<?php echo $pix ?>/clown.gif')" width="15" height="15"></td>
+ <td>clown</td>
+ <td><FONT FACE=Courier>:o)</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/blackeye.gif" onclick="insert('<?php echo $pix ?>/blackeye.gif')" width="15" height="15"></td>
+ <td>black eye</td>
+ <td><FONT FACE=Courier>P-|</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/angry.gif" onclick="insert('<?php echo $pix ?>/angry.gif')" width="15" height="15"></td>
+ <td>angry</td>
+ <td><FONT FACE=Courier>8-[</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/dead.gif" onclick="insert('<?php echo $pix ?>/dead.gif')" width="15" height="15"></td>
+ <td>dead</td>
+ <td><FONT FACE=Courier>xx-P</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/sleepy.gif" onclick="insert('<?php echo $pix ?>/sleepy.gif')" width="15" height="15"></td>
+ <td>sleepy</td>
+ <td><FONT FACE=Courier>|-.</td>
+ </tr>
+ <tr>
+ <td><img alt border="0" hspace="10" src="<?php echo $pix ?>/evil.gif" onclick="insert('<?php echo $pix ?>/evil.gif')" width="15" height="15"></td>
+ <td>evil</td>
+ <td><FONT FACE=Courier>}-]</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+
+ </td>
+ </tr>
+<tr><td><table width="100%"><tr><td valign="middle" width="90%"><hr width="100%"></td></tr></table></td></tr>
+<tr><td align="right">
+ <input type="button" value="<?php print(get_string("close","htmlarea"));?>" onclick="cancel()"></td></tr>
+</table>
+</body>
+</html>
--- /dev/null
+<html>\r
+ <head>\r
+ <title>Editor Help</title>\r
+ <style>\r
+ body, td, p, div { font-family: arial; font-size: x-small; }\r
+ </style>\r
+ </head>\r
+<body>\r
+\r
+<h2>Editor Help<hr></h2>\r
+\r
+Todo...\r
+\r
+\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+<html>
+<head><title>Fullscreen Editor</title>
+<style type="text/css">
+@import url(../htmlarea.css);
+html, body { margin: 0px; border: 0px; background-color: buttonface; } </style>
+
+
+<script type="text/javascript" src="../htmlarea.php<?php echo $id ?>"></script>
+<script type="text/javascript" src="../lang/en.js"></script>
+<script type="text/javascript" src="../dialog.js"></script>
+
+<script type="text/javascript">
+// load same scripts that were present in the opener page
+var scripts = opener.document.getElementsByTagName("script");
+var head = document.getElementsByTagName("head")[0];
+for (var i = 0; i < scripts.length; ++i) {
+ var script = scripts[i];
+ if (typeof script.src != "undefined" && /\S/.test(script.src)) {
+ // document.write("<scr" + "ipt type=" + "\"script/javascript\"");
+ // document.write(" src=\"../" + script.src + "\"></scr" + "ipt>");
+ var new_script = document.createElement("script");
+ if (/^http:/i.test(script.src)) {
+ new_script.src = script.src;
+ } else {
+ new_script.src = "../" + script.src;
+ }
+ head.appendChild(new_script);
+ }
+}
+</script>
+
+<script type="text/javascript">
+
+var parent_object = null;
+var editor = null; // to be initialized later [ function init() ]
+
+/* ---------------------------------------------------------------------- *\
+ Function :
+ Description :
+\* ---------------------------------------------------------------------- */
+
+function _CloseOnEsc(ev) {
+ if (document.all) {
+ // IE
+ ev = window.event;
+ }
+ if (ev.keyCode == 27) {
+ // update_parent();
+ window.close();
+ return;
+ }
+}
+
+/* ---------------------------------------------------------------------- *\
+ Function : cloneObject
+ Description : copy an object by value instead of by reference
+ Usage : var newObj = cloneObject(oldObj);
+\* ---------------------------------------------------------------------- */
+
+function cloneObject(obj) {
+ var newObj = new Object;
+
+ // check for array objects
+ if (obj.constructor.toString().indexOf("function Array(") == 1) {
+ newObj = obj.constructor();
+ }
+
+ // check for function objects (as usual, IE is fucked up)
+ if (obj.constructor.toString().indexOf("function Function(") == 1) {
+ newObj = obj; // just copy reference to it
+ } else for (var n in obj) {
+ var node = obj[n];
+ if (typeof node == 'object') { newObj[n] = cloneObject(node); }
+ else { newObj[n] = node; }
+ }
+
+ return newObj;
+}
+
+/* ---------------------------------------------------------------------- *\
+ Function : resize_editor
+ Description : resize the editor when the user resizes the popup
+\* ---------------------------------------------------------------------- */
+
+function resize_editor() { // resize editor to fix window
+ var newHeight;
+ if (document.all) {
+ // IE
+ newHeight = document.body.offsetHeight - editor._toolbar.offsetHeight;
+ if (newHeight < 0) { newHeight = 0; }
+ } else {
+ // Gecko
+ newHeight = window.innerHeight - editor._toolbar.offsetHeight;
+ }
+ if (editor.config.statusBar) {
+ newHeight -= editor._statusBar.offsetHeight;
+ }
+ editor._textArea.style.height = editor._iframe.style.height = newHeight + "px";
+}
+
+/* ---------------------------------------------------------------------- *\
+ Function : init
+ Description : run this code on page load
+\* ---------------------------------------------------------------------- */
+
+function init() {
+ parent_object = opener.HTMLArea._object;
+ var config = cloneObject( parent_object.config );
+ config.editorURL = "../";
+ config.width = "100%";
+ config.height = "auto";
+
+ // change maximize button to minimize button
+ config.btnList["popupeditor"] = [ 'Minimize Editor', 'images/fullscreen_minimize.gif', true,
+ function() { window.close(); } ];
+
+ // generate editor and resize it
+ editor = new HTMLArea("editor", config);
+ editor.generate();
+ editor._iframe.style.width = "100%";
+ editor._textArea.style.width = "100%";
+ resize_editor();
+
+ // set child window contents and event handlers, after a small delay
+ setTimeout(function() {
+ editor.setHTML(parent_object.getInnerHTML());
+
+ // switch mode if needed
+ if (parent_object._mode == "textmode") { editor.setMode("textmode"); }
+
+ // continuously update parent editor window
+ setInterval(update_parent, 500);
+
+ // setup event handlers
+ document.body.onkeypress = _CloseOnEsc;
+ editor._doc.body.onkeypress = _CloseOnEsc;
+ editor._textArea.onkeypress = _CloseOnEsc;
+ window.onresize = resize_editor;
+ }, 333); // give it some time to meet the new frame
+}
+
+/* ---------------------------------------------------------------------- *\
+ Function : update_parent
+ Description : update parent window editor field with contents from child window
+\* ---------------------------------------------------------------------- */
+
+function update_parent() {
+ // use the fast version
+ parent_object.setHTML(editor.getInnerHTML());
+}
+
+
+</script>
+</head>
+<body scroll="no" onload="init()" onunload="update_parent()">
+
+<form style="margin: 0px; border: 1px solid; border-color: threedshadow threedhighlight threedhighlight threedshadow;">
+<textarea name="editor" id="editor" style="width:100%; height:300px"> </textarea>
+</form>
+
+</body></html>
--- /dev/null
+<?php
+ include("../../../config.php");
+?>
+<html>
+<head><title>Fullscreen Editor</title>
+<style type="text/css">
+@import url(../htmlarea.css);
+html, body { margin: 0px; border: 0px; background-color: buttonface; } </style>
+
+
+<script type="text/javascript" src="../htmlarea.php<?php print($id != "")?"?id=$id":"";?>"></script>
+<script type="text/javascript" src="../lang/en.php"></script>
+<script type="text/javascript" src="../dialog.js"></script>
+
+<script type="text/javascript">
+// load same scripts that were present in the opener page
+var scripts = opener.document.getElementsByTagName("script");
+var head = document.getElementsByTagName("head")[0];
+for (var i = 0; i < scripts.length; ++i) {
+ var script = scripts[i];
+ if (typeof script.src != "undefined" && /\S/.test(script.src)) {
+ // document.write("<scr" + "ipt type=" + "\"script/javascript\"");
+ // document.write(" src=\"../" + script.src + "\"></scr" + "ipt>");
+ var new_script = document.createElement("script");
+ if (/^http:/i.test(script.src)) {
+ new_script.src = script.src;
+ } else {
+ new_script.src = "../" + script.src;
+ }
+ head.appendChild(new_script);
+ }
+}
+</script>
+
+<script type="text/javascript">
+
+var parent_object = null;
+var editor = null; // to be initialized later [ function init() ]
+
+/* ---------------------------------------------------------------------- *\
+ Function :
+ Description :
+\* ---------------------------------------------------------------------- */
+
+function _CloseOnEsc(ev) {
+ if (document.all) {
+ // IE
+ ev = window.event;
+ }
+ if (ev.keyCode == 27) {
+ // update_parent();
+ window.close();
+ return;
+ }
+}
+
+/* ---------------------------------------------------------------------- *\
+ Function : cloneObject
+ Description : copy an object by value instead of by reference
+ Usage : var newObj = cloneObject(oldObj);
+\* ---------------------------------------------------------------------- */
+
+function cloneObject(obj) {
+ var newObj = new Object;
+
+ // check for array objects
+ if (obj.constructor.toString().indexOf("function Array(") == 1) {
+ newObj = obj.constructor();
+ }
+
+ // check for function objects (as usual, IE is fucked up)
+ if (obj.constructor.toString().indexOf("function Function(") == 1) {
+ newObj = obj; // just copy reference to it
+ } else for (var n in obj) {
+ var node = obj[n];
+ if (typeof node == 'object') { newObj[n] = cloneObject(node); }
+ else { newObj[n] = node; }
+ }
+
+ return newObj;
+}
+
+/* ---------------------------------------------------------------------- *\
+ Function : resize_editor
+ Description : resize the editor when the user resizes the popup
+\* ---------------------------------------------------------------------- */
+
+function resize_editor() { // resize editor to fix window
+ var newHeight;
+ if (document.all) {
+ // IE
+ newHeight = document.body.offsetHeight - editor._toolbar.offsetHeight;
+ if (newHeight < 0) { newHeight = 0; }
+ } else {
+ // Gecko
+ newHeight = window.innerHeight - editor._toolbar.offsetHeight;
+ }
+ if (editor.config.statusBar) {
+ newHeight -= editor._statusBar.offsetHeight;
+ }
+ editor._textArea.style.height = editor._iframe.style.height = newHeight + "px";
+}
+
+/* ---------------------------------------------------------------------- *\
+ Function : init
+ Description : run this code on page load
+\* ---------------------------------------------------------------------- */
+
+function init() {
+ parent_object = opener.HTMLArea._object;
+ var config = cloneObject( parent_object.config );
+ config.editorURL = "../";
+ config.width = "100%";
+ config.height = "auto";
+
+ // change maximize button to minimize button
+ config.btnList["popupeditor"] = [ 'Minimize Editor', '<?php echo $CFG->wwwroot ?>/lib/editor/images/fullscreen_minimize.gif', true,
+ function() { window.close(); } ];
+
+ // generate editor and resize it
+ editor = new HTMLArea("editor", config);
+ editor.generate();
+ editor._iframe.style.width = "100%";
+ editor._textArea.style.width = "100%";
+ resize_editor();
+
+ // set child window contents and event handlers, after a small delay
+ setTimeout(function() {
+ editor.setHTML(parent_object.getInnerHTML());
+
+ // switch mode if needed
+ if (parent_object._mode == "textmode") { editor.setMode("textmode"); }
+
+ // continuously update parent editor window
+ setInterval(update_parent, 500);
+
+ // setup event handlers
+ document.body.onkeypress = _CloseOnEsc;
+ editor._doc.body.onkeypress = _CloseOnEsc;
+ editor._textArea.onkeypress = _CloseOnEsc;
+ window.onresize = resize_editor;
+ }, 333); // give it some time to meet the new frame
+}
+
+/* ---------------------------------------------------------------------- *\
+ Function : update_parent
+ Description : update parent window editor field with contents from child window
+\* ---------------------------------------------------------------------- */
+
+function update_parent() {
+ // use the fast version
+ parent_object.setHTML(editor.getInnerHTML());
+}
+
+
+</script>
+</head>
+<body scroll="no" onload="init()" onunload="update_parent()">
+
+<form style="margin: 0px; border: 1px solid; border-color: threedshadow threedhighlight threedhighlight threedshadow;">
+<textarea name="editor" id="editor" style="width:100%; height:300px"> </textarea>
+</form>
+
+</body></html>
--- /dev/null
+<html style="width: 398; height: 218">
+
+<head>
+ <title>Insert Image</title>
+
+<script type="text/javascript" src="popup.js"></script>
+
+<script type="text/javascript">
+var preview_window = null;
+
+function Init() {
+ __dlg_init();
+ document.getElementById("f_url").focus();
+};
+
+function onOK() {
+ var required = {
+ "f_url": "You must enter the URL",
+ "f_alt": "Please enter the alternate text"
+ };
+ for (var i in required) {
+ var el = document.getElementById(i);
+ if (!el.value) {
+ alert(required[i]);
+ el.focus();
+ return false;
+ }
+ }
+ // pass data back to the calling window
+ var fields = ["f_url", "f_alt", "f_align", "f_border",
+ "f_horiz", "f_vert"];
+ var param = new Object();
+ for (var i in fields) {
+ var id = fields[i];
+ var el = document.getElementById(id);
+ param[id] = el.value;
+ }
+ if (preview_window) {
+ preview_window.close();
+ }
+ __dlg_close(param);
+ return false;
+};
+
+function onCancel() {
+ if (preview_window) {
+ preview_window.close();
+ }
+ __dlg_close(null);
+ return false;
+};
+
+function onPreview() {
+ var f_url = document.getElementById("f_url");
+ var url = f_url.value;
+ if (!url) {
+ alert("You have to enter an URL first");
+ f_url.focus();
+ return false;
+ }
+ var img = new Image();
+ img.src = url;
+ var win = null;
+ if (!document.all) {
+ win = window.open("about:blank", "ha_imgpreview", "toolbar=no,menubar=no,personalbar=no,innerWidth=100,innerHeight=100,scrollbars=no,resizable=yes");
+ } else {
+ win = window.open("about:blank", "ha_imgpreview", "channelmode=no,directories=no,height=100,width=100,location=no,menubar=no,resizable=yes,scrollbars=no,toolbar=no");
+ }
+ preview_window = win;
+ var doc = win.document;
+ var body = doc.body;
+ if (body) {
+ body.innerHTML = "";
+ body.style.padding = "0px";
+ body.style.margin = "0px";
+ var el = doc.createElement("img");
+ el.src = url;
+
+ var table = doc.createElement("table");
+ body.appendChild(table);
+ table.style.width = "100%";
+ table.style.height = "100%";
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+ var tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ var td = doc.createElement("td");
+ tr.appendChild(td);
+ td.style.textAlign = "center";
+
+ td.appendChild(el);
+ win.resizeTo(el.offsetWidth + 30, el.offsetHeight + 30);
+ }
+ win.focus();
+ return false;
+};
+</script>
+
+<style type="text/css">
+html, body {
+ background: ButtonFace;
+ color: ButtonText;
+ font: 11px Tahoma,Verdana,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+body { padding: 5px; }
+table {
+ font: 11px Tahoma,Verdana,sans-serif;
+}
+form p {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+.fl { width: 9em; float: left; padding: 2px 5px; text-align: right; }
+.fr { width: 6em; float: left; padding: 2px 5px; text-align: right; }
+fieldset { padding: 0px 10px 5px 5px; }
+select, input, button { font: 11px Tahoma,Verdana,sans-serif; }
+button { width: 70px; }
+.space { padding: 2px; }
+
+.title { background: #ddf; color: #000; font-weight: bold; font-size: 120%; padding: 3px 10px; margin-bottom: 10px;
+border-bottom: 1px solid black; letter-spacing: 2px;
+}
+form { padding: 0px; margin: 0px; }
+</style>
+
+</head>
+
+<body onload="Init()">
+
+<div class="title">Insert Image</div>
+
+<form action="" method="get">
+<table border="0" width="100%" style="padding: 0px; margin: 0px">
+ <tbody>
+
+ <tr>
+ <td style="width: 7em; text-align: right">Image URL:</td>
+ <td><input type="text" name="url" id="f_url" style="width:75%"
+ title="Enter the image URL here" />
+ <button name="preview" onclick="return onPreview()"
+ title="Preview the image in a new window">Preview</button>
+ </td>
+ </tr>
+ <tr>
+ <td style="width: 7em; text-align: right">Alternate text:</td>
+ <td><input type="text" name="alt" id="f_alt" style="width:100%"
+ title="For browsers that don't support images" /></td>
+ </tr>
+
+ </tbody>
+</table>
+
+<p />
+
+<fieldset style="float: left; margin-left: 5px;">
+<legend>Layout</legend>
+
+<div class="space"></div>
+
+<div class="fl">Alignment:</div>
+<select size="1" name="align" id="f_align"
+ title="Positioning of this image">
+ <option value="" >Not set</option>
+ <option value="left" >Left</option>
+ <option value="right" >Right</option>
+ <option value="texttop" >Texttop</option>
+ <option value="absmiddle" >Absmiddle</option>
+ <option value="baseline" selected="1" >Baseline</option>
+ <option value="absbottom" >Absbottom</option>
+ <option value="bottom" >Bottom</option>
+ <option value="middle" >Middle</option>
+ <option value="top" >Top</option>
+</select>
+
+<p />
+
+<div class="fl">Border thickness:</div>
+<input type="text" name="border" id="f_border" size="5"
+title="Leave empty for no border" />
+
+<div class="space"></div>
+
+</fieldset>
+
+<fieldset style="float:right; margin-right: 5px;">
+<legend>Spacing</legend>
+
+<div class="space"></div>
+
+<div class="fr">Horizontal:</div>
+<input type="text" name="horiz" id="f_horiz" size="5"
+title="Horizontal padding" />
+
+<p />
+
+<div class="fr">Vertical:</div>
+<input type="text" name="vert" id="f_vert" size="5"
+title="Vertical padding" />
+
+<div class="space"></div>
+
+</fieldset>
+
+<div style="margin-top: 85px; text-align: right;">
+<hr />
+<button type="button" name="ok" onclick="return onOK();">OK</button>
+<button type="button" name="cancel" onclick="return onCancel();">Cancel</button>
+</div>
+
+</form>
+
+</body>
+</html>
--- /dev/null
+<?php include("../../../config.php");
+
+ require_variable($id);
+
+ if (!$course = get_record("course", "id", $id)) {
+ $course->fullname = ""; // Just to keep display happy, though browsing may fail
+ }
+?>
+<html style="width: 398; height: 218">
+
+<head>
+ <title>Insert Image</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="popup.js"></script>
+
+<script type="text/javascript">
+var preview_window = null;
+
+function Init() {
+ __dlg_init();
+ //document.getElementById("f_url").focus();
+};
+
+function onOK() {
+ var required = {
+ "f_url": "You must enter the URL",
+ "f_alt": "Please enter the alternate text"
+ };
+ for (var i in required) {
+ var el = document.getElementById(i);
+ if (!el.value) {
+ alert(required[i]);
+ el.focus();
+ return false;
+ }
+ }
+ // pass data back to the calling window
+ var fields = ["f_url", "f_alt", "f_align", "f_border",
+ "f_horiz", "f_vert"];
+ var param = new Object();
+ for (var i in fields) {
+ var id = fields[i];
+ var el = document.getElementById(id);
+ param[id] = el.value;
+ }
+ if (preview_window) {
+ preview_window.close();
+ }
+ __dlg_close(param);
+ return false;
+};
+
+function onCancel() {
+ if (preview_window) {
+ preview_window.close();
+ }
+ __dlg_close(null);
+ return false;
+};
+
+function onPreview() {
+ var f_url = document.getElementById("f_url");
+ var url = f_url.value;
+ if (!url) {
+ alert("You have to enter an URL first");
+ f_url.focus();
+ return false;
+ }
+ var img = new Image();
+ img.src = url;
+ var win = null;
+ if (!document.all) {
+ win = window.open("about:blank", "ha_imgpreview", "toolbar=no,menubar=no,personalbar=no,innerWidth=100,innerHeight=100,scrollbars=no,resizable=yes");
+ } else {
+ win = window.open("about:blank", "ha_imgpreview", "channelmode=no,directories=no,height=100,width=100,location=no,menubar=no,resizable=yes,scrollbars=no,toolbar=no");
+ }
+ preview_window = win;
+ var doc = win.document;
+ var body = doc.body;
+ if (body) {
+ body.innerHTML = "";
+ body.style.padding = "0px";
+ body.style.margin = "0px";
+ var el = doc.createElement("img");
+ el.src = url;
+
+ var table = doc.createElement("table");
+ body.appendChild(table);
+ table.style.width = "100%";
+ table.style.height = "100%";
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+ var tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ var td = doc.createElement("td");
+ tr.appendChild(td);
+ td.style.textAlign = "center";
+
+ td.appendChild(el);
+ win.resizeTo(el.offsetWidth + 30, el.offsetHeight + 30);
+ }
+ win.focus();
+ return false;
+};
+function set_url_value()
+{
+ var url = "<?php echo $CFG->wwwroot ?>/lib/editor/courseimages.php?id=<?php echo $id ?>";
+ window.open(url,'koje','toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=no, copyhistory=yes, width=700, height=450, top=150, left=200');
+}
+</script>
+
+<style type="text/css">
+html, body {
+ background: ButtonFace;
+ color: ButtonText;
+ font: 11px Tahoma,Verdana,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+body { padding: 5px; }
+table {
+ font: 11px Tahoma,Verdana,sans-serif;
+}
+form p {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+.fl { width: 9em; float: left; padding: 2px 5px; text-align: right; }
+.fr { width: 6em; float: left; padding: 2px 5px; text-align: right; }
+fieldset { padding: 0px 10px 5px 5px; }
+select, input, button { font: 11px Tahoma,Verdana,sans-serif; }
+button { width: 70px; }
+.space { padding: 2px; }
+
+.title { background: #ddf; color: #000; font-weight: bold; font-size: 120%; padding: 3px 10px; margin-bottom: 10px;
+border-bottom: 1px solid black; letter-spacing: 2px;
+}
+form { padding: 0px; margin: 0px; }
+</style>
+
+</head>
+
+<body onload="Init()">
+
+<div class="title"><?php print(get_string("insertimage","htmlarea"));?></div>
+
+<form action="" method="get">
+<table border="0" width="100%" style="padding: 0px; margin: 0px">
+ <tbody>
+
+ <tr>
+ <td style="width: 7em; text-align: right"><?php print(get_string("imageurl","htmlarea"));?>:</td>
+ <td><input type="text" name="url" id="f_url" style="width:75%"
+ title="Enter the image URL here" />
+ <button name="preview" onclick="return onPreview();"
+ title="Preview the image in a new window"><?php print(get_string("preview","htmlarea"));?></button>
+ </td>
+ </tr>
+ <tr>
+ <td style="width: 7em; text-align: right"><?php print(get_string("alternatetext","htmlarea"));?>:</td>
+ <td><input type="text" name="alt" id="f_alt" style="width:100%"
+ title="For browsers that don't support images" /></td>
+ </tr>
+
+ </tbody>
+</table>
+
+<p />
+
+<fieldset style="float: left; margin-left: 5px;">
+<legend><?php print(get_string("layout","htmlarea"));?></legend>
+
+<div class="space"></div>
+
+<div class="fl"><?php print(get_string("alignment","htmlarea"));?>:</div>
+<select size="1" name="align" id="f_align"
+ title="Positioning of this image">
+ <option value="" ><?php print(get_string("notset","htmlarea"));?></option>
+ <option value="left" ><?php print(get_string("left","htmlarea"));?></option>
+ <option value="right" ><?php print(get_string("right","htmlarea"));?></option>
+ <option value="texttop" ><?php print(get_string("texttop","htmlarea"));?></option>
+ <option value="absmiddle" ><?php print(get_string("absmiddle","htmlarea"));?></option>
+ <option value="baseline" selected="1" ><?php print(get_string("baseline","htmlarea"));?></option>
+ <option value="absbottom" ><?php print(get_string("absbottom","htmlarea"));?></option>
+ <option value="bottom" ><?php print(get_string("bottom","htmlarea"));?></option>
+ <option value="middle" ><?php print(get_string("middle","htmlarea"));?></option>
+ <option value="top" ><?php print(get_string("top","htmlarea"));?></option>
+</select>
+
+<p />
+
+<div class="fl"><?php print(get_string("borderthickness","htmlarea"));?>:</div>
+<input type="text" name="border" id="f_border" size="5"
+title="Leave empty for no border" />
+
+<div class="space"></div>
+
+</fieldset>
+
+<fieldset style="float:right; margin-right: 5px;">
+<legend><?php print(get_string("spacing","htmlarea"));?></legend>
+
+<div class="space"></div>
+
+<div class="fr"><?php print(get_string("horizontal","htmlarea"));?>:</div>
+<input type="text" name="horiz" id="f_horiz" size="5"
+title="Horizontal padding" />
+
+<p />
+
+<div class="fr"><?php print(get_string("vertical","htmlarea"));?>:</div>
+<input type="text" name="vert" id="f_vert" size="5"
+title="Vertical padding" />
+
+<div class="space"></div>
+
+</fieldset>
+
+<div style="margin-top: 85px; text-align: right;">
+<hr />
+<?php
+print(isteacher($id))?"<button title=\"$course->fullname\" type=\"button\" name=\"browse\" onclick=\"set_url_value()\">".get_string("browse","htmlarea")."</button> \n":"";
+?>
+<button type="button" name="ok" onclick="return onOK();"><?php print(get_string("ok","htmlarea"));?></button>
+<button type="button" name="cancel" onclick="return onCancel();"><?php print(get_string("cancel","htmlarea"));?></button>
+</div>
+
+</form>
+
+</body>
+</html>
--- /dev/null
+<html style="width: 398; height: 218">
+
+<head>
+ <title>Insert Table</title>
+
+<script type="text/javascript" src="popup.js"></script>
+<script type="text/javascript" src="../lang/en.php"></script>
+<script type="text/javascript">
+
+function Init() {
+ __dlg_init();
+ document.getElementById('f_rows').focus();
+};
+
+function onOK() {
+ var required = {
+ "f_rows": "You must enter a number of rows",
+ "f_cols": "You must enter a number of columns"
+ };
+ for (var i in required) {
+ var el = document.getElementById(i);
+ if (!el.value) {
+ alert(required[i]);
+ el.focus();
+ return false;
+ }
+ }
+ var fields = ["f_rows", "f_cols", "f_width", "f_unit",
+ "f_align", "f_border", "f_spacing", "f_padding"];
+ var param = new Object();
+ for (var i in fields) {
+ var id = fields[i];
+ var el = document.getElementById(id);
+ param[id] = el.value;
+ }
+ __dlg_close(param);
+ return false;
+};
+
+function onCancel() {
+ __dlg_close(null);
+ return false;
+};
+
+</script>
+
+<style type="text/css">
+html, body {
+ background: ButtonFace;
+ color: ButtonText;
+ font: 11px Tahoma,Verdana,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+body { padding: 5px; }
+table {
+ font: 11px Tahoma,Verdana,sans-serif;
+}
+form p {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+.fl { width: 9em; float: left; padding: 2px 5px; text-align: right; }
+.fr { width: 7em; float: left; padding: 2px 5px; text-align: right; }
+fieldset { padding: 0px 10px 5px 5px; }
+select, input, button { font: 11px Tahoma,Verdana,sans-serif; }
+button { width: 70px; }
+.space { padding: 2px; }
+
+.title { background: #ddf; color: #000; font-weight: bold; font-size: 120%; padding: 3px 10px; margin-bottom: 10px;
+border-bottom: 1px solid black; letter-spacing: 2px;
+}
+form { padding: 0px; margin: 0px; }
+</style>
+
+</head>
+
+<body onload="Init()">
+
+<div class="title">Insert Table</div>
+
+<form action="" method="get">
+<table border="0" style="padding: 0px; margin: 0px">
+ <tbody>
+
+ <tr>
+ <td style="width: 4em; text-align: right">Rows:</td>
+ <td><input type="text" name="f_rows" id="f_rows" size="5" title="Number of rows" value="2" /></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td style="width: 4em; text-align: right">Cols:</td>
+ <td><input type="text" name="f_cols" id="f_cols" size="5" title="Number of columns" value="4" /></td>
+ <td style="width: 4em; text-align: right">Width:</td>
+ <td><input type="text" name="f_width" id="f_width" size="5" title="Width of the table" value="100" /></td>
+ <td><select size="1" name="f_unit" id="f_unit" title="Width unit">
+ <option value="%" selected="1" >Percent</option>
+ <option value="px" >Pixels</option>
+ <option value="em" >Em</option>
+ </select></td>
+ </tr>
+
+ </tbody>
+</table>
+
+<p />
+
+<fieldset style="float: left; margin-left: 5px;">
+<legend>Layout</legend>
+
+<div class="space"></div>
+
+<div class="fl">Alignment:</div>
+<select size="1" name="f_align" id="f_align"
+ title="Positioning of this image">
+ <option value="" selected="1" >Not set</option>
+ <option value="left" >Left</option>
+ <option value="right" >Right</option>
+ <option value="texttop" >Texttop</option>
+ <option value="absmiddle" >Absmiddle</option>
+ <option value="baseline" >Baseline</option>
+ <option value="absbottom" >Absbottom</option>
+ <option value="bottom" >Bottom</option>
+ <option value="middle" >Middle</option>
+ <option value="top" >Top</option>
+</select>
+
+<p />
+
+<div class="fl">Border thickness:</div>
+<input type="text" name="f_border" id="f_border" size="5" value="1"
+title="Leave empty for no border" />
+<!--
+<p />
+
+<div class="fl">Collapse borders:</div>
+<input type="checkbox" name="collapse" id="f_collapse" />
+-->
+<div class="space"></div>
+
+</fieldset>
+
+<fieldset style="float:right; margin-right: 5px;">
+<legend>Spacing</legend>
+
+<div class="space"></div>
+
+<div class="fr">Cell spacing:</div>
+<input type="text" name="f_spacing" id="f_spacing" size="5" value="1"
+title="Space between adjacent cells" />
+
+<p />
+
+<div class="fr">Cell padding:</div>
+<input type="text" name="f_padding" id="f_padding" size="5" value="1"
+title="Space between content and border in cell" />
+
+<div class="space"></div>
+
+</fieldset>
+
+<div style="margin-top: 85px; text-align: right;">
+<hr />
+<button type="button" name="ok" onclick="return onOK();">OK</button>
+<button type="button" name="cancel" onclick="return onCancel();">Cancel</button>
+</div>
+
+</form>
+
+</body>
+</html>
--- /dev/null
+<?php
+ include("../../../config.php");
+?>
+<html style="width: 398; height: 218">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>Insert Table</title>
+<script type="text/javascript" src="popup.js"></script>
+<script type="text/javascript">
+
+function Init() {
+ __dlg_init();
+ document.getElementById('f_rows').focus();
+};
+
+function onOK() {
+ var required = {
+ "f_rows": "You must enter a number of rows",
+ "f_cols": "You must enter a number of columns"
+ };
+ for (var i in required) {
+ var el = document.getElementById(i);
+ if (!el.value) {
+ alert(required[i]);
+ el.focus();
+ return false;
+ }
+ }
+ var fields = ["f_rows", "f_cols", "f_width", "f_unit",
+ "f_align", "f_border", "f_spacing", "f_padding"];
+ var param = new Object();
+ for (var i in fields) {
+ var id = fields[i];
+ var el = document.getElementById(id);
+ param[id] = el.value;
+ }
+ __dlg_close(param);
+ return false;
+};
+
+function onCancel() {
+ __dlg_close(null);
+ return false;
+};
+
+</script>
+
+<style type="text/css">
+html, body {
+ background: ButtonFace;
+ color: ButtonText;
+ font: 11px Tahoma,Verdana,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+body { padding: 5px; }
+table {
+ font: 11px Tahoma,Verdana,sans-serif;
+}
+form p {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+.fl { width: 9em; float: left; padding: 2px 5px; text-align: right; }
+.fr { width: 7em; float: left; padding: 2px 5px; text-align: right; }
+fieldset { padding: 0px 10px 5px 5px; }
+select, input, button { font: 11px Tahoma,Verdana,sans-serif; }
+button { width: 70px; }
+.space { padding: 2px; }
+
+.title { background: #ddf; color: #000; font-weight: bold; font-size: 120%; padding: 3px 10px; margin-bottom: 10px;
+border-bottom: 1px solid black; letter-spacing: 2px;
+}
+form { padding: 0px; margin: 0px; }
+</style>
+
+</head>
+
+<body onload="Init()">
+
+<div class="title"><?php print(get_string("inserttable","htmlarea"));?></div>
+
+<form action="" method="get">
+<table border="0" style="padding: 0px; margin: 0px">
+ <tbody>
+
+ <tr>
+ <td style="width: 4em; text-align: right"><?php print(get_string("rows","htmlarea"));?>:</td>
+ <td><input type="text" name="f_rows" id="f_rows" size="5" title="Number of rows" value="2" /></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td style="width: 4em; text-align: right"><?php print(get_string("cols","htmlarea"));?>:</td>
+ <td><input type="text" name="f_cols" id="f_cols" size="5" title="Number of columns" value="4" /></td>
+ <td style="width: 4em; text-align: right"><?php print(get_string("width","htmlarea"));?>:</td>
+ <td><input type="text" name="f_width" id="f_width" size="5" title="Width of the table" value="100" /></td>
+ <td><select size="1" name="f_unit" id="f_unit" title="Width unit">
+ <option value="%" selected="1" ><?php print(get_string("percent","htmlarea"));?></option>
+ <option value="px" ><?php print(get_string("pixels","htmlarea"));?></option>
+ <option value="em" >Em</option>
+ </select></td>
+ </tr>
+
+ </tbody>
+</table>
+
+<p />
+
+<fieldset style="float: left; margin-left: 5px;">
+<legend><?php print(get_string("layout","htmlarea"));?></legend>
+
+<div class="space"></div>
+
+<div class="fl"><?php print(get_string("alignment","htmlarea"));?>:</div>
+<select size="1" name="f_align" id="f_align"
+ title="Positioning of this image">
+ <option value="" selected="1" ><?php print(get_string("notset","htmlarea"));?></option>
+ <option value="left" ><?php print(get_string("left","htmlarea"));?></option>
+ <option value="right" ><?php print(get_string("right","htmlarea"));?></option>
+ <option value="texttop" ><?php print(get_string("texttop","htmlarea"));?></option>
+ <option value="absmiddle" ><?php print(get_string("absmiddle","htmlarea"));?></option>
+ <option value="baseline" ><?php print(get_string("baseline","htmlarea"));?></option>
+ <option value="absbottom" ><?php print(get_string("absbottom","htmlarea"));?></option>
+ <option value="bottom" ><?php print(get_string("bottom","htmlarea"));?></option>
+ <option value="middle" ><?php print(get_string("middle","htmlarea"));?></option>
+ <option value="top" ><?php print(get_string("top","htmlarea"));?></option>
+</select>
+
+<p />
+
+<div class="fl"><?php print(get_string("borderthickness","htmlarea"));?>:</div>
+<input type="text" name="f_border" id="f_border" size="5" value="1"
+title="Leave empty for no border" />
+<!--
+<p />
+
+<div class="fl">Collapse borders:</div>
+<input type="checkbox" name="collapse" id="f_collapse" />
+-->
+<div class="space"></div>
+
+</fieldset>
+
+<fieldset style="float:right; margin-right: 5px;">
+<legend><?php print(get_string("spacing","htmlarea"));?></legend>
+
+<div class="space"></div>
+
+<div class="fr"><?php print(get_string("cellspacing","htmlarea"));?>:</div>
+<input type="text" name="f_spacing" id="f_spacing" size="5" value="1"
+title="Space between adjacent cells" />
+
+<p />
+
+<div class="fr"><?php print(get_string("cellpadding","htmlarea"));?>:</div>
+<input type="text" name="f_padding" id="f_padding" size="5" value="1"
+title="Space between content and border in cell" />
+
+<div class="space"></div>
+
+</fieldset>
+
+<div style="margin-top: 85px; text-align: right;">
+<hr />
+<button type="button" name="ok" onclick="return onOK();"><?php print(get_string("ok","htmlarea"));?></button>
+<button type="button" name="cancel" onclick="return onCancel();"><?php print(get_string("cancel","htmlarea"));?></button>
+</div>
+
+</form>
+
+</body>
+</html>
--- /dev/null
+<html style="width: 398; height: 218">
+
+<head>
+ <title>Insert Image</title>
+
+<script type="text/javascript" src="popup.js"></script>
+
+<script type="text/javascript">
+var preview_window = null;
+
+function Init() {
+ __dlg_init();
+ document.getElementById("f_url").focus();
+};
+
+function onOK() {
+ var required = {
+ "f_url": "You must enter the URL",
+ "f_alt": "Please enter the alternate text"
+ };
+ for (var i in required) {
+ var el = document.getElementById(i);
+ if (!el.value) {
+ alert(required[i]);
+ el.focus();
+ return false;
+ }
+ }
+ // pass data back to the calling window
+ var fields = ["f_url", "f_alt", "f_align", "f_border",
+ "f_horiz", "f_vert"];
+ var param = new Object();
+ for (var i in fields) {
+ var id = fields[i];
+ var el = document.getElementById(id);
+ param[id] = el.value;
+ }
+ if (preview_window) {
+ preview_window.close();
+ }
+ __dlg_close(param);
+ return false;
+};
+
+function onCancel() {
+ if (preview_window) {
+ preview_window.close();
+ }
+ __dlg_close(null);
+ return false;
+};
+
+function onPreview() {
+ /* var f_url = document.getElementById("f_url");
+ var url = f_url.value;
+ if (!url) {
+ alert("You have to enter an URL first");
+ f_url.focus();
+ return false;
+ }
+ var img = new Image();
+ img.src = url;
+ var win = null;
+ if (!document.all) {
+ win = window.open("about:blank", "ha_imgpreview", "toolbar=no,menubar=no,personalbar=no,innerWidth=100,innerHeight=100,scrollbars=no,resizable=yes");
+ } else {
+ win = window.open("about:blank", "ha_imgpreview", "channelmode=no,directories=no,height=100,width=100,location=no,menubar=no,resizable=yes,scrollbars=no,toolbar=no");
+ }
+ preview_window = win;
+ var doc = win.document;
+ var body = doc.body;
+ if (body) {
+ body.innerHTML = "";
+ body.style.padding = "0px";
+ body.style.margin = "0px";
+ var el = doc.createElement("img");
+ el.src = url;
+
+ var table = doc.createElement("table");
+ body.appendChild(table);
+ table.style.width = "100%";
+ table.style.height = "100%";
+ var tbody = doc.createElement("tbody");
+ table.appendChild(tbody);
+ var tr = doc.createElement("tr");
+ tbody.appendChild(tr);
+ var td = doc.createElement("td");
+ tr.appendChild(td);
+ td.style.textAlign = "center";
+
+ td.appendChild(el);
+ win.resizeTo(el.offsetWidth + 30, el.offsetHeight + 30);
+ }
+ win.focus();
+ return false; */
+};
+</script>
+
+<style type="text/css">
+html, body {
+ background: ButtonFace;
+ color: ButtonText;
+ font: 11px Tahoma,Verdana,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+body { padding: 5px; }
+table {
+ font: 11px Tahoma,Verdana,sans-serif;
+}
+form p {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+.fl { width: 9em; float: left; padding: 2px 5px; text-align: right; }
+.fr { width: 6em; float: left; padding: 2px 5px; text-align: right; }
+fieldset { padding: 0px 10px 5px 5px; }
+select, input, button { font: 11px Tahoma,Verdana,sans-serif; }
+button { width: 70px; }
+.space { padding: 2px; }
+
+.title { background: #ddf; color: #000; font-weight: bold; font-size: 120%; padding: 3px 10px; margin-bottom: 10px;
+border-bottom: 1px solid black; letter-spacing: 2px;
+}
+form { padding: 0px; margin: 0px; }
+</style>
+
+</head>
+
+<body onload="Init()">
+
+<div class="title">Insert Image</div>
+
+<form action="" method="get">
+<table border="0" width="100%" style="padding: 0px; margin: 0px">
+ <tbody>
+
+ <tr>
+ <td style="width: 7em; text-align: right">Image URL:</td>
+ <td><input type="text" name="url" id="f_url" style="width:75%"
+ title="Enter the image URL here" />
+ <button name="preview" onclick="window.open('http://server.janne.com/hallinta/tiedotteet/images.php','my_new_window','toolbar=no, location=no, directories=no, status=yes, menubar=no, scrollbars=no, resizable=no, copyhistory=yes, width=400, height=300, top=100, left=100');"
+ title="Preview the image in a new window">Preview</button>
+ </td>
+ </tr>
+ <tr>
+ <td style="width: 7em; text-align: right">Alternate text:</td>
+ <td><input type="text" name="alt" id="f_alt" style="width:100%"
+ title="For browsers that don't support images" /></td>
+ </tr>
+
+ </tbody>
+</table>
+
+<p />
+
+<fieldset style="float: left; margin-left: 5px;">
+<legend>Layout</legend>
+
+<div class="space"></div>
+
+<div class="fl">Alignment:</div>
+<select size="1" name="align" id="f_align"
+ title="Positioning of this image">
+ <option value="" >Not set</option>
+ <option value="left" >Left</option>
+ <option value="right" >Right</option>
+ <option value="texttop" >Texttop</option>
+ <option value="absmiddle" >Absmiddle</option>
+ <option value="baseline" selected="1" >Baseline</option>
+ <option value="absbottom" >Absbottom</option>
+ <option value="bottom" >Bottom</option>
+ <option value="middle" >Middle</option>
+ <option value="top" >Top</option>
+</select>
+
+<p />
+
+<div class="fl">Border thickness:</div>
+<input type="text" name="border" id="f_border" size="5"
+title="Leave empty for no border" />
+
+<div class="space"></div>
+
+</fieldset>
+
+<fieldset style="float:right; margin-right: 5px;">
+<legend>Spacing</legend>
+
+<div class="space"></div>
+
+<div class="fr">Horizontal:</div>
+<input type="text" name="horiz" id="f_horiz" size="5"
+title="Horizontal padding" />
+
+<p />
+
+<div class="fr">Vertical:</div>
+<input type="text" name="vert" id="f_vert" size="5"
+title="Vertical padding" />
+
+<div class="space"></div>
+
+</fieldset>
+
+<div style="margin-top: 85px; text-align: right;">
+<hr />
+<button type="button" name="ok" onclick="return onOK();">OK</button>
+<button type="button" name="cancel" onclick="return onCancel();">Cancel</button>
+</div>
+
+</form>
+
+</body>
+</html>
--- /dev/null
+function __dlg_onclose() {
+ if (!document.all) {
+ opener.Dialog._return(null);
+ }
+};
+
+function __dlg_init() {
+ if (!document.all) {
+ // init dialogArguments, as IE gets it
+ window.dialogArguments = opener.Dialog._arguments;
+ window.sizeToContent();
+ window.sizeToContent(); // for reasons beyond understanding,
+ // only if we call it twice we get the
+ // correct size.
+ window.addEventListener("unload", __dlg_onclose, true);
+ // center on parent
+ var px1 = opener.screenX;
+ var px2 = opener.screenX + opener.outerWidth;
+ var py1 = opener.screenY;
+ var py2 = opener.screenY + opener.outerHeight;
+ var x = (px2 - px1 - window.outerWidth) / 2;
+ var y = (py2 - py1 - window.outerHeight) / 2;
+ window.moveTo(x, y);
+ var body = document.body;
+ window.innerHeight = body.offsetHeight + 10;
+ window.innerWidth = body.offsetWidth + 10;
+ window.focus();
+ } else {
+ var body = document.body;
+ window.dialogWidth = body.offsetWidth + "px";
+ window.dialogHeight = body.offsetHeight + 50 + "px";
+ }
+};
+
+// closes the dialog and passes the return info upper.
+function __dlg_close(val) {
+ if (document.all) { // IE
+ window.returnValue = val;
+ } else {
+ opener.Dialog._return(val);
+ }
+ window.close();
+};
--- /dev/null
+<!-- note: this version of the color picker is optimized for IE 5.5+ only -->\r
+\r
+<html style="width: 238px; height: 182px"><head><title>Select Color</title>\r
+\r
+<script type="text/javascript" src="popup.js"></script>\r
+\r
+<script type="text/javascript">\r
+\r
+function _CloseOnEsc() {\r
+ if (event.keyCode == 27) { window.close(); return; }\r
+}\r
+\r
+function Init() { // run on page load\r
+ __dlg_init(); // <!-- this can be found in popup.js -->\r
+ document.body.onkeypress = _CloseOnEsc;\r
+\r
+ var color = window.dialogArguments;\r
+ color = ValidateColor(color) || '000000';\r
+ View(color); // set default color\r
+}\r
+\r
+function View(color) { // preview color\r
+ document.getElementById("ColorPreview").style.backgroundColor = '#' + color;\r
+ document.getElementById("ColorHex").value = '#' + color;\r
+}\r
+\r
+function Set(string) { // select color\r
+ var color = ValidateColor(string);\r
+ if (color == null) { alert("Invalid color code: " + string); } // invalid color\r
+ else { // valid color\r
+ View(color); // show selected color\r
+ __dlg_close(color);\r
+ }\r
+}\r
+\r
+function ValidateColor(string) { // return valid color code\r
+ string = string || '';\r
+ string = string + "";\r
+ string = string.toUpperCase();\r
+ var chars = '0123456789ABCDEF';\r
+ var out = '';\r
+\r
+ for (var i=0; i<string.length; i++) { // remove invalid color chars\r
+ var schar = string.charAt(i);\r
+ if (chars.indexOf(schar) != -1) { out += schar; }\r
+ }\r
+\r
+ if (out.length != 6) { return null; } // check length\r
+ return out;\r
+}\r
+\r
+</script>\r
+</head>\r
+<body style="background:ButtonFace; margin:0px; padding:0px" onload="Init()">\r
+\r
+<form method="get" style="margin:0px; padding:0px" onSubmit="Set(document.getElementById('ColorHex').value); return false;">\r
+<table border="0px" cellspacing="0px" cellpadding="4" width="100%">\r
+ <tr>\r
+ <td style="background:buttonface" valign=center><div style="background-color: #000000; padding: 1; height: 21px; width: 50px"><div id="ColorPreview" style="height: 100%; width: 100%"></div></div></td>\r
+ <td style="background:buttonface" valign=center><input type="text" name="ColorHex"\r
+ id="ColorHex" value="" size=15 style="font-size: 12px"></td>\r
+ <td style="background:buttonface" width=100%></td>\r
+ </tr>\r
+</table>\r
+</form>\r
+\r
+<table border="0" cellspacing="1px" cellpadding="0px" width="100%" bgcolor="#000000" style="cursor: hand;">\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#003300 onMouseOver=View('003300') onClick=Set('003300') height="10px" width="10px"></td>\r
+<td bgcolor=#006600 onMouseOver=View('006600') onClick=Set('006600') height="10px" width="10px"></td>\r
+<td bgcolor=#009900 onMouseOver=View('009900') onClick=Set('009900') height="10px" width="10px"></td>\r
+<td bgcolor=#00CC00 onMouseOver=View('00CC00') onClick=Set('00CC00') height="10px" width="10px"></td>\r
+<td bgcolor=#00FF00 onMouseOver=View('00FF00') onClick=Set('00FF00') height="10px" width="10px"></td>\r
+<td bgcolor=#330000 onMouseOver=View('330000') onClick=Set('330000') height="10px" width="10px"></td>\r
+<td bgcolor=#333300 onMouseOver=View('333300') onClick=Set('333300') height="10px" width="10px"></td>\r
+<td bgcolor=#336600 onMouseOver=View('336600') onClick=Set('336600') height="10px" width="10px"></td>\r
+<td bgcolor=#339900 onMouseOver=View('339900') onClick=Set('339900') height="10px" width="10px"></td>\r
+<td bgcolor=#33CC00 onMouseOver=View('33CC00') onClick=Set('33CC00') height="10px" width="10px"></td>\r
+<td bgcolor=#33FF00 onMouseOver=View('33FF00') onClick=Set('33FF00') height="10px" width="10px"></td>\r
+<td bgcolor=#660000 onMouseOver=View('660000') onClick=Set('660000') height="10px" width="10px"></td>\r
+<td bgcolor=#663300 onMouseOver=View('663300') onClick=Set('663300') height="10px" width="10px"></td>\r
+<td bgcolor=#666600 onMouseOver=View('666600') onClick=Set('666600') height="10px" width="10px"></td>\r
+<td bgcolor=#669900 onMouseOver=View('669900') onClick=Set('669900') height="10px" width="10px"></td>\r
+<td bgcolor=#66CC00 onMouseOver=View('66CC00') onClick=Set('66CC00') height="10px" width="10px"></td>\r
+<td bgcolor=#66FF00 onMouseOver=View('66FF00') onClick=Set('66FF00') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#333333 onMouseOver=View('333333') onClick=Set('333333') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#000033 onMouseOver=View('000033') onClick=Set('000033') height="10px" width="10px"></td>\r
+<td bgcolor=#003333 onMouseOver=View('003333') onClick=Set('003333') height="10px" width="10px"></td>\r
+<td bgcolor=#006633 onMouseOver=View('006633') onClick=Set('006633') height="10px" width="10px"></td>\r
+<td bgcolor=#009933 onMouseOver=View('009933') onClick=Set('009933') height="10px" width="10px"></td>\r
+<td bgcolor=#00CC33 onMouseOver=View('00CC33') onClick=Set('00CC33') height="10px" width="10px"></td>\r
+<td bgcolor=#00FF33 onMouseOver=View('00FF33') onClick=Set('00FF33') height="10px" width="10px"></td>\r
+<td bgcolor=#330033 onMouseOver=View('330033') onClick=Set('330033') height="10px" width="10px"></td>\r
+<td bgcolor=#333333 onMouseOver=View('333333') onClick=Set('333333') height="10px" width="10px"></td>\r
+<td bgcolor=#336633 onMouseOver=View('336633') onClick=Set('336633') height="10px" width="10px"></td>\r
+<td bgcolor=#339933 onMouseOver=View('339933') onClick=Set('339933') height="10px" width="10px"></td>\r
+<td bgcolor=#33CC33 onMouseOver=View('33CC33') onClick=Set('33CC33') height="10px" width="10px"></td>\r
+<td bgcolor=#33FF33 onMouseOver=View('33FF33') onClick=Set('33FF33') height="10px" width="10px"></td>\r
+<td bgcolor=#660033 onMouseOver=View('660033') onClick=Set('660033') height="10px" width="10px"></td>\r
+<td bgcolor=#663333 onMouseOver=View('663333') onClick=Set('663333') height="10px" width="10px"></td>\r
+<td bgcolor=#666633 onMouseOver=View('666633') onClick=Set('666633') height="10px" width="10px"></td>\r
+<td bgcolor=#669933 onMouseOver=View('669933') onClick=Set('669933') height="10px" width="10px"></td>\r
+<td bgcolor=#66CC33 onMouseOver=View('66CC33') onClick=Set('66CC33') height="10px" width="10px"></td>\r
+<td bgcolor=#66FF33 onMouseOver=View('66FF33') onClick=Set('66FF33') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#666666 onMouseOver=View('666666') onClick=Set('666666') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#000066 onMouseOver=View('000066') onClick=Set('000066') height="10px" width="10px"></td>\r
+<td bgcolor=#003366 onMouseOver=View('003366') onClick=Set('003366') height="10px" width="10px"></td>\r
+<td bgcolor=#006666 onMouseOver=View('006666') onClick=Set('006666') height="10px" width="10px"></td>\r
+<td bgcolor=#009966 onMouseOver=View('009966') onClick=Set('009966') height="10px" width="10px"></td>\r
+<td bgcolor=#00CC66 onMouseOver=View('00CC66') onClick=Set('00CC66') height="10px" width="10px"></td>\r
+<td bgcolor=#00FF66 onMouseOver=View('00FF66') onClick=Set('00FF66') height="10px" width="10px"></td>\r
+<td bgcolor=#330066 onMouseOver=View('330066') onClick=Set('330066') height="10px" width="10px"></td>\r
+<td bgcolor=#333366 onMouseOver=View('333366') onClick=Set('333366') height="10px" width="10px"></td>\r
+<td bgcolor=#336666 onMouseOver=View('336666') onClick=Set('336666') height="10px" width="10px"></td>\r
+<td bgcolor=#339966 onMouseOver=View('339966') onClick=Set('339966') height="10px" width="10px"></td>\r
+<td bgcolor=#33CC66 onMouseOver=View('33CC66') onClick=Set('33CC66') height="10px" width="10px"></td>\r
+<td bgcolor=#33FF66 onMouseOver=View('33FF66') onClick=Set('33FF66') height="10px" width="10px"></td>\r
+<td bgcolor=#660066 onMouseOver=View('660066') onClick=Set('660066') height="10px" width="10px"></td>\r
+<td bgcolor=#663366 onMouseOver=View('663366') onClick=Set('663366') height="10px" width="10px"></td>\r
+<td bgcolor=#666666 onMouseOver=View('666666') onClick=Set('666666') height="10px" width="10px"></td>\r
+<td bgcolor=#669966 onMouseOver=View('669966') onClick=Set('669966') height="10px" width="10px"></td>\r
+<td bgcolor=#66CC66 onMouseOver=View('66CC66') onClick=Set('66CC66') height="10px" width="10px"></td>\r
+<td bgcolor=#66FF66 onMouseOver=View('66FF66') onClick=Set('66FF66') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#999999 onMouseOver=View('999999') onClick=Set('999999') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#000099 onMouseOver=View('000099') onClick=Set('000099') height="10px" width="10px"></td>\r
+<td bgcolor=#003399 onMouseOver=View('003399') onClick=Set('003399') height="10px" width="10px"></td>\r
+<td bgcolor=#006699 onMouseOver=View('006699') onClick=Set('006699') height="10px" width="10px"></td>\r
+<td bgcolor=#009999 onMouseOver=View('009999') onClick=Set('009999') height="10px" width="10px"></td>\r
+<td bgcolor=#00CC99 onMouseOver=View('00CC99') onClick=Set('00CC99') height="10px" width="10px"></td>\r
+<td bgcolor=#00FF99 onMouseOver=View('00FF99') onClick=Set('00FF99') height="10px" width="10px"></td>\r
+<td bgcolor=#330099 onMouseOver=View('330099') onClick=Set('330099') height="10px" width="10px"></td>\r
+<td bgcolor=#333399 onMouseOver=View('333399') onClick=Set('333399') height="10px" width="10px"></td>\r
+<td bgcolor=#336699 onMouseOver=View('336699') onClick=Set('336699') height="10px" width="10px"></td>\r
+<td bgcolor=#339999 onMouseOver=View('339999') onClick=Set('339999') height="10px" width="10px"></td>\r
+<td bgcolor=#33CC99 onMouseOver=View('33CC99') onClick=Set('33CC99') height="10px" width="10px"></td>\r
+<td bgcolor=#33FF99 onMouseOver=View('33FF99') onClick=Set('33FF99') height="10px" width="10px"></td>\r
+<td bgcolor=#660099 onMouseOver=View('660099') onClick=Set('660099') height="10px" width="10px"></td>\r
+<td bgcolor=#663399 onMouseOver=View('663399') onClick=Set('663399') height="10px" width="10px"></td>\r
+<td bgcolor=#666699 onMouseOver=View('666699') onClick=Set('666699') height="10px" width="10px"></td>\r
+<td bgcolor=#669999 onMouseOver=View('669999') onClick=Set('669999') height="10px" width="10px"></td>\r
+<td bgcolor=#66CC99 onMouseOver=View('66CC99') onClick=Set('66CC99') height="10px" width="10px"></td>\r
+<td bgcolor=#66FF99 onMouseOver=View('66FF99') onClick=Set('66FF99') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#CCCCCC onMouseOver=View('CCCCCC') onClick=Set('CCCCCC') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#0000CC onMouseOver=View('0000CC') onClick=Set('0000CC') height="10px" width="10px"></td>\r
+<td bgcolor=#0033CC onMouseOver=View('0033CC') onClick=Set('0033CC') height="10px" width="10px"></td>\r
+<td bgcolor=#0066CC onMouseOver=View('0066CC') onClick=Set('0066CC') height="10px" width="10px"></td>\r
+<td bgcolor=#0099CC onMouseOver=View('0099CC') onClick=Set('0099CC') height="10px" width="10px"></td>\r
+<td bgcolor=#00CCCC onMouseOver=View('00CCCC') onClick=Set('00CCCC') height="10px" width="10px"></td>\r
+<td bgcolor=#00FFCC onMouseOver=View('00FFCC') onClick=Set('00FFCC') height="10px" width="10px"></td>\r
+<td bgcolor=#3300CC onMouseOver=View('3300CC') onClick=Set('3300CC') height="10px" width="10px"></td>\r
+<td bgcolor=#3333CC onMouseOver=View('3333CC') onClick=Set('3333CC') height="10px" width="10px"></td>\r
+<td bgcolor=#3366CC onMouseOver=View('3366CC') onClick=Set('3366CC') height="10px" width="10px"></td>\r
+<td bgcolor=#3399CC onMouseOver=View('3399CC') onClick=Set('3399CC') height="10px" width="10px"></td>\r
+<td bgcolor=#33CCCC onMouseOver=View('33CCCC') onClick=Set('33CCCC') height="10px" width="10px"></td>\r
+<td bgcolor=#33FFCC onMouseOver=View('33FFCC') onClick=Set('33FFCC') height="10px" width="10px"></td>\r
+<td bgcolor=#6600CC onMouseOver=View('6600CC') onClick=Set('6600CC') height="10px" width="10px"></td>\r
+<td bgcolor=#6633CC onMouseOver=View('6633CC') onClick=Set('6633CC') height="10px" width="10px"></td>\r
+<td bgcolor=#6666CC onMouseOver=View('6666CC') onClick=Set('6666CC') height="10px" width="10px"></td>\r
+<td bgcolor=#6699CC onMouseOver=View('6699CC') onClick=Set('6699CC') height="10px" width="10px"></td>\r
+<td bgcolor=#66CCCC onMouseOver=View('66CCCC') onClick=Set('66CCCC') height="10px" width="10px"></td>\r
+<td bgcolor=#66FFCC onMouseOver=View('66FFCC') onClick=Set('66FFCC') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#FFFFFF onMouseOver=View('FFFFFF') onClick=Set('FFFFFF') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#0000FF onMouseOver=View('0000FF') onClick=Set('0000FF') height="10px" width="10px"></td>\r
+<td bgcolor=#0033FF onMouseOver=View('0033FF') onClick=Set('0033FF') height="10px" width="10px"></td>\r
+<td bgcolor=#0066FF onMouseOver=View('0066FF') onClick=Set('0066FF') height="10px" width="10px"></td>\r
+<td bgcolor=#0099FF onMouseOver=View('0099FF') onClick=Set('0099FF') height="10px" width="10px"></td>\r
+<td bgcolor=#00CCFF onMouseOver=View('00CCFF') onClick=Set('00CCFF') height="10px" width="10px"></td>\r
+<td bgcolor=#00FFFF onMouseOver=View('00FFFF') onClick=Set('00FFFF') height="10px" width="10px"></td>\r
+<td bgcolor=#3300FF onMouseOver=View('3300FF') onClick=Set('3300FF') height="10px" width="10px"></td>\r
+<td bgcolor=#3333FF onMouseOver=View('3333FF') onClick=Set('3333FF') height="10px" width="10px"></td>\r
+<td bgcolor=#3366FF onMouseOver=View('3366FF') onClick=Set('3366FF') height="10px" width="10px"></td>\r
+<td bgcolor=#3399FF onMouseOver=View('3399FF') onClick=Set('3399FF') height="10px" width="10px"></td>\r
+<td bgcolor=#33CCFF onMouseOver=View('33CCFF') onClick=Set('33CCFF') height="10px" width="10px"></td>\r
+<td bgcolor=#33FFFF onMouseOver=View('33FFFF') onClick=Set('33FFFF') height="10px" width="10px"></td>\r
+<td bgcolor=#6600FF onMouseOver=View('6600FF') onClick=Set('6600FF') height="10px" width="10px"></td>\r
+<td bgcolor=#6633FF onMouseOver=View('6633FF') onClick=Set('6633FF') height="10px" width="10px"></td>\r
+<td bgcolor=#6666FF onMouseOver=View('6666FF') onClick=Set('6666FF') height="10px" width="10px"></td>\r
+<td bgcolor=#6699FF onMouseOver=View('6699FF') onClick=Set('6699FF') height="10px" width="10px"></td>\r
+<td bgcolor=#66CCFF onMouseOver=View('66CCFF') onClick=Set('66CCFF') height="10px" width="10px"></td>\r
+<td bgcolor=#66FFFF onMouseOver=View('66FFFF') onClick=Set('66FFFF') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#FF0000 onMouseOver=View('FF0000') onClick=Set('FF0000') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#990000 onMouseOver=View('990000') onClick=Set('990000') height="10px" width="10px"></td>\r
+<td bgcolor=#993300 onMouseOver=View('993300') onClick=Set('993300') height="10px" width="10px"></td>\r
+<td bgcolor=#996600 onMouseOver=View('996600') onClick=Set('996600') height="10px" width="10px"></td>\r
+<td bgcolor=#999900 onMouseOver=View('999900') onClick=Set('999900') height="10px" width="10px"></td>\r
+<td bgcolor=#99CC00 onMouseOver=View('99CC00') onClick=Set('99CC00') height="10px" width="10px"></td>\r
+<td bgcolor=#99FF00 onMouseOver=View('99FF00') onClick=Set('99FF00') height="10px" width="10px"></td>\r
+<td bgcolor=#CC0000 onMouseOver=View('CC0000') onClick=Set('CC0000') height="10px" width="10px"></td>\r
+<td bgcolor=#CC3300 onMouseOver=View('CC3300') onClick=Set('CC3300') height="10px" width="10px"></td>\r
+<td bgcolor=#CC6600 onMouseOver=View('CC6600') onClick=Set('CC6600') height="10px" width="10px"></td>\r
+<td bgcolor=#CC9900 onMouseOver=View('CC9900') onClick=Set('CC9900') height="10px" width="10px"></td>\r
+<td bgcolor=#CCCC00 onMouseOver=View('CCCC00') onClick=Set('CCCC00') height="10px" width="10px"></td>\r
+<td bgcolor=#CCFF00 onMouseOver=View('CCFF00') onClick=Set('CCFF00') height="10px" width="10px"></td>\r
+<td bgcolor=#FF0000 onMouseOver=View('FF0000') onClick=Set('FF0000') height="10px" width="10px"></td>\r
+<td bgcolor=#FF3300 onMouseOver=View('FF3300') onClick=Set('FF3300') height="10px" width="10px"></td>\r
+<td bgcolor=#FF6600 onMouseOver=View('FF6600') onClick=Set('FF6600') height="10px" width="10px"></td>\r
+<td bgcolor=#FF9900 onMouseOver=View('FF9900') onClick=Set('FF9900') height="10px" width="10px"></td>\r
+<td bgcolor=#FFCC00 onMouseOver=View('FFCC00') onClick=Set('FFCC00') height="10px" width="10px"></td>\r
+<td bgcolor=#FFFF00 onMouseOver=View('FFFF00') onClick=Set('FFFF00') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#00FF00 onMouseOver=View('00FF00') onClick=Set('00FF00') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#990033 onMouseOver=View('990033') onClick=Set('990033') height="10px" width="10px"></td>\r
+<td bgcolor=#993333 onMouseOver=View('993333') onClick=Set('993333') height="10px" width="10px"></td>\r
+<td bgcolor=#996633 onMouseOver=View('996633') onClick=Set('996633') height="10px" width="10px"></td>\r
+<td bgcolor=#999933 onMouseOver=View('999933') onClick=Set('999933') height="10px" width="10px"></td>\r
+<td bgcolor=#99CC33 onMouseOver=View('99CC33') onClick=Set('99CC33') height="10px" width="10px"></td>\r
+<td bgcolor=#99FF33 onMouseOver=View('99FF33') onClick=Set('99FF33') height="10px" width="10px"></td>\r
+<td bgcolor=#CC0033 onMouseOver=View('CC0033') onClick=Set('CC0033') height="10px" width="10px"></td>\r
+<td bgcolor=#CC3333 onMouseOver=View('CC3333') onClick=Set('CC3333') height="10px" width="10px"></td>\r
+<td bgcolor=#CC6633 onMouseOver=View('CC6633') onClick=Set('CC6633') height="10px" width="10px"></td>\r
+<td bgcolor=#CC9933 onMouseOver=View('CC9933') onClick=Set('CC9933') height="10px" width="10px"></td>\r
+<td bgcolor=#CCCC33 onMouseOver=View('CCCC33') onClick=Set('CCCC33') height="10px" width="10px"></td>\r
+<td bgcolor=#CCFF33 onMouseOver=View('CCFF33') onClick=Set('CCFF33') height="10px" width="10px"></td>\r
+<td bgcolor=#FF0033 onMouseOver=View('FF0033') onClick=Set('FF0033') height="10px" width="10px"></td>\r
+<td bgcolor=#FF3333 onMouseOver=View('FF3333') onClick=Set('FF3333') height="10px" width="10px"></td>\r
+<td bgcolor=#FF6633 onMouseOver=View('FF6633') onClick=Set('FF6633') height="10px" width="10px"></td>\r
+<td bgcolor=#FF9933 onMouseOver=View('FF9933') onClick=Set('FF9933') height="10px" width="10px"></td>\r
+<td bgcolor=#FFCC33 onMouseOver=View('FFCC33') onClick=Set('FFCC33') height="10px" width="10px"></td>\r
+<td bgcolor=#FFFF33 onMouseOver=View('FFFF33') onClick=Set('FFFF33') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#0000FF onMouseOver=View('0000FF') onClick=Set('0000FF') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#990066 onMouseOver=View('990066') onClick=Set('990066') height="10px" width="10px"></td>\r
+<td bgcolor=#993366 onMouseOver=View('993366') onClick=Set('993366') height="10px" width="10px"></td>\r
+<td bgcolor=#996666 onMouseOver=View('996666') onClick=Set('996666') height="10px" width="10px"></td>\r
+<td bgcolor=#999966 onMouseOver=View('999966') onClick=Set('999966') height="10px" width="10px"></td>\r
+<td bgcolor=#99CC66 onMouseOver=View('99CC66') onClick=Set('99CC66') height="10px" width="10px"></td>\r
+<td bgcolor=#99FF66 onMouseOver=View('99FF66') onClick=Set('99FF66') height="10px" width="10px"></td>\r
+<td bgcolor=#CC0066 onMouseOver=View('CC0066') onClick=Set('CC0066') height="10px" width="10px"></td>\r
+<td bgcolor=#CC3366 onMouseOver=View('CC3366') onClick=Set('CC3366') height="10px" width="10px"></td>\r
+<td bgcolor=#CC6666 onMouseOver=View('CC6666') onClick=Set('CC6666') height="10px" width="10px"></td>\r
+<td bgcolor=#CC9966 onMouseOver=View('CC9966') onClick=Set('CC9966') height="10px" width="10px"></td>\r
+<td bgcolor=#CCCC66 onMouseOver=View('CCCC66') onClick=Set('CCCC66') height="10px" width="10px"></td>\r
+<td bgcolor=#CCFF66 onMouseOver=View('CCFF66') onClick=Set('CCFF66') height="10px" width="10px"></td>\r
+<td bgcolor=#FF0066 onMouseOver=View('FF0066') onClick=Set('FF0066') height="10px" width="10px"></td>\r
+<td bgcolor=#FF3366 onMouseOver=View('FF3366') onClick=Set('FF3366') height="10px" width="10px"></td>\r
+<td bgcolor=#FF6666 onMouseOver=View('FF6666') onClick=Set('FF6666') height="10px" width="10px"></td>\r
+<td bgcolor=#FF9966 onMouseOver=View('FF9966') onClick=Set('FF9966') height="10px" width="10px"></td>\r
+<td bgcolor=#FFCC66 onMouseOver=View('FFCC66') onClick=Set('FFCC66') height="10px" width="10px"></td>\r
+<td bgcolor=#FFFF66 onMouseOver=View('FFFF66') onClick=Set('FFFF66') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#FFFF00 onMouseOver=View('FFFF00') onClick=Set('FFFF00') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#990099 onMouseOver=View('990099') onClick=Set('990099') height="10px" width="10px"></td>\r
+<td bgcolor=#993399 onMouseOver=View('993399') onClick=Set('993399') height="10px" width="10px"></td>\r
+<td bgcolor=#996699 onMouseOver=View('996699') onClick=Set('996699') height="10px" width="10px"></td>\r
+<td bgcolor=#999999 onMouseOver=View('999999') onClick=Set('999999') height="10px" width="10px"></td>\r
+<td bgcolor=#99CC99 onMouseOver=View('99CC99') onClick=Set('99CC99') height="10px" width="10px"></td>\r
+<td bgcolor=#99FF99 onMouseOver=View('99FF99') onClick=Set('99FF99') height="10px" width="10px"></td>\r
+<td bgcolor=#CC0099 onMouseOver=View('CC0099') onClick=Set('CC0099') height="10px" width="10px"></td>\r
+<td bgcolor=#CC3399 onMouseOver=View('CC3399') onClick=Set('CC3399') height="10px" width="10px"></td>\r
+<td bgcolor=#CC6699 onMouseOver=View('CC6699') onClick=Set('CC6699') height="10px" width="10px"></td>\r
+<td bgcolor=#CC9999 onMouseOver=View('CC9999') onClick=Set('CC9999') height="10px" width="10px"></td>\r
+<td bgcolor=#CCCC99 onMouseOver=View('CCCC99') onClick=Set('CCCC99') height="10px" width="10px"></td>\r
+<td bgcolor=#CCFF99 onMouseOver=View('CCFF99') onClick=Set('CCFF99') height="10px" width="10px"></td>\r
+<td bgcolor=#FF0099 onMouseOver=View('FF0099') onClick=Set('FF0099') height="10px" width="10px"></td>\r
+<td bgcolor=#FF3399 onMouseOver=View('FF3399') onClick=Set('FF3399') height="10px" width="10px"></td>\r
+<td bgcolor=#FF6699 onMouseOver=View('FF6699') onClick=Set('FF6699') height="10px" width="10px"></td>\r
+<td bgcolor=#FF9999 onMouseOver=View('FF9999') onClick=Set('FF9999') height="10px" width="10px"></td>\r
+<td bgcolor=#FFCC99 onMouseOver=View('FFCC99') onClick=Set('FFCC99') height="10px" width="10px"></td>\r
+<td bgcolor=#FFFF99 onMouseOver=View('FFFF99') onClick=Set('FFFF99') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#00FFFF onMouseOver=View('00FFFF') onClick=Set('00FFFF') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#9900CC onMouseOver=View('9900CC') onClick=Set('9900CC') height="10px" width="10px"></td>\r
+<td bgcolor=#9933CC onMouseOver=View('9933CC') onClick=Set('9933CC') height="10px" width="10px"></td>\r
+<td bgcolor=#9966CC onMouseOver=View('9966CC') onClick=Set('9966CC') height="10px" width="10px"></td>\r
+<td bgcolor=#9999CC onMouseOver=View('9999CC') onClick=Set('9999CC') height="10px" width="10px"></td>\r
+<td bgcolor=#99CCCC onMouseOver=View('99CCCC') onClick=Set('99CCCC') height="10px" width="10px"></td>\r
+<td bgcolor=#99FFCC onMouseOver=View('99FFCC') onClick=Set('99FFCC') height="10px" width="10px"></td>\r
+<td bgcolor=#CC00CC onMouseOver=View('CC00CC') onClick=Set('CC00CC') height="10px" width="10px"></td>\r
+<td bgcolor=#CC33CC onMouseOver=View('CC33CC') onClick=Set('CC33CC') height="10px" width="10px"></td>\r
+<td bgcolor=#CC66CC onMouseOver=View('CC66CC') onClick=Set('CC66CC') height="10px" width="10px"></td>\r
+<td bgcolor=#CC99CC onMouseOver=View('CC99CC') onClick=Set('CC99CC') height="10px" width="10px"></td>\r
+<td bgcolor=#CCCCCC onMouseOver=View('CCCCCC') onClick=Set('CCCCCC') height="10px" width="10px"></td>\r
+<td bgcolor=#CCFFCC onMouseOver=View('CCFFCC') onClick=Set('CCFFCC') height="10px" width="10px"></td>\r
+<td bgcolor=#FF00CC onMouseOver=View('FF00CC') onClick=Set('FF00CC') height="10px" width="10px"></td>\r
+<td bgcolor=#FF33CC onMouseOver=View('FF33CC') onClick=Set('FF33CC') height="10px" width="10px"></td>\r
+<td bgcolor=#FF66CC onMouseOver=View('FF66CC') onClick=Set('FF66CC') height="10px" width="10px"></td>\r
+<td bgcolor=#FF99CC onMouseOver=View('FF99CC') onClick=Set('FF99CC') height="10px" width="10px"></td>\r
+<td bgcolor=#FFCCCC onMouseOver=View('FFCCCC') onClick=Set('FFCCCC') height="10px" width="10px"></td>\r
+<td bgcolor=#FFFFCC onMouseOver=View('FFFFCC') onClick=Set('FFFFCC') height="10px" width="10px"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#FF00FF onMouseOver=View('FF00FF') onClick=Set('FF00FF') height="10px" width="10px"></td>\r
+<td bgcolor=#000000 onMouseOver=View('000000') onClick=Set('000000') height="10px" width="10px"></td>\r
+<td bgcolor=#9900FF onMouseOver=View('9900FF') onClick=Set('9900FF') height="10px" width="10px"></td>\r
+<td bgcolor=#9933FF onMouseOver=View('9933FF') onClick=Set('9933FF') height="10px" width="10px"></td>\r
+<td bgcolor=#9966FF onMouseOver=View('9966FF') onClick=Set('9966FF') height="10px" width="10px"></td>\r
+<td bgcolor=#9999FF onMouseOver=View('9999FF') onClick=Set('9999FF') height="10px" width="10px"></td>\r
+<td bgcolor=#99CCFF onMouseOver=View('99CCFF') onClick=Set('99CCFF') height="10px" width="10px"></td>\r
+<td bgcolor=#99FFFF onMouseOver=View('99FFFF') onClick=Set('99FFFF') height="10px" width="10px"></td>\r
+<td bgcolor=#CC00FF onMouseOver=View('CC00FF') onClick=Set('CC00FF') height="10px" width="10px"></td>\r
+<td bgcolor=#CC33FF onMouseOver=View('CC33FF') onClick=Set('CC33FF') height="10px" width="10px"></td>\r
+<td bgcolor=#CC66FF onMouseOver=View('CC66FF') onClick=Set('CC66FF') height="10px" width="10px"></td>\r
+<td bgcolor=#CC99FF onMouseOver=View('CC99FF') onClick=Set('CC99FF') height="10px" width="10px"></td>\r
+<td bgcolor=#CCCCFF onMouseOver=View('CCCCFF') onClick=Set('CCCCFF') height="10px" width="10px"></td>\r
+<td bgcolor=#CCFFFF onMouseOver=View('CCFFFF') onClick=Set('CCFFFF') height="10px" width="10px"></td>\r
+<td bgcolor=#FF00FF onMouseOver=View('FF00FF') onClick=Set('FF00FF') height="10px" width="10px"></td>\r
+<td bgcolor=#FF33FF onMouseOver=View('FF33FF') onClick=Set('FF33FF') height="10px" width="10px"></td>\r
+<td bgcolor=#FF66FF onMouseOver=View('FF66FF') onClick=Set('FF66FF') height="10px" width="10px"></td>\r
+<td bgcolor=#FF99FF onMouseOver=View('FF99FF') onClick=Set('FF99FF') height="10px" width="10px"></td>\r
+<td bgcolor=#FFCCFF onMouseOver=View('FFCCFF') onClick=Set('FFCCFF') height="10px" width="10px"></td>\r
+<td bgcolor=#FFFFFF onMouseOver=View('FFFFFF') onClick=Set('FFFFFF') height="10px" width="10px"></td>\r
+</tr>\r
+</table>\r
+\r
+</body></html>\r
--- /dev/null
+function PopupWin(editor, title, handler, initFunction) {
+ this.editor = editor;
+ this.handler = handler;
+ var dlg = window.open("", "__ha_dialog",
+ "toolbar=no,menubar=no,personalbar=no,width=600,height=600," +
+ "scrollbars=no,resizable=no");
+ this.window = dlg;
+ var doc = dlg.document;
+ this.doc = doc;
+ var self = this;
+
+ var base = document.baseURI || document.URL;
+ if (base && base.match(/(.*)\/([^\/]+)/)) {
+ base = RegExp.$1 + "/";
+ }
+ this.baseURL = base;
+
+ doc.open();
+ var html = "<html><head><title>" + title + "</title>\n";
+ // html += "<base href='" + base + "htmlarea.js' />\n";
+ html += "<style type='text/css'>@import url(" + base + "htmlarea.css);</style></head>\n";
+ html += "<body class='dialog popupwin' id='--HA-body'></body></html>";
+ doc.write(html);
+ doc.close();
+
+ // sometimes I Hate Mozilla... ;-(
+ function init2() {
+ var body = doc.body;
+ if (!body) {
+ setTimeout(init2, 25);
+ return false;
+ }
+ dlg.title = title;
+ doc.documentElement.style.padding = "0px";
+ doc.documentElement.style.margin = "0px";
+ var content = doc.createElement("div");
+ content.className = "content";
+ self.content = content;
+ body.appendChild(content);
+ self.element = body;
+ initFunction(self);
+ dlg.focus();
+ };
+ init2();
+};
+
+PopupWin.prototype.callHandler = function() {
+ var tags = ["input", "textarea", "select"];
+ var params = new Object();
+ for (var ti in tags) {
+ var tag = tags[ti];
+ var els = this.content.getElementsByTagName(tag);
+ for (var j = 0; j < els.length; ++j) {
+ var el = els[j];
+ var val = el.value;
+ if (el.tagName.toLowerCase() == "input") {
+ if (el.type == "checkbox") {
+ val = el.checked;
+ }
+ }
+ params[el.name] = val;
+ }
+ }
+ this.handler(this, params);
+ return false;
+};
+
+PopupWin.prototype.close = function() {
+ this.window.close();
+};
+
+PopupWin.prototype.addButtons = function() {
+ var self = this;
+ var div = this.doc.createElement("div");
+ this.content.appendChild(div);
+ div.className = "buttons";
+ for (var i = 0; i < arguments.length; ++i) {
+ var btn = arguments[i];
+ var button = this.doc.createElement("button");
+ div.appendChild(button);
+ button.innerHTML = HTMLArea.I18N.buttons[btn];
+ switch (btn) {
+ case "ok":
+ button.onclick = function() {
+ self.callHandler();
+ self.close();
+ return false;
+ };
+ break;
+ case "cancel":
+ button.onclick = function() {
+ self.close();
+ return false;
+ };
+ break;
+ }
+ }
+};
+
+PopupWin.prototype.showAtElement = function() {
+ var self = this;
+ // Mozilla needs some time to realize what's goin' on..
+ setTimeout(function() {
+ var w = self.content.offsetWidth + 4;
+ var h = self.content.offsetHeight + 4;
+ // size to content -- that's fuckin' buggy in all fuckin' browsers!!!
+ // so that we set a larger size for the dialog window and then center
+ // the element inside... phuck!
+
+ // center...
+ var el = self.content;
+ var s = el.style;
+ // s.width = el.offsetWidth + "px";
+ // s.height = el.offsetHeight + "px";
+ s.position = "absolute";
+ s.left = (w - el.offsetWidth) / 2 + "px";
+ s.top = (h - el.offsetHeight) / 2 + "px";
+ if (HTMLArea.is_gecko) {
+ self.window.innerWidth = w;
+ self.window.innerHeight = h;
+ } else {
+ self.window.resizeTo(w + 8, h + 35);
+ }
+ }, 25);
+};
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 3.2//EN">
+<html> <head>
+<title>HTMLArea-3.0 Reference</title>
+
+<style type="text/css">
+ @import url(htmlarea.css);
+ body { font: 14px verdana,sans-serif; background: #fff; color: #000; }
+ h1, h2 { font-family:tahoma,sans-serif; }
+ h1 { border-bottom: 2px solid #000; }
+ h2 { border-bottom: 1px solid #aaa; }
+ h3, h4 { margin-bottom: 0px; font-family: Georgia,serif; font-style: italic; }
+ h4 { font-size: 90%; margin-left: 1em; }
+ acronym { border-bottom: 1px dotted #063; color: #063; }
+ p { margin-left: 2em; margin-top: 0.3em; }
+ li p { margin-left: 0px; }
+ .abstract { padding: 5px; margin: 0px 10em; font-size: 90%; border: 1px dashed #aaa; background: #eee;}
+ li { margin-left: 2em; }
+ em { color: #042; }
+ a { color: #00f; }
+ a:hover { color: #f00; }
+ a:active { color: #f80; }
+ span.browser { font-weight: bold; color: #864; }
+ .fixme { font-size: 20px; font-weight: bold; color: red; background: #fab;
+padding: 5px; text-align: center; }
+ .code {
+ background: #e4efff; padding: 5px; border: 1px dashed #abc; margin-left: 2em; margin-right: 2em;
+ font-family: fixed,"lucidux mono","andale mono","courier new",monospace;
+ }
+ .note, .warning { font-weight: bold; color: #0a0; font-variant: small-caps; }
+ .warning { color: #a00; }
+
+.string {
+ color: #06c;
+} /* font-lock-string-face */
+.comment {
+ color: #840;
+} /* font-lock-comment-face */
+.variable-name {
+ color: #000;
+} /* font-lock-variable-name-face */
+.type {
+ color: #008;
+ font-weight: bold;
+} /* font-lock-type-face */
+.reference {
+ color: #048;
+} /* font-lock-reference-face */
+.preprocessor {
+ color: #808;
+} /* font-lock-preprocessor-face */
+.keyword {
+ color: #00f;
+ font-weight: bold;
+} /* font-lock-keyword-face */
+.function-name {
+ color: #044;
+} /* font-lock-function-name-face */
+.html-tag {
+ font-weight: bold;
+} /* html-tag-face */
+.html-helper-italic {
+ font-style: italic;
+} /* html-helper-italic-face */
+.html-helper-bold {
+ font-weight: bold;
+} /* html-helper-bold-face */
+
+</style>
+
+<script type="text/javascript" src="htmlarea.js"></script>
+<script type="text/javascript" src="dialog.js"></script>
+<script tyle="text/javascript" src="lang/en.js"></script>
+
+</head>
+
+<body onload="HTMLArea.replace('TA')">
+
+
+<h1>HTMLArea-3.0 Documentation</h1>
+
+<div class="abstract" style="color: red; font-weight: bold">
+
+ This documentation contains valid information, but is outdated in the
+ terms that it does not covers all the features of HTMLArea. A new
+ documentation project will be started, based on LaTeX.
+
+</div>
+
+
+<h2>Introduction</h2>
+
+<h3>What is HTMLArea?</h3>
+
+<p>HTMLArea is a free <acronym title="What You See Is What You Get"
+>WYSIWYG</acronym> editor replacement for <code><textarea></code>
+fields. By adding a few simple lines of JavaScript code to your web page
+you can replace a regular textarea with a rich text editor that lets your
+users do the following:</p>
+
+<ul>
+ <li>Format text to be bold, italicized, or underlined.</li>
+ <li>Change the face, size, style and color.</li>
+ <li>Left, center, or right-justify paragraphs.</li>
+ <li>Make bulleted or numbered lists.</li>
+ <li>Indent or un-indent paragraphs.</li>
+ <li>Insert a horizontal line.</li>
+ <li>Insert hyperlinks and images.</li>
+ <li>View the raw HTML source of what they're editing.</li>
+ <li>and much more...</li>
+</ul>
+
+<p>Some of the interesting features of HTMLArea that set's it apart from
+other web based WYSIWYG editors are as follows:</p>
+
+<ul>
+ <li>It's lightweight, fast loading and can transform a regular textarea
+ into a rich-text editor with a single line of JavaScript.</li>
+ <li>Generates clean, valid HTML.</li>
+ <li>It's 100% backwards compatible with older or non-supported browsers
+ (they get the original textarea field).</li>
+ <li>It's free and can be incorporated into any free or commercial
+ program.</li>
+ <li>It works with any server-side languages (ASP, PHP, Perl, Java,
+ etc).</li>
+ <li>It's written in JavaScript and can be easily viewed, modified or
+ extended.</li>
+ <li>It remembers entered content when a user navigates away and then hits
+ "back" in their browser.</li>
+ <li>Since it replaces existing textareas it doesn't require a lot of code
+ to add it to your pages (just one line).</li>
+ <li>Did we mention it was free? ;-)</li>
+</ul>
+
+<h3>Is it really free? What's the catch?</h3>
+
+<p>Yes! It's really free. You can use it, modify it, distribute it with your
+software, or do just about anything you like with it.</p>
+
+<h3>What are the browser requirements?</h3>
+
+<p>HTMLArea requires <span class="browser"><a
+href="http://www.microsoft.com/ie">Internet Explorer</a> >= 5.5</span>
+(Windows only), or <span class="browser"><a
+href="http://mozilla.org">Mozilla</a> >= 1.3-Beta</span> on any platform.
+Any browser based on <a href="http://mozilla.org/newlayout">Gecko</a> will
+also work, provided that Gecko version is at least the one included in
+Mozilla-1.3-Beta (for example, <a
+href="http://galeon.sf.net">Galeon-1.2.8</a>). However, it is backwards
+compatible with other browsers. They will get a regular textarea field
+instead of a WYSIWYG editor.</p>
+
+<h3>Can I see an example of what it looks like?</h3>
+
+<p>Just make sure you're using one of the browsers mentioned above and see
+below.</p>
+
+<form onsubmit="return false;">
+<textarea id="TA" style="width: 100%; height: 15em;">
+<p>Here is some sample text in textarea that's been transformed with <font
+color="#0000CC"><b>HTMLArea</b></font>.<br />
+You can make things <b>bold</b>, <i>italic</i>, <u>underline</u>. You can change the
+<font size="3">size</font> and <b><font color="#0000CC">c</font><font color="#00CC00">o</font><font color="#00CCCC">l</font><font color="#CC0000">o</font><font color="#CC00CC">r</font><font color="#CCCC00">s</font><font color="#CCCCCC">!</font></b>
+And lots more...</p>
+
+<p align="center"><font size="4" color="#ff0000"><b><u>Try HTMLArea
+today!</u></b></font><br /></p>
+</textarea>
+</form>
+
+<h3>Where can I find out more info, download the latest version and talk to
+other HTMLArea users?</h3>
+
+<p>You can find out more about HTMLArea and download the latest version on
+the <a href="http://www.interactivetools.com/products/htmlarea/">HTMLArea
+homepage</a> and you can talk to other HTMLArea users and post any comments
+or suggestions you have in the <a
+href="http://www.interactivetools.com/iforum/Open_Source_C3/htmlArea_v3.0_-_Alpha_Release_F14/"
+>HTMLArea forum</a>.</p>
+
+<h2>Keyboard shortcuts</h2>
+
+<p>The editor provides the following key combinations:</p>
+
+<ul>
+ <li>CTRL-A -- select all</li>
+ <li>CTRL-B -- bold</li>
+ <li>CTRL-I -- italic</li>
+ <li>CTRL-U -- underline</li>
+ <li>CTRL-S -- strikethrough</li>
+ <li>CTRL-L -- justify left</li>
+ <li>CTRL-E -- justify center</li>
+ <li>CTRL-R -- justify right</li>
+ <li>CTRL-J -- justify full</li>
+ <li>CTRL-1 .. CTRL-6 -- headings (<h1> .. <h6>)</li>
+</ul>
+
+<h2>Installation</h2>
+
+<h3>How do I add HTMLArea to my web page?</h3>
+
+<p>It's easy. First you need to upload HTMLArea files to your website.
+Just follow these steps.</p>
+
+<ol>
+ <li>Download the latest version from the <a
+ href="http://www.interactivetools.com/products/htmlarea/">htmlArea
+ homepage</a>.</li>
+ <li>Unzip the files onto your local computer (making sure to maintain the
+ directory structure contained in the zip).</li>
+ <li>Create a new folder on your website called /htmlarea/ (make sure it's
+ NOT inside the cgi-bin).</li>
+ <li>Transfer all the HTMLArea files from your local computer into the
+ /htmlarea/ folder on your website.</li>
+ <li>Open the example page /htmlarea/example.html with your browser to make
+ sure everything works.</li>
+</ol>
+
+<p>Once htmlArea is on your website all you need to do is add some
+JavaScript to any pages that you want to add WYSIWYG editors to. Here's how
+to do that.</p>
+
+<ol>
+
+ <li>Include the "htmlarea.js" script:
+ <pre class="code"
+ ><span class="function-name"><</span><span class="html-tag">script</span> <span class="variable-name">type=</span><span class="string">"text/javascript"</span> <span class="variable-name">src=</span><span class="string">"/htmlarea/htmlarea.js"</span><span class="function-name">></span><span class="paren-face-match"><</span><span class="html-tag">/script</span><span class="paren-face-match">></span></pre>
+ </li>
+
+ <li>If you are using popup dialogs, i.e. for insert table, insert image,
+ select color, then you need to include the "dialog.js" file. This is
+ recommended anyway.
+ <pre class="code"
+ ><span class="function-name"><</span><span class="html-tag">script</span> <span class="variable-name">type=</span><span class="string">"text/javascript"</span> <span class="variable-name">src=</span><span class="string">"/htmlarea/dialog.js"</span><span class="paren-face-match">></span><span class="function-name"><</span><span class="html-tag">/script</span><span class="function-name">></span></pre>
+ </li>
+
+ <li>Include the corresponding language definition file. <span
+ class="note">Note</span>:
+ internationalization is available only since version 3.0. Check the files
+ containing "lang" in the distribution ZIP. If your preferred language is
+ not there yet and you decide to write it, please consider sending it to
+ us so that it gets included in the next release.
+ <pre class="code"
+ ><span class="function-name"><</span><span class="html-tag">script</span> <span class="variable-name">type=</span><span class="string">"text/javascript"</span> <span class="variable-name">src=</span><span class="string">"/htmlarea/lang/en.js"</span><span class="function-name">></span><span class="paren-face-match"><</span><span class="html-tag">/script</span><span class="paren-face-match">></span></pre>
+
+ <li>Include the stylesheet (be sure to put this inside the HEAD tag):
+ <pre class="code"
+ ><span class="function-name"><</span><span class="html-tag">style</span> <span class="variable-name">type=</span><span class="string">"text/css"</span><span class="function-name">></span>@import url<span class="function-name">(</span>/htmlarea/htmlarea.css<span class="function-name">)</span><span class="paren-face-match"><</span><span class="html-tag">/style</span><span class="paren-face-match">></span></pre>
+ </li>
+
+ <li><p>If you want to change all your <textarea>-s into
+ HTMLArea-s then you can use the simplest way to create HTMLArea:</p>
+ <pre class="code"
+ ><span class="function-name"><</span><span class="html-tag">script</span> <span class="variable-name">type=</span><span class="string">"text/javascript"</span> <span class="variable-name">defer=</span><span class="string">"1"</span><span class="function-name">></span>
+ HTMLArea.replaceAll<span class="function-name">()</span>;
+<span class="paren-face-match"><</span><span class="html-tag">/script</span><span class="paren-face-match">></span></pre>
+ <p><span class="note">Note:</span> you can also add the
+ <code>HTMLArea.replaceAll()</code> code to the <code>onload</code>
+ event handler for the <code>body</code> element, if you find it more appropriate.</p>
+
+ <p>A different approach, if you have more than one textarea and only want
+ to change one of them, is to use <code>HTMLArea.replace("id")</code> --
+ pass the <code>id</code> of your textarea. Do not use the
+ <code>name</code> attribute anymore, it's not a standard solution!</p>
+
+</ol>
+
+<h3>I want to change the editor settings, how do I do that?</h3>
+
+<p>While it's true that all you need is one line of JavaScript to create an
+htmlArea WYSIWYG editor, you can also specify more config settings in the
+code to control how the editor works and looks. Here's an example of some of
+the available settings:</p>
+
+<pre class="code"
+><span class="keyword">var</span> <span class="variable-name">config</span> = <span class="keyword">new</span> HTMLArea.Config(); <span class="comment">// create a new configuration object
+</span> <span class="comment">// having all the default values
+</span>config.width = '<span class="string">90%</span>';
+config.height = '<span class="string">200px</span>';
+
+<span class="comment">// the following sets a style for the page body (black text on yellow page)
+// and makes all paragraphs be bold by default
+</span>config.pageStyle =
+ '<span class="string">body { background-color: yellow; color: black; font-family: verdana,sans-serif } </span>' +
+ '<span class="string">p { font-width: bold; } </span>';
+
+<span class="comment">// the following replaces the textarea with the given id with a new
+// HTMLArea object having the specified configuration
+</span>HTMLArea.replace('<span class="string">id</span>', config);</pre>
+
+<p><span class="warning">Important:</span> It's recommended that you add
+custom features and configuration to a separate file. This will ensure you
+that when we release a new official version of HTMLArea you'll have no
+trouble upgrading it.</p>
+
+<h3>How do I customize the toolbar?</h3>
+
+<p>Using the configuration object introduced above allows you to completely
+control what the toolbar contains. Following is an example of a one-line,
+customized toolbar, much simpler than the default one:</p>
+
+<pre class="code"
+><span class="keyword">var</span> <span class="variable-name">config</span> = <span class="keyword">new</span> HTMLArea.Config();
+config.toolbar = [
+ ['<span class="string">fontname</span>', '<span class="string">space</span>',
+ '<span class="string">fontsize</span>', '<span class="string">space</span>',
+ '<span class="string">formatblock</span>', '<span class="string">space</span>',
+ '<span class="string">bold</span>', '<span class="string">italic</span>', '<span class="string">underline</span>']
+];
+HTMLArea.replace('<span class="string">id</span>', config);</pre>
+
+<p>The toolbar is an Array of Array objects. Each array in the toolbar
+defines a new line. The default toolbar looks like this:</p>
+
+<pre class="code"
+>config.toolbar = [
+[ "<span class="string">fontname</span>", "<span class="string">space</span>",
+ "<span class="string">fontsize</span>", "<span class="string">space</span>",
+ "<span class="string">formatblock</span>", "<span class="string">space</span>",
+ "<span class="string">bold</span>", "<span class="string">italic</span>", "<span class="string">underline</span>", "<span class="string">separator</span>",
+ "<span class="string">strikethrough</span>", "<span class="string">subscript</span>", "<span class="string">superscript</span>", "<span class="string">separator</span>",
+ "<span class="string">copy</span>", "<span class="string">cut</span>", "<span class="string">paste</span>", "<span class="string">space</span>", "<span class="string">undo</span>", "<span class="string">redo</span>" ],
+
+[ "<span class="string">justifyleft</span>", "<span class="string">justifycenter</span>", "<span class="string">justifyright</span>", "<span class="string">justifyfull</span>", "<span class="string">separator</span>",
+ "<span class="string">insertorderedlist</span>", "<span class="string">insertunorderedlist</span>", "<span class="string">outdent</span>", "<span class="string">indent</span>", "<span class="string">separator</span>",
+ "<span class="string">forecolor</span>", "<span class="string">hilitecolor</span>", "<span class="string">textindicator</span>", "<span class="string">separator</span>",
+ "<span class="string">inserthorizontalrule</span>", "<span class="string">createlink</span>", "<span class="string">insertimage</span>", "<span class="string">inserttable</span>", "<span class="string">htmlmode</span>", "<span class="string">separator</span>",
+ "<span class="string">popupeditor</span>", "<span class="string">separator</span>", "<span class="string">showhelp</span>", "<span class="string">about</span>" ]
+];</pre>
+
+<p>Except three strings, all others in the examples above need to be defined
+in the <code>config.btnList</code> object (detailed a bit later in this
+document). The three exceptions are: 'space', 'separator' and 'linebreak'.
+These three have the following meaning, and need not be present in
+<code>btnList</code>:</p>
+
+<ul>
+ <li>'space' -- Inserts a space of 5 pixels (the width is configurable by external
+ <acronym title="Cascading Style Sheets">CSS</acronym>) at the current
+ position in the toolbar.</li>
+ <li>'separator' -- Inserts a small vertical separator, for visually grouping related
+ buttons.</li>
+ <li>'linebreak' -- Starts a new line in the toolbar. Subsequent controls will be
+ inserted on the new line.</li>
+</ul>
+
+<p><span class="warning">Important:</span> It's recommended that you add
+custom features and configuration to a separate file. This will ensure you
+that when we release a new official version of HTMLArea you'll have no
+trouble upgrading it.</p>
+
+<h3>How do I create custom buttons?</h3>
+
+<p>By design, the toolbar is easily extensible. For adding a custom button
+one needs to follow two steps.</p>
+
+<h4 id="regbtn">1. Register the button in <code>config.btnList</code>.</h4>
+
+<p>For each button in the toolbar, HTMLArea needs to know the following
+information:</p>
+<ul>
+ <li>a name for it (we call it the ID of the button);</li>
+ <li>the path to an image to be displayed in the toolbar;</li>
+ <li>a tooltip for it;</li>
+ <li>whether the button is enabled or not in text mode;</li>
+ <li>what to do when the button is clicked;</li>
+</ul>
+<p>You need to provide all this information for registering a new button
+too. The button ID can be any string identifier and it's used when
+defining the toolbar, as you saw above. We recommend starting
+it with "my-" so that it won't clash with the standard ID-s (those from
+the default toolbar).</p>
+
+<p class="note">Register button example #1</p>
+
+<pre class="code"
+><span class="comment">// get a default configuration
+</span><span class="keyword">var</span> <span class="variable-name">config</span> = <span class="keyword">new</span> HTMLArea.Config();
+<span class="comment">// register the new button using Config.registerButton.
+// parameters: button ID, tooltip, image, textMode,
+</span>config.registerButton("<span class="string">my-hilite</span>", "<span class="string">Highlight text</span>", "<span class="string">my-hilite.gif</span>", <span class="keyword">false</span>,
+<span class="comment">// function that gets called when the button is clicked
+</span> <span class="keyword">function</span>(editor, id) {
+ editor.surroundHTML('<span class="string"><span class="hilite"></span>', '<span class="string"></span></span>');
+ }
+);</pre>
+
+<p>An alternate way of calling registerButton is exemplified above. Though
+the code might be a little bit larger, using this form makes your code more
+maintainable. It doesn't even needs comments as it's pretty clear.</p>
+
+<p class="note">Register button example #2</p>
+
+<pre class="code"
+><span class="keyword">var</span> <span class="variable-name">config</span> = <span class="keyword">new</span> HTMLArea.Config();
+config.registerButton({
+ id : "<span class="string">my-hilite</span>",
+ tooltip : "<span class="string">Highlight text</span>",
+ image : "<span class="string">my-hilite.gif</span>",
+ textMode : <span class="keyword">false</span>,
+ action : <span class="keyword">function</span>(editor, id) {
+ editor.surroundHTML('<span class="string"><span class="hilite"></span>', '<span class="string"></span></span>');
+ }
+});</pre>
+
+<p>You might notice that the "action" function receives two parameters:
+<b>editor</b> and <b>id</b>. In the examples above we only used the
+<b>editor</b> parameter. But it could be helpful for you to understand
+both:</p>
+
+<ul>
+ <li><b>editor</b> is a reference to the HTMLArea object. Since our entire
+ code now has an <acronym title="Object Oriented Programming">OOP</acronym>-like
+ design, you need to have a reference to
+ the editor object in order to do things with it. In previous versions of
+ HTMLArea, in order to identify the object an ID was used -- the ID of the
+ HTML element. In this version ID-s are no longer necessary.</li>
+
+ <li><b>id</b> is the button ID. Wondering why is this useful? Well, you
+ could use the same handler function (presuming that it's not an anonymous
+ function like in the examples above) for more buttons. You can <a
+ href="#btnex">see an example</a> a bit later in this document.</li>
+</ul>
+
+<h4>2. Inserting it into the toolbar</h4>
+
+<p>At this step you need to specify where in the toolbar to insert the
+button, or just create the whole toolbar again as you saw in the previous
+section. You use the button ID, as shown in the examples of customizing the
+toolbar in the previous section.</p>
+
+<p>For the sake of completion, following there are another examples.</p>
+
+<p class="note">Append your button to the default toolbar</p>
+
+<pre class="code"
+>config.toolbar.push([ "<span class="string">my-hilite</span>" ]);</pre>
+
+<p class="note">Customized toolbar</p>
+
+<pre class="code"
+>config.toolbar = [
+ ['<span class="string">fontname</span>', '<span class="string">space</span>',
+ '<span class="string">fontsize</span>', '<span class="string">space</span>',
+ '<span class="string">formatblock</span>', '<span class="string">space</span>',
+ '<span class="string">separator</span>', '<span class="string">my-hilite</span>', '<span class="string">separator</span>', '<span class="string">space</span>', <span class="comment">// here's your button
+</span> '<span class="string">bold</span>', '<span class="string">italic</span>', '<span class="string">underline</span>', '<span class="string">space</span>']
+];</pre>
+
+<p><span class="note">Note:</span> in the example above our new button is
+between two vertical separators. But this is by no means required. You can
+put it wherever you like. Once registered in the btnList (<a
+href="#regbtn">step 1</a>) your custom button behaves just like a default
+button.</p>
+
+<p><span class="warning">Important:</span> It's recommended that you add
+custom features and configuration to a separate file. This will ensure you
+that when we release a new official version of HTMLArea you'll have no
+trouble upgrading it.</p>
+
+<h4 id="btnex">A complete example</h4>
+
+<p>Please note that it is by no means necessary to include the following
+code into the htmlarea.js file. On the contrary, it might not work there.
+The configuration system is designed such that you can always customize the
+editor <em>from outside files</em>, thus keeping the htmlarea.js file
+intact. This will make it easy for you to upgrade your HTMLArea when we
+release a new official version. OK, I promise it's the last time I said
+this. ;)</p>
+
+<pre class="code"
+><span class="comment">// All our custom buttons will call this function when clicked.
+// We use the <b>buttonId</b> parameter to determine what button
+// triggered the call.
+</span><span class="keyword">function</span> <span class="function-name">clickHandler</span>(editor, buttonId) {
+ <span class="keyword">switch</span> (buttonId) {
+ <span class="keyword">case</span> "<span class="string">my-toc</span>":
+ editor.insertHTML("<span class="string"><h1>Table Of Contents</h1></span>");
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> "<span class="string">my-date</span>":
+ editor.insertHTML((<span class="keyword">new</span> Date()).toString());
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> "<span class="string">my-bold</span>":
+ editor.execCommand("<span class="string">bold</span>");
+ editor.execCommand("<span class="string">italic</span>");
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> "<span class="string">my-hilite</span>":
+ editor.surroundHTML("<span class="string"><span class=\"hilite\"></span>", "<span class="string"></span></span>");
+ <span class="keyword">break</span>;
+ }
+};
+
+<span class="comment">// Create a new configuration object
+</span><span class="keyword">var</span> <span class="variable-name">config</span> = <span class="keyword">new</span> HTMLArea.Config();
+
+<span class="comment">// Register our custom buttons
+</span>config.registerButton("<span class="string">my-toc</span>", "<span class="string">Insert TOC</span>", "<span class="string">my-toc.gif</span>", <span class="keyword">false</span>, clickHandler);
+config.registerButton("<span class="string">my-date</span>", "<span class="string">Insert date/time</span>", "<span class="string">my-date.gif</span>", <span class="keyword">false</span>, clickHandler);
+config.registerButton("<span class="string">my-bold</span>", "<span class="string">Toggle bold/italic</span>", "<span class="string">my-bold.gif</span>", <span class="keyword">false</span>, clickHandler);
+config.registerButton("<span class="string">my-hilite</span>", "<span class="string">Hilite selection</span>", "<span class="string">my-hilite.gif</span>", <span class="keyword">false</span>, clickHandler);
+
+<span class="comment">// Append the buttons to the default toolbar
+</span>config.toolbar.push(["<span class="string">linebreak</span>", "<span class="string">my-toc</span>", "<span class="string">my-date</span>", "<span class="string">my-bold</span>", "<span class="string">my-hilite</span>"]);
+
+<span class="comment">// Replace an existing textarea with an HTMLArea object having the above config.
+</span>HTMLArea.replace("<span class="string">textAreaID</span>", config);</pre>
+
+
+<hr />
+<address>© <a href="http://interactivetools.com" title="Visit our website"
+>InteractiveTools.com</a> 2002, 2003.
+<br />
+HTMLArea v3.0 developed by <a
+href="http://students.infoiasi.ro/~mishoo">Mihai Bazon</a> for
+InteractiveTools.com.
+<br />
+Documentation written by Mihai Bazon.
+</address>
+<!-- hhmts start -->
+Last modified on Sun Aug 3 16:11:23 2003
+<!-- hhmts end -->
+<!-- doc-lang: English -->
+</body> </html>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 3.2//EN">
+<html>
+ <head>
+ <title>HTMLArea-3.0-beta release notes</title>
+ </head>
+
+ <body>
+
+ <h1>HTMLArea-3.0-beta release notes</h1>
+
+ <p>This release was compiled on Aug 11, 2003 [21:30] GMT.</p>
+
+
+ <p>Changes since 3.0-Alpha:</p>
+
+ <ul>
+
+ <li>Performance improvements.</li>
+
+ <li>Many bugs fixed.</li>
+
+ <li>Plugin infrastructure.</li>
+
+ <li>TableOperations plugin.</li>
+
+ <li>SpellChecker plugin.</li>
+
+ <li>Status bar.</li>
+
+ <li>API for registering custom buttons and drop-down boxes in the
+ toolbar.</li>
+
+ <li>Toolbar can contain text labels.</li>
+
+ <li>Cut, copy, paste, undo, redo buttons.</li>
+
+ </ul>
+
+ <h2>Rationale for Beta</h2>
+
+ <p>Why was this released as "Beta"? The code is quite stable and it
+ didn't deserve a "Beta" qualification. However, there are some things
+ left to do for the real 3.0 version. These things will not affect the
+ API to work with HTMLArea, in other words, you can install the Beta
+ right now and then install the final release without modifying your
+ code. That's if you don't modify HTMLArea itself. ;-)</p>
+
+ <h2>To-Do before 3.0 final</h2>
+
+ <ol>
+
+ <li>We should use a single popup interface. Currently there are two:
+ dialog.js and popupwin.js; dialog.js emulates modal dialogs, which
+ sucks when you want to open "select-color" from another popup and not
+ from the editor itself. Very buggy in IE. We should probably use only
+ modeless dialogs (that is, popupwin.js).</li>
+
+ <li>Internationalization for the SpellChecker plugin.</li>
+
+ <li>Internationalization for the TableOperations plugin.</li>
+
+ <li>People who sent translations are invited to re-iterate through
+ their work and make it up-to-date with lang/en.js which is the main
+ lang file for HTMLArea-3.0. Some things have changed but not all
+ translations are updated.</li>
+
+ <li><strong>Documentation</strong>.</li>
+
+ </ol>
+
+
+ <hr />
+ <address><a href="http://students.infoiasi.ro/~mishoo/">Mihai Bazon</a></address>
+<!-- Created: Sun Aug 3 16:55:08 EEST 2003 -->
+<!-- hhmts start -->
+Last modified on Sun Aug 10 19:31:39 2003
+<!-- hhmts end -->
+<!-- doc-lang: English -->
+ </body>
+</html>
+
+
--- /dev/null
+// All our custom buttons will call this function when clicked.
+// We use the buttonId parameter to determine what button
+// triggered the call.
+function clickHandler(editor, buttonId) {
+ switch (buttonId) {
+ case "my-toc":
+ editor.insertHTML("<h1>Table Of Contents</h1>");
+ break;
+ case "my-date":
+ editor.insertHTML((new Date()).toString());
+ break;
+ }
+};
+
+// Create a new configuration object
+var config = new HTMLArea.Config();
+
+// Register our custom buttons
+config.registerButton("my-toc", "Insert TOC", "images/em.icon.smile.gif", false, clickHandler);
+config.registerButton("my-date", "Insert date/time", "icon_ins_char.gif", false, clickHandler);
+
+// Append the buttons to the default toolbar
+config.toolbar.push(["linebreak", "my-toc", "my-date"]);
+
+// Replace an existing textarea with an HTMLArea object having the above config.
+HTMLArea.replace("TA", config);
\ No newline at end of file