//------------------------------------------------------------------------------
// IPS Cross-Browser Rich Text Editor
//------------------------------------------------------------------------------
// Supports Mozilla 1.3+ (Firefox, etc) and IE 5.5+
// (c) 2005 Invision Power Services, Inc.
// http://www.invisionpower.com
// http://www.ibresource.ru
//------------------------------------------------------------------------------
/*--------------------------------------------*/
// INIT variables
/*--------------------------------------------*/
var i_am = 'rte';
//document.getElementById( 'debugmsg' ).value
var DEBUG = 0;
var isRichText = false;
var css_off = false;
var rng;
var currentRTE;
var allRTEs = "";
var rte = "";
// Defined in enable_design_mode
var rtewindow;
var rtedocument;
var rteready;
var isIE;
var isGecko;
var isSafari;
var isKonqueror;
var imagesPath;
var includesPath;
//-------------------------------
// Set global scope
//-------------------------------
var g_buttons;
var g_rte;
var g_tablewidth;
var g_width_unit;
var g_imagesPath;
var g_width;
var g_height;
var g_readOnly;
var g_includesPath
var g_imagesPath;
var g_html;
var g_DEBUG;
//-------------------------------
// Font & size
//-------------------------------
var currentfont;
var currentsize;
var button_status = new Array();
//-------------------------------
// Viewing HTML?
//-------------------------------
var viewingHTML = 0;
//-------------------------------
// Viewing Full screen?
//-------------------------------
var viewingFS = 0;
//-------------------------------
// Define which buttons change
// when clicked
//-------------------------------
var buttons_update = new Array(
"bold",
"italic",
"underline",
"strikethrough",
//"superscript",
//"subscript",
"justifyleft",
"justifycenter",
"justifyright",
"insertorderedlist",
"insertunorderedlist"
);
var opentags =
{
'bold' : 0,
'italic' : 0,
'strikethrough' : 0,
'underline' : 0,
'forecolor' : 0,
'fontname' : 0,
'fontsize' : 0
};
/*--------------------------------------------*/
// INIT RTE
/*--------------------------------------------*/
function init_rte( imgPath, incPath )
{
//-------------------------------
// Set browser vars
//-------------------------------
var ua = navigator.userAgent.toLowerCase();
isIE = ((ua.indexOf("msie") != -1) && (ua.indexOf("opera") == -1) && (ua.indexOf("webtv") == -1));
isGecko = (ua.indexOf("gecko") != -1);
isSafari = (ua.indexOf("safari") != -1);
isKonqueror = (ua.indexOf("konqueror") != -1);
//-------------------------------
// Check to see if designMode mode
// is available. Should be for:
// IE5.5+ Mozilla 1.3+
// Safari 2+
//-------------------------------
if ( document.getElementById && document.designMode && ! isSafari &&!isKonqueror )
{
isRichText = true;
}
if ( isIE )
{
document.onmouseover = ie_raise_button;
document.onmouseout = ie_normal_button;
document.onmousedown = ie_lower_button;
document.onmouseup = ie_raise_button;
}
//-------------------------------
// Set paths vars
//-------------------------------
imagesPath = imgPath;
includesPath = incPath;
//-------------------------------
//for testing standard textarea, uncomment the following line
//isRichText = false;
//-------------------------------
}
/*--------------------------------------------*/
// Write Rich text to screen
/*--------------------------------------------*/
function write_rte_editor(fieldname, html, width, height, buttons, readOnly)
{
rte = fieldname;
if ( isRichText )
{
if (allRTEs.length > 0)
{
allRTEs += ";";
}
allRTEs += rte;
write_rte(html, width, height, buttons, readOnly);
}
else
{
write_default(html, width, height, buttons, readOnly);
}
}
/*--------------------------------------------*/
// Write the default info
/*--------------------------------------------*/
function write_default(html, width, height, buttons, readOnly)
{
if ( ! readOnly )
{
document.writeln('');
}
else
{
document.writeln('');
}
}
/*--------------------------------------------*/
// Display RTE
/*--------------------------------------------*/
function write_rte(html, width, height, buttons, readOnly)
{
if (readOnly) buttons = false;
var o_width = width;
var o_height = height;
width = parseInt( width );
height = parseInt( height );
//-------------------------------
// Adjust minimum table widths
//-------------------------------
if ( isIE )
{
if ( ! o_width.match( /%/ ) )
{
if ( buttons && (width < 400) )
{
width = 400;
}
g_width_unit = 'px';
var tablewidth = width + 4;
}
else
{
g_width_unit = '%';
var tablewidth = width ;
width = width;
}
}
else
{
if ( ! o_width.match( /%/ ) )
{
if ( buttons && (width < 400) )
{
width = 400;
}
g_width_unit = 'px';
var tablewidth = width + 2;
}
else
{
g_width_unit = '%';
var tablewidth = width;
width = width;
}
}
//-------------------------------
// Globalize
//-------------------------------
g_buttons = buttons;
g_rte = rte;
g_tablewidth = tablewidth;
g_imagesPath = imagesPath;
g_width = width;
g_height = height;
g_readOnly = readOnly;
g_includesPath = includesPath;
g_imagesPath = imagesPath;
g_html = html;
g_DEBUG = DEBUG;
boo_to_int = isRichText == true ? 1 : 0;
//-------------------------------
// Use HTML from rte_html.js
//-------------------------------
document.writeln( "" );
write_rte_html();
document.getElementById('hdn_rte_content').value = html;
enable_design_mode( make_newlines_safe(html), readOnly );
//-------------------------------
// Make buttons unselectable
//-------------------------------
poss_images = document.getElementsByTagName('img');
if ( poss_images.length )
{
for( var i in poss_images )
{
if ( poss_images[i].className == 'rteimage' )
{
poss_images[i].unselectable = true;
}
}
}
//-----------------------------------------------------------
// This setting makes hitting the enter key in IE turn into a
// newline (br tag) instead of the default action of using a
// paragraph tag. This causes the indent and list options not
// to work properly in IE, so do not enable this unless you
// don't care about those options. This functionality will be
// expanded upon in a future release, however this is not due
// to an issue with IPB, but rather due to how IE handles the
// javascript exec_command functions, and is beyond our control
// without a lot of javascript rewriting of those functions,
// specific to IE behaviors.
//-----------------------------------------------------------
var ie_ptags_to_newlines = false;
if( isIE && ie_ptags_to_newlines )
{
rtewindow = frames[rte];
rtewindow.document.onkeydown = function () {
if (rtewindow.event.keyCode == 13)
{ // ENTER
//wrap_tags( "
\n", "\n" );
var sel = rtewindow.document.selection;
var ts = rtewindow.document.selection.createRange();
var t = ts.htmlText.replace(/
]*)>(.*)<\/p>/i, '$2');
if ( (sel.type == "Text" || sel.type == "None") )
{
ts.pasteHTML( "
" + t + "\n" );
}
else
{
rtewindow.document.body.innerHTML += "
\n";
}
rtewindow.event.returnValue = false;
ts.select();
rtewindow.focus();
}
}
}
}
/*--------------------------------------------*/
// Enable Design Mode
/*--------------------------------------------*/
function enable_design_mode(html, readOnly)
{
//-------------------------------
// Got char set?
//-------------------------------
var use_charset = '';
try
{
use_charset = g_CHARSET;
}
catch(e)
{
}
use_charset = use_charset ? use_charset : 'windows-1251';
//-------------------------------
// Print...
//-------------------------------
var frameHtml = "\n";
frameHtml += "
]*)>(.*)<\/p>/i, '$2'); if ( (sel.type == "Text" || sel.type == "None") ) { has_closed = true; ts.pasteHTML( opentag + t + closetag ); } else { has_closed = true; rtewindow.document.body.innerHTML += opentag + closetag; } } else { //------------------------------- // FRAGment, not a game of UT //------------------------------- var frag = rtewindow.document.createDocumentFragment(); var span = rtewindow.document.createElement("span"); //------------------------------- // Apply tags... //------------------------------- sel_html = _gecko_get_html(); //------------------------------- // Remove empty span tags //------------------------------- if ( sel_html ) { has_closed = true; span.innerHTML = opentag + sel_html + closetag; } else { span.innerHTML = opentag + closetag; } while ( span.firstChild ) { frag.appendChild(span.firstChild); } _gecko_insert_node_at_selection( frag ); } //------------------------------- // Add tag to stack //------------------------------- rtewindow.focus(); return has_closed; } /*--------------------------------------------*/ // Do fontsize, etc /*--------------------------------------------*/ function do_select(selectname) { if ( isIE ) { //------------------------------- // Get current selected range //------------------------------- var selection = rtewindow.document.selection; if (selection != null) { rng = selection.createRange(); } } else { //------------------------------- // Get currently selected range //------------------------------- var selection = rtewindow.getSelection(); rng = selection.getRangeAt(selection.rangeCount - 1).cloneRange(); } var idx = document.getElementById(selectname).selectedIndex; var selected = document.getElementById(selectname).options[idx].value; rtewindow.focus(); _gecko_kill_css(); rtewindow.document.execCommand(selectname, false, selected); rte_button_update( selectname ); rtewindow.focus(); } /*--------------------------------------------*/ // Launch fullscreen... /*--------------------------------------------*/ function launch_rte_fs() { if ( viewingFS ) { //return false; } //------------------------------- // If viewing source, switch back // to design view //------------------------------- if ( viewingHTML ) { toggleHTMLSrc(); } viewingFS = 1; window.open( ipb_var_base_url + '&act=rtefs', 'RTEFS', 'width=640,height=480,resizable=yes,scrollbars=yes,location=no,directories=no,status=no,menubar=no,toolbar=no'); } /*--------------------------------------------*/ // Launch div... /*--------------------------------------------*/ function launch_div() { // Could do with sorting this mess out. window.open( includesPath + 'insert_div.html', 'DIV', 'width=550,height=340,resizable=yes,scrollbars=yes,location=no,directories=no,status=no,menubar=no,toolbar=no'); } /*--------------------------------------------*/ // Manage tables... /*--------------------------------------------*/ function write_div( param, pwindow ) { try { if ( pwindow ) { pwindow.close(); } } catch (e) { } if ( ! param ) { return false; } //------------------------------- // Build remap array //------------------------------- var remap_elements = { "f_fontfamily" : "font-family" , "f_fontsize" : "font-size" , "f_color" : "color" , "f_backgroundimage" : "background-image" , "f_backgroundrepeat" : "background-repeat", "f_backgroundcolor" : "background-color" , "f_border" : "border" , "f_padding" : "padding" , "f_margin" : "margin" }; //------------------------------- // Get tag type //------------------------------- var tagtype = param["f_type"]; var style = ""; if ( ! param["f_classes"] || param["f_classes"] == "__none" ) { //------------------------------- // Start building style //------------------------------- style += "style='"; for (var field in param) { var value = param[field]; if ( ! value || value == 'undefined' || field == 'undefined' || ! remap_elements[ field ] ) { continue; } if ( remap_elements[ field ] && value ) { style += remap_elements[ field ] + ":" + value + ";"; } } //------------------------------- // Finish style //------------------------------- style += param["f_other"]+"'"; } else { style += "class='" + param["f_classes"] + "'"; } //------------------------------- // Pass to handler //------------------------------- if ( style ) { wrap_tags( "<"+tagtype+" " + style + ">", ""+tagtype+">" ); } else { wrap_tags( "<"+tagtype+">", ""+tagtype+">" ) } return true; } /*--------------------------------------------*/ // Launch tables... /*--------------------------------------------*/ function launch_table() { // Could do with sorting this mess out. window.open( includesPath + 'insert_table.html', 'TABLE', 'width=450,height=220,resizable=yes,scrollbars=yes,location=no,directories=no,status=no,menubar=no,toolbar=no'); } /*--------------------------------------------*/ // Manage tables... /*--------------------------------------------*/ function write_tables( param, pwindow ) { try { if ( pwindow ) { pwindow.close(); } } catch (e) { } var sel = _my_getselection(); var range = _my_createrange(sel); if ( ! param ) { return false; } var doc = rtewindow.document; //------------------------------- // 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. //------------------------------- ( ! isIE ) { td.appendChild(doc.createElement("br")); } } } if ( isIE ) { range.pasteHTML(table.outerHTML); } else { //------------------------------- // insert the table //------------------------------- _gecko_insert_node_at_selection(table); } return true; } /*--------------------------------------------*/ // KeyBoard Handler /*--------------------------------------------*/ function kb_handler(evt) { var rte = evt.target.id; if (evt.ctrlKey) { var key = String.fromCharCode(evt.charCode).toLowerCase(); var cmd = ''; switch (key) { case 'b': cmd = "bold"; break; case 'i': cmd = "italic"; break; case 'u': cmd = "underline"; break; }; if (cmd) { format_text(cmd, true); //------------------------------- // evt.target.ownerDocument.execCommand(cmd, false, true); // stop the event bubble //------------------------------- evt.preventDefault(); evt.stopPropagation(); } } } function docChanged (evt) { alert('changed'); } /*--------------------------------------------*/ // Get selection (MY) /*--------------------------------------------*/ function _my_getselection() { if ( isIE ) { return rtewindow.document.selection; } else { return rtewindow.getSelection(); } } /*--------------------------------------------*/ // Create Range (my) /*--------------------------------------------*/ function _my_createrange( sel ) { if ( isIE ) { return sel.createRange(); } else { rtewindow.focus(); return sel ? sel.getRangeAt(0) : rtewindow.document.createRange(); } } /*--------------------------------------------*/ // Gecko work around to get selection HTML /*--------------------------------------------*/ function _gecko_get_html() { var sel = _my_getselection(); var range = _my_createrange(sel); var root = range.cloneContents(); return _gecko_read_nodes(root, false); } /*--------------------------------------------*/ // Gecko read nodes (based on midas) /*--------------------------------------------*/ function _gecko_read_nodes(root, toptag) { var html = ""; var moz_check = /_moz/i; switch (root.nodeType) { case Node.ELEMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE: { var closed; if (toptag) { closed = !root.hasChildNodes(); html = '<' + root.tagName.toLowerCase(); var attr = root.attributes; for (i = 0; i < attr.length; ++i) { var a = attr.item(i); if (!a.specified || a.name.match(moz_check) || a.value.match(moz_check)) { continue; } html += " " + a.name.toLowerCase() + '="' + a.value + '"'; } html += closed ? " />" : ">"; } for (var i = root.firstChild; i; i = i.nextSibling) { html += _gecko_read_nodes(i, true); } if (toptag && !closed) { html += "" + root.tagName.toLowerCase() + ">"; } } break; case Node.TEXT_NODE: { html = htmlspecialchars(root.data); } break; } return html; } /*--------------------------------------------*/ // Gecko insert html mode at selection (midas) /*--------------------------------------------*/ function _gecko_insert_node_at_selection(text) { var sel = _my_getselection(); var range = _my_createrange(sel); sel.removeAllRanges(); range.deleteContents(); var node = range.startContainer; var pos = range.startOffset; switch (node.nodeType) { case Node.ELEMENT_NODE: { if (text.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { selNode = text.firstChild; } else { selNode = text; } node.insertBefore(text, node.childNodes[pos]); _gecko_add_range(selNode); } break; case Node.TEXT_NODE: { if ( text.nodeType == Node.TEXT_NODE ) { var text_length = pos + text.length; node.insertData(pos, text.data); range = rtewindow.document.createRange(); range.setEnd( node, text_length); range.setStart(node, text_length); sel.addRange(range); } else { node = node.splitText(pos); var selNode; if (text.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { selNode = text.firstChild; } else { selNode = text; } node.parentNode.insertBefore(text, node); _gecko_add_range(selNode); } } break; } } /*--------------------------------------------*/ // Turns off CSS mode for gecko /*--------------------------------------------*/ function _gecko_kill_css() { //------------------------------- // Moz likes to use styles // Not really sure if this needs // to be fired before each command // midas shows not, experience shows // that it does. //------------------------------- if ( ! isIE ) { try { rtewindow.document.execCommand("useCSS", false, true); css_off = true; } catch (e) { css_off = false; } } } /*--------------------------------------------*/ // Add range of inserted text /*--------------------------------------------*/ function _gecko_add_range(node) { rtewindow.focus(); var sel = rtewindow.getSelection(); var range = null; range = rtewindow.document.createRange(); range.selectNodeContents(node); sel.removeAllRanges(); sel.addRange(range); } /*--------------------------------------------*/ // IE ONLY: Button raised /*--------------------------------------------*/ function ie_raise_button(e) { var el = window.event.srcElement; className = el.className; if (className == 'rteimage' || className == 'rteImageLowered') { el.className = 'rteImageRaised'; } } /*--------------------------------------------*/ // IE ONLY: Normal Button /*--------------------------------------------*/ function ie_normal_button(e) { var el = window.event.srcElement; className = el.className; var command; try { if ( el.id ) { command = el.id.toString(); command = command.replace( /do_/, "" ); } } catch (e) { // Who cares } if (className == 'rteImageRaised' || className == 'rteImageLowered') { if ( ! command || button_status[ command ] != true ) { el.className = 'rteimage'; } else { el.className = 'rteImageRaised'; } } } /*--------------------------------------------*/ // IE ONLY: Lowered Button /*--------------------------------------------*/ function ie_lower_button(e) { var el = window.event.srcElement; className = el.className; if (className == 'rteimage' || className == 'rteImageRaised') { el.className = 'rteImageLowered'; } } /*--------------------------------------------*/ // Strip HTML /*--------------------------------------------*/ function strip_html(oldString) { var newString = oldString.replace(/(<([^>]+)>)/ig,""); //------------------------------- //replace carriage returns and line feeds //------------------------------- newString = newString.replace(/\r\n/g," "); newString = newString.replace(/\n/g," "); newString = newString.replace(/\r/g," "); //------------------------------- //trim string //------------------------------- newString = trim(newString); return newString; } /*--------------------------------------------*/ // Newlines safe /*--------------------------------------------*/ function make_newlines_safe(t) { //t = t.replace(/\n/g,'\\n'); //------------------------------- //replace carriage returns and line feeds //------------------------------- //t = t.replace(/\r/g,'\\r'); //------------------------------- //trim string //------------------------------- //t = trim(t); return t; } /*--------------------------------------------*/ // TRIM /*--------------------------------------------*/ function trim(inputString) { //------------------------------- // Removes leading and trailing spaces from the passed string. Also removes // consecutive spaces and replaces it with one space. If something besides // a string is passed in (null, custom object, etc.) then return the input. //------------------------------- if (typeof inputString != "string") return inputString; var retValue = inputString; var ch = retValue.substring(0, 1); while (ch == " ") { //------------------------------- // Check for spaces at the beginning of the string //------------------------------- retValue = retValue.substring(1, retValue.length); ch = retValue.substring(0, 1); } ch = retValue.substring(retValue.length - 1, retValue.length); while (ch == " ") { //------------------------------- // Check for spaces at the end of the string //------------------------------- retValue = retValue.substring(0, retValue.length - 1); ch = retValue.substring(retValue.length - 1, retValue.length); } //------------------------------- // Note that there are two spaces // in the string - look for multiple // spaces within the string //------------------------------- while (retValue.indexOf(" ") != -1) { //------------------------------- // Again, there are two spaces in each of the strings //------------------------------- retValue = retValue.substring(0, retValue.indexOf(" ")) + retValue.substring(retValue.indexOf(" ") + 1, retValue.length); } return retValue; } //*-------------------------------------------------------------------------*/ // Validate form (must always have this method defined!) /*-------------------------------------------------------------------------*/ function ValidateForm( isMsg ) { //----------------------------------------- // Sync RTE //----------------------------------------- update_rte(); MessageLength = 3; //postfieldobj.value.length; errors = ""; //----------------------------------------- // Check for remove attachments //----------------------------------------- try { if ( postformobj.removeattachid.value > 0 ) { okdelete = confirm( js_remove_attach ); if ( okdelete == true ) { return true; } else { postformobj.removeattachid.value = 0; return false; } } } catch(error) { // } if ( isMsg == 1) { if ( postformobj.msg_title.value.length < 2 ) { errors = js_msg_no_title; } } if ( MessageLength < 2 ) { errors = js_no_message; } if ( MessageMax != 0 ) { if (MessageLength > MessageMax) { errors = js_max_length + " " + MessageMax + " " + js_characters + ". " + js_current + ": " + MessageLength; } } if ( errors != "" && Override == "" ) { alert(errors); return false; } else { try { postformobj.submit.disabled = true; } catch(e) { try { postformobj.dosubmit.disabled = true; } catch(e) { } } return true; } } /*--------------------------------------------*/ // HTML SPECIAL CHARACTERS (PHP emulation) /*--------------------------------------------*/ function htmlspecialchars(html) { html = html.replace(/"/, """); html = html.replace(/, "<"); html = html.replace(/>/, ">"); return html; }