Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
wysiwyg.inc
1 <?php
32 class wysiwyg
33 {
34 
41  var $name = 'wysiwyg';
42 
50  var $body_type = 'iframe';
51 
59  var $init_onload = TRUE;
60 
61 
68  var $show_status_bar = TRUE;
69 
76  var $web_path = '';
77 
84  var $stylesheet = '';
85 
92  var $width = 'auto';
93 
100  var $height = 'auto';
101 
108  var $relative_href_checks = Array();
109 
116  var $absolute_url_checks = Array();
117 
124  var $textarea_extras = '';
125 
132  var $contents = '';
133 
140  var $_plugins = Array();
141 
148  var $_loaded_plugins = Array();
149 
156  var $_group_open = FALSE;
157 
166  var $init = FALSE;
167 
174  var $dir_attribute = '';
175 
176 
184  function wysiwyg($name='wysiwyg', $web_path='')
185  {
186  $this->name = $name;
187  $this->web_path = $web_path;
188  global $ROOT_PATH;
189  $ROOT_PATH = realpath(dirname(__FILE__)).'/';
190 
191  }//end constructor
192 
193 
200  function paint()
201  {
202 
203  global $WYSIWYG_INIT;
204  if (!$WYSIWYG_INIT) {
205  ?>
206  <script type="text/javascript">
207  //
208  // htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
209  // This copyright notice MUST stay intact for use (see license.txt).
210  //
211  // A free WYSIWYG editor replacement for <textarea> fields.
212  // For full source code and docs, visit http://www.interactivetools.com/
213  //
214  // Version 3.0 developed by Mihai Bazon for InteractiveTools.
215  // http://students.infoiasi.ro/~mishoo
216  //
217  // Modifications for PHP Plugin Based System
218  // developed by Greg Sherwood for Squiz.Net.
219  // http://www.squiz.net/
220  // greg@squiz.net
221  //
222 
223  // Creates a new HTMLArea object. Tries to replace the textarea with the given
224  // ID with it.
225  function HTMLArea(textarea, config) {
226  if (HTMLArea.checkSupportedBrowser()) {
227  if (typeof config == "undefined") {
228  eval("this.config = new HTMLArea.Config_" + textarea + "();");
229  } else {
230  this.config = config;
231  }
232 
233  this._htmlArea = null;
234  this._textArea = textarea;
235  this._uniqueID = textarea;
236  this._editMode = "wysiwyg";
237  this._initialised = false;
238  this._timerToolbar = null;
239  this._toolbarObjects = Array();
240  this._toolbarMenus = Array();
241  this._tmp = Array();
242 <?php $this->print_plugin_vars() ?>
243  }
244  };
245  </script>
246 
247  <script type="text/javascript">
248  // Creates the toolbar and appends it to the _htmlarea
249  HTMLArea.prototype._createToolbar = function () {
250  var editor = this; // to access this in nested functions
251 
252  var toolbar = document.createElement("div");
253  <?php
254  if ($this->width != 'auto') {
255  echo 'toolbar.style.width = "'.$this->width.'";';
256  }
257  ?>
258  var innerToolbar = document.createElement("div");
259 
260  var table = document.createElement("table");
261  table.border = "0px";
262  table.cellSpacing = "1px";
263  table.cellPadding = "1px";
264 
265  toolbar.appendChild(table);
266  // TBODY is required for IE, otherwise you don't see anything in the TABLE.
267  var tb_body = document.createElement("tbody");
268  table.appendChild(tb_body);
269  tb_row = document.createElement("tr");
270  tb_body.appendChild(tb_row);
271  tb_cell = document.createElement("td");
272  tb_row.appendChild(tb_cell);
273  tb_cell.appendChild(innerToolbar);
274 
275  this._toolbar = toolbar;
276  toolbar.className = "htmlarea-toolbar";
277  toolbar.unselectable = "1";
278  var tb_objects = new Object();
279  this._toolbarObjects = tb_objects;
280 
281  // updates the state of a toolbar element
282  function setButtonStatus(id, newval) {
283  var oldval = this[id];
284  var el = this.element;
285  var img = el.style.backgroundImage;
286  if (img != "" && oldval != newval) {
287  switch (id) {
288  case "enabled":
289  if (newval) {
290  HTMLArea._removeClass(el, "htmlarea-buttonDisabled");
291  el.style.backgroundImage = "url(" + editor.imgURL("toolbar.png") + ")";
292  el.disabled = false;
293  } else {
294  HTMLArea._addClass(el, "htmlarea-buttonDisabled");
295  el.style.backgroundImage = "url(" + editor.imgURL("toolbar_disabled.png") + ")";
296  el.disabled = true;
297  }
298  break;
299  case "active":
300  if (newval) {
301  HTMLArea._addClass(el, "htmlarea-buttonPressed");
302  } else {
303  HTMLArea._removeClass(el, "htmlarea-buttonPressed");
304  }
305  break;
306  }
307  this[id] = newval;
308  }
309  };
310 
311  // this function will handle creation of combo boxes
312  function createSelect(txt) {
313  var options = null;
314  var el = null;
315  var sel = null;
316  var cmd = null;
317  switch (txt) {
318 <?php $this->print_plugin_create_select()?>
319  }
320  if (options) {
321  sel = document.createElement("select");
322  sel.className = "htmlarea-select";
323  var obj = {
324  name: txt, // field name
325  element: sel, // the UI element (SELECT)
326  enabled: true, // is it enabled?
327  text: false, // enabled in text mode?
328  cmd: cmd, // command ID
329  state: setButtonStatus // for changing state
330  };
331  tb_objects[txt] = obj;
332  for (var i in options) {
333  var op = document.createElement("option");
334  op.appendChild(document.createTextNode(i));
335  op.value = options[i];
336  sel.appendChild(op);
337  }
338  el = document.createElement("div");
339  el.className = "htmlarea-select-container";
340  el.appendChild(sel);
341 
342  HTMLArea._addEvent(sel, "change", function () {
343  editor._comboSelected(sel, txt);
344  });
345  }
346  return el;
347  };
348 
349  // appends a new button to toolbar
350  function createButton(container, txt) {
351  // the element that will be created
352  var el = null;
353  var btn = null;
354  switch (txt) {
355  case "separator":
356  el = document.createElement("div");
357  el2 = document.createElement("div");
358  el.appendChild(el2);
359  el.className = "htmlarea-separator";
360  break;
361  case "space":
362  el = document.createElement("div");
363  el.className = "htmlarea-space";
364  break;
365  case "linebreak":
366  el = document.createElement("div");
367  el.className = "htmlarea-space";
368  break;
369 <?php $this->print_plugin_button_type()?>
370  default:
371  btn = editor.config.btnList[txt];
372  break;
373  }
374  if (!el && btn) {
375  el = document.createElement("div");
376  el.title = btn[1];
377  el.className = "htmlarea-button";
378 
379  // let's just pretend we have a button object, and
380  // assign all the needed information to it.
381  var obj = {
382  name : txt, // the button name (i.e. 'bold')
383  element : el, // the UI element (SPAN)
384  enabled : true, // is it enabled?
385  active : false, // is it pressed?
386  text : btn[2], // enabled in text mode?
387  cmd : btn[0], // the command ID
388  state : setButtonStatus, // for changing state
389  context : btn[4] || null // will be disabled if outside this element
390  };
391  tb_objects[txt] = obj;
392 
393  // handlers to emulate nice flat toolbar buttons
394  HTMLArea._addEvent(el, "mouseover", function () {
395  if (obj.enabled) {
396  HTMLArea._addClass(el, "htmlarea-buttonHover");
397  }
398  });
399  HTMLArea._addEvent(el, "mouseout", function () {
400  if (obj.enabled) with (HTMLArea) {
401  _removeClass(el, "htmlarea-buttonHover");
402  _removeClass(el, "htmlarea-buttonActive");
403  (obj.active) && _addClass(el, "htmlarea-buttonPressed");
404  }
405  });
406  HTMLArea._addEvent(el, "mousedown", function (ev) {
407  if (obj.enabled) with (HTMLArea) {
408  _addClass(el, "htmlarea-buttonActive");
409  _removeClass(el, "htmlarea-buttonPressed");
410  _stopEvent(is_ie ? window.event : ev);
411  }
412  });
413  // when clicked, do the following:
414  HTMLArea._addEvent(el, "click", function (ev) {
415  if (obj.enabled) with (HTMLArea) {
416  _removeClass(el, "htmlarea-buttonActive");
417  _removeClass(el, "htmlarea-buttonHover");
418  editor._buttonClicked(txt);
419  _stopEvent(is_ie ? window.event : ev);
420  }
421  });
422 
423  var buttonID = editor._uniqueID + "_" + txt;
424  buttonID = buttonID.toLowerCase();
425  el.id = buttonID + "_span";
426  var btnTop = Math.floor(btn[3] / 5);
427  var btnLeft = ((btn[3] - (btnTop * 5)) * 18);
428  el.style.background = "url(" + editor.imgURL("toolbar.png") + ") scroll -" + btnLeft + "px -" + (btnTop * 20) + "px";
429  el.innerHTML = '<img src="' + editor.imgURL("blank.gif") + '" width="17" height="19" />';
430 
431  } else if (!el) {
432  el = createSelect(txt);
433  }
434  if (el) {
435  container.appendChild(el);
436  } else if (txt != 'addkeyword') {
437  // Do not throw error if the missing plugin is "add keyword" because
438  // its possible for plugin to get miss out from js code if a div content having keywords
439  // is linked (new link) elsewhere next to the div content without any keywords
440  // See bug #4475
441  alert(js_translate('unknown_toolbar_item', txt));
442  }
443  return el;
444  };
445 
446  for (var i in this.config.toolbar) {
447  var group = this.config.toolbar[i];
448  for (var j in group) {
449  var menu = group[j];
450  if (menu.length < 1) {
451  continue;
452  } else if (menu.length == 1) {
453  createButton(innerToolbar, menu[0]);
454  } else {
455  var div = document.createElement("div");
456  editor._toolbar.appendChild(div);
457  div.style.display = "none";
458  div.style.position = "absolute";
459  div.className = "htmlarea-menu";
460  div.id = menu[0] + "_menu";
461 
462  createButton(innerToolbar, menu[0]);
463  this.config.btnList[menu[0]][0] = "htmlarea-showmenu-" + menu[0];
464 
465  for (var x in menu) {
466  if (x == 0) { continue; }
467 
468  var table = document.createElement("table");
469  var tb_body = document.createElement("tbody");
470  var tb_row = document.createElement("tr");
471  var tb_cell = document.createElement("td");
472  table.appendChild(tb_body);
473  tb_body.appendChild(tb_row);
474  tb_row.appendChild(tb_cell);
475 
476  table.className = "htmlarea-menuRow";
477  createButton(tb_cell, menu[x])
478 
479  tb_cell = document.createElement("td");
480  tb_cell.className = "htmlarea-menuText";
481  tb_row.appendChild(tb_cell);
482 
483  var title = document.createTextNode(this.config.btnList[menu[x]][1]);
484  tb_cell.appendChild(title);
485 
486  div.appendChild(table);
487  }
488  this._toolbarMenus[div.id] = div;
489  }
490  }
491  }
492  };
493 
494  // updates enabled/disable/active state of the toolbar elements
495  HTMLArea.prototype.updateToolbar = function(allEnabled, noStatus) {
496  var doc = this._doc;
497  var text = (this._editMode == "textmode");
498  var ancestors = null;
499  var htmlareaFound = false;
500 
501  if (!text) {
502  ancestors = this.getAllAncestors();
503  if (this.config.statusBar && !noStatus) {
504  this._statusBarTree.innerHTML = ''; // clear
505  for (var i = ancestors.length; --i >= 0;) {
506  var el = ancestors[i];
507  if (!el) {
508  // hell knows why we get here; this
509  // could be a classic example of why
510  // it's good to check for conditions
511  // that are impossible to happen ;-)
512  continue;
513  }
514 
515  // work out the display name for this tag
516  var txt = el.tagName.toLowerCase();
517  if (el.id) {
518  txt += "#" + el.id;
519  }
520  if (el.className) {
521  txt += "." + el.className;
522  }
523 
524  // if we are playing with an editable DIV, we need to tread carefully
525  // because there is no body tag expect the one for the whole page
526  // so we fudge the results a little to skip tags outside of the editor
527  if (HTMLArea.is_ie && this.config.bodyType.toLowerCase() != 'iframe') {
528  if (el.id == "htmlarea") {
529  htmlareaFound = true;
530  i--;
531  var el = ancestors[i];
532  txt = 'body';
533  }
534  if (!htmlareaFound) { continue; }
535  }
536 
537  var a = document.createElement("a");
538  a.href = "#";
539  a.el = el;
540  a.editor = this;
541  a.onclick = function() {
542  this.blur();
543  this.editor.selectNodeContents(this.el);
544  this.editor.updateToolbar(true);
545  return false;
546  };
547  a.oncontextmenu = function() {
548  // TODO: add context menu here
549  this.blur();
550  var info = "Inline style:\n\n";
551  info += this.el.style.cssText.split(/;\s*/).join(";\n");
552  alert(info);
553  return false;
554  };
555  a.title = el.style.cssText;
556  // NOTE: Using appendChild here causes IE to clear its undo/redo queue
557  a.appendChild(document.createTextNode(txt));
558  this._statusBarTree.appendChild(a);
559  if (i != 0) {
560  this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
561  }
562  }
563  }
564  }
565 
566  for (var i in this._toolbarObjects) {
567  var btn = this._toolbarObjects[i];
568  var cmd = btn.cmd;
569 
570  var inContext = true;
571  if (btn.context && !text) {
572  inContext = false;
573  var context = btn.context;
574  var attrs = [];
575  if (/(.*)\[(.*?)\]/.test(context)) {
576  context = RegExp.$1;
577  attrs = RegExp.$2.split(",");
578  }
579  context = context.toLowerCase();
580  var match = (context == "*");
581  htmlareaFound = false;
582  for (var k = ancestors.length; --k >= 0;) {
583  var el = ancestors[k];
584  if (!el) {
585  // the impossible really happens.
586  continue;
587  }
588 
589  // if we are playing with an editable DIV, we need to tread carefully
590  // so we can skip tags outside of the editor
591 
592  if (HTMLArea.is_ie && this.config.bodyType.toLowerCase() != 'iframe') {
593  if (el.id == "htmlarea") {
594  htmlareaFound = true;
595  k--;
596  el = ancestors[k];
597  }
598  if (!htmlareaFound) { continue; }
599  }
600 
601  if (match || (el.tagName.toLowerCase() == context)) {
602  inContext = true;
603  for (var ka in attrs) {
604  if (!eval("ancestors[k]." + attrs[ka])) {
605  inContext = false;
606  break;
607  }
608  }
609  if (inContext) { break; }
610  }
611  }
612  }
613 
614  if (typeof cmd == "function") { continue; }
615 
616  if (allEnabled == null) {
617  var btnEnabled = (!text || btn.text) && inContext;
618  } else {
619  var btnEnabled = allEnabled;
620  }
621  btn.state("enabled", btnEnabled);
622 
623  cmd = cmd.toLowerCase();
624  switch (cmd) {
625 <?php $this->print_plugin_update_toolbar()?>
626  default:
627  try {
628  btn.state("active", (!text && doc.queryCommandState(cmd)));
629  } catch (e) {}
630  break;
631  }
632  }
633  };
634 
635  // gets called before the form is submitted
636  HTMLArea.prototype._formSubmit = function() {
637  var editor = this; // needed in nested functions
638  var html = this.getHTML(); // HTML that will be set
639  // can be modified by plugins
640 
641 <?php $this->print_plugin_form_submit()?>
642 
643  // retrieve the HTML
644  html = this.make_relative_hrefs(html);
645  this._textArea.value = html;
646  };
647  </script>
648 
649 <?php $this->print_plugin_generic_functions()?>
650 
651  <script type="text/javascript">
652  // txt is the name of the button, as in config.toolbar
653  HTMLArea.prototype._buttonClicked = function(txt) {
654  //stores original scrollbar position to circumvent
655  //some IE craziness.
656  var original_scroll = document.body.scrollTop;
657  var editor = this; // needed in nested functions
658 
659  this.focusEditor();
660  //set scrollbar back
661  document.body.scrollTop = original_scroll;
662  var btn = this.config.btnList[txt];
663  if (!btn) {
664  alert("FIXME: Unconfigured button!");
665  return false;
666  }
667  var cmd = btn[0];
668  if (typeof cmd == "function") {
669  return cmd(this, txt);
670  }
671 
672  if (/htmlarea-showmenu-/.test(cmd.toLowerCase())) {
673  re = new RegExp("htmlarea-showmenu-");
674  var menuID = cmd.replace(re, "");
675  var menu = document.getElementById(menuID + "_menu");
676 
677  if (menu.style.display == "none") {
678  // hide all menus that are showing
679  for (var x in this._toolbarMenus) {
680  this._toolbarMenus[x].style.display = "none";
681  }
682 
683  menu.style.display = "block";
684 
685  var buttonID = editor._uniqueID + "_" + menuID + "_span";
686  buttonID = buttonID.toLowerCase();
687  var button = document.getElementById(buttonID);
688 
689  if (HTMLArea.is_ie) {
690  menu.style.pixelTop = button.offsetTop + 23;
691  menu.style.pixelLeft = button.offsetLeft;
692  } else {
693  menu.style.top = button.offsetTop + 23;
694  menu.style.left = button.offsetLeft;
695  }
696  } else {
697  menu.style.display = "none";
698  }
699 
700  } else {
701  // hide all menus that are showing
702  for (var x in this._toolbarMenus) {
703  this._toolbarMenus[x].style.display = "none";
704  }
705 
706  switch (cmd.toLowerCase()) {
707  <?php $this->print_plugin_button_clicks()?>
708  default:
709  this._execCommand(btn[0], false, "");
710  break;
711  }
712  }
713  this.updateToolbar();
714  //set scrollbar back
715  document.body.scrollTop = original_scroll;
716  return false;
717  };
718 
719  // el is reference to the SELECT object
720  // txt is the name of the select field, as in config.toolbar
721  HTMLArea.prototype._comboSelected = function(el, txt) {
722  this.focusEditor();
723  var value = el.options[el.selectedIndex].value;
724  switch (txt) {
725 <?php $this->print_plugin_combo_selected()?>
726  default:
727  alert(js_translate('combo_box_not_implemented', txt));
728  break;
729  }
730  };
731 
732  // the execCommand function (intercepts some commands and replaces them with
733  // our own implementation)
734  HTMLArea.prototype._execCommand = function(cmdID, UI, param) {
735  switch (cmdID.toLowerCase()) {
736 <?php $this->print_plugin_exec_commands()?>
737  default:
738  this._doc.execCommand(cmdID, UI, param);
739  break;
740  }
741  this.focusEditor();
742  };
743 
744 //bunch of tags to compare when examining nodes
745 HTMLArea.prototype.inline_tags = ['a', 'abbr', 'acronym', 'b', 'bdo', 'big', 'cite', 'code', 'dfn', 'em', 'font', 'i', 'kbd', 'label', 'q', 's', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'textarea', 'tt', 'u', 'var'];
746 
747 // Finds the first parent element of a given node whose display is probably not inline
748 HTMLArea.prototype.parentBlock = function(node) {
749 
750  while ( node.parentNode && ( node.nodeType != 1 || this.inArray( node.tagName.toLowerCase(), this.inline_tags ) ) ) node = node.parentNode;
751  return node;
752 };
753 
754 // Returns true if a given value is in an array, false otherwise
755 HTMLArea.prototype.inArray = function(val, arr) {
756 
757  for (var i=0; i < arr.length; i++)
758  if (val == arr[i]) return true;
759  return false;
760 };
761 
762 // Sets dir attribute value
763 HTMLArea.prototype.set_dir_attr = function(val) {
764  this._dir_attr = val;
765 };
766 
767 // Internal function for recursively itterating over a all nodes in a fragment
768 // If a callback function returns a non-null value, that is returned and the crawl is therefore broken
769 HTMLArea.prototype.traverseChildrenNodes = function(me, callback) {
770 
771  if ( me.firstChild ) {
772  var myChild = me.firstChild;
773  var retVal;
774  while (myChild) {
775  if ( (retVal=callback(myChild)) != null ) return retVal;
776  if ( (retVal=this.traverseChildrenNodes(myChild, callback)) != null ) return retVal;
777  myChild = myChild.nextSibling;
778  }
779  }
780 };
781 
782 // Callback function to be performed on each node in the hierarchy
783 // Sets flag to true if we find actual text or an element that's not usually displayed inline
784 HTMLArea.prototype.containsText = function(node) {
785 
786  if (node.nodeType == 1 && HTMLArea.prototype.inArray(node.nodeName.toLowerCase(), HTMLArea.prototype.inline_tags) && node.hasChildNodes() && node.firstChild.nodeValue) return true;
787  else if (node.nodeType == 3 && node.nodeValue != '') return true;
788  else return null;
789 
790 };
791 
792 // Inserts a node down into a node, on the left side of the tree under target
793 HTMLArea.prototype.insertDeepLeft = function(target, toInsert) {
794 
795  var digger = target;
796  while ( digger.firstChild && digger.firstChild.nodeType == 1 ) digger = digger.firstChild;
797  var refNode = digger.firstChild ? digger.firstChild : null;
798  //falling.insertBefore(toInsert, refNode);
799  digger.innerHTML = toInsert;
800 };
801 
802  HTMLArea.prototype.isElem = function(node,type) {
803  return node.nodeName.toLowerCase() == type.toLowerCase();
804  };
805 
806 
807  // A generic event handler for things that happen in the IFRAME's document.
808  // This function also handles key bindings.
809 
810  HTMLArea.prototype._editorEvent = function(ev) {
811  var editor = this;
812  // Make enter without shift add <p> tags in Moz as well as IE
813  if (ev.type == "keypress" && !HTMLArea.is_ie) {
814  var keyCode = ev.keyCode;
815  if (keyCode == 13 && !ev.shiftKey && this._iframe.contentWindow.getSelection) {
816 
817  // Get the selection and solid references to what we're dealing with chopping
818  var sel = this._iframe.contentWindow.getSelection();
819 
820  // Set the start and end points such that they're going /forward/ through the document
821  var leftRange = this._doc.createRange();
822  var rightRange = this._doc.createRange();
823 
824  // While getting anchorNode and focusNode, for some reason Firefox is getting parent node of the
825  // selected node instead of selected node itself when non-text node is selected
826  // See bug #3826
827  var sAnchorNode = sel.anchorNode;
828  var sFocusNode = sel.focusNode;
829  var sAnchorOffset = sel.anchorOffset;
830  var sFocusOffset = sel.focusOffset;
831 
832  if (sAnchorNode.nodeName == 'BODY' && sAnchorNode.childNodes[sAnchorOffset]) {
833  sAnchorNode = sAnchorNode.childNodes[sAnchorOffset];
834  sAnchorOffset = 0;
835 
836  while(sAnchorNode.childNodes[0] && sAnchorNode.nodeName != '#text')
837  sAnchorNode = sAnchorNode.childNodes[0];
838  }
839 
840  if (sFocusNode.nodeName == 'BODY' && sFocusNode.childNodes[sFocusOffset]) {
841  sFocusNode = sFocusNode.childNodes[sFocusOffset];
842  sFocusOffset = 0;
843 
844  while(sFocusNode.childNodes[0] && sFocusNode.nodeName != '#text')
845  sFocusNode = sFocusNode.childNodes[0];
846  }
847 
848  if (sAnchorNode.nodeName != '#text') {
849  empty_node = document.createTextNode('');
850  if (sAnchorNode.nodeName != 'BR') {
851  sAnchorNode.appendChild(empty_node);
852  } else {
853  sAnchorNode.parentNode.insertBefore(empty_node, sAnchorNode.nextSibling);
854  }
855  sAnchorNode = empty_node;
856  sAnchorOffset = 0;
857  }
858 
859  if (sFocusNode.nodeName != '#text') {
860  empty_node = document.createTextNode('');
861  if (sFocusNode.nodeName != 'BR') {
862  sFocusNode.appendChild(empty_node);
863  } else {
864  sFocusNode.parentNode.insertBefore(empty_node, sFocusNode.nextSibling);
865  }
866  sFocusNode = empty_node;
867  sFocusOffset = 0;
868  }
869 
870  leftRange.setStart(sAnchorNode,sAnchorOffset);
871  rightRange.setStart(sFocusNode,sFocusOffset);
872  leftRange.collapse(true);
873  rightRange.collapse(true);
874 
875  var direct = leftRange.compareBoundaryPoints(leftRange.START_TO_END,rightRange);
876 
877  var startNode = (direct<0) ? sAnchorNode : sFocusNode;
878  var startOffset = (direct<0) ? sAnchorOffset : sFocusOffset;
879  var endNode = (direct<0) ? sFocusNode : sAnchorNode;
880  var endOffset = (direct<0) ? sFocusOffset : sAnchorOffset;
881 
882  // When Focus node and Anchor node are same point and foucs node is at end of an anchor node, add an extra blank node
883  // This is to make sure that paragraphs are created outside the node. See bug #3978
884  if (startOffset == endOffset && startNode == endNode && endNode.nodeValue && endNode.nodeValue.length == endOffset && endNode.parentNode && this.isElem(endNode.parentNode,'a') && endNode.parentNode.parentNode) {
885 
886  blank_node = document.createTextNode('');
887  endNode.parentNode.parentNode.appendChild(blank_node);
888 
889  startNode = blank_node;
890  startOffset = 0;
891  endNode = blank_node;
892  endOffset = 0;
893  }
894 
895  // Find the parent blocks of nodes at either end, and get paragraph attributes
896  var startBlock = this.parentBlock(startNode);
897  var endBlock = this.parentBlock(endNode);
898  var leftParagraphAttributes = new Array();
899  var rightParagraphAttributes = new Array();
900 
901  // Inside a list Item, let the browser handle it.
902  if ( this.isElem(startBlock,'li') || this.isElem(endBlock,'li') ) return;
903 
904  // if line starts with a header
905  var isInsideHeader = this.isElem(startBlock, 'h1') || this.isElem(startBlock, 'h2') || this.isElem(startBlock, 'h3') || this.isElem(startBlock, 'h4') || this.isElem(startBlock, 'h5') || this.isElem(startBlock, 'h6');
906  var headerType = null;
907  if (isInsideHeader) {
908  if (this.isElem(startBlock, 'h1')) headerType = 'h1';
909  else if (this.isElem(startBlock, 'h2')) headerType = 'h2';
910  else if (this.isElem(startBlock, 'h3')) headerType = 'h3';
911  else if (this.isElem(startBlock, 'h4')) headerType = 'h4';
912  else if (this.isElem(startBlock, 'h5')) headerType = 'h5';
913  else if (this.isElem(startBlock, 'h6')) headerType = 'h6';
914  }
915 
916  if ( this.isElem(startBlock,'p') ) {
917  for ( var i=0; i < startBlock.attributes.length; i++ ) {
918  leftParagraphAttributes[ startBlock.attributes[i].nodeName ] = startBlock.attributes[i].nodeValue;
919  }
920  }
921  if ( this.isElem(endBlock,'p') ) {
922  for ( var i=0; i < endBlock.attributes.length; i++ ) {
923  rightParagraphAttributes[ endBlock.attributes[i].nodeName ] = endBlock.attributes[i].nodeValue;
924  }
925  }
926  var choppingStart = startNode;
927  var choppingEnd = endNode;
928 
929  while ( ( choppingStart.previousSibling && !this.isElem(choppingStart.previousSibling,'p') ) || ( choppingStart.parentNode && choppingStart.parentNode != startBlock && choppingStart.parentNode.nodeType != 9 ) )
930  choppingStart = choppingStart.previousSibling ? choppingStart.previousSibling : choppingStart.parentNode;
931 
932  while ( ( choppingEnd.nextSibling && !this.isElem(choppingEnd.nextSibling,'p') ) || ( choppingEnd.parentNode && choppingEnd.parentNode != endBlock && choppingEnd.parentNode.nodeType != 9 ) )
933  choppingEnd = choppingEnd.nextSibling ? choppingEnd.nextSibling : choppingEnd.parentNode;
934 
935  // Set up new paragraphs
936  var leftParagraph = null;
937  var rightParagraph = null;
938 
939  // this element will be used if u r in a header
940  var newParagraph = null;
941 
942  if (!isInsideHeader) {
943  // if we are not inside a heading keep the original behaviour
944  leftParagraph = this._doc.createElement('p');
945  rightParagraph = this._doc.createElement('p');
946  } else {
947  if (headerType != null) {
948  // create two paragraph with the header
949  leftParagraph = this._doc.createElement(headerType);
950  rightParagraph = this._doc.createElement(headerType);
951  } else {
952  // error headerType should not be null!
953  }
954 
955  newParagraph = this._doc.createElement('p')
956  }
957 
958  for ( var attrName in leftParagraphAttributes ) {
959  var thisAttr = this._doc.createAttribute(attrName);
960  thisAttr.value = leftParagraphAttributes[attrName];
961  leftParagraph.setAttributeNode(thisAttr);
962  }
963  for ( var attrName in rightParagraphAttributes ) {
964  var thisAttr = this._doc.createAttribute(attrName);
965  thisAttr.value = rightParagraphAttributes[attrName];
966  rightParagraph.setAttributeNode(thisAttr);
967  }
968 
969  // Split the data into left and right paragraphs
970 
971  //For Left paragraph
972  leftRange.setStartBefore(choppingStart);
973  leftRange.setEnd(startNode,startOffset);
974  leftParagraph.appendChild(leftRange.cloneContents());
975 
976  //For Right Paragraph
977  rightRange.setEndAfter(choppingEnd);
978  rightRange.setStart(endNode,endOffset);
979  rightParagraph.appendChild(rightRange.cloneContents());
980 
981  // containsData is true if there is data in the paragraph,
982  //if false, insert a non-breaking space
983  var isLeftParagraphEmpty = false;
984  var containsData = false;
985  containsData = this.traverseChildrenNodes(leftParagraph, this.containsText);
986 
987  if ( containsData != true ) {
988  this.insertDeepLeft(leftParagraph, '&nbsp;');
989  isLeftParagraphEmpty = true;
990  }
991 
992  // containsData is true if there is data in the paragraph,
993  //if false, insert a non-breaking space
994  var isRightParagraphEmpty = false;
995  containsData = false;
996  containsData = this.traverseChildrenNodes(rightParagraph, this.containsText);
997  if ( containsData != true ) {
998  this.insertDeepLeft(rightParagraph, '&nbsp;');
999  isRightParagraphEmpty = true;
1000  }
1001 
1002  // Get the range of everything to be replaced
1003  var fullRange = this._doc.createRange();
1004 
1005  if (!isInsideHeader) {
1006  //if no previous sibling, and parent is a paragraph, set start of range before parent, so it will be removed.
1007  //otherwise set to original start range
1008  if ( !choppingStart.previousSibling && this.isElem(choppingStart.parentNode,'p') ) {
1009  fullRange.setStartBefore(choppingStart.parentNode);
1010  } else {
1011  fullRange.setStart(leftRange.startContainer, leftRange.startOffset);
1012  }
1013 
1014  if ( !choppingEnd.nextSibling && this.isElem(choppingEnd.parentNode,'p') ) {
1015  fullRange.setEndAfter(choppingEnd.parentNode);
1016  } else {
1017  fullRange.setEnd(rightRange.endContainer, rightRange.endOffset);
1018  }
1019  } else {
1020 
1021  // if we are inside a header we will chop the whole line
1022  // and replace it with 3 new lines.
1023  // - first line will from starting to the cursor position of the old line
1024  // - second line will an empty p tag
1025  // - third line will be from the cursor position to the ending of the old line
1026  fullRange.setStartBefore(choppingStart.parentNode);
1027  fullRange.setEndAfter(choppingEnd.parentNode);
1028  }
1029 
1030  //remove data from range and insert new paragraphs
1031  fullRange.deleteContents();
1032 
1033  if (isInsideHeader) {
1034  // add the rightParagraph
1035  if (!isRightParagraphEmpty) fullRange.insertNode(rightParagraph);
1036  // add a space in the new paragraph
1037  newParagraph.appendChild(document.createTextNode(""+'\u00A0'));
1038  fullRange.insertNode(newParagraph);
1039  // add the rightParagraph
1040  if (!isLeftParagraphEmpty) fullRange.insertNode(leftParagraph);
1041 
1042  // set the selection to the start of the new paragraph (new)
1043  var cursorRange = this._doc.createRange();
1044  cursorRange.setStart(newParagraph,0);
1045  cursorRange.collapse(true);
1046  sel = this._iframe.contentWindow.getSelection();
1047  sel.removeAllRanges();
1048  sel.addRange(cursorRange);
1049  } else {
1050  // we keep the old behaviour
1051  fullRange.insertNode(rightParagraph);
1052  fullRange.insertNode(leftParagraph);
1053  if ( rightParagraph.firstChild ) {
1054  while ( this.inArray(rightParagraph.firstChild && rightParagraph.firstChild.nodeName.toLowerCase(), this.inline_tags) ) rightParagraph = rightParagraph.firstChild;
1055  if ( rightParagraph.firstChild && rightParagraph.firstChild.nodeType == 3 ) rightParagraph = rightParagraph.firstChild; // and text, if they've got it
1056 
1057  // Set the selection to the start of the new paragraph (right)
1058  var cursorRange = this._doc.createRange();
1059  cursorRange.setStart(rightParagraph,0);
1060  cursorRange.collapse(true);
1061  sel = this._iframe.contentWindow.getSelection();
1062  sel.removeAllRanges();
1063  sel.addRange(cursorRange);
1064  }
1065  }
1066 
1067 
1068  // Stop event to prevent no IE browsers adding the <br>
1069  HTMLArea._stopEvent(ev);
1070  }
1071  }
1072 
1073 <?php $this->print_plugin_event_handlers()?>
1074 
1075  var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");
1076  if (keyEvent && ev.ctrlKey) {
1077  var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
1078  var sel = null;
1079  var range = null;
1080  var cmd = null;
1081  var value = null;
1082  switch (key) {
1083 <?php $this->print_plugin_shortcuts()?>
1084  }
1085  if (cmd) {
1086  // execute simple command
1087  this._execCommand(cmd, false, value);
1088  HTMLArea._stopEvent(ev);
1089  }
1090  }
1091 
1092  // update the toolbar state after some time
1093  // if we are not just generally typing
1094  if (!keyEvent || (keyEvent && ev.ctrlKey)) {
1095  // hide all menus that are showing
1096  for (var x in this._toolbarMenus) {
1097  this._toolbarMenus[x].style.display = "none";
1098  }
1099 
1100  setTimeout(function() {
1101  editor.updateToolbar();
1102  }, 50);
1103  }
1104  };
1105 
1106  // retrieve the HTML
1107  HTMLArea.prototype.getHTML = function() {
1108  var retVal = '';
1109 <?php $this->print_plugin_pre_get_html()?>
1110  switch (this._editMode) {
1111  case "textmode":
1112  if (HTMLArea.is_gecko) {
1113  var html = this._iframe.contentWindow.document.body.ownerDocument.createRange();
1114  html.selectNodeContents(this._iframe.contentWindow.document.body);
1115  retVal = html.toString();
1116  } else if (HTMLArea.is_ie) {
1117  retVal = this._docContent.innerText;
1118  }
1119  break;
1120  case "wysiwyg":
1121  if (HTMLArea.is_gecko) {
1122  retVal = this._iframe.contentWindow.document.body.innerHTML;
1123  // if we are using firefox and if it is the initial container
1124  // we need to remove any new line character because
1125  // firefox will keep on adding <br> and additional \n(\n)
1126  // do make sure our body (div) is visible
1127  // see bug #5148 br tag created in the WYSIWYG as soon as it's created
1128  if (HTMLArea.is_ff && retVal.match(/^<br>(\n)*$/) != null) {
1129  retVal = retVal.replace(/^<br>(\n)*$/, '');
1130  }
1131  } else if (HTMLArea.is_ie) {
1132  retVal = this._docContent.innerHTML;
1133  }
1134 
1135 <?php
1136 // Don't replace entities on UTF8 systems
1137 if (SQ_CONF_DEFAULT_CHARACTER_SET != 'utf-8') {
1138  echo 'retVal = this.replace_entities(retVal);';
1139 } else {
1140 // Exceptions. See #5051
1141 ?>
1142  var entities_array = {
1143  173 : '&shy;' //soft hyphen
1144  };
1145 
1146  retVal = this.replace_entities(retVal, entities_array);
1147 
1148 <?php
1149 }
1150 ?>
1151 
1152  break;
1153  default:
1154  alert(js_translate('undefined_mode', '<' + this._editMode + '>'));
1155  return false;
1156  }
1157  // let any plugins modify the returned HTML if they want to
1158 <?php $this->print_plugin_get_html()?>
1159 
1160  if (this._editMode == "textmode") {
1161  retVal = this.make_absolute_urls(retVal);
1162  } else {
1163  retVal = this.make_relative_hrefs(retVal);
1164  }
1165  return retVal;
1166  };
1167 
1169  // because the editor automatically prepend the http:// business to
1170  // all relative urls, remove them from the html
1171  HTMLArea.prototype.make_relative_hrefs = function(html_code) {
1172 
1173  var html = new String(html_code);
1174  var e = '';
1175  var re = null;
1176 
1177  // remove any references to this pop-up's url
1178  var current_location = document.location.toString();
1179 
1180  // escape any special reg exp chars in the location
1181  current_location = current_location.replace(/([.?+*^$\\\[\]\(\)\{\}|])/gi, '\\$1');
1182  re = new RegExp(current_location + '[/]?', "gi");
1183  html = html.replace(re, "");
1184 
1185  // now replace any ampersands with '&amp;' as IE seems to anyway sometimes (even in href's)
1186  current_location = current_location.replace(/\&/gi, "&amp;");
1187  re = new RegExp(current_location, "gi");
1188  html = html.replace(re, "");
1189  for (var i = 0; i < this.config.relativeHrefChecks.length; i++) {
1190  e = this.config.relativeHrefChecks[i][0];
1191  re = new RegExp(e, "gi");
1192  html = html.replace(re, this.config.relativeHrefChecks[i][1]);
1193  }
1194 
1195  // any other links
1196  e = 'http[s]?://<?php echo $_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']?>';
1197  re = new RegExp(e, "gi");
1198  html = html.replace(re, "");
1199 
1200  return html;
1201  };
1202 
1204  // reconstruct all relative HREFs into absolute URLs before the browser
1205  // decides to do it for us anyway
1206  HTMLArea.prototype.make_absolute_urls = function(html_code) {
1207 
1208  var html = new String(html_code);
1209  var e = '';
1210  var re = null;
1211 
1212  for (var i = 0; i < this.config.absoluteUrlChecks.length; i++) {
1213  e = this.config.absoluteUrlChecks[i][0];
1214  re = new RegExp(e, "gi");
1215  html = html.replace(re, this.config.absoluteUrlChecks[i][1]);
1216 
1217  }
1218 
1219  return html;
1220  };
1221 
1222  // We want to replace actual entities in the html(like �) with
1223  // Their correct &bla; representations
1224  HTMLArea.prototype.replace_entities = function(html_code, entities_array) {
1225 
1226  if (entities_array == undefined){
1227  var entities_array = {
1228  160 : '&nbsp;', // non-breaking space
1229  161 : '&iexcl;', // inverted exclamation mark
1230  162 : '&cent;', // cent sign
1231  163 : '&pound;', // pound sterling sign
1232  164 : '&curren;', // general currency sign
1233  165 : '&yen;', // yen sign
1234  166 : '&brvbar;', // broken vertical bar
1235  167 : '&sect;', // section sign
1236  168 : '&uml;', // spacing dieresis or umlaut
1237  169 : '&copy;', // copyright sign
1238  170 : '&ordf;', // feminine ordinal indicator
1239  171 : '&laquo;', // left (double) angle quote (guillemet)
1240  172 : '&not;', // logical not sign
1241  173 : '&shy;', // soft hyphen
1242  174 : '&reg;', // registered trademark sign
1243  175 : '&macr;', // spacing macron (long) accent
1244  176 : '&deg;', // degree sign
1245  177 : '&plusmn;', // plus-or-minus sign
1246  178 : '&sup2;', // superscript 2
1247  179 : '&sup3;', // superscript 3
1248  180 : '&acute;', // spacing acute accent
1249  181 : '&micro;', // micro sign
1250  182 : '&para;', // paragraph sigh, pilcrown sign
1251  183 : '&middot;', // middle dot, centred dot
1252  184 : '&cedil;', // spacing cedilla
1253  185 : '&sup1;', // superscript 1
1254  186 : '&ordm;', // masculine ordinal indicator
1255  187 : '&raquo;', // right (double) angle quote (guillemet)
1256  188 : '&frac14;', // fraction 1/4
1257  189 : '&frac12;', // fraction 1/2
1258  190 : '&frac34;', // fraction 3/4
1259  191 : '&iquest;', // inverted question mark
1260  192 : '&Agrave;', // capital A grave
1261  193 : '&Aacute;', // capital A acute
1262  194 : '&Acirc;', // capital A circumflex
1263  195 : '&Atilde;', // capital A tilde
1264  196 : '&Auml;', // capital A dieresis or umlaut
1265  197 : '&Aring;', // capital A ring
1266  198 : '&AElig;', // capital AE ligature
1267  199 : '&Ccedil;', // capital C cedilla
1268  200 : '&Egrave;', // capital E grave
1269  201 : '&Eacute;', // capital E acute
1270  202 : '&Ecirc;', // capital E circumflex
1271  203 : '&Euml;', // capital E dieresis or umlaut
1272  204 : '&Igrave;', // capital I grave
1273  205 : '&Iacute;', // capital I acute
1274  206 : '&Icirc;', // capital I circumflex
1275  207 : '&Iuml;', // capital I dieresis or umlaut
1276  208 : '&ETH;', // capital ETH
1277  209 : '&Ntilde;', // capital N tilde
1278  210 : '&Ograve;', // capital O grave
1279  211 : '&Oacute;', // capital O acute
1280  212 : '&Ocirc;', // capital O circumflex
1281  213 : '&Otilde;', // capital O tilde
1282  214 : '&Ouml;', // capital O dieresis or umlaut
1283  215 : '&times;', // multiplication sign
1284  216 : '&Oslash;', // capital O slash
1285  217 : '&Ugrave;', // capital U grave
1286  218 : '&Uacute;', // capital U acute
1287  219 : '&Ucirc;', // capital U circumflex
1288  220 : '&Uuml;', // capital U dieresis or umlaut
1289  221 : '&Yacute;', // capital Y acute
1290  222 : '&THORN;', // capital THORN
1291  223 : '&szlig;', // small sharp s, sz ligature
1292  224 : '&agrave;', // small a grave
1293  225 : '&aacute;', // small a acute
1294  226 : '&acirc;', // small a circumflex
1295  227 : '&atilde;', // small a tilde
1296  228 : '&auml;', // small a dieresis or umlaut
1297  229 : '&aring;', // small a ring
1298  230 : '&aelig;', // small ae ligature
1299  231 : '&ccedil;', // small c cedilla
1300  232 : '&egrave;', // small e grave
1301  233 : '&eacute;', // small e acute
1302  234 : '&ecirc;', // small e circumflex
1303  235 : '&euml;', // small e dieresis or umlaut
1304  236 : '&igrave;', // small i grave
1305  237 : '&iacute;', // small i acute
1306  238 : '&icirc;', // small i circumflex
1307  239 : '&iuml;', // small i dieresis or umlaut
1308  240 : '&eth;', // small eth
1309  241 : '&ntilde;', // small n tilde
1310  242 : '&ograve;', // small o grave
1311  243 : '&oacute;', // small o acute
1312  244 : '&ocirc;', // small o circumflex
1313  245 : '&otilde;', // small o tilde
1314  246 : '&ouml;', // small o dieresis or umlaut
1315  247 : '&divide;', // division sign
1316  248 : '&oslash;', // small o slash
1317  249 : '&ugrave;', // small u grave
1318  250 : '&uacute;', // small u acute
1319  251 : '&ucirc;', // small u circumflex
1320  252 : '&uuml;', // small u dieresis or umlaut
1321  253 : '&yacute;', // small y acute
1322  254 : '&thorn;', // small thorn
1323  255 : '&yuml;', // small y dieresis or umlaut
1324  256 : '&#256;', // uppercase roman A with macron
1325  257 : '&#257;', // lowercase roman a with macron
1326  274 : '&#274;', // uppercase roman E with macron
1327  275 : '&#275;', // lowercase roman e with macron
1328  298 : '&#298;', // uppercase roman I with macron
1329  299 : '&#299;', // lowercase roman i with macron
1330  332 : '&#332;', // uppercase roman O with macron
1331  333 : '&#333;', // lowercase roman o with macron
1332  362 : '&#362;', // uppercase roman U with macron
1333  363 : '&#363;', // lowercase roman u with macron
1334  338 : '&OElig;', // capital ligature OE
1335  339 : '&oelig;', // small ligature oe
1336  352 : '&Scaron;', // capital S with caron
1337  353 : '&scaron;', // small s with caron
1338  376 : '&Yuml;', // capital Y with diaeres
1339  402 : '&fnof;', // small italic f, function of, florin
1340  710 : '&circ;', // modifier letter circumflex accent
1341  732 : '&tilde;', // small tilde
1342  913 : '&Alpha;', // capital Alhpa
1343  914 : '&Beta;', // capital Beta
1344  915 : '&Gamma;', // capital Gamma
1345  916 : '&Delta;', // capital Delta
1346  917 : '&Epsilon;', // capital Epsilon
1347  918 : '&Zeta;', // capital Zeta
1348  919 : '&Eta;', // capital Eta
1349  920 : '&Theta;', // capital Theta
1350  921 : '&Iota;', // capital Iota
1351  922 : '&Kappa;', // capital Kappa
1352  923 : '&Lambda;', // capital Lambda
1353  924 : '&Mu;', // capital Mu
1354  925 : '&Nu;', // capital Nu
1355  926 : '&Xi;', // capital Xi
1356  927 : '&Omicron;', // capital Omicron
1357  928 : '&Pi;', // capital Pi
1358  929 : '&Rho;', // capital Rho
1359  931 : '&Sigma;', // capital Sigma
1360  932 : '&Tau;', // capital Tau
1361  933 : '&Upsilon;', // capital Upsilon
1362  934 : '&Phi;', // capital Phi
1363  935 : '&Chi;', // capital Chi
1364  936 : '&Psi;', // capital Psi
1365  937 : '&Omega;', // capital Omega
1366  945 : '&alpha;', // small alpha
1367  946 : '&beta;', // small beta
1368  947 : '&gamma;', // small gamma
1369  948 : '&delta;', // small delta
1370  949 : '&epsilon;', // small epsilon
1371  950 : '&zeta;', // small zeta
1372  951 : '&eta;', // small eta
1373  952 : '&theta;', // small theta
1374  953 : '&iota;', // small iota
1375  954 : '&kappa;', // small kappa
1376  955 : '&lambda;', // small lambda
1377  956 : '&mu;', // small mu
1378  957 : '&nu;', // small nu
1379  958 : '&xi;', // small xi
1380  959 : '&omicron;', // small omicron
1381  960 : '&pi;', // small pi
1382  961 : '&rho;', // small rho
1383  962 : '&sigmaf;', // small sigma final
1384  963 : '&sigma;', // small sigma
1385  964 : '&tau;', // small tau
1386  965 : '&upsilon;', // small upsilon
1387  966 : '&phi;', // small phi
1388  967 : '&chi;', // small chi
1389  968 : '&psi;', // small psi
1390  969 : '&omega;', // small omega
1391  977 : '&thetasym;', // small theta symbol
1392  978 : '&upsih;', // small upsilon symbol
1393  982 : '&piv;', // small pi symbol
1394  8224 : '&dagger;', // dagger
1395  8225 : '&Dagger;', // 2 sided dagger
1396  8226 : '&bull;', // bullet
1397  8230 : '&hellip;', // horizontal ellipsis
1398  8240 : '&permil;', // per mile
1399  8242 : '&prime;', // prime, minute mark
1400  8243 : '&Prime;', // double prime, second mark
1401  8249 : '&lsaquo;', // single left angle quotation
1402  8250 : '&rsaquo;', // single right angle quotation
1403  8254 : '&oline;', // overline
1404  8260 : '&frasl;', // fraction slash
1405  8472 : '&weierp;', // weierstrassian function p
1406  8465 : '&image;', // imaginary part of
1407  8476 : '&real;', // real part of
1408  8482 : '&trade;', // trademark sign
1409  8501 : '&alefsym;', // first transfinite cardinal
1410  8592 : '&larr;', // left arrow
1411  8593 : '&uarr;', // up arrow
1412  8594 : '&rarr;', // right arrow
1413  8595 : '&darr;', // down arrow
1414  8596 : '&harr;', // left-right arrow
1415  8629 : '&crarr;', // carriage return arrow
1416  8656 : '&lArr;', // left double arrow
1417  8657 : '&uArr;', // up double arrow
1418  8658 : '&rArr;', // right double arrow
1419  8659 : '&dArr;', // down double arrow
1420  8660 : '&hArr;', // left-right double arrow
1421  8704 : '&forall;', // for all
1422  8706 : '&part;', // partial differential
1423  8707 : '&exist;', // there exists
1424  8709 : '&empty;', // empty set, diameter
1425  8711 : '&nabla;', // backward difference
1426  8712 : '&isin;', // element of
1427  8713 : '&notin;', // not an element of
1428  8715 : '&ni;', // contains as member
1429  8719 : '&prod;', // product sign
1430  8721 : '&sum;', // n-ary sumation
1431  8722 : '&minus;', // minus sign
1432  8727 : '&lowast;', // asterisk operator
1433  8730 : '&radic;', // radical sign
1434  8733 : '&prop;', // proportional to
1435  8734 : '&infin;', // infinity
1436  8736 : '&ang;', // angle
1437  8743 : '&and;', // logical and, wedge
1438  8744 : '&or;', // logical or, vee
1439  8745 : '&cap;', // intersection, cap
1440  8746 : '&cup;', // union, cup
1441  8747 : '&int;', // integral
1442  8756 : '&there4;', // therefore
1443  8764 : '&sim;', // tilde operator, similar to
1444  8773 : '&cong;', // approximatley equal to
1445  8776 : '&asymp;', // asymptotic to
1446  8800 : '&ne;', // not equal to
1447  8801 : '&equiv;', // identical to
1448  8804 : '&le;', // less-than or equal to
1449  8805 : '&ge;', // greater-than or equal to
1450  8834 : '&sub;', // subset of
1451  8835 : '&sup;', // superset of
1452  8836 : '&nsub;', // not a subset of
1453  8838 : '&sube;', // subset of or equal to
1454  8839 : '&supe;', // superset of or qual to
1455  8853 : '&oplus;', // direct sum
1456  8855 : '&otimes;', // vector product
1457  8869 : '&perp;', // perpendicular
1458  8901 : '&sdot;', // dot operator
1459  8968 : '&lceil;', // apl upstile
1460  8969 : '&rceil;', // right ceiling
1461  8970 : '&lfloor;', // apl downstile
1462  8971 : '&rfloor;', // right floor
1463  9001 : '&lang;', // left-pointing angle bracket, bra
1464  9002 : '&rang;', // right-pointing angle bracket, ket
1465  9674 : '&loz;', // lozenge
1466  9824 : '&spades;', // black spade suit
1467  9827 : '&clubs;', // black club suit, shamrock
1468  9829 : '&hearts;', // black heart suit, valentine
1469  9830 : '&diams;' // black diamond suit
1470  };
1471  }
1472 
1473  // Carry out replacement for each entities (see bug #4182)
1474  for(var code in entities_array) {
1475  entity = entities_array[code];
1476 
1477  var reg = new RegExp("&#" + code + ";", "g");
1478  html_code = html_code.replace(reg, entity);
1479 
1480  var reg = new RegExp(String.fromCharCode(code), "g");
1481  html_code = html_code.replace(reg, entity);
1482  }
1483 
1484  return html_code;
1485  };
1486 
1487  </script>
1488 
1489  <script type="text/javascript" src="<?php echo $this->web_path?>core/core.js"></script>
1490  <script type="text/javascript" src="<?php echo $this->web_path?>core/utility.js"></script>
1491  <script type="text/javascript" src="<?php echo $this->web_path?>core/htmlarea-lang-en.js"></script>
1492  <script type="text/javascript" src="<?php echo $this->web_path?>core/dialog.js"></script>
1493 
1494  <style type="text/css">
1495  @import url("<?php echo $this->web_path?>core/htmlarea.css");
1496  </style>
1497  <?php
1498  $WYSIWYG_INIT = TRUE;
1499  }//end if
1500  ?>
1501 
1502  <?php $this->print_plugin_functions()?>
1503 
1504  <?php $this->print_config()?>
1505 
1506  <script type="text/javascript">
1507  // Handle unknown IE tags
1508  if (HTMLArea.is_ie) {
1509  // Create a dummy "abbr" element for the DOM
1510  var dom_abbr = document.createElement('ABBR');
1511  }
1512  </script>
1513 
1514  <textarea id="<?php echo $this->name?>" name="<?php echo $this->name?>" <?php echo $this->textarea_extras?>><?php echo htmlentities($this->contents, ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET); ?></textarea>
1515 
1516  <script type="text/javascript">
1517  editor_<?php echo $this->name?> = new HTMLArea("<?php echo $this->name?>");
1518  editor_<?php echo $this->name?>._createToolbar();
1519  editor_<?php echo $this->name?>._createStatusBar();
1520  editor_<?php echo $this->name?>.updateToolbar(false);
1521  editor_<?php echo $this->name?>.set_dir_attr('<?php echo $this->dir_attribute?>');
1522  <?php
1523  if ($this->init_onload) {
1524  ?>
1525  OtherOnLoad_<?php echo $this->name?> = (window.onload) ? window.onload : new Function;
1526  window.onload = function() {
1527  editor_<?php echo $this->name?>.generate();
1528  OtherOnLoad_<?php echo $this->name?>();
1529  };
1530  <?php
1531  }
1532  ?>
1533  </script>
1534  <?php
1535 
1536  }//end paint()
1537 
1538 
1546  function process()
1547  {
1548  if (!isset($_REQUEST[$this->name])) return FALSE;
1549  $html = $_REQUEST[$this->name];
1550 
1551  // replace out special form tags back with real form tags
1552  $html = preg_replace('|<HTMLAREA_FORM ([^>]*?)>(.*?)</HTMLAREA_FORM>|is', '<FORM \\1>\\2</FORM>', $html);
1553 
1554  if (!isset($_POST['ees_content_no_filter'])) {
1555  for (reset($this->_loaded_plugins); NULL !== ($k = key($this->_loaded_plugins)); next($this->_loaded_plugins)) {
1556  $plugin =& $this->_loaded_plugins[$k];
1557  $plugin->process($html);
1558  }
1559  }
1560 
1561  return $html;
1562 
1563  }//end process()
1564 
1565 
1574  function print_config()
1575  {
1576  ?>
1577  <script type="text/javascript">
1578  HTMLArea.Config_<?php echo $this->name?> = function () {
1579  this.version = "3.0";
1580 
1581  // enable creation of a status bar?
1582  this.statusBar = <?php echo ($this->show_status_bar) ? 'true' : 'false'; ?>;
1583 
1584  this.width = "<?php echo $this->width?>";
1585  this.height = "<?php echo $this->height?>";
1586 
1587  // use an iFrame or a DIV?
1588  this.bodyType = "<?php echo $this->body_type?>";
1589 
1590  // the next parameter specifies whether the toolbar should be included
1591  // in the size or not.
1592  this.sizeIncludesToolbar = true;
1593 
1594  this.bodyStyle = "background-color: #ffffff; font-family: verdana,sans-serif";
1595  this.styleSheet = "<?php echo $this->stylesheet?>";
1596  this.editorURL = "";
1597 
1598  // URL-s
1599  this.imgURL = "<?php echo $this->web_path?>images/";
1600  this.pluginURL = "<?php echo $this->web_path?>plugins/";
1601 
1602  this.replaceNextLines = 0;
1603  this.plainTextInput = 0;
1604 
1605  this.relativeHrefChecks = Array(
1606  <?php
1607  $i = count($this->relative_href_checks);
1608  foreach ($this->relative_href_checks as $find => $replace) {
1609  echo "Array(\"$find\", \"$replace\")".(($i > 1) ? ',' : '')."\n";
1610  $i--;
1611  }
1612  ?>
1613  );
1614  this.absoluteUrlChecks = Array(
1615  <?php
1616  $i = count($this->absolute_url_checks);
1617  foreach ($this->absolute_url_checks as $find => $replace) {
1618  echo "Array(\"$find\", \"$replace\")".(($i > 1) ? ',' : '')."\n";
1619  $i--;
1620  }
1621  ?>
1622  );
1623 
1624  this.toolbar = [
1625  <?php
1626  if (!empty($this->_plugins)) {
1627  $output = '';
1628  foreach ($this->_plugins as $plugin_group) {
1629  if (!empty($plugin_group)) {
1630  $pre_group_output = $output;
1631  $output .= '[ ';
1632  $printed_buttons = FALSE;
1633  foreach ($plugin_group as $name) {
1634  $plugin = $this->_loaded_plugins[$name];
1635  foreach ($plugin->buttons as $id => $button) {
1636  if (isset($button['menu_end'])) {
1637  $output .= ' ], ';
1638  continue;
1639  }
1640  if (isset($button['menu_start'])) $output .= '[ ';
1641  if ($id) $output .= '["'.$id.'"], ';
1642  $printed_buttons = TRUE;
1643  }
1644  unset($plugin);
1645  }
1646  if ($printed_buttons) {
1647  $output .= '["separator"] ],'."\n\t\t\t\t\t\t\t";
1648  } else {
1649  $output = $pre_group_output;
1650  }//end else
1651  }
1652  }
1653  $output = trim($output,"\t\n,");
1654  echo $output;
1655  }
1656  ?>
1657  ];
1658 
1659  // ID, CMD, ToolTip, In text mode?, Image Position, Context
1660  this.btnList = {
1661  <?php
1662  $output = '';
1663  foreach ($this->_loaded_plugins as $name => $plugin) {
1664  foreach ($plugin->buttons as $id => $button) {
1665  if (isset($button['menu_end'])) continue;
1666  if (!$button['command']) continue;
1667  $output .= "$id:".str_repeat(' ',30 - strlen($id))."[\"".$button['command']."\",".str_repeat(' ',30 - strlen($button['command']))."\"".$button['tooltip']."\",".str_repeat(' ',30 - strlen($button['tooltip']))."".$button['textmode'].", ".(($button['textmode'] === 'true') ? ' ' : '')."".$button['position'].", \"".$button['context']."\"],\n\t\t\t\t\t\t";
1668  }
1669  }
1670  $output = trim($output,"\t\n,");
1671  echo $output;
1672  ?>
1673  };
1674 
1675  // initialize tooltips from the I18N module
1676  for (var i in this.btnList) {
1677  var btn = this.btnList[i];
1678  if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {
1679  btn[1] = HTMLArea.I18N.tooltips[i];
1680  }
1681  }
1682  };
1683  </script>
1684  <?php
1685 
1686  }//end print_config()
1687 
1688 
1695  function print_plugin_vars()
1696  {
1697  foreach ($this->_loaded_plugins as $name => $plugin) {
1698  $plugin->print_plugin_vars();
1699  }
1700 
1701  }//end print_plugin_vars()
1702 
1703 
1710  function print_plugin_shortcuts()
1711  {
1712  foreach ($this->_loaded_plugins as $name => $plugin) {
1713  $plugin->print_plugin_shortcuts();
1714  }
1715 
1716  }//end print_plugin_shortcuts()
1717 
1718 
1725  function print_plugin_create_select()
1726  {
1727  foreach ($this->_loaded_plugins as $name => $plugin) {
1728  $plugin->print_plugin_create_select();
1729  }
1730 
1731  }//end print_plugin_create_select()
1732 
1733 
1740  function print_plugin_functions()
1741  {
1742  foreach ($this->_loaded_plugins as $name => $plugin) {
1743  $plugin->paint();
1744  }
1745 
1746  }//end print_plugin_functions()
1747 
1748 
1755  function print_plugin_generic_functions()
1756  {
1757  foreach ($this->_loaded_plugins as $name => $plugin) {
1758  $plugin->paint_generic();
1759  }
1760 
1761  }//end print_plugin_generic_functions()
1762 
1763 
1770  function print_plugin_button_clicks()
1771  {
1772  foreach ($this->_loaded_plugins as $name => $plugin) {
1773  $plugin->print_plugin_button_click();
1774  }
1775 
1776  }//end print_plugin_button_clicks()
1777 
1778 
1785  function print_plugin_combo_selected()
1786  {
1787  foreach ($this->_loaded_plugins as $name => $plugin) {
1788  $plugin->print_plugin_combo_selected();
1789  }
1790 
1791  }//end print_plugin_combo_selected()
1792 
1793 
1800  function print_plugin_button_type()
1801  {
1802  foreach ($this->_loaded_plugins as $name => $plugin) {
1803  $plugin->print_plugin_button_type();
1804  }
1805 
1806  }//end print_plugin_button_type()
1807 
1808 
1815  function print_plugin_update_toolbar()
1816  {
1817  foreach ($this->_loaded_plugins as $name => $plugin) {
1818  $plugin->print_plugin_update_toolbar();
1819  }
1820 
1821  }//end print_plugin_update_toolbar()
1822 
1823 
1830  function print_plugin_exec_commands()
1831  {
1832  foreach ($this->_loaded_plugins as $name => $plugin) {
1833  $plugin->print_plugin_exec_command();
1834  }
1835 
1836  }//end print_plugin_exec_commands()
1837 
1838 
1845  function print_plugin_pre_get_html()
1846  {
1847  foreach ($this->_loaded_plugins as $name => $plugin) {
1848  $plugin->print_plugin_pre_get_html();
1849  }
1850 
1851  }//end print_plugin_pre_get_html()
1852 
1853 
1860  function print_plugin_get_html()
1861  {
1862  foreach ($this->_loaded_plugins as $name => $plugin) {
1863  $plugin->print_plugin_get_html();
1864  }
1865 
1866  }//end print_plugin_get_html()
1867 
1868 
1875  function print_plugin_form_submit()
1876  {
1877  foreach ($this->_loaded_plugins as $name => $plugin) {
1878  $plugin->print_plugin_form_submit();
1879  }
1880 
1881  }//end print_plugin_form_submit()
1882 
1883 
1890  function print_plugin_event_handlers()
1891  {
1892  foreach ($this->_loaded_plugins as $name => $plugin) {
1893  $plugin->print_plugin_event_handler();
1894  }
1895 
1896  }//end print_plugin_event_handlers()
1897 
1898 
1913  function add_plugin($name)
1914  {
1915  global $ROOT_PATH;
1916  if (!isset($this->_loaded_plugins[$name])) {
1917  require_once($ROOT_PATH.'/plugins/'.$name.'/'.$name.'.inc');
1918  $this->_loaded_plugins[$name] = new $name($this);
1919  }
1920 
1921  $own_group = FALSE;
1922  if ($this->_loaded_plugins[$name]->_show_in_toolbar && !$this->_group_open) {
1923  if (!$this->open_group()) return FALSE;
1924  $own_group = TRUE;
1925  }
1926 
1927  $groupid = count($this->_plugins) - 1;
1928  $this->_plugins[$groupid][] = $name;
1929 
1930  if ($own_group) return $this->close_group();
1931  return TRUE;
1932 
1933  }//end add_plugin()
1934 
1935 
1943  function open_group()
1944  {
1945  if ($this->_group_open) return FALSE;
1946  $this->_group_open = TRUE;
1947  $new_group = Array();
1948  return array_push($this->_plugins, $new_group);
1949 
1950  }//end open_group()
1951 
1952 
1960  function close_group()
1961  {
1962  if (!$this->_group_open) return FALSE;
1963  $this->_group_open = FALSE;
1964  return TRUE;
1965 
1966  }//end close_group()
1967 
1968 
1978  function plugin_loaded($name)
1979  {
1980  return isset($this->_loaded_plugins[$name]);
1981 
1982  }//end plugin_loaded()
1983 
1984 
1993  function &get_plugin($name)
1994  {
1995  if (!isset($this->_loaded_plugins[$name])) {
1996  $null = NULL;
1997  return $null;
1998  }
1999  return $this->_loaded_plugins[$name];
2000 
2001  }//end get_plugin()
2002 
2003 
2012  function set_body_type($type='iframe')
2013  {
2014  $this->body_type = $type;
2015 
2016  }//end set_body_type()
2017 
2018 
2027  function set_init_onload($init=TRUE)
2028  {
2029  $this->init_onload = (bool) $init;
2030 
2031  }//end set_init_onload()
2032 
2033 
2042  function set_show_status_bar($show=TRUE)
2043  {
2044  $this->show_status_bar = (bool)$show;
2045 
2046  }//end set_show_status_bar()
2047 
2048 
2057  function set_width($width='auto')
2058  {
2059  if (is_numeric($width)) $width .= 'px';
2060  $this->width = $width;
2061 
2062  }//end set_width()
2063 
2064 
2073  function set_height($height='auto')
2074  {
2075  if (is_numeric($height)) $height .= 'px';
2076  $this->height = $height;
2077 
2078  }//end set_height()
2079 
2080 
2090  function set_textarea_extras($extras='')
2091  {
2092  $this->textarea_extras = trim($extras);
2093 
2094  }//end set_textarea_extras()
2095 
2096 
2105  function set_contents($contents)
2106  {
2107  foreach ($this->_loaded_plugins as $name => $plugin) {
2108  $plugin->set_contents($contents);
2109  }
2110 
2111  // replace form tags in the HTML with special form tags that will not cuase JS errors
2112  // due to nested forms on the page
2113  $contents = preg_replace('|<FORM ([^>]*?)>(.*?)</FORM>|is', '<HTMLAREA_FORM \\1>\\2</HTMLAREA_FORM>', $contents);
2114  $this->contents = $contents;
2115 
2116  }//end set_contents()
2117 
2118 
2127  function set_stylesheet($stylesheet)
2128  {
2129  $this->stylesheet = $stylesheet;
2130 
2131  }//end set_stylesheet()
2132 
2133 
2142  function set_dir_attr($dir_attribute)
2143  {
2144  $this->dir_attribute = $dir_attribute;
2145  }//end set_dir_attr()
2146 
2147 
2157  function add_relative_href_check($find, $replace)
2158  {
2159  $find = str_replace('\\', '\\\\\\', $find);
2160  $replace = str_replace('\\', '\\\\\\', $replace);
2161  $this->relative_href_checks[$find] = $replace;
2162 
2163  }//end add_relative_href_check()
2164 
2165 
2175  function add_absolute_url_check($find, $replace)
2176  {
2177  $find = str_replace('\\', '\\\\\\', $find);
2178  $replace = str_replace('\\', '\\\\\\', $replace);
2179  $this->absolute_url_checks[$find] = $replace;
2180 
2181  }//end add_absolute_url_check()
2182 
2183 
2184 }//end class
2185 
2186 ?>