annotate forum/Themes/default/scripts/editor.js @ 82:6dd719d7c78b website

Add Sonic Annotator pages (probably best to host it here in future rather than with the omras2 site)
author Chris Cannam
date Mon, 20 Jan 2014 10:48:02 +0000
parents e3e11437ecea
children
rev   line source
Chris@76 1 // *** smc_Editor class.
Chris@76 2 function smc_Editor(oOptions)
Chris@76 3 {
Chris@76 4 this.opt = oOptions;
Chris@76 5
Chris@76 6 // Create some links to the editor object.
Chris@76 7 this.oTextHandle = null;
Chris@76 8 this.sCurrentText = 'sText' in this.opt ? this.opt.sText : '';
Chris@76 9
Chris@76 10 // How big?
Chris@76 11 this.sEditWidth = 'sEditWidth' in this.opt ? this.opt.sEditWidth : '70%';
Chris@76 12 this.sEditHeight = 'sEditHeight' in this.opt ? this.opt.sEditHeight : '150px';
Chris@76 13
Chris@76 14 this.showDebug = false;
Chris@76 15 this.bRichTextEnabled = 'bWysiwyg' in this.opt && this.opt.bWysiwyg;
Chris@76 16 // This doesn't work on Opera as they cannot restore focus after clicking a BBC button.
Chris@76 17 this.bRichTextPossible = !this.opt.bRichEditOff && ((is_ie5up && !is_ie50) || is_ff || is_opera95up || is_safari || is_chrome) && !(is_iphone || is_android);
Chris@76 18
Chris@76 19 this.oFrameHandle = null;
Chris@76 20 this.oFrameDocument = null;
Chris@76 21 this.oFrameWindow = null;
Chris@76 22
Chris@76 23 // These hold the breadcrumb.
Chris@76 24 this.oBreadHandle = null;
Chris@76 25 this.oResizerElement = null;
Chris@76 26
Chris@76 27 // Kinda holds all the useful stuff.
Chris@76 28 this.aKeyboardShortcuts = new Array();
Chris@76 29
Chris@76 30 // This tracks the cursor position on IE to avoid refocus problems.
Chris@76 31 this.cursorX = 0;
Chris@76 32 this.cursorY = 0;
Chris@76 33
Chris@76 34 // This is all the elements that can have a simple execCommand.
Chris@76 35 this.oSimpleExec = {
Chris@76 36 b: 'bold',
Chris@76 37 u: 'underline',
Chris@76 38 i: 'italic',
Chris@76 39 s: 'strikethrough',
Chris@76 40 left: 'justifyleft',
Chris@76 41 center: 'justifycenter',
Chris@76 42 right: 'justifyright',
Chris@76 43 hr: 'inserthorizontalrule',
Chris@76 44 list: 'insertunorderedlist',
Chris@76 45 orderlist: 'insertorderedlist',
Chris@76 46 sub: 'subscript',
Chris@76 47 sup: 'superscript',
Chris@76 48 indent: 'indent',
Chris@76 49 outdent: 'outdent'
Chris@76 50 }
Chris@76 51
Chris@76 52 // Codes to call a private function
Chris@76 53 this.oSmfExec = {
Chris@76 54 unformat: 'removeFormatting',
Chris@76 55 toggle: 'toggleView'
Chris@76 56 }
Chris@76 57
Chris@76 58 // Any special breadcrumb mappings to ensure we show a consistant tag name.
Chris@76 59 this.breadCrumbNameTags = {
Chris@76 60 strike: 's',
Chris@76 61 strong: 'b',
Chris@76 62 em: 'i'
Chris@76 63 }
Chris@76 64
Chris@76 65 this.aBreadCrumbNameStyles = [
Chris@76 66 {
Chris@76 67 sStyleType: 'text-decoration',
Chris@76 68 sStyleValue: 'underline',
Chris@76 69 sBbcTag: 'u'
Chris@76 70 },
Chris@76 71 {
Chris@76 72 sStyleType: 'text-decoration',
Chris@76 73 sStyleValue: 'line-through',
Chris@76 74 sBbcTag: 's'
Chris@76 75 },
Chris@76 76 {
Chris@76 77 sStyleType: 'text-align',
Chris@76 78 sStyleValue: 'left',
Chris@76 79 sBbcTag: 'left'
Chris@76 80 },
Chris@76 81 {
Chris@76 82 sStyleType: 'text-align',
Chris@76 83 sStyleValue: 'center',
Chris@76 84 sBbcTag: 'center'
Chris@76 85 },
Chris@76 86 {
Chris@76 87 sStyleType: 'text-align',
Chris@76 88 sStyleValue: 'right',
Chris@76 89 sBbcTag: 'right'
Chris@76 90 },
Chris@76 91 {
Chris@76 92 sStyleType: 'font-weight',
Chris@76 93 sStyleValue: 'bold',
Chris@76 94 sBbcTag: 'b'
Chris@76 95 },
Chris@76 96 {
Chris@76 97 sStyleType: 'font-style',
Chris@76 98 sStyleValue: 'italic',
Chris@76 99 sBbcTag: 'i'
Chris@76 100 }
Chris@76 101 ];
Chris@76 102
Chris@76 103 // All the fonts in the world.
Chris@76 104 this.aFontFaces = [
Chris@76 105 'Arial',
Chris@76 106 'Arial Black',
Chris@76 107 'Impact',
Chris@76 108 'Verdana',
Chris@76 109 'Times New Roman',
Chris@76 110 'Georgia',
Chris@76 111 'Andale Mono',
Chris@76 112 'Trebuchet MS',
Chris@76 113 'Comic Sans MS'
Chris@76 114 ];
Chris@76 115 // Font maps (HTML => CSS size)
Chris@76 116 this.aFontSizes = [
Chris@76 117 0,
Chris@76 118 8,
Chris@76 119 10,
Chris@76 120 12,
Chris@76 121 14,
Chris@76 122 18,
Chris@76 123 24,
Chris@76 124 36
Chris@76 125 ];
Chris@76 126 // Color maps! (hex => name)
Chris@76 127 this.oFontColors = {
Chris@76 128 black: '#000000',
Chris@76 129 red: '#ff0000',
Chris@76 130 yellow: '#ffff00',
Chris@76 131 pink: '#ffc0cb',
Chris@76 132 green: '#008000',
Chris@76 133 orange: '#ffa500',
Chris@76 134 purple: '#800080',
Chris@76 135 blue: '#0000ff',
Chris@76 136 beige: '#f5f5dc',
Chris@76 137 brown: '#a52a2a',
Chris@76 138 teal: '#008080',
Chris@76 139 navy: '#000080',
Chris@76 140 maroon: '#800000',
Chris@76 141 limegreen: '#32cd32'
Chris@76 142 }
Chris@76 143
Chris@76 144 this.sFormId = 'sFormId' in this.opt ? this.opt.sFormId : 'postmodify';
Chris@76 145 this.iArrayPosition = smf_editorArray.length;
Chris@76 146
Chris@76 147 // Current resize state.
Chris@76 148 this.osmc_EditorCurrentResize = {};
Chris@76 149
Chris@76 150 this.init();
Chris@76 151 }
Chris@76 152
Chris@76 153 smc_Editor.prototype.init = function()
Chris@76 154 {
Chris@76 155 // Define the event wrapper functions.
Chris@76 156 var oCaller = this;
Chris@76 157 this.aEventWrappers = {
Chris@76 158 editorKeyUp: function(oEvent) {return oCaller.editorKeyUp(oEvent);},
Chris@76 159 shortcutCheck: function(oEvent) {return oCaller.shortcutCheck(oEvent);},
Chris@76 160 editorBlur: function(oEvent) {return oCaller.editorBlur(oEvent);},
Chris@76 161 editorFocus: function(oEvent) {return oCaller.editorFocus(oEvent);},
Chris@76 162 startResize: function(oEvent) {return oCaller.startResize(oEvent);},
Chris@76 163 resizeOverDocument: function(oEvent) {return oCaller.resizeOverDocument(oEvent);},
Chris@76 164 endResize: function(oEvent) {return oCaller.endResize(oEvent);},
Chris@76 165 resizeOverIframe: function(oEvent) {return oCaller.resizeOverIframe(oEvent);}
Chris@76 166 };
Chris@76 167
Chris@76 168 // Set the textHandle.
Chris@76 169 this.oTextHandle = document.getElementById(this.opt.sUniqueId);
Chris@76 170
Chris@76 171 // Ensure the currentText is set correctly depending on the mode.
Chris@76 172 if (this.sCurrentText == '' && !this.bRichTextEnabled)
Chris@76 173 this.sCurrentText = getInnerHTML(this.oTextHandle).php_unhtmlspecialchars();
Chris@76 174
Chris@76 175 // Only try to do this if rich text is supported.
Chris@76 176 if (this.bRichTextPossible)
Chris@76 177 {
Chris@76 178 // Make the iframe itself, stick it next to the current text area, and give it an ID.
Chris@76 179 this.oFrameHandle = document.createElement('iframe');
Chris@76 180 this.oFrameHandle.src = 'about:blank';
Chris@76 181 this.oFrameHandle.id = 'html_' + this.opt.sUniqueId;
Chris@76 182 this.oFrameHandle.className = 'rich_editor_frame';
Chris@76 183 this.oFrameHandle.style.display = 'none';
Chris@76 184 this.oFrameHandle.style.margin = '0px';
Chris@76 185 this.oFrameHandle.tabIndex = this.oTextHandle.tabIndex;
Chris@76 186 this.oTextHandle.parentNode.appendChild(this.oFrameHandle);
Chris@76 187
Chris@76 188 // Create some handy shortcuts.
Chris@76 189 this.oFrameDocument = this.oFrameHandle.contentDocument ? this.oFrameHandle.contentDocument : ('contentWindow' in this.oFrameHandle ? this.oFrameHandle.contentWindow.document : this.oFrameHandle.document);
Chris@76 190 this.oFrameWindow = 'contentWindow' in this.oFrameHandle ? this.oFrameHandle.contentWindow : this.oFrameHandle.document.parentWindow;
Chris@76 191
Chris@76 192 // Create the debug window... and stick this under the main frame - make it invisible by default.
Chris@76 193 this.oBreadHandle = document.createElement('div');
Chris@76 194 this.oBreadHandle.id = 'bread_' . uid;
Chris@76 195 this.oBreadHandle.style.visibility = 'visible';
Chris@76 196 this.oBreadHandle.style.display = 'none';
Chris@76 197 this.oFrameHandle.parentNode.appendChild(this.oBreadHandle);
Chris@76 198
Chris@76 199 // Size the iframe dimensions to something sensible.
Chris@76 200 this.oFrameHandle.style.width = this.sEditWidth;
Chris@76 201 this.oFrameHandle.style.height = this.sEditHeight;
Chris@76 202 this.oFrameHandle.style.visibility = 'visible';
Chris@76 203
Chris@76 204 // Only bother formatting the debug window if debug is enabled.
Chris@76 205 if (this.showDebug)
Chris@76 206 {
Chris@76 207 this.oBreadHandle.style.width = this.sEditWidth;
Chris@76 208 this.oBreadHandle.style.height = '20px';
Chris@76 209 this.oBreadHandle.className = 'windowbg2';
Chris@76 210 this.oBreadHandle.style.border = '1px black solid';
Chris@76 211 this.oBreadHandle.style.display = '';
Chris@76 212 }
Chris@76 213
Chris@76 214 // Populate the editor with nothing by default.
Chris@76 215 if (!is_opera95up)
Chris@76 216 {
Chris@76 217 this.oFrameDocument.open();
Chris@76 218 this.oFrameDocument.write('');
Chris@76 219 this.oFrameDocument.close();
Chris@76 220 }
Chris@76 221
Chris@76 222 // Right to left mode?
Chris@76 223 if (this.opt.bRTL)
Chris@76 224 {
Chris@76 225 this.oFrameDocument.dir = "rtl";
Chris@76 226 this.oFrameDocument.body.dir = "rtl";
Chris@76 227 }
Chris@76 228
Chris@76 229 // Mark it as editable...
Chris@76 230 if (this.oFrameDocument.body.contentEditable)
Chris@76 231 this.oFrameDocument.body.contentEditable = true;
Chris@76 232 else
Chris@76 233 {
Chris@76 234 this.oFrameHandle.style.display = '';
Chris@76 235 this.oFrameDocument.designMode = 'on';
Chris@76 236 this.oFrameHandle.style.display = 'none';
Chris@76 237 }
Chris@76 238
Chris@76 239 // Now we need to try and style the editor - internet explorer allows us to do the whole lot.
Chris@76 240 if (document.styleSheets['editor_css'] || document.styleSheets['editor_ie_css'])
Chris@76 241 {
Chris@76 242 var oMyStyle = this.oFrameDocument.createElement('style');
Chris@76 243 this.oFrameDocument.documentElement.firstChild.appendChild(oMyStyle);
Chris@76 244 oMyStyle.styleSheet.cssText = document.styleSheets['editor_ie_css'] ? document.styleSheets['editor_ie_css'].cssText : document.styleSheets['editor_css'].cssText;
Chris@76 245 }
Chris@76 246 // Otherwise we seem to have to try to rip out each of the styles one by one!
Chris@76 247 else if (document.styleSheets.length)
Chris@76 248 {
Chris@76 249 var bFoundSomething = false;
Chris@76 250 // First we need to find the right style sheet.
Chris@76 251 for (var i = 0, iNumStyleSheets = document.styleSheets.length; i < iNumStyleSheets; i++)
Chris@76 252 {
Chris@76 253 // Start off looking for the right style sheet.
Chris@76 254 if (!document.styleSheets[i].href || document.styleSheets[i].href.indexOf('editor') < 1)
Chris@76 255 continue;
Chris@76 256
Chris@76 257 // Firefox won't allow us to get a CSS file which ain't in the right URL.
Chris@76 258 try
Chris@76 259 {
Chris@76 260 if (document.styleSheets[i].cssRules.length < 1)
Chris@76 261 continue;
Chris@76 262 }
Chris@76 263 catch (e)
Chris@76 264 {
Chris@76 265 continue;
Chris@76 266 }
Chris@76 267
Chris@76 268 // Manually try to find the rich_editor class.
Chris@76 269 for (var r = 0, iNumRules = document.styleSheets[i].cssRules.length; r < iNumRules; r++)
Chris@76 270 {
Chris@76 271 // Got the main editor?
Chris@76 272 if (document.styleSheets[i].cssRules[r].selectorText == '.rich_editor')
Chris@76 273 {
Chris@76 274 // Set some possible styles.
Chris@76 275 if (document.styleSheets[i].cssRules[r].style.color)
Chris@76 276 this.oFrameDocument.body.style.color = document.styleSheets[i].cssRules[r].style.color;
Chris@76 277 if (document.styleSheets[i].cssRules[r].style.backgroundColor)
Chris@76 278 this.oFrameDocument.body.style.backgroundColor = document.styleSheets[i].cssRules[r].style.backgroundColor;
Chris@76 279 if (document.styleSheets[i].cssRules[r].style.fontSize)
Chris@76 280 this.oFrameDocument.body.style.fontSize = document.styleSheets[i].cssRules[r].style.fontSize;
Chris@76 281 if (document.styleSheets[i].cssRules[r].style.fontFamily)
Chris@76 282 this.oFrameDocument.body.style.fontFamily = document.styleSheets[i].cssRules[r].style.fontFamily;
Chris@76 283 if (document.styleSheets[i].cssRules[r].style.border)
Chris@76 284 this.oFrameDocument.body.style.border = document.styleSheets[i].cssRules[r].style.border;
Chris@76 285 bFoundSomething = true;
Chris@76 286 }
Chris@76 287 // The frame?
Chris@76 288 else if (document.styleSheets[i].cssRules[r].selectorText == '.rich_editor_frame')
Chris@76 289 {
Chris@76 290 if (document.styleSheets[i].cssRules[r].style.border)
Chris@76 291 this.oFrameHandle.style.border = document.styleSheets[i].cssRules[r].style.border;
Chris@76 292 }
Chris@76 293 }
Chris@76 294 }
Chris@76 295
Chris@76 296 // Didn't find it?
Chris@76 297 if (!bFoundSomething)
Chris@76 298 {
Chris@76 299 // Do something that is better than nothing.
Chris@76 300 this.oFrameDocument.body.style.color = 'black';
Chris@76 301 this.oFrameDocument.body.style.backgroundColor = 'white';
Chris@76 302 this.oFrameDocument.body.style.fontSize = '78%';
Chris@76 303 this.oFrameDocument.body.style.fontFamily = '"Verdana", "Arial", "Helvetica", "sans-serif"';
Chris@76 304 this.oFrameDocument.body.style.border = 'none';
Chris@76 305 this.oFrameHandle.style.border = '1px solid #808080';
Chris@76 306 if (is_opera)
Chris@76 307 this.oFrameDocument.body.style.height = '99%';
Chris@76 308 }
Chris@76 309 }
Chris@76 310
Chris@76 311 // Apply the class...
Chris@76 312 this.oFrameDocument.body.className = 'rich_editor';
Chris@76 313
Chris@76 314 // Set the frame padding/margin inside the editor.
Chris@76 315 this.oFrameDocument.body.style.padding = '1px';
Chris@76 316 this.oFrameDocument.body.style.margin = '0';
Chris@76 317
Chris@76 318 // Listen for input.
Chris@76 319 this.oFrameDocument.instanceRef = this;
Chris@76 320 this.oFrameHandle.instanceRef = this;
Chris@76 321 this.oTextHandle.instanceRef = this;
Chris@76 322
Chris@76 323 // Attach addEventListener for those browsers that don't support it.
Chris@76 324 createEventListener(this.oFrameHandle);
Chris@76 325 createEventListener(this.oFrameDocument);
Chris@76 326 createEventListener(this.oTextHandle);
Chris@76 327 createEventListener(window);
Chris@76 328 createEventListener(document);
Chris@76 329
Chris@76 330 // Attach functions to the key and mouse events.
Chris@76 331 this.oFrameDocument.addEventListener('keyup', this.aEventWrappers.editorKeyUp, true);
Chris@76 332 this.oFrameDocument.addEventListener('mouseup', this.aEventWrappers.editorKeyUp, true);
Chris@76 333 this.oFrameDocument.addEventListener('keydown', this.aEventWrappers.shortcutCheck, true);
Chris@76 334 this.oTextHandle.addEventListener('keydown', this.aEventWrappers.shortcutCheck, true);
Chris@76 335
Chris@76 336 if (is_ie)
Chris@76 337 {
Chris@76 338 this.oFrameDocument.addEventListener('blur', this.aEventWrappers.editorBlur, true);
Chris@76 339 this.oFrameDocument.addEventListener('focus', this.aEventWrappers.editorFocus, true);
Chris@76 340 }
Chris@76 341
Chris@76 342 // Show the iframe only if wysiwyrg is on - and hide the text area.
Chris@76 343 this.oTextHandle.style.display = this.bRichTextEnabled ? 'none' : '';
Chris@76 344 this.oFrameHandle.style.display = this.bRichTextEnabled ? '' : 'none';
Chris@76 345 this.oBreadHandle.style.display = this.bRichTextEnabled ? '' : 'none';
Chris@76 346 }
Chris@76 347 // If we can't do advanced stuff then just do the basics.
Chris@76 348 else
Chris@76 349 {
Chris@76 350 // Cannot have WYSIWYG anyway!
Chris@76 351 this.bRichTextEnabled = false;
Chris@76 352
Chris@76 353 // We need some of the event handlers.
Chris@76 354 createEventListener(this.oTextHandle);
Chris@76 355 createEventListener(window);
Chris@76 356 createEventListener(document);
Chris@76 357 }
Chris@76 358
Chris@76 359 // Make sure we set the message mode correctly.
Chris@76 360 document.getElementById(this.opt.sUniqueId + '_mode').value = this.bRichTextEnabled ? 1 : 0;
Chris@76 361
Chris@76 362 // Show the resizer.
Chris@76 363 if (document.getElementById(this.opt.sUniqueId + '_resizer') && (!is_opera || is_opera95up) && !(is_chrome && !this.bRichTextEnabled))
Chris@76 364 {
Chris@76 365 // Currently nothing is being resized...I assume!
Chris@76 366 window.smf_oCurrentResizeEditor = null;
Chris@76 367
Chris@76 368 this.oResizerElement = document.getElementById(this.opt.sUniqueId + '_resizer');
Chris@76 369 this.oResizerElement.style.display = '';
Chris@76 370
Chris@76 371 createEventListener(this.oResizerElement);
Chris@76 372 this.oResizerElement.addEventListener('mousedown', this.aEventWrappers.startResize, false);
Chris@76 373 }
Chris@76 374
Chris@76 375 // Set the text - if WYSIWYG is enabled that is.
Chris@76 376 if (this.bRichTextEnabled)
Chris@76 377 {
Chris@76 378 this.insertText(this.sCurrentText, true);
Chris@76 379
Chris@76 380 // Better make us the focus!
Chris@76 381 this.setFocus();
Chris@76 382 }
Chris@76 383
Chris@76 384 // Finally, register shortcuts.
Chris@76 385 this.registerDefaultShortcuts();
Chris@76 386 this.updateEditorControls();
Chris@76 387 }
Chris@76 388
Chris@76 389 // Return the current text.
Chris@76 390 smc_Editor.prototype.getText = function(bPrepareEntities, bModeOverride)
Chris@76 391 {
Chris@76 392 var bCurMode = typeof(bModeOverride) != 'undefined' ? bModeOverride : this.bRichTextEnabled;
Chris@76 393
Chris@76 394 if (!bCurMode || this.oFrameDocument == null)
Chris@76 395 {
Chris@76 396 var sText = this.oTextHandle.value;
Chris@76 397 if (bPrepareEntities)
Chris@76 398 sText = sText.replace(/</g, '#smlt#').replace(/>/g, '#smgt#').replace(/&/g, '#smamp#');
Chris@76 399 }
Chris@76 400 else
Chris@76 401 {
Chris@76 402 var sText = this.oFrameDocument.body.innerHTML;
Chris@76 403 if (bPrepareEntities)
Chris@76 404 sText = sText.replace(/&lt;/g, '#smlt#').replace(/&gt;/g, '#smgt#').replace(/&amp;/g, '#smamp#');
Chris@76 405 }
Chris@76 406
Chris@76 407 // Clean it up - including removing semi-colons.
Chris@76 408 if (bPrepareEntities)
Chris@76 409 sText = sText.replace(/&nbsp;/g, ' ').replace(/;/g, '#smcol#');
Chris@76 410
Chris@76 411 // Return it.
Chris@76 412 return sText;
Chris@76 413 }
Chris@76 414
Chris@76 415 // Return the current text.
Chris@76 416 smc_Editor.prototype.unprotectText = function(sText)
Chris@76 417 {
Chris@76 418 var bCurMode = typeof(bModeOverride) != 'undefined' ? bModeOverride : this.bRichTextEnabled;
Chris@76 419
Chris@76 420 // This restores smlt, smgt and smamp into boring entities, to unprotect against XML'd information like quotes.
Chris@76 421 sText = sText.replace(/#smlt#/g, '&lt;').replace(/#smgt#/g, '&gt;').replace(/#smamp#/g, '&amp;');
Chris@76 422
Chris@76 423 // Return it.
Chris@76 424 return sText;
Chris@76 425 }
Chris@76 426
Chris@76 427 smc_Editor.prototype.editorKeyUp = function()
Chris@76 428 {
Chris@76 429 // Rebuild the breadcrumb.
Chris@76 430 this.updateEditorControls();
Chris@76 431 }
Chris@76 432
Chris@76 433 smc_Editor.prototype.editorBlur = function()
Chris@76 434 {
Chris@76 435 if (!is_ie)
Chris@76 436 return;
Chris@76 437
Chris@76 438 // Need to do something here.
Chris@76 439 }
Chris@76 440
Chris@76 441 smc_Editor.prototype.editorFocus = function()
Chris@76 442 {
Chris@76 443 if (!is_ie)
Chris@76 444 return;
Chris@76 445
Chris@76 446 // Need to do something here.
Chris@76 447 }
Chris@76 448
Chris@76 449 // Rebuild the breadcrumb etc - and set things to the correct context.
Chris@76 450 smc_Editor.prototype.updateEditorControls = function()
Chris@76 451 {
Chris@76 452 // Everything else is specific to HTML mode.
Chris@76 453 if (!this.bRichTextEnabled)
Chris@76 454 {
Chris@76 455 // Set none of the buttons active.
Chris@76 456 if (this.opt.oBBCBox)
Chris@76 457 this.opt.oBBCBox.setActive([]);
Chris@76 458 return;
Chris@76 459 }
Chris@76 460
Chris@76 461 var aCrumb = new Array();
Chris@76 462 var aAllCrumbs = new Array();
Chris@76 463 var iMaxLength = 6;
Chris@76 464
Chris@76 465 // What is the current element?
Chris@76 466 var oCurTag = this.getCurElement();
Chris@76 467
Chris@76 468 var i = 0;
Chris@76 469 while (typeof(oCurTag) == 'object' && oCurTag != null && oCurTag.nodeName.toLowerCase() != 'body' && i < iMaxLength)
Chris@76 470 {
Chris@76 471 aCrumb[i++] = oCurTag;
Chris@76 472 oCurTag = oCurTag.parentNode;
Chris@76 473 }
Chris@76 474
Chris@76 475 // Now print out the tree.
Chris@76 476 var sTree = '';
Chris@76 477 var sCurFontName = '';
Chris@76 478 var sCurFontSize = '';
Chris@76 479 var sCurFontColor = '';
Chris@76 480 for (var i = 0, iNumCrumbs = aCrumb.length; i < iNumCrumbs; i++)
Chris@76 481 {
Chris@76 482 var sCrumbName = aCrumb[i].nodeName.toLowerCase();
Chris@76 483
Chris@76 484 // Does it have an alternative name?
Chris@76 485 if (sCrumbName in this.breadCrumbNameTags)
Chris@76 486 sCrumbName = this.breadCrumbNameTags[sCrumbName];
Chris@76 487 // Don't bother with this...
Chris@76 488 else if (sCrumbName == 'p')
Chris@76 489 continue;
Chris@76 490 // A link?
Chris@76 491 else if (sCrumbName == 'a')
Chris@76 492 {
Chris@76 493 var sUrlInfo = aCrumb[i].getAttribute('href');
Chris@76 494 sCrumbName = 'url';
Chris@76 495 if (typeof(sUrlInfo) == 'string')
Chris@76 496 {
Chris@76 497 if (sUrlInfo.substr(0, 3) == 'ftp')
Chris@76 498 sCrumbName = 'ftp';
Chris@76 499 else if (sUrlInfo.substr(0, 6) == 'mailto')
Chris@76 500 sCrumbName = 'email';
Chris@76 501 }
Chris@76 502 }
Chris@76 503 else if (sCrumbName == 'span' || sCrumbName == 'div')
Chris@76 504 {
Chris@76 505 if (aCrumb[i].style)
Chris@76 506 {
Chris@76 507 for (var j = 0, iNumStyles = this.aBreadCrumbNameStyles.length; j < iNumStyles; j++)
Chris@76 508 {
Chris@76 509 // Do we have a font?
Chris@76 510 if (aCrumb[i].style.fontFamily && aCrumb[i].style.fontFamily != '' && sCurFontName == '')
Chris@76 511 {
Chris@76 512 sCurFontName = aCrumb[i].style.fontFamily;
Chris@76 513 sCrumbName = 'face';
Chris@76 514 }
Chris@76 515 // ... or a font size?
Chris@76 516 if (aCrumb[i].style.fontSize && aCrumb[i].style.fontSize != '' && sCurFontSize == '')
Chris@76 517 {
Chris@76 518 sCurFontSize = aCrumb[i].style.fontSize;
Chris@76 519 sCrumbName = 'size';
Chris@76 520 }
Chris@76 521 // ... even color?
Chris@76 522 if (aCrumb[i].style.color && aCrumb[i].style.color != '' && sCurFontColor == '')
Chris@76 523 {
Chris@76 524 sCurFontColor = aCrumb[i].style.color;
Chris@76 525 if (in_array(sCurFontColor, this.oFontColors))
Chris@76 526 sCurFontColor = array_search(sCurFontColor, this.oFontColors);
Chris@76 527 sCrumbName = 'color';
Chris@76 528 }
Chris@76 529
Chris@76 530 if (this.aBreadCrumbNameStyles[j].sStyleType == 'text-align' && aCrumb[i].style.textAlign && aCrumb[i].style.textAlign == this.aBreadCrumbNameStyles[j].sStyleValue)
Chris@76 531 sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag;
Chris@76 532 else if (this.aBreadCrumbNameStyles[j].sStyleType == 'text-decoration' && aCrumb[i].style.textDecoration && aCrumb[i].style.textDecoration == this.aBreadCrumbNameStyles[j].sStyleValue)
Chris@76 533 sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag;
Chris@76 534 else if (this.aBreadCrumbNameStyles[j].sStyleType == 'font-weight' && aCrumb[i].style.fontWeight && aCrumb[i].style.fontWeight == this.aBreadCrumbNameStyles[j].sStyleValue)
Chris@76 535 sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag;
Chris@76 536 else if (this.aBreadCrumbNameStyles[j].sStyleType == 'font-style' && aCrumb[i].style.fontStyle && aCrumb[i].style.fontStyle == this.aBreadCrumbNameStyles[j].sStyleValue)
Chris@76 537 sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag;
Chris@76 538 }
Chris@76 539 }
Chris@76 540 }
Chris@76 541 // Do we have a font?
Chris@76 542 else if (sCrumbName == 'font')
Chris@76 543 {
Chris@76 544 if (aCrumb[i].getAttribute('face') && sCurFontName == '')
Chris@76 545 {
Chris@76 546 sCurFontName = aCrumb[i].getAttribute('face').toLowerCase();
Chris@76 547 sCrumbName = 'face';
Chris@76 548 }
Chris@76 549 if (aCrumb[i].getAttribute('size') && sCurFontSize == '')
Chris@76 550 {
Chris@76 551 sCurFontSize = aCrumb[i].getAttribute('size');
Chris@76 552 sCrumbName = 'size';
Chris@76 553 }
Chris@76 554 if (aCrumb[i].getAttribute('color') && sCurFontColor == '')
Chris@76 555 {
Chris@76 556 sCurFontColor = aCrumb[i].getAttribute('color');
Chris@76 557 if (in_array(sCurFontColor, this.oFontColors))
Chris@76 558 sCurFontColor = array_search(sCurFontColor, this.oFontColors);
Chris@76 559 sCrumbName = 'color';
Chris@76 560 }
Chris@76 561 // Something else - ignore.
Chris@76 562 if (sCrumbName == 'font')
Chris@76 563 continue;
Chris@76 564 }
Chris@76 565
Chris@76 566 sTree += (i != 0 ? '&nbsp;<strong>&gt;</strong>' : '') + '&nbsp;' + sCrumbName;
Chris@76 567 aAllCrumbs[aAllCrumbs.length] = sCrumbName;
Chris@76 568 }
Chris@76 569
Chris@76 570 // Since we're in WYSIWYG state, show the toggle button as active.
Chris@76 571 aAllCrumbs[aAllCrumbs.length] = 'toggle';
Chris@76 572
Chris@76 573 this.opt.oBBCBox.setActive(aAllCrumbs);
Chris@76 574
Chris@76 575 // Try set the font boxes correct.
Chris@76 576 this.opt.oBBCBox.setSelect('sel_face', sCurFontName);
Chris@76 577 this.opt.oBBCBox.setSelect('sel_size', sCurFontSize);
Chris@76 578 this.opt.oBBCBox.setSelect('sel_color', sCurFontColor);
Chris@76 579
Chris@76 580 if (this.showDebug)
Chris@76 581 setInnerHTML(this.oBreadHandle, sTree);
Chris@76 582 }
Chris@76 583
Chris@76 584 // Set the HTML content to be that of the text box - if we are in wysiwyg mode.
Chris@76 585 smc_Editor.prototype.doSubmit = function()
Chris@76 586 {
Chris@76 587 if (this.bRichTextEnabled)
Chris@76 588 this.oTextHandle.value = this.oFrameDocument.body.innerHTML;
Chris@76 589 }
Chris@76 590
Chris@76 591 // Populate the box with text.
Chris@76 592 smc_Editor.prototype.insertText = function(sText, bClear, bForceEntityReverse, iMoveCursorBack)
Chris@76 593 {
Chris@76 594 if (bForceEntityReverse)
Chris@76 595 sText = this.unprotectText(sText);
Chris@76 596
Chris@76 597 // Erase it all?
Chris@76 598 if (bClear)
Chris@76 599 {
Chris@76 600 if (this.bRichTextEnabled)
Chris@76 601 {
Chris@76 602 // This includes a work around for FF to get the cursor to show!
Chris@76 603 this.oFrameDocument.body.innerHTML = sText;
Chris@76 604
Chris@76 605 // If FF trick the cursor into coming back!
Chris@76 606 if (is_ff || is_opera)
Chris@76 607 {
Chris@76 608 // For some entirely unknown reason FF3 Beta 2 and some Opera versions
Chris@76 609 // require this.
Chris@76 610 this.oFrameDocument.body.contentEditable = false;
Chris@76 611
Chris@76 612 this.oFrameDocument.designMode = 'off';
Chris@76 613 this.oFrameDocument.designMode = 'on';
Chris@76 614 }
Chris@76 615 }
Chris@76 616 else
Chris@76 617 this.oTextHandle.value = sText;
Chris@76 618 }
Chris@76 619 else
Chris@76 620 {
Chris@76 621 this.setFocus();
Chris@76 622 if (this.bRichTextEnabled)
Chris@76 623 {
Chris@76 624 // IE croaks if you have an image selected and try to insert!
Chris@76 625 if ('selection' in this.oFrameDocument && this.oFrameDocument.selection.type != 'Text' && this.oFrameDocument.selection.type != 'None' && this.oFrameDocument.selection.clear)
Chris@76 626 this.oFrameDocument.selection.clear();
Chris@76 627
Chris@76 628 var oRange = this.getRange();
Chris@76 629
Chris@76 630 if (oRange.pasteHTML)
Chris@76 631 {
Chris@76 632 oRange.pasteHTML(sText);
Chris@76 633
Chris@76 634 // Do we want to move the cursor back at all?
Chris@76 635 if (iMoveCursorBack)
Chris@76 636 oRange.moveEnd('character', -iMoveCursorBack);
Chris@76 637
Chris@76 638 oRange.select();
Chris@76 639 }
Chris@76 640 else
Chris@76 641 {
Chris@76 642 // If the cursor needs to be positioned, insert the last fragment first.
Chris@76 643 if (typeof(iMoveCursorBack) != 'undefined' && iMoveCursorBack > 0 && sText.length > iMoveCursorBack)
Chris@76 644 {
Chris@76 645 var oSelection = this.getSelect(false, false);
Chris@76 646 var oRange = oSelection.getRangeAt(0);
Chris@76 647 oRange.insertNode(this.oFrameDocument.createTextNode(sText.substr(sText.length - iMoveCursorBack)));
Chris@76 648 }
Chris@76 649
Chris@76 650 this.smf_execCommand('inserthtml', false, typeof(iMoveCursorBack) == 'undefined' ? sText : sText.substr(0, sText.length - iMoveCursorBack));
Chris@76 651 }
Chris@76 652 }
Chris@76 653 else
Chris@76 654 {
Chris@76 655 replaceText(sText, this.oTextHandle);
Chris@76 656 }
Chris@76 657 }
Chris@76 658 }
Chris@76 659
Chris@76 660
Chris@76 661 // Special handler for WYSIWYG.
Chris@76 662 smc_Editor.prototype.smf_execCommand = function(sCommand, bUi, sValue)
Chris@76 663 {
Chris@76 664 return this.oFrameDocument.execCommand(sCommand, bUi, sValue);
Chris@76 665 }
Chris@76 666
Chris@76 667 smc_Editor.prototype.insertSmiley = function(oSmileyProperties)
Chris@76 668 {
Chris@76 669 // In text mode we just add it in as we always did.
Chris@76 670 if (!this.bRichTextEnabled)
Chris@76 671 this.insertText(' ' + oSmileyProperties.sCode);
Chris@76 672
Chris@76 673 // Otherwise we need to do a whole image...
Chris@76 674 else
Chris@76 675 {
Chris@76 676 var iUniqueSmileyId = 1000 + Math.floor(Math.random() * 100000);
Chris@76 677 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;" />');
Chris@76 678 }
Chris@76 679 }
Chris@76 680
Chris@76 681 smc_Editor.prototype.handleButtonClick = function (oButtonProperties)
Chris@76 682 {
Chris@76 683 this.setFocus();
Chris@76 684
Chris@76 685 // A special SMF function?
Chris@76 686 if (oButtonProperties.sCode in this.oSmfExec)
Chris@76 687 this[this.oSmfExec[oButtonProperties.sCode]]();
Chris@76 688
Chris@76 689 else
Chris@76 690 {
Chris@76 691 // In text this is easy...
Chris@76 692 if (!this.bRichTextEnabled)
Chris@76 693 {
Chris@76 694 // Replace?
Chris@76 695 if (!('sAfter' in oButtonProperties) || oButtonProperties.sAfter == null)
Chris@76 696 replaceText(oButtonProperties.sBefore.replace(/\\n/g, '\n'), this.oTextHandle)
Chris@76 697
Chris@76 698 // Surround!
Chris@76 699 else
Chris@76 700 surroundText(oButtonProperties.sBefore.replace(/\\n/g, '\n'), oButtonProperties.sAfter.replace(/\\n/g, '\n'), this.oTextHandle)
Chris@76 701 }
Chris@76 702 else
Chris@76 703 {
Chris@76 704 // Is it easy?
Chris@76 705 if (oButtonProperties.sCode in this.oSimpleExec)
Chris@76 706 this.smf_execCommand(this.oSimpleExec[oButtonProperties.sCode], false, null);
Chris@76 707
Chris@76 708 // A link?
Chris@76 709 else if (oButtonProperties.sCode == 'url' || oButtonProperties.sCode == 'email' || oButtonProperties.sCode == 'ftp')
Chris@76 710 this.insertLink(oButtonProperties.sCode);
Chris@76 711
Chris@76 712 // Maybe an image?
Chris@76 713 else if (oButtonProperties.sCode == 'img')
Chris@76 714 this.insertImage();
Chris@76 715
Chris@76 716 // Everything else means doing something ourselves.
Chris@76 717 else if ('sBefore' in oButtonProperties)
Chris@76 718 this.insertCustomHTML(oButtonProperties.sBefore.replace(/\\n/g, '\n'), oButtonProperties.sAfter.replace(/\\n/g, '\n'));
Chris@76 719
Chris@76 720 }
Chris@76 721 }
Chris@76 722
Chris@76 723 this.updateEditorControls();
Chris@76 724
Chris@76 725 // Finally set the focus.
Chris@76 726 this.setFocus();
Chris@76 727 }
Chris@76 728
Chris@76 729 // Changing a select box?
Chris@76 730 smc_Editor.prototype.handleSelectChange = function (oSelectProperties)
Chris@76 731 {
Chris@76 732 this.setFocus();
Chris@76 733
Chris@76 734 var sValue = oSelectProperties.oSelect.value;
Chris@76 735 if (sValue == '')
Chris@76 736 return true;
Chris@76 737
Chris@76 738 // Changing font face?
Chris@76 739 if (oSelectProperties.sName == 'sel_face')
Chris@76 740 {
Chris@76 741 // Not in HTML mode?
Chris@76 742 if (!this.bRichTextEnabled)
Chris@76 743 {
Chris@76 744 sValue = sValue.replace(/"/, '');
Chris@76 745 surroundText('[font=' + sValue + ']', '[/font]', this.oTextHandle);
Chris@76 746 oSelectProperties.oSelect.selectedIndex = 0;
Chris@76 747 }
Chris@76 748 else
Chris@76 749 {
Chris@76 750 if (is_webkit)
Chris@76 751 this.smf_execCommand('styleWithCSS', false, true);
Chris@76 752 this.smf_execCommand('fontname', false, sValue);
Chris@76 753 }
Chris@76 754 }
Chris@76 755
Chris@76 756 // Font size?
Chris@76 757 else if (oSelectProperties.sName == 'sel_size')
Chris@76 758 {
Chris@76 759 // Are we in boring mode?
Chris@76 760 if (!this.bRichTextEnabled)
Chris@76 761 {
Chris@76 762 surroundText('[size=' + this.aFontSizes[sValue] + 'pt]', '[/size]', this.oTextHandle);
Chris@76 763 oSelectProperties.oSelect.selectedIndex = 0;
Chris@76 764 }
Chris@76 765
Chris@76 766 else
Chris@76 767 this.smf_execCommand('fontsize', false, sValue);
Chris@76 768 }
Chris@76 769 // Or color even?
Chris@76 770 else if (oSelectProperties.sName == 'sel_color')
Chris@76 771 {
Chris@76 772 // Are we in boring mode?
Chris@76 773 if (!this.bRichTextEnabled)
Chris@76 774 {
Chris@76 775 surroundText('[color=' + sValue + ']', '[/color]', this.oTextHandle);
Chris@76 776 oSelectProperties.oSelect.selectedIndex = 0;
Chris@76 777 }
Chris@76 778
Chris@76 779 else
Chris@76 780 this.smf_execCommand('forecolor', false, sValue);
Chris@76 781 }
Chris@76 782
Chris@76 783 this.updateEditorControls();
Chris@76 784
Chris@76 785 return true;
Chris@76 786 }
Chris@76 787
Chris@76 788 // Put in some custom HTML.
Chris@76 789 smc_Editor.prototype.insertCustomHTML = function(sLeftTag, sRightTag)
Chris@76 790 {
Chris@76 791 var sSelection = this.getSelect(true, true);
Chris@76 792 if (sSelection.length == 0)
Chris@76 793 sSelection = '';
Chris@76 794
Chris@76 795 // Are we overwriting?
Chris@76 796 if (sRightTag == '')
Chris@76 797 this.insertText(sLeftTag);
Chris@76 798 // If something was selected, replace and position cursor at the end of it.
Chris@76 799 else if (sSelection.length > 0)
Chris@76 800 this.insertText(sLeftTag + sSelection + sRightTag, false, false, 0);
Chris@76 801 // Wrap the tags around the cursor position.
Chris@76 802 else
Chris@76 803 this.insertText(sLeftTag + sRightTag, false, false, sRightTag.length);
Chris@76 804
Chris@76 805 }
Chris@76 806
Chris@76 807 // Insert a URL link.
Chris@76 808 smc_Editor.prototype.insertLink = function(sType)
Chris@76 809 {
Chris@76 810 if (sType == 'email')
Chris@76 811 var sPromptText = oEditorStrings['prompt_text_email'];
Chris@76 812 else if (sType == 'ftp')
Chris@76 813 var sPromptText = oEditorStrings['prompt_text_ftp'];
Chris@76 814 else
Chris@76 815 var sPromptText = oEditorStrings['prompt_text_url'];
Chris@76 816
Chris@76 817 // IE has a nice prompt for this - others don't.
Chris@76 818 if (sType != 'email' && sType != 'ftp' && is_ie)
Chris@76 819 this.smf_execCommand('createlink', true, 'http://');
Chris@76 820
Chris@76 821 else
Chris@76 822 {
Chris@76 823 // Ask them where to link to.
Chris@76 824 var sText = prompt(sPromptText, sType == 'email' ? '' : (sType == 'ftp' ? 'ftp://' : 'http://'));
Chris@76 825 if (!sText)
Chris@76 826 return;
Chris@76 827
Chris@76 828 if (sType == 'email' && sText.indexOf('mailto:') != 0)
Chris@76 829 sText = 'mailto:' + sText;
Chris@76 830
Chris@76 831 // Check if we have text selected and if not force us to have some.
Chris@76 832 var oCurText = this.getSelect(true, true);
Chris@76 833
Chris@76 834 if (oCurText.toString().length != 0)
Chris@76 835 {
Chris@76 836 this.smf_execCommand('unlink');
Chris@76 837 this.smf_execCommand('createlink', false, sText);
Chris@76 838 }
Chris@76 839 else
Chris@76 840 this.insertText('<a href="' + sText + '">' + sText + '</a>');
Chris@76 841 }
Chris@76 842 }
Chris@76 843
Chris@76 844 smc_Editor.prototype.insertImage = function(sSrc)
Chris@76 845 {
Chris@76 846 if (!sSrc)
Chris@76 847 {
Chris@76 848 sSrc = prompt(oEditorStrings['prompt_text_img'], 'http://');
Chris@76 849 if (!sSrc || sSrc.length < 10)
Chris@76 850 return;
Chris@76 851 }
Chris@76 852 this.smf_execCommand('insertimage', false, sSrc);
Chris@76 853 }
Chris@76 854
Chris@76 855 smc_Editor.prototype.getSelect = function(bWantText, bWantHTMLText)
Chris@76 856 {
Chris@76 857 if (is_ie && 'selection' in this.oFrameDocument)
Chris@76 858 {
Chris@76 859 // Just want plain text?
Chris@76 860 if (bWantText && !bWantHTMLText)
Chris@76 861 return this.oFrameDocument.selection.createRange().text;
Chris@76 862 // We want the HTML flavoured variety?
Chris@76 863 else if (bWantHTMLText)
Chris@76 864 return this.oFrameDocument.selection.createRange().htmlText;
Chris@76 865
Chris@76 866 return this.oFrameDocument.selection;
Chris@76 867 }
Chris@76 868
Chris@76 869 // This is mainly Firefox.
Chris@76 870 if ('getSelection' in this.oFrameWindow)
Chris@76 871 {
Chris@76 872 // Plain text?
Chris@76 873 if (bWantText && !bWantHTMLText)
Chris@76 874 return this.oFrameWindow.getSelection().toString();
Chris@76 875
Chris@76 876 // HTML is harder - currently using: http://www.faqts.com/knowledge_base/view.phtml/aid/32427
Chris@76 877 else if (bWantHTMLText)
Chris@76 878 {
Chris@76 879 var oSelection = this.oFrameWindow.getSelection();
Chris@76 880 if (oSelection.rangeCount > 0)
Chris@76 881 {
Chris@76 882 var oRange = oSelection.getRangeAt(0);
Chris@76 883 var oClonedSelection = oRange.cloneContents();
Chris@76 884 var oDiv = this.oFrameDocument.createElement('div');
Chris@76 885 oDiv.appendChild(oClonedSelection);
Chris@76 886 return oDiv.innerHTML;
Chris@76 887 }
Chris@76 888 else
Chris@76 889 return '';
Chris@76 890 }
Chris@76 891
Chris@76 892 // Want the whole object then.
Chris@76 893 return this.oFrameWindow.getSelection();
Chris@76 894 }
Chris@76 895
Chris@76 896 // If we're here it's not good.
Chris@76 897 return this.oFrameDocument.getSelection();
Chris@76 898 }
Chris@76 899
Chris@76 900 smc_Editor.prototype.getRange = function()
Chris@76 901 {
Chris@76 902 // Get the current selection.
Chris@76 903 var oSelection = this.getSelect();
Chris@76 904
Chris@76 905 if (!oSelection)
Chris@76 906 return null;
Chris@76 907
Chris@76 908 if (is_ie && oSelection.createRange)
Chris@76 909 return oSelection.createRange();
Chris@76 910
Chris@76 911 return oSelection.rangeCount == 0 ? null : oSelection.getRangeAt(0);
Chris@76 912 }
Chris@76 913
Chris@76 914 // Get the current element.
Chris@76 915 smc_Editor.prototype.getCurElement = function()
Chris@76 916 {
Chris@76 917 var oRange = this.getRange();
Chris@76 918
Chris@76 919 if (!oRange)
Chris@76 920 return null;
Chris@76 921
Chris@76 922 if (is_ie)
Chris@76 923 {
Chris@76 924 if (oRange.item)
Chris@76 925 return oRange.item(0);
Chris@76 926 else
Chris@76 927 return oRange.parentElement();
Chris@76 928 }
Chris@76 929 else
Chris@76 930 {
Chris@76 931 var oElement = oRange.commonAncestorContainer;
Chris@76 932 return this.getParentElement(oElement);
Chris@76 933 }
Chris@76 934 }
Chris@76 935
Chris@76 936 smc_Editor.prototype.getParentElement = function(oNode)
Chris@76 937 {
Chris@76 938 if (oNode.nodeType == 1)
Chris@76 939 return oNode;
Chris@76 940
Chris@76 941 for (var i = 0; i < 50; i++)
Chris@76 942 {
Chris@76 943 if (!oNode.parentNode)
Chris@76 944 break;
Chris@76 945
Chris@76 946 oNode = oNode.parentNode;
Chris@76 947 if (oNode.nodeType == 1)
Chris@76 948 return oNode;
Chris@76 949 }
Chris@76 950 return null;
Chris@76 951 }
Chris@76 952
Chris@76 953 // Remove formatting for the selected text.
Chris@76 954 smc_Editor.prototype.removeFormatting = function()
Chris@76 955 {
Chris@76 956 // Do both at once.
Chris@76 957 if (this.bRichTextEnabled)
Chris@76 958 {
Chris@76 959 this.smf_execCommand('removeformat');
Chris@76 960 this.smf_execCommand('unlink');
Chris@76 961 }
Chris@76 962 // Otherwise do a crude move indeed.
Chris@76 963 else
Chris@76 964 {
Chris@76 965 // Get the current selection first.
Chris@76 966 if (this.oTextHandle.caretPos)
Chris@76 967 var sCurrentText = this.oTextHandle.caretPos.text;
Chris@76 968
Chris@76 969 else if ('selectionStart' in this.oTextHandle)
Chris@76 970 var sCurrentText = this.oTextHandle.value.substr(this.oTextHandle.selectionStart, (this.oTextHandle.selectionEnd - this.oTextHandle.selectionStart));
Chris@76 971
Chris@76 972 else
Chris@76 973 return;
Chris@76 974
Chris@76 975 // Do bits that are likely to have attributes.
Chris@76 976 sCurrentText = sCurrentText.replace(RegExp("\\[/?(url|img|iurl|ftp|email|img|color|font|size|list|bdo).*?\\]", "g"), '');
Chris@76 977 // Then just anything that looks like BBC.
Chris@76 978 sCurrentText = sCurrentText.replace(RegExp("\\[/?[A-Za-z]+\\]", "g"), '');
Chris@76 979
Chris@76 980 replaceText(sCurrentText, this.oTextHandle);
Chris@76 981 }
Chris@76 982 }
Chris@76 983
Chris@76 984 // Toggle wysiwyg/normal mode.
Chris@76 985 smc_Editor.prototype.toggleView = function(bView)
Chris@76 986 {
Chris@76 987 if (!this.bRichTextPossible)
Chris@76 988 {
Chris@76 989 alert(oEditorStrings['wont_work']);
Chris@76 990 return false;
Chris@76 991 }
Chris@76 992
Chris@76 993 // Overriding or alternating?
Chris@76 994 if (typeof(bView) == 'undefined')
Chris@76 995 bView = !this.bRichTextEnabled;
Chris@76 996
Chris@76 997 this.requestParsedMessage(bView);
Chris@76 998
Chris@76 999 return true;
Chris@76 1000 }
Chris@76 1001
Chris@76 1002 // Request the message in a different form.
Chris@76 1003 smc_Editor.prototype.requestParsedMessage = function(bView)
Chris@76 1004 {
Chris@76 1005 // Replace with a force reload.
Chris@76 1006 if (!window.XMLHttpRequest)
Chris@76 1007 {
Chris@76 1008 alert(oEditorStrings['func_disabled']);
Chris@76 1009 return;
Chris@76 1010 }
Chris@76 1011
Chris@76 1012 // Get the text.
Chris@76 1013 var sText = this.getText(true, !bView).replace(/&#/g, "&#38;#").php_to8bit().php_urlencode();
Chris@76 1014
Chris@76 1015 this.tmpMethod = sendXMLDocument;
Chris@76 1016 this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=' + (bView ? 1 : 0) + ';' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, this.onToggleDataReceived);
Chris@76 1017 delete tmpMethod;
Chris@76 1018 }
Chris@76 1019
Chris@76 1020 smc_Editor.prototype.onToggleDataReceived = function(oXMLDoc)
Chris@76 1021 {
Chris@76 1022 var sText = '';
Chris@76 1023 for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++)
Chris@76 1024 sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue;
Chris@76 1025
Chris@76 1026 // What is this new view we have?
Chris@76 1027 this.bRichTextEnabled = oXMLDoc.getElementsByTagName('message')[0].getAttribute('view') != '0';
Chris@76 1028
Chris@76 1029 if (this.bRichTextEnabled)
Chris@76 1030 {
Chris@76 1031 this.oFrameHandle.style.display = '';
Chris@76 1032 if (this.showDebug)
Chris@76 1033 this.oBreadHandle.style.display = '';
Chris@76 1034 this.oTextHandle.style.display = 'none';
Chris@76 1035 }
Chris@76 1036 else
Chris@76 1037 {
Chris@76 1038 sText = sText.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
Chris@76 1039 this.oFrameHandle.style.display = 'none';
Chris@76 1040 this.oBreadHandle.style.display = 'none';
Chris@76 1041 this.oTextHandle.style.display = '';
Chris@76 1042 }
Chris@76 1043
Chris@76 1044 // First we focus.
Chris@76 1045 this.setFocus();
Chris@76 1046
Chris@76 1047 this.insertText(sText, true);
Chris@76 1048
Chris@76 1049 // Record the new status.
Chris@76 1050 document.getElementById(this.opt.sUniqueId + '_mode').value = this.bRichTextEnabled ? '1' : '0';
Chris@76 1051
Chris@76 1052 // Rebuild the bread crumb!
Chris@76 1053 this.updateEditorControls();
Chris@76 1054 }
Chris@76 1055
Chris@76 1056 // Set the focus for the editing window.
Chris@76 1057 smc_Editor.prototype.setFocus = function(force_both)
Chris@76 1058 {
Chris@76 1059 if (!this.bRichTextEnabled)
Chris@76 1060 this.oTextHandle.focus();
Chris@76 1061 else if (is_ff || is_opera)
Chris@76 1062 this.oFrameHandle.focus();
Chris@76 1063 else
Chris@76 1064 this.oFrameWindow.focus();
Chris@76 1065 }
Chris@76 1066
Chris@76 1067 // Start up the spellchecker!
Chris@76 1068 smc_Editor.prototype.spellCheckStart = function()
Chris@76 1069 {
Chris@76 1070 if (!spellCheck)
Chris@76 1071 return false;
Chris@76 1072
Chris@76 1073 // If we're in HTML mode we need to get the non-HTML text.
Chris@76 1074 if (this.bRichTextEnabled)
Chris@76 1075 {
Chris@76 1076 var sText = escape(this.getText(true, 1).php_to8bit());
Chris@76 1077
Chris@76 1078 this.tmpMethod = sendXMLDocument;
Chris@76 1079 this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=0;' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, this.onSpellCheckDataReceived);
Chris@76 1080 delete tmpMethod;
Chris@76 1081 }
Chris@76 1082 // Otherwise start spellchecking right away.
Chris@76 1083 else
Chris@76 1084 spellCheck(this.sFormId, this.opt.sUniqueId);
Chris@76 1085
Chris@76 1086 return true;
Chris@76 1087 }
Chris@76 1088
Chris@76 1089 // This contains the spellcheckable text.
Chris@76 1090 smc_Editor.prototype.onSpellCheckDataReceived = function(oXMLDoc)
Chris@76 1091 {
Chris@76 1092 var sText = '';
Chris@76 1093 for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++)
Chris@76 1094 sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue;
Chris@76 1095
Chris@76 1096 sText = sText.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
Chris@76 1097
Chris@76 1098 this.oTextHandle.value = sText;
Chris@76 1099 spellCheck(this.sFormId, this.opt.sUniqueId);
Chris@76 1100 }
Chris@76 1101
Chris@76 1102 // Function called when the Spellchecker is finished and ready to pass back.
Chris@76 1103 smc_Editor.prototype.spellCheckEnd = function()
Chris@76 1104 {
Chris@76 1105 // If HTML edit put the text back!
Chris@76 1106 if (this.bRichTextEnabled)
Chris@76 1107 {
Chris@76 1108 var sText = escape(this.getText(true, 0).php_to8bit());
Chris@76 1109
Chris@76 1110 this.tmpMethod = sendXMLDocument;
Chris@76 1111 this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=1;' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, smf_editorArray[this.iArrayPosition].onSpellCheckCompleteDataReceived);
Chris@76 1112 delete tmpMethod;
Chris@76 1113 }
Chris@76 1114 else
Chris@76 1115 this.setFocus();
Chris@76 1116 }
Chris@76 1117
Chris@76 1118 // The corrected text.
Chris@76 1119 smc_Editor.prototype.onSpellCheckCompleteDataReceived = function(oXMLDoc)
Chris@76 1120 {
Chris@76 1121 var sText = '';
Chris@76 1122 for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++)
Chris@76 1123 sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue;
Chris@76 1124
Chris@76 1125 this.insertText(sText, true);
Chris@76 1126 this.setFocus();
Chris@76 1127 }
Chris@76 1128
Chris@76 1129 smc_Editor.prototype.resizeTextArea = function(newHeight, newWidth, is_change)
Chris@76 1130 {
Chris@76 1131 // Work out what the new height is.
Chris@76 1132 if (is_change)
Chris@76 1133 {
Chris@76 1134 // We'll assume pixels but may not be.
Chris@76 1135 newHeight = this._calculateNewDimension(this.oTextHandle.style.height, newHeight);
Chris@76 1136 if (newWidth)
Chris@76 1137 newWidth = this._calculateNewDimension(this.oTextHandle.style.width, newWidth);
Chris@76 1138 }
Chris@76 1139
Chris@76 1140 // Do the HTML editor - but only if it's enabled!
Chris@76 1141 if (this.bRichTextPossible)
Chris@76 1142 {
Chris@76 1143 this.oFrameHandle.style.height = newHeight;
Chris@76 1144 if (newWidth)
Chris@76 1145 this.oFrameHandle.style.width = newWidth;
Chris@76 1146 }
Chris@76 1147 // Do the text box regardless!
Chris@76 1148 this.oTextHandle.style.height = newHeight;
Chris@76 1149 if (newWidth)
Chris@76 1150 this.oTextHandle.style.width = newWidth;
Chris@76 1151 }
Chris@76 1152
Chris@76 1153 // A utility instruction to save repetition when trying to work out what to change on a height/width.
Chris@76 1154 smc_Editor.prototype._calculateNewDimension = function(old_size, change_size)
Chris@76 1155 {
Chris@76 1156 // We'll assume pixels but may not be.
Chris@76 1157 changeReg = change_size.toString().match(/(-)?(\d+)(\D*)/);
Chris@76 1158 curReg = old_size.toString().match(/(\d+)(\D*)/);
Chris@76 1159
Chris@76 1160 if (!changeReg[3])
Chris@76 1161 changeReg[3] = 'px';
Chris@76 1162
Chris@76 1163 if (changeReg[1] == '-')
Chris@76 1164 changeReg[2] = 0 - changeReg[2];
Chris@76 1165
Chris@76 1166 // Both the same type?
Chris@76 1167 if (changeReg[3] == curReg[2])
Chris@76 1168 {
Chris@76 1169 new_size = parseInt(changeReg[2]) + parseInt(curReg[1]);
Chris@76 1170 if (new_size < 50)
Chris@76 1171 new_size = 50;
Chris@76 1172 new_size = new_size.toString() + changeReg[3];
Chris@76 1173 }
Chris@76 1174 // Is the change a percentage?
Chris@76 1175 else if (changeReg[3] == '%')
Chris@76 1176 new_size = (parseInt(curReg[1]) + parseInt((parseInt(changeReg[2]) * parseInt(curReg[1])) / 100)).toString() + 'px';
Chris@76 1177 // Otherwise just guess!
Chris@76 1178 else
Chris@76 1179 new_size = (parseInt(curReg[1]) + (parseInt(changeReg[2]) / 10)).toString() + '%';
Chris@76 1180
Chris@76 1181 return new_size;
Chris@76 1182 }
Chris@76 1183
Chris@76 1184 // Register default keyboard shortcuts.
Chris@76 1185 smc_Editor.prototype.registerDefaultShortcuts = function()
Chris@76 1186 {
Chris@76 1187 if (is_ff)
Chris@76 1188 {
Chris@76 1189 this.registerShortcut('b', 'ctrl', 'b');
Chris@76 1190 this.registerShortcut('u', 'ctrl', 'u');
Chris@76 1191 this.registerShortcut('i', 'ctrl', 'i');
Chris@76 1192 this.registerShortcut('p', 'alt', 'preview');
Chris@76 1193 this.registerShortcut('s', 'alt', 'submit');
Chris@76 1194 }
Chris@76 1195 }
Chris@76 1196
Chris@76 1197 // Register a keyboard shortcut.
Chris@76 1198 smc_Editor.prototype.registerShortcut = function(sLetter, sModifiers, sCodeName)
Chris@76 1199 {
Chris@76 1200 if (!sCodeName)
Chris@76 1201 return;
Chris@76 1202
Chris@76 1203 var oNewShortcut = {
Chris@76 1204 code : sCodeName,
Chris@76 1205 key: sLetter.toUpperCase().charCodeAt(0),
Chris@76 1206 alt : false,
Chris@76 1207 ctrl : false
Chris@76 1208 };
Chris@76 1209
Chris@76 1210 var aSplitModifiers = sModifiers.split(',');
Chris@76 1211 for(var i = 0, n = aSplitModifiers.length; i < n; i++)
Chris@76 1212 if (aSplitModifiers[i] in oNewShortcut)
Chris@76 1213 oNewShortcut[aSplitModifiers[i]] = true;
Chris@76 1214
Chris@76 1215 this.aKeyboardShortcuts[this.aKeyboardShortcuts.length] = oNewShortcut;
Chris@76 1216 }
Chris@76 1217
Chris@76 1218 // Check whether the key has triggered a shortcut?
Chris@76 1219 smc_Editor.prototype.checkShortcut = function(oEvent)
Chris@76 1220 {
Chris@76 1221 // To be a shortcut it needs to be one of these, duh!
Chris@76 1222 if (!oEvent.altKey && !oEvent.ctrlKey)
Chris@76 1223 return false;
Chris@76 1224
Chris@76 1225 var sReturnCode = false;
Chris@76 1226
Chris@76 1227 // Let's take a look at each of our shortcuts shall we?
Chris@76 1228 for (var i = 0, n = this.aKeyboardShortcuts.length; i < n; i++)
Chris@76 1229 {
Chris@76 1230 // Found something?
Chris@76 1231 if (oEvent.altKey == this.aKeyboardShortcuts[i].alt && oEvent.ctrlKey == this.aKeyboardShortcuts[i].ctrl && oEvent.keyCode == this.aKeyboardShortcuts[i].key)
Chris@76 1232 sReturnCode = this.aKeyboardShortcuts[i].code;
Chris@76 1233 }
Chris@76 1234
Chris@76 1235 return sReturnCode;
Chris@76 1236 }
Chris@76 1237
Chris@76 1238 // The actual event check for the above!
Chris@76 1239 smc_Editor.prototype.shortcutCheck = function(oEvent)
Chris@76 1240 {
Chris@76 1241 var sFoundCode = this.checkShortcut(oEvent);
Chris@76 1242
Chris@76 1243 // Run it and exit.
Chris@76 1244 if (typeof(sFoundCode) == 'string' && sFoundCode != '')
Chris@76 1245 {
Chris@76 1246 var bCancelEvent = false;
Chris@76 1247 if (sFoundCode == 'submit')
Chris@76 1248 {
Chris@76 1249 // So much to do!
Chris@76 1250 var oForm = document.getElementById(this.sFormId);
Chris@76 1251 submitThisOnce(oForm);
Chris@76 1252 submitonce(oForm);
Chris@76 1253 smc_saveEntities(oForm.name, ['subject', this.opt.sUniqueId, 'guestname', 'evtitle', 'question']);
Chris@76 1254 oForm.submit();
Chris@76 1255
Chris@76 1256 bCancelEvent = true;
Chris@76 1257 }
Chris@76 1258 else if (sFoundCode == 'preview')
Chris@76 1259 {
Chris@76 1260 previewPost();
Chris@76 1261 bCancelEvent = true;
Chris@76 1262 }
Chris@76 1263 else
Chris@76 1264 bCancelEvent = this.opt.oBBCBox.emulateClick(sFoundCode);
Chris@76 1265
Chris@76 1266 if (bCancelEvent)
Chris@76 1267 {
Chris@76 1268 if (is_ie && oEvent.cancelBubble)
Chris@76 1269 oEvent.cancelBubble = true;
Chris@76 1270
Chris@76 1271 else if (oEvent.stopPropagation)
Chris@76 1272 {
Chris@76 1273 oEvent.stopPropagation();
Chris@76 1274 oEvent.preventDefault();
Chris@76 1275 }
Chris@76 1276
Chris@76 1277 return false;
Chris@76 1278 }
Chris@76 1279 }
Chris@76 1280
Chris@76 1281 return true;
Chris@76 1282 }
Chris@76 1283
Chris@76 1284 // This is the method called after clicking the resize bar.
Chris@76 1285 smc_Editor.prototype.startResize = function(oEvent)
Chris@76 1286 {
Chris@76 1287 if ('event' in window)
Chris@76 1288 oEvent = window.event;
Chris@76 1289
Chris@76 1290 if (!oEvent || window.smf_oCurrentResizeEditor != null)
Chris@76 1291 return true;
Chris@76 1292
Chris@76 1293 window.smf_oCurrentResizeEditor = this.iArrayPosition;
Chris@76 1294
Chris@76 1295 var aCurCoordinates = smf_mousePose(oEvent);
Chris@76 1296 this.osmc_EditorCurrentResize.old_y = aCurCoordinates[1];
Chris@76 1297 this.osmc_EditorCurrentResize.old_rel_y = null;
Chris@76 1298 this.osmc_EditorCurrentResize.cur_height = parseInt(this.oTextHandle.style.height);
Chris@76 1299
Chris@76 1300 // Set the necessary events for resizing.
Chris@76 1301 var oResizeEntity = is_ie ? document : window;
Chris@76 1302 oResizeEntity.addEventListener('mousemove', this.aEventWrappers.resizeOverDocument, false);
Chris@76 1303
Chris@76 1304 if (this.bRichTextPossible)
Chris@76 1305 this.oFrameDocument.addEventListener('mousemove', this.aEventWrappers.resizeOverIframe, false);
Chris@76 1306
Chris@76 1307 document.addEventListener('mouseup', this.aEventWrappers.endResize, true);
Chris@76 1308
Chris@76 1309 if (this.bRichTextPossible)
Chris@76 1310 this.oFrameDocument.addEventListener('mouseup', this.aEventWrappers.endResize, true);
Chris@76 1311
Chris@76 1312 return false;
Chris@76 1313 }
Chris@76 1314
Chris@76 1315 // This is kind of a cheat, as it only works over the IFRAME.
Chris@76 1316 smc_Editor.prototype.resizeOverIframe = function(oEvent)
Chris@76 1317 {
Chris@76 1318 if ('event' in window)
Chris@76 1319 oEvent = window.event;
Chris@76 1320
Chris@76 1321 if (!oEvent || window.smf_oCurrentResizeEditor == null)
Chris@76 1322 return true;
Chris@76 1323
Chris@76 1324 var newCords = smf_mousePose(oEvent);
Chris@76 1325
Chris@76 1326 if (this.osmc_EditorCurrentResize.old_rel_y == null)
Chris@76 1327 this.osmc_EditorCurrentResize.old_rel_y = newCords[1];
Chris@76 1328 else
Chris@76 1329 {
Chris@76 1330 var iNewHeight = newCords[1] - this.osmc_EditorCurrentResize.old_rel_y + this.osmc_EditorCurrentResize.cur_height;
Chris@76 1331 if (iNewHeight < 0)
Chris@76 1332 this.endResize();
Chris@76 1333 else
Chris@76 1334 this.resizeTextArea(iNewHeight + 'px', 0, false);
Chris@76 1335 }
Chris@76 1336
Chris@76 1337 return false;
Chris@76 1338 }
Chris@76 1339
Chris@76 1340 // This resizes an editor.
Chris@76 1341 smc_Editor.prototype.resizeOverDocument = function (oEvent)
Chris@76 1342 {
Chris@76 1343 if ('event' in window)
Chris@76 1344 oEvent = window.event;
Chris@76 1345
Chris@76 1346 if (!oEvent || window.smf_oCurrentResizeEditor == null)
Chris@76 1347 return true;
Chris@76 1348
Chris@76 1349 var newCords = smf_mousePose(oEvent);
Chris@76 1350
Chris@76 1351 var iNewHeight = newCords[1] - this.osmc_EditorCurrentResize.old_y + this.osmc_EditorCurrentResize.cur_height;
Chris@76 1352 if (iNewHeight < 0)
Chris@76 1353 this.endResize();
Chris@76 1354 else
Chris@76 1355 this.resizeTextArea(iNewHeight + 'px', 0, false);
Chris@76 1356
Chris@76 1357 return false;
Chris@76 1358 }
Chris@76 1359
Chris@76 1360 smc_Editor.prototype.endResize = function (oEvent)
Chris@76 1361 {
Chris@76 1362 if ('event' in window)
Chris@76 1363 oEvent = window.event;
Chris@76 1364
Chris@76 1365 if (window.smf_oCurrentResizeEditor == null)
Chris@76 1366 return true;
Chris@76 1367
Chris@76 1368 window.smf_oCurrentResizeEditor = null;
Chris@76 1369
Chris@76 1370 // Remove the event...
Chris@76 1371 var oResizeEntity = is_ie ? document : window;
Chris@76 1372 oResizeEntity.removeEventListener('mousemove', this.aEventWrappers.resizeOverDocument, false);
Chris@76 1373
Chris@76 1374 if (this.bRichTextPossible)
Chris@76 1375 this.oFrameDocument.removeEventListener('mousemove', this.aEventWrappers.resizeOverIframe, false);
Chris@76 1376
Chris@76 1377 document.removeEventListener('mouseup', this.aEventWrappers.endResize, true);
Chris@76 1378
Chris@76 1379 if (this.bRichTextPossible)
Chris@76 1380 this.oFrameDocument.removeEventListener('mouseup', this.aEventWrappers.endResize, true);
Chris@76 1381
Chris@76 1382 return false;
Chris@76 1383 }
Chris@76 1384
Chris@76 1385 // *** smc_SmileyBox class.
Chris@76 1386 function smc_SmileyBox(oOptions)
Chris@76 1387 {
Chris@76 1388 this.opt = oOptions;
Chris@76 1389 this.oSmileyRowsContent = {};
Chris@76 1390 this.oSmileyPopupWindow = null;
Chris@76 1391 this.init();
Chris@76 1392 }
Chris@76 1393
Chris@76 1394 smc_SmileyBox.prototype.init = function ()
Chris@76 1395 {
Chris@76 1396 // Get the HTML content of the smileys visible on the post screen.
Chris@76 1397 this.getSmileyRowsContent('postform');
Chris@76 1398
Chris@76 1399 // Inject the HTML.
Chris@76 1400 setInnerHTML(document.getElementById(this.opt.sContainerDiv), this.opt.sSmileyBoxTemplate.easyReplace({
Chris@76 1401 smileyRows: this.oSmileyRowsContent.postform,
Chris@76 1402 moreSmileys: this.opt.oSmileyLocations.popup.length == 0 ? '' : this.opt.sMoreSmileysTemplate.easyReplace({
Chris@76 1403 moreSmileysId: this.opt.sUniqueId + '_addMoreSmileys'
Chris@76 1404 })
Chris@76 1405 }));
Chris@76 1406
Chris@76 1407 // Initialize the smileys.
Chris@76 1408 this.initSmileys('postform', document);
Chris@76 1409
Chris@76 1410 // Initialize the [more] button.
Chris@76 1411 if (this.opt.oSmileyLocations.popup.length > 0)
Chris@76 1412 {
Chris@76 1413 var oMoreLink = document.getElementById(this.opt.sUniqueId + '_addMoreSmileys');
Chris@76 1414 oMoreLink.instanceRef = this;
Chris@76 1415 oMoreLink.onclick = function () {
Chris@76 1416 this.instanceRef.handleShowMoreSmileys();
Chris@76 1417 return false;
Chris@76 1418 }
Chris@76 1419 }
Chris@76 1420 }
Chris@76 1421
Chris@76 1422 // Loop through the smileys to setup the HTML.
Chris@76 1423 smc_SmileyBox.prototype.getSmileyRowsContent = function (sLocation)
Chris@76 1424 {
Chris@76 1425 // If it's already defined, don't bother.
Chris@76 1426 if (sLocation in this.oSmileyRowsContent)
Chris@76 1427 return;
Chris@76 1428
Chris@76 1429 this.oSmileyRowsContent[sLocation] = '';
Chris@76 1430
Chris@76 1431 for (var iSmileyRowIndex = 0, iSmileyRowCount = this.opt.oSmileyLocations[sLocation].length; iSmileyRowIndex < iSmileyRowCount; iSmileyRowIndex++)
Chris@76 1432 {
Chris@76 1433 var sSmileyRowContent = '';
Chris@76 1434 for (var iSmileyIndex = 0, iSmileyCount = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex].length; iSmileyIndex < iSmileyCount; iSmileyIndex++)
Chris@76 1435 sSmileyRowContent += this.opt.sSmileyTemplate.easyReplace({
Chris@76 1436 smileySource: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sSrc.php_htmlspecialchars(),
Chris@76 1437 smileyDescription: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sDescription.php_htmlspecialchars(),
Chris@76 1438 smileyCode: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sCode.php_htmlspecialchars(),
Chris@76 1439 smileyId: this.opt.sUniqueId + '_' + sLocation + '_' + iSmileyRowIndex.toString() + '_' + iSmileyIndex.toString()
Chris@76 1440 });
Chris@76 1441
Chris@76 1442 this.oSmileyRowsContent[sLocation] += this.opt.sSmileyRowTemplate.easyReplace({
Chris@76 1443 smileyRow: sSmileyRowContent
Chris@76 1444 });
Chris@76 1445 }
Chris@76 1446 }
Chris@76 1447
Chris@76 1448 smc_SmileyBox.prototype.initSmileys = function (sLocation, oDocument)
Chris@76 1449 {
Chris@76 1450 for (var iSmileyRowIndex = 0, iSmileyRowCount = this.opt.oSmileyLocations[sLocation].length; iSmileyRowIndex < iSmileyRowCount; iSmileyRowIndex++)
Chris@76 1451 {
Chris@76 1452 for (var iSmileyIndex = 0, iSmileyCount = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex].length; iSmileyIndex < iSmileyCount; iSmileyIndex++)
Chris@76 1453 {
Chris@76 1454 var oSmiley = oDocument.getElementById(this.opt.sUniqueId + '_' + sLocation + '_' + iSmileyRowIndex.toString() + '_' + iSmileyIndex.toString());
Chris@76 1455 oSmiley.instanceRef = this;
Chris@76 1456 oSmiley.style.cursor = 'pointer';
Chris@76 1457 oSmiley.onclick = function () {
Chris@76 1458 this.instanceRef.clickHandler(this);
Chris@76 1459 return false;
Chris@76 1460 }
Chris@76 1461 }
Chris@76 1462 }
Chris@76 1463 }
Chris@76 1464
Chris@76 1465 smc_SmileyBox.prototype.clickHandler = function (oSmileyImg)
Chris@76 1466 {
Chris@76 1467 // Dissect the id...
Chris@76 1468 var aMatches = oSmileyImg.id.match(/([^_]+)_(\d+)_(\d+)$/);
Chris@76 1469 if (aMatches.length != 4)
Chris@76 1470 return false;
Chris@76 1471
Chris@76 1472 // ...to determine its exact smiley properties.
Chris@76 1473 var sLocation = aMatches[1];
Chris@76 1474 var iSmileyRowIndex = aMatches[2];
Chris@76 1475 var iSmileyIndex = aMatches[3];
Chris@76 1476 var oProperties = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex];
Chris@76 1477
Chris@76 1478 if ('sClickHandler' in this.opt)
Chris@76 1479 eval(this.opt.sClickHandler + '(oProperties)');
Chris@76 1480
Chris@76 1481 return false;
Chris@76 1482 }
Chris@76 1483
Chris@76 1484 smc_SmileyBox.prototype.handleShowMoreSmileys = function ()
Chris@76 1485 {
Chris@76 1486 // Focus the window if it's already opened.
Chris@76 1487 if (this.oSmileyPopupWindow != null && 'closed' in this.oSmileyPopupWindow && !this.oSmileyPopupWindow.closed)
Chris@76 1488 {
Chris@76 1489 this.oSmileyPopupWindow.focus();
Chris@76 1490 return;
Chris@76 1491 }
Chris@76 1492
Chris@76 1493 // Get the smiley HTML.
Chris@76 1494 this.getSmileyRowsContent('popup');
Chris@76 1495
Chris@76 1496 // Open the popup.
Chris@76 1497 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');
Chris@76 1498
Chris@76 1499 // Paste the template in the popup.
Chris@76 1500 this.oSmileyPopupWindow.document.open('text/html', 'replace');
Chris@76 1501 this.oSmileyPopupWindow.document.write(this.opt.sMoreSmileysPopupTemplate.easyReplace({
Chris@76 1502 smileyRows: this.oSmileyRowsContent.popup,
Chris@76 1503 moreSmileysCloseLinkId: this.opt.sUniqueId + '_closeMoreSmileys'
Chris@76 1504 }));
Chris@76 1505 this.oSmileyPopupWindow.document.close();
Chris@76 1506
Chris@76 1507 // Initialize the smileys that are in the popup window.
Chris@76 1508 this.initSmileys('popup', this.oSmileyPopupWindow.document);
Chris@76 1509
Chris@76 1510 // Add a function to the close window button.
Chris@76 1511 var aCloseLink = this.oSmileyPopupWindow.document.getElementById(this.opt.sUniqueId + '_closeMoreSmileys');
Chris@76 1512 aCloseLink.instanceRef = this;
Chris@76 1513 aCloseLink.onclick = function () {
Chris@76 1514 this.instanceRef.oSmileyPopupWindow.close();
Chris@76 1515 return false;
Chris@76 1516 }
Chris@76 1517 }
Chris@76 1518
Chris@76 1519
Chris@76 1520 // *** smc_BBCButtonBox class.
Chris@76 1521 function smc_BBCButtonBox(oOptions)
Chris@76 1522 {
Chris@76 1523 this.opt = oOptions;
Chris@76 1524 this.init();
Chris@76 1525 }
Chris@76 1526
Chris@76 1527 smc_BBCButtonBox.prototype.init = function ()
Chris@76 1528 {
Chris@76 1529 var sBbcContent = '';
Chris@76 1530 for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
Chris@76 1531 {
Chris@76 1532 var sRowContent = '';
Chris@76 1533 var bPreviousWasDivider = false;
Chris@76 1534 for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
Chris@76 1535 {
Chris@76 1536 var oCurButton = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
Chris@76 1537 switch (oCurButton.sType)
Chris@76 1538 {
Chris@76 1539 case 'button':
Chris@76 1540 if (oCurButton.bEnabled)
Chris@76 1541 {
Chris@76 1542 sRowContent += this.opt.sButtonTemplate.easyReplace({
Chris@76 1543 buttonId: this.opt.sUniqueId.php_htmlspecialchars() + '_button_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString(),
Chris@76 1544 buttonSrc: oCurButton.sImage.php_htmlspecialchars(),
Chris@76 1545 buttonDescription: oCurButton.sDescription.php_htmlspecialchars()
Chris@76 1546 });
Chris@76 1547
Chris@76 1548 bPreviousWasDivider = false;
Chris@76 1549 }
Chris@76 1550 break;
Chris@76 1551
Chris@76 1552 case 'divider':
Chris@76 1553 if (!bPreviousWasDivider)
Chris@76 1554 sRowContent += this.opt.sDividerTemplate;
Chris@76 1555
Chris@76 1556 bPreviousWasDivider = true;
Chris@76 1557 break;
Chris@76 1558
Chris@76 1559 case 'select':
Chris@76 1560 var sOptions = '';
Chris@76 1561
Chris@76 1562 // Fighting javascript's idea of order in a for loop... :P
Chris@76 1563 if ('' in oCurButton.oOptions)
Chris@76 1564 sOptions = '<option value="">' + oCurButton.oOptions[''].php_htmlspecialchars() + '</option>';
Chris@76 1565 for (var sSelectValue in oCurButton.oOptions)
Chris@76 1566 // we've been through this before
Chris@76 1567 if (sSelectValue != '')
Chris@76 1568 sOptions += '<option value="' + sSelectValue.php_htmlspecialchars() + '">' + oCurButton.oOptions[sSelectValue].php_htmlspecialchars() + '</option>';
Chris@76 1569
Chris@76 1570 sRowContent += this.opt.sSelectTemplate.easyReplace({
Chris@76 1571 selectName: oCurButton.sName,
Chris@76 1572 selectId: this.opt.sUniqueId.php_htmlspecialchars() + '_select_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString(),
Chris@76 1573 selectOptions: sOptions
Chris@76 1574 });
Chris@76 1575
Chris@76 1576 bPreviousWasDivider = false;
Chris@76 1577 break;
Chris@76 1578 }
Chris@76 1579 }
Chris@76 1580 sBbcContent += this.opt.sButtonRowTemplate.easyReplace({
Chris@76 1581 buttonRow: sRowContent
Chris@76 1582 });
Chris@76 1583 }
Chris@76 1584
Chris@76 1585 var oBbcContainer = document.getElementById(this.opt.sContainerDiv);
Chris@76 1586 setInnerHTML(oBbcContainer, sBbcContent);
Chris@76 1587
Chris@76 1588 for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
Chris@76 1589 {
Chris@76 1590 for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
Chris@76 1591 {
Chris@76 1592 var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
Chris@76 1593 switch (oCurControl.sType)
Chris@76 1594 {
Chris@76 1595 case 'button':
Chris@76 1596 if (!oCurControl.bEnabled)
Chris@76 1597 break;
Chris@76 1598
Chris@76 1599 oCurControl.oImg = document.getElementById(this.opt.sUniqueId.php_htmlspecialchars() + '_button_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString());
Chris@76 1600 oCurControl.oImg.style.cursor = 'pointer';
Chris@76 1601 if ('sButtonBackgroundImage' in this.opt)
Chris@76 1602 oCurControl.oImg.style.backgroundImage = 'url(' + this.opt.sButtonBackgroundImage + ')';
Chris@76 1603
Chris@76 1604 oCurControl.oImg.instanceRef = this;
Chris@76 1605 oCurControl.oImg.onmouseover = function () {
Chris@76 1606 this.instanceRef.handleButtonMouseOver(this);
Chris@76 1607 };
Chris@76 1608 oCurControl.oImg.onmouseout = function () {
Chris@76 1609 this.instanceRef.handleButtonMouseOut(this);
Chris@76 1610 };
Chris@76 1611 oCurControl.oImg.onclick = function () {
Chris@76 1612 this.instanceRef.handleButtonClick(this);
Chris@76 1613 };
Chris@76 1614
Chris@76 1615 oCurControl.oImg.bIsActive = false;
Chris@76 1616 oCurControl.oImg.bHover = false;
Chris@76 1617 break;
Chris@76 1618
Chris@76 1619 case 'select':
Chris@76 1620 oCurControl.oSelect = document.getElementById(this.opt.sUniqueId.php_htmlspecialchars() + '_select_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString());
Chris@76 1621
Chris@76 1622 oCurControl.oSelect.instanceRef = this;
Chris@76 1623 oCurControl.oSelect.onchange = oCurControl.onchange = function () {
Chris@76 1624 this.instanceRef.handleSelectChange(this);
Chris@76 1625 }
Chris@76 1626 break;
Chris@76 1627 }
Chris@76 1628 }
Chris@76 1629 }
Chris@76 1630 }
Chris@76 1631
Chris@76 1632 smc_BBCButtonBox.prototype.handleButtonMouseOver = function (oButtonImg)
Chris@76 1633 {
Chris@76 1634 oButtonImg.bHover = true;
Chris@76 1635 this.updateButtonStatus(oButtonImg);
Chris@76 1636 }
Chris@76 1637
Chris@76 1638 smc_BBCButtonBox.prototype.handleButtonMouseOut = function (oButtonImg)
Chris@76 1639 {
Chris@76 1640 oButtonImg.bHover = false;
Chris@76 1641 this.updateButtonStatus(oButtonImg);
Chris@76 1642 }
Chris@76 1643
Chris@76 1644 smc_BBCButtonBox.prototype.updateButtonStatus = function (oButtonImg)
Chris@76 1645 {
Chris@76 1646 var sNewURL = '';
Chris@76 1647 if (oButtonImg.bHover && oButtonImg.bIsActive && 'sActiveButtonBackgroundImageHover' in this.opt)
Chris@76 1648 sNewURL = 'url(' + this.opt.sActiveButtonBackgroundImageHover + ')';
Chris@76 1649 else if (!oButtonImg.bHover && oButtonImg.bIsActive && 'sActiveButtonBackgroundImage' in this.opt)
Chris@76 1650 sNewURL = 'url(' + this.opt.sActiveButtonBackgroundImage + ')';
Chris@76 1651 else if (oButtonImg.bHover && 'sButtonBackgroundImageHover' in this.opt)
Chris@76 1652 sNewURL = 'url(' + this.opt.sButtonBackgroundImageHover + ')';
Chris@76 1653 else if ('sButtonBackgroundImage' in this.opt)
Chris@76 1654 sNewURL = 'url(' + this.opt.sButtonBackgroundImage + ')';
Chris@76 1655
Chris@76 1656 if (oButtonImg.style.backgroundImage != sNewURL)
Chris@76 1657 oButtonImg.style.backgroundImage = sNewURL;
Chris@76 1658 }
Chris@76 1659
Chris@76 1660 smc_BBCButtonBox.prototype.handleButtonClick = function (oButtonImg)
Chris@76 1661 {
Chris@76 1662 // Dissect the id attribute...
Chris@76 1663 var aMatches = oButtonImg.id.match(/(\d+)_(\d+)$/);
Chris@76 1664 if (aMatches.length != 3)
Chris@76 1665 return false;
Chris@76 1666
Chris@76 1667 // ...so that we can point to the exact button.
Chris@76 1668 var iButtonRowIndex = aMatches[1];
Chris@76 1669 var iButtonIndex = aMatches[2];
Chris@76 1670 var oProperties = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
Chris@76 1671 oProperties.bIsActive = oButtonImg.bIsActive;
Chris@76 1672
Chris@76 1673 if ('sButtonClickHandler' in this.opt)
Chris@76 1674 eval(this.opt.sButtonClickHandler + '(oProperties)');
Chris@76 1675
Chris@76 1676 return false;
Chris@76 1677 }
Chris@76 1678
Chris@76 1679 smc_BBCButtonBox.prototype.handleSelectChange = function (oSelectControl)
Chris@76 1680 {
Chris@76 1681 // Dissect the id attribute...
Chris@76 1682 var aMatches = oSelectControl.id.match(/(\d+)_(\d+)$/);
Chris@76 1683 if (aMatches.length != 3)
Chris@76 1684 return false;
Chris@76 1685
Chris@76 1686 // ...so that we can point to the exact button.
Chris@76 1687 var iButtonRowIndex = aMatches[1];
Chris@76 1688 var iButtonIndex = aMatches[2];
Chris@76 1689 var oProperties = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
Chris@76 1690
Chris@76 1691 if ('sSelectChangeHandler' in this.opt)
Chris@76 1692 eval(this.opt.sSelectChangeHandler + '(oProperties)');
Chris@76 1693
Chris@76 1694 return true;
Chris@76 1695 }
Chris@76 1696
Chris@76 1697 smc_BBCButtonBox.prototype.setActive = function (aButtons)
Chris@76 1698 {
Chris@76 1699 for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
Chris@76 1700 {
Chris@76 1701 for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
Chris@76 1702 {
Chris@76 1703 var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
Chris@76 1704 if (oCurControl.sType == 'button' && oCurControl.bEnabled)
Chris@76 1705 {
Chris@76 1706 oCurControl.oImg.bIsActive = in_array(oCurControl.sCode, aButtons);
Chris@76 1707 this.updateButtonStatus(oCurControl.oImg);
Chris@76 1708 }
Chris@76 1709 }
Chris@76 1710 }
Chris@76 1711 }
Chris@76 1712
Chris@76 1713 smc_BBCButtonBox.prototype.emulateClick = function (sCode)
Chris@76 1714 {
Chris@76 1715 for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
Chris@76 1716 {
Chris@76 1717 for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
Chris@76 1718 {
Chris@76 1719 var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
Chris@76 1720 if (oCurControl.sType == 'button' && oCurControl.sCode == sCode)
Chris@76 1721 {
Chris@76 1722 eval(this.opt.sButtonClickHandler + '(oCurControl)');
Chris@76 1723 return true;
Chris@76 1724 }
Chris@76 1725 }
Chris@76 1726 }
Chris@76 1727 return false;
Chris@76 1728 }
Chris@76 1729
Chris@76 1730 smc_BBCButtonBox.prototype.setSelect = function (sSelectName, sValue)
Chris@76 1731 {
Chris@76 1732 if (!('sButtonClickHandler' in this.opt))
Chris@76 1733 return;
Chris@76 1734
Chris@76 1735 for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++)
Chris@76 1736 {
Chris@76 1737 for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++)
Chris@76 1738 {
Chris@76 1739 var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex];
Chris@76 1740 if (oCurControl.sType == 'select' && oCurControl.sName == sSelectName)
Chris@76 1741 oCurControl.oSelect.value = sValue;
Chris@76 1742 }
Chris@76 1743 }
Chris@76 1744 }