1/** 2 * Bitrix HTML Editor 3.0 3 * Date: 24.04.13 4 * Time: 4:23 5 * 6 * Commands class 7 * Rich Text Query/Formatting Commands 8 */ 9(function() 10{ 11 function BXEditorActions(editor) 12 { 13 this.editor = editor; 14 this.document = editor.sandbox.GetDocument(); 15 BX.addCustomEvent(this.editor, 'OnIframeReInit', BX.proxy(function(){this.document = this.editor.sandbox.GetDocument();}, this)); 16 this.actions = this.GetActionList(); 17 this.contentActionIndex = { 18 removeFormat: 1, 19 bold: 1, 20 italic: 1, 21 underline: 1, 22 strikeout: 1, 23 fontSize: 1, 24 foreColor: 1, 25 backgroundColor: 1, 26 formatInline: 1, 27 formatBlock: 1, 28 createLink: 1, 29 insertHTML: 1, 30 insertImage: 1, 31 insertLineBreak: 1, 32 removeLink: 1, 33 insertOrderedList: 1, 34 insertUnorderedList: 1, 35 align: 1, 36 indent: 1, 37 outdent: 1, 38 formatStyle: 1, 39 fontFamily: 1, 40 universalFormatStyle: 1, 41 quote: 1, 42 code: 1, 43 sub: 1, 44 sup: 1, 45 insertSmile: 1 46 }; 47 } 48 49 BXEditorActions.prototype = 50 { 51 IsSupportedByBrowser: function(action) 52 { 53 // Following actions are supported but contain bugs in some browsers 54 var 55 isIe = BX.browser.IsIE() || BX.browser.IsIE10() || BX.browser.IsIE11(), 56 arBuggyActions = { 57 indent: isIe, 58 outdent: isIe, 59 formatBlock: isIe, 60 insertUnorderedList: BX.browser.IsIE() || BX.browser.IsOpera(), 61 insertOrderedList: BX.browser.IsIE() || BX.browser.IsOpera() 62 }, 63 arSupportedActions = { // Firefox throws some errors for queryCommandSupported... 64 insertHTML: BX.browser.IsFirefox() 65 }; 66 67 if (!arBuggyActions[action]) 68 { 69 // Firefox throws errors when invoking queryCommandSupported or queryCommandEnabled 70 try { 71 return this.document.queryCommandSupported(action); 72 } catch(e1) {} 73 74 try { 75 return this.document.queryCommandEnabled(action); 76 } catch(e2) { 77 return !!arSupportedActions[action]; 78 } 79 } 80 return false; 81 }, 82 83 IsSupported: function(action) 84 { 85 return !!this.actions[action]; 86 }, 87 88 IsContentAction: function(action) 89 { 90 return this.contentActionIndex[action]; 91 }, 92 93 Exec: function(action, value, bSilent) 94 { 95 var 96 _this = this, 97 oAction = this.actions[action], 98 func = oAction && oAction.exec, 99 isContentAction = this.IsContentAction(action), 100 result = null; 101 102 if (!bSilent) 103 { 104 this.editor.On("OnBeforeCommandExec", [isContentAction, action, oAction, value]); 105 } 106 107 if (isContentAction) 108 { 109 this.editor.Focus(false); 110 } 111 112 if (func) 113 { 114 result = func.apply(oAction, arguments); 115 } 116 else 117 { 118 try 119 { 120 result = this.document.execCommand(action, false, value); 121 } catch(e){} 122 } 123 124 if (isContentAction) 125 { 126 setTimeout(function(){_this.editor.Focus(false);}, 1); 127 } 128 129 if (!bSilent) 130 { 131 this.editor.On("OnAfterCommandExec", [isContentAction, action]); 132 } 133 134 return result; 135 }, 136 137 CheckState: function(action, value) 138 { 139 var 140 oAction = this.actions[action], 141 result = null; 142 143 if (oAction && oAction.state) 144 { 145 result = oAction.state.apply(oAction, arguments); 146 } 147 else 148 { 149 try 150 { 151 result = this.document.queryCommandState(action); 152 } 153 catch(e) 154 { 155 result = false;
156 } 157 } 158 return result; 159 }, 160 161 /** 162 * Get the current command's value 163 * 164 * @param {String} command The command string which to check (eg. "formatBlock") 165 * @return {String} The command value 166 * @example 167 * var currentBlockElement = commands.value("formatBlock"); 168 */ 169 GetValue: function(command) 170 { 171 var 172 obj = this.commands[command], 173 method = obj && obj.value; 174 175 if (method) 176 { 177 return method.call(obj, this.composer, command); 178 } 179 else 180 { 181 try { 182 // try/catch for buggy firefox 183 return this.document.queryCommandValue(command); 184 } catch(e) { 185 return null; 186 } 187 } 188 }, 189 190 GetActionList: function() 191 { 192 this.actions = { 193 changeView: this.GetChangeView(), 194 splitMode: this.GetChangeSplitMode(), 195 fullscreen: this.GetFullscreen(), 196 changeTemplate: this.GetChangeTemplate(), 197 removeFormat: this.GetRemoveFormat(), 198 199 // Format text 200 bold: this.GetBold(), 201 italic: this.GetItalic(), 202 underline: this.GetUnderline(), 203 strikeout: this.GetStrikeout(), 204 // Size 205 fontSize: this.GetFontSize(), 206 // Color 207 foreColor: this.GetForeColor(), 208 backgroundColor: this.GetBackgroundColor(), 209 // 210 formatInline: this.GetFormatInline(), 211 formatBlock: this.GetFormatBlock(), 212 // Insert 213 createLink: this.GetCreateLink(), 214 insertHTML: this.GetInsertHTML(), 215 insertImage: this.GetInsertImage(), 216 insertLineBreak: this.GetInsertLineBreak(), 217 insertTable: this.GetInsertTable(), 218 removeLink: this.GetRemoveLink(), 219 insertHr: this.GetInsertHr(), 220 // Lists 221 insertOrderedList: this.GetInsertList({bOrdered: true}), 222 insertUnorderedList: this.GetInsertList({bOrdered: false}), 223 align: this.GetAlign(), 224 indent: this.GetIndent(), 225 outdent: this.GetOutdent(), 226 227 formatStyle: this.GetFormatStyle(), 228 fontFamily: this.GetFontFamily(), 229 universalFormatStyle: this.GetUniversalFormatStyle(), 230 231 selectNode: this.GetSelectNode(), 232 doUndo: this.GetUndoRedo(true), 233 doRedo: this.GetUndoRedo(false), 234 235 sub: this.GetSubSup('sub'), 236 sup: this.GetSubSup('sup'), 237 quote: this.GetQuote(), 238 code: this.GetCode(), 239 insertSmile: this.GetInsertSmile(), 240 tableOperation: this.GetTableOperation(), 241 242 // Bbcodes actions 243 formatBbCode: this.GetFormatBbCode() 244 }; 245 246 this.editor.On("OnGetActionsList"); 247 248 return this.actions; 249 }, 250 251 GetChangeView: function() 252 { 253 var _this = this; 254 return { 255 exec: function() 256 { 257 var value = arguments[1]; 258 if ({'code': 1, 'wysiwyg': 1, 'split': 1}[value]) 259 _this.editor.SetView(value) 260 }, 261 262 state: function() { 263 return false; 264 }, 265 266 value: function() {} 267 }; 268 }, 269 270 GetChangeSplitMode: function() 271 { 272 var _this = this; 273 return { 274 exec: function() 275 { 276 _this.editor.SetSplitMode(arguments[1] == 1); 277 }, 278 279 state: function() { 280 return _this.editor.GetSplitMode(); 281 }, 282 283 value: function() {} 284 }; 285 }, 286 287 GetFullscreen: function() 288 { 289 var _this = this; 290 return { 291 exec: function() 292 { 293 _this.editor.Expand(); 294 }, 295 296 state: function() 297 { 298 return _this.editor.IsExpanded(); 299 }, 300 301 value: function() {} 302 }; 303 }, 304 305 /** 306 * formatInline scenarios for tag "B" (| = caret, |foo| = selected text) 307 * 308 * #1 caret in unformatted text: 309 * abcdefg| 310 * output: 311 * abcdefg<b>|</b> 312 * 313 * #2 unformatted text selected: 314 * abc|deg|h 315 * output: 316 * abc<b>|deg|</b>h 317 * 318 * #3 unformatted text selected across boundaries: 319 * ab|c <span>defg|h</span> 320 * output: 321 * ab<b>|c </b><span><b>defg</b>|h</span> 322 * 323 * #4 formatted text entirely selected 324 * <b>|abc|</b> 325 * output: 326 * |abc| 327 * 328 * #5 formatted text partially selected 329 * <b>ab|c|</b> 330 * output: 331 * <b>ab</b>|c| 332 * 333 * #6 formatted text selected across boundaries 334 * <span>ab|c</span> <b>de|fgh</b> 335 * output: 336 * <span>ab|c</span> de|<b>fgh</b> 337 */ 338 GetFormatInline: function() 339 { 340 var 341 _this = this, 342 TAG_ALIAS = { 343 strong: "b", 344 em: "i", 345 b: "strong", 346 i: "em" 347 }, 348 htmlApplier = {}; 349 350 function getKey(tagName, style, className) 351 { 352 var key = tagName + ":"; 353 if (className) 354 key += className; 355 if (style) 356 { 357 for (var i in style) 358 if (style.hasOwnProperty(i)) 359 key += i + '=' + style[i] + ';'; 360 } 361 return key; 362 } 363 364 function getStyler(tagName, style, className) 365 { 366 var key = getKey(tagName, style, className); 367 if (!htmlApplier[key]) 368 { 369 var alias = TAG_ALIAS[tagName]; 370 var tags = alias ? [tagName.toLowerCase(), alias.toLowerCase()] : [tagName.toLowerCase()]; 371 htmlApplier[key] = new _this.editor.HTMLStyler(_this.editor, tags, style, className, true); 372 } 373 return htmlApplier[key]; 374 } 375 376 return { 377 exec: function(command, value, tagName, arStyle, cssClass, params) 378 { 379 params = (!params || typeof params != 'object') ? {} : params; 380 _this.editor.iframeView.Focus(); 381 var range = _this.editor.selection.GetRange(); 382 if (!range) 383 { 384 return false;
385 } 386 387 var applier = getStyler(tagName, arStyle, cssClass); 388 389 if (params.bClear) 390 { 391 range = applier.UndoToRange(range, false); 392 } 393 else 394 { 395 range = applier.ToggleRange(range); 396 } 397 398 setTimeout(function() 399 { 400 _this.editor.selection.SetSelection(range); 401 402 // If we format text in the table - we could accidentally broke table, 403 // so now we should check and repair all possible tables (mantis: #66342) 404 var 405 table, i, j, k, cellNode, 406 tables = [], 407 bogusNodes = [], 408 nodes = range.getNodes([1]); 409 410 for (i = 0; i < nodes.length; i++) 411 { 412 if (BX.util.in_array(nodes[i].nodeName, ['TD', 'TR', 'TH'])) 413 { 414 table = BX.findParent(nodes[i], function(n) 415 { 416 return n.nodeName == "TABLE"; 417 }, _this.editor.GetIframeDoc().body); 418 419 if (table && !BX.util.in_array(table, tables)) 420 { 421 tables.push(table); 422 } 423 } 424 } 425 426 // Now we have list of tables. In most cases it will be one node. 427 for (i = 0; i < tables.length; i++) 428 { 429 table = tables[i]; 430 for (j = 0; j < table.rows.length; j++) 431 { 432 for (k = 0; k < table.rows[j].childNodes.length; k++) 433 { 434 cellNode = table.rows[j].childNodes[k]; 435 // If it's cell inside the row, it should at least be on of the table dedicated tag. If not - we will remove it to save html valid. 436 if (cellNode 437 && cellNode.nodeType == 1 438 && !BX.util.in_array(cellNode.nodeName, _this.editor.TABLE_TAGS)) 439 { 440 bogusNodes.push(cellNode); 441 } 442 } 443 } 444 } 445 446 // Clean invalid tags inside the table. 447 for (i = 0; i < bogusNodes.length; i++) 448 { 449 BX.cleanNode(bogusNodes[i], true); 450 } 451 452 var lastCreatedNode = _this.editor.selection.GetSelectedNode(); 453 if (lastCreatedNode && lastCreatedNode.nodeType == 1) 454 { 455 _this.editor.lastCreatedId = Math.round(Math.random() * 1000000); 456 lastCreatedNode.setAttribute('data-bx-last-created-id', _this.editor.lastCreatedId); 457 } 458 459 }, 10); 460 }, 461 462 state: function(action, value, tagName, arStyle, cssClass) 463 { 464 var 465 doc = _this.editor.GetIframeDoc(), 466 aliasTagName = TAG_ALIAS[tagName] || tagName; 467 468 // Check whether the document contains a node with the desired tagName 469 if ( 470 !_this.editor.util.DocumentHasTag(doc, tagName) 471 && 472 (tagName != aliasTagName && !_this.editor.util.DocumentHasTag(doc, aliasTagName)) 473 ) 474 { 475 return false; 476 } 477 478 var range = _this.editor.selection.GetRange(); 479 if (!range) 480 { 481 return false; 482 } 483 484 var applier = getStyler(tagName, arStyle, cssClass); 485 return applier.IsAppliedToRange(range, false); 486 }, 487 488 value: BX.DoNothing 489 }; 490 }, 491 492 /* 493 * TODO: 1. Clean useless spans when H1-H6 are created 494 * */ 495 GetFormatBlock: function() 496 { 497 var 498 _this = this, 499 DEFAULT_NODE_NAME = "DIV", 500 blockTags = _this.editor.GetBlockTags(), 501 nestedBlockTags = _this.editor.NESTED_BLOCK_TAGS; 502 503 /** 504 * Remove similiar classes (based on classRegExp) 505 * and add the desired class name 506 */ 507 function _addClass(element, className, classRegExp) 508 { 509 if (element.className) 510 { 511 _removeClass(element, classRegExp); 512 element.className += " " + className; 513 } 514 else 515 { 516 element.className = className; 517 } 518 } 519 520 function _removeClass(element, classRegExp) 521 { 522 element.className = element.className.replace(classRegExp, ""); 523 } 524 525 /** 526 * Adds line breaks before and after the given node if the previous and next siblings 527 * aren't already causing a visual line break (block element or <br>) 528 */ 529 function addBrBeforeAndAfter(node) 530 { 531 var 532 nextSibling = _this.editor.util.GetNextNotEmptySibling(node), 533 previousSibling = _this.editor.util.GetPreviousNotEmptySibling(node); 534 535 if (nextSibling && !isBrOrBlockElement(nextSibling)) 536 { 537 node.parentNode.insertBefore(_this.document.createElement("BR"), nextSibling); 538 } 539 540 if (previousSibling && !isBrOrBlockElement(previousSibling)) 541 { 542 node.parentNode.insertBefore(_this.document.createElement("BR"), node); 543 } 544 } 545 546 // Removes line breaks before and after the given node 547 function removeBrBeforeAndAfter(node) 548 { 549 var 550 nextSibling = _this.editor.util.GetNextNotEmptySibling(node), 551 previousSibling = _this.editor.util.GetPreviousNotEmptySibling(node); 552 553 if (nextSibling && nextSibling.nodeName === "BR") 554 { 555 nextSibling.parentNode.removeChild(nextSibling); 556 } 557 558 if (previousSibling && previousSibling.nodeName === "BR") 559 { 560 previousSibling.parentNode.removeChild(previousSibling); 561 } 562 } 563 564 function removeLastChildBr(node) 565 { 566 var lastChild = node.lastChild; 567 if (lastChild && lastChild.nodeName === "BR") 568 { 569 lastChild.parentNode.removeChild(lastChild); 570 } 571 } 572 573 function isBrOrBlockElement(element) 574 { 575 return element.nodeName === "BR" || _this.editor.util.IsBlockElement(element); 576 } 577 578 // Execute native query command 579 function execNativeCommand(command, nodeName, className, style) 580 { 581 if (className || style) 582 { 583// var eventListener = dom.observe(doc, "DOMNodeInserted", function(event) 584// { 585// var target = event.target, 586// displayStyle; 587// if (target.nodeType !== 1 /* element node */) 588// return; 589// 590// displayStyle = dom.getStyle("display").from(target); 591// if (displayStyle.substr(0, 6) !== "inline") 592// {
593// // Make sure that only block elements receive the given class 594// target.className += " " + className; 595// } 596// }); 597 } 598 _this.document.execCommand(command, false, nodeName); 599// if (eventListener) 600// eventListener.stop(); 601 } 602 603 function _hasClasses(element) 604 { 605 return !element.className || BX.util.trim(element.className) === ''; 606 } 607 608 function applyBlockElement(node, newNodeName, className, arStyle) 609 { 610 // Rename current block element to new block element and add class 611 if (newNodeName) 612 { 613 if (node.nodeName !== newNodeName) 614 { 615 node = _this.editor.util.RenameNode(node, newNodeName); 616 } 617 if (className) 618 { 619 node.className = className; 620 } 621 if (arStyle && arStyle.length > 0) 622 { 623 _this.editor.util.SetCss(node, arStyle); 624 } 625 } 626 else // Get rid of node 627 { 628 // Insert a line break afterwards and beforewards when there are siblings 629 // that are not of type line break or block element 630 addBrBeforeAndAfter(node); 631 _this.editor.util.ReplaceWithOwnChildren(node); 632 } 633 } 634 635 return { 636 exec: function(command, nodeName, className, arStyle, params) 637 { 638 params = params || {}; 639 nodeName = typeof nodeName === "string" ? nodeName.toUpperCase() : nodeName; 640 var 641 childBlockNodes, i, createdBlockNodes, 642 range = params.range || _this.editor.selection.GetRange(), 643 blockElement = nodeName ? _this.actions.formatBlock.state(command, nodeName, className, arStyle) : false, 644 selectedNode; 645 646 if (params.range) 647 _this.editor.selection.RestoreBookmark(); 648 649 if (blockElement) // Same block element 650 { 651 var wholeBlockSelected = range.startContainer == blockElement.firstChild && range.endContainer == blockElement.lastChild; 652 653 // Divs and blockquotes can be inside each other 654 if (nodeName && BX.util.in_array(nodeName, nestedBlockTags) && params.nestedBlocks !== false) 655 { 656 // create new div 657 blockElement = _this.document.createElement(nodeName || DEFAULT_NODE_NAME); 658 if (className) 659 { 660 blockElement.className = className; 661 } 662 663 // Select line with wrap 664 _this.editor.selection.Surround(blockElement); 665 _this.editor.selection.SelectNode(blockElement); 666 _this.editor.util.SetCss(blockElement, arStyle); 667 668 var nextBr = _this.editor.util.GetNextNotEmptySibling(blockElement); 669 if (nextBr && nextBr.nodeName == 'BR') 670 BX.remove(nextBr); 671 672 setTimeout(function() 673 { 674 if (blockElement) 675 { 676 _this.editor.selection.SelectNode(blockElement); 677 } 678 }, 50); 679 } 680 else if (params && params.splitBlock && !wholeBlockSelected) 681 { 682 var childBlock = _this.document.createElement(nodeName || DEFAULT_NODE_NAME); 683 684 _this.editor.selection.Surround(childBlock); 685 _this.editor.selection.SelectNode(childBlock); 686 687 if (className) 688 childBlock.className = className; 689 690 _this.editor.util.SetCss(childBlock, arStyle); 691 692 if (blockElement && blockElement.lastChild) 693 { 694 var _range = range.cloneRange(); 695 _range.setStartAfter(childBlock); 696 _range.setEndAfter(blockElement.lastChild); 697 698 var afterBlock = blockElement.cloneNode(false); 699 _this.editor.selection.SetSelection(_range); 700 _this.editor.selection.Surround(afterBlock); 701 702 var firstBr = _this.editor.selection._GetNonTextFirstChild(afterBlock); 703 if (firstBr && firstBr.nodeName == 'BR') 704 BX.remove(firstBr); 705 } 706 707 _this.editor.selection.SetSelection(range); 708 709 setTimeout(function() 710 { 711 if (childBlock) 712 { 713 _this.editor.selection.SelectNode(childBlock); 714 } 715 }, 50); 716 } 717 else 718 { 719 _this.editor.util.SetCss(blockElement, arStyle); 720 if (className) 721 { 722 blockElement.className = className; 723 724 // Bug workaround for bug with rendering in Firefox 725 if (BX.browser.IsFirefox()) 726 { 727 var tmpId = "bx-editor-temp-" + Math.round(Math.random() * 1000000); 728 blockElement.id = tmpId; 729 blockElement.parentNode.innerHTML = blockElement.parentNode.innerHTML; 730 blockElement = _this.editor.GetIframeElement(tmpId); 731 if (blockElement) 732 blockElement.removeAttribute("id"); 733 } 734 } 735 736 setTimeout(function() 737 { 738 if (blockElement) 739 { 740 _this.editor.selection.SelectNode(blockElement); 741 } 742 }, 50); 743 } 744 } 745 else 746 { 747 // Find other block element and rename it (<h2></h2> => <h1></h1>) 748 if (nodeName === null || BX.util.in_array(nodeName, blockTags)) 749 { 750 blockElement = false;
751 selectedNode = _this.editor.selection.GetSelectedNode(); 752 753 if (selectedNode) 754 { 755 if (selectedNode.nodeType == 1 && BX.util.in_array(selectedNode.nodeName, blockTags)) 756 { 757 blockElement = selectedNode; 758 } 759 else 760 { 761 blockElement = BX.findParent(selectedNode, function(n) 762 { 763 return BX.util.in_array(n.nodeName, blockTags); 764 }, _this.document.body); 765 } 766 } 767 else 768 { 769 var 770 commonAncestor = _this.editor.selection.GetCommonAncestorForRange(range); 771 772 if (commonAncestor && commonAncestor.nodeName !== 'BODY' && 773 BX.util.in_array(commonAncestor.nodeName, blockTags)) 774 { 775 blockElement = commonAncestor; 776 } 777 } 778 779 if (blockElement && !_this.actions.quote.checkNode(blockElement)) 780 { 781 _this.editor.selection.ExecuteAndRestoreSimple(function() 782 { 783 applyBlockElement(blockElement, nodeName, className, arStyle); 784 }); 785 return true; 786 } 787 } 788 789 blockElement = BX.create(nodeName || DEFAULT_NODE_NAME, {}, _this.document); 790 if (className) 791 { 792 blockElement.className = className; 793 } 794 _this.editor.util.SetCss(blockElement, arStyle); 795 796 if (range.collapsed || selectedNode && selectedNode.nodeType == 3 /* text node*/) 797 { 798 // Select node 799 _this.editor.selection.SelectNode(selectedNode); 800 } 801 else if (range.collapsed) 802 { 803 // Select line with wrap 804 _this.editor.selection.SelectLine(); 805 } 806 807 _this.editor.selection.Surround(blockElement, range); 808 removeBrBeforeAndAfter(blockElement); 809 removeLastChildBr(blockElement); 810 811 // Used in align action 812 if (params.leaveChilds) 813 { 814 return blockElement; 815 } 816 817 if (nodeName && !BX.util.in_array(nodeName, nestedBlockTags)) 818 { 819 range = _this.editor.selection.GetRange(); 820 createdBlockNodes = range.getNodes([1]); 821 822 // 1. Find all child "P" and remove them 823 if (nodeName == 'P') 824 { 825 childBlockNodes = blockElement.getElementsByTagName(nodeName); 826 // Clean empty bogus H1, H2, H3... 827 for (i = 0; i < createdBlockNodes.length; i++) 828 { 829 if (arStyle && _this.editor.util.CheckCss(createdBlockNodes[i], arStyle, false) && _this.editor.util.IsEmptyNode(createdBlockNodes[i], true, true)) 830 { 831 BX.remove(createdBlockNodes[i]); 832 } 833 } 834 835 while (childBlockNodes[0]) 836 { 837 addBrBeforeAndAfter(childBlockNodes[0]); 838 _this.editor.util.ReplaceWithOwnChildren(childBlockNodes[0]); 839 } 840 } 841 else if (nodeName.substr(0, 1) == 'H') 842 { 843 // Clean empty bogus H1, H2, H3... 844 for (i = 0; i < createdBlockNodes.length; i++) 845 { 846 if (createdBlockNodes[i].nodeName !== nodeName && _this.editor.util.IsEmptyNode(createdBlockNodes[i], true, true)) 847 { 848 BX.remove(createdBlockNodes[i]); 849 } 850 } 851 852 var childHeaders = BX.findChild(blockElement, function(n) 853 { 854 return BX.util.in_array(n.nodeName, blockTags) && n.nodeName.substr(0, 1) == 'H'; 855 }, true, true); 856 857 for (i = 0; i < childHeaders.length; i++) 858 { 859 addBrBeforeAndAfter(childHeaders[i]); 860 _this.editor.util.ReplaceWithOwnChildren(childHeaders[i]); 861 } 862 } 863 } 864 865 if (blockElement && params.bxTagParams && typeof params.bxTagParams == 'object') 866 { 867 _this.editor.SetBxTag(blockElement, params.bxTagParams); 868 } 869 870 if (blockElement && blockElement.parentNode) 871 { 872 var parent = blockElement.parentNode; 873 if (parent.nodeName == 'UL' || parent.nodeName == 'OL') 874 { 875 // 1. Clean empty LI before and after 876 var li = _this.editor.util.GetPreviousNotEmptySibling(blockElement); 877 if (_this.editor.util.IsEmptyLi(li)) 878 { 879 BX.remove(li); 880 } 881 li = _this.editor.util.GetNextNotEmptySibling(blockElement);
882 if (_this.editor.util.IsEmptyLi(li)) 883 { 884 BX.remove(li); 885 } 886 887 // 2. If parent list doesn't have other items - put it inside blockquote 888 if (!_this.editor.util.GetPreviousNotEmptySibling(blockElement) && !_this.editor.util.GetNextNotEmptySibling(blockElement)) 889 { 890 var blockElementNew = blockElement.cloneNode(false); 891 parent.parentNode.insertBefore(blockElementNew, parent); 892 _this.editor.util.ReplaceWithOwnChildren(blockElement); 893 blockElementNew.appendChild(parent); 894 } 895 } 896 897 if (blockElement.firstChild && blockElement.firstChild.nodeName == 'BLOCKQUOTE') 898 { 899 var prev = _this.editor.util.GetPreviousNotEmptySibling(blockElement); 900 if (prev && prev.nodeName == 'BLOCKQUOTE' && _this.editor.util.IsEmptyNode(prev)) 901 { 902 BX.remove(prev); 903 } 904 } 905 906 if ((blockElement.nodeName == 'BLOCKQUOTE' || blockElement.nodeName == 'PRE') && _this.editor.util.IsEmptyNode(blockElement)) 907 { 908 blockElement.innerHTML = ''; 909 var br = _this.document.createElement("br"); 910 blockElement.appendChild(br); 911 _this.editor.selection.SetAfter(br); 912 } 913 } 914 915 setTimeout(function() 916 { 917 if (blockElement) 918 { 919 _this.editor.selection.SelectNode(blockElement); 920 } 921 }, 10); 922 923 return true; 924 } 925 }, 926 927 state: function(command, nodeName, className, style) 928 { 929 nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName; 930 var 931 result = false, 932 selectedNode = _this.editor.selection.GetSelectedNode(); 933 934 if (selectedNode && selectedNode.nodeName) 935 { 936 if (selectedNode.nodeName != nodeName) 937 { 938 selectedNode = BX.findParent(selectedNode, function(n) 939 { 940 return n.nodeName == nodeName; 941 }, _this.document.body); 942 } 943 result = (selectedNode && selectedNode.tagName == nodeName) ? selectedNode : false; 944 } 945 else 946 { 947 var 948 range = _this.editor.selection.GetRange(), 949 commonAncestor = _this.editor.selection.GetCommonAncestorForRange(range); 950 951 if (commonAncestor.nodeName == nodeName) 952 { 953 result = commonAncestor; 954 } 955 } 956 return result; 957 }, 958 959 value: BX.DoNothing, 960 961 removeBrBeforeAndAfter: removeBrBeforeAndAfter, 962 addBrBeforeAndAfter: addBrBeforeAndAfter 963 }; 964 }, 965 966 GetRemoveFormat: function() 967 { 968 var 969 FORMAT_NODES_INLINE = { 970 "B": 1, 971 "STRONG": 1, 972 "I": 1, 973 "EM": 1, 974 "U": 1, 975 "DEL": 1, 976 "S": 1, 977 "STRIKE": 1, 978 "A": 1, 979 "SPAN" : 1, 980 "CODE" : 1, 981 "NOBR" : 1, 982 "Q" : 1, 983 "FONT" : 1, 984 "CENTER": 1, 985 "CITE": 1 986 }, 987 FORMAT_NODES_BLOCK = { 988 "H1": 1, 989 "H2": 1, 990 "H3": 1, 991 "H4": 1, 992 "H5": 1, 993 "H6": 1, 994 "DIV": 1, 995 "P": 1, 996 "LI": 1, 997 "UL" : 1, 998 "OL" : 1, 999 "MENU" : 1, 1000 "BLOCKQUOTE": 1, 1001 "PRE": 1 1002 }, 1003 _this = this; 1004 1005 function checkAndCleanNode(node, doc) 1006 { 1007 if (!node) 1008 return; 1009 var nodeName = node.nodeName; 1010 if (FORMAT_NODES_INLINE[nodeName]) 1011 { 1012 _this.editor.util.ReplaceWithOwnChildren(node); 1013 } 1014 else if (FORMAT_NODES_BLOCK[nodeName]) 1015 { 1016 _this.actions.formatBlock.addBrBeforeAndAfter(node); 1017 _this.editor.util.ReplaceWithOwnChildren(node); 1018 } 1019 else 1020 { 1021 node.removeAttribute("style"); 1022 node.removeAttribute("class"); 1023 node.removeAttribute("align"); 1024 1025 if (_this.editor.bbCode && node.nodeName == 'TABLE') 1026 { 1027 node.removeAttribute('align'); 1028 } 1029 } 1030 } 1031 1032 function checkTableNode(node) 1033 { 1034 return BX.findParent(node, function(n) 1035 { 1036 return n.nodeName == "TABLE"; 1037 }, _this.editor.GetIframeDoc().body); 1038 } 1039 1040 function getUnitaryParent(textNode) 1041 { 1042 var 1043 prevSibling, nextSibling, 1044 parent = textNode.parentNode; 1045 1046 while (parent && parent.nodeName !== 'BODY') 1047 { 1048 prevSibling = parent.previousSibling && !_this.editor.util.IsEmptyNode(parent.previousSibling); 1049 nextSibling = parent.nextSibling && !_this.editor.util.IsEmptyNode(parent.nextSibling); 1050 1051 if (prevSibling || nextSibling || parent.parentNode.nodeName == 'BODY') 1052 { 1053 break; 1054 } 1055 parent = parent.parentNode; 1056 } 1057 return parent; 1058 } 1059 1060 function checkParentList(node) 1061 { 1062 var 1063 doc = _this.editor.GetIframeDoc(), 1064 list = BX.findParent(node, function(n) 1065 { 1066 return n.nodeName == "UL" || n.nodeName == "OL" || n.nodeName == "MENU"; 1067 }, doc.body); 1068 1069 if (list) 1070 { 1071 var 1072 i, child, 1073 listBefore = list.cloneNode(false), 1074 listAfter = list.cloneNode(false), 1075 before = true; 1076 1077 BX.cleanNode(listBefore); 1078 BX.cleanNode(listAfter); 1079 1080 for (i = 0; i < list.childNodes.length; i++) 1081 { 1082 child = list.childNodes[i]; 1083 if (child == node) 1084 { 1085 before = false;
1086 } 1087 1088 if (child.nodeName == 'LI') 1089 { 1090 if (!_this.editor.util.IsEmptyNode(child, true, true)) 1091 { 1092 (before ? listBefore : listAfter).appendChild(child.cloneNode(true)); 1093 } 1094 } 1095 } 1096 1097 if (listBefore.childNodes.length > 0) 1098 { 1099 list.parentNode.insertBefore(listBefore, list); 1100 } 1101 list.parentNode.insertBefore(node, list); 1102 if (listAfter.childNodes.length > 0) 1103 { 1104 list.parentNode.insertBefore(listAfter, list); 1105 } 1106 BX.remove(list); 1107 return true; 1108 } 1109 return false; 1110 } 1111 1112 function cleanNodes(nodes, doc) 1113 { 1114 if (nodes && nodes.length > 0) 1115 { 1116 var i, len, sorted = []; 1117 for (i = 0, len = nodes.length; i < len; i++) 1118 { 1119 if (!_this.editor.util.CheckSurrogateNode(nodes[i])) 1120 { 1121 sorted.push({node: nodes[i], nesting: _this.editor.util.GetNodeDomOffset(nodes[i])}); 1122 } 1123 } 1124 sorted = sorted.sort(function(a, b){return b.nesting - a.nesting}); 1125 for (i = 0, len = sorted.length; i < len; i++) 1126 { 1127 checkAndCleanNode(sorted[i].node, doc); 1128 } 1129 } 1130 } 1131 1132 function _selectAndGetNodes(node) 1133 { 1134 var 1135 i, found = false, 1136 range = _this.editor.selection.SelectNode(node), 1137 nodes = range.getNodes([1]); 1138 1139 if (node.nodeType == 1) 1140 { 1141 for (i = 0; i < nodes.length; i++) 1142 { 1143 if (nodes[i] == node) 1144 { 1145 found = true; 1146 break; 1147 } 1148 } 1149 if (!found) 1150 { 1151 nodes = [node].concat(nodes); 1152 } 1153 } 1154 1155 if (!nodes || typeof nodes != 'object' || nodes.length == 0) 1156 { 1157 nodes = [node]; 1158 } 1159 return nodes; 1160 } 1161 1162 function getNodesFromTo(nodeStart, nodeEnd) 1163 { 1164 var list = []; 1165 if (nodeStart && (!nodeEnd || nodeStart == nodeEnd)) 1166 { 1167 list.push(nodeStart); 1168 } 1169 else if (!nodeStart && nodeEnd) 1170 { 1171 list.push(nodeEnd); 1172 } 1173 1174 return list; 1175 } 1176 1177 1178 return { 1179 exec: function(action, value) 1180 { 1181 var range = _this.editor.selection.GetRange(); 1182 1183 if (!range || _this.editor.iframeView.IsEmpty()) 1184 return; 1185 1186 var 1187 bSurround = true, 1188 i, 1189 textNodes, textNode, node, tmpNode, 1190 nodes = range.getNodes([1]), 1191 doc = _this.editor.GetIframeDoc(); 1192 1193 // Range is collapsed or text node is selected 1194 if (nodes.length == 0) 1195 { 1196 textNodes = range.getNodes([3]); 1197 1198 if (textNodes && textNodes.length == 1) 1199 { 1200 textNode = textNodes[0]; 1201 } 1202 1203 if (!textNode && range.startContainer == range.endContainer) 1204 { 1205 if (range.startContainer.nodeType == 3) 1206 { 1207 textNode = range.startContainer; 1208 } 1209 else 1210 { 1211 bSurround = false; 1212 nodes = _selectAndGetNodes(range.startContainer); 1213 } 1214 } 1215 1216 if (textNode && nodes.length == 0) 1217 { 1218 node = getUnitaryParent(textNode); 1219 if (node && (node.nodeName != 'BODY' || range.collapsed)) 1220 { 1221 bSurround = false; 1222 nodes = _selectAndGetNodes(node); 1223 } 1224 } 1225 } 1226 else 1227 { 1228 var 1229 updateSel = false, 1230 clearRanges = [], 1231 startTableCheck = checkTableNode(range.startContainer), 1232 endTableCheck = checkTableNode(range.endContainer); 1233 1234 if (startTableCheck) 1235 { 1236 clearRanges.push( 1237 { 1238 startContainer: range.startContainer, 1239 startOffset: range.startOffset, 1240 end: startTableCheck 1241 } 1242 ); 1243 1244 range.setStartAfter(startTableCheck); 1245 updateSel = true; 1246 } 1247 1248 if (endTableCheck) 1249 { 1250 updateSel = true; 1251 clearRanges.push( 1252 { 1253 start: endTableCheck, 1254 endContainer: range.endContainer, 1255 endOffset: range.endOffset 1256 } 1257 ); 1258 range.setEndBefore(endTableCheck); 1259 } 1260 1261 1262 var 1263 startList = _this.editor.util.FindParentEx(range.startContainer, function(n){return n.nodeName == "UL" || n.nodeName == "OL" || n.nodeName == "MENU";}, doc.body), 1264 endList = _this.editor.util.FindParentEx(range.endContainer, function(n){return n.nodeName == "UL" || n.nodeName == "OL" || n.nodeName == "MENU";}, doc.body); 1265 //listNodes = getNodesFromTo(startList, endList); 1266 1267 if (startList) 1268 { 1269 range.setStartBefore(startList); 1270 if (!endList) 1271 range.setEndAfter(startList); 1272 updateSel = true; 1273 } 1274 1275 if (endList) 1276 { 1277 updateSel = true; 1278 range.setEndAfter(endList); 1279 if (!startList) 1280 range.setStartBefore(endList); 1281 } 1282 1283 if (updateSel) 1284 { 1285 _this.editor.selection.SetSelection(range); 1286 nodes = range.getNodes([1]); 1287 } 1288 } 1289 1290 if (bSurround) 1291 { 1292 tmpNode = doc.createElement("span"); 1293 _this.editor.selection.Surround(tmpNode, range); 1294 nodes = _selectAndGetNodes(tmpNode); 1295 } 1296 1297 if (nodes && nodes.length > 0) 1298 { 1299 _this.editor.selection.ExecuteAndRestoreSimple(function() 1300 { 1301 cleanNodes(nodes, doc); 1302 }); 1303 } 1304 1305 if (clearRanges && clearRanges.length > 0) 1306 { 1307 var 1308 _range = range.cloneRange(); 1309 1310 for (i = 0; i < clearRanges.length; i++) 1311 { 1312 if (clearRanges[i].start) 1313 { 1314 _range.setStartBefore(clearRanges[i].start); 1315 } 1316 else 1317 { 1318 _range.setStart(clearRanges[i].startContainer, clearRanges[i].startOffset); 1319 } 1320 if (clearRanges[i].end) 1321 { 1322 _range.setEndAfter(clearRanges[i].end); 1323 } 1324 else 1325 { 1326 _range.setEnd(clearRanges[i].endContainer, clearRanges[i].endOffset); 1327 } 1328 _this.editor.selection.SetSelection(_range); 1329 cleanNodes(_range.getNodes([1]), doc); 1330 } 1331 1332 _this.editor.selection.SetSelection(range); 1333 } 1334 1335 if (bSurround && tmpNode && tmpNode.parentNode) 1336 { 1337 if (checkParentList(tmpNode)) 1338 { 1339 _this.editor.selection.SelectNode(tmpNode); 1340 } 1341 1342 _this.editor.selection.ExecuteAndRestoreSimple(function() 1343 { 1344 _this.editor.util.ReplaceWithOwnChildren(tmpNode); 1345 }); 1346 } 1347 1348 if (!_this.editor.iframeView.IsEmpty(true)) 1349 { 1350 _this.actions.formatBlock.exec('formatBlock', null); 1351 } 1352 }, 1353 state: BX.DoNothing, 1354 value: BX.DoNothing 1355 }; 1356 }, 1357 1358 GetBold: function() 1359 { 1360 var _this = this; 1361 return { 1362 exec: function(action, value) 1363 { 1364 // Iframe 1365 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 1366 { 1367 return _this.actions.formatInline.exec(action, value, "b"); 1368 } 1369 else // BBCode mode 1370 {
1371 return _this.actions.formatBbCode.exec(action, {tag: 'B'}); 1372 } 1373 }, 1374 state: function(action, value) 1375 { 1376 return _this.actions.formatInline.state(action, value, "b"); 1377 }, 1378 value: BX.DoNothing 1379 }; 1380 }, 1381 1382 GetItalic: function() 1383 { 1384 var _this = this; 1385 return { 1386 exec: function(action, value) 1387 { 1388 // Iframe 1389 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 1390 { 1391 return _this.actions.formatInline.exec(action, value, "i"); 1392 } 1393 else 1394 { 1395 return _this.actions.formatBbCode.exec(action, {tag: 'I'}); 1396 } 1397 }, 1398 state: function(action, value) 1399 { 1400 return _this.actions.formatInline.state(action, value, "i"); 1401 }, 1402 value: BX.DoNothing 1403 }; 1404 }, 1405 1406 GetUnderline: function() 1407 { 1408 var _this = this; 1409 return { 1410 exec: function(action, value) 1411 { 1412 // Iframe 1413 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 1414 { 1415 return _this.actions.formatInline.exec(action, value, "u"); 1416 } 1417 else 1418 { 1419 return _this.actions.formatBbCode.exec(action, {tag: 'U'}); 1420 } 1421 }, 1422 state: function(action, value) 1423 { 1424 return _this.actions.formatInline.state(action, value, "u"); 1425 }, 1426 value: BX.DoNothing 1427 }; 1428 }, 1429 1430 GetStrikeout: function() 1431 { 1432 var _this = this; 1433 return { 1434 exec: function(action, value) 1435 { 1436 // Iframe 1437 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 1438 { 1439 return _this.actions.formatInline.exec(action, value, "del"); 1440 } 1441 else 1442 { 1443 return _this.actions.formatBbCode.exec(action, {tag: 'S'}); 1444 } 1445 }, 1446 state: function(action, value) 1447 { 1448 return _this.actions.formatInline.state(action, value, "del"); 1449 }, 1450 value: BX.DoNothing 1451 }; 1452 }, 1453 1454 GetFontSize: function() 1455 { 1456 var _this = this; 1457 return { 1458 exec: function(action, value) 1459 { 1460 var res; 1461 // Iframe 1462 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 1463 { 1464 if (value > 0) // Format 1465 res = _this.actions.formatInline.exec(action, value, "span", {fontSize: value + 'pt'}); 1466 else // Clear font-size format 1467 res = _this.actions.formatInline.exec(action, value, "span", {fontSize: null}, null, {bClear: true}); 1468 } 1469 else // textarea + bbcode 1470 { 1471 res = _this.actions.formatBbCode.exec(action, {tag: 'SIZE', value: value + 'pt'}); 1472 } 1473 return res; 1474 }, 1475 1476 state: function(action, value) 1477 { 1478 return _this.actions.formatInline.state(action, value, "span", {fontSize: value + 'pt'}); 1479 }, 1480 1481 value: BX.DoNothing 1482 }; 1483 }, 1484 1485 GetForeColor: function() 1486 { 1487 var _this = this; 1488 1489 function checkListItemColor() 1490 { 1491 var 1492 doc = _this.editor.GetIframeDoc(), 1493 i, item, res = [], 1494 range = _this.editor.selection.GetRange(); 1495 1496 if (range) 1497 { 1498 var nodes = range.getNodes([1]); 1499 1500 if (nodes.length == 0 && range.startContainer == range.endContainer && range.startContainer.nodeName != 'BODY') 1501 { 1502 nodes = [range.startContainer]; 1503 } 1504 1505 for (i = 0; i < nodes.length; i++) 1506 { 1507 item = BX.findParent(nodes[i], function(n){ 1508 return n.nodeName == 'LI' && n.style && n.style.color; 1509 }, doc.body); 1510 1511 if (item) 1512 { 1513 res.push(item); 1514 } 1515 } 1516 } 1517 1518 return res.length === 0 ? false : res; 1519 } 1520 1521 function checkAndApplyColorListItems(value) 1522 { 1523 var 1524 doc = _this.editor.GetIframeDoc(), 1525 node = _this.editor.selection.GetSelectedNode(), 1526 i, j, spans, range, span, li, 1527 nodes; 1528 1529 if (node && (node.nodeType === 3 || node.nodeName == 'SPAN')) 1530 { 1531 span = node.nodeName == 'SPAN' ? node : BX.findParent(node, {tag: 'span'}, doc.body); 1532 if (span && span.style.color) 1533 { 1534 li = BX.findParent(span, {tag: 'li'}, doc.body); 1535 if (li) 1536 { 1537 if (li.childNodes.length == 1 && li.firstChild == span) 1538 { 1539 _this.editor.selection.ExecuteAndRestoreSimple(function() 1540 { 1541 li.style.color = value; 1542 span.style.color = ''; 1543 if (BX.util.trim(span.style.cssText) == '') 1544 { 1545 _this.editor.util.ReplaceWithOwnChildren(span); 1546 } 1547 }); 1548 } 1549 } 1550 } 1551 } 1552 else 1553 { 1554 if (!node) 1555 { 1556 range = _this.editor.selection.GetRange(); 1557 nodes = range.getNodes([1]); 1558 } 1559 else 1560 { 1561 nodes = [node]; 1562 } 1563 1564 for (i = 0; i < nodes.length; i++) 1565 { 1566 if (nodes[i] && nodes[i].nodeName == "LI" && !_this.editor.bbCode) 1567 { 1568 // 1. Add color to LI 1569 nodes[i].style.color = value; 1570 // Find and clear all spans created by action 1571 spans = BX.findChild(nodes[i], function(n) 1572 { 1573 return n.nodeName == "SPAN"; 1574 }, true, true); 1575 1576 _this.editor.selection.ExecuteAndRestoreSimple(function() 1577 { 1578 for (j = 0; j < spans.length; j++) 1579 { 1580 if (spans[j] && spans[j].parentNode && 1581 spans[j].parentNode.parentNode && 1582 spans[j].parentNode.parentNode.parentNode) 1583 { 1584 try{ 1585 spans[j].style.color = ''; 1586 if (BX.util.trim(spans[j].style.cssText) == '') 1587 { 1588 _this.editor.util.ReplaceWithOwnChildren(spans[j]); 1589 } 1590 } 1591 catch(e) 1592 {} 1593 } 1594 } 1595 }); 1596 } 1597 } 1598 } 1599 } 1600 1601 return { 1602 exec: function(action, value) 1603 { 1604 var res; 1605 1606 // Iframe 1607 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 1608 { 1609 if (value == '') 1610 { 1611 res = _this.actions.formatInline.exec(action, value, "span", {color: null}, null, {bClear: true}); 1612 } 1613 else 1614 { 1615 res = _this.actions.formatInline.exec(action, value, "span", {color: value}); 1616 checkAndApplyColorListItems(value); 1617 } 1618 } 1619 else if (value) // textarea + bbcode 1620 {
1621 res = _this.actions.formatBbCode.exec(action, {tag: 'COLOR', value: value}); 1622 } 1623 1624 return res; 1625 }, 1626 1627 state: function(action, value) 1628 { 1629 var state = _this.actions.formatInline.state(action, value, "span", {color: value}); 1630 if (!state) 1631 { 1632 state = checkListItemColor(); 1633 } 1634 1635 return state; 1636 }, 1637 1638 value: BX.DoNothing 1639 }; 1640 }, 1641 1642 GetBackgroundColor: function() 1643 { 1644 var _this = this; 1645 1646 return { 1647 exec: function(action, value) 1648 { 1649 var res; 1650 if (value == '') 1651 { 1652 res = _this.actions.formatInline.exec(action, value, "span", {backgroundColor: null}, null, {bClear: true}); 1653 } 1654 else 1655 { 1656 res = _this.actions.formatInline.exec(action, value, "span", {backgroundColor: value}); 1657 } 1658 return res; 1659 }, 1660 1661 state: function(action, value) 1662 { 1663 return _this.actions.formatInline.state(action, value, "span", {backgroundColor: value}); 1664 }, 1665 1666 value: BX.DoNothing 1667 }; 1668 }, 1669 1670 GetCreateLink: function() 1671 { 1672 var 1673 ATTRIBUTES = ['title', 'id', 'name', 'target', 'rel'], 1674 _this = this; 1675 1676 return { 1677 exec: function(action, value) 1678 { 1679 // Only for bbCode == true 1680 if (_this.editor.bbCode && _this.editor.synchro.IsFocusedOnTextarea()) 1681 { 1682 _this.editor.textareaView.Focus(); 1683 var linkHtml = "[URL=" + _this.editor.util.spaceUrlEncode(value.href) + "]" + (value.text || value.href) + "[/URL]"; 1684 _this.editor.textareaView.WrapWith(false, false, linkHtml); 1685 } 1686 else 1687 { 1688 _this.editor.iframeView.Focus(); 1689 1690 var 1691 nodeToSetCarret, 1692 params = (value && typeof(value) === "object") ? value : {href: value}, 1693 i, link, linksCount = 0, lastLink, 1694 links; 1695 1696 function applyAttributes(link, params) 1697 { 1698 var attr; 1699 link.removeAttribute("class"); 1700 link.removeAttribute("target"); 1701 1702 for (attr in params) 1703 { 1704 if (params.hasOwnProperty(attr) && BX.util.in_array(attr, ATTRIBUTES)) 1705 { 1706 if (params[attr] == '' || params[attr] == undefined) 1707 { 1708 link.removeAttribute(attr); 1709 } 1710 else 1711 { 1712 link.setAttribute(attr, params[attr]); 1713 } 1714 } 1715 } 1716 if (params.className) 1717 link.className = params.className; 1718 link.href = _this.editor.util.spaceUrlEncode(params.href) || ''; 1719 1720 if (params.noindex) 1721 { 1722 link.setAttribute("data-bx-noindex", "Y"); 1723 } 1724 else 1725 { 1726 link.removeAttribute("data-bx-noindex"); 1727 } 1728 } 1729 1730 links = value.node ? [value.node] : _this.actions.formatInline.state(action, value, "a"); 1731 if (links) 1732 { 1733 // Selection contains links 1734 for (i = 0; i < links.length; i++) 1735 { 1736 link = links[i]; 1737 if (link) 1738 { 1739 applyAttributes(link, params); 1740 lastLink = link; 1741 linksCount++; 1742 } 1743 } 1744 1745 if (linksCount === 1 && lastLink && (lastLink.querySelector && !lastLink.querySelector("*")) && params.text != '') 1746 { 1747 _this.editor.util.SetTextContent(lastLink, params.text); 1748 } 1749 nodeToSetCarret = lastLink; 1750 1751 if (nodeToSetCarret) 1752 _this.editor.selection.SetAfter(nodeToSetCarret); 1753 1754 setTimeout(function() 1755 { 1756 if (nodeToSetCarret) 1757 _this.editor.selection.SetAfter(nodeToSetCarret); 1758 }, 10); 1759 } 1760 else 1761 { 1762 1763 var 1764 tmpClass = "_bx-editor-temp-" + Math.round(Math.random() * 1000000), 1765 invisText, 1766 isEmpty, 1767 textContent; 1768 1769 // Create LINKS 1770 _this.actions.formatInline.exec(action, value, "A", false, tmpClass); 1771 1772 if (_this.document.querySelectorAll) 1773 { 1774 links = _this.document.querySelectorAll("A." + tmpClass); 1775 } 1776 else 1777 { 1778 links = []; 1779 } 1780 1781 for (i = 0; i < links.length; i++) 1782 { 1783 link = links[i]; 1784 if (link) 1785 { 1786 applyAttributes(link, params); 1787 } 1788 } 1789 1790 nodeToSetCarret = link; // last link 1791 1792 if (links.length === 1) 1793 { 1794 textContent = _this.editor.util.GetTextContent(link); 1795 isEmpty = textContent === "" || textContent === _this.editor.INVISIBLE_SPACE; 1796 1797 if (textContent != params.text) 1798 { 1799 _this.editor.util.SetTextContent(link, params.text || params.href); 1800 } 1801 1802 if (link.querySelector && !link.querySelector("*") && isEmpty) // Link is empty 1803 { 1804 _this.editor.util.SetTextContent(link, params.text || params.href); 1805 } 1806 } 1807 1808 if (link) 1809 { 1810 if (link.nextSibling && link.nextSibling.nodeType == 3 && _this.editor.util.IsEmptyNode(link.nextSibling)) 1811 { 1812 invisText = link.nextSibling; 1813 } 1814 else 1815 { 1816 invisText = _this.editor.util.GetInvisibleTextNode(); 1817 } 1818 _this.editor.util.InsertAfter(invisText, link); 1819 nodeToSetCarret = invisText; 1820 } 1821 1822 if (nodeToSetCarret) 1823 _this.editor.selection.SetAfter(nodeToSetCarret); 1824 1825 setTimeout(function() 1826 { 1827 if (nodeToSetCarret) 1828 _this.editor.selection.SetAfter(nodeToSetCarret); 1829 }, 10); 1830 } 1831 } 1832 }, 1833 1834 state: function(action, value) 1835 { 1836 return _this.actions.formatInline.state(action, value, "a"); 1837 }, 1838 1839 value: BX.DoNothing 1840 }; 1841 }, 1842 1843 GetRemoveLink: function() 1844 { 1845 var _this = this; 1846 return { 1847 exec: function(action, value) 1848 { 1849 _this.editor.iframeView.Focus(); 1850 1851 var 1852 i, link, links; 1853 1854 if (value && typeof value == 'object') 1855 { 1856 links = value; 1857 } 1858 else 1859 { 1860 links = _this.actions.formatInline.state(action, value, "a"); 1861 } 1862 1863 if (links) 1864 { 1865 // Selection contains links 1866 _this.editor.selection.ExecuteAndRestoreSimple(function() 1867 { 1868 for (i = 0; i < links.length; i++) 1869 { 1870 link = links[i]; 1871 if (link) 1872 { 1873 _this.editor.util.ReplaceWithOwnChildren(links[i]); 1874 } 1875 } 1876 }); 1877 } 1878 }, 1879 1880 state: function(action, value) 1881 { 1882 //return _this.actions.formatInline.state(action, value, "a"); 1883 }, 1884 value: BX.DoNothing 1885 }; 1886 }, 1887 1888 GetInsertHTML: function() 1889 { 1890 var _this = this; 1891 1892 return { 1893 exec: function(action, html) 1894 { 1895 _this.editor.iframeView.Focus(); 1896 1897 if (_this.IsSupportedByBrowser(action)) 1898 _this.document.execCommand(action, false, html); 1899 else 1900 _this.editor.selection.InsertHTML(html); 1901 }, 1902 1903 state: function() 1904 { 1905 return false;
1906 }, 1907 1908 value: BX.DoNothing 1909 }; 1910 }, 1911 1912 GetInsertImage: function() 1913 { 1914 var 1915 ATTRIBUTES = ['title', 'alt', 'width', 'height', 'align'], 1916 _this = this; 1917 1918 return { 1919 exec: function(action, value) 1920 { 1921 if (value.src == '') 1922 return; 1923 1924 value.src = _this.editor.util.spaceUrlEncode(value.src); 1925 1926 // Only for bbCode == true 1927 if (_this.editor.bbCode && _this.editor.synchro.IsFocusedOnTextarea()) 1928 { 1929 _this.editor.textareaView.Focus(); 1930 var size = ''; 1931 if (value.width) 1932 size += ' WIDTH=' + parseInt(value.width); 1933 if (value.height) 1934 size += ' HEIGHT=' + parseInt(value.height); 1935 var imgHtml = "[IMG" + size + "]" + value.src + "[/IMG]"; 1936 _this.editor.textareaView.WrapWith(false, false, imgHtml); 1937 return; 1938 } 1939 1940 _this.editor.iframeView.Focus(); 1941 var 1942 params = (value && typeof(value) === "object") ? value : {src: value}, 1943 image = value.image || _this.actions.insertImage.state(action, value), 1944 invisText, sibl; 1945 1946 function applyAttributes(image, params) 1947 { 1948 var attr, appAttr; 1949 image.removeAttribute("class"); 1950 image.setAttribute('data-bx-orig-src', params.src || ''); 1951 1952 for (attr in params) 1953 { 1954 if (params.hasOwnProperty(attr) && BX.util.in_array(attr, ATTRIBUTES)) 1955 { 1956 if (params[attr] == '' || params[attr] == undefined) 1957 { 1958 image.removeAttribute(attr); 1959 } 1960 else 1961 { 1962 appAttr = image.getAttribute('data-bx-app-ex-' + attr); 1963 if (!appAttr || _this.editor.phpParser.AdvancedPhpGetFragmentByCode(appAttr, true) != params[attr]) 1964 { 1965 image.setAttribute(attr, params[attr]); 1966 if (appAttr) 1967 { 1968 image.removeAttribute('data-bx-app-ex-' + attr); 1969 } 1970 } 1971 } 1972 } 1973 } 1974 1975 if (params.className) 1976 { 1977 image.className = params.className; 1978 } 1979 1980 appAttr = image.getAttribute('data-bx-app-ex-src'); 1981 if (!appAttr || _this.editor.phpParser.AdvancedPhpGetFragmentByCode(appAttr, true) != params.src) 1982 { 1983 image.src = params.src || ''; 1984 if (appAttr) 1985 { 1986 image.removeAttribute('data-bx-app-ex-src'); 1987 } 1988 } 1989 1990 } 1991 1992 if (!image) 1993 { 1994 image = _this.document.createElement('IMG'); 1995 applyAttributes(image, params); 1996 _this.editor.selection.InsertNode(image); 1997 } 1998 else 1999 { 2000 applyAttributes(image, params); 2001 } 2002 2003 var 2004 nodeToSetCarret = image, 2005 parentLink = (image.parentNode && image.parentNode.nodeName == 'A') ? image.parentNode : null; 2006 2007 if (params.link) 2008 { 2009 if (parentLink) 2010 { 2011 // Just change url 2012 parentLink.href = params.link; 2013 } 2014 else 2015 { 2016 // Add surrounding link 2017 parentLink = _this.document.createElement('A'); 2018 parentLink.href = params.link; 2019 image.parentNode.insertBefore(parentLink, image); 2020 parentLink.appendChild(image); 2021 } 2022 nodeToSetCarret = parentLink; 2023 } 2024 else if (parentLink) 2025 { 2026 // Remove parent link 2027 _this.editor.util.ReplaceWithOwnChildren(parentLink); 2028 } 2029 2030 // For IE it's impssible to set the caret after an <img> if it's the lastChild in the document 2031 if (BX.browser.IsIE()) 2032 { 2033 _this.editor.selection.SetAfter(image); 2034 sibl = image.nextSibling; 2035 if (sibl && sibl.nodeType == 3 && _this.editor.util.IsEmptyNode(sibl)) 2036 invisText = sibl; 2037 else 2038 invisText = _this.editor.util.GetInvisibleTextNode(); 2039 _this.editor.selection.InsertNode(invisText); 2040 nodeToSetCarret = invisText; 2041 } 2042 2043 _this.editor.selection.SetAfter(nodeToSetCarret); 2044 _this.editor.util.Refresh(); 2045 }, 2046 2047 state: function() 2048 { 2049 var 2050 selectedNode, 2051 text, 2052 selectedImg; 2053 2054 if (!_this.editor.util.DocumentHasTag(_this.document, 'IMG')) 2055 return false; 2056 2057 selectedNode = _this.editor.selection.GetSelectedNode(); 2058 if (!selectedNode) 2059 return false; 2060 2061 if (selectedNode.nodeName === 'IMG') 2062 return selectedNode; 2063 2064 if (selectedNode.nodeType !== 1 /* element node */) 2065 return false;
2066 2067 text = BX.util.trim(_this.editor.selection.GetText()); 2068 if (text && text != _this.editor.INVISIBLE_SPACE) 2069 return false; 2070 2071 selectedImg = _this.editor.selection.GetNodes(1, function(node) 2072 { 2073 return node.nodeName === "IMG"; 2074 }); 2075 2076 // Works only for one image 2077 if (selectedImg.length !== 1) 2078 return false; 2079 2080 return selectedImg[0]; 2081 }, 2082 2083 value: BX.DoNothing 2084// value: function(composer) { 2085// var image = this.state(composer); 2086// return image && image.src; 2087// } 2088 }; 2089 }, 2090 2091 GetInsertLineBreak: function() 2092 { 2093 var 2094 _this = this, 2095 br = "<br>" + (BX.browser.IsOpera() ? " " : ""); 2096 2097 return { 2098 exec: function(action) 2099 { 2100 if (_this.IsSupportedByBrowser(action)) 2101 { 2102 _this.document.execCommand(action, false, null); 2103 } 2104 else 2105 { 2106 _this.actions.insertHTML.exec("insertHTML", br); 2107 } 2108 2109 if (BX.browser.IsChrome() || BX.browser.IsSafari() || BX.browser.IsIE10()) 2110 { 2111 _this.editor.selection.ScrollIntoView(); 2112 } 2113 }, 2114 2115 state: BX.DoNothing, 2116 value: BX.DoNothing 2117 }; 2118 }, 2119 2120 GetInsertHr: function() 2121 { 2122 var 2123 _this = this, 2124 html = "<hr>" + (BX.browser.IsOpera() ? " " : ""); 2125 2126 return { 2127 exec: function(action) 2128 { 2129 _this.actions.insertHTML.exec("insertHTML", html); 2130 if (BX.browser.IsChrome() || BX.browser.IsSafari() || BX.browser.IsIE10()) 2131 { 2132 _this.editor.selection.ScrollIntoView(); 2133 } 2134 }, 2135 2136 state: BX.DoNothing, 2137 value: BX.DoNothing 2138 }; 2139 }, 2140 2141 GetInsertTable: function() 2142 { 2143 var 2144 _this = this, 2145 ATTRIBUTES = ['title', 'id', 'border', 'cellSpacing', 'cellPadding', 'align']; 2146 2147 function replaceTdByTh(td) 2148 { 2149 var 2150 //i, attribute, 2151 th = _this.document.createElement('TH'); 2152 2153 while (td.firstChild) 2154 th.appendChild(td.firstChild); 2155 2156 //for (i = 0; i < td.attributes.length; i++) 2157 //{ 2158 // attribute = td.attributes[i]; 2159 // th.setAttribute(td.getAttribute()); 2160 //} 2161 _this.editor.util.ReplaceNode(td, th); 2162 } 2163 2164 function replaceThByTd(th) 2165 { 2166 var 2167 td = _this.document.createElement('TD'); 2168 2169 while (th.firstChild) 2170 td.appendChild(th.firstChild); 2171 2172 _this.editor.util.ReplaceNode(th, td); 2173 } 2174 2175 function applyAttributes(table, params) 2176 { 2177 var attr; 2178 table.removeAttribute("class"); 2179 2180 for (attr in params) 2181 { 2182 if (params.hasOwnProperty(attr) && BX.util.in_array(attr, ATTRIBUTES)) 2183 { 2184 if (params[attr] === '' || params[attr] == undefined) 2185 { 2186 table.removeAttribute(attr); 2187 } 2188 else 2189 { 2190 table.setAttribute(attr, params[attr]); 2191 } 2192 } 2193 } 2194 if (params.className) 2195 { 2196 table.className = params.className; 2197 } 2198 2199 table.removeAttribute("data-bx-no-border"); 2200 if (table.getAttribute("border") == 0 || !table.getAttribute("border")) 2201 { 2202 table.removeAttribute("border"); 2203 table.setAttribute("data-bx-no-border", "Y"); 2204 } 2205 2206 if (params.width) 2207 { 2208 if (parseInt(params.width) == params.width) 2209 { 2210 params.width = params.width + 'px'; 2211 } 2212 2213 if (table.getAttribute("width")) 2214 { 2215 table.setAttribute("width", params.width); 2216 } 2217 else 2218 { 2219 table.style.width = params.width; 2220 } 2221 } 2222 2223 if (params.height) 2224 { 2225 if (parseInt(params.height) == params.height) 2226 { 2227 params.height = params.height + 'px'; 2228 } 2229 2230 if (table.getAttribute("height")) 2231 { 2232 table.setAttribute("height", params.height); 2233 } 2234 else 2235 { 2236 table.style.height = params.height; 2237 } 2238 } 2239 2240 var r, c, pCell; 2241 for(r = 0; r < table.rows.length; r++) 2242 { 2243 for(c = 0; c < table.rows[r].cells.length; c++) 2244 { 2245 pCell = table.rows[r].cells[c]; 2246 if ( 2247 ((params.headers == 'top' || params.headers == 'topleft') && r == 0) 2248 || 2249 ((params.headers == 'left' || params.headers == 'topleft') && c == 0) 2250 ) 2251 { 2252 replaceTdByTh(pCell); 2253 } 2254 else if (pCell.nodeName == 'TH') 2255 { 2256 replaceThByTd(pCell); 2257 } 2258 } 2259 } 2260 2261 var pCaption = BX.findChild(table, {tag: 'CAPTION'}, false); 2262 if (params.caption) 2263 { 2264 if (pCaption) 2265 { 2266 pCaption.innerHTML = BX.util.htmlspecialchars(params.caption); 2267 } 2268 else 2269 { 2270 pCaption = _this.document.createElement('CAPTION'); 2271 pCaption.innerHTML = BX.util.htmlspecialchars(params.caption); 2272 table.insertBefore(pCaption, table.firstChild); 2273 } 2274 } 2275 else if (pCaption) 2276 { 2277 BX.remove(pCaption); 2278 } 2279 } 2280 2281 return { 2282 exec: function(action, params) 2283 { 2284 // Iframe 2285 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 2286 { 2287 if (!params || !params.rows || !params.cols) 2288 { 2289 return false;
2290 } 2291 2292 _this.editor.iframeView.Focus(); 2293 var table = params.table || _this.actions.insertTable.state(action, params); 2294 2295 params.rows = parseInt(params.rows) || 1; 2296 params.cols = parseInt(params.cols) || 1; 2297 2298 if (params.align == 'left' || _this.editor.bbCode) 2299 { 2300 params.align = ''; 2301 } 2302 2303 if (!table) 2304 { 2305 table = _this.document.createElement('TABLE'); 2306 2307 var 2308 tbody = table.appendChild(_this.document.createElement('TBODY')), 2309 r, c, row, cell; 2310 2311 params.rows = parseInt(params.rows) || 1; 2312 params.cols = parseInt(params.cols) || 1; 2313 2314 for(r = 0; r < params.rows; r++) 2315 { 2316 row = tbody.insertRow(-1); 2317 for(c = 0; c < params.cols; c++) 2318 { 2319 cell = BX.adjust(row.insertCell(-1), {html: ' '}); 2320 } 2321 } 2322 applyAttributes(table, params); 2323 _this.editor.selection.InsertNode(table); 2324 2325 var nextNode = _this.editor.util.GetNextNotEmptySibling(table); 2326 if (!nextNode) 2327 { 2328 _this.editor.util.InsertAfter(BX.create('BR', {}, _this.document), table); 2329 } 2330 2331 if (nextNode && nextNode.nodeName == 'BR' && !nextNode.nextSibling) 2332 { 2333 _this.editor.util.InsertAfter(_this.editor.util.GetInvisibleTextNode(), nextNode); 2334 } 2335 } 2336 else 2337 { 2338 applyAttributes(table, params); 2339 } 2340 2341 var nodeToSetCarret = table.rows[0].cells[0].firstChild; 2342 if (nodeToSetCarret) 2343 { 2344 _this.editor.selection.SetAfter(nodeToSetCarret); 2345 } 2346 2347 // For Firefox refresh white markers 2348 setTimeout(function(){_this.editor.util.Refresh(table);}, 10); 2349 } 2350 else // bbcode + textarea 2351 { 2352 _this.editor.textareaView.Focus(); 2353 var 2354 tbl = '', 2355 i, j, 2356 cellHTML = _this.editor.INVISIBLE_SPACE; 2357 2358 if (params.rows > 0 && params.cols > 0) 2359 { 2360 tbl += "[TABLE]\n"; 2361 for(i = 0; i < params.rows; i++) 2362 { 2363 tbl += "\t[TR]\n"; 2364 for(j = 0; j < params.cols; j++) 2365 { 2366 tbl += "\t\t[TD]" + cellHTML + "[/TD]\n"; 2367 } 2368 tbl += "\t[/TR]\n"; 2369 } 2370 tbl += "[/TABLE]\n"; 2371 } 2372 2373 _this.editor.textareaView.WrapWith(false, false, tbl); 2374 } 2375 }, 2376 2377 state: function(action, value) 2378 { 2379 var 2380 selectedNode, 2381 selectedTable; 2382 2383 if (!_this.editor.util.DocumentHasTag(_this.document, 'TABLE')) 2384 { 2385 return false; 2386 } 2387 2388 selectedNode = _this.editor.selection.GetSelectedNode(); 2389 if (!selectedNode) 2390 { 2391 return false; 2392 } 2393 2394 if (selectedNode.nodeName === 'TABLE') 2395 { 2396 return selectedNode; 2397 } 2398 2399 if (selectedNode.nodeType !== 1 /* element node */) 2400 { 2401 return false; 2402 } 2403 2404 selectedTable = _this.editor.selection.GetNodes(1, function(node) 2405 { 2406 return node.nodeName === "TABLE"; 2407 }); 2408 2409 // Works only for one table 2410 if (selectedTable.length !== 1) 2411 { 2412 return false; 2413 } 2414 2415 return selectedTable[0]; 2416 }, 2417 value: BX.DoNothing 2418 }; 2419 }, 2420 2421 // 2422 GetInsertList: function(params) 2423 { 2424 var _this = this; 2425 var 2426 bOrdered = !!params.bOrdered, 2427 listTag = bOrdered ? 'OL' : 'UL', 2428 otherListTag = bOrdered ? 'UL' : 'OL'; 2429 2430 function getNextNotEmptySibling(node) 2431 { 2432 var nextSibling = node.nextSibling; 2433 while (nextSibling && nextSibling.nodeType == 3 && _this.editor.util.IsEmptyNode(nextSibling, true)) 2434 { 2435 nextSibling = nextSibling.nextSibling; 2436 } 2437 return nextSibling; 2438 } 2439 2440 function removeList(list) 2441 { 2442 if (list.nodeName !== "MENU" && list.nodeName !== "UL" && list.nodeName !== "OL") 2443 { 2444 return; 2445 } 2446 2447 var 2448 frag = _this.document.createDocumentFragment(), 2449 previousSibling = list.previousSibling, 2450 firstChild, 2451 lastChild, 2452 bAppendBr, 2453 listItem; 2454 2455 if (previousSibling && !_this.editor.util.IsBlockElement(previousSibling) && !_this.editor.util.IsEmptyNode(previousSibling, true)) 2456 { 2457 frag.appendChild(_this.document.createElement("BR")); 2458 } 2459 2460 while (listItem = list.firstChild) 2461 { 2462 lastChild = listItem.lastChild; 2463 while (firstChild = listItem.firstChild) 2464 { 2465 // Custom bullit items 2466 if (firstChild.nodeName == 'I' && firstChild.innerHTML == '' && firstChild.className != '') 2467 { 2468 BX.remove(firstChild); 2469 firstChild = listItem.firstChild; 2470 if (!firstChild) 2471 break; 2472 } 2473 2474 bAppendBr = firstChild === lastChild && 2475 !_this.editor.util.IsBlockElement(firstChild) && 2476 firstChild.nodeName !== "BR"; 2477 2478 frag.appendChild(firstChild); 2479 if (bAppendBr) 2480 { 2481 frag.appendChild(_this.document.createElement("BR")); 2482 } 2483 } 2484 listItem.parentNode.removeChild(listItem); 2485 } 2486 2487 var nextSibling = _this.editor.util.GetNextNotEmptySibling(list); 2488 if (nextSibling && nextSibling.nodeName == 'BR' && frag.lastChild && frag.lastChild.nodeName == 'BR') 2489 { 2490 // Remove unnecessary BR 2491 BX.remove(frag.lastChild); 2492 } 2493 2494 list.parentNode.replaceChild(frag, list); 2495 } 2496 2497 function convertToList(element, listType) 2498 { 2499 if (!element || !element.parentNode) 2500 return false;
2501 2502 var nodeName = element.nodeName.toUpperCase(); 2503 2504 if (nodeName === "UL" || nodeName === "OL" || nodeName === "MENU") 2505 { 2506 return element; 2507 } 2508 2509 var 2510 list = _this.document.createElement(listType), 2511 childNode, 2512 currentLi; 2513 2514 while (element.firstChild) 2515 { 2516 currentLi = currentLi || list.appendChild(_this.document.createElement("li")); 2517 childNode = element.firstChild; 2518 2519 if (_this.editor.util.IsBlockElement(childNode)) 2520 { 2521 currentLi = currentLi.firstChild ? list.appendChild(_this.document.createElement("li")) : currentLi; 2522 currentLi.appendChild(childNode); 2523 currentLi = null; 2524 continue; 2525 } 2526 2527 if (childNode.nodeName === "BR") 2528 { 2529 currentLi = currentLi.firstChild ? null : currentLi; 2530 element.removeChild(childNode); 2531 continue; 2532 } 2533 2534 currentLi.appendChild(childNode); 2535 } 2536 2537 element.parentNode.replaceChild(list, element); 2538 return list; 2539 } 2540 2541 function isListNode(n) 2542 { 2543 return n.nodeName == 'OL' || n.nodeName == 'UL' || n.nodeName == 'MENU' 2544 } 2545 2546 function getSelectedList(tag, node) 2547 { 2548 if (!node) 2549 { 2550 node = _this.editor.selection.GetSelectedNode(); 2551 } 2552 2553 if (!node || node.nodeName == 'BODY') 2554 { 2555 var 2556 range = _this.editor.selection.GetRange(), 2557 commonAncestor = _this.editor.selection.GetCommonAncestorForRange(range); 2558 2559 if (commonAncestor && isListNode(commonAncestor)) 2560 { 2561 node = commonAncestor; 2562 } 2563 else 2564 { 2565 var 2566 i, onlyList = true, list, parentList, 2567 nodes = range.getNodes([1]), 2568 l = nodes.length; 2569 2570 if (commonAncestor) 2571 { 2572 list = parentList = BX.findParent(commonAncestor, isListNode, _this.document.body); 2573 } 2574 2575 if (!parentList) 2576 { 2577 for (i = 0; i < l; i++) 2578 { 2579 parentList = BX.findParent(nodes[i], isListNode, commonAncestor); 2580 if (!parentList || (list && parentList != list)) 2581 { 2582 onlyList = false; 2583 break; 2584 } 2585 list = parentList; 2586 } 2587 } 2588 2589 if (!list) 2590 { 2591 var 2592 _list = false; 2593 for (i = 0; i < l; i++) 2594 { 2595 if (isListNode(nodes[i])) 2596 { 2597 _list = nodes[i]; 2598 break; 2599 } 2600 } 2601 2602 if (_list) 2603 { 2604 var ok = true; 2605 for (i = 0; i < l; i++) 2606 { 2607 if (nodes[i] == _list || 2608 BX.findParent(nodes[i], function(n){return n === _list;}, _this.document.body) || 2609 nodes[i].nodeName == 'BR' || 2610 _this.editor.util.IsEmptyNode(nodes[i]) 2611 ) 2612 { 2613 } 2614 else 2615 { 2616 ok = false; 2617 break; 2618 } 2619 } 2620 2621 if (ok) 2622 { 2623 list = _list; 2624 } 2625 } 2626 } 2627 2628 if (list) 2629 { 2630 node = list; 2631 } 2632 } 2633 } 2634 2635 return node && node.nodeName == tag ? node : BX.findParent(node, {tagName: tag}, _this.document.body); 2636 } 2637 2638 2639 function customBullit(list, bullitParams) 2640 { 2641 if (!list) 2642 return false; 2643 2644 if (!bullitParams) 2645 bullitParams = {tag: 'I', remove: true}; 2646 2647 var i, node, fch, doc = list.ownerDocument; 2648 for (i = 0; i < list.childNodes.length; i++) 2649 { 2650 node = list.childNodes[i]; 2651 if (node && node.nodeType == 1 && node.nodeName == 'LI') 2652 { 2653 fch = node.firstChild; 2654 // Custom bullit already here 2655 if (fch.nodeName == bullitParams.tag && fch.innerHTML == '') 2656 { 2657 if (bullitParams.remove) 2658 BX.remove(fch); 2659 else 2660 fch.className = bullitParams.className; 2661 } 2662 else // Add custom bullits to the list 2663 { 2664 node.insertBefore(BX.create(bullitParams.tag, {props: {className: bullitParams.className}}, doc), fch); 2665 } 2666 } 2667 } 2668 } 2669 2670 function getCustomBullitClass(list) 2671 { 2672 if (!list) 2673 return false; 2674 2675 var i, node, fch; 2676 for (i = 0; i < list.childNodes.length; i++) 2677 { 2678 node = list.childNodes[i]; 2679 if (node && node.nodeType == 1 && node.nodeName == 'LI') 2680 { 2681 fch = node.firstChild; 2682 if (fch && fch.nodeName == 'I' && fch.innerHTML == '' && fch.className !== '') 2683 { 2684 return fch.className; 2685 } 2686 } 2687 } 2688 2689 return false;
2690 } 2691 2692 function checkCustomBullitList(list, bullitClass, setFocusAfterLastBullit) 2693 { 2694 if (list && (list.nodeName == 'UL' || list.nodeName == 'OL')) 2695 { 2696 var i, node, fch, doc = list.ownerDocument, lastBullit; 2697 for (i = 0; i < list.childNodes.length; i++) 2698 { 2699 node = list.childNodes[i]; 2700 if (node && node.nodeType == 1 && node.nodeName == 'LI' && node.firstChild) 2701 { 2702 fch = node.firstChild; 2703 if (fch.nodeName == 'I' && fch.innerHTML == '' && fch.className !== '') 2704 { 2705 if (!bullitClass) 2706 bullitClass = fch.className; 2707 } 2708 else if (fch.nodeName == 'I' && fch.innerHTML == '' && bullitClass) 2709 { 2710 fch.className = bullitClass; 2711 lastBullit = fch; 2712 } 2713 else if(fch && bullitClass) 2714 { 2715 lastBullit = node.insertBefore(BX.create('I', {props: {className: bullitClass}}, doc), fch); 2716 } 2717 } 2718 } 2719 2720 if (setFocusAfterLastBullit && lastBullit) 2721 _this.editor.selection._MoveCursorAfterNode(lastBullit); 2722 } 2723 } 2724 2725 return { 2726 exec: function(action, params) 2727 { 2728 // Iframe 2729 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 2730 { 2731 var range = _this.editor.selection.GetRange(); 2732 2733 if (_this.IsSupportedByBrowser(action) && range.collapsed) 2734 { 2735 _this.document.execCommand(action, false, null); 2736 } 2737 else 2738 { 2739 var 2740 selectedNode = _this.editor.selection.GetSelectedNode(), 2741 list = getSelectedList(listTag, selectedNode), 2742 otherList = getSelectedList(otherListTag, selectedNode), 2743 isEmpty, 2744 tempElement; 2745 2746 if (list) 2747 { 2748 _this.editor.selection.ExecuteAndRestoreSimple(function() 2749 { 2750 removeList(list); 2751 }); 2752 } 2753 else if (otherList) 2754 { 2755 _this.editor.selection.ExecuteAndRestoreSimple(function() 2756 { 2757 _this.editor.util.RenameNode(otherList, listTag); 2758 }); 2759 } 2760 else 2761 { 2762 tempElement = _this.document.createElement("span"); 2763 _this.editor.selection.Surround(tempElement); 2764 isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === _this.editor.INVISIBLE_SPACE; 2765 _this.editor.selection.ExecuteAndRestoreSimple(function() 2766 { 2767 // mantis #54087 2768 var i, spans = tempElement.getElementsByTagName('SPAN'); 2769 for (i = spans.length - 1; i >= 0; i--) 2770 { 2771 // Clean spans without classes styles and id's 2772 if (!spans[i].className && !spans[i].id && !spans[i].style.cssText) 2773 { 2774 _this.editor.util.ReplaceWithOwnChildren(spans[i]); 2775 } 2776 } 2777 list = convertToList(tempElement, listTag); 2778 }); 2779 2780 if (list) 2781 { 2782 var i = 0, item; 2783 while (i < list.childNodes.length) 2784 { 2785 item = list.childNodes[i]; 2786 if (item.nodeName == 'LI') 2787 { 2788 if (_this.editor.util.IsEmptyNode(item, true, true)) 2789 { 2790 BX.remove(item); 2791 continue; 2792 } 2793 i++; 2794 } 2795 else if (item.nodeType == 1) 2796 { 2797 BX.remove(item); 2798 } 2799 } 2800 2801 // Mantis: #53646, #53820 2802 var prevSib = _this.editor.util.GetPreviousNotEmptySibling(list); 2803 if (prevSib && ( 2804 prevSib.nodeName == 'BLOCKQUOTE' || 2805 prevSib.nodeName == 'PRE' || 2806 prevSib.nodeName == 'UL' || 2807 prevSib.nodeName == 'OL' 2808 ) 2809 && list.childNodes[0] && BX.findChild(list.childNodes[0], {tag: prevSib.nodeName})) 2810 { 2811 if (BX.util.trim(_this.editor.util.GetTextContent(prevSib)) == '') 2812 { 2813 BX.remove(prevSib); 2814 } 2815 } 2816 } 2817 2818 if (isEmpty && list && list.querySelector) 2819 { 2820 _this.editor.selection.SelectNode(list.querySelector("li")); 2821 } 2822 } 2823 } 2824 } 2825 else // bbcode + textarea 2826 { 2827 if (params && params.items) 2828 { 2829 _this.editor.textareaView.Focus(); 2830 var lst = '[LIST' + (bOrdered ? '=1' : '')+ ']\n', it; 2831 2832 for(it = 0; it < params.items.length; it++) 2833 { 2834 lst += "\t[*]" + params.items[it] + "\n"; 2835 } 2836 lst += "[/LIST]\n"; 2837 2838 _this.editor.textareaView.WrapWith(false, false, lst); 2839 } 2840 } 2841 }, 2842 2843 state: function() 2844 { 2845 return getSelectedList(listTag) || false; 2846 }, 2847 2848 value: BX.DoNothing, 2849 2850 customBullit: customBullit, 2851 getCustomBullitClass: getCustomBullitClass, 2852 checkCustomBullitList: checkCustomBullitList 2853 }; 2854 }, 2855 2856 GetAlign: function() 2857 { 2858 var 2859 CLASS_NAME_TMP = 'bx-align-tmp', 2860 LIST_ALIGN_ATTR = 'data-bx-checked-align-list', 2861 DEFAULT_VALUE = 'left', 2862 TABLE_NODES = {TD: 1, TR: 1, TH: 1, TABLE: 1, TBODY: 1, CAPTION: 1, COL: 1, COLGROUP: 1, TFOOT: 1, THEAD: 1}, 2863 ALIGN_NODES = {IMG: 1, P: 1, DIV: 1, TABLE: 1, H1: 1, H2: 1, H3: 1, H4: 1, H5: 1, H6: 1}, 2864 _this = this; 2865 2866 function checkNodeAlign(n) 2867 { 2868 var 2869 nodeName = n.nodeName, align, 2870 res = false;
2871 2872 if (n.nodeType === 1) 2873 { 2874 align = n.style.textAlign; 2875 if (n.style.textAlign) 2876 { 2877 res = {node: n, style: align}; 2878 } 2879 2880 if (ALIGN_NODES[nodeName]) 2881 { 2882 align = n.getAttribute('align'); 2883 if (align) 2884 { 2885 if (res) 2886 { 2887 res.attribute = align; 2888 } 2889 else 2890 { 2891 res = {node: n, attribute: align}; 2892 } 2893 } 2894 } 2895 } 2896 return res; 2897 } 2898 2899 function isCell(n) 2900 { 2901 return n && (n.nodeName == 'TD' || n.nodeName == 'TH'); 2902 } 2903 2904 function alignTableNode(n, value) 2905 { 2906 n.setAttribute('data-bx-tmp-align', value); 2907 n.style.textAlign = value; 2908 } 2909 2910 function alignTable(n, value) 2911 { 2912 if (value == 'left' || value == 'justify' || _this.editor.bbCode) 2913 { 2914 n.removeAttribute('align'); 2915 } 2916 else 2917 { 2918 n.setAttribute('align', value); 2919 } 2920 } 2921 2922 function checkAlignTable(table, value) 2923 { 2924 var 2925 ths, 2926 i, res = true, 2927 tds = table.getElementsByTagName("TD"); 2928 2929 for (i = 0; i < tds.length; i++) 2930 { 2931 if (tds[i].getAttribute('data-bx-tmp-align') != value) 2932 { 2933 res = false; 2934 break; 2935 } 2936 } 2937 2938 if (res) 2939 { 2940 ths = table.getElementsByTagName("TH"); 2941 for (i = 0; i < ths.length; i++) 2942 { 2943 if (ths[i].getAttribute('data-bx-tmp-align') != value) 2944 { 2945 res = false; 2946 break; 2947 } 2948 } 2949 } 2950 2951 if (res) 2952 { 2953 alignTable(table, value); 2954 } 2955 } 2956 2957 function createAlignNodeInside(node, value) 2958 { 2959 var alignNode = BX.create("DIV", {style: {textAlign: value}, html: node.innerHTML}, _this.editor.GetIframeDoc()); 2960 node.innerHTML = ''; 2961 node.appendChild(alignNode); 2962 return alignNode; 2963 } 2964 2965 function createAlignNodeOutside(node, value) 2966 { 2967 var alignNode = BX.create("DIV", {style: {textAlign: value}}, _this.editor.GetIframeDoc()); 2968 node.parentNode.insertBefore(alignNode, node); 2969 alignNode.appendChild(node); 2970 return alignNode; 2971 } 2972 2973 function checkListItemsAlign(list, item, value) 2974 { 2975 var 2976 doc = _this.editor.GetIframeDoc(), 2977 bb = _this.editor.bbCode; 2978 if (!list && item) 2979 { 2980 list = BX.findParent(item, function(n) 2981 { 2982 return n.nodeName == 'OL' || n.nodeName == 'UL' || n.nodeName == 'MENU'; 2983 }, doc); 2984 } 2985 2986 if (list && !list.getAttribute(LIST_ALIGN_ATTR)) 2987 { 2988 var 2989 i, clean = true, 2990 lis = list.getElementsByTagName('LI'); 2991 2992 for (i = 0; i < lis.length; i++) 2993 { 2994 if (lis[i].style.textAlign !== value) 2995 { 2996 clean = false; 2997 break; 2998 } 2999 } 3000 3001 if (bb) 3002 { 3003 list.style.textAlign = ''; 3004 if (list.style.cssText == '') 3005 { 3006 list.removeAttribute('style'); 3007 } 3008 cleanListItemsAlign(list); 3009 3010 createAlignNodeOutside(list, value); 3011 } 3012 else if (clean) 3013 { 3014 list.style.textAlign = value; 3015 cleanListItemsAlign(list); 3016 } 3017 3018 list.setAttribute(LIST_ALIGN_ATTR, 'Y'); 3019 return list; 3020 } 3021 3022 return false; 3023 } 3024 3025 function cleanListItemsAlign(list) 3026 { 3027 var i, lis = list.getElementsByTagName('LI'); 3028 for (i = 0; i < lis.length; i++) 3029 { 3030 lis[i].style.textAlign = ''; 3031 if (lis[i].style.cssText == '') 3032 { 3033 lis[i].removeAttribute('style'); 3034 } 3035 } 3036 } 3037 3038 function pushTable(arr, newTable) 3039 { 3040 if (newTable && newTable.nodeName == 'TABLE') 3041 { 3042 var i, found = false; 3043 for (i = 0; i < arr.length; i++) 3044 { 3045 if (arr[i] == newTable) 3046 { 3047 found = true; 3048 break; 3049 } 3050 } 3051 if (!found) 3052 { 3053 arr.push(newTable); 3054 } 3055 } 3056 return arr; 3057 } 3058 3059 return { 3060 exec: function(action, value) 3061 { 3062 var res; 3063 3064 // Iframe 3065 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 3066 { 3067 var 3068 i, 3069 tagName = 'P', 3070 range = _this.editor.selection.GetRange(), 3071 blockElement = false, 3072 tableElement = false, 3073 listNode = false, 3074 bookmark = _this.editor.selection.GetBookmark(), 3075 selectedNode = _this.editor.selection.GetSelectedNode(); 3076 3077 if (selectedNode) 3078 { 3079 if (_this.editor.util.IsBlockNode(selectedNode)) 3080 { 3081 blockElement = selectedNode; 3082 } 3083 else if (selectedNode.nodeType == 1 && TABLE_NODES[selectedNode.nodeName]) 3084 { 3085 tableElement = selectedNode; 3086 res = true; 3087 setTimeout(function(){ 3088 _this.editor.selection.SelectNode(tableElement); 3089 if (tableElement.nodeName == 'TABLE') 3090 { 3091 alignTable(tableElement, value); 3092 } 3093 }, 10); 3094 } 3095 else 3096 { 3097 if (selectedNode.nodeName == 'LI') 3098 { 3099 listNode = selectedNode; 3100 } 3101 else if (selectedNode.nodeName == 'OL' || selectedNode.nodeName == 'UL' || selectedNode.nodeName == 'MENU') 3102 { 3103 if (_this.editor.bbCode) 3104 { 3105 createAlignNodeOutside(selectedNode, value); 3106 selectedNode.style.textAlign = ''; 3107 } 3108 else 3109 { 3110 selectedNode.style.textAlign = value; 3111 } 3112 res = true; 3113 cleanListItemsAlign(selectedNode); 3114 setTimeout(function(){_this.editor.selection.SelectNode(selectedNode);}, 10); 3115 } 3116 else 3117 { 3118 listNode = BX.findParent(selectedNode, function(n) 3119 { 3120 return n.nodeName == 'LI'; 3121 }, _this.document.body); 3122 3123 } 3124 3125 if (listNode) 3126 { 3127 if (_this.editor.bbCode) 3128 { 3129 createAlignNodeInside(listNode, value); 3130 listNode.style.textAlign = ''; 3131 } 3132 else 3133 { 3134 listNode.style.textAlign = value; 3135 } 3136 res = true; 3137 setTimeout(function(){_this.editor.selection.SelectNode(listNode);}, 10); 3138 } 3139 else 3140 { 3141 blockElement = BX.findParent(selectedNode, function(n) 3142 { 3143 return _this.editor.util.IsBlockNode(n) && !_this.actions.quote.checkNode(n); 3144 }, _this.document.body); 3145 } 3146 } 3147 } 3148 else 3149 { 3150 // In Chrome when we select some parts of table we apply align for tds, but if entire table was selected - we trying to continue align other elements. 3151 var 3152 tables = [], 3153 tableIsHere = false, 3154 arLists = [], arLis = [], 3155 nodes = range.getNodes([1]); 3156 3157 for (i = 0; i < nodes.length; i++) 3158 {
3159 if (isCell(nodes[i])) 3160 { 3161 tables = pushTable(tables, BX.findParent(nodes[i], {tagName: 'TABLE'})); 3162 alignTableNode(nodes[i], value); 3163 res = true; 3164 } 3165 3166 if (nodes[i].nodeName == 'TABLE') 3167 { 3168 tables = pushTable(tables, nodes[i]); 3169 tableIsHere = true; 3170 } 3171 else if (nodes[i].nodeName == 'OL' || nodes[i].nodeName == 'UL' || nodes[i].nodeName == 'MENU') 3172 { 3173 nodes[i].style.textAlign = value; 3174 arLists.push(nodes[i]); 3175 res = true; 3176 } 3177 else if (nodes[i].nodeName == 'LI') 3178 { 3179 nodes[i].style.textAlign = value; 3180 res = true; 3181 arLis.push(nodes[i]); 3182 } 3183 } 3184 3185 for (i = 0; i < tables.length; i++) 3186 { 3187 checkAlignTable(tables[i], value); 3188 } 3189 3190 // Example: ctra+a was pressed 3191 if (res) 3192 { 3193 var commonAncestor = _this.editor.selection.GetCommonAncestorForRange(range); 3194 if (commonAncestor && commonAncestor.nodeName == 'BODY') 3195 { 3196 res = false; 3197 } 3198 } 3199 3200 for (i = 0; i < arLists.length; i++) 3201 { 3202 cleanListItemsAlign(arLists[i]); 3203 } 3204 var arCheckedLists = [], checkedList; 3205 3206 for (i = 0; i < arLis.length; i++) 3207 { 3208 checkedList = checkListItemsAlign(false, arLis[i], value); 3209 if (checkedList) 3210 { 3211 arCheckedLists.push(checkedList); 3212 } 3213 } 3214 for (i = 0; i < arCheckedLists.length; i++) 3215 { 3216 arCheckedLists[i].removeAttribute(LIST_ALIGN_ATTR); 3217 } 3218 } 3219 3220 if (!res) 3221 { 3222 // Simple situation - we inside of the block element - just add text-align to it... 3223 if (blockElement) 3224 { 3225 if (_this.editor.bbCode) 3226 { 3227 createAlignNodeInside(blockElement, value); 3228 blockElement.style.textAlign = ''; 3229 } 3230 else 3231 { 3232 // Accept all block tags except DIVs 3233 tagName = blockElement.tagName != 'DIV' ? blockElement.tagName : 'P'; 3234 res = _this.actions.formatBlock.exec('formatBlock', tagName, null, {textAlign: value}); 3235 } 3236 _this.editor.util.Refresh(blockElement); 3237 } 3238 else if(tableElement) 3239 { 3240 if (isCell(tableElement)) 3241 { 3242 alignTableNode(tableElement, value); 3243 } 3244 else 3245 { 3246 var 3247 tds = BX.findChild(tableElement, isCell, true, true), 3248 ths = BX.findChild(tableElement, isCell, true, true); 3249 3250 for (i = 0; i < tds.length; i++) 3251 { 3252 alignTableNode(tds[i], value); 3253 } 3254 for (i = 0; i < ths.length; i++) 3255 { 3256 alignTableNode(ths[i], value); 3257 } 3258 } 3259 } 3260 else if (range.collapsed) 3261 { 3262 res = _this.actions.formatBlock.exec('formatBlock', 'P', CLASS_NAME_TMP, {textAlign: value}); 3263 3264 // Selection workaround mantis:53937 3265 var 3266 focusNode, 3267 alignNodes = _this.document.querySelectorAll("." + CLASS_NAME_TMP); 3268 3269 for (i = 0; i <= alignNodes.length; i++) 3270 { 3271 BX.removeClass(alignNodes[i], CLASS_NAME_TMP); 3272 if (i == 0) 3273 { 3274 focusNode = alignNodes[i].firstNode; 3275 if (!focusNode) 3276 focusNode = alignNodes[i].appendChild(_this.editor.util.GetInvisibleTextNode()); 3277 setTimeout(function() 3278 { 3279 if (focusNode) 3280 _this.editor.selection.SetAfter(focusNode); 3281 }, 100); 3282 } 3283 } 3284 } 3285 else 3286 { 3287 // Image selected 3288 var image = _this.actions.insertImage.state(); 3289 3290 if (!res && false) 3291 { 3292 var onlyPar = true; 3293 nodes = range.getNodes([1]); 3294 if (nodes && nodes.length > 0) 3295 { 3296 for (i = 0; i < nodes.length; i++) 3297 { 3298 if (nodes[i].nodeName == "P") 3299 { 3300 nodes[i].style.textAlign = value; 3301 } 3302 else 3303 { 3304 onlyPar = false; 3305 } 3306 } 3307 res = onlyPar; 3308 } 3309 } 3310 3311 // Mixed content 3312 if (!res) 3313 { 3314 tagName = _this.editor.bbCode ? 'DIV' : 'P'; 3315 3316 res = _this.actions.formatBlock.exec('formatBlock', tagName, null, {textAlign: value}, {leaveChilds: true, splitBlock: true}); 3317 if (res && typeof res == 'object' && res.nodeName == tagName) 3318 { 3319 var 3320 iter = 0, maxIter = 2000, prev, 3321 child, newPar, createNewPar = false;
3322 3323 // mantis:#54026 3324 if (res.firstChild && res.firstChild.nodeName == 'BLOCKQUOTE') 3325 { 3326 prev = _this.editor.util.GetPreviousNotEmptySibling(res); 3327 if (prev && prev.nodeName == 'BLOCKQUOTE' && _this.editor.util.IsEmptyNode(prev)) 3328 { 3329 BX.remove(prev); 3330 } 3331 } 3332 3333 i = 0; 3334 3335 while (i < res.childNodes.length || iter > maxIter) 3336 { 3337 child = res.childNodes[i]; 3338 if(_this.editor.util.IsBlockNode(child)) 3339 { 3340 child.style.textAlign = value; 3341 createNewPar = true; 3342 i++; 3343 } 3344 else 3345 { 3346 if (!newPar || createNewPar) 3347 { 3348 newPar = _this.document.createElement(tagName); 3349 newPar.style.textAlign = value; 3350 res.insertBefore(newPar, child); 3351 i++; 3352 } 3353 3354 newPar.appendChild(child); 3355 createNewPar = false; 3356 } 3357 iter++; 3358 } 3359 3360 // Clean useless <p></p> before and after 3361 if (res.previousSibling && res.previousSibling.nodeName == "P" && _this.editor.util.IsEmptyNode(res.previousSibling, true, true)) 3362 { 3363 BX.remove(res.previousSibling); 3364 } 3365 if (res.nextSibling && res.nextSibling.nodeName == "P" && _this.editor.util.IsEmptyNode(res.nextSibling, true, true)) 3366 { 3367 BX.remove(res.nextSibling); 3368 } 3369 _this.editor.util.ReplaceWithOwnChildren(res); 3370 3371 // Selection workaround mantis:53937 3372 setTimeout(function() 3373 { 3374 if (newPar) 3375 _this.editor.selection.SelectNode(newPar); 3376 }, 100); 3377 } 3378 } 3379 3380 if (image) 3381 { 3382 // For Firefox 3383 _this.editor.util.Refresh(image); 3384 } 3385 } 3386 } 3387 3388 setTimeout(function(){_this.editor.selection.SetBookmark(bookmark);}, 10); 3389 } 3390 else // bbcode + textarea 3391 { 3392 if (value) 3393 { 3394 res = _this.actions.formatBbCode.exec(action, {tag: value.toUpperCase()}); 3395 } 3396 } 3397 3398 return res; 3399 }, 3400 state: function(action, value) 3401 { 3402 var 3403 alignRes, node, 3404 selectedNode = _this.editor.selection.GetSelectedNode(); 3405 3406 if (selectedNode) 3407 { 3408 alignRes = checkNodeAlign(selectedNode); 3409 if (!alignRes) 3410 { 3411 node = BX.findParent(selectedNode, function(n) 3412 { 3413 alignRes = checkNodeAlign(n); 3414 return alignRes; 3415 }, _this.document.body); 3416 } 3417 3418 return { 3419 node: alignRes ? alignRes.node : null, 3420 value: alignRes ? (alignRes.style || alignRes.attribute) : DEFAULT_VALUE, 3421 res: alignRes 3422 }; 3423 } 3424 else 3425 { 3426 var 3427 result = {node: null, value: DEFAULT_VALUE, res: true}, 3428 range = _this.editor.selection.GetRange(); 3429 if (!range.collapsed) 3430 { 3431 var 3432 alRes, 3433 curValue = '', i, 3434 nodes = range.getNodes([1]); 3435 3436 for (i = 0; i < nodes.length; i++) 3437 { 3438 if (!_this.editor.util.CheckSurrogateNode(nodes[i]) && 3439 nodes[i].nodeName !== 'BR' && 3440 _this.editor.util.GetNodeDomOffset(nodes[i]) == 0 3441 ) 3442 { 3443 alRes = checkNodeAlign(nodes[i]); 3444 value = alRes ? (alRes.style || alRes.attribute) : DEFAULT_VALUE; 3445 3446 if (!curValue) 3447 { 3448 curValue = value; 3449 } 3450 3451 if (value != curValue) 3452 { 3453 result.res = false; 3454 break; 3455 } 3456 3457 } 3458 } 3459 if (result.res) 3460 { 3461 result.value = curValue; 3462 } 3463 } 3464 else 3465 { 3466 result.res = false; 3467 } 3468 3469 return result; 3470 } 3471 }, 3472 value: BX.DoNothing 3473 }; 3474 }, 3475 3476 GetIndent: function() 3477 { 3478 var _this = this; 3479 return { 3480 exec: function(action) 3481 { 3482 // Mantis bug workaround: #59811 3483 var rng = _this.editor.selection.GetRange(); 3484 if (rng && rng.collapsed && rng.endContainer == rng.startContainer && BX.util.in_array(rng.startContainer.nodeName, ['TD', 'TR', 'TH']) && arguments[1] !== rng.startContainer.nodeName) 3485 { 3486 var 3487 id = 'bxed_bogus_node_59811', 3488 focusNode = rng.startContainer.appendChild(BX.create('SPAN', {props: {id: id}, html: ' '}, _this.document));is_text = _this.editor.util.GetInvisibleTextNode(); 3489 3490 if (focusNode) 3491 { 3492 rng.setStartBefore(focusNode); 3493 rng.setEndAfter(focusNode); 3494 _this.editor.selection.SetSelection(rng); 3495 3496 return setTimeout(function() 3497 { 3498 _this.actions.indent.exec(action, rng.startContainer.nodeName); 3499 3500 var focusNode = _this.editor.GetIframeElement(id); 3501 if (focusNode) 3502 { 3503 _this.editor.selection.SetAfter(focusNode); 3504 BX.remove(focusNode); 3505 } 3506 3507 }, 0); 3508 } 3509 } 3510 3511 if (_this.IsSupportedByBrowser(action)) 3512 { 3513 _this.document.execCommand(action); 3514 } 3515 else 3516 { 3517 _this.actions.formatBlock.exec('formatBlock', 'BLOCKQUOTE'); 3518 } 3519 3520 var range = _this.editor.selection.GetRange(); 3521 if (range) 3522 { 3523 var bqCnt = 0, i, nodes = range.getNodes([1]); 3524 3525 for (i = 0; i < nodes.length; i++) 3526 { 3527 if (nodes[i].nodeName == 'BLOCKQUOTE') 3528 { 3529 bqCnt++; 3530 nodes[i].removeAttribute('style'); 3531 } 3532 } 3533 3534 if (range.startContainer) 3535 { 3536 var 3537 invis_text, 3538 bq = false, 3539 node = range.startContainer; 3540 while (node) 3541 { 3542 node = BX.findParent(node, {tag: 'BLOCKQUOTE'}, _this.document.body); 3543 if (node) 3544 { 3545 if (!bq) 3546 bq = node; 3547 node.removeAttribute('style'); 3548 } 3549 } 3550 3551 if (bq) 3552 { 3553 invis_text = _this.editor.util.GetInvisibleTextNode();
3554 bq.appendChild(invis_text); 3555 _this.editor.selection.SetAfter(invis_text); 3556 } 3557 } 3558 } 3559 }, 3560 state: function(action, value) 3561 { 3562 var 3563 res = false, 3564 range = _this.editor.selection.GetRange(); 3565 if (range) 3566 { 3567 var commonAncestor = _this.editor.selection.GetCommonAncestorForRange(range); 3568 if (commonAncestor && commonAncestor.nodeType == 1 && commonAncestor.nodeName === 'BLOCKQUOTE') 3569 { 3570 res = commonAncestor; 3571 } 3572 } 3573 return res; 3574 }, 3575 value: BX.DoNothing 3576 }; 3577 }, 3578 3579 GetOutdent: function() 3580 { 3581 var _this = this; 3582 return { 3583 exec: function(action, value) 3584 { 3585 var 3586 i, 3587 attr = 'data-bx-tmp-flag', 3588 doc = _this.editor.GetIframeDoc(), 3589 blockNodes = doc.getElementsByTagName('BLOCKQUOTE'); 3590 3591 //if (blockNodes && blockNodes.length > 0) 3592 { 3593 var 3594 parNodesToClear = [], 3595 parNodes = doc.getElementsByTagName('P'); 3596 for (i = 0; i < parNodes.length; i++) 3597 { 3598 parNodes[i].setAttribute(attr, 'Y'); 3599 } 3600 _this.document.execCommand(action); 3601 3602 parNodes = doc.getElementsByTagName('P'); 3603 for (i = 0; i < parNodes.length; i++) 3604 { 3605 if (!parNodes[i].getAttribute(attr)) 3606 { 3607 parNodesToClear.push(parNodes[i]); 3608 } 3609 else 3610 { 3611 parNodes[i].removeAttribute(attr); 3612 } 3613 } 3614 3615 _this.editor.selection.ExecuteAndRestoreSimple(function() 3616 { 3617 for (i = 0; i < parNodesToClear.length; i++) 3618 { 3619 _this.actions.formatBlock.addBrBeforeAndAfter(parNodesToClear[i]); 3620 _this.editor.util.ReplaceWithOwnChildren(parNodesToClear[i]); 3621 } 3622 }); 3623 } 3624 }, 3625 state: function(action, value) 3626 { 3627 var 3628 range = _this.editor.selection.GetRange(); 3629 3630 return false; 3631 }, 3632 value: BX.DoNothing 3633 }; 3634 }, 3635 3636 GetFontFamily: function() 3637 { 3638 var _this = this; 3639 return { 3640 exec: function(action, value) 3641 { 3642 var res; 3643 3644 // Iframe 3645 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 3646 { 3647 if (value) 3648 res = _this.actions.formatInline.exec(action, value, "span", {fontFamily: value}); 3649 else // Clear fontFamily format 3650 res = _this.actions.formatInline.exec(action, value, "span", {fontFamily: null}, null, {bClear: true}); 3651 } 3652 else // textarea + bbcode 3653 { 3654 return _this.actions.formatBbCode.exec(action, {tag: 'FONT', value: value}); 3655 } 3656 3657 return res; 3658 }, 3659 3660 state: function(action, value) 3661 { 3662 return _this.actions.formatInline.state(action, value, "span", {fontFamily: value}); 3663 }, 3664 3665 value: BX.DoNothing 3666 }; 3667 }, 3668 3669 GetFormatStyle: function() 3670 { 3671 var 3672 _this = this, 3673 styleSel = this.editor.toolbar.controls.StyleSelector, 3674 classList = styleSel ? styleSel.checkedClasses : [], 3675 tagList = styleSel ? styleSel.checkedTags : []; 3676 3677 function isNodeSuitable(node) 3678 { 3679 if (node && node.nodeType == 1 && node.nodeName !== 'BODY' && 3680 ( 3681 BX.util.in_array(node.nodeName, tagList) || 3682 BX.util.in_array(node.className, classList) 3683 )) 3684 { 3685 return !_this.editor.GetBxTag(node.id).tag; 3686 } 3687 return false; 3688 } 3689 3690 return { 3691 exec: function(action, value) 3692 { 3693 if (!value) // Clear font style 3694 { 3695 return _this.actions.removeFormat.exec('removeFormat'); 3696 } 3697 else if (typeof value === 'string') // Tag name - H1, H2 3698 { 3699 return _this.actions.formatBlock.exec('formatBlock', value); 3700 } 3701 else if (typeof value === 'object') // class name from template-s css 3702 { 3703 // Handle font awesome list 3704 if (value.tag == 'UL') 3705 { 3706 var list = _this.actions.insertUnorderedList.state(); 3707 if (list && value.className && value.className.indexOf('~~') !== -1) 3708 { 3709 var cn = value.className.split('~~'); 3710 if (cn && cn.length >=2 ) 3711 { 3712 var 3713 listClass = cn[0], 3714 bullitClass = cn[1]; 3715 3716 list.className = listClass; 3717 _this.actions.insertUnorderedList.customBullit(list, {tag: 'I', className: bullitClass, html: ''}); 3718 } 3719 } 3720 else if (list) 3721 { 3722 list.className = value.className || ''; 3723 _this.actions.insertUnorderedList.customBullit(list, false); 3724 } 3725 } 3726 else if (value.tag) 3727 { 3728 var 3729 className = value.className, 3730 tag = value.tag.toUpperCase(); 3731 3732 // Inline 3733 if (tag == 'SPAN') 3734 { 3735 //command, value, tagName, arStyle, cssClass, params) 3736 _this.actions.formatInline.exec(action, value, tag, false, className); 3737 } 3738 else //if (tag == 'P') 3739 { 3740 _this.actions.formatBlock.exec('formatBlock', tag, className, null, {nestedBlocks: false}); 3741 } 3742 } 3743 3744 if (!_this.editor.util.FirstLetterSupported()) 3745 { 3746 _this.editor.parser.FirstLetterCheckNodes('', '', true); 3747 } 3748 } 3749 }, 3750 3751 state: function(action, value) 3752 { 3753 var 3754 result = false, 3755 selectedNode = _this.editor.selection.GetSelectedNode(); 3756 3757 if (selectedNode) 3758 { 3759 if (isNodeSuitable(selectedNode)) 3760 { 3761 result = selectedNode; 3762 } 3763 else 3764 { 3765 result = BX.findParent(selectedNode, isNodeSuitable, _this.document.body); 3766 } 3767 } 3768 else 3769 { 3770 var 3771 range = _this.editor.selection.GetRange(), 3772 commonAncestor = _this.editor.selection.GetCommonAncestorForRange(range); 3773 3774 if (isNodeSuitable(commonAncestor)) 3775 { 3776 result = commonAncestor; 3777 } 3778 } 3779 3780 return result; 3781 }, 3782 value: BX.DoNothing 3783 }; 3784 }, 3785 3786 GetChangeTemplate: function() 3787 { 3788 var _this = this; 3789 return { 3790 exec: function(action, value) 3791 { 3792 _this.editor.ApplyTemplate(value); 3793 }, 3794 3795 state: function(action, value) 3796 { 3797 return _this.editor.GetTemplateId(); 3798 }, 3799 3800 value: BX.DoNothing 3801 }; 3802 }, 3803 3804 GetSelectNode: function() 3805 { 3806 var _this = this; 3807 return { 3808 exec: function(action, node) 3809 { 3810 if (!_this.editor.iframeView.IsFocused()) 3811 { 3812 _this.editor.iframeView.Focus(); 3813 } 3814 3815 if (node === false || (node && node.nodeName == 'BODY')) // Select all 3816 { 3817 if (_this.IsSupportedByBrowser('SelectAll')) 3818 { 3819 _this.document.execCommand('SelectAll'); 3820 } 3821 else 3822 { 3823 _this.editor.selection.SelectNode(node); 3824 } 3825 } 3826 else 3827 { 3828 _this.editor.selection.SelectNode(node); 3829 3830 } 3831 }, 3832 3833 state: BX.DoNothing, 3834 value: BX.DoNothing 3835 }; 3836 }, 3837 3838 GetUndoRedo: function(bUndo) 3839 { 3840 var _this = this; 3841 return { 3842 exec: function(action) 3843 { 3844 if (action == 'doUndo') 3845 { 3846 _this.editor.undoManager.Undo(); 3847 } 3848 else if(action == 'doRedo') 3849 { 3850 _this.editor.undoManager.Redo(); 3851 } 3852 }, 3853 state: BX.DoNothing, 3854 value: BX.DoNothing 3855 }; 3856 }, 3857 3858 GetUniversalFormatStyle: function() 3859 { 3860 var 3861 TEMP_CLASS = 'bx-tmp-ufs-class', 3862 STATUS_ATTR = 'data-bx-tmp-status', 3863 _this = this; 3864
3865 function getAncestorNodes(nodes) 3866 { 3867 var result = []; 3868 if (nodes && nodes.length > 0) 3869 { 3870 var 3871 i, len, sorted = [], node, status; 3872 for (i = 0, len = nodes.length; i < len; i++) 3873 { 3874 if (!_this.editor.util.CheckSurrogateNode(nodes[i]) && nodes[i].nodeName !== 'BR') 3875 { 3876 nodes[i].setAttribute(STATUS_ATTR, 'Y'); 3877 sorted.push({node: nodes[i], nesting: _this.editor.util.GetNodeDomOffset(nodes[i])}); 3878 } 3879 } 3880 sorted = sorted.sort(function(a, b){return a.nesting - b.nesting}); 3881 for (i = 0, len = sorted.length; i < len; i++) 3882 { 3883 node = sorted[i].node; 3884 status = node.getAttribute(STATUS_ATTR); 3885 if (status == 'Y' && !findUnIncludedNodes(node)) 3886 { 3887 // Prevent including all childs of this node to result 3888 BX.findChild(node, function(n) 3889 { 3890 if (n.nodeType == 1 && n.nodeName !== 'BR' && n.setAttribute) 3891 { 3892 n.setAttribute(STATUS_ATTR, n.className == TEMP_CLASS ? 'GET_RID_OF' : 'SKIP'); 3893 } 3894 return false; 3895 }, true, true); 3896 3897 result.push(node); 3898 } 3899 } 3900 } 3901 return result; 3902 } 3903 3904 function findUnIncludedNodes(node) 3905 { 3906 var res = BX.findChild(node, function(n) 3907 { 3908 return n.nodeType == 1 && n.nodeName !== 'BR' && n.getAttribute && n.getAttribute(STATUS_ATTR) !== 'Y'; 3909 }, true, false); 3910 return !!res; 3911 } 3912 3913 function adjustNodeStyle(node, className, style) 3914 { 3915 try 3916 { 3917 if (className !== false) 3918 { 3919 if (className == '') 3920 { 3921 node.removeAttribute('class'); 3922 } 3923 else 3924 { 3925 node.className = className; 3926 } 3927 } 3928 if (style !== false) 3929 { 3930 if (style == '') 3931 { 3932 node.removeAttribute('style'); 3933 } 3934 else 3935 { 3936 node.style.cssText = style; 3937 } 3938 } 3939 } 3940 catch(e) 3941 {} 3942 } 3943 3944 return { 3945 exec: function(action, value) 3946 { 3947 if (value.nodes && value.nodes.length > 0) 3948 { 3949 for (i = 0; i < value.nodes.length; i++) 3950 { 3951 adjustNodeStyle(value.nodes[i], value.className, value.style); 3952 } 3953 } 3954 else 3955 { 3956 _this.actions.formatInline.exec(action, value, "span", false, TEMP_CLASS); 3957 3958 if (document.querySelectorAll) 3959 { 3960 var tmpSpanNodes = _this.editor.GetIframeDoc().querySelectorAll('.' + TEMP_CLASS); 3961 if (tmpSpanNodes) 3962 { 3963 for (i = 0; i < tmpSpanNodes.length; i++) 3964 { 3965 node = tmpSpanNodes[i]; 3966 if (BX.util.trim(node.innerHTML) == '') 3967 { 3968 _this.editor.util.ReplaceWithOwnChildren(node); 3969 } 3970 } 3971 } 3972 } 3973 3974 var 3975 i, node, 3976 nodes = _this.actions.universalFormatStyle.state(action), 3977 existNodes = getAncestorNodes(nodes); 3978 3979 for (i = 0; i < existNodes.length; i++) 3980 { 3981 adjustNodeStyle(existNodes[i], value.className, value.style); 3982 } 3983 3984 if (document.querySelectorAll) 3985 { 3986 tmpSpanNodes = _this.editor.GetIframeDoc().querySelectorAll('.' + TEMP_CLASS); 3987 if (tmpSpanNodes) 3988 { 3989 for (i = 0; i < tmpSpanNodes.length; i++) 3990 { 3991 node = tmpSpanNodes[i]; 3992 if (node.getAttribute(STATUS_ATTR) == 'GET_RID_OF') 3993 { 3994 _this.editor.util.ReplaceWithOwnChildren(tmpSpanNodes[i]); 3995 } 3996 else 3997 { 3998 adjustNodeStyle(tmpSpanNodes[i], value.className, value.style); 3999 } 4000 } 4001 } 4002 } 4003 } 4004 }, 4005 state: function(action) 4006 { 4007 var range = _this.editor.selection.GetRange(); 4008 4009 if (range) 4010 { 4011 var 4012 textNodes, textNode, node, 4013 nodes = range.getNodes([1]); 4014 4015 // Range is collapsed or text node is selected 4016 if (nodes.length == 0) 4017 { 4018 textNodes = range.getNodes([3]); 4019 if (textNodes && textNodes.length == 1) 4020 { 4021 textNode = textNodes[0]; 4022 } 4023 4024 if (!textNode && range.startContainer == range.endContainer) 4025 { 4026 if (range.startContainer.nodeType == 3) 4027 { 4028 textNode = range.startContainer; 4029 } 4030 else 4031 { 4032 _this.editor.selection.SelectNode(range.startContainer); 4033 nodes = [range.startContainer]; 4034 } 4035 } 4036 4037 if (textNode && nodes.length == 0) 4038 { 4039 node = textNode.parentNode; 4040 if (node) 4041 { 4042 nodes = [node]; 4043 } 4044 } 4045 } 4046 return nodes; 4047 } 4048 }, 4049 value: BX.DoNothing 4050 }; 4051 }, 4052 4053 GetSubSup: function(type) 4054 { 4055 var _this = this; 4056 4057 type = type == 'sup' ? 'sup' : 'sub'; 4058 4059 return { 4060 exec: function(action, value) 4061 { 4062 return _this.actions.formatInline.exec(action, value, type); 4063 }, 4064 state: function(action, value) 4065 { 4066 return _this.actions.formatInline.state(action, value, type); 4067 }, 4068 value: BX.DoNothing 4069 }; 4070 }, 4071 4072 GetQuote: function() 4073 { 4074 var 4075 range, 4076 externalSelection, 4077 _this = this; 4078 4079 function checkNode(n) 4080 { 4081 return n && n.className == 'bxhtmled-quote' && n.nodeName == 'BLOCKQUOTE'; 4082 } 4083 4084 function setExternalSelection(text) 4085 { 4086 externalSelection = text; 4087 } 4088 function getExternalSelection() 4089 { 4090 return externalSelection; 4091 } 4092 function setRange(rng) 4093 { 4094 return range = rng; 4095 } 4096 function setExternalSelectionFromRange(range) 4097 { 4098 range = range || _this.editor.selection.GetRange(_this.editor.selection.GetSelection(document)); 4099 4100 if (range) 4101 { 4102 const nodeTypeText = 3; 4103 const isTextSelected = range.startContainer.nodeType === nodeTypeText && range.startContainer === range.endContainer; 4104 const isFileNameSelected = isTextSelected && range.startContainer.parentNode.hasAttribute('bx-attach-file-id'); 4105 if (isFileNameSelected) 4106 { 4107 // select node next to text to put the <a> tag to a quote 4108 const startContainer = range.startContainer; 4109 range.setStartBefore(startContainer.parentElement); 4110 range.setEndAfter(startContainer); 4111 } 4112 var tmpDiv; 4113 // mantis:64329 4114 if (range.startContainer == range.endContainer && range.startOffset == 0 && range.endOffset == range.endContainer.length && range.startContainer.parentNode && range.startContainer.parentNode.nodeName == 'A' && range.startContainer.parentNode.href) 4115 { 4116 tmpDiv = BX.create('DIV', {html: range.startContainer.parentNode.href}, _this.editor.GetIframeDoc()); 4117 } 4118 else 4119 { 4120 var html = range.toHtml(); 4121 html = html.replace(/<br.*?>/ig, "#BX_BR#"); 4122 tmpDiv = BX.create('DIV', {html: BX.util.htmlspecialchars(html)}, _this.editor.GetIframeDoc()); 4123 } 4124 4125 var extSel = _this.editor.util.GetTextContentEx(tmpDiv); 4126 extSel = extSel.replace(/#BX_BR#/ig, "<br>"); 4127 setExternalSelection(extSel); 4128 BX.remove(tmpDiv); 4129 } 4130 else 4131 { 4132 setExternalSelection(''); 4133 } 4134 } 4135 4136 function checkSpaceAfterQuotes(target) 4137 { 4138 var 4139 i, quote, 4140 quotes = target.querySelectorAll("blockquote.bxhtmled-quote"); 4141 4142 for (i = 0; i < quotes.length; i++) 4143 { 4144 quote = quotes[i]; 4145 if (quote.nextSibling === null) 4146 { 4147 _this.editor.util.InsertAfter(_this.editor.util.GetInvisibleTextNode(), quote); 4148 } 4149 } 4150 } 4151 4152 return { 4153 exec: function(action) 4154 { 4155 var 4156 res = false, 4157 sel = getExternalSelection(); 4158 4159 if (_this.editor.bbCode && _this.editor.synchro.IsFocusedOnTextarea()) 4160 { 4161 _this.editor.textareaView.Focus(); 4162 if(sel) 4163 { 4164 res = _this.editor.textareaView.WrapWith(false, false, "[QUOTE]" + sel + "[/QUOTE]"); 4165 } 4166 else 4167 {
4168 res = _this.actions.formatBbCode.exec(action, {tag: 'QUOTE'}); 4169 } 4170 } 4171 else 4172 { 4173 range = _this.editor.iframeView.GetSelection().getRangeAt(0); 4174 _this.editor.iframeView.Focus(); 4175 if (range) 4176 { 4177 _this.editor.selection.SetSelection(range); 4178 } 4179 4180 if(sel) 4181 { 4182 var quoteId = 'bxq_' + Math.round(Math.random() * 1000000); 4183 4184 _this.editor.InsertHtml('<blockquote id="' + quoteId + '" class="bxhtmled-quote">' + sel + '</blockquote>' + _this.editor.INVISIBLE_SPACE, range); 4185 4186 setTimeout(function() 4187 { 4188 var quote = _this.editor.GetIframeElement(quoteId); 4189 if (quote) 4190 { 4191 var prev = quote.previousSibling; 4192 if (prev && prev.nodeType == 3 && _this.editor.util.IsEmptyNode(prev) && 4193 prev.previousSibling && prev.previousSibling.nodeName == 'BR') 4194 { 4195 BX.remove(prev); 4196 } 4197 quote.removeAttribute('id'); 4198 } 4199 }, 0); 4200 } 4201 else 4202 { 4203 if (!range && _this.editor.selection.lastRange) 4204 range = _this.editor.selection.lastRange; 4205 4206 res = _this.actions.formatBlock.exec('formatBlock', 'blockquote', 'bxhtmled-quote', false, {range: range}); 4207 } 4208 4209 if(!sel) 4210 _this.editor.selection.ScrollIntoView(); 4211 } 4212 4213 range = null; 4214 return res; 4215 }, 4216 state: function() 4217 { 4218 return _this.actions.formatBlock.state('formatBlock', 'blockquote', 'bxhtmled-quote'); 4219 }, 4220 value: BX.DoNothing, 4221 setExternalSelectionFromRange : setExternalSelectionFromRange, 4222 setExternalSelection : setExternalSelection, 4223 getExternalSelection : getExternalSelection, 4224 checkSpaceAfterQuotes: checkSpaceAfterQuotes, 4225 setRange : setRange, 4226 checkNode: checkNode 4227 }; 4228 }, 4229 4230 GetCode: function() 4231 { 4232 var _this = this; 4233 return { 4234 exec: function(action) 4235 { 4236 // Iframe 4237 if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea()) 4238 { 4239 var codeElement = _this.actions.code.state(); 4240 if (codeElement) 4241 { 4242 var innerHtml = BX.util.trim(codeElement.innerHTML); 4243 if (innerHtml == '<br>' || innerHtml === '') 4244 { 4245 _this.editor.selection.SetAfter(codeElement); 4246 BX.remove(codeElement); 4247 } 4248 else 4249 { 4250 _this.editor.selection.ExecuteAndRestoreSimple(function() 4251 { 4252 codeElement.className = ''; 4253 codeElement = _this.editor.util.RenameNode(codeElement, 'P'); 4254 }); 4255 } 4256 } 4257 else 4258 { 4259 _this.actions.formatBlock.exec('formatBlock', 'pre', 'bxhtmled-code'); 4260 } 4261 } 4262 else // bbcode + textarea 4263 { 4264 return _this.actions.formatBbCode.exec(action, {tag: 'CODE'}); 4265 } 4266 }, 4267 state: function() 4268 { 4269 return _this.actions.formatBlock.state('formatBlock', 'pre', 'bxhtmled-code'); 4270 }, 4271 value: BX.DoNothing 4272 }; 4273 }, 4274 4275 GetInsertSmile: function() 4276 { 4277 var _this = this; 4278 return { 4279 exec: function(action, value) 4280 { 4281 var smile = _this.editor.smilesIndex[value]; 4282 if (_this.editor.bbCode && _this.editor.synchro.IsFocusedOnTextarea()) 4283 { 4284 _this.editor.textareaView.Focus(); 4285 _this.editor.textareaView.WrapWith(false, false, " " + smile.code + " "); 4286 } 4287 else 4288 { 4289 _this.editor.iframeView.Focus(); 4290 if (smile) 4291 { 4292 var smileImg = BX.create("IMG", {props: 4293 { 4294 src: smile.path, 4295 title: smile.name || smile.code 4296 }}, _this.editor.iframeView.document); 4297 _this.editor.SetBxTag(smileImg, {tag: "smile", params: smile}); 4298 if (smile.width) 4299 smileImg.style.width = parseInt(smile.width) + 'px'; 4300 if (smile.height) 4301 smileImg.style.height = parseInt(smile.height) + 'px'; 4302 _this.editor.selection.InsertNode(smileImg); 4303 4304 var textBefore = _this.editor.iframeView.document.createTextNode(' '); 4305 smileImg.parentNode.insertBefore(textBefore, smileImg); 4306 var textAfer = BX.create("SPAN", {html: ' '}, _this.editor.iframeView.document); 4307 _this.editor.util.InsertAfter(textAfer, smileImg); 4308 _this.editor.selection.SetAfter(textAfer); 4309 setTimeout(function(){_this.editor.selection.SetAfter(textAfer);}, 10); 4310 } 4311 } 4312 }, 4313 state: BX.DoNothing, 4314 value: BX.DoNothing 4315 }; 4316 }, 4317 4318 GetTableOperation: function() 4319 { 4320 var 4321 _this = this, 4322 newCellHtml = ' '; 4323 4324 function createTableMatrix(oTable) 4325 { 4326 var aRows = oTable.rows; 4327 // Row and Column counters. 4328 var 4329 arMatrix = [], 4330 i, // index 4331 c, j, oCell, rs, cs, 4332 iColSpan, iRowSpan, 4333 r = -1; // row 4334 4335 for (i = 0; i < aRows.length; i++) 4336 { 4337 r++; 4338 if (!arMatrix[r]) 4339 { 4340 arMatrix[r] = []; 4341 } 4342 4343 c = -1; 4344 4345 for (j = 0; j < aRows[i].cells.length; j++) 4346 { 4347 oCell = aRows[i].cells[j]; 4348 4349 c++; 4350 while (arMatrix[r][c]) 4351 { 4352 c++; 4353 } 4354 4355 iColSpan = isNaN(oCell.colSpan) ? 1 : oCell.colSpan; 4356 iRowSpan = isNaN(oCell.rowSpan) ? 1 : oCell.rowSpan; 4357 4358 for(rs = 0; rs < iRowSpan; rs++) 4359 { 4360 if (!arMatrix[r + rs]) 4361 { 4362 arMatrix[r + rs] = []; 4363 } 4364 4365 for (cs = 0;
4365 cs < iColSpan; cs++) 4366 { 4367 arMatrix[r + rs][c + cs] = aRows[i].cells[j]; 4368 } 4369 } 4370 4371 c += iColSpan - 1; 4372 } 4373 } 4374 return arMatrix; 4375 } 4376 4377 function getIndexes(oCell, arMatrix) 4378 { 4379 var 4380 i, j, 4381 arIndexes = []; 4382 4383 for (i = 0; i < arMatrix.length; i++) 4384 { 4385 for (j = 0, l = arMatrix[i].length; j < l; j++) 4386 { 4387 if (arMatrix[i][j] == oCell) 4388 { 4389 arIndexes.push({r : i, c : j}); 4390 } 4391 } 4392 } 4393 return arIndexes; 4394 } 4395 4396 function getCellIndexInfo(ind) 4397 { 4398 var 4399 rows = [], cols = [], 4400 indInfo = { 4401 cells: 0 4402 }, 4403 ii; 4404 4405 for(ii = 0; ii < ind.length; ii++) 4406 { 4407 indInfo.cells++; 4408 indInfo.maxRow = ii === 0 ? ind[ii].r : Math.max(ind[ii].r, indInfo.maxRow); 4409 indInfo.minRow = ii === 0 ? ind[ii].r : Math.min(ind[ii].r, indInfo.minRow); 4410 indInfo.maxCol = ii === 0 ? ind[ii].c : Math.max(ind[ii].c, indInfo.maxCol); 4411 indInfo.minCol = ii === 0 ? ind[ii].c : Math.min(ind[ii].c, indInfo.minCol); 4412 4413 if (!BX.util.in_array(ind[ii].r, rows)) 4414 rows.push(ind[ii].r); 4415 4416 if (!BX.util.in_array(ind[ii].c, cols)) 4417 cols.push(ind[ii].c); 4418 } 4419 4420 indInfo.rows = rows.length; 4421 indInfo.cols = cols.length; 4422 4423 return indInfo; 4424 } 4425 4426 function findAndPushAndUniqueCell(cells, node, table) 4427 { 4428 if (node) 4429 { 4430 node = getParentCell(node, table); 4431 4432 if (node && !BX.util.in_array(node, cells)) 4433 cells.push(node); 4434 } 4435 4436 return cells; 4437 } 4438 4439 function getSelectedCells(range, table) 4440 { 4441 var cells = [], cell; 4442 4443 if (BX.browser.IsFirefox()) 4444 { 4445 var 4446 i, rng, start, end, 4447 sel = rangy.getNativeSelection(_this.editor.sandbox.GetWindow()); 4448 4449 for (i = 0; i < sel.rangeCount; i++) 4450 { 4451 rng = sel.getRangeAt(i); 4452 4453 start = rng.startContainer.nodeType === 1 ? rng.startContainer.childNodes[rng.startOffset] : rng.startContainer; 4454 end = rng.endContainer.nodeType === 1 ? rng.endContainer.childNodes[rng.endOffset] : rng.endContainer; 4455 4456 cells = findAndPushAndUniqueCell(cells, start, table); 4457 cells = findAndPushAndUniqueCell(cells, end, table); 4458 } 4459 } 4460 else 4461 { 4462 if (range.collapsed) 4463 { 4464 cell = getParentCell(range.startContainer); 4465 cells = findAndPushAndUniqueCell(cells, cell, table); 4466 } 4467 else 4468 { 4469 var nodes = range.getNodes([1]); 4470 for (i = 0; i < nodes.length; i++) 4471 { 4472 if (nodes[i].nodeName == 'TD' || nodes[i].nodeName == 'TH') 4473 { 4474 cells = findAndPushAndUniqueCell(cells, nodes[i], table); 4475 } 4476 } 4477 } 4478 } 4479 4480 return cells; 4481 } 4482 4483 function insertColumn(element, table, actionType) 4484 { 4485 var td = BX.findParent(element, {tag: 'TD'}); 4486 4487 if (!td) 4488 return; 4489 4490 var 4491 tr = td.parentNode, 4492 cellInd = actionType == 'insertColumnLeft' ? td.cellIndex : td.cellIndex + 1, 4493 rowInd = tr.rowIndex, 4494 mtx = createTableMatrix(table), 4495 arInd = getIndexes(td, mtx); 4496 4497 tr.insertCell(cellInd).innerHTML = newCellHtml; 4498 4499 var 4500 r, ind, i, c, j, 4501 curFullCellInd = actionType == 'insertColumnLeft' ? arInd[0].c : arInd[0].c + 1; 4502 4503 for (j = 0; j < table.rows.length; j++) 4504 { 4505 r = table.rows[j]; 4506 if (r.rowIndex == rowInd) 4507 { 4508 continue; 4509 } 4510 4511 ind = 0; 4512 i = 0; 4513 for(i = 0; i < r.cells.length; i++) 4514 { 4515 c = r.cells[i]; 4516 arInd = getIndexes(c, mtx); 4517 if (arInd[0].c >= curFullCellInd) 4518 { 4519 ind = c.cellIndex; 4520 break; 4521 } 4522 ind = i + 1; 4523 } 4524 4525 r.insertCell(ind).innerHTML = ' '; 4526 } 4527 } 4528 4529 function insertRow(element, table, actionType) 4530 { 4531 var tr = BX.findParent(element, {tag: 'TR'}); 4532 if (!tr || !table) 4533 return; 4534 4535 var 4536 i, newCell, 4537 rowInd = actionType == 'insertRowUpper' ? tr.rowIndex : tr.rowIndex + 1, 4538 newRow = table.insertRow(rowInd); 4539 4540 for(i = 0; i < tr.cells.length; i++) 4541 { 4542 newCell = newRow.insertCell(i); 4543 newCell.innerHTML = newCellHtml; 4544 newCell.colSpan = tr.cells[i].colSpan; 4545 } 4546 } 4547 4548 function insertCell(element, table, actionType) 4549 { 4550 var td = getParentCell(element, table); 4551 if (!td || !table) 4552 return; 4553 4554 var 4555 tr = td.parentNode, 4556 cellInd = actionType == 'insertCellLeft' ? td.cellIndex : td.cellIndex + 1; 4557 4558 tr.insertCell(cellInd).innerHTML = newCellHtml; 4559 } 4560 4561 function getParentCell(node, table) 4562 { 4563 if (node.nodeName == 'TD' || node.nodeName == 'TH') 4564 return node; 4565 4566 return BX.findParent(node, function(n) 4567 { 4568 return n.nodeName == 'TD' || n.nodeName == 'TH' 4569 }, table); 4570 } 4571 4572 4573 function getMergeState(cells, table) 4574 { 4575 var 4576 indInfo, i, 4577 mtx = createTableMatrix(table), 4578 firstIndInfo = getCellIndexInfo(getIndexes(cells[0], mtx)), 4579 lastIndInfo = firstIndInfo, 4580 gaps = false, 4581 sameRow = true, 4582 sameCol = true; 4583
4584 for(i = 1; i < cells.length; i++) 4585 { 4586 indInfo = getCellIndexInfo(getIndexes(cells[i], mtx)); 4587 sameRow = sameRow && indInfo.rows == firstIndInfo.rows && indInfo.maxRow == firstIndInfo.maxRow && indInfo.minRow == firstIndInfo.minRow; 4588 sameCol = sameCol && indInfo.cols == firstIndInfo.cols && indInfo.maxCol == firstIndInfo.maxCol && indInfo.minCol == firstIndInfo.minCol; 4589 4590 gaps = gaps || 4591 (sameRow && Math.abs(indInfo.minCol - lastIndInfo.maxCol) > 1) 4592 || 4593 (sameCol && Math.abs(indInfo.minRow - lastIndInfo.maxRow) > 1) 4594 || 4595 !sameRow && !sameCol; 4596 4597 lastIndInfo = indInfo; 4598 } 4599 4600 return { 4601 sameCol : sameCol, 4602 sameRow: sameRow, 4603 gaps: gaps 4604 }; 4605 } 4606 4607 function canBeMerged(cells, range, table) 4608 { 4609 if (!cells) 4610 cells = getSelectedCells(range, table); 4611 4612 if (!cells || cells.length < 2) 4613 return false; 4614 4615 var mergeState = getMergeState(cells, table); 4616 return !mergeState.gaps && (!mergeState.sameRow && mergeState.sameCol || mergeState.sameRow && !mergeState.sameCol); 4617 } 4618 4619 function canBeMergedWithRight(range, table) 4620 { 4621 var cells = getSelectedCells(range, table); 4622 if (!cells || cells.length !== 1) 4623 return false; 4624 4625 var 4626 mtx = createTableMatrix(table), 4627 ind = getIndexes(cells[0], mtx); 4628 4629 if (ind.length < 1) 4630 return false; 4631 4632 var 4633 i, rightTd, 4634 maxCol = ind[ind.length - 1].c, // Max col 4635 res = true, c; 4636 4637 for (i = 0; i < ind.length; i++) 4638 { 4639 if (ind[i].c == maxCol) 4640 { 4641 if (mtx[ind[i].r] && mtx[ind[i].r][ind[i].c + 1]) 4642 { 4643 c = mtx[ind[i].r][ind[i].c + 1]; 4644 4645 if (rightTd === undefined) 4646 rightTd = c; 4647 else if (rightTd !== c) 4648 res = false; 4649 } 4650 else 4651 { 4652 res = false; 4653 } 4654 } 4655 } 4656 4657 res = res && rightTd && canBeMerged([cells[0], rightTd], range, table); 4658 4659 return res; 4660 } 4661 4662 function canBeMergedWithBottom(range, table) 4663 { 4664 var cells = getSelectedCells(range, table); 4665 if (!cells || cells.length !== 1) 4666 return false; 4667 4668 var 4669 mtx = createTableMatrix(table), 4670 ind = getIndexes(cells[0], mtx); 4671 4672 if (ind.length < 1) 4673 return false; 4674 4675 var 4676 i, bottomTd, 4677 maxRow = ind[ind.length - 1].r, // Max row 4678 res = true, c; 4679 4680 for (i = 0; i < ind.length; i++) 4681 { 4682 if (ind[i].r == maxRow) 4683 { 4684 if (mtx[maxRow + 1] && mtx[maxRow + 1][ind[i].c]) 4685 { 4686 c = mtx[maxRow + 1][ind[i].c]; 4687 if (bottomTd === undefined) 4688 bottomTd = c; 4689 else if (bottomTd !== c) 4690 res = false; 4691 } 4692 else 4693 { 4694 res = false; 4695 } 4696 } 4697 } 4698 4699 res = res && bottomTd && canBeMerged([cells[0], bottomTd], range, table); 4700 4701 return res; 4702 } 4703 4704 function mergeCells(range, table, cells) 4705 { 4706 if (!cells) 4707 cells = getSelectedCells(range, table); 4708 4709 if (cells.length < 2) 4710 return; 4711 4712 var 4713 mergeState = getMergeState(cells, table), 4714 i, tr, 4715 newCellColSpan = 0, 4716 newCellRowSpan = 0, 4717 newCellContent = ''; 4718 4719 // Horizontal cells 4720 if (mergeState.sameRow && !mergeState.sameCol && !mergeState.gaps) 4721 { 4722 for(i = 0; i < cells.length; i++) 4723 { 4724 newCellContent += ' ' + BX.util.trim(cells[i].innerHTML); 4725 tr = cells[i].parentNode; 4726 newCellColSpan += cells[i].colSpan; 4727 4728 if (i > 0) 4729 tr.removeChild(cells[i]); 4730 } 4731 4732 cells[0].colSpan = newCellColSpan; 4733 cells[0].innerHTML = BX.util.trim(newCellContent); 4734 } 4735 // vertical cells 4736 else if (!mergeState.sameRow && mergeState.sameCol && !mergeState.gaps) 4737 { 4738 for(i = 0; i < cells.length; i++) 4739 { 4740 newCellContent += ' ' + BX.util.trim(cells[i].innerHTML); 4741 tr = cells[i].parentNode; 4742 newCellRowSpan += cells[i].rowSpan; 4743 4744 if (i > 0) 4745 tr.removeChild(cells[i]); 4746 } 4747 4748 cells[0].rowSpan = newCellRowSpan; 4749 cells[0].innerHTML = BX.util.trim(newCellContent); 4750 } 4751 else 4752 { 4753 alert(BX.message('BXEdTableMergeError')); 4754 } 4755 } 4756 4757 function mergeRightCell(range, table) 4758 { 4759 var cells = getSelectedCells(range, table); 4760 4761 if (!cells || cells.length !== 1) 4762 return false;
4763 4764 var tr = BX.findParent(cells[0], {tag: 'TR'}, table); 4765 4766 if (cells[0].cellIndex < tr.cells.length - 1) 4767 { 4768 cells.push(tr.cells[cells[0].cellIndex + 1]); 4769 } 4770 4771 return mergeCells(range, table, cells); 4772 } 4773 4774 function mergeBottomCell(range, table) 4775 { 4776 var cells = getSelectedCells(range, table); 4777 if (!cells || cells.length !== 1) 4778 return false; 4779 4780 var 4781 mtx = createTableMatrix(table), 4782 ind = getIndexes(cells[0], mtx), 4783 i, bottomTd, 4784 maxRow = ind[ind.length - 1].r, // Max row 4785 res = true, c; 4786 4787 for (i = 0; i < ind.length; i++) 4788 { 4789 if (ind[i].r == maxRow) 4790 { 4791 if (mtx[maxRow + 1] && mtx[maxRow + 1][ind[i].c]) 4792 { 4793 c = mtx[maxRow + 1][ind[i].c]; 4794 if (bottomTd === undefined) 4795 bottomTd = c; 4796 else if (bottomTd !== c) 4797 res = false; 4798 } 4799 else 4800 { 4801 res = false; 4802 } 4803 } 4804 } 4805 4806 if (res) 4807 { 4808 cells.push(bottomTd); 4809 return mergeCells(range, table, cells); 4810 } 4811 } 4812 4813 function mergeRow(range, table) 4814 { 4815 var cells = getSelectedCells(range, table); 4816 if (!cells || cells.length !== 1) 4817 return false; 4818 4819 var 4820 i, newCells = [], 4821 tr = cells[0].parentNode; 4822 4823 for(i = 0; i < tr.cells.length; i++) 4824 { 4825 newCells.push(tr.cells[i]); 4826 } 4827 4828 return mergeCells(range, table, newCells); 4829 } 4830 4831 function mergeColumn(range, table) 4832 { 4833 var cells = getSelectedCells(range, table); 4834 if (!cells || cells.length !== 1) 4835 return false; 4836 4837 var 4838 i, j, newCells = [], 4839 mtx = createTableMatrix(table), 4840 indInfo = getCellIndexInfo(getIndexes(cells[0], mtx)); 4841 4842 for (i = 0; i < mtx.length; i++) 4843 { 4844 for (j = indInfo.minCol; j <= indInfo.minCol; j++) 4845 { 4846 newCells = findAndPushAndUniqueCell(newCells, mtx[i][j], table); 4847 } 4848 } 4849 4850 return mergeCells(range, table, newCells); 4851 } 4852 4853 function splitHorizontally(range, table) 4854 { 4855 var cells = getSelectedCells(range, table); 4856 4857 if (!cells || cells.length != 1) 4858 return false; 4859 4860 var 4861 i, j, 4862 realInd = 0, 4863 realIndI, 4864 trI, 4865 newCell, 4866 colSpan = cells[0].colSpan, 4867 rowSpan = cells[0].rowSpan, 4868 tr = cells[0].parentNode; 4869 4870 for(i = 0; i <= cells[0].cellIndex; i++) 4871 realInd += tr.cells[i].colSpan; 4872 4873 if (colSpan > 1) 4874 { 4875 cells[0].colSpan--; 4876 } 4877 else 4878 { 4879 for(j = 0; j < table.rows.length; j++) 4880 { 4881 if (j == tr.rowIndex || j >= tr.rowIndex && j < tr.rowIndex + rowSpan) 4882 continue; 4883 4884 realIndI = 0; 4885 trI = table.rows[j]; 4886 4887 i = 0; 4888 while (realIndI < realInd && i < trI.cells.length) 4889 realIndI += trI.cells[i++].colSpan; 4890 4891 i--; 4892 trI.cells[i].colSpan += 1; 4893 4894 // mantis: 71909 4895 if (trI.cells[i].rowSpan > 1) 4896 j = j + trI.cells[i].rowSpan - 1; 4897 } 4898 } 4899 newCell = tr.insertCell(cells[0].cellIndex + 1); 4900 newCell.rowSpan = cells[0].rowSpan; 4901 newCell.innerHTML = newCellHtml; 4902 } 4903 4904 function splitVertically(range, table) 4905 { 4906 var cells = getSelectedCells(range, table); 4907 4908 if (!cells || cells.length != 1) 4909 return false; 4910 4911 var 4912 i, r, c, row, cell, 4913 indI, 4914 fullRowInd, realCellInd, 4915 mtx = createTableMatrix(table), 4916 ind = getIndexes(cells[0], mtx), 4917 tr = cells[0].parentNode, 4918 //maxCellCount = arTMX[0].length; //max count of cell in table 4919 curRowIndex = tr.rowIndex, 4920 curCellIndex = cells[0].cellIndex, 4921 curFullRowInd = ind[0].r, 4922 curFullCellInd = ind[0].c, 4923 bOneW = true, 4924 bOneH = true; 4925 4926 for(i = 1; i < ind.length; i++) 4927 { 4928 if (ind[i].r != curFullRowInd) 4929 bOneH = false; 4930 if (ind[i].c != curFullCellInd) 4931 bOneW = false; 4932 } 4933 4934 if (bOneH) // if rowSpan == 1 and we have to split this cell 4935 { 4936 var 4937 newRow = table.insertRow(tr.rowIndex + 1), 4938 newCell = newRow.insertCell(-1); 4939 4940 newCell.innerHTML = newCellHtml; 4941 if (!bOneW) 4942 newCell.colSpan = cells[0].colSpan; 4943 4944 4945 for(r = 0; r <= curFullRowInd; r++) 4946 { 4947 row = table.rows[r]; 4948 for(c = 0; c < row.cells.length; c++) 4949 { 4950 cell = row.cells[c]; 4951 if (r == curRowIndex && c == curCellIndex) 4952 continue; 4953 4954 fullRowInd = r; // oRow.rowIndex 4955 if (cell.rowSpan > 1) 4956 fullRowInd += cell.rowSpan - 1; 4957 4958 if (fullRowInd >= curFullRowInd) 4959 cell.rowSpan++; 4960 } 4961 } 4962 } 4963 else // If cell has rowspan > 1 4964 {
4965 row = table.rows[curRowIndex + --cells[0].rowSpan]; 4966 realCellInd = false; 4967 for(c = 0; c < row.cells.length; c++) 4968 { 4969 indI = getIndexes(row.cells[c], mtx); 4970 for(i = 0; i < indI.length; i++) 4971 { 4972 if (indI[i].c > curCellIndex) 4973 realCellInd = 0; 4974 else if (indI[i].c + 1 == curCellIndex) 4975 realCellInd = row.cells[c].cellIndex + 1; 4976 4977 if (realCellInd !== false) 4978 break; 4979 } 4980 } 4981 4982 newCell = row.insertCell(realCellInd); 4983 newCell.innerHTML = newCellHtml; 4984 if (!bOneW) 4985 newCell.colSpan = cells[0].colSpan; 4986 } 4987 } 4988 4989 // Remove 4990 function removeColumn(range, table) 4991 { 4992 var cells = getSelectedCells(range, table); 4993 if (!cells || cells.length != 1) 4994 return false; 4995 var cell = cells[0]; 4996 4997 if (!cell) 4998 return false; 4999 5000 var 5001 tr, td, 5002 mtx = createTableMatrix(table), 5003 ind = getIndexes(cell, mtx), 5004 j; 5005 5006 for (j = 0; j < mtx.length; j++) 5007 { 5008 td = mtx[j][ind[0].c]; 5009 if (td && td.parentNode) 5010 { 5011 tr = td.parentNode; 5012 BX.remove(td); 5013 if (tr.cells.length == 0) 5014 BX.remove(tr); 5015 } 5016 } 5017 5018 if (table.rows.length == 0) 5019 BX.remove(table); 5020 } 5021 5022 function removeRow(range, table) 5023 { 5024 var cells = getSelectedCells(range, table); 5025 if (!cells || cells.length != 1) 5026 return false; 5027 var cell = cells[0]; 5028 5029 5030 if (!cell) 5031 return false 5032 5033 BX.remove(cell.parentNode); 5034 5035 if (table.rows.length == 0) 5036 BX.remove(table); 5037 } 5038 5039 function removeCell(range, table, cell) 5040 { 5041 if (!cell) 5042 { 5043 var cells = getSelectedCells(range, table); 5044 if (!cells || cells.length != 1) 5045 return false; 5046 cell = cells[0]; 5047 } 5048 5049 if (!cell) 5050 return false; 5051 5052 var tr = cell.parentNode; 5053 BX.remove(cell); 5054 5055 if (tr.cells.length == 0) 5056 BX.remove(tr); 5057 5058 if (table.rows.length == 0) 5059 BX.remove(table); 5060 } 5061 5062 function removeSelectedCells(range, table) 5063 { 5064 var cells = getSelectedCells(range, table); 5065 if (!cells || cells.length == 1) 5066 return false; 5067 5068 var i, tr; 5069 for (i = 0; i < cells.length; i++) 5070 { 5071 tr = cells[i].parentNode; 5072 BX.remove(cells[i]); 5073 5074 if (tr.cells.length == 0) 5075 BX.remove(tr); 5076 } 5077 5078 if (table.rows.length == 0) 5079 BX.remove(table); 5080 } 5081 5082 return { 5083 exec: function(action, value) 5084 { 5085 var node = value.range.commonAncestorContainer; 5086 5087 switch (value.actionType) 5088 { 5089 // Insert 5090 case 'insertColumnLeft': 5091 case 'insertColumnRight': 5092 insertColumn(node, value.tableNode, value.actionType); 5093 break; 5094 case 'insertRowUpper': 5095 case 'insertRowLower': 5096 insertRow(node, value.tableNode, value.actionType); 5097 break; 5098 case 'insertCellLeft': 5099 case 'insertCellRight': 5100 insertCell(node, value.tableNode, value.actionType); 5101 break; 5102 5103 // Remove 5104 case 'removeColumn': 5105 removeColumn(value.range, value.tableNode); 5106 break; 5107 case 'removeRow': 5108 removeRow(value.range, value.tableNode); 5109 break; 5110 case 'removeCell': 5111 removeCell(value.range, value.tableNode); 5112 break; 5113 case 'removeSelectedCells': 5114 removeSelectedCells(value.range, value.tableNode); 5115 break; 5116 5117 // Merge 5118 case 'mergeSelectedCells': 5119 mergeCells(value.range, value.tableNode); 5120 break; 5121 case 'mergeRightCell': 5122 mergeRightCell(value.range, value.tableNode); 5123 break; 5124 case 'mergeBottomCell': 5125 mergeBottomCell(value.range, value.tableNode); 5126 break; 5127 case 'mergeRow': 5128 mergeRow(value.range, value.tableNode); 5129 break; 5130 case 'mergeColumn': 5131 mergeColumn(value.range, value.tableNode); 5132 break; 5133 5134 // split 5135 case 'splitHorizontally': 5136 splitHorizontally(value.range, value.tableNode); 5137 break; 5138 case 'splitVertically': 5139 splitVertically(value.range, value.tableNode); 5140 break; 5141 } 5142 5143 }, 5144 state: BX.DoNothing, 5145 value: BX.DoNothing, 5146 getSelectedCells: getSelectedCells, 5147 canBeMerged: canBeMerged, 5148 canBeMergedWithRight: canBeMergedWithRight, 5149 canBeMergedWithBottom: canBeMergedWithBottom 5150 }; 5151 }, 5152 5153
5154 GetFormatBbCode: function() 5155 { 5156 var _this = this; 5157 return { 5158 view: 'textarea', 5159 exec: function(action, params) 5160 { 5161 var 5162 value = params.value, 5163 tag = params.tag.toUpperCase(), 5164 tag_end = tag; 5165 5166 if (tag == 'FONT' || tag == 'COLOR' || tag == 'SIZE') 5167 { 5168 tag += "=" + value; 5169 } 5170 5171 if(params.singleTag === true) 5172 _this.editor.textareaView.WrapWith("[", "]", tag); 5173 else 5174 _this.editor.textareaView.WrapWith("[" + tag + "]", "[/" + tag_end + "]"); 5175 }, 5176 state: BX.DoNothing, 5177 value: BX.DoNothing 5178 }; 5179 } 5180 }; 5181 5182 window.BXEditorActions = BXEditorActions; 5183})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.