2014-02-03 21:58:48 -05:00
|
|
|
// *** smc_Editor class.
|
|
|
|
function smc_Editor(oOptions)
|
|
|
|
{
|
|
|
|
this.opt = oOptions;
|
|
|
|
|
|
|
|
// Create some links to the editor object.
|
|
|
|
this.oTextHandle = null;
|
|
|
|
this.sCurrentText = 'sText' in this.opt ? this.opt.sText : '';
|
|
|
|
|
|
|
|
// How big?
|
|
|
|
this.sEditWidth = 'sEditWidth' in this.opt ? this.opt.sEditWidth : '70%';
|
|
|
|
this.sEditHeight = 'sEditHeight' in this.opt ? this.opt.sEditHeight : '150px';
|
|
|
|
|
|
|
|
this.showDebug = false;
|
|
|
|
this.bRichTextEnabled = 'bWysiwyg' in this.opt && this.opt.bWysiwyg;
|
|
|
|
// This doesn't work on Opera as they cannot restore focus after clicking a BBC button.
|
|
|
|
this.bRichTextPossible = !this.opt.bRichEditOff && ((is_ie5up && !is_ie50) || is_ff || is_opera95up || is_safari || is_chrome) && !(is_iphone || is_android);
|
|
|
|
|
|
|
|
this.oFrameHandle = null;
|
|
|
|
this.oFrameDocument = null;
|
|
|
|
this.oFrameWindow = null;
|
|
|
|
|
|
|
|
// These hold the breadcrumb.
|
|
|
|
this.oBreadHandle = null;
|
|
|
|
this.oResizerElement = null;
|
|
|
|
|
|
|
|
// Kinda holds all the useful stuff.
|
|
|
|
this.aKeyboardShortcuts = new Array();
|
|
|
|
|
|
|
|
// This tracks the cursor position on IE to avoid refocus problems.
|
|
|
|
this.cursorX = 0;
|
|
|
|
this.cursorY = 0;
|
|
|
|
|
|
|
|
// This is all the elements that can have a simple execCommand.
|
|
|
|
this.oSimpleExec = {
|
|
|
|
b: 'bold',
|
|
|
|
u: 'underline',
|
|
|
|
i: 'italic',
|
|
|
|
s: 'strikethrough',
|
|
|
|
left: 'justifyleft',
|
|
|
|
center: 'justifycenter',
|
|
|
|
right: 'justifyright',
|
|
|
|
hr: 'inserthorizontalrule',
|
|
|
|
list: 'insertunorderedlist',
|
|
|
|
orderlist: 'insertorderedlist',
|
|
|
|
sub: 'subscript',
|
|
|
|
sup: 'superscript',
|
|
|
|
indent: 'indent',
|
|
|
|
outdent: 'outdent'
|
|
|
|
}
|
|
|
|
|
|
|
|
// Codes to call a private function
|
|
|
|
this.oSmfExec = {
|
|
|
|
unformat: 'removeFormatting',
|
|
|
|
toggle: 'toggleView'
|
|
|
|
}
|
|
|
|
|
|
|
|
// Any special breadcrumb mappings to ensure we show a consistant tag name.
|
|
|
|
this.breadCrumbNameTags = {
|
|
|
|
strike: 's',
|
|
|
|
strong: 'b',
|
|
|
|
em: 'i'
|
|
|
|
}
|
|
|
|
|
|
|
|
this.aBreadCrumbNameStyles = [
|
|
|
|
{
|
|
|
|
sStyleType: 'text-decoration',
|
|
|
|
sStyleValue: 'underline',
|
|
|
|
sBbcTag: 'u'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sStyleType: 'text-decoration',
|
|
|
|
sStyleValue: 'line-through',
|
|
|
|
sBbcTag: 's'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sStyleType: 'text-align',
|
|
|
|
sStyleValue: 'left',
|
|
|
|
sBbcTag: 'left'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sStyleType: 'text-align',
|
|
|
|
sStyleValue: 'center',
|
|
|
|
sBbcTag: 'center'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sStyleType: 'text-align',
|
|
|
|
sStyleValue: 'right',
|
|
|
|
sBbcTag: 'right'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sStyleType: 'font-weight',
|
|
|
|
sStyleValue: 'bold',
|
|
|
|
sBbcTag: 'b'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sStyleType: 'font-style',
|
|
|
|
sStyleValue: 'italic',
|
|
|
|
sBbcTag: 'i'
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
// All the fonts in the world.
|
|
|
|
this.aFontFaces = [
|
|
|
|
'Arial',
|
|
|
|
'Arial Black',
|
|
|
|
'Impact',
|
|
|
|
'Verdana',
|
|
|
|
'Times New Roman',
|
|
|
|
'Georgia',
|
|
|
|
'Andale Mono',
|
|
|
|
'Trebuchet MS',
|
|
|
|
'Comic Sans MS'
|
|
|
|
];
|
|
|
|
// Font maps (HTML => CSS size)
|
|
|
|
this.aFontSizes = [
|
|
|
|
0,
|
|
|
|
8,
|
|
|
|
10,
|
|
|
|
12,
|
|
|
|
14,
|
|
|
|
18,
|
|
|
|
24,
|
|
|
|
36
|
|
|
|
];
|
|
|
|
// Color maps! (hex => name)
|
|
|
|
this.oFontColors = {
|
|
|
|
black: '#000000',
|
|
|
|
red: '#ff0000',
|
|
|
|
yellow: '#ffff00',
|
|
|
|
pink: '#ffc0cb',
|
|
|
|
green: '#008000',
|
|
|
|
orange: '#ffa500',
|
|
|
|
purple: '#800080',
|
|
|
|
blue: '#0000ff',
|
|
|
|
beige: '#f5f5dc',
|
|
|
|
brown: '#a52a2a',
|
|
|
|
teal: '#008080',
|
|
|
|
navy: '#000080',
|
|
|
|
maroon: '#800000',
|
|
|
|
limegreen: '#32cd32'
|
|
|
|
}
|
|
|
|
|
|
|
|
this.sFormId = 'sFormId' in this.opt ? this.opt.sFormId : 'postmodify';
|
|
|
|
this.iArrayPosition = smf_editorArray.length;
|
|
|
|
|
|
|
|
// Current resize state.
|
|
|
|
this.osmc_EditorCurrentResize = {};
|
|
|
|
|
|
|
|
this.init();
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.init = function()
|
|
|
|
{
|
|
|
|
// Define the event wrapper functions.
|
|
|
|
var oCaller = this;
|
|
|
|
this.aEventWrappers = {
|
|
|
|
editorKeyUp: function(oEvent) {return oCaller.editorKeyUp(oEvent);},
|
|
|
|
shortcutCheck: function(oEvent) {return oCaller.shortcutCheck(oEvent);},
|
|
|
|
editorBlur: function(oEvent) {return oCaller.editorBlur(oEvent);},
|
|
|
|
editorFocus: function(oEvent) {return oCaller.editorFocus(oEvent);},
|
|
|
|
startResize: function(oEvent) {return oCaller.startResize(oEvent);},
|
|
|
|
resizeOverDocument: function(oEvent) {return oCaller.resizeOverDocument(oEvent);},
|
|
|
|
endResize: function(oEvent) {return oCaller.endResize(oEvent);},
|
|
|
|
resizeOverIframe: function(oEvent) {return oCaller.resizeOverIframe(oEvent);}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set the textHandle.
|
|
|
|
this.oTextHandle = document.getElementById(this.opt.sUniqueId);
|
|
|
|
|
|
|
|
// Ensure the currentText is set correctly depending on the mode.
|
|
|
|
if (this.sCurrentText == '' && !this.bRichTextEnabled)
|
|
|
|
this.sCurrentText = getInnerHTML(this.oTextHandle).php_unhtmlspecialchars();
|
|
|
|
|
|
|
|
// Only try to do this if rich text is supported.
|
|
|
|
if (this.bRichTextPossible)
|
|
|
|
{
|
|
|
|
// Make the iframe itself, stick it next to the current text area, and give it an ID.
|
|
|
|
this.oFrameHandle = document.createElement('iframe');
|
|
|
|
this.oFrameHandle.src = 'about:blank';
|
|
|
|
this.oFrameHandle.id = 'html_' + this.opt.sUniqueId;
|
|
|
|
this.oFrameHandle.className = 'rich_editor_frame';
|
|
|
|
this.oFrameHandle.style.display = 'none';
|
|
|
|
this.oFrameHandle.style.margin = '0px';
|
|
|
|
this.oFrameHandle.tabIndex = this.oTextHandle.tabIndex;
|
|
|
|
this.oTextHandle.parentNode.appendChild(this.oFrameHandle);
|
|
|
|
|
|
|
|
// Create some handy shortcuts.
|
|
|
|
this.oFrameDocument = this.oFrameHandle.contentDocument ? this.oFrameHandle.contentDocument : ('contentWindow' in this.oFrameHandle ? this.oFrameHandle.contentWindow.document : this.oFrameHandle.document);
|
|
|
|
this.oFrameWindow = 'contentWindow' in this.oFrameHandle ? this.oFrameHandle.contentWindow : this.oFrameHandle.document.parentWindow;
|
|
|
|
|
|
|
|
// Create the debug window... and stick this under the main frame - make it invisible by default.
|
|
|
|
this.oBreadHandle = document.createElement('div');
|
|
|
|
this.oBreadHandle.id = 'bread_' . uid;
|
|
|
|
this.oBreadHandle.style.visibility = 'visible';
|
|
|
|
this.oBreadHandle.style.display = 'none';
|
|
|
|
this.oFrameHandle.parentNode.appendChild(this.oBreadHandle);
|
|
|
|
|
|
|
|
// Size the iframe dimensions to something sensible.
|
|
|
|
this.oFrameHandle.style.width = this.sEditWidth;
|
|
|
|
this.oFrameHandle.style.height = this.sEditHeight;
|
|
|
|
this.oFrameHandle.style.visibility = 'visible';
|
|
|
|
|
|
|
|
// Only bother formatting the debug window if debug is enabled.
|
|
|
|
if (this.showDebug)
|
|
|
|
{
|
|
|
|
this.oBreadHandle.style.width = this.sEditWidth;
|
|
|
|
this.oBreadHandle.style.height = '20px';
|
|
|
|
this.oBreadHandle.className = 'windowbg2';
|
|
|
|
this.oBreadHandle.style.border = '1px black solid';
|
|
|
|
this.oBreadHandle.style.display = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate the editor with nothing by default.
|
|
|
|
if (!is_opera95up)
|
|
|
|
{
|
|
|
|
this.oFrameDocument.open();
|
|
|
|
this.oFrameDocument.write('');
|
|
|
|
this.oFrameDocument.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Right to left mode?
|
|
|
|
if (this.opt.bRTL)
|
|
|
|
{
|
|
|
|
this.oFrameDocument.dir = "rtl";
|
|
|
|
this.oFrameDocument.body.dir = "rtl";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark it as editable...
|
|
|
|
if (this.oFrameDocument.body.contentEditable)
|
|
|
|
this.oFrameDocument.body.contentEditable = true;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.oFrameHandle.style.display = '';
|
|
|
|
this.oFrameDocument.designMode = 'on';
|
|
|
|
this.oFrameHandle.style.display = 'none';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now we need to try and style the editor - internet explorer allows us to do the whole lot.
|
|
|
|
if (document.styleSheets['editor_css'] || document.styleSheets['editor_ie_css'])
|
|
|
|
{
|
|
|
|
var oMyStyle = this.oFrameDocument.createElement('style');
|
|
|
|
this.oFrameDocument.documentElement.firstChild.appendChild(oMyStyle);
|
|
|
|
oMyStyle.styleSheet.cssText = document.styleSheets['editor_ie_css'] ? document.styleSheets['editor_ie_css'].cssText : document.styleSheets['editor_css'].cssText;
|
|
|
|
}
|
|
|
|
// Otherwise we seem to have to try to rip out each of the styles one by one!
|
|
|
|
else if (document.styleSheets.length)
|
|
|
|
{
|
|
|
|
var bFoundSomething = false;
|
|
|
|
// First we need to find the right style sheet.
|
|
|
|
for (var i = 0, iNumStyleSheets = document.styleSheets.length; i < iNumStyleSheets; i++)
|
|
|
|
{
|
|
|
|
// Start off looking for the right style sheet.
|
|
|
|
if (!document.styleSheets[i].href || document.styleSheets[i].href.indexOf('editor') < 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Firefox won't allow us to get a CSS file which ain't in the right URL.
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (document.styleSheets[i].cssRules.length < 1)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
catch (e)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Manually try to find the rich_editor class.
|
|
|
|
for (var r = 0, iNumRules = document.styleSheets[i].cssRules.length; r < iNumRules; r++)
|
|
|
|
{
|
|
|
|
// Got the main editor?
|
|
|
|
if (document.styleSheets[i].cssRules[r].selectorText == '.rich_editor')
|
|
|
|
{
|
|
|
|
// Set some possible styles.
|
|
|
|
if (document.styleSheets[i].cssRules[r].style.color)
|
|
|
|
this.oFrameDocument.body.style.color = document.styleSheets[i].cssRules[r].style.color;
|
|
|
|
if (document.styleSheets[i].cssRules[r].style.backgroundColor)
|
|
|
|
this.oFrameDocument.body.style.backgroundColor = document.styleSheets[i].cssRules[r].style.backgroundColor;
|
|
|
|
if (document.styleSheets[i].cssRules[r].style.fontSize)
|
|
|
|
this.oFrameDocument.body.style.fontSize = document.styleSheets[i].cssRules[r].style.fontSize;
|
|
|
|
if (document.styleSheets[i].cssRules[r].style.fontFamily)
|
|
|
|
this.oFrameDocument.body.style.fontFamily = document.styleSheets[i].cssRules[r].style.fontFamily;
|
|
|
|
if (document.styleSheets[i].cssRules[r].style.border)
|
|
|
|
this.oFrameDocument.body.style.border = document.styleSheets[i].cssRules[r].style.border;
|
|
|
|
bFoundSomething = true;
|
|
|
|
}
|
|
|
|
// The frame?
|
|
|
|
else if (document.styleSheets[i].cssRules[r].selectorText == '.rich_editor_frame')
|
|
|
|
{
|
|
|
|
if (document.styleSheets[i].cssRules[r].style.border)
|
|
|
|
this.oFrameHandle.style.border = document.styleSheets[i].cssRules[r].style.border;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Didn't find it?
|
|
|
|
if (!bFoundSomething)
|
|
|
|
{
|
|
|
|
// Do something that is better than nothing.
|
|
|
|
this.oFrameDocument.body.style.color = 'black';
|
|
|
|
this.oFrameDocument.body.style.backgroundColor = 'white';
|
|
|
|
this.oFrameDocument.body.style.fontSize = '78%';
|
|
|
|
this.oFrameDocument.body.style.fontFamily = '"Verdana", "Arial", "Helvetica", "sans-serif"';
|
|
|
|
this.oFrameDocument.body.style.border = 'none';
|
|
|
|
this.oFrameHandle.style.border = '1px solid #808080';
|
|
|
|
if (is_opera)
|
|
|
|
this.oFrameDocument.body.style.height = '99%';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the class...
|
|
|
|
this.oFrameDocument.body.className = 'rich_editor';
|
|
|
|
|
|
|
|
// Set the frame padding/margin inside the editor.
|
|
|
|
this.oFrameDocument.body.style.padding = '1px';
|
|
|
|
this.oFrameDocument.body.style.margin = '0';
|
|
|
|
|
|
|
|
// Listen for input.
|
|
|
|
this.oFrameDocument.instanceRef = this;
|
|
|
|
this.oFrameHandle.instanceRef = this;
|
|
|
|
this.oTextHandle.instanceRef = this;
|
|
|
|
|
|
|
|
// Attach addEventListener for those browsers that don't support it.
|
|
|
|
createEventListener(this.oFrameHandle);
|
|
|
|
createEventListener(this.oFrameDocument);
|
|
|
|
createEventListener(this.oTextHandle);
|
|
|
|
createEventListener(window);
|
|
|
|
createEventListener(document);
|
|
|
|
|
|
|
|
// Attach functions to the key and mouse events.
|
|
|
|
this.oFrameDocument.addEventListener('keyup', this.aEventWrappers.editorKeyUp, true);
|
|
|
|
this.oFrameDocument.addEventListener('mouseup', this.aEventWrappers.editorKeyUp, true);
|
|
|
|
this.oFrameDocument.addEventListener('keydown', this.aEventWrappers.shortcutCheck, true);
|
|
|
|
this.oTextHandle.addEventListener('keydown', this.aEventWrappers.shortcutCheck, true);
|
|
|
|
|
|
|
|
if (is_ie)
|
|
|
|
{
|
|
|
|
this.oFrameDocument.addEventListener('blur', this.aEventWrappers.editorBlur, true);
|
|
|
|
this.oFrameDocument.addEventListener('focus', this.aEventWrappers.editorFocus, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show the iframe only if wysiwyrg is on - and hide the text area.
|
|
|
|
this.oTextHandle.style.display = this.bRichTextEnabled ? 'none' : '';
|
|
|
|
this.oFrameHandle.style.display = this.bRichTextEnabled ? '' : 'none';
|
|
|
|
this.oBreadHandle.style.display = this.bRichTextEnabled ? '' : 'none';
|
|
|
|
}
|
|
|
|
// If we can't do advanced stuff then just do the basics.
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Cannot have WYSIWYG anyway!
|
|
|
|
this.bRichTextEnabled = false;
|
|
|
|
|
|
|
|
// We need some of the event handlers.
|
|
|
|
createEventListener(this.oTextHandle);
|
|
|
|
createEventListener(window);
|
|
|
|
createEventListener(document);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure we set the message mode correctly.
|
|
|
|
document.getElementById(this.opt.sUniqueId + '_mode').value = this.bRichTextEnabled ? 1 : 0;
|
|
|
|
|
|
|
|
// Show the resizer.
|
|
|
|
if (document.getElementById(this.opt.sUniqueId + '_resizer') && (!is_opera || is_opera95up) && !(is_chrome && !this.bRichTextEnabled))
|
|
|
|
{
|
|
|
|
// Currently nothing is being resized...I assume!
|
|
|
|
window.smf_oCurrentResizeEditor = null;
|
|
|
|
|
|
|
|
this.oResizerElement = document.getElementById(this.opt.sUniqueId + '_resizer');
|
|
|
|
this.oResizerElement.style.display = '';
|
|
|
|
|
|
|
|
createEventListener(this.oResizerElement);
|
|
|
|
this.oResizerElement.addEventListener('mousedown', this.aEventWrappers.startResize, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the text - if WYSIWYG is enabled that is.
|
|
|
|
if (this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
this.insertText(this.sCurrentText, true);
|
|
|
|
|
|
|
|
// Better make us the focus!
|
|
|
|
this.setFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, register shortcuts.
|
|
|
|
this.registerDefaultShortcuts();
|
|
|
|
this.updateEditorControls();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the current text.
|
|
|
|
smc_Editor.prototype.getText = function(bPrepareEntities, bModeOverride)
|
|
|
|
{
|
|
|
|
var bCurMode = typeof(bModeOverride) != 'undefined' ? bModeOverride : this.bRichTextEnabled;
|
|
|
|
|
|
|
|
if (!bCurMode || this.oFrameDocument == null)
|
|
|
|
{
|
|
|
|
var sText = this.oTextHandle.value;
|
|
|
|
if (bPrepareEntities)
|
|
|
|
sText = sText.replace(/</g, '#smlt#').replace(/>/g, '#smgt#').replace(/&/g, '#smamp#');
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var sText = this.oFrameDocument.body.innerHTML;
|
|
|
|
if (bPrepareEntities)
|
|
|
|
sText = sText.replace(/</g, '#smlt#').replace(/>/g, '#smgt#').replace(/&/g, '#smamp#');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean it up - including removing semi-colons.
|
|
|
|
if (bPrepareEntities)
|
|
|
|
sText = sText.replace(/ /g, ' ').replace(/;/g, '#smcol#');
|
|
|
|
|
|
|
|
// Return it.
|
|
|
|
return sText;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the current text.
|
|
|
|
smc_Editor.prototype.unprotectText = function(sText)
|
|
|
|
{
|
|
|
|
var bCurMode = typeof(bModeOverride) != 'undefined' ? bModeOverride : this.bRichTextEnabled;
|
|
|
|
|
|
|
|
// This restores smlt, smgt and smamp into boring entities, to unprotect against XML'd information like quotes.
|
|
|
|
sText = sText.replace(/#smlt#/g, '<').replace(/#smgt#/g, '>').replace(/#smamp#/g, '&');
|
|
|
|
|
|
|
|
// Return it.
|
|
|
|
return sText;
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.editorKeyUp = function()
|
|
|
|
{
|
|
|
|
// Rebuild the breadcrumb.
|
|
|
|
this.updateEditorControls();
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.editorBlur = function()
|
|
|
|
{
|
|
|
|
if (!is_ie)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Need to do something here.
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.editorFocus = function()
|
|
|
|
{
|
|
|
|
if (!is_ie)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Need to do something here.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rebuild the breadcrumb etc - and set things to the correct context.
|
|
|
|
smc_Editor.prototype.updateEditorControls = function()
|
|
|
|
{
|
|
|
|
// Everything else is specific to HTML mode.
|
|
|
|
if (!this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
// Set none of the buttons active.
|
|
|
|
if (this.opt.oBBCBox)
|
|
|
|
this.opt.oBBCBox.setActive([]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var aCrumb = new Array();
|
|
|
|
var aAllCrumbs = new Array();
|
|
|
|
var iMaxLength = 6;
|
|
|
|
|
|
|
|
// What is the current element?
|
|
|
|
var oCurTag = this.getCurElement();
|
|
|
|
|
|
|
|
var i = 0;
|
|
|
|
while (typeof(oCurTag) == 'object' && oCurTag != null && oCurTag.nodeName.toLowerCase() != 'body' && i < iMaxLength)
|
|
|
|
{
|
|
|
|
aCrumb[i++] = oCurTag;
|
|
|
|
oCurTag = oCurTag.parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now print out the tree.
|
|
|
|
var sTree = '';
|
|
|
|
var sCurFontName = '';
|
|
|
|
var sCurFontSize = '';
|
|
|
|
var sCurFontColor = '';
|
|
|
|
for (var i = 0, iNumCrumbs = aCrumb.length; i < iNumCrumbs; i++)
|
|
|
|
{
|
|
|
|
var sCrumbName = aCrumb[i].nodeName.toLowerCase();
|
|
|
|
|
|
|
|
// Does it have an alternative name?
|
|
|
|
if (sCrumbName in this.breadCrumbNameTags)
|
|
|
|
sCrumbName = this.breadCrumbNameTags[sCrumbName];
|
|
|
|
// Don't bother with this...
|
|
|
|
else if (sCrumbName == 'p')
|
|
|
|
continue;
|
|
|
|
// A link?
|
|
|
|
else if (sCrumbName == 'a')
|
|
|
|
{
|
|
|
|
var sUrlInfo = aCrumb[i].getAttribute('href');
|
|
|
|
sCrumbName = 'url';
|
|
|
|
if (typeof(sUrlInfo) == 'string')
|
|
|
|
{
|
|
|
|
if (sUrlInfo.substr(0, 3) == 'ftp')
|
|
|
|
sCrumbName = 'ftp';
|
|
|
|
else if (sUrlInfo.substr(0, 6) == 'mailto')
|
|
|
|
sCrumbName = 'email';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (sCrumbName == 'span' || sCrumbName == 'div')
|
|
|
|
{
|
|
|
|
if (aCrumb[i].style)
|
|
|
|
{
|
|
|
|
for (var j = 0, iNumStyles = this.aBreadCrumbNameStyles.length; j < iNumStyles; j++)
|
|
|
|
{
|
|
|
|
// Do we have a font?
|
|
|
|
if (aCrumb[i].style.fontFamily && aCrumb[i].style.fontFamily != '' && sCurFontName == '')
|
|
|
|
{
|
|
|
|
sCurFontName = aCrumb[i].style.fontFamily;
|
|
|
|
sCrumbName = 'face';
|
|
|
|
}
|
|
|
|
// ... or a font size?
|
|
|
|
if (aCrumb[i].style.fontSize && aCrumb[i].style.fontSize != '' && sCurFontSize == '')
|
|
|
|
{
|
|
|
|
sCurFontSize = aCrumb[i].style.fontSize;
|
|
|
|
sCrumbName = 'size';
|
|
|
|
}
|
|
|
|
// ... even color?
|
|
|
|
if (aCrumb[i].style.color && aCrumb[i].style.color != '' && sCurFontColor == '')
|
|
|
|
{
|
|
|
|
sCurFontColor = aCrumb[i].style.color;
|
|
|
|
if (in_array(sCurFontColor, this.oFontColors))
|
|
|
|
sCurFontColor = array_search(sCurFontColor, this.oFontColors);
|
|
|
|
sCrumbName = 'color';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.aBreadCrumbNameStyles[j].sStyleType == 'text-align' && aCrumb[i].style.textAlign && aCrumb[i].style.textAlign == this.aBreadCrumbNameStyles[j].sStyleValue)
|
|
|
|
sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag;
|
|
|
|
else if (this.aBreadCrumbNameStyles[j].sStyleType == 'text-decoration' && aCrumb[i].style.textDecoration && aCrumb[i].style.textDecoration == this.aBreadCrumbNameStyles[j].sStyleValue)
|
|
|
|
sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag;
|
|
|
|
else if (this.aBreadCrumbNameStyles[j].sStyleType == 'font-weight' && aCrumb[i].style.fontWeight && aCrumb[i].style.fontWeight == this.aBreadCrumbNameStyles[j].sStyleValue)
|
|
|
|
sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag;
|
|
|
|
else if (this.aBreadCrumbNameStyles[j].sStyleType == 'font-style' && aCrumb[i].style.fontStyle && aCrumb[i].style.fontStyle == this.aBreadCrumbNameStyles[j].sStyleValue)
|
|
|
|
sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Do we have a font?
|
|
|
|
else if (sCrumbName == 'font')
|
|
|
|
{
|
|
|
|
if (aCrumb[i].getAttribute('face') && sCurFontName == '')
|
|
|
|
{
|
|
|
|
sCurFontName = aCrumb[i].getAttribute('face').toLowerCase();
|
|
|
|
sCrumbName = 'face';
|
|
|
|
}
|
|
|
|
if (aCrumb[i].getAttribute('size') && sCurFontSize == '')
|
|
|
|
{
|
|
|
|
sCurFontSize = aCrumb[i].getAttribute('size');
|
|
|
|
sCrumbName = 'size';
|
|
|
|
}
|
|
|
|
if (aCrumb[i].getAttribute('color') && sCurFontColor == '')
|
|
|
|
{
|
|
|
|
sCurFontColor = aCrumb[i].getAttribute('color');
|
|
|
|
if (in_array(sCurFontColor, this.oFontColors))
|
|
|
|
sCurFontColor = array_search(sCurFontColor, this.oFontColors);
|
|
|
|
sCrumbName = 'color';
|
|
|
|
}
|
|
|
|
// Something else - ignore.
|
|
|
|
if (sCrumbName == 'font')
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
sTree += (i != 0 ? ' <strong>></strong>' : '') + ' ' + sCrumbName;
|
|
|
|
aAllCrumbs[aAllCrumbs.length] = sCrumbName;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since we're in WYSIWYG state, show the toggle button as active.
|
|
|
|
aAllCrumbs[aAllCrumbs.length] = 'toggle';
|
|
|
|
|
|
|
|
this.opt.oBBCBox.setActive(aAllCrumbs);
|
|
|
|
|
|
|
|
// Try set the font boxes correct.
|
|
|
|
this.opt.oBBCBox.setSelect('sel_face', sCurFontName);
|
|
|
|
this.opt.oBBCBox.setSelect('sel_size', sCurFontSize);
|
|
|
|
this.opt.oBBCBox.setSelect('sel_color', sCurFontColor);
|
|
|
|
|
|
|
|
if (this.showDebug)
|
|
|
|
setInnerHTML(this.oBreadHandle, sTree);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the HTML content to be that of the text box - if we are in wysiwyg mode.
|
|
|
|
smc_Editor.prototype.doSubmit = function()
|
|
|
|
{
|
|
|
|
if (this.bRichTextEnabled)
|
|
|
|
this.oTextHandle.value = this.oFrameDocument.body.innerHTML;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate the box with text.
|
|
|
|
smc_Editor.prototype.insertText = function(sText, bClear, bForceEntityReverse, iMoveCursorBack)
|
|
|
|
{
|
|
|
|
if (bForceEntityReverse)
|
|
|
|
sText = this.unprotectText(sText);
|
|
|
|
|
|
|
|
// Erase it all?
|
|
|
|
if (bClear)
|
|
|
|
{
|
|
|
|
if (this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
// This includes a work around for FF to get the cursor to show!
|
|
|
|
this.oFrameDocument.body.innerHTML = sText;
|
|
|
|
|
|
|
|
// If FF trick the cursor into coming back!
|
|
|
|
if (is_ff || is_opera)
|
|
|
|
{
|
|
|
|
// For some entirely unknown reason FF3 Beta 2 and some Opera versions
|
|
|
|
// require this.
|
|
|
|
this.oFrameDocument.body.contentEditable = false;
|
|
|
|
|
|
|
|
this.oFrameDocument.designMode = 'off';
|
|
|
|
this.oFrameDocument.designMode = 'on';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
this.oTextHandle.value = sText;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.setFocus();
|
|
|
|
if (this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
// IE croaks if you have an image selected and try to insert!
|
|
|
|
if ('selection' in this.oFrameDocument && this.oFrameDocument.selection.type != 'Text' && this.oFrameDocument.selection.type != 'None' && this.oFrameDocument.selection.clear)
|
|
|
|
this.oFrameDocument.selection.clear();
|
|
|
|
|
|
|
|
var oRange = this.getRange();
|
|
|
|
|
|
|
|
if (oRange.pasteHTML)
|
|
|
|
{
|
|
|
|
oRange.pasteHTML(sText);
|
|
|
|
|
|
|
|
// Do we want to move the cursor back at all?
|
|
|
|
if (iMoveCursorBack)
|
|
|
|
oRange.moveEnd('character', -iMoveCursorBack);
|
|
|
|
|
|
|
|
oRange.select();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If the cursor needs to be positioned, insert the last fragment first.
|
|
|
|
if (typeof(iMoveCursorBack) != 'undefined' && iMoveCursorBack > 0 && sText.length > iMoveCursorBack)
|
|
|
|
{
|
|
|
|
var oSelection = this.getSelect(false, false);
|
|
|
|
var oRange = oSelection.getRangeAt(0);
|
|
|
|
oRange.insertNode(this.oFrameDocument.createTextNode(sText.substr(sText.length - iMoveCursorBack)));
|
|
|
|
}
|
|
|
|
|
|
|
|
this.smf_execCommand('inserthtml', false, typeof(iMoveCursorBack) == 'undefined' ? sText : sText.substr(0, sText.length - iMoveCursorBack));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
replaceText(sText, this.oTextHandle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Special handler for WYSIWYG.
|
|
|
|
smc_Editor.prototype.smf_execCommand = function(sCommand, bUi, sValue)
|
|
|
|
{
|
|
|
|
return this.oFrameDocument.execCommand(sCommand, bUi, sValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.insertSmiley = function(oSmileyProperties)
|
|
|
|
{
|
|
|
|
// In text mode we just add it in as we always did.
|
|
|
|
if (!this.bRichTextEnabled)
|
|
|
|
this.insertText(' ' + oSmileyProperties.sCode);
|
|
|
|
|
|
|
|
// Otherwise we need to do a whole image...
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var iUniqueSmileyId = 1000 + Math.floor(Math.random() * 100000);
|
|
|
|
this.insertText('<img src="' + oSmileyProperties.sSrc + '" id="smiley_' + iUniqueSmileyId + '_' + oSmileyProperties.sSrc.replace(/^.*\//, '') + '" onresizestart="return false;" align="bottom" alt="" title="' + oSmileyProperties.sDescription.php_htmlspecialchars() + '" style="padding: 0 3px 0 3px;" />');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.handleButtonClick = function (oButtonProperties)
|
|
|
|
{
|
|
|
|
this.setFocus();
|
|
|
|
|
|
|
|
// A special SMF function?
|
|
|
|
if (oButtonProperties.sCode in this.oSmfExec)
|
|
|
|
this[this.oSmfExec[oButtonProperties.sCode]]();
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// In text this is easy...
|
|
|
|
if (!this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
// Replace?
|
|
|
|
if (!('sAfter' in oButtonProperties) || oButtonProperties.sAfter == null)
|
|
|
|
replaceText(oButtonProperties.sBefore.replace(/\\n/g, '\n'), this.oTextHandle)
|
|
|
|
|
|
|
|
// Surround!
|
|
|
|
else
|
|
|
|
surroundText(oButtonProperties.sBefore.replace(/\\n/g, '\n'), oButtonProperties.sAfter.replace(/\\n/g, '\n'), this.oTextHandle)
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Is it easy?
|
|
|
|
if (oButtonProperties.sCode in this.oSimpleExec)
|
|
|
|
this.smf_execCommand(this.oSimpleExec[oButtonProperties.sCode], false, null);
|
|
|
|
|
|
|
|
// A link?
|
|
|
|
else if (oButtonProperties.sCode == 'url' || oButtonProperties.sCode == 'email' || oButtonProperties.sCode == 'ftp')
|
|
|
|
this.insertLink(oButtonProperties.sCode);
|
|
|
|
|
|
|
|
// Maybe an image?
|
|
|
|
else if (oButtonProperties.sCode == 'img')
|
|
|
|
this.insertImage();
|
|
|
|
|
|
|
|
// Everything else means doing something ourselves.
|
|
|
|
else if ('sBefore' in oButtonProperties)
|
|
|
|
this.insertCustomHTML(oButtonProperties.sBefore.replace(/\\n/g, '\n'), oButtonProperties.sAfter.replace(/\\n/g, '\n'));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.updateEditorControls();
|
|
|
|
|
|
|
|
// Finally set the focus.
|
|
|
|
this.setFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Changing a select box?
|
|
|
|
smc_Editor.prototype.handleSelectChange = function (oSelectProperties)
|
|
|
|
{
|
|
|
|
this.setFocus();
|
|
|
|
|
|
|
|
var sValue = oSelectProperties.oSelect.value;
|
|
|
|
if (sValue == '')
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Changing font face?
|
|
|
|
if (oSelectProperties.sName == 'sel_face')
|
|
|
|
{
|
|
|
|
// Not in HTML mode?
|
|
|
|
if (!this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
sValue = sValue.replace(/"/, '');
|
|
|
|
surroundText('[font=' + sValue + ']', '[/font]', this.oTextHandle);
|
|
|
|
oSelectProperties.oSelect.selectedIndex = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (is_webkit)
|
|
|
|
this.smf_execCommand('styleWithCSS', false, true);
|
|
|
|
this.smf_execCommand('fontname', false, sValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Font size?
|
|
|
|
else if (oSelectProperties.sName == 'sel_size')
|
|
|
|
{
|
|
|
|
// Are we in boring mode?
|
|
|
|
if (!this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
surroundText('[size=' + this.aFontSizes[sValue] + 'pt]', '[/size]', this.oTextHandle);
|
|
|
|
oSelectProperties.oSelect.selectedIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
this.smf_execCommand('fontsize', false, sValue);
|
|
|
|
}
|
|
|
|
// Or color even?
|
|
|
|
else if (oSelectProperties.sName == 'sel_color')
|
|
|
|
{
|
|
|
|
// Are we in boring mode?
|
|
|
|
if (!this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
surroundText('[color=' + sValue + ']', '[/color]', this.oTextHandle);
|
|
|
|
oSelectProperties.oSelect.selectedIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
this.smf_execCommand('forecolor', false, sValue);
|
|
|
|
}
|
|
|
|
|
2014-02-03 23:09:40 -05:00
|
|
|
else if (oSelectProperties.sName == 'sel_code')
|
|
|
|
{
|
|
|
|
// Are we in boring mode?
|
|
|
|
if (!this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
surroundText('[code=' + sValue + ']', '[/code]', this.oTextHandle);
|
|
|
|
oSelectProperties.oSelect.selectedIndex = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-03 21:58:48 -05:00
|
|
|
this.updateEditorControls();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put in some custom HTML.
|
|
|
|
smc_Editor.prototype.insertCustomHTML = function(sLeftTag, sRightTag)
|
|
|
|
{
|
|
|
|
var sSelection = this.getSelect(true, true);
|
|
|
|
if (sSelection.length == 0)
|
|
|
|
sSelection = '';
|
|
|
|
|
|
|
|
// Are we overwriting?
|
|
|
|
if (sRightTag == '')
|
|
|
|
this.insertText(sLeftTag);
|
|
|
|
// If something was selected, replace and position cursor at the end of it.
|
|
|
|
else if (sSelection.length > 0)
|
|
|
|
this.insertText(sLeftTag + sSelection + sRightTag, false, false, 0);
|
|
|
|
// Wrap the tags around the cursor position.
|
|
|
|
else
|
|
|
|
this.insertText(sLeftTag + sRightTag, false, false, sRightTag.length);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert a URL link.
|
|
|
|
smc_Editor.prototype.insertLink = function(sType)
|
|
|
|
{
|
|
|
|
if (sType == 'email')
|
|
|
|
var sPromptText = oEditorStrings['prompt_text_email'];
|
|
|
|
else if (sType == 'ftp')
|
|
|
|
var sPromptText = oEditorStrings['prompt_text_ftp'];
|
|
|
|
else
|
|
|
|
var sPromptText = oEditorStrings['prompt_text_url'];
|
|
|
|
|
|
|
|
// IE has a nice prompt for this - others don't.
|
|
|
|
if (sType != 'email' && sType != 'ftp' && is_ie)
|
|
|
|
this.smf_execCommand('createlink', true, 'http://');
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Ask them where to link to.
|
|
|
|
var sText = prompt(sPromptText, sType == 'email' ? '' : (sType == 'ftp' ? 'ftp://' : 'http://'));
|
|
|
|
if (!sText)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sType == 'email' && sText.indexOf('mailto:') != 0)
|
|
|
|
sText = 'mailto:' + sText;
|
|
|
|
|
|
|
|
// Check if we have text selected and if not force us to have some.
|
|
|
|
var oCurText = this.getSelect(true, true);
|
|
|
|
|
|
|
|
if (oCurText.toString().length != 0)
|
|
|
|
{
|
|
|
|
this.smf_execCommand('unlink');
|
|
|
|
this.smf_execCommand('createlink', false, sText);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
this.insertText('<a href="' + sText + '">' + sText + '</a>');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.insertImage = function(sSrc)
|
|
|
|
{
|
|
|
|
if (!sSrc)
|
|
|
|
{
|
|
|
|
sSrc = prompt(oEditorStrings['prompt_text_img'], 'http://');
|
|
|
|
if (!sSrc || sSrc.length < 10)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.smf_execCommand('insertimage', false, sSrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.getSelect = function(bWantText, bWantHTMLText)
|
|
|
|
{
|
|
|
|
if (is_ie && 'selection' in this.oFrameDocument)
|
|
|
|
{
|
|
|
|
// Just want plain text?
|
|
|
|
if (bWantText && !bWantHTMLText)
|
|
|
|
return this.oFrameDocument.selection.createRange().text;
|
|
|
|
// We want the HTML flavoured variety?
|
|
|
|
else if (bWantHTMLText)
|
|
|
|
return this.oFrameDocument.selection.createRange().htmlText;
|
|
|
|
|
|
|
|
return this.oFrameDocument.selection;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is mainly Firefox.
|
|
|
|
if ('getSelection' in this.oFrameWindow)
|
|
|
|
{
|
|
|
|
// Plain text?
|
|
|
|
if (bWantText && !bWantHTMLText)
|
|
|
|
return this.oFrameWindow.getSelection().toString();
|
|
|
|
|
|
|
|
// HTML is harder - currently using: http://www.faqts.com/knowledge_base/view.phtml/aid/32427
|
|
|
|
else if (bWantHTMLText)
|
|
|
|
{
|
|
|
|
var oSelection = this.oFrameWindow.getSelection();
|
|
|
|
if (oSelection.rangeCount > 0)
|
|
|
|
{
|
|
|
|
var oRange = oSelection.getRangeAt(0);
|
|
|
|
var oClonedSelection = oRange.cloneContents();
|
|
|
|
var oDiv = this.oFrameDocument.createElement('div');
|
|
|
|
oDiv.appendChild(oClonedSelection);
|
|
|
|
return oDiv.innerHTML;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Want the whole object then.
|
|
|
|
return this.oFrameWindow.getSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're here it's not good.
|
|
|
|
return this.oFrameDocument.getSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.getRange = function()
|
|
|
|
{
|
|
|
|
// Get the current selection.
|
|
|
|
var oSelection = this.getSelect();
|
|
|
|
|
|
|
|
if (!oSelection)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
if (is_ie && oSelection.createRange)
|
|
|
|
return oSelection.createRange();
|
|
|
|
|
|
|
|
return oSelection.rangeCount == 0 ? null : oSelection.getRangeAt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the current element.
|
|
|
|
smc_Editor.prototype.getCurElement = function()
|
|
|
|
{
|
|
|
|
var oRange = this.getRange();
|
|
|
|
|
|
|
|
if (!oRange)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
if (is_ie)
|
|
|
|
{
|
|
|
|
if (oRange.item)
|
|
|
|
return oRange.item(0);
|
|
|
|
else
|
|
|
|
return oRange.parentElement();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var oElement = oRange.commonAncestorContainer;
|
|
|
|
return this.getParentElement(oElement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.getParentElement = function(oNode)
|
|
|
|
{
|
|
|
|
if (oNode.nodeType == 1)
|
|
|
|
return oNode;
|
|
|
|
|
|
|
|
for (var i = 0; i < 50; i++)
|
|
|
|
{
|
|
|
|
if (!oNode.parentNode)
|
|
|
|
break;
|
|
|
|
|
|
|
|
oNode = oNode.parentNode;
|
|
|
|
if (oNode.nodeType == 1)
|
|
|
|
return oNode;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove formatting for the selected text.
|
|
|
|
smc_Editor.prototype.removeFormatting = function()
|
|
|
|
{
|
|
|
|
// Do both at once.
|
|
|
|
if (this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
this.smf_execCommand('removeformat');
|
|
|
|
this.smf_execCommand('unlink');
|
|
|
|
}
|
|
|
|
// Otherwise do a crude move indeed.
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Get the current selection first.
|
|
|
|
if (this.oTextHandle.caretPos)
|
|
|
|
var sCurrentText = this.oTextHandle.caretPos.text;
|
|
|
|
|
|
|
|
else if ('selectionStart' in this.oTextHandle)
|
|
|
|
var sCurrentText = this.oTextHandle.value.substr(this.oTextHandle.selectionStart, (this.oTextHandle.selectionEnd - this.oTextHandle.selectionStart));
|
|
|
|
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Do bits that are likely to have attributes.
|
|
|
|
sCurrentText = sCurrentText.replace(RegExp("\\[/?(url|img|iurl|ftp|email|img|color|font|size|list|bdo).*?\\]", "g"), '');
|
|
|
|
// Then just anything that looks like BBC.
|
|
|
|
sCurrentText = sCurrentText.replace(RegExp("\\[/?[A-Za-z]+\\]", "g"), '');
|
|
|
|
|
|
|
|
replaceText(sCurrentText, this.oTextHandle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Toggle wysiwyg/normal mode.
|
|
|
|
smc_Editor.prototype.toggleView = function(bView)
|
|
|
|
{
|
|
|
|
if (!this.bRichTextPossible)
|
|
|
|
{
|
|
|
|
alert(oEditorStrings['wont_work']);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overriding or alternating?
|
|
|
|
if (typeof(bView) == 'undefined')
|
|
|
|
bView = !this.bRichTextEnabled;
|
|
|
|
|
|
|
|
this.requestParsedMessage(bView);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request the message in a different form.
|
|
|
|
smc_Editor.prototype.requestParsedMessage = function(bView)
|
|
|
|
{
|
|
|
|
// Replace with a force reload.
|
|
|
|
if (!window.XMLHttpRequest)
|
|
|
|
{
|
|
|
|
alert(oEditorStrings['func_disabled']);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the text.
|
|
|
|
var sText = this.getText(true, !bView).replace(/&#/g, "&#").php_to8bit().php_urlencode();
|
|
|
|
|
|
|
|
this.tmpMethod = sendXMLDocument;
|
|
|
|
this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=' + (bView ? 1 : 0) + ';' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, this.onToggleDataReceived);
|
|
|
|
delete tmpMethod;
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.onToggleDataReceived = function(oXMLDoc)
|
|
|
|
{
|
|
|
|
var sText = '';
|
|
|
|
for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++)
|
|
|
|
sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue;
|
|
|
|
|
|
|
|
// What is this new view we have?
|
|
|
|
this.bRichTextEnabled = oXMLDoc.getElementsByTagName('message')[0].getAttribute('view') != '0';
|
|
|
|
|
|
|
|
if (this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
this.oFrameHandle.style.display = '';
|
|
|
|
if (this.showDebug)
|
|
|
|
this.oBreadHandle.style.display = '';
|
|
|
|
this.oTextHandle.style.display = 'none';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sText = sText.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
|
|
|
this.oFrameHandle.style.display = 'none';
|
|
|
|
this.oBreadHandle.style.display = 'none';
|
|
|
|
this.oTextHandle.style.display = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// First we focus.
|
|
|
|
this.setFocus();
|
|
|
|
|
|
|
|
this.insertText(sText, true);
|
|
|
|
|
|
|
|
// Record the new status.
|
|
|
|
document.getElementById(this.opt.sUniqueId + '_mode').value = this.bRichTextEnabled ? '1' : '0';
|
|
|
|
|
|
|
|
// Rebuild the bread crumb!
|
|
|
|
this.updateEditorControls();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the focus for the editing window.
|
|
|
|
smc_Editor.prototype.setFocus = function(force_both)
|
|
|
|
{
|
|
|
|
if (!this.bRichTextEnabled)
|
|
|
|
this.oTextHandle.focus();
|
|
|
|
else if (is_ff || is_opera)
|
|
|
|
this.oFrameHandle.focus();
|
|
|
|
else
|
|
|
|
this.oFrameWindow.focus();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start up the spellchecker!
|
|
|
|
smc_Editor.prototype.spellCheckStart = function()
|
|
|
|
{
|
|
|
|
if (!spellCheck)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// If we're in HTML mode we need to get the non-HTML text.
|
|
|
|
if (this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
var sText = escape(this.getText(true, 1).php_to8bit());
|
|
|
|
|
|
|
|
this.tmpMethod = sendXMLDocument;
|
|
|
|
this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=0;' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, this.onSpellCheckDataReceived);
|
|
|
|
delete tmpMethod;
|
|
|
|
}
|
|
|
|
// Otherwise start spellchecking right away.
|
|
|
|
else
|
|
|
|
spellCheck(this.sFormId, this.opt.sUniqueId);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This contains the spellcheckable text.
|
|
|
|
smc_Editor.prototype.onSpellCheckDataReceived = function(oXMLDoc)
|
|
|
|
{
|
|
|
|
var sText = '';
|
|
|
|
for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++)
|
|
|
|
sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue;
|
|
|
|
|
|
|
|
sText = sText.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
|
|
|
|
|
|
|
this.oTextHandle.value = sText;
|
|
|
|
spellCheck(this.sFormId, this.opt.sUniqueId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function called when the Spellchecker is finished and ready to pass back.
|
|
|
|
smc_Editor.prototype.spellCheckEnd = function()
|
|
|
|
{
|
|
|
|
// If HTML edit put the text back!
|
|
|
|
if (this.bRichTextEnabled)
|
|
|
|
{
|
|
|
|
var sText = escape(this.getText(true, 0).php_to8bit());
|
|
|
|
|
|
|
|
this.tmpMethod = sendXMLDocument;
|
|
|
|
this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=1;' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, smf_editorArray[this.iArrayPosition].onSpellCheckCompleteDataReceived);
|
|
|
|
delete tmpMethod;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
this.setFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
// The corrected text.
|
|
|
|
smc_Editor.prototype.onSpellCheckCompleteDataReceived = function(oXMLDoc)
|
|
|
|
{
|
|
|
|
var sText = '';
|
|
|
|
for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++)
|
|
|
|
sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue;
|
|
|
|
|
|
|
|
this.insertText(sText, true);
|
|
|
|
this.setFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.resizeTextArea = function(newHeight, newWidth, is_change)
|
|
|
|
{
|
|
|
|
// Work out what the new height is.
|
|
|
|
if (is_change)
|
|
|
|
{
|
|
|
|
// We'll assume pixels but may not be.
|
|
|
|
newHeight = this._calculateNewDimension(this.oTextHandle.style.height, newHeight);
|
|
|
|
if (newWidth)
|
|
|
|
newWidth = this._calculateNewDimension(this.oTextHandle.style.width, newWidth);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do the HTML editor - but only if it's enabled!
|
|
|
|
if (this.bRichTextPossible)
|
|
|
|
{
|
|
|
|
this.oFrameHandle.style.height = newHeight;
|
|
|
|
if (newWidth)
|
|
|
|
this.oFrameHandle.style.width = newWidth;
|
|
|
|
}
|
|
|
|
// Do the text box regardless!
|
|
|
|
this.oTextHandle.style.height = newHeight;
|
|
|
|
if (newWidth)
|
|
|
|
this.oTextHandle.style.width = newWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A utility instruction to save repetition when trying to work out what to change on a height/width.
|
|
|
|
smc_Editor.prototype._calculateNewDimension = function(old_size, change_size)
|
|
|
|
{
|
|
|
|
// We'll assume pixels but may not be.
|
|
|
|
changeReg = change_size.toString().match(/(-)?(\d+)(\D*)/);
|
|
|
|
curReg = old_size.toString().match(/(\d+)(\D*)/);
|
|
|
|
|
|
|
|
if (!changeReg[3])
|
|
|
|
changeReg[3] = 'px';
|
|
|
|
|
|
|
|
if (changeReg[1] == '-')
|
|
|
|
changeReg[2] = 0 - changeReg[2];
|
|
|
|
|
|
|
|
// Both the same type?
|
|
|
|
if (changeReg[3] == curReg[2])
|
|
|
|
{
|
|
|
|
new_size = parseInt(changeReg[2]) + parseInt(curReg[1]);
|
|
|
|
if (new_size < 50)
|
|
|
|
new_size = 50;
|
|
|
|
new_size = new_size.toString() + changeReg[3];
|
|
|
|
}
|
|
|
|
// Is the change a percentage?
|
|
|
|
else if (changeReg[3] == '%')
|
|
|
|
new_size = (parseInt(curReg[1]) + parseInt((parseInt(changeReg[2]) * parseInt(curReg[1])) / 100)).toString() + 'px';
|
|
|
|
// Otherwise just guess!
|
|
|
|
else
|
|
|
|
new_size = (parseInt(curReg[1]) + (parseInt(changeReg[2]) / 10)).toString() + '%';
|
|
|
|
|
|
|
|
return new_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register default keyboard shortcuts.
|
|
|
|
smc_Editor.prototype.registerDefaultShortcuts = function()
|
|
|
|
{
|
|
|
|
if (is_ff)
|
|
|
|
{
|
|
|
|
this.registerShortcut('b', 'ctrl', 'b');
|
|
|
|
this.registerShortcut('u', 'ctrl', 'u');
|
|
|
|
this.registerShortcut('i', 'ctrl', 'i');
|
|
|
|
this.registerShortcut('p', 'alt', 'preview');
|
|
|
|
this.registerShortcut('s', 'alt', 'submit');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register a keyboard shortcut.
|
|
|
|
smc_Editor.prototype.registerShortcut = function(sLetter, sModifiers, sCodeName)
|
|
|
|
{
|
|
|
|
if (!sCodeName)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var oNewShortcut = {
|
|
|
|
code : sCodeName,
|
|
|
|
key: sLetter.toUpperCase().charCodeAt(0),
|
|
|
|
alt : false,
|
|
|
|
ctrl : false
|
|
|
|
};
|
|
|
|
|
|
|
|
var aSplitModifiers = sModifiers.split(',');
|
|
|
|
for(var i = 0, n = aSplitModifiers.length; i < n; i++)
|
|
|
|
if (aSplitModifiers[i] in oNewShortcut)
|
|
|
|
oNewShortcut[aSplitModifiers[i]] = true;
|
|
|
|
|
|
|
|
this.aKeyboardShortcuts[this.aKeyboardShortcuts.length] = oNewShortcut;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether the key has triggered a shortcut?
|
|
|
|
smc_Editor.prototype.checkShortcut = function(oEvent)
|
|
|
|
{
|
|
|
|
// To be a shortcut it needs to be one of these, duh!
|
|
|
|
if (!oEvent.altKey && !oEvent.ctrlKey)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
var sReturnCode = false;
|
|
|
|
|
|
|
|
// Let's take a look at each of our shortcuts shall we?
|
|
|
|
for (var i = 0, n = this.aKeyboardShortcuts.length; i < n; i++)
|
|
|
|
{
|
|
|
|
// Found something?
|
|
|
|
if (oEvent.altKey == this.aKeyboardShortcuts[i].alt && oEvent.ctrlKey == this.aKeyboardShortcuts[i].ctrl && oEvent.keyCode == this.aKeyboardShortcuts[i].key)
|
|
|
|
sReturnCode = this.aKeyboardShortcuts[i].code;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sReturnCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The actual event check for the above!
|
|
|
|
smc_Editor.prototype.shortcutCheck = function(oEvent)
|
|
|
|
{
|
|
|
|
var sFoundCode = this.checkShortcut(oEvent);
|
|
|
|
|
|
|
|
// Run it and exit.
|
|
|
|
if (typeof(sFoundCode) == 'string' && sFoundCode != '')
|
|
|
|
{
|
|
|
|
var bCancelEvent = false;
|
|
|
|
if (sFoundCode == 'submit')
|
|
|
|
{
|
|
|
|
// So much to do!
|
|
|
|
var oForm = document.getElementById(this.sFormId);
|
|
|
|
submitThisOnce(oForm);
|
|
|
|
submitonce(oForm);
|
|
|
|
smc_saveEntities(oForm.name, ['subject', this.opt.sUniqueId, 'guestname', 'evtitle', 'question']);
|
|
|
|
oForm.submit();
|
|
|
|
|
|
|
|
bCancelEvent = true;
|
|
|
|
}
|
|
|
|
else if (sFoundCode == 'preview')
|
|
|
|
{
|
|
|
|
previewPost();
|
|
|
|
bCancelEvent = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
bCancelEvent = this.opt.oBBCBox.emulateClick(sFoundCode);
|
|
|
|
|
|
|
|
if (bCancelEvent)
|
|
|
|
{
|
|
|
|
if (is_ie && oEvent.cancelBubble)
|
|
|
|
oEvent.cancelBubble = true;
|
|
|
|
|
|
|
|
else if (oEvent.stopPropagation)
|
|
|
|
{
|
|
|
|
oEvent.stopPropagation();
|
|
|
|
oEvent.preventDefault();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the method called after clicking the resize bar.
|
|
|
|
smc_Editor.prototype.startResize = function(oEvent)
|
|
|
|
{
|
|
|
|
if ('event' in window)
|
|
|
|
oEvent = window.event;
|
|
|
|
|
|
|
|
if (!oEvent || window.smf_oCurrentResizeEditor != null)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
window.smf_oCurrentResizeEditor = this.iArrayPosition;
|
|
|
|
|
|
|
|
var aCurCoordinates = smf_mousePose(oEvent);
|
|
|
|
this.osmc_EditorCurrentResize.old_y = aCurCoordinates[1];
|
|
|
|
this.osmc_EditorCurrentResize.old_rel_y = null;
|
|
|
|
this.osmc_EditorCurrentResize.cur_height = parseInt(this.oTextHandle.style.height);
|
|
|
|
|
|
|
|
// Set the necessary events for resizing.
|
|
|
|
var oResizeEntity = is_ie ? document : window;
|
|
|
|
oResizeEntity.addEventListener('mousemove', this.aEventWrappers.resizeOverDocument, false);
|
|
|
|
|
|
|
|
if (this.bRichTextPossible)
|
|
|
|
this.oFrameDocument.addEventListener('mousemove', this.aEventWrappers.resizeOverIframe, false);
|
|
|
|
|
|
|
|
document.addEventListener('mouseup', this.aEventWrappers.endResize, true);
|
|
|
|
|
|
|
|
if (this.bRichTextPossible)
|
|
|
|
this.oFrameDocument.addEventListener('mouseup', this.aEventWrappers.endResize, true);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is kind of a cheat, as it only works over the IFRAME.
|
|
|
|
smc_Editor.prototype.resizeOverIframe = function(oEvent)
|
|
|
|
{
|
|
|
|
if ('event' in window)
|
|
|
|
oEvent = window.event;
|
|
|
|
|
|
|
|
if (!oEvent || window.smf_oCurrentResizeEditor == null)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
var newCords = smf_mousePose(oEvent);
|
|
|
|
|
|
|
|
if (this.osmc_EditorCurrentResize.old_rel_y == null)
|
|
|
|
this.osmc_EditorCurrentResize.old_rel_y = newCords[1];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var iNewHeight = newCords[1] - this.osmc_EditorCurrentResize.old_rel_y + this.osmc_EditorCurrentResize.cur_height;
|
|
|
|
if (iNewHeight < 0)
|
|
|
|
this.endResize();
|
|
|
|
else
|
|
|
|
this.resizeTextArea(iNewHeight + 'px', 0, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This resizes an editor.
|
|
|
|
smc_Editor.prototype.resizeOverDocument = function (oEvent)
|
|
|
|
{
|
|
|
|
if ('event' in window)
|
|
|
|
oEvent = window.event;
|
|
|
|
|
|
|
|
if (!oEvent || window.smf_oCurrentResizeEditor == null)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
var newCords = smf_mousePose(oEvent);
|
|
|
|
|
|
|
|
var iNewHeight = newCords[1] - this.osmc_EditorCurrentResize.old_y + this.osmc_EditorCurrentResize.cur_height;
|
|
|
|
if (iNewHeight < 0)
|
|
|
|
this.endResize();
|
|
|
|
else
|
|
|
|
this.resizeTextArea(iNewHeight + 'px', 0, false);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_Editor.prototype.endResize = function (oEvent)
|
|
|
|
{
|
|
|
|
if ('event' in window)
|
|
|
|
oEvent = window.event;
|
|
|
|
|
|
|
|
if (window.smf_oCurrentResizeEditor == null)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
window.smf_oCurrentResizeEditor = null;
|
|
|
|
|
|
|
|
// Remove the event...
|
|
|
|
var oResizeEntity = is_ie ? document : window;
|
|
|
|
oResizeEntity.removeEventListener('mousemove', this.aEventWrappers.resizeOverDocument, false);
|
|
|
|
|
|
|
|
if (this.bRichTextPossible)
|
|
|
|
this.oFrameDocument.removeEventListener('mousemove', this.aEventWrappers.resizeOverIframe, false);
|
|
|
|
|
|
|
|
document.removeEventListener('mouseup', this.aEventWrappers.endResize, true);
|
|
|
|
|
|
|
|
if (this.bRichTextPossible)
|
|
|
|
this.oFrameDocument.removeEventListener('mouseup', this.aEventWrappers.endResize, true);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// *** smc_SmileyBox class.
|
|
|
|
function smc_SmileyBox(oOptions)
|
|
|
|
{
|
|
|
|
this.opt = oOptions;
|
|
|
|
this.oSmileyRowsContent = {};
|
|
|
|
this.oSmileyPopupWindow = null;
|
|
|
|
this.init();
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_SmileyBox.prototype.init = function ()
|
|
|
|
{
|
|
|
|
// Get the HTML content of the smileys visible on the post screen.
|
|
|
|
this.getSmileyRowsContent('postform');
|
|
|
|
|
|
|
|
// Inject the HTML.
|
|
|
|
setInnerHTML(document.getElementById(this.opt.sContainerDiv), this.opt.sSmileyBoxTemplate.easyReplace({
|
|
|
|
smileyRows: this.oSmileyRowsContent.postform,
|
|
|
|
moreSmileys: this.opt.oSmileyLocations.popup.length == 0 ? '' : this.opt.sMoreSmileysTemplate.easyReplace({
|
|
|
|
moreSmileysId: this.opt.sUniqueId + '_addMoreSmileys'
|
|
|
|
})
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Initialize the smileys.
|
|
|
|
this.initSmileys('postform', document);
|
|
|
|
|
|
|
|
// Initialize the [more] button.
|
|
|
|
if (this.opt.oSmileyLocations.popup.length > 0)
|
|
|
|
{
|
|
|
|
var oMoreLink = document.getElementById(this.opt.sUniqueId + '_addMoreSmileys');
|
|
|
|
oMoreLink.instanceRef = this;
|
|
|
|
oMoreLink.onclick = function () {
|
|
|
|
this.instanceRef.handleShowMoreSmileys();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop through the smileys to setup the HTML.
|
|
|
|
smc_SmileyBox.prototype.getSmileyRowsContent = function (sLocation)
|
|
|
|
{
|
|
|
|
// If it's already defined, don't bother.
|
|
|
|
if (sLocation in this.oSmileyRowsContent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.oSmileyRowsContent[sLocation] = '';
|
|
|
|
|
|
|
|
for (var iSmileyRowIndex = 0, iSmileyRowCount = this.opt.oSmileyLocations[sLocation].length; iSmileyRowIndex < iSmileyRowCount; iSmileyRowIndex++)
|
|
|
|
{
|
|
|
|
var sSmileyRowContent = '';
|
|
|
|
for (var iSmileyIndex = 0, iSmileyCount = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex].length; iSmileyIndex < iSmileyCount; iSmileyIndex++)
|
|
|
|
sSmileyRowContent += this.opt.sSmileyTemplate.easyReplace({
|
|
|
|
smileySource: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sSrc.php_htmlspecialchars(),
|
|
|
|
smileyDescription: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sDescription.php_htmlspecialchars(),
|
|
|
|
smileyCode: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sCode.php_htmlspecialchars(),
|
|
|
|
smileyId: this.opt.sUniqueId + '_' + sLocation + '_' + iSmileyRowIndex.toString() + '_' + iSmileyIndex.toString()
|
|
|
|
});
|
|
|
|
|
|
|
|
this.oSmileyRowsContent[sLocation] += this.opt.sSmileyRowTemplate.easyReplace({
|
|
|
|
smileyRow: sSmileyRowContent
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_SmileyBox.prototype.initSmileys = function (sLocation, oDocument)
|
|
|
|
{
|
|
|
|
for (var iSmileyRowIndex = 0, iSmileyRowCount = this.opt.oSmileyLocations[sLocation].length; iSmileyRowIndex < iSmileyRowCount; iSmileyRowIndex++)
|
|
|
|
{
|
|
|
|
for (var iSmileyIndex = 0, iSmileyCount = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex].length; iSmileyIndex < iSmileyCount; iSmileyIndex++)
|
|
|
|
{
|
|
|
|
var oSmiley = oDocument.getElementById(this.opt.sUniqueId + '_' + sLocation + '_' + iSmileyRowIndex.toString() + '_' + iSmileyIndex.toString());
|
|
|
|
oSmiley.instanceRef = this;
|
|
|
|
oSmiley.style.cursor = 'pointer';
|
|
|
|
oSmiley.onclick = function () {
|
|
|
|
this.instanceRef.clickHandler(this);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_SmileyBox.prototype.clickHandler = function (oSmileyImg)
|
|
|
|
{
|
|
|
|
// Dissect the id...
|
|
|
|
var aMatches = oSmileyImg.id.match(/([^_]+)_(\d+)_(\d+)$/);
|
|
|
|
if (aMatches.length != 4)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// ...to determine its exact smiley properties.
|
|
|
|
var sLocation = aMatches[1];
|
|
|
|
var iSmileyRowIndex = aMatches[2];
|
|
|
|
var iSmileyIndex = aMatches[3];
|
|
|
|
var oProperties = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex];
|
|
|
|
|
|
|
|
if ('sClickHandler' in this.opt)
|
|
|
|
eval(this.opt.sClickHandler + '(oProperties)');
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_SmileyBox.prototype.handleShowMoreSmileys = function ()
|
|
|
|
{
|
|
|
|
// Focus the window if it's already opened.
|
|
|
|
if (this.oSmileyPopupWindow != null && 'closed' in this.oSmileyPopupWindow && !this.oSmileyPopupWindow.closed)
|
|
|
|
{
|
|
|
|
this.oSmileyPopupWindow.focus();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the smiley HTML.
|
|
|
|
this.getSmileyRowsContent('popup');
|
|
|
|
|
|
|
|
// Open the popup.
|
|
|
|
this.oSmileyPopupWindow = window.open('about:blank', this.opt.sUniqueId + '_addMoreSmileysPopup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,width=480,height=220,resizable=yes');
|
|
|
|
|
|
|
|
// Paste the template in the popup.
|
|
|
|
this.oSmileyPopupWindow.document.open('text/html', 'replace');
|
|
|
|
this.oSmileyPopupWindow.document.write(this.opt.sMoreSmileysPopupTemplate.easyReplace({
|
|
|
|
smileyRows: this.oSmileyRowsContent.popup,
|
|
|
|
moreSmileysCloseLinkId: this.opt.sUniqueId + '_closeMoreSmileys'
|
|
|
|
}));
|
|
|
|
this.oSmileyPopupWindow.document.close();
|
|
|
|
|
|
|
|
// Initialize the smileys that are in the popup window.
|
|
|
|
this.initSmileys('popup', this.oSmileyPopupWindow.document);
|
|
|
|
|
|
|
|
// Add a function to the close window button.
|
|
|
|
var aCloseLink = this.oSmileyPopupWindow.document.getElementById(this.opt.sUniqueId + '_closeMoreSmileys');
|
|
|
|
aCloseLink.instanceRef = this;
|
|
|
|
aCloseLink.onclick = function () {
|
|
|
|
this.instanceRef.oSmileyPopupWindow.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// *** smc_BBCButtonBox class.
|
|
|
|
function smc_BBCButtonBox(oOptions)
|
|
|
|
{
|
|
|
|
this.opt = oOptions;
|
|
|
|
this.init();
|
|
|
|
|
|
|
|
var items = ['sActiveButtonBackgroundImageHover', 'sActiveButtonBackgroundImage', 'sButtonBackgroundImageHover', 'sButtonBackgroundImage'];
|
|
|
|
for (var i = 0; i < items.length; i++)
|
|
|
|
{
|
|
|
|
if (items[i] in this.opt)
|
|
|
|
this.opt[items[i]] = this.opt[items[i]].replace(' ', '%20');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.init = function ()
|
|
|
|
{
|
|
|
|
var sBbcContent = '';
|
|
|
|
for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
|
|
|
|
{
|
|
|
|
var sRowContent = '';
|
|
|
|
var bPreviousWasDivider = false;
|
|
|
|
for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
|
|
|
|
{
|
|
|
|
var oCurButton = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
|
|
|
|
switch (oCurButton.sType)
|
|
|
|
{
|
|
|
|
case 'button':
|
|
|
|
if (oCurButton.bEnabled)
|
|
|
|
{
|
|
|
|
sRowContent += this.opt.sButtonTemplate.easyReplace({
|
|
|
|
buttonId: this.opt.sUniqueId.php_htmlspecialchars() + '_button_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString(),
|
|
|
|
buttonSrc: oCurButton.sImage.php_htmlspecialchars(),
|
|
|
|
buttonDescription: oCurButton.sDescription.php_htmlspecialchars()
|
|
|
|
});
|
|
|
|
|
|
|
|
bPreviousWasDivider = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'divider':
|
|
|
|
if (!bPreviousWasDivider)
|
|
|
|
sRowContent += this.opt.sDividerTemplate;
|
|
|
|
|
|
|
|
bPreviousWasDivider = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'select':
|
|
|
|
var sOptions = '';
|
|
|
|
|
|
|
|
// Fighting javascript's idea of order in a for loop... :P
|
|
|
|
if ('' in oCurButton.oOptions)
|
|
|
|
sOptions = '<option value="">' + oCurButton.oOptions[''].php_htmlspecialchars() + '</option>';
|
|
|
|
for (var sSelectValue in oCurButton.oOptions)
|
|
|
|
// we've been through this before
|
|
|
|
if (sSelectValue != '')
|
|
|
|
sOptions += '<option value="' + sSelectValue.php_htmlspecialchars() + '">' + oCurButton.oOptions[sSelectValue].php_htmlspecialchars() + '</option>';
|
|
|
|
|
|
|
|
sRowContent += this.opt.sSelectTemplate.easyReplace({
|
|
|
|
selectName: oCurButton.sName,
|
|
|
|
selectId: this.opt.sUniqueId.php_htmlspecialchars() + '_select_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString(),
|
|
|
|
selectOptions: sOptions
|
|
|
|
});
|
|
|
|
|
|
|
|
bPreviousWasDivider = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sBbcContent += this.opt.sButtonRowTemplate.easyReplace({
|
|
|
|
buttonRow: sRowContent
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
var oBbcContainer = document.getElementById(this.opt.sContainerDiv);
|
|
|
|
setInnerHTML(oBbcContainer, sBbcContent);
|
|
|
|
|
|
|
|
for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
|
|
|
|
{
|
|
|
|
for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
|
|
|
|
{
|
|
|
|
var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
|
|
|
|
switch (oCurControl.sType)
|
|
|
|
{
|
|
|
|
case 'button':
|
|
|
|
if (!oCurControl.bEnabled)
|
|
|
|
break;
|
|
|
|
|
|
|
|
oCurControl.oImg = document.getElementById(this.opt.sUniqueId.php_htmlspecialchars() + '_button_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString());
|
|
|
|
oCurControl.oImg.style.cursor = 'pointer';
|
|
|
|
if ('sButtonBackgroundImage' in this.opt)
|
|
|
|
oCurControl.oImg.style.backgroundImage = 'url(' + this.opt.sButtonBackgroundImage + ')';
|
|
|
|
|
|
|
|
oCurControl.oImg.instanceRef = this;
|
|
|
|
oCurControl.oImg.onmouseover = function () {
|
|
|
|
this.instanceRef.handleButtonMouseOver(this);
|
|
|
|
};
|
|
|
|
oCurControl.oImg.onmouseout = function () {
|
|
|
|
this.instanceRef.handleButtonMouseOut(this);
|
|
|
|
};
|
|
|
|
oCurControl.oImg.onclick = function () {
|
|
|
|
this.instanceRef.handleButtonClick(this);
|
|
|
|
};
|
|
|
|
|
|
|
|
oCurControl.oImg.bIsActive = false;
|
|
|
|
oCurControl.oImg.bHover = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'select':
|
|
|
|
oCurControl.oSelect = document.getElementById(this.opt.sUniqueId.php_htmlspecialchars() + '_select_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString());
|
|
|
|
|
|
|
|
oCurControl.oSelect.instanceRef = this;
|
|
|
|
oCurControl.oSelect.onchange = oCurControl.onchange = function () {
|
|
|
|
this.instanceRef.handleSelectChange(this);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.handleButtonMouseOver = function (oButtonImg)
|
|
|
|
{
|
|
|
|
oButtonImg.bHover = true;
|
|
|
|
this.updateButtonStatus(oButtonImg);
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.handleButtonMouseOut = function (oButtonImg)
|
|
|
|
{
|
|
|
|
oButtonImg.bHover = false;
|
|
|
|
this.updateButtonStatus(oButtonImg);
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.updateButtonStatus = function (oButtonImg)
|
|
|
|
{
|
|
|
|
var sNewURL = '';
|
|
|
|
if (oButtonImg.bHover && oButtonImg.bIsActive && 'sActiveButtonBackgroundImageHover' in this.opt)
|
|
|
|
sNewURL = 'url(' + this.opt.sActiveButtonBackgroundImageHover + ')';
|
|
|
|
else if (!oButtonImg.bHover && oButtonImg.bIsActive && 'sActiveButtonBackgroundImage' in this.opt)
|
|
|
|
sNewURL = 'url(' + this.opt.sActiveButtonBackgroundImage + ')';
|
|
|
|
else if (oButtonImg.bHover && 'sButtonBackgroundImageHover' in this.opt)
|
|
|
|
sNewURL = 'url(' + this.opt.sButtonBackgroundImageHover + ')';
|
|
|
|
else if ('sButtonBackgroundImage' in this.opt)
|
|
|
|
sNewURL = 'url(' + this.opt.sButtonBackgroundImage + ')';
|
|
|
|
|
|
|
|
if (oButtonImg.style.backgroundImage != sNewURL)
|
|
|
|
oButtonImg.style.backgroundImage = sNewURL;
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.handleButtonClick = function (oButtonImg)
|
|
|
|
{
|
|
|
|
// Dissect the id attribute...
|
|
|
|
var aMatches = oButtonImg.id.match(/(\d+)_(\d+)$/);
|
|
|
|
if (aMatches.length != 3)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// ...so that we can point to the exact button.
|
|
|
|
var iButtonRowIndex = aMatches[1];
|
|
|
|
var iButtonIndex = aMatches[2];
|
|
|
|
var oProperties = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
|
|
|
|
oProperties.bIsActive = oButtonImg.bIsActive;
|
|
|
|
|
|
|
|
if ('sButtonClickHandler' in this.opt)
|
|
|
|
eval(this.opt.sButtonClickHandler + '(oProperties)');
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.handleSelectChange = function (oSelectControl)
|
|
|
|
{
|
|
|
|
// Dissect the id attribute...
|
|
|
|
var aMatches = oSelectControl.id.match(/(\d+)_(\d+)$/);
|
|
|
|
if (aMatches.length != 3)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// ...so that we can point to the exact button.
|
|
|
|
var iButtonRowIndex = aMatches[1];
|
|
|
|
var iButtonIndex = aMatches[2];
|
|
|
|
var oProperties = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
|
|
|
|
|
|
|
|
if ('sSelectChangeHandler' in this.opt)
|
|
|
|
eval(this.opt.sSelectChangeHandler + '(oProperties)');
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.setActive = function (aButtons)
|
|
|
|
{
|
|
|
|
for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
|
|
|
|
{
|
|
|
|
for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
|
|
|
|
{
|
|
|
|
var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
|
|
|
|
if (oCurControl.sType == 'button' && oCurControl.bEnabled)
|
|
|
|
{
|
|
|
|
oCurControl.oImg.bIsActive = in_array(oCurControl.sCode, aButtons);
|
|
|
|
this.updateButtonStatus(oCurControl.oImg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.emulateClick = function (sCode)
|
|
|
|
{
|
|
|
|
for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
|
|
|
|
{
|
|
|
|
for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
|
|
|
|
{
|
|
|
|
var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
|
|
|
|
if (oCurControl.sType == 'button' && oCurControl.sCode == sCode)
|
|
|
|
{
|
|
|
|
eval(this.opt.sButtonClickHandler + '(oCurControl)');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
smc_BBCButtonBox.prototype.setSelect = function (sSelectName, sValue)
|
|
|
|
{
|
|
|
|
if (!('sButtonClickHandler' in this.opt))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
|
|
|
|
{
|
|
|
|
for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
|
|
|
|
{
|
|
|
|
var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
|
|
|
|
if (oCurControl.sType == 'select' && oCurControl.sName == sSelectName)
|
|
|
|
oCurControl.oSelect.value = sValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|