/* KOIVI TTW WYSIWYG Editor Copyright (C) 2005 Justin Koivisto Version 3.2.4 Last Modified: 4/3/2006 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 Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Full license agreement notice can be found in the LICENSE file contained within this distribution package. Justin Koivisto justin.koivisto@gmail.com http://koivi.com */ /** * WYSIWYG_Editor * * Class constructor. Configures and displays the editor object according to values passed * This is an implementation of OO-Javascript based on my previous editor. * * @param instance_name string the name of the variable you assigned to this instance ( example: myEdt = new WYSIWYG_Editor('myEdt'); ) * also used as the basis for the name and id attributes for this editor instance in the HTML (hidden input and iframe) * @param content string a string of the content to display in the editor. * @param path string the URI path to this directory to use for editor components (pallete, iamges, etc.) * @param fwidth int the width in pixels of the editor interface on the screen * @param fheight int the height in pixels of the editor interface on the screen * @param styleHref string the URI of the stylesheet to use for the editor's content window * @param spellCheckPath string the URI of the spellerpages install **/ smilies = new Array(); smilies[0] = Array( ':D', 'icon_biggrin.gif', 'Very Happy' ); smilies[1] = Array( ':)', 'icon_smile.gif', 'Smile' ); smilies[2] = Array( ':(', 'icon_sad.gif', 'Sad' ); smilies[3] = Array( ':o', 'icon_surprised.gif', 'Surprised' ); smilies[4] = Array( ':shock:', 'icon_eek.gif', 'Shocked' ); smilies[5] = Array( ':?', 'icon_confused.gif', 'Confused' ); smilies[6] = Array( '8)', 'icon_cool.gif', 'Cool' ); smilies[7] = Array( ':lol:', 'icon_lol.gif', 'Laughing' ); smilies[8] = Array( ':x', 'icon_mad.gif', 'Mad' ); smilies[9] = Array( ':P', 'icon_razz.gif', 'Razz' ); smilies[10] = Array( ':oops:', 'icon_redface.gif', 'Embarassed' ); smilies[11] = Array( ':cry:', 'icon_cry.gif', 'Crying or Very sad' ); smilies[12] = Array( ':evil:', 'icon_evil.gif', 'Evil or Very Mad' ); smilies[13] = Array( ':twisted:', 'icon_twisted.gif', 'Twisted Evil' ); smilies[14] = Array( ':roll:', 'icon_rolleyes.gif', 'Rolling Eyes' ); smilies[15] = Array( ':wink:', 'icon_wink.gif', 'Wink' ); smilies[16] = Array( ':!:', 'icon_exclaim.gif', 'Exclamation' ); smilies[17] = Array( ':?:', 'icon_question.gif', 'Question' ); smilies[18] = Array( ':idea:', 'icon_idea.gif', 'Idea' ); smilies[19] = Array( ':arrow:', 'icon_arrow.gif', 'Arrow' ); smilies[20] = Array( ':|', 'icon_neutral.gif', 'Neutral' ); smilies[21] = Array( ':mrgreen:', 'icon_mrgreen.gif', 'Mr. Green' ); smilies[22] = Array( ':pirate:', 'icon_pirate.gif', 'Pirate' ); smilies[23] = Array( ':noparking:', 'icon_noparking.gif', 'No Parking' ); smilies[24] = Array( ':dollar:', 'icon_dollar.gif', 'Dollar' ); function WYSIWYG_Editor(instance_name, hidden_name, content, path, fwidth, fheight, styleHref, spellCheckPath){ // for each of the passed parameters, we need to be sure they are defined, or we will use a default value // the name used to create the object - used to define the name of the field that contains the HTML on submission if(typeof(instance_name)=='undefined'){ alert("ERROR: No instance name was passed for the editor."); return false; }else{ this.instance_name=instance_name; } // the initial HTML source content for the editor if(typeof(content)=='undefined'){ this.content=''; }else{ this.content=content; } // define the path to use for the editor components like images if(typeof(path)=='undefined'){ this.wysiwyg_path=''; // default value }else{ path.replace(/[\/\\]$/,''); // remove trailing slashes this.wysiwyg_path=path; } // define the pixel dimensions of the editor if(typeof(fwidth)=='number' && Math.round(fwidth) > 50){ this.frame_width=Math.round(fwidth); // default value }else{ this.frame_width=548; // default width } if(typeof(fheight)=='number' && Math.round(fheight) > 50){ this.frame_height=Math.round(fheight); }else{ this.frame_height=250; // default height } // define the stylesheet to use for the editor components like images if(typeof(styleHref)=='undefined'){ this.stylesheet=''; // default value }else{ this.stylesheet=styleHref; } if(typeof(spellCheckPath)=='undefined'){ this.spell_path=''; // default value }else{ // show spell check button requires Speller Pages (http://spellerpages.sourceforge.net/) this.spell_path=spellCheckPath; } // properties that depended on the validated values above this.wysiwyg_content=this.instance_name+'_WYSIWYG_Editor'; // the editor IFRMAE element id this.wysiwyg_hidden=hidden_name; // the editor's hidden field to store the HTML in for the post this.wysiwyg_speller=this.instance_name+'_speller'; // the editor's hidden textarea to store the HTML in for the spellchecker this.ta_rows=Math.round(this.frame_height/15); // number of rows for textarea for unsupported browsers this.ta_cols=Math.round(this.frame_width/8); // number of cols for textarea for unsupported browsers // other property defaults this.viewMode=1; // by default, set to design view this._X = this._Y = 0; // these are used to determine mouse position when clicked // the folloing properties are safe to set through the object variable, for example: // var editor = new WYSIWYG_Editor('editor'); // editor.allow_mode_toggle = false; // below are just the defaults that I use most of the time // insert table defaults this.table_border = 1; // default border used when inserting tables this.table_cell_padding = 3; // default cellpadding used when inserting tables this.table_cell_spacing = 0; // default cellspacing used when inserting tables // tool bar display this.allow_mode_toggle = false; // allow users to switch to source code mode this.font_format_toolbar1 = true; // buttons for font family, size, and style this.font_format_toolbar2 = true; // buttons for font color and background-color this.font_format_toolbar3 = true; // buttons for bold, italic, underline this.font_format_toolbar4 = true; // buttons for superscript, subscript this.alignment_toolbar1 = true; // buttons for left, center, right, full justify this.alignment_toolbar2 = true; // buttons for indent, outdent this.web_toolbar1 = true; // buttons for add, remove hyperlinks this.web_toolbar2 = true; // buttons for ordered, unordered lists this.web_toolbar3 = true; // buttons for horizontal rule, insert table this.web_toolbar4 = false; // buttons for insert image and save (submit form) this.web_toolbar5 = true; // buttons for smilies this.misc_format_toolbar = false; // buttons for remove format, copy, paste, redo, undo, etc. // this button is not implemented on the version for koivi.com // it is only currently available in WAFCMS (being developed as a // proprietary CMS currently). If enabled, an insert-image button // will appear in web_toolbar4 which calls the InsertImage method this.image_button = false; // this makes a save icon that submits the form in web_toolbar4 this.save_button = false; this.smilies_button = true; // what kind of separator to use after each toolbar // "br" is a line break, "|" is an image separator, false is nothing this.font_format_toolbar1_after = false; this.font_format_toolbar2_after = '|'; this.font_format_toolbar3_after = '|'; this.font_format_toolbar4_after = false; this.alignment_toolbar1_after = '|'; this.alignment_toolbar2_after = '|'; this.web_toolbar1_after = '|'; this.web_toolbar2_after = '|'; this.web_toolbar3_after = '|'; this.web_toolbar4_after = false; this.web_toolbar5_after = false; this.misc_format_toolbar_after = false; } /** * WYSIWYG_Editor::prepareSubmit * * Use this in the onSubmit event for the form that the editor is displayed inside. * Puts the HTML content into a hidden form field for the submission **/ WYSIWYG_Editor.prototype.prepareSubmit = function (){ if(this.viewMode == 2){ // be sure this is in design view before submission this.toggleMode(); } var htmlCode=document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML; document.getElementById(this.wysiwyg_hidden).value=htmlCode; return true; } /** * WYSIWYG_Editor::display * * Display the editor interface for the user **/ WYSIWYG_Editor.prototype.display = function (){ if(this.isSupported()){ this._display_editor(); this._load_content(); var thedoc = document.getElementById(this.wysiwyg_content).contentWindow.document; thedoc.designMode='On'; // MSIE has caching problems... // http://technet2.microsoft.com/WindowsServer/en/Library/8e06b837-0027-4f47-95d6-0a60579904bc1033.mspx thedoc = document.getElementById(this.wysiwyg_content).contentWindow.document; thedoc.open(); thedoc.write(''); if(this.stylesheet){ // must be done after the document has been opened thedoc.write(''); } thedoc.write(''); thedoc.write(this.content); thedoc.write(''); thedoc.close(); }else{ this._display_textarea(); this._load_content(); } } /** * WYSIWYG_Editor::_display_textarea * * Used to display a substitute for the wysiwyg editor HTML interface to non-supported browsers **/ WYSIWYG_Editor.prototype._display_textarea = function (){ document.write('
'); } WYSIWYG_Editor.prototype.mouse_over = function( name ) { document.images[this.instance_name+'-'+name].src = '/images/wysiwyg/' + name + '_alt.png'; } WYSIWYG_Editor.prototype.mouse_out = function( name ) { document.images[this.instance_name+'-'+name].src = '/images/wysiwyg/' + name + '.png'; } /** * WYSIWYG_Editor::_display_editor * * Used to display the actual wysiwyg editor HTML interface to supported browsers **/ WYSIWYG_Editor.prototype._display_editor = function (){ var html = ''; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += '
'; html += '
'; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += '
'; if(this.font_format_toolbar1){ html += ' '; html += ' '; html += ' '; if(this.font_format_toolbar1_after=='br'){ html += '
'; }else if(this.font_format_toolbar1_after=='|'){ html += ' |'; } } html += '
'; if(this.font_format_toolbar2){ html += ' Font Color'; html += ' Background Color'; if(this.font_format_toolbar2_after=='br'){ html += '
'; }else if(this.font_format_toolbar2_after=='|'){ html += ' |'; } } if(this.font_format_toolbar3){ html += ' Bold'; html += ' Italic'; html += ' Underline'; if(this.font_format_toolbar3_after=='br'){ html += '
'; }else if(this.font_format_toolbar3_after=='|'){ html += ' |'; } } if(this.font_format_toolbar4){ html += ' Superscript'; html += ' Subscript'; if(this.font_format_toolbar4_after=='br'){ html += '
'; }else if(this.font_format_toolbar4_after=='|'){ html += ' |'; } } html += '
'; if(this.alignment_toolbar1){ html += ' Left'; html += ' Center'; html += ' Right'; html += ' Full'; if(this.alignment_toolbar1_after=='br'){ html += '
'; }else if(this.alignment_toolbar1_after=='|'){ html += ' |'; } } if(this.alignment_toolbar2){ html += ' Indent'; html += ' Outdent'; if(this.alignment_toolbar2_after=='br'){ html += '
'; }else if(this.alignment_toolbar2_after=='|'){ html += ' |'; } } if(this.web_toolbar1){ html += ' Hyperlink'; html += ' Remove Link'; if(this.web_toolbar1_after=='br'){ html += '
'; }else if(this.web_toolbar1_after=='|'){ html += ' |'; } } if(this.web_toolbar2){ html += ' Ordered List'; html += ' Bulleted List'; if(this.web_toolbar2_after=='br'){ html += '
'; }else if(this.web_toolbar2_after=='|'){ html += ' |'; } } if(this.web_toolbar3){ html += ' Horizontal Rule'; html += ' Insert Table'; if(this.web_toolbar3_after=='br'){ html += '
'; }else if(this.web_toolbar3_after=='|'){ html += ' |'; } } if(this.web_toolbar4){ if(this.image_button) html += ' Insert Image'; if(this.spell_path.length > 0) html += ' Check Spelling'; if(this.save_button) html += ' '; if(this.web_toolbar4_after=='br'){ html += '
'; }else if(this.web_toolbar4_after=='|'){ html += ' |'; } } if(this.web_toolbar5){ if(this.smilies_button) html += ' Insert Smilies'; if(this.web_toolbar5_after=='br'){ html += '
'; }else if(this.web_toolbar5_after=='|'){ html += ' |'; } } if(this.misc_format_toolbar){ html += '
'; html += ' Remove Formatting'; html += ' |'; html += ' Undo'; html += ' Redo'; html += ' |'; html += ' Cut'; html += ' Copy'; html += ' Paste'; if(this.misc_format_toolbar=='br'){ html += '
'; }else if(this.misc_format_toolbar_after=='|'){ html += ' |'; } } html += '
'; html += '
'; html += '
'; if(this.allow_mode_toggle){ html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += '
'; html += ' '; html += ' Toggle Mode'; html += '
'; } html += '
'; document.write( html ); } /** * WYSIWYG_Editor::doTextFormat * * Apply a text formatting command to the selected text in the editor (or starting at the current cursor position) * * @param command string Which of the editor/browser text formatting commands to apply **/ WYSIWYG_Editor.prototype.doTextFormat = function (command, optn, evnt){ if((command=='forecolor') || (command=='hilitecolor')){ this.getPallete(command, optn, evnt); }else if(command=='createlink'){ var szURL=prompt('Enter a URL:', ''); if(document.getElementById(this.wysiwyg_content).contentWindow.document.queryCommandEnabled(command)){ document.getElementById(this.wysiwyg_content).contentWindow.document.execCommand('CreateLink',false,szURL); return true; }else return false; }else{ if(document.getElementById(this.wysiwyg_content).contentWindow.document.queryCommandEnabled(command)){ document.getElementById(this.wysiwyg_content).contentWindow.document.execCommand(command, false, optn); return true; }else return false; } document.getElementById(this.wysiwyg_content).contentWindow.focus(); } /** * WYSIWYG_Editor::_load_content * * Puts the passed content into the editor or textarea (Use this one *after* displaying the editor.) * * @param content string The string containing the properly-formatted HTML source to use */ WYSIWYG_Editor.prototype._load_content = function (){ document.getElementById(this.wysiwyg_hidden).value=this.content; } /** * WYSIWYG_Editor::toggleMode * * Toggles between design view and source view in the IFRAME element **/ WYSIWYG_Editor.prototype.toggleMode = function (){ // change the display styles if(this.viewMode == 2){ document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontFamily = ''; document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontSize = ''; document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.color = ''; document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontWeight = ''; document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.backgroundColor = ''; document.getElementById(this.instance_name+'_toolbars').style.visibility='visible'; }else{ document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontFamily = 'monospace'; document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontSize = '10pt'; document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.color = '#000'; document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.backgroundColor = '#fff'; document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontWeight = 'normal'; document.getElementById(this.instance_name+'_toolbars').style.visibility='hidden'; } // do the content swapping if(this.isMSIE()){ this._toggle_mode_ie(); }else{ this._toggle_mode_gecko(); } } /** * WYSIWYG_Editor::_toggle_mode_ie * * Toggles between design view and source view in the IFRAME element for MSIE **/ WYSIWYG_Editor.prototype._toggle_mode_ie = function (){ if(this.viewMode == 2){ document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML = document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerText; document.getElementById(this.wysiwyg_content).contentWindow.focus(); this.viewMode = 1; // WYSIWYG }else{ document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerText = document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML; document.getElementById(this.wysiwyg_content).contentWindow.focus(); this.viewMode = 2; // Code } } /** * WYSIWYG_Editor::_toggle_mode_gecko * * Toggles between design view and source view in the IFRAME element for Gecko browsers **/ WYSIWYG_Editor.prototype._toggle_mode_gecko = function (){ if(this.viewMode == 2){ var html = document.getElementById(this.wysiwyg_content).contentWindow.document.body.ownerDocument.createRange(); html.selectNodeContents(document.getElementById(this.wysiwyg_content).contentWindow.document.body); document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML = html.toString(); document.getElementById(this.wysiwyg_content).contentWindow.focus(); this.viewMode = 1; // WYSIWYG }else{ var html = document.createTextNode(document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML); document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML = ''; document.getElementById(this.wysiwyg_content).contentWindow.document.body.appendChild(html); document.getElementById(this.wysiwyg_content).contentWindow.focus(); this.viewMode = 2; // Code } } /** * WYSIWYG_Editor::_get_offset_left * * Used to define position of pop-up pallete window in Gecko browsers **/ WYSIWYG_Editor.prototype._get_offset_left = function (elm){ var mOffsetLeft=elm.offsetLeft; var mOffsetParent=elm.offsetParent; while(mOffsetParent){ mOffsetLeft += mOffsetParent.offsetLeft; mOffsetParent=mOffsetParent.offsetParent; } return mOffsetLeft; } /** * WYSIWYG_Editor::_get_offset_top * * Used to define position of pop-up pallete window in Gecko browsers **/ WYSIWYG_Editor.prototype._get_offset_top = function (elm){ var mOffsetTop=elm.offsetTop; var mOffsetParent=elm.offsetParent; while(mOffsetParent){ mOffsetTop += mOffsetParent.offsetTop; mOffsetParent=mOffsetParent.offsetParent; } return mOffsetTop; } /** * WYSIWYG_Editor::insertTable * * Used for inserting a table into the IFRAME content window **/ WYSIWYG_Editor.prototype.insertTable = function (){ colstext = prompt('Enter the number of columns per row.'); rowstext = prompt('Enter the number of rows to create.'); rows = parseInt(rowstext,'0'); cols = parseInt(colstext,'0'); if(this.isMSIE()){ return this._insert_table_ie(cols,rows); }else{ return this._insert_table_gecko(cols,rows); } } /** * WYSIWYG_Editor::_insert_table_ie * * This is the browser engine-specific code for inserting a table for MSIE browsers. * * @param cols The number of columns to create * @param rows The number of rows to create **/ WYSIWYG_Editor.prototype._insert_table_ie = function (cols, rows){ document.getElementById(this.wysiwyg_content).contentWindow.focus(); //get current selected range var cursor=document.getElementById(this.wysiwyg_content).contentWindow.document.selection.createRange(); if((rows > 0) && (cols > 0)){ var tableHTML = ''; while(rows>0){ rows--; var rowCols = cols; tableHTML = tableHTML + ''; while(parseInt(rowCols)>0){ rowCols--; tableHTML=tableHTML+''; } tableHTML = tableHTML + ''; } tableHTML = tableHTML + '
 
'; cursor.pasteHTML(tableHTML); document.getElementById(this.wysiwyg_content).contentWindow.focus(); } return true; } /** * WYSIWYG_Editor::_insert_table_gecko * * This is the browser engine-specific code for inserting a table for Gecko browsers. * * @param cols The number of columns to create * @param rows The number of rows to create **/ WYSIWYG_Editor.prototype._insert_table_gecko = function (cols, rows){ contentWin=document.getElementById(this.wysiwyg_content).contentWindow; if((rows > 0) && (cols > 0)){ table=contentWin.document.createElement('table'); table.setAttribute('border', this.table_border); table.setAttribute('cellpadding', this.table_cell_padding); table.setAttribute('cellspacing', this.table_cell_spacing); tbody=contentWin.document.createElement('tbody'); for(i=0;i