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.