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