/* * * @(#) $Id: html_editor.js,v 1.51 2014/09/28 02:58:16 mlemos Exp $ * */ /*jslint browser: true, devel: true, plusplus: true, sloppy: true, white: true */ var ML; if(ML === undefined) { ML = {}; } if(ML.HTMLEditor === undefined) { ML.HTMLEditor = {}; } if(ML.HTMLEditor.Editor === undefined) { ML.HTMLEditor.HTMLEditors = {}; ML.HTMLEditor.defaultVisualToolbar = { 'character': [ { type: 'bold' }, { type: 'italic' }, { type: 'underline' }, { type: 'strikethrough' }, { type: 'createlink' }, { type: 'unlink' } ], 'paragraph': [ { type: 'justifyleft' }, { type: 'justifycenter' }, { type: 'justifyright' }, { type: 'justifyfull' }, { type: 'space' }, { type: 'formatblock' }, { type: 'inserttemplate' } ], 'document': [ { type: 'copy' }, { type: 'cut' }, { type: 'paste' }, { type: 'delete' }, { type: 'space' }, { type: 'undo' }, { type: 'redo' }, { type: 'space' }, { type: 'html' } ] }; ML.HTMLEditor.defaultHTMLToolbar = { 'document': [ { type: 'visual' } ] }; ML.HTMLEditor.linkEmulationStyle = 'color: #0000FF; text-decoration: underline'; ML.HTMLEditor.Editor = function() { this.id = ''; this.error = ''; this.debug = true; this.editorStyle = 'background-color: #ffffff; border-style: solid; border-width: 1px; margin: 0px; border-color: #707070 #e0e0e0 #e0e0e0 #707070'; this.templateVariableStyle = 'border-style: dashed; border-width: 1px; margin: 1px; padding: 1px'; this.templateMarkStyle = 'background-color: #cedee6; border-style: solid; border-width: 1px; margin: 0px; padding: 2px; border-color: #e0e0e0 #707070 #707070 #e0e0e0; font-size: 8pt; opacity: 0.75; filter:alpha(opacity=75)'; this.menuStyle = 'background-color: #d0d0d0; border-style: solid; border-width: 1px; margin: 0px; border-color: #e0e0e0 #707070 #707070 #e0e0e0'; this.itemStyle = 'padding: 4px; color: #000000'; this.itemSelectStyle = 'padding: 4px; color: #ffffff; background-color: #000080'; this.mode = 'visual'; this.showToolbars = true; this.templateVariables = {}; this.externalCSS = []; this.openVariable = '{'; this.closeVariable = '}'; this.alternativesMark = '+'; /* * private variables */ this.editor = null; this.textarea = null; this.iframe = null; this.editorDocument = null; this.htmlEditor = null; this.visualEditor = null; this.lastMenu = ''; this.lastMenuTime = 0; this.menuDelayTime = 100; this.lastOpenedMenu = null; var hasAttribute = function (element, attribute) { if(element.hasAttribute) { return(element.hasAttribute(attribute)); } return element.attributes[attribute] !== undefined; }, getElementsByName = function(element, name, tags) { var l = [], t, e, i; for(t = 0; t < tags.length; ++t) { e = element.getElementsByTagName(tags[t]); for(i = 0; i < e.length; ++i) { if(e[i].getAttribute('name') === name) { l[l.length] = e[i]; } } } return l; }, getElementBox = function (element) { var b, s, o, p, box = {}, d = element.ownerDocument, win = d.defaultView || d.parentWindow; if(element.getBoundingClientRect) { b = element.getBoundingClientRect(); box.x = b.left + d.body.scrollLeft; box.y = b.top + d.body.scrollTop; box.width = b.right - b.left + 1; box.height = b.bottom - b.top + 1; } else { if(d.getBoxObjectFor) { b = d.getBoxObjectFor(element); box.x = b.x; box.y = b.y; box.width = b.width; box.height = b.height; if(win.getComputedStyle) { s = win.getComputedStyle(element, null); o = parseInt(s.borderLeftWidth, 10) + parseInt(s.borderRightWidth, 10); box.x -= o; box.width += o; o = parseInt(s.borderTopWidth, 10) + parseInt(s.borderBottomWidth, 10); box.y -= o; box.height += o; } } else { p = element.style.position; element.style.position = 'relative'; box.x = element.offsetLeft; box.y = element.offsetTop; box.width = element.offsetWidth; box.height = element.offsetHeight; element.style.position = p; } } return box; }, getElementSize = function (element) { var box = getElementBox(element); return { width: box.width, height: box.height }; }, repositionElement = function (element, parent, frame) { var s, b = getElementBox(parent), x = b.x, y = b.y + b.height, w = parseInt(b.width, 10); if(!isNaN(w)) { s = getElementSize(element); if(!isNaN(parseInt(s.width, 10))) { x += (w - parseInt(s.width, 10)) / 2; if(x < 0) { x = 0; } } } if(frame) { b = getElementBox(frame); x += b.x; y += b.y; if(typeof(frame.contentWindow.pageXOffset) === 'number') { x -= frame.contentWindow.pageXOffset; y -= frame.contentWindow.pageYOffset; } else { if(frame.contentWindow.document.documentElement && frame.contentWindow.document.compatMode && frame.contentWindow.document.compatMode !== "BackCompat") { x -= frame.contentWindow.document.documentElement.scrollLeft; y -= frame.contentWindow.document.documentElement.scrollTop; } else { x -= frame.contentWindow.document.body.scrollLeft; y -= frame.contentWindow.document.body.scrollTop; } } } element.style.left = x + 'px'; element.style.top = y + 'px'; }, addEventListener = function(element, event, listener, capture) { if(element.addEventListener) { element.addEventListener(event, listener, capture); } else { if(element.attachEvent) { element.attachEvent('on' + event, listener); } } }, replaceStrings = function(value, replace) { var v, c, p, f; for(v in replace) { c = ''; p = 0; while(p < value.length) { f = value.indexOf(v, p); if(f === -1) { c += value.substring(p); break; } c += value.substring(p, f) + replace[v]; p = f + v.length; } value = c; } return value; }, encodeHTML = function(value) { return replaceStrings(value, { '&': '&', '"': '"', '<': '<', '>': '>' }); }, escapeHTML = function(value) { return value.replace(/]*)>([^<]*)<\/a>/gi, '$2'); }, encodeString = function(value) { return "'" + replaceStrings(value, { "'": "\\'" }) + "'"; }, expandTemplates = function(e, value, insert) { var v, r, i, n, p, b, f, s, tv, style, a, h, t, expand, c, variables = {}, created = {}; if(insert) { for(v in e.templateVariables) { v = e.openVariable + v + e.closeVariable; r = getElementsByName(e.editorDocument, v, ['div', 'span']); for(i = 0; i < r.length; ++i) { n = (variables[v] ? variables[v].length : 0); if(n === 0) { variables[v] = []; } variables[v][n] = r[i].id; } } } style = e.templateVariableStyle.length ? ' style="' + encodeHTML(e.templateVariableStyle) + '"' : ''; v = value; value = ''; p = 0; while(p < v.length) { b = v.indexOf(e.openVariable, p); if(b === -1) { value += v.substring(p); break; } b += e.openVariable.length; f = v.indexOf(e.closeVariable, b); if(f === -1) { value += v.substring(p); break; } s = v.indexOf(' ', b); if(s === -1 || s > f) { s = f; } tv = v.substring(b, s); if(e.templateVariables[tv]) { if(e.templateVariables[tv].alternatives) { a = (s === f ? null : v.substring(s + 1, f)); if(a && !e.templateVariables[tv].alternatives[a]) { a = null; } } else { a = null; } h = (e.templateVariables[tv].inline !== undefined); if(h) { t = (e.templateVariables[tv].inline ? 'span' : 'div'); i = encodeHTML(e.openVariable + tv + e.closeVariable); n = (variables[i] ? variables[i].length : 0); expand = '<' + t + ' id="' + i + '_' + n +'" name="' + i + '"' + style + ' contentEditable="false"' + (e.templateVariables[tv].title ? ' title="' + encodeHTML(e.templateVariables[tv].title) + '"' : '') + (a ? ' data="' + encodeHTML(a) + '"' : '') + '>' + t + '>'; } else { expand = (a ? e.templateVariables[tv].alternatives[a].value : e.templateVariables[tv].value); } value += v.substring(p, b - e.openVariable.length) + expand; p = f + e.closeVariable.length; if(h) { if(n === 0) { variables[i] = []; } c = (created[tv] ? created[tv].length : 0); if(c === 0) { created[tv] = []; } variables[i][n] = created[tv][c] = i + '_' + n; } } else { value += v.substring(p, b); p = b; } } return { value: value, created: created }; }, handleMenus = function(e) { return function(event) { var id, parent, now, menu; if(event.target) { parent = event.target; id = parent.id; } else { if(event.srcElement) { parent = event.srcElement; id = parent.id; } else { return; } } if(id.length === 0 || !parent) { return; } now = (new Date()).getTime(); if(e.lastMenu !== id || now - e.lastMenuTime > e.menuDelayTime) { menu = e.visualEditor.ownerDocument.getElementById(id + '_menu'); if(e.toggleMenu(menu, parent, e.iframe)) { menu.setAttribute('data', parent.parentNode.id); } e.lastMenu = id; e.lastMenuTime = now; } if(event) { if(event.preventDefault) { event.preventDefault(); } event.cancelBubble = true; event.returnValue = false; } }; }, convertValue = function(e, toEditor) { var value, v, n, r, t, a, replace; if(toEditor) { value = expandTemplates(e, e.textarea.value, false).value; e.editorDocument.body.innerHTML = value; return value; } t = document.getElementById(e.id + '_temporary'); t.innerHTML = e.editorDocument.body.innerHTML; for(v in e.templateVariables) { if(e.templateVariables[v].inline !== undefined) { r = getElementsByName(t, e.openVariable + v + e.closeVariable, ['div', 'span']); for(n = 0; n < r.length; ++n) { r[n].parentNode.replaceChild(document.createTextNode(e.openVariable + v + (hasAttribute(r[n], 'data') ? ' ' + r[n].getAttribute('data') : '') + e.closeVariable), r[n]); } } } value = t.innerHTML; t.innerHTML = ''; for(v in e.templateVariables) { if(e.templateVariables[v].inline === undefined) { replace = {}; replace[e.templateVariables[v].value] = e.openVariable + v + e.closeVariable; if(e.templateVariables[v].alternatives) { for(a in e.templateVariables[v].alternatives) { replace[e.templateVariables[v].alternatives[a].value] = e.openVariable + v + ' ' + a + e.closeVariable; } } value = replaceStrings(value, replace); } } return value; }, renderToolbar = function(e, toolbars) { var t, b, type, setup, action, o, oo, menu, comma, id, v, content, title, render = '
';
for(b = 0; b < toolbars[t].length; ++b)
{
type = toolbars[t][b].type;
switch(type)
{
case 'bold':
case 'italic':
case 'underline':
case 'strikethrough':
case 'createlink':
case 'unlink':
case 'copy':
case 'cut':
case 'paste':
case 'delete':
case 'undo':
case 'redo':
case 'html':
case 'visual':
case 'justifyleft':
case 'justifycenter':
case 'justifyright':
case 'justifyfull':
action = setup + 'if(!e.execCommand("' + type + '", true) && !e.debug) { alert("Could not execute the ' + type + ' command in this browser.") }';
switch(type)
{
case 'bold':
content = 'B';
title = 'Bold';
break;
case 'italic':
content = 'I';
title = 'Italic';
break;
case 'underline':
content = 'U';
title = 'Underline';
break;
case 'strikethrough':
content = 'S';
title = 'Strike-through';
break;
case 'createlink':
content = 'www';
title = 'Create link';
action = 'var url = prompt("Link URL:", "http://www."); if(url === null || url === "") return false; ' + setup + 'if(!e.execCommand("' + type + '", url) && !e.debug) { alert("Could not execute the ' + type + ' command in this browser.") }';
break;
case 'unlink':
content = 'www';
title = 'Remove link';
break;
case 'copy':
content = 'Copy';
title = 'Copy selected';
break;
case 'cut':
content = 'Cut';
title = 'Cut selected';
break;
case 'paste':
content = 'Paste';
title = 'Paste copied';
break;
case 'delete':
content = 'Delete';
title = 'Delete selected';
break;
case 'undo':
content = 'Undo';
title = 'Undo';
break;
case 'redo':
content = 'Redo';
title = 'Redo';
break;
case 'justifyleft':
content = 'Left';
title = 'Justify left';
break;
case 'justifycenter':
content = 'Center';
title = 'Justify center';
break;
case 'justifyright':
content = 'Right';
title = 'Justify right';
break;
case 'justifyfull':
content = 'Full';
title = 'Justify full';
break;
case 'visual':
content = 'Visual';
title = 'Edit visually';
action = setup + 'e.setEditMode("visual");';
break;
case 'html':
content = 'HTML';
title = 'Edit HTML';
action = setup + 'e.setEditMode("html");';
break;
default:
if(e.debug)
{
alert('toolbar element of type ' + type + ' is not implemented');
}
action = '';
content = title = type;
break;
}
if(action.length)
{
render += '';
}
break;
case 'justify':
case 'inserttemplate':
case 'formatblock':
switch(type)
{
case 'justify':
menu = 'Justify';
o = {
'justifyleft': 'Left',
'justifycenter': 'Center',
'justifyright': 'Right',
'justifyfull': 'Full'
};
action = 'if(!e.execCommand({item}, true) && !e.debug) { alert("Could not execute the justify command in this browser.") }';
break;
case 'inserttemplate':
menu = 'Insert template';
o = {};
if(e.templateVariables.length === 0)
{
break;
}
comma = '';
for(v in e.templateVariables)
{
if(e.templateVariables[v].inline !== undefined)
{
o[e.openVariable + v + e.closeVariable] = (e.templateVariables[v].title || v);
}
comma = ', ';
}
action = 'if(!e.execCommand("inserthtml", {item}) && !e.debug) { alert("Could not execute the inserthtml command in this browser.") }; e.loadTemplates();';
break;
case 'formatblock':
menu = 'Format block';
o = {
'p': ' Paragraph ', 'pre': 'Preformatted', 'address': 'Address', 'h1': ' Heading 1', 'h2': 'Heading 2', 'h3': 'Heading 3', 'h4': 'Heading 4', 'h5': 'Heading 5', 'h6': 'Heading 6' }; action = 'if(!e.execCommand("formatblock", {item} ) && !e.debug) { alert("Could not execute the formatblock command in this browser.") }'; break; } id = e.id + '_' + type; oo = ''; for(v in o) { oo += '' + o[v] + ' ';
}
render += '' + oo + ' ';
break;
case 'separator':
render += (toolbars[t][b].content || ' | ');
break;
case 'space':
render += (toolbars[t][b].content || ' ');
break;
default:
if(e.debug)
{
alert('toolbar element of type ' + type + ' is not implemented');
}
break;
}
}
render += ' |