Iframe editable et insertion de code (IE & Firefox)

WRInaute passionné
Petite question auquel quelqu'un a surement déjà été confronté.

Je suis en train de mettre à jour un editeur WYSIWYG tout veillot qui ne marchait que sous IE pour une version compatible firefox.

J'utilise pour ça une iframe (je connais pas d'autres moyen vraiment efficace) editable (supporté depuis la 1.3 de FF je crois). Tout ce qui est mise en gras, italique, souligné, ... très simple à mettre en oeuvre avec des execCommand().

Ce genre de fonction :
function setContent(action){
if(IE){
ewin = window.frames['editeur'];
edoc = ewin.document;
}
if(MOZ){
ewin = document.getElementById('editeur').contentWindow;
edoc = document.getElementById('editeur').contentDocument;
}
edoc.execCommand(action, false, null);
ewin.focus();
}



Seulement je bloque pour réaliser des opération plus complexe; je voudrais par exemple, sur une selection réalisée sur du texte dans ma iframe, insérer des balises plus personalisées (du genre <a class="nomclasse" href="url">Mon texte selectionné</a>).

En gros faire comme le execCommand() quand il met du gras mais là en insérant le code que je souhaite; puis en replaçant le curseur au bon endroit.

Je suis a peu prés sure que ça existe déjà mais j'ai pas réussi à trouver une méthode convenable.

Si quelqu'un à la solution je suis prenneur, merci!
 
WRInaute passionné
Oui je connais; je me suis basé sur un tout simple (j'ai besoin de fonction "de base" comme mettre en gras, souligné, faire un lien); mais j'ai besoin d'y ajouter quelques fonctionnalité spécifique pour mon site.

En gros :
- j'ai une liste déroulante avec des noms de groupe
- je sélectionne du texte ds mon iframe; je vais cliquer sur le nom du groupe dans la liste déroulante et ça me créé un lien automatiquement (un lien avec un attribut class="") vers la page du groupe.

J'ai ça dans une version qui ne marche que pour IE, j'essaye donc d'adapter pour une version qui marcherait aussi sous firefox. J'ai tout repris depuis le début pour coder propre mais je bloque sur ça.
 
WRInaute passionné
Ah, en cherchant via HTMLarea que je ne connaissais pas je suis tombé sur la definition de mozilla pour les méthodes possibles.

J'ai donc trouvé inserthtml; ça devrait résoudre mon problème. Je test et j'éditerais le nom du topic si ça marche ;)

merci
 
WRInaute accro
tu sais que sur ces editeurs, tu peux desactiver les fonctions pour ne garder que celles que tu veux utiliser ?
 
WRInaute passionné
J'ai réussi, après moulte essais, le SAX / DOM et javascript n'étant pas trop ma spécialité.

Y'a peut être plus propre mais si jamais ça peut aider certains qui souhaite éviter d'utiliserun script déjà tout fait et un peu obscure si on souhaite y ajouter des fonctionnalités :

Je me suis inspiré de différents editeur et plus particuliérement de celui exemple de mozilla : http://www.mozilla.org/editor/midasdemo/


Tout d'abord un javascript (j'ai mis les balises script mais le mieux reste de le placer dans un fichier a part.

Javascript :
Code:
<script type="text/javascript">
var command = "";
IE  = window.ActiveXObject ? true : false;
MOZ = window.sidebar       ? true : false;


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;
    }
	else
	{
		if (kids[i].className == "imageperso") 
		{
		  kids[i].onmouseover = tbmouseover;
		  kids[i].onmouseout = tbmouseout;
		  kids[i].onmousedown = tbmousedown;
		  kids[i].onmouseup = tbmouseup;     
		}
	}
  }
}

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 getTexteselect()
{
	if (MOZ)
	{
		var sel = e.contentWindow.getSelection();
	}
	else
	{
		var sel = e.contentWindow.document.selection.createRange().text;
	}
	
	return sel;
}


function getIframe()
{
	e = document.getElementById("edit");
	
	return e;
}


function inserercode(code,e)
{
	if(MOZ)
	{
	   insertNodeAtSelection(e.contentWindow, code)
    }
    else
    {
	   var insert_pos = e.contentWindow.document.selection.createRange();
	   insert_pos.pasteHTML(code);
	}
}

function liengroupe(url)
{
	var e = getIframe();
	var sel = getTexteselect();
   
   if (MOZ)
   {
	   lien = e.contentWindow.document.createElement("a");  
	   texte = e.contentWindow.document.createTextNode(sel);
	   lien.appendChild(texte);   
	   lien.setAttribute("class", "groupe");
	   lien.setAttribute("href", url);
	   
   }
   else
   {
	   var lien = "<a href=\"" + url + "\" class=\"groupe\">" + sel + "</a>";	   
   }
	
	inserercode(lien, e);
}


function setClasse(nomClasse)
{
	var e = getIframe();
	var sel = getTexteselect();
	
	if (MOZ)
   {
	   span = e.contentWindow.document.createElement("span");  
	   texte = e.contentWindow.document.createTextNode(sel);
	   span.appendChild(texte);   
	   span.setAttribute("class", nomClasse);	   
   }
   else
   {
	   var span = "<span class=\"" + nomClasse + "\">" + sel + "</a>";	   
   }
   
   inserercode(span, e);
}

function insertNodeAtSelection(win, insertNode)
{
	// get current selection
     
	if (MOZ)
	{
		var sel = e.contentWindow.getSelection();
	}
	else
	{
		var sel = e.contentWindow.document.selection.text;
	}
	  

      // 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 level of Mozilla.");
  }

  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>

Ensuite un CSS à ajouter a votre feuille de style ou à insérer dans votre page entre les baslies head :

CSS :
Code:
<style type="text/css">
.imagebutton {height: 22; width: 23; border: solid 2px #C0C0C0; background-color: #C0C0C0}
.imageperso {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>

A rajouter à votre balise body :
Code:
<body  onload="Start()">

Ensuite placer ça où vous souhaitez voir apparaitre votre éditeur WYSIWYG :

(ajouter ou virer des commentaires selon ce que vous souhaitez)
(j'ai un select et un bouton spécfique à mes besoins, mais ça montre un exemple d'utilisation pour une personnalisation)

Code:
<table bgcolor="#C0C0C0" id="toolbar1">
<tr>
<td>
<div class="imagebutton" id="cut"><img class="image" src="img/cut.gif" alt="Cut" title="Cut"></div>
</td>
<td>
<div class="imagebutton" id="copy"><img class="image" src="img/copy.gif" alt="Copy" title="Copy"></div>
</td>
<td>
<div class="imagebutton" id="paste"><img class="image" src="img/paste.gif" alt="Paste" title="Paste"></div>
<td>
</td>
<td>
</td>
<td>
<div class="imagebutton" id="undo"><img class="image" src="img/undo.gif" alt="Undo" title="Undo"></div>
</td>
<td>
<div class="imagebutton" id="redo"><img class="image" src="img/redo.gif" alt="Redo" title="Redo"></div>
</td>
<td>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="createlink"><img class="image" src="img/link.gif" alt="Insert Link" title="Insert Link"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="createimage"><img class="image" src="img/image.gif" alt="Insert Image" title="Insert Image"></div>
</td>
<td>
</td>
</tr>
</table>
<br>
<table bgcolor="#C0C0C0" id="toolbar2">
<tr>
<td>
<select name="groupe" id="list_grp"  onChange="liengroupe(this.options[this.selectedIndex].value)">
<option>-- Liste item --</option>
<?php
// là vous pouvez insérer une liste d'item avec dans le value l'URL voulue
?>
</select>
<!--
<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="imageperso" id="grp"><img class="image" src="img/groupe.gif" alt="Groupe" title="Couleur groupe" onclick="setClasse('groupe');"></div>
</td>
<td>
<div class="imagebutton" id="bold"><img class="image" src="img/bold.gif" alt="Bold" title="Bold"></div>
</td>
<td>
<div class="imagebutton" id="italic"><img class="image" src="img/italic.gif" alt="Italic" title="Italic"></div>
</td>

<td>
<div class="imagebutton" id="underline"><img class="image" src="img/underline.gif" alt="Underline" title="Underline"></div>
</td>
<td>
</td>
<!--
<td>
<div style="left: 10;" class="imagebutton" id="forecolor"><img class="image" src="img/forecolor.gif" alt="Text Color" title="Text Color"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="hilitecolor"><img class="image" src="img/backcolor.gif" alt="Background Color" title="Background Color"></div>
</td>
-->
<td>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="justifyleft"><img class="image" src="img/ciJustifyleft.gif" alt="Align Left" title="Align Left"></div>
</td>
<td>

<div style="left: 40;" class="imagebutton" id="justifycenter"><img class="image" src="img/ciJustifycenter.gif" alt="Center" title="Center"></div>
</td>
<td>
<div style="left: 70;" class="imagebutton" id="justifyright"><img class="image" src="img/ciJustifyright.gif" alt="Align Right" title="Align Right"></div>
</td>
<td>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="insertorderedlist"><img class="image" src="img/orderedlist.gif" alt="Ordered List" title="Ordered List"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="insertunorderedlist"><img class="image" src="img/unorderedlist.gif" alt="Unordered List" title="Unordered List"></div>
</td>
<td>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="outdent"><img class="image" src="img/outdent.gif" alt="Outdent" title="Outdent"></div>

</td>
<td>
<div style="left: 40;" class="imagebutton" id="indent"><img class="image" src="img/indent.gif" alt="Indent" title="Indent"></div>
</td>
</tr>
</table>
<br>
<iframe id="edit" width="100%" height="200px"></iframe>
<iframe width="250" height="170" id="colorpalette" src="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("toolbar1").style.visibility="hidden";
    document.getElementById("toolbar2").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("toolbar1").style.visibility="visible";
    document.getElementById("toolbar2").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)">
Voir source HTML</input>
<!--
<input checked type="checkbox" onclick="usecss(this.checked)">
Use CSS</input>
-->
<!--
<input type="checkbox" onclick="readonly(this.checked)">
Read only</input>
-->

Voilà, mon code n'est peut être pas parfait mais il marche sur IE et Firefox, après libre à chacun de l'adapter (pour l'utiliser dans un formulaire il reste encore à ajouter une fonction pour récupérer le texte de l'iframe et le placer dans un textarea, et une méthode pour placer le texte dans cette iframe aussi. Mais les bases sont là.

(en ésperant que ça puisse servir à quelqu'un).[/code]
 
Discussions similaires
Haut