/*
** Copyright (c) 2012 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code that generates WYSIWYG text editors on
** web pages.
*/
#include <assert.h>
#include <ctype.h>
#include "config.h"
#include "wysiwyg.h"
/*
** Output code for a WYSIWYG editor. The caller must have already generated
** the <form> that will contain the editor, and the call must generate the
** corresponding </form> after this routine returns. The caller must include
** an onsubmit= attribute on the <form> element that invokes the
** wysiwygSubmit() function.
**
** There can only be a single WYSIWYG editor per frame.
*/
void wysiwygEditor(
const char *zId, /* ID for this editor */
const char *zContent, /* Initial content (HTML) */
int w, int h /* Initial width and height */
){
@ <style type="text/css">
@ .intLink { cursor: pointer; }
@ img.intLink { border: 0; }
@ #wysiwygBox {
@ border: 1px #000000 solid;
@ padding: 12px;
@ }
@ #editMode label { cursor: pointer; }
@ </style>
@ <input id="wysiwygValue" type="hidden" name="%s(zId)">
@ <div id="editModeDiv">Edit mode:
@ <select id="editMode" size=1 onchange="setDocMode(this.selectedIndex)">
@ <option value="0">WYSIWYG</option>
@ <option value="1">Raw HTML</option>
@ </select></div>
@ <div id="toolBar1">
@ <select onchange="formatDoc('formatblock',this[this.selectedIndex].value);
@ this.selectedIndex=0;">
@ <option selected>- formatting -</option>
@ <option value="h1">Title 1 <h1></option>
@ <option value="h2">Title 2 <h2></option>
@ <option value="h3">Title 3 <h3></option>
@ <option value="h4">Title 4 <h4></option>
@ <option value="h5">Title 5 <h5></option>
@ <option value="h6">Subtitle <h6></option>
@ <option value="p">Paragraph <p></option>
@ <option value="pre">Preformatted <pre></option>
@ </select>
@ <select onchange="formatDoc('fontname',this[this.selectedIndex].value);
@ this.selectedIndex=0;">
@ <option class="heading" selected>- font -</option>
@ <option>Arial</option>
@ <option>Arial Black</option>
@ <option>Courier New</option>
@ <option>Times New Roman</option>
@ </select>
@ <select onchange="formatDoc('fontsize',this[this.selectedIndex].value);
@ this.selectedIndex=0;">
@ <option class="heading" selected>- size -</option>
@ <option value="1">Very small</option>
@ <option value="2">A bit small</option>
@ <option value="3">Normal</option>
@ <option value="4">Medium-large</option>
@ <option value="5">Big</option>
@ <option value="6">Very big</option>
@ <option value="7">Maximum</option>
@ </select>
@ <select onchange="formatDoc('forecolor',this[this.selectedIndex].value);
@ this.selectedIndex=0;">
@ <option class="heading" selected>- color -</option>
@ <option value="red">Red</option>
@ <option value="blue">Blue</option>
@ <option value="green">Green</option>
@ <option value="black">Black</option>
@ </select>
@ </div>
@ <div id="toolBar2">
@ <img class="intLink" title="Undo" onclick="formatDoc('undo');"
@ src="data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7
@ /I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq704680
@ 7TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfE
@ whdRIH4fh/DZMICe3/C4nBQBADs=">
@ <img class="intLink" title="Redo" onclick="formatDoc('redo');"
@ src="data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1/
@ ///yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9Na
@ EDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw==">
@ <img class="intLink" title="Remove formatting"
@ onclick="formatDoc('removeFormat')"
@ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AA
@ AABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwA
@ AAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB5
@ 01ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqG
@ Xz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMz
@ deiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3//sf37/1/c02cCG1lB8f//f95
@ DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af///2ckaHBp7+7wmavP5n76+P2C
@ lrLIYl8H9W36auJCbCxM4szMTJac7Kza////R3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tA
@ wMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n
@ 7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1
@ lsXc4Db7z8C3r8p7Qjf///2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj
@ 5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwf
@ fj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv//vPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrj
@ b/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0////fv77//8nLy+7MCc
@ XmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc66
@ 6s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7//8/4//9/pgOnH
@ 6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg==">
@ <img class="intLink" title="Bold" onclick="formatDoc('bold');"
@ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
@ YAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs=" />
@ <img class="intLink" title="Italic" onclick="formatDoc('italic');"
@ src="data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf///yH5BAEAAAMALA
@ AAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw==" />
@ <img class="intLink" title="Underline" onclick="formatDoc('underline');"
@ src="data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj////////yH5BAEAAAIALA
@ AAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA
@ 7" />
@ <img class="intLink" title="Left align"
@ onclick="formatDoc('justifyleft');"
@ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
@ YAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw==" />
@ <img class="intLink" title="Center align"
@ onclick="formatDoc('justifycenter');"
@ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
@ YAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7" />
@ <img class="intLink" title="Right align"
@ onclick="formatDoc('justifyright');"
@ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
@ YAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw==" />
@ <img class="intLink" title="Numbered list"
@ onclick="formatDoc('insertorderedlist');"
@ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P////
@ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEO
@ zyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs=" />
@ <img class="intLink" title="Dotted list"
@ onclick="formatDoc('insertunorderedlist');"
@ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv////
@ ///yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud6
@ 4UwiuKnigGQliQuWOyKQykgAAOw==" />
@ <img class="intLink" title="Quote"
@ onclick="formatDoc('formatblock','blockquote');"
@ src="data:image/gif;base64,R0lGODlhFgAWAIQXAC1NqjFRjkBgmT9nqUJnsk9xrFJ7u2
@ R9qmKBt1iGzHmOrm6Sz4OXw3Odz4Cl2ZSnw6KxyqO306K63bG70bTB0rDI3bvI4P/////////
@ //////////////////////////yH5BAEKAB8ALAAAAAAWABYAAAVP4CeOZGmeaKqubEs2Cekk
@ ErvEI1zZuOgYFlakECEZFi0GgTGKEBATFmJAVXweVOoKEQgABB9IQDCmrLpjETrQQlhHjINrT
@ q/b7/i8fp8PAQA7" />
@ <img class="intLink" title="Add indentation"
@ onclick="formatDoc('outdent');"
@ src="data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P
@ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMcz
@ sYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs=" />
@ <img class="intLink" title="Delete indentation"
@ onclick="formatDoc('indent');"
@ src="data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3N
@ Ha7P///////////////////////////////yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650
@ B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw==">
@ <img class="intLink" title="Hyperlink"
@ onclick="var sLnk=prompt('Target URL:','');
@ if(sLnk&&sLnk!=''){formatDoc('createlink',sLnk)}"
@ src="data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb
@ /I19Ha7Pv8/f///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq704682
@ 7/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghG
@ i9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7" />
#if 0 /* Cut/Copy/Paste requires special browser permissions for security
** reasons. So omit these buttons */
@ <img class="intLink" title="Cut"
@ onclick="formatDoc('cut');"
@ src="data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1
@ dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P/////////////////////////////
@ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnE
@ bGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9
@ ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw==" />
@ <img class="intLink" title="Copy"
@ onclick="formatDoc('copy');"
@ src="data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31
@ iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OP
@ s++bx/Pv8/f///////////////yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/
@ kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQ
@ ACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAA
@ Grpy+wsbKzIiEAOw==" />
@ <img class="intLink" title="Paste"
@ onclick="formatDoc('paste');"
@ src="data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrp
@ qbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f/////////////////////
@ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvB
@ SyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQ
@ o9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAA
@ MOaK+bLAOrtLUyt7i5uiUhADs=" />
#endif
@ </div>
@ <div id="wysiwygBox"
@ style="resize:both; overflow:auto; width: %d(w)em; height: %d(h)em;"
@ contenteditable="true">%s(zContent)</div>
@ <script>
@ var oDoc;
@
@ /* Initialize the document editor */
@ function initDoc() {
@ oDoc = document.getElementById("wysiwygBox");
@ if (!isWysiwyg()) { setDocMode(true); }
@ }
@
@ /* Return true if the document editor is in WYSIWYG mode. Return
@ ** false if it is in Markup mode */
@ function isWysiwyg() {
@ return document.getElementById("editMode").selectedIndex==0;
@ }
@
@ /* Invoke this routine prior to submitting the HTML content back
@ ** to the server */
@ function wysiwygSubmit() {
@ if(oDoc.style.whiteSpace=="pre-wrap"){setDocMode(0);}
@ document.getElementById("wysiwygValue").value=oDoc.innerHTML;
@ }
@
@ /* Run the editing command if in WYSIWYG mode */
@ function formatDoc(sCmd, sValue) {
@ if (isWysiwyg()){
@ document.execCommand("styleWithCSS", false, false);
@ document.execCommand(sCmd, false, sValue);
@ oDoc.focus();
@ }
@ }
@
@ /* Change the editing mode. Convert to markup if the argument
@ ** is true and wysiwyg if the argument is false. */
@ function setDocMode(bToMarkup) {
@ var oContent;
@ if (bToMarkup) {
@ /* WYSIWYG -> Markup */
@ var linebreak = new RegExp("</p><p>","ig");
@ oContent = document.createTextNode(
@ oDoc.innerHTML.replace(linebreak,"</p>\n\n<p>"));
@ oDoc.innerHTML = "";
@ oDoc.style.whiteSpace = "pre-wrap";
@ oDoc.appendChild(oContent);
@ document.getElementById("toolBar1").style.visibility="hidden";
@ document.getElementById("toolBar2").style.visibility="hidden";
@ } else {
@ /* Markup -> WYSIWYG */
@ if (document.all) {
@ oDoc.innerHTML = oDoc.innerText;
@ } else {
@ oContent = document.createRange();
@ oContent.selectNodeContents(oDoc.firstChild);
@ oDoc.innerHTML = oContent.toString();
@ }
@ oDoc.style.whiteSpace = "normal";
@ document.getElementById("toolBar1").style.visibility="visible";
@ document.getElementById("toolBar2").style.visibility="visible";
@ }
@ oDoc.focus();
@ }
@ initDoc();
@ </script>
}