1

我目前正在开展一个项目,以启动并运行富文本编辑器,该编辑器允许在按下标记键后在光标位置出现自动完成建议,例如“@”。我已经通过 jQuery 在 textarea 中毫无问题地完成了这项工作,但它在 RTE iFrame 中表现不佳。

我通过 AJAX 扫描服务器文件以检查为自动完成“标签”键入的文本的自动完成示例。

Mozilla 编辑器:www-archive.mozilla.org/editor/midasdemo/

自动完成来自:www.amirharel.com/2011/03/07/implementing-autocomplete-jquery-plugin-for-textarea/

<html>
<head>

<style type="text/css">
.imagebutton {height: 22; width: 23; border: solid 2px #C0C0C0; background-color: #C0C0C0}
.image {position: relative; left: 1; top: 1; height:20; width:21; border:none;}
.toolbar {height: 30; background-color: #C0C0C0;}
</style>

<script>

var command = "";

function InitToolbarButtons() {
  var kids = document.getElementsByTagName('DIV');
  for (var i=0; i < kids.length; i++) {
    if (kids[i].className == "imagebutton") {
      kids[i].onmouseover = tbmouseover;
      kids[i].onmouseout = tbmouseout;
      kids[i].onmousedown = tbmousedown;
      kids[i].onmouseup = tbmouseup;
      kids[i].onclick = tbclick;
    }
  }
}
function tbmousedown(e)
{
  var evt = e ? e : window.event; 
  this.firstChild.style.left = 2;
  this.firstChild.style.top = 2;
  this.style.border="inset 2px";
  if (evt.returnValue) {
    evt.returnValue = false;
  } else if (evt.preventDefault) {
    evt.preventDefault( );
  } else {
    return false;
  }
}
function tbmouseup()
{
  this.firstChild.style.left = 1;
  this.firstChild.style.top = 1;
  this.style.border="outset 2px";
}
function tbmouseout()
{
  this.style.border="solid 2px #C0C0C0";
}
function tbmouseover()
{
  this.style.border="outset 2px";
}
  function insertNodeAtSelection(win, insertNode)
  {
      // get current selection
      var sel = win.getSelection();
      // get the first range of the selection
      // (there's almost always only one range)
      var range = sel.getRangeAt(0);
      // deselect everything
      sel.removeAllRanges();
      // remove content of current selection from document
      range.deleteContents();
      // get location of current selection
      var container = range.startContainer;
      var pos = range.startOffset;
      // make a new range for the new selection
      range=document.createRange();
      if (container.nodeType==3 && insertNode.nodeType==3) {
        // if we insert text in a textnode, do optimized insertion
        container.insertData(pos, insertNode.nodeValue);
        // put cursor after inserted text
        range.setEnd(container, pos+insertNode.length);
        range.setStart(container, pos+insertNode.length);
      } else {
        var afterNode;
        if (container.nodeType==3) {
          // when inserting into a textnode
          // we create 2 new textnodes
          // and put the insertNode in between
          var textNode = container;
          container = textNode.parentNode;
          var text = textNode.nodeValue;
          // text before the split
          var textBefore = text.substr(0,pos);
          // text after the split
          var textAfter = text.substr(pos);
          var beforeNode = document.createTextNode(textBefore);
          afterNode = document.createTextNode(textAfter);
          // insert the 3 new nodes before the old one
          container.insertBefore(afterNode, textNode);
          container.insertBefore(insertNode, afterNode);
          container.insertBefore(beforeNode, insertNode);
          // remove the old node
          container.removeChild(textNode);
        } else {
          // else simply insert the node
          afterNode = container.childNodes[pos];
          container.insertBefore(insertNode, afterNode);
        }
        range.setEnd(afterNode, 0);
        range.setStart(afterNode, 0);
      }
      sel.addRange(range);
  };
function getOffsetTop(elm) {
  var mOffsetTop = elm.offsetTop;
  var mOffsetParent = elm.offsetParent;
  while(mOffsetParent){
    mOffsetTop += mOffsetParent.offsetTop;
    mOffsetParent = mOffsetParent.offsetParent;
  }

  return mOffsetTop;
}
function getOffsetLeft(elm) {
  var mOffsetLeft = elm.offsetLeft;
  var mOffsetParent = elm.offsetParent;
  while(mOffsetParent){
    mOffsetLeft += mOffsetParent.offsetLeft;
    mOffsetParent = mOffsetParent.offsetParent;
  }

  return mOffsetLeft;
}
function tbclick()
{
  if ((this.id == "forecolor") || (this.id == "hilitecolor")) {
    parent.command = this.id;
    buttonElement = document.getElementById(this.id);
    document.getElementById("colorpalette").style.left = getOffsetLeft(buttonElement);
    document.getElementById("colorpalette").style.top = getOffsetTop(buttonElement) + buttonElement.offsetHeight;
    document.getElementById("colorpalette").style.visibility="visible";
  } else if (this.id == "createlink") {
    var szURL = prompt("Enter a URL:", "http://");
    if ((szURL != null) && (szURL != "")) {
      document.getElementById('edit').contentWindow.document.execCommand("CreateLink",false,szURL);
    }
  } else if (this.id == "createimage") {
    imagePath = prompt('Enter Image URL:', 'http://');
    if ((imagePath != null) && (imagePath != "")) {
      document.getElementById('edit').contentWindow.document.execCommand('InsertImage', false, imagePath);
    }
  } else if (this.id == "createtable") {
    e = document.getElementById("edit");
    rowstext = prompt("enter rows");
    colstext = prompt("enter cols");
    rows = parseInt(rowstext);
    cols = parseInt(colstext);
    if ((rows > 0) && (cols > 0)) {
      table = e.contentWindow.document.createElement("table");
      table.setAttribute("border", "1");
      table.setAttribute("cellpadding", "2");
      table.setAttribute("cellspacing", "2");
      tbody = e.contentWindow.document.createElement("tbody");
      for (var i=0; i < rows; i++) {
        tr =e.contentWindow.document.createElement("tr");
        for (var j=0; j < cols; j++) {
          td =e.contentWindow.document.createElement("td");
          br =e.contentWindow.document.createElement("br");
          td.appendChild(br);
          tr.appendChild(td);
        }
        tbody.appendChild(tr);
      }
      table.appendChild(tbody);      
      insertNodeAtSelection(e.contentWindow, table);
    }
  } else {
    document.getElementById('edit').contentWindow.document.execCommand(this.id, false, null);
  }
}
function Select(selectname)
{
  var cursel = document.getElementById(selectname).selectedIndex;
  /* First one is always a label */
  if (cursel != 0) {
    var selected = document.getElementById(selectname).options[cursel].value;
    document.getElementById('edit').contentWindow.document.execCommand(selectname, false, selected);
    document.getElementById(selectname).selectedIndex = 0;
  }
  document.getElementById("edit").contentWindow.focus();
}
function dismisscolorpalette()
{
  document.getElementById("colorpalette").style.visibility="hidden";
}
function Start() {
  document.getElementById('edit').contentWindow.document.designMode = "on";
  try {
    document.getElementById('edit').contentWindow.document.execCommand("undo", false, null);
  }  catch (e) {
    alert("This demo is not supported on your browser.");
  }
  InitToolbarButtons();
  if (document.addEventListener) {
    document.addEventListener("mousedown", dismisscolorpalette, true);
    document.getElementById("edit").contentWindow.document.addEventListener("mousedown", dismisscolorpalette, true);
    document.addEventListener("keypress", dismisscolorpalette, true);
    document.getElementById("edit").contentWindow.document.addEventListener("keypress", dismisscolorpalette, true);
  } else if (document.attachEvent) {
    document.attachEvent("mousedown", dismisscolorpalette, true);
    document.getElementById("edit").contentWindow.document.attachEvent("mousedown", dismisscolorpalette, true);
    document.attachEvent("keypress", dismisscolorpalette, true);
    document.getElementById("edit").contentWindow.document.attachEvent("keypress", dismisscolorpalette, true);
  }
}
</script>

<style type="text/css" style="display:none">ul.auto-list{display:none;position:absolute;top:0px;left:0px;border:1px solid green;background-color:#A3DF99;padding:0;margin:0;list-style:none;}ul.auto-list>li:hover,ul.auto-list>li[data-selected=true]{background-color:#236574;}ul.auto-list>li{border:1px solid gray;cursor:default;padding:2px;}mark{font-weight:bold;}</style>
<style type="text/css">#ta{width:300px;height :100px;font-size:11px;font-family:"Helvetica Neue",Arial,sans-serif;white-space:pre;}</style>
<script src="js/jquery-1.5.js" type="text/javascript"></script>
<script src="js/autocomplete.js" type="text/javascript"></script>
<script type="text/javascript">
        var tags = [];
        function initEditorTextarea(){
            $.ajax("autotags.txt",{
                success: function(data, textStatus, jqXHR){
                    tags = data.replace(/\r/g, "" ).split("\n"); 
                    $("#editor textarea").autocomplete({
                        wordCount:1,
                        on: {
                            query: function(text,cb){
                                var words = [];
                                for( var i=0; i<tags.length; i++ ){
                                    if( tags[i].toLowerCase().indexOf(text.toLowerCase()) == 0 ) words.push(tags[i]);
                                    if( words.length > 5 ) break;
                                }
                                cb(words);                              
                            }
                        }
                    });
                }
            });
        }
        $(document).ready(function(){
            Start();
            initEditorTextarea();
        });
    </script>
</head>
<body>
    <div id="editor">
        <textarea style="width: 750px; height: 150px; margin-bottom: 10px;"></textarea>
    </div>
<table bgcolor="#C0C0C0" id="toolbar">
<tr>
<td>
<select id="formatblock" onchange="Select(this.id);">
  <option value="<p>">Normal</option>
  <option value="<p>">Paragraph</option>
  <option value="<h1>">Heading 1 <H1></option>
  <option value="<h2>">Heading 2 <H2></option>
  <option value="<h3>">Heading 3 <H3></option>
  <option value="<h4>">Heading 4 <H4></option>
  <option value="<h5>">Heading 5 <H5></option>
  <option value="<h6>">Heading 6 <H6></option>
  <option value="<address>">Address <ADDR></option>
  <option value="<pre>">Formatted <PRE></option>
</select>
</td>
<td>
<select id="fontname" onchange="Select(this.id);">
  <option value="Font">Font</option>
  <option value="Arial">Arial</option>
  <option value="Courier">Courier</option>
  <option value="Times New Roman">Times New Roman</option>
</select>
</td>
<td>
<select unselectable="on" id="fontsize" onchange="Select(this.id);">
  <option value="Size">Size</option>
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
  <option value="4">4</option>
  <option value="5">5</option>
  <option value="6">6</option>
  <option value="7">7</option>  
</select>
</td>
<td>
<div class="imagebutton" id="bold"><img class="image" src="images/bold.gif" alt="Bold" title="Bold"></div>
</td>
<td>
<div class="imagebutton" id="italic"><img class="image" src="images/italic.gif" alt="Italic" title="Italic"></div>
</td>
<td>
<div class="imagebutton" id="underline"><img class="image" src="images/underline.gif" alt="Underline" title="Underline"></div>
</td>
<td>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="forecolor"><img class="image" src="images/forecolor.gif" alt="Text Color" title="Text Color"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="hilitecolor"><img class="image" src="images/backcolor.gif" alt="Background Color" title="Background Color"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="justifyleft"><img class="image" src="images/justifyleft.gif" alt="Align Left" title="Align Left"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="justifycenter"><img class="image" src="images/justifycenter.gif" alt="Center" title="Center"></div>
</td>
<td>
<div style="left: 70;" class="imagebutton" id="justifyright"><img class="image" src="images/justifyright.gif" alt="Align Right" title="Align Right"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="insertorderedlist"><img class="image" src="images/orderedlist.gif" alt="Ordered List" title="Ordered List"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="insertunorderedlist"><img class="image" src="images/unorderedlist.gif" alt="Unordered List" title="Unordered List"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="outdent"><img class="image" src="images/outdent.gif" alt="Outdent" title="Outdent"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="indent"><img class="image" src="images/indent.gif" alt="Indent" title="Indent"></div>
<td>
<div style="left: 10;" class="imagebutton" id="createlink"><img class="image" src="images/link.gif" alt="Insert Link" title="Insert Link"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="createimage"><img class="image" src="images/image.gif" alt="Insert Image" title="Insert Image"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="createtable"><img class="image" src="images/table.gif" alt="Insert Table" title="Insert Table"></div>
</td>
</tr>
</table>
<br>
<iframe id="edit" width="100%" height="400px"></iframe>
<iframe width="250" height="170" id="colorpalette" src="images/colors.html" style="visibility:hidden; position: absolute;"></iframe>

<script>
function viewsource(source)
{
  var html;
  if (source) {
    html = document.createTextNode(document.getElementById('edit').contentWindow.document.body.innerHTML);
    document.getElementById('edit').contentWindow.document.body.innerHTML = "";
    html = document.getElementById('edit').contentWindow.document.importNode(html,false);
    document.getElementById('edit').contentWindow.document.body.appendChild(html);
    document.getElementById("toolbar").style.visibility="hidden"; 
  } else {
    html = document.getElementById('edit').contentWindow.document.body.ownerDocument.createRange();
    html.selectNodeContents(document.getElementById('edit').contentWindow.document.body);
    document.getElementById('edit').contentWindow.document.body.innerHTML = html.toString();
    document.getElementById("toolbar").style.visibility="visible"; 
  }
}

function usecss(source)
{
  document.getElementById('edit').contentWindow.document.execCommand("useCSS", false, !(source));  
}

function readonly(source)
{
    document.getElementById('edit').contentWindow.document.execCommand("readonly", false, !(source));  
}
</script>
<input type="checkbox" onclick="viewsource(this.checked)">
View HTML Source</input>

</body>
</html>

我花了很多时间研究基于浏览器的解决方案和基于 JS 的选项,但它们都没有奏效。我能找到的最好的例子是 Confluence RTE 环境:https ://confluence.atlassian.com/display/CONF33/Using+Autocomplete+in+the+Rich+Text+Editor

我不需要任何超级花哨的东西,只需要能够运行所见即所得的基本富文本编辑器,并能够在特定的键输入上自动完成。我应该如何让这个工作?

编辑:必须更改代码上方的链接,因为我是新来的,所以它们不会自动链接。

4

0 回答 0