Your Ad Here

Posted By

jatkins on 12/08/10


Tagged


Versions (?)

Who likes this?

1 person have marked this snippet as a favorite

brownrl


WYSIWYG Editor


 / Published in: JavaScript
 

  1. /*
  2.  * Created October 19, 2010 by Josh Atkins
  3.  * Updated December 7, 2010
  4.  * Released into the public domain
  5.  */
  6.  
  7. function get(elmnt) {
  8. return document.getElementById(elmnt);
  9. }
  10.  
  11. function find_index(array, string) {
  12. i = 0;
  13. for(i=0;i<array.length;i++) {
  14. if(array[i]==string) break;
  15. }
  16. return array[i] == string ? i : -1;
  17. }
  18.  
  19. var commands = ['bold', 'italic', 'underline', 'strikethrough', 'justifyleft', 'justifycenter', 'justifyright', 'insertunorderedlist', 'insertorderedlist', 'indent', 'outdent', 'subscript', 'superscript', 'insertlink', 'unlink', 'selectimage', 'inserttable', 'insertrowabove', 'insertrowbelow', 'insertcolumnbefore', 'insertcolumnafter'], titles = ['Bold', 'Italic', 'Underline', 'Strikethrough', 'Left Align', 'Center Align', 'Right Align', 'Insert Bulleted List', 'Insert Numbered List', 'Indent Text', 'Outdent Text', 'Subscript', 'Superscript', 'Insert Link', 'Remove Link', 'Insert Image', 'Insert Table', 'Insert Table Row Above', 'Insert Table Row Below', 'Insert Table Column Before', 'Insert Table Column After'], wysiwygCount = 0, wysiwygs = Array(), currentWYSIWYG = null, i, n, wysiwygContainer, wysiwyg, toolbar, button, buttonLink, buttonImage, cmd_value, cmd_true_or_false, click_code, tableDialog, tableRowsLabel, tableRowsInput, tableColumnsLabel, tableColumnsInput, tableInsertButton, tableHeading, newTable, newTableBody, newTableRow, newTableColumn, tableCount = 0, currentTable, cursorPosition, table, textAreaID, newTitles, commandsLength;
  20.  
  21. function toggleVisibility(elmnt) {
  22. get(elmnt).className = get(elmnt).className == 'hidden' ? '' : 'hidden';
  23. return get(elmnt).className == '';
  24. }
  25.  
  26. function positionBelow(elmntAbove, elmntBelow) {
  27. get(elmntBelow).style.top = get(elmntAbove).offsetTop + get(elmntAbove).offsetHeight + 'px';
  28. get(elmntBelow).style.left = get(elmntAbove).offsetLeft + 'px';
  29. if(get(elmntBelow).style.position!='absolute')
  30. get(elmntBelow).style.position = 'absolute';
  31. }
  32.  
  33. function replaceTextAreaWithWYSIWYG(textArea, onlyCommands, wysiwygWidth, wysiwygHeight) {
  34. wysiwygs[wysiwygCount] = textArea; // security issues?
  35. wysiwygCount++;
  36. wysiwygContainer = document.createElement('div');
  37. wysiwygContainer.id = 'wysiwygContainer' + wysiwygCount;
  38. wysiwygContainer.className = 'wysiwyg';
  39. wysiwyg = document.createElement('iframe');
  40. wysiwyg.id = 'WYSIWYG' + wysiwygCount;
  41. wysiwyg.className = 'wysiwyg';
  42. wysiwyg.frameBorder = '0';
  43. tableDialog = document.createElement('fieldset');
  44. tableDialog.id = 'fieldsetInsertTable' + wysiwygCount;
  45. tableDialog.className = 'hidden';
  46. tableHeading = document.createElement('h2');
  47. tableHeading.innerHTML = 'Insert Table';
  48. tableRowsLabel = document.createElement('label');
  49. tableRowsLabel.htmlFor = 'txtRowsInput' + wysiwygCount;
  50. tableRowsLabel.innerHTML = 'Number of rows:';
  51. tableRowsInput = document.createElement('input');
  52. tableRowsInput.type = 'text';
  53. tableRowsInput.id = 'txtRowsInput' + wysiwygCount;
  54. tableColumnsLabel = document.createElement('label');
  55. tableColumnsLabel.htmlFor = 'txtColumnsInput' + wysiwygCount;
  56. tableColumnsLabel.innerHTML = 'Number of columns:';
  57. tableColumnsInput = document.createElement('input');
  58. tableColumnsInput.type = 'text';
  59. tableColumnsInput.id = 'txtColumnsInput' + wysiwygCount;
  60. tableInsertButton = document.createElement('button');
  61. tableInsertButton.innerHTML = 'Insert';
  62. tableInsertButton.setAttribute('onclick', 'return insertTable();');
  63. insertLinkDialog = document.createElement('fieldset');
  64. insertLinkDialog.id = 'fieldsetInsertLink' + wysiwygCount;
  65. insertLinkDialog.className = 'hidden';
  66. insertLinkLabel = document.createElement('label');
  67. insertLinkLabel.id = 'lblLinkLabel' + wysiwygCount;
  68. insertLinkLabel.htmlFor = 'txtLinkURL' + wysiwygCount;
  69. insertLinkLabel.innerHTML = 'URL:';
  70. insertLinkInput = document.createElement('input');
  71. insertLinkInput.type = 'text';
  72. insertLinkInput.id = 'txtLinkURL' + wysiwygCount;
  73. insertLinkButton = document.createElement('button');
  74. insertLinkButton.innerHTML = 'Insert';
  75. insertLinkButton.setAttribute('onclick', 'wysiwygCommand(get(\'txtLinkURL'+wysiwygCount+'\').value, false, \'createlink\'); get(\'txtLinkURL'+wysiwygCount+'\').value = \'\'; get(\'btn'+wysiwygCount+'cmdinsertlink\').className = toggleVisibility(\'fieldsetInsertLink'+wysiwygCount+'\') ? \'pressed\' : \'\'; return false;');
  76. toolbar = document.createElement('ul');
  77. toolbar.id = 'ulWYSIWYGToolbar' + wysiwygCount;
  78. toolbar.className = 'wysiwyg';
  79. if(onlyCommands) {
  80. var newCommands = new Array(), commandIndex;
  81. newTitles = new Array();
  82. for(onlyCommand in onlyCommands) {
  83. commandIndex = find_index(commands, onlyCommands[onlyCommand]);
  84. newCommands.push(commands[commandIndex]);
  85. newTitles.push(titles[commandIndex]);
  86. }
  87. }
  88. commandsLength = newCommands ? newCommands.length : commands.length;
  89. for(i=0;i<commandsLength;i++) {
  90. button = document.createElement('li');
  91. buttonLink = document.createElement('a');
  92. buttonLink.href = '#';
  93. buttonLink.setAttribute('onclick', 'return false;');
  94. buttonImage = document.createElement('img');
  95. buttonImage.id = 'btn' + wysiwygCount + 'cmd' + (newCommands ? newCommands[i] : commands[i]);
  96. buttonImage.src = '/assets/images/wysiwyg/' + (newCommands? newCommands[i] : commands[i]) + '.gif';
  97. buttonImage.alt = newTitles ? newTitles[i] : titles[i];
  98. buttonImage.title = newTitles ? newTitles[i] : titles[i];
  99. switch(commands[i]) {
  100. case 'insertlink':
  101. click_function = 'positionBelow(\'btn'+wysiwygCount+'cmdinsertlink\', \'fieldsetInsertLink'+wysiwygCount+'\'); this.className = toggleVisibility(\'fieldsetInsertLink'+wysiwygCount+'\') ? \'pressed\' : \'\';';
  102. break;
  103. case 'selectimage':
  104. click_function = 'positionBelow(\'btn'+wysiwygCount+'cmdselectimage\', \'divImages\'); this.className = toggleVisibility(\'divImages\') ? \'pressed\' : \'\'';
  105. break;
  106. case 'inserttable':
  107. click_function = 'positionBelow(\'btn'+wysiwygCount+'cmdinserttable\', \'fieldsetInsertTable'+wysiwygCount+'\'); this.className = toggleVisibility(\'fieldsetInsertTable'+wysiwygCount+'\') ? \'pressed\' : \'\';';
  108. break;
  109. case 'insertrowabove':
  110. click_function = 'insertRow(true);';
  111. break;
  112. case 'insertcolumnbefore':
  113. click_function = 'insertColumn(true);';
  114. break;
  115. case 'insertrowbelow':
  116. click_function = 'insertRow(false);';
  117. break;
  118. case 'insertcolumnafter':
  119. click_function = 'insertColumn(false);';
  120. break;
  121. default:
  122. click_function = 'textAreaID = \''+textArea+'\'; currentWYSIWYG = this.parentNode.parentNode.parentNode.parentNode.id.substring(16); wysiwygCommand(this.id.substring(3));';
  123. }
  124. buttonImage.setAttribute('onclick', click_function);
  125. buttonImage.setAttribute('onmousedown', renewPageLock);
  126. buttonLink.appendChild(buttonImage)
  127. button.appendChild(buttonLink);
  128. toolbar.appendChild(button);
  129. }
  130. wysiwygContainer.appendChild(toolbar);
  131. wysiwygContainer.appendChild(insertLinkDialog);
  132. wysiwygContainer.appendChild(tableDialog);
  133. wysiwygContainer.appendChild(wysiwyg);
  134. brClear = document.createElement('br');
  135. brClear.className = 'clear';
  136. wysiwygContainer.appendChild(brClear);
  137. get(textArea).parentNode.insertBefore(wysiwygContainer, get(textArea));
  138. insertLinkDialog.appendChild(insertLinkLabel);
  139. insertLinkDialog.appendChild(insertLinkInput);
  140. insertLinkDialog.appendChild(insertLinkButton);
  141. tableDialog.appendChild(tableHeading);
  142. tableDialog.appendChild(tableRowsLabel);
  143. tableDialog.appendChild(tableRowsInput);
  144. tableDialog.appendChild(document.createElement('br'));
  145. tableDialog.appendChild(tableColumnsLabel);
  146. tableDialog.appendChild(tableColumnsInput);
  147. tableDialog.appendChild(document.createElement('br'));
  148. tableDialog.appendChild(tableInsertButton);
  149. textAreaID = textArea;
  150. initializeWYSIWYG(wysiwygCount, textAreaID, wysiwygWidth, wysiwygHeight);
  151. }
  152.  
  153. function initializeWYSIWYG(wysiwygID, textArea, wysiwygWidth, wysiwygHeight) {
  154. get('WYSIWYG'+wysiwygID).parentNode.style.width = wysiwygWidth || get(textAreaID).offsetWidth + 'px';
  155. get('WYSIWYG'+wysiwygID).parentNode.style.height = wysiwygHeight || get(textAreaID).offsetHeight + 'px';
  156. get('WYSIWYG'+wysiwygID).contentDocument.write('<br />'+textArea);
  157. get('WYSIWYG'+wysiwygID).contentDocument.body.innerHTML = get(textArea).value;
  158. get('WYSIWYG'+wysiwygID).contentDocument.body.style.fontFamily = 'Arial';
  159. get('WYSIWYG'+wysiwygID).contentDocument.body.style.fontSize = '10pt';
  160. get('WYSIWYG'+wysiwygID).contentDocument.designMode = 'on';
  161. get('WYSIWYG'+wysiwygID).contentDocument.body.id = 'editor' + wysiwygID;
  162. wysiwygs[parseInt(wysiwygID)-1] = textArea;
  163. get('WYSIWYG'+wysiwygID).contentDocument.execCommand('styleWithCSS', true, null);
  164. get('WYSIWYG'+wysiwygID).height = get('wysiwygContainer'+wysiwygID).offsetHeight - get('ulWYSIWYGToolbar'+wysiwygID).offsetHeight - 58 + 'px';
  165. textAreaID = textArea;
  166. get(textAreaID).className = 'hidden';
  167. get('WYSIWYG'+wysiwygID).contentDocument.addEventListener('mouseup', function() {
  168. currentWYSIWYG = this.body.id.substring(6);
  169. textAreaID = wysiwygs[parseInt(this.body.id.substring(6))-1];
  170. updateButtonStates();
  171. }, false);
  172. get('WYSIWYG'+wysiwygID).contentDocument.addEventListener('keyup', function(e) {
  173. updateTextArea();
  174. updateButtonStates();
  175. }, false);
  176. get('WYSIWYG'+wysiwygID).contentDocument.addEventListener('keypress', function(e) {
  177. currentWYSIWYG = this.body.id.substring(6);
  178. textAreaID = wysiwygs[parseInt(this.body.id.substring(6))-1];
  179. handleKeyPress(e);
  180. return false;
  181. }, true);
  182. }
  183.  
  184. function insertImage(id, name) {
  185. wysiwygCommand("<a href=\"/uploads/view/" + id + "\"><img src=\"/uploads/download/" + id + "/resized\" alt=\"" + name + "\" title=\"" + name + "\" /></a>", null, "inserthtml");
  186. get('btn'+currentWYSIWYG+'cmdselectimage').className = toggleVisibility('divImages') ? 'pressed' : '';
  187. updateTextArea();
  188. }
  189.  
  190. function updateTextArea() {
  191. get(textAreaID).value = get('WYSIWYG'+currentWYSIWYG).contentDocument.body.innerHTML;
  192. get('WYSIWYG'+currentWYSIWYG).contentWindow.focus();
  193. }
  194.  
  195. function wysiwygCommand(value, true_or_false, called_from_func_cmd) {
  196. if(!value)
  197. value = null;
  198. if(!true_or_false)
  199. true_or_false = false;
  200. if(called_from_func_cmd)
  201. get('WYSIWYG'+currentWYSIWYG).contentDocument.execCommand(called_from_func_cmd, true_or_false, value);
  202. else
  203. get('WYSIWYG'+currentWYSIWYG).contentDocument.execCommand(value.substring(0, currentWYSIWYG.length+3)==currentWYSIWYG+'cmd' ? value.substring(currentWYSIWYG.length+3) : value, false, null);//cmd_value, cmd_true_or_false);
  204. updateButtonStates();
  205. }
  206.  
  207. function updateButtonStates() {
  208. for(i=0;i<=13;i++) {
  209. if(get('btn'+currentWYSIWYG+'cmd'+commands[i]))
  210. try {
  211. get('btn'+currentWYSIWYG+'cmd'+commands[i]).className = get('WYSIWYG'+currentWYSIWYG).contentDocument.queryCommandState(commands[i]) ? 'pressed' : '';
  212. }
  213. catch(err) {
  214. }
  215. if(i==6) i = 9;
  216. }
  217. updateTextArea();
  218. }
  219.  
  220. function insertTable() {
  221. newTable = document.createElement('table');
  222. tableCount++;
  223. for(i=0;i<parseInt(get('txtRowsInput'+currentWYSIWYG).value);i++) {
  224. newTableRow = document.createElement('tr');
  225. for(n=0;n<parseInt(get('txtColumnsInput'+currentWYSIWYG).value);n++) {
  226. newTableCell = document.createElement('td');
  227. newTableCell.style.border = '1px solid #d3d3d3';
  228. newTableCell.style.width = '5em';
  229. newTableCell.style.height = '1.8em';
  230. newTableCell.style.fontSize = '80%';
  231. newTableCell.style.verticalAlign = 'top';
  232. newTableRow.appendChild(newTableCell);
  233. }
  234. newTable.appendChild(newTableRow);
  235. }
  236. cursorPosition = get('WYSIWYG'+currentWYSIWYG).contentWindow.getSelection().getRangeAt(0).startOffset;
  237. get('WYSIWYG'+currentWYSIWYG).contentDocument.body.innerHTML = get('WYSIWYG'+currentWYSIWYG).contentDocument.body.innerHTML.substring(0, cursorPosition) + "<table id=\"table"+tableCount+"\" style=\"border-collapse: collapse; border: 1px solid white; margin: 0.5em;\">" + newTable.innerHTML + "</table>" + get('WYSIWYG'+currentWYSIWYG).contentDocument.body.innerHTML.substring(cursorPosition);
  238. get('btn'+currentWYSIWYG+'cmdinserttable').className = toggleVisibility('fieldsetInsertTable'+currentWYSIYWG) ? 'pressed' : '';
  239. get('txtRowsInput'+currentWYSIWYG).value = '';
  240. get('txtColumnsInput'+currentWYSIWYG).value = '';
  241. updateTextArea();
  242. return false;
  243. }
  244.  
  245. function insertRow(aboveCurrentRow) {
  246. tableRow = get('WYSIWYG'+currentWYSIWYG).contentWindow.getSelection().focusNode.parentNode.parentNode;
  247. if(tableRow=='[object HTMLTableRowElement]') {
  248. newTableRow = document.createElement('tr');
  249. for(i=0;i<tableRow.childNodes.length;i++) {
  250. newTableCell = document.createElement('td');
  251. newTableCell.style.border = '1px solid #d3d3d3';
  252. newTableCell.style.width = '5em';
  253. newTableCell.style.height = '1.8em';
  254. newTableCell.style.fontSize = '80%';
  255. newTableCell.style.verticalAlign = 'top';
  256. newTableRow.appendChild(newTableCell);
  257. }
  258. tableRow.parentNode.insertBefore(newTableRow, aboveCurrentRow==true?tableRow:tableRow.nextSibling);
  259. }
  260. }
  261.  
  262. function insertColumn(afterCurrentColumn) {
  263. tableRow = get('WYSIWYG'+currentWYSIWYG).contentWindow.getSelection().focusNode.parentNode.parentNode;
  264. if(tableRow=='[object HTMLTableRowElement]') {
  265. cellIndex = get('WYSIWYG'+currentWYSIWYG).contentWindow.getSelection().focusNode.parentNode.cellIndex;
  266. table = tableRow.parentNode;
  267. for(i=0;i<table.childNodes.length;i++) {
  268. newTableCell = document.createElement('td');
  269. newTableCell.style.border = '1px solid #d3d3d3';
  270. newTableCell.style.width = '5em';
  271. newTableCell.style.height = '1.8em';
  272. newTableCell.style.fontSize = '80%';
  273. newTableCell.style.verticalAlign = 'top';
  274. table.childNodes[i].insertBefore(newTableCell, afterCurrentColumn==true?table.childNodes[i].childNodes[cellIndex]:table.childNodes[i].childNodes[cellIndex].nextSibling);
  275. }
  276. }
  277. updateTextArea();
  278. }
  279.  
  280. function handleKeyPress(keyEvent) {
  281. charCode = keyEvent.charCode;
  282. if(charCode>97)
  283. charCode -= 32;
  284. if(keyEvent.ctrlKey) {
  285. switch(charCode) {
  286. case 66: // bold
  287. wysiwygCommand('bold');
  288. break;
  289. case 73: // italic
  290. wysiwygCommand('italic');
  291. break;
  292. case 85: // underline
  293. wysiwygCommand('underline');
  294. break;
  295. case 76: // left align
  296. wysiwygCommand('justifyleft');
  297. break;
  298. case 69: // center align
  299. wysiwygCommand('justifycenter');
  300. break;
  301. case 82: // right align
  302. wysiwygCommand('justifyright');
  303. break;
  304. }
  305. keyEvent.preventDefault();
  306. }
  307. }
  308.  
  309. function offsetRight(element) {
  310. return document.body.offsetWidth - (document.getElementById(element).offsetLeft + document.getElementById(element).offsetWidth);
  311. }
  312.  
  313. function offsetBottom(element) {
  314. return document.getElementById(element).offsetTop + document.getElementById(element).offsetHeight;
  315. }

Report this snippet  

Comments

RSS Icon Subscribe to comments
Posted By: brownrl on April 8, 2011

This looks interesting.... does it work? I tried in a simple html file and it seems no?

I like the coding style, if it works I am sure other devs can extend how they want.

You need to login to post a comment.