Fossil SCM

fossil-scm / src / fossil.wikiedit-wysiwyg.js
Source Blame History 490 lines
a759842… stephan 1 /**
a759842… stephan 2 A slight adaptation of fossil's legacy wysiwyg wiki editor which
a759842… stephan 3 makes it usable with the newer editor's edit widget replacement
a759842… stephan 4 API.
a759842… stephan 5
a759842… stephan 6 Requires: window.fossil, fossil.dom, and that the current page is
a759842… stephan 7 /wikiedit. If called from another page it returns without effect.
a759842… stephan 8
a759842… stephan 9 Caveat: this is an all-or-nothing solution. That is, once plugged
a759842… stephan 10 in to /wikiedit, it cannot be removed without reloading the page.
a759842… stephan 11 That is a limitation of the current editor-widget-swapping API.
a759842… stephan 12 */
a759842… stephan 13 (function(F/*fossil object*/){
a759842… stephan 14 'use strict';
a759842… stephan 15 if(!F || !F.page || F.page.name!=='wikiedit') return;
a759842… stephan 16
a759842… stephan 17 const D = F.dom;
a759842… stephan 18
a759842… stephan 19 ////////////////////////////////////////////////////////////////////////
a759842… stephan 20 // Install an app-specific stylesheet...
a759842… stephan 21 (function(){
a759842… stephan 22 const head = document.head || document.querySelector('head'),
a759842… stephan 23 styleTag = document.createElement('style'),
a759842… stephan 24 styleCSS = `
a759842… stephan 25 .intLink { cursor: pointer; }
a759842… stephan 26 img.intLink { border: 0; }
a759842… stephan 27 #wysiwyg-container {
a759842… stephan 28 display: flex;
a759842… stephan 29 flex-direction: column;
a759842… stephan 30 max-width: 100% /* w/o this, toolbars don't wrap properly! */
a759842… stephan 31 }
a759842… stephan 32 #wysiwygBox {
a759842… stephan 33 border: 1px solid rgba(127,127,127,0.3);
a759842… stephan 34 border-radius: 0.25em;
a759842… stephan 35 padding: 0.25em 1em;
a759842… stephan 36 margin: 0;
a759842… stephan 37 overflow: auto;
a759842… stephan 38 min-height: 20em;
a759842… stephan 39 resize: vertical;
a759842… stephan 40 }
a759842… stephan 41 #wysiwygEditMode { /* wrapper for radio buttons */
a759842… stephan 42 border: 1px solid rgba(127,127,127,0.3);
a759842… stephan 43 border-radius: 0.25em;
a759842… stephan 44 padding: 0 0.35em 0 0.35em
a759842… stephan 45 }
a759842… stephan 46 #wysiwygEditMode > * {
a759842… stephan 47 vertical-align: text-top;
a759842… stephan 48 }
a759842… stephan 49 #wysiwygEditMode label { cursor: pointer; }
a759842… stephan 50 #wysiwyg-toolbars {
a759842… stephan 51 margin: 0 0 0.25em 0;
a759842… stephan 52 display: flex;
a759842… stephan 53 flex-wrap: wrap;
a759842… stephan 54 flex-direction: column;
a759842… stephan 55 align-items: flex-start;
a759842… stephan 56 }
a759842… stephan 57 #wysiwyg-toolbars > * {
a759842… stephan 58 margin: 0 0.5em 0.25em 0;
a759842… stephan 59 }
a759842… stephan 60 #wysiwyg-toolBar1, #wysiwyg-toolBar2 {
a759842… stephan 61 margin: 0 0.2em 0.2em 0;
a759842… stephan 62 display: flex;
a759842… stephan 63 flex-flow: row wrap;
a759842… stephan 64 }
a759842… stephan 65 #wysiwyg-toolBar1 > * { /* formatting buttons */
a759842… stephan 66 vertical-align: middle;
a759842… stephan 67 margin: 0 0.25em 0.25em 0;
a759842… stephan 68 }
a759842… stephan 69 #wysiwyg-toolBar2 > * { /* icons */
a759842… stephan 70 border: 1px solid rgba(127,127,127,0.3);
a759842… stephan 71 vertical-align: baseline;
a759842… stephan 72 margin: 0.1em;
a759842… stephan 73 }
a759842… stephan 74 `;
a759842… stephan 75 head.appendChild(styleTag);
a759842… stephan 76 styleTag.type = 'text/css';
a759842… stephan 77 D.append(styleTag, styleCSS);
a759842… stephan 78 })();
a759842… stephan 79
a759842… stephan 80 const outerContainer = D.attr(D.div(), 'id', 'wysiwyg-container'),
a759842… stephan 81 toolbars = D.attr(D.div(), 'id', 'wysiwyg-toolbars'),
a759842… stephan 82 toolbar1 = D.attr(D.div(), 'id', 'wysiwyg-toolBar1'),
a759842… stephan 83 // ^^^ formatting options
a759842… stephan 84 toolbar2 = D.attr(D.div(), 'id', 'wysiwyg-toolBar2')
a759842… stephan 85 // ^^^^ action icon buttons
a759842… stephan 86 ;
a759842… stephan 87 D.append(outerContainer, D.append(toolbars, toolbar1, toolbar2));
a759842… stephan 88
a759842… stephan 89 /** Returns a function which simplifies adding a list of options
a759842… stephan 90 to the given select element. See below for example usage. */
a759842… stephan 91 const addOptions = function(select){
a759842… stephan 92 return function ff(value, label){
a759842… stephan 93 D.option(select, value, label || value);
a759842… stephan 94 return ff;
a759842… stephan 95 };
a759842… stephan 96 };
a759842… stephan 97
a759842… stephan 98 ////////////////////////////////////////////////////////////////////////
a759842… stephan 99 // Edit mode selection (radio buttons).
a759842… stephan 100 const radio0 =
a759842… stephan 101 D.attr(
a759842… stephan 102 D.input('radio'),
a759842… stephan 103 'name','wysiwyg-mode',
a759842… stephan 104 'id', 'wysiwyg-mode-0',
a759842… stephan 105 'value',0,
a759842… stephan 106 'checked',true),
a759842… stephan 107 radio1 = D.attr(
a759842… stephan 108 D.input('radio'),
a759842… stephan 109 'id','wysiwyg-mode-1',
a759842… stephan 110 'name','wysiwyg-mode',
a759842… stephan 111 'value',1),
a759842… stephan 112 radios = D.append(
a759842… stephan 113 D.attr(D.span(), 'id', 'wysiwygEditMode'),
a759842… stephan 114 radio0, D.append(
a759842… stephan 115 D.attr(D.label(), 'for', 'wysiwyg-mode-0'),
a759842… stephan 116 "WYSIWYG"
a759842… stephan 117 ),
a759842… stephan 118 radio1, D.append(
a759842… stephan 119 D.attr(D.label(), 'for', 'wysiwyg-mode-1'),
a759842… stephan 120 "Raw HTML"
a759842… stephan 121 )
a759842… stephan 122 );
a759842… stephan 123 D.append(toolbar1, radios);
a759842… stephan 124 const radioHandler = function(){setDocMode(+this.value)};
a759842… stephan 125 radio0.addEventListener('change',radioHandler, false);
a759842… stephan 126 radio1.addEventListener('change',radioHandler, false);
a759842… stephan 127
a759842… stephan 128
a759842… stephan 129 ////////////////////////////////////////////////////////////////////////
a759842… stephan 130 // Text formatting options...
a759842… stephan 131 var select;
a759842… stephan 132 select = D.addClass(D.select(), 'format');
a759842… stephan 133 select.dataset.format = "formatblock";
a759842… stephan 134 D.append(toolbar1, select);
a759842… stephan 135 addOptions(select)(
a759842… stephan 136 '', '- formatting -')(
a759842… stephan 137 "h1", "Title 1 <h1>")(
a759842… stephan 138 "h2", "Title 2 <h2>")(
a759842… stephan 139 "h3", "Title 3 <h3>")(
a759842… stephan 140 "h4", "Title 4 <h4>")(
a759842… stephan 141 "h5", "Title 5 <h5>")(
a759842… stephan 142 "h6", "Subtitle <h6>")(
a759842… stephan 143 "p", "Paragraph <p>")(
a759842… stephan 144 "pre", "Preformatted <pre>");
a759842… stephan 145
a759842… stephan 146 select = D.addClass(D.select(), 'format');
a759842… stephan 147 select.dataset.format = "fontname";
a759842… stephan 148 D.append(toolbar1, select);
a759842… stephan 149 D.addClass(
a759842… stephan 150 D.option(select, '', '- font -'),
a759842… stephan 151 "heading"
a759842… stephan 152 );
a759842… stephan 153 addOptions(select)(
a759842… stephan 154 'Arial')(
a759842… stephan 155 'Arial Black')(
a759842… stephan 156 'Courier New')(
a759842… stephan 157 'Times New Roman');
a759842… stephan 158
a759842… stephan 159 select = D.addClass(D.select(), 'format');
a759842… stephan 160 D.append(toolbar1, select);
a759842… stephan 161 select.dataset.format = "fontsize";
a759842… stephan 162 D.addClass(
a759842… stephan 163 D.option(select, '', '- size -'),
a759842… stephan 164 "heading"
a759842… stephan 165 );
a759842… stephan 166 addOptions(select)(
a759842… stephan 167 "1", "Very small")(
a759842… stephan 168 "2", "A bit small")(
a759842… stephan 169 "3", "Normal")(
a759842… stephan 170 "4", "Medium-large")(
a759842… stephan 171 "5", "Big")(
a759842… stephan 172 "6", "Very big")(
a759842… stephan 173 "7", "Maximum");
a759842… stephan 174
a759842… stephan 175 select = D.addClass(D.select(), 'format');
a759842… stephan 176 D.append(toolbar1, select);
a759842… stephan 177 select.dataset.format = 'forecolor';
a759842… stephan 178 D.addClass(
a759842… stephan 179 D.option(select, '', '- color -'),
a759842… stephan 180 "heading"
a759842… stephan 181 );
a759842… stephan 182 addOptions(select)(
a759842… stephan 183 "red", "Red")(
a759842… stephan 184 "blue", "Blue")(
a759842… stephan 185 "green", "Green")(
a759842… stephan 186 "black", "Black")(
a759842… stephan 187 "grey", "Grey")(
a759842… stephan 188 "yellow", "Yellow")(
a759842… stephan 189 "cyan", "Cyan")(
a759842… stephan 190 "magenta", "Magenta");
a759842… stephan 191
a759842… stephan 192
a759842… stephan 193 ////////////////////////////////////////////////////////////////////////
a759842… stephan 194 // Icon-based toolbar...
a759842… stephan 195 /**
a759842… stephan 196 Inject the icons...
a759842… stephan 197
a759842… stephan 198 mkbuiltins strips anything which looks like a C++-style comment,
a759842… stephan 199 even if it's in a string literal, and thus the runs of "/"
a759842… stephan 200 characters in the DOM element data attributes have been mangled
a759842… stephan 201 to work around that: we simply use \x2f for every 2nd slash.
a759842… stephan 202 */
a759842… stephan 203 (function f(title,format,src){
a759842… stephan 204 const img = D.img();
a759842… stephan 205 D.append(toolbar2, img);
a759842… stephan 206 D.addClass(img, 'intLink');
a759842… stephan 207 D.attr(img, 'title', title);
a759842… stephan 208 img.dataset.format = format;
a759842… stephan 209 D.attr(img, 'src', 'string'===typeof src ? src : src.join(''));
a759842… stephan 210 return f;
a759842… stephan 211 })(
a759842… stephan 212 'Undo', 'undo',
a759842… stephan 213 ["data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7",
a759842… stephan 214 "/I19DV3NHa7P/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f",
a759842… stephan 215 "/\x2f/\x2f/\x2f/yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq704680",
a759842… stephan 216 "7TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfE",
a759842… stephan 217 "whdRIH4fh/DZMICe3/C4nBQBADs="]
a759842… stephan 218 )(
a759842… stephan 219 'Redo','redo',
a759842… stephan 220 ["data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1/",
a759842… stephan 221 "/\x2f/yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9Na",
a759842… stephan 222 "EDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw=="]
a759842… stephan 223 )(
a759842… stephan 224 "Remove formatting",
a759842… stephan 225 "removeFormat",
a759842… stephan 226 ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AA",
a759842… stephan 227 "AABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwA",
a759842… stephan 228 "AAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB5",
a759842… stephan 229 "01ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqG",
a759842… stephan 230 "Xz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMz",
a759842… stephan 231 "deiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3/\x2fsf37/1/c02cCG1lB8f/\x2ff95",
a759842… stephan 232 "DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af/\x2f/2ckaHBp7+7wmavP5n76+P2C",
a759842… stephan 233 "lrLIYl8H9W36auJCbCxM4szMTJac7Kza/\x2f/\x2fR3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tA",
a759842… stephan 234 "wMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n",
a759842… stephan 235 "7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1",
a759842… stephan 236 "lsXc4Db7z8C3r8p7Qjf/\x2f/2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj",
a759842… stephan 237 "5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwf",
a759842… stephan 238 "fj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv/\x2fvPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrj",
a759842… stephan 239 "b/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0/\x2f/\x2ffv77/\x2f8nLy+7MCc",
a759842… stephan 240 "XmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc66",
a759842… stephan 241 "6s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7/\x2f8/4/\x2f9/pgOnH",
a759842… stephan 242 "6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg=="]
a759842… stephan 243 )(
a759842… stephan 244 "Bold",
a759842… stephan 245 "bold",
a759842… stephan 246 ["data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB",
a759842… stephan 247 "YAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs="]
a759842… stephan 248 )(
a759842… stephan 249 "Italic",
a759842… stephan 250 "italic",
a759842… stephan 251 ["data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf/\x2f/yH5BAEAAAMALA",
a759842… stephan 252 "AAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw=="]
a759842… stephan 253 )(
a759842… stephan 254 "Underline",
a759842… stephan 255 "underline",
a759842… stephan 256 ["data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj/\x2f/\x2f/\x2f/\x2fyH5BAEAAAIALA",
a759842… stephan 257 "AAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA",
a759842… stephan 258 "7"]
a759842… stephan 259 )(
a759842… stephan 260 "Left align",
a759842… stephan 261 "justifyleft",
a759842… stephan 262 ["data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB",
a759842… stephan 263 "YAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw=="]
a759842… stephan 264 )(
a759842… stephan 265 "Center align",
a759842… stephan 266 "justifycenter",
a759842… stephan 267 ["data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB",
a759842… stephan 268 "YAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7"]
a759842… stephan 269 )(
a759842… stephan 270 "Right align",
a759842… stephan 271 "justifyright",
a759842… stephan 272 ["data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB",
a759842… stephan 273 "YAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw=="]
a759842… stephan 274 )(
a759842… stephan 275 "Numbered list",
a759842… stephan 276 "insertorderedlist",
a759842… stephan 277 ["data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P/\x2f/\x2f",
a759842… stephan 278 "/\x2f/yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEO",
a759842… stephan 279 "zyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs="]
a759842… stephan 280 )(
a759842… stephan 281 "Dotted list",
a759842… stephan 282 "insertunorderedlist",
a759842… stephan 283 ["data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv/\x2f/\x2f",
a759842… stephan 284 "/\x2f/yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud6",
a759842… stephan 285 "4UwiuKnigGQliQuWOyKQykgAAOw=="]
a759842… stephan 286 )(
a759842… stephan 287 "Quote",
a759842… stephan 288 "formatblock",
a759842… stephan 289 ["data:image/gif;base64,R0lGODlhFgAWAIQXAC1NqjFRjkBgmT9nqUJnsk9xrFJ7u2",
a759842… stephan 290 "R9qmKBt1iGzHmOrm6Sz4OXw3Odz4Cl2ZSnw6KxyqO306K63bG70bTB0rDI3bvI4P",
a759842… stephan 291 "/\x2f/\x2f/\x2f/\x2f/",
a759842… stephan 292 "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f",
a759842… stephan 293 "/\x2f/\x2f/\x2fyH5BAEKAB8ALAAAAAAWABYAAAVP4CeOZGmeaKqubEs2Cekk",
a759842… stephan 294 "ErvEI1zZuOgYFlakECEZFi0GgTGKEBATFmJAVXweVOoKEQgABB9IQDCmrLpjETrQQlhHjINrT",
a759842… stephan 295 "q/b7/i8fp8PAQA7"]
a759842… stephan 296 )(
a759842… stephan 297 "Delete indentation",
a759842… stephan 298 "outdent",
a759842… stephan 299 ["data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P",
a759842… stephan 300 "/\x2f/yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMcz",
a759842… stephan 301 "sYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs="]
a759842… stephan 302 )(
a759842… stephan 303 "Add indentation",
a759842… stephan 304 "indent",
a759842… stephan 305 ["data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3N",
a759842… stephan 306 "Ha7P/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f",
a759842… stephan 307 "/\x2f/\x2f/yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650",
a759842… stephan 308 "B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw=="
a759842… stephan 309 ]
a759842… stephan 310 )(
a759842… stephan 311 "Hyperlink",
a759842… stephan 312 "createlink",
a759842… stephan 313 ["data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb",
a759842… stephan 314 "/I19Ha7Pv8/f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f",
a759842… stephan 315 "/yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq704682",
a759842… stephan 316 "7/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghG",
a759842… stephan 317 "i9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7"]
a759842… stephan 318 )(
a759842… stephan 319 "Cut",
a759842… stephan 320 "cut",
a759842… stephan 321 ["data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1",
a759842… stephan 322 "dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f",
a759842… stephan 323 "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/",
a759842… stephan 324 "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f",
a759842… stephan 325 "yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnE",
a759842… stephan 326 "bGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9",
a759842… stephan 327 "ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw=="]
a759842… stephan 328 )(
a759842… stephan 329 "Copy",
a759842… stephan 330 "copy",
a759842… stephan 331 ["data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31",
a759842… stephan 332 "iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OP",
a759842… stephan 333 "s++bx/Pv8/f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f",
a759842… stephan 334 "/yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/",
a759842… stephan 335 "kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQ",
a759842… stephan 336 "ACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAA",
a759842… stephan 337 "Grpy+wsbKzIiEAOw=="]
a759842… stephan 338 )(
a759842… stephan 339 /* Paste, when activated via JS, has no effect in some (maybe all)
a759842… stephan 340 environments. Activated externally, e.g. keyboard, it works. */
a759842… stephan 341 "Paste (does not work in all environments)",
a759842… stephan 342 "paste",
a759842… stephan 343 ["data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrp",
a759842… stephan 344 "qbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f/\x2f/\x2f/\x2f",
a759842… stephan 345 "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/",
a759842… stephan 346 "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f",
a759842… stephan 347 "yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvB",
a759842… stephan 348 "SyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQ",
a759842… stephan 349 "o9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAA",
a759842… stephan 350 "MOaK+bLAOrtLUyt7i5uiUhADs="]
a759842… stephan 351 );
a759842… stephan 352
a759842… stephan 353 ////////////////////////////////////////////////////////////////////////
a759842… stephan 354 // The main editor area...
a759842… stephan 355 const oDoc = D.attr(D.div(), 'id', "wysiwygBox");
a759842… stephan 356 D.attr(oDoc, 'contenteditable', 'true');
a759842… stephan 357 D.append(outerContainer, oDoc);
a759842… stephan 358
a759842… stephan 359 /* Initialize the document editor */
a759842… stephan 360 function initDoc() {
a759842… stephan 361 initEventHandlers();
a759842… stephan 362 if (!isWysiwyg()) { setDocMode(true); }
a759842… stephan 363 }
a759842… stephan 364
a759842… stephan 365 function initEventHandlers() {
a759842… stephan 366 //console.debug("initEventHandlers()");
a759842… stephan 367 const handleDropDown = function() {
a759842… stephan 368 formatDoc(this.dataset.format,this[this.selectedIndex].value);
a759842… stephan 369 this.selectedIndex = 0;
a759842… stephan 370 };
a759842… stephan 371
a759842… stephan 372 const handleFormatButton = function() {
a759842… stephan 373 var extra;
a759842… stephan 374 switch (this.dataset.format) {
a759842… stephan 375 case 'createlink':
a759842… stephan 376 const sLnk = prompt('Target URL:','');
a759842… stephan 377 if(sLnk) extra = sLnk;
a759842… stephan 378 break;
a759842… stephan 379 case 'formatblock':
a759842… stephan 380 extra = 'blockquote';
a759842… stephan 381 break;
a759842… stephan 382 }
a759842… stephan 383 formatDoc(this.dataset.format, extra);
a759842… stephan 384 };
a759842… stephan 385
a759842… stephan 386 var i, controls = outerContainer.querySelectorAll('select.format');
a759842… stephan 387 for(i = 0; i < controls.length; i++) {
a759842… stephan 388 controls[i].addEventListener('change', handleDropDown, false);;
a759842… stephan 389 }
a759842… stephan 390 controls = outerContainer.querySelectorAll('.intLink');
a759842… stephan 391 for(i = 0; i < controls.length; i++) {
a759842… stephan 392 controls[i].addEventListener('click', handleFormatButton, false);
a759842… stephan 393 }
a759842… stephan 394 }
a759842… stephan 395
a759842… stephan 396 /* Return true if the document editor is in WYSIWYG mode. Return
a759842… stephan 397 ** false if it is in Markup mode */
a759842… stephan 398 function isWysiwyg() {
a759842… stephan 399 return radio0.checked;
a759842… stephan 400 }
a759842… stephan 401
a759842… stephan 402 /* Run the editing command if in WYSIWYG mode */
a759842… stephan 403 function formatDoc(sCmd, sValue) {
a759842… stephan 404 if (isWysiwyg()){
a759842… stephan 405 try {
a759842… stephan 406 // First, try the W3C draft standard way, which has
a759842… stephan 407 // been working on all non-IE browsers for a while.
a759842… stephan 408 // It is also supported by IE11 and higher.
a759842… stephan 409 document.execCommand("styleWithCSS", false, false);
a759842… stephan 410 } catch (e) {
a759842… stephan 411 try {
a759842… stephan 412 // For IE9 or IE10, this should work.
a759842… stephan 413 document.execCommand("useCSS", 0, true);
a759842… stephan 414 } catch (e) {
a759842… stephan 415 // OK, that apparently did not work, do nothing.
a759842… stephan 416 }
a759842… stephan 417 }
a759842… stephan 418 document.execCommand(sCmd, false, sValue);
a759842… stephan 419 oDoc.focus();
a759842… stephan 420 }
a759842… stephan 421 }
a759842… stephan 422
a759842… stephan 423 /* Change the editing mode. Convert to markup if the argument
a759842… stephan 424 ** is true and wysiwyg if the argument is false. */
a759842… stephan 425 function setDocMode(bToMarkup, content) {
a759842… stephan 426 if(undefined===content){
a759842… stephan 427 content = bToMarkup ? oDoc.innerHTML : oDoc.innerText;
a759842… stephan 428 }
a759842… stephan 429 if(!setDocMode.linebreak){
a759842… stephan 430 setDocMode.linebreak = new RegExp("</p><p>","ig");
a759842… stephan 431 }
a759842… stephan 432 if(!setDocMode.toHide){
a759842… stephan 433 setDocMode.toHide = toolbars.querySelectorAll(
a759842… stephan 434 '#wysiwyg-toolBar1 > *:not(#wysiwygEditMode), '
a759842… stephan 435 +'#wysiwyg-toolBar2');
a759842… stephan 436 }
a759842… stephan 437 if (bToMarkup) {
a759842… stephan 438 /* WYSIWYG -> Markup */
a759842… stephan 439 // Legacy did this: content=content.replace(setDocMode.linebreak,"</p>\n\n<p>")
a759842… stephan 440 D.append(D.clearElement(oDoc), content)
a759842… stephan 441 oDoc.style.whiteSpace = "pre-wrap";
a759842… stephan 442 D.addClass(setDocMode.toHide, 'hidden');
a759842… stephan 443 } else {
a759842… stephan 444 /* Markup -> WYSIWYG */
a759842… stephan 445 D.parseHtml(D.clearElement(oDoc), content);
a759842… stephan 446 oDoc.style.whiteSpace = "normal";
a759842… stephan 447 D.removeClass(setDocMode.toHide, 'hidden');
a759842… stephan 448 }
a759842… stephan 449 oDoc.focus();
a759842… stephan 450 }
a759842… stephan 451
a759842… stephan 452 ////////////////////////////////////////////////////////////////////////
a759842… stephan 453 // A hook which can be activated via a site skin to plug this editor
e2bdc10… danield 454 // into the wikiedit page.
a759842… stephan 455 F.page.wysiwyg = {
a759842… stephan 456 // only for debugging: oDoc: oDoc,
a759842… stephan 457 /*
a759842… stephan 458 Replaces wikiedit's default editor widget with this wysiwyg
a759842… stephan 459 editor.
a759842… stephan 460
a759842… stephan 461 Must either be called via an onPageLoad handler via the site
a759842… stephan 462 skin's footer or else it can be called manually from the dev
a759842… stephan 463 tools console. Calling it too early (e.g. in the page footer
d83638e… danield 464 outside of an onPageLoad handler) will crash because wikiedit
a759842… stephan 465 has not been initialized.
a759842… stephan 466 */
a759842… stephan 467 init: function(){
a759842… stephan 468 initDoc();
a759842… stephan 469 const content = F.page.wikiContent() || '';
a759842… stephan 470 var isDirty = false /* keep from stashing too often */;
a759842… stephan 471 F.page.setContentMethods(
a759842… stephan 472 function(){
a759842… stephan 473 const rc = isWysiwyg() ? oDoc.innerHTML : oDoc.innerText;
a759842… stephan 474 return rc;
a759842… stephan 475 },
a759842… stephan 476 function(content){
a759842… stephan 477 isDirty = false;
a759842… stephan 478 setDocMode(radio0.checked ? 0 : 1, content);
a759842… stephan 479 }
a759842… stephan 480 );
a759842… stephan 481 oDoc.addEventListener('blur', function(){
a759842… stephan 482 if(isDirty) F.page.notifyOfChange();
a759842… stephan 483 }, false);
a759842… stephan 484 oDoc.addEventListener('input', function(){isDirty = true}, false);
a759842… stephan 485 F.page.wikiContent(content)/*feed it back in to our widget*/;
a759842… stephan 486 F.page.replaceEditorElement(outerContainer);
a759842… stephan 487 F.message("Replaced wiki editor widget with legacy wysiwyg editor.");
a759842… stephan 488 }
a759842… stephan 489 };
a759842… stephan 490 })(window.fossil);

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button