1/** 2 * Functions for text editing (toolbar stuff) 3 * 4 * @todo most of the stuff in here should be revamped and then moved to toolbar.js 5 * @author Andreas Gohr <andi@splitbrain.org> 6 */ 7 8/** 9 * Creates a toolbar button through the DOM 10 * Called for each entry of toolbar definition array (built by inc/toolbar.php and extended via js) 11 * 12 * Style the buttons through the toolbutton class 13 * 14 * @param {string} icon image filename, relative to folder lib/images/toolbar/ 15 * @param {string} label title of button, show on mouseover 16 * @param {string} key hint in title of button for access key 17 * @param {string} id id of button, and '<id>_ico' of icon 18 * @param {string} classname for styling buttons 19 * 20 * @author Andreas Gohr <andi@splitbrain.org> 21 * @author Michal Rezler <m.rezler@centrum.cz> 22 */ 23function createToolButton(icon,label,key,id,classname){ 24 var $btn = jQuery(document.createElement('button')), 25 $ico = jQuery(document.createElement('img')); 26 27 // prepare the basic button stuff 28 $btn.addClass('toolbutton'); 29 if(classname){ 30 $btn.addClass(classname); 31 } 32 33 $btn.attr('title', label).attr('aria-controls', 'wiki__text'); 34 if(key){ 35 $btn.attr('title', label + ' ['+key.toUpperCase()+']') 36 .attr('accessKey', key); 37 } 38 39 // set IDs if given 40 if(id){ 41 $btn.attr('id', id); 42 $ico.attr('id', id+'_ico'); 43 } 44 45 // create the icon and add it to the button 46 if(icon.substr(0,1) !== '/'){ 47 icon = DOKU_BASE + 'lib/images/toolbar/' + icon; 48 } 49 $ico.attr('src', icon); 50 $ico.attr('alt', ''); 51 $ico.attr('width', 16); 52 $ico.attr('height', 16); 53 $btn.append($ico); 54 55 // we have to return a DOM object (for compatibility reasons) 56 return $btn[0]; 57} 58 59/** 60 * Creates a picker window for inserting text 61 * 62 * The given list can be an associative array with text,icon pairs 63 * or a simple list of text. Style the picker window through the picker 64 * class or the picker buttons with the pickerbutton class. Picker 65 * windows are appended to the body and created invisible. 66 * 67 * @param {string} id the ID to assign to the picker 68 * @param {Array} props the properties for the picker 69 * @param {string} edid the ID of the textarea 70 * @return DOMobject the created picker 71 * @author Andreas Gohr <andi@splitbrain.org> 72 */ 73function createPicker(id,props,edid){ 74 // create the wrapping div 75 var $picker = jQuery(document.createElement('div')); 76 77 $picker.addClass('picker a11y'); 78 if(props['class']){ 79 $picker.addClass(props['class']); 80 } 81 82 $picker.attr('id', id).css('position', 'absolute'); 83 84 function $makebutton(title) { 85 var $btn = jQuery(document.createElement('button')) 86 .addClass('pickerbutton').attr('title', title) 87 .attr('aria-controls', edid) 88 .on('click', bind(pickerInsert, title, edid)) 89 .appendTo($picker); 90 return $btn; 91 } 92 93 jQuery.each(props.list, function (key, item) { 94 if (!props.list.hasOwnProperty(key)) { 95 return; 96 } 97 98 if(isNaN(key)){ 99 // associative array -> treat as text => image pairs 100 if (item.substr(0,1) !== '/') { 101 item = DOKU_BASE+'lib/images/'+props.icobase+'/'+item; 102 } 103 jQuery(document.createElement('img')) 104 .attr('src', item) 105 .attr('alt', '') 106 .css('height', '16') 107 .appendTo($makebutton(key)); 108 }else if (typeof item == 'string'){ 109 // a list of text -> treat as text picker 110 $makebutton(item).text(item); 111 }else{ 112 // a list of lists -> treat it as subtoolbar 113 initToolbar($picker,edid,props.list); 114 return false; // all buttons handled already 115 } 116 117 }); 118 jQuery('body').append($picker); 119 120 // we have to return a DOM object (for compatibility reasons) 121 return $picker[0]; 122} 123 124/** 125 * Called by picker buttons to insert Text and close the picker again 126 * 127 * @author Andreas Gohr <andi@splitbrain.org> 128 */ 129function pickerInsert(text,edid){ 130 insertAtCarret(edid,text); 131 pickerClose(); 132} 133 134/** 135 * Add button action for signature button 136 * 137 * @param {jQuery} $btn Button element to add the action to 138 * @param {Array} props Associative array of button properties 139 * @param {string} edid ID of the editor textarea 140 * @return {string} picker id for aria-controls attribute 141 * @author Gabriel Birke <birke@d-scribe.de> 142 */ 143function addBtnActionSignature($btn, props, edid) { 144 if(typeof SIG != 'undefined' && SIG != ''){ 145 $btn.on('click', function (e) { 146 insertAtCarret(edid,SIG); 147 e.preventDefault(); 148 }); 149 return edid; 150 } 151 return ''; 152} 153 154/** 155 * Determine the current section level while editing 156 * 157 * @param {string} textboxId ID of the text field 158 * 159 * @author Andreas Gohr <gohr@cosmocode.de> 160 */ 161function currentHeadlineLevel(textboxId){ 162 var field = jQuery('#' + textboxId)[0], 163 s = false, 164 opts = [field.value.substr(0,DWgetSelection(field).start)]; 165 if (field.form && field.form.prefix) { 166 // we need to look in prefix context 167 opts.push(field.form.prefix.value); 168 } 169 170 jQuery.each(opts, function (_, opt) { 171 // Check whether there is a headline in the given string 172 var str = "\n" + opt, 173 lasthl = str.lastIndexOf("\n=="); 174 if (lasthl !== -1) { 175 s = str.substr(lasthl+1,6); 176 return false; 177 } 178 }); 179 if (s === false) { 180 return 0; 181 } 182 return 7 - s.match(/^={2,6}/)[0].length; 183} 184 185 186/** 187 * global var used for not saved yet warning 188 */ 189window.textChanged = false; 190 191/** 192 * global var which stores original editor content 193 */ 194window.doku_edit_text_content = ''; 195/** 196 * Delete the draft before leaving the page 197 */ 198function deleteDraft() { 199 if (is_opera || window.keepDraft) { 200 return; 201 } 202 203 var $dwform = jQuery('#dw__editform'); 204 205 if($dwform.length === 0) { 206 return; 207 } 208 209 // remove a possibly saved draft using ajax 210 jQuery.post(DOKU_BASE + 'lib/exe/ajax.php', 211 { 212 call: 'draftdel', 213 id: $dwform.find('input[name=id]').val(), 214 sectok: $dwform.find('input[name=sectok]').val() 215 } 216 ); 217} 218 219/** 220 * Activate "not saved" dialog, add draft deletion to page unload, 221 * add handlers to monitor changes 222 * Note: textChanged could be set by e.g. html_edit() as well 223 * 224 * Sets focus to the editbox as well 225 */ 226jQuery(function () { 227 var $editform = jQuery('#dw__editform'); 228 if ($editform.length == 0) { 229 return; 230 } 231 232 var $edit_text = jQuery('#wiki__text'); 233 if ($edit_text.length > 0) { 234 if($edit_text.attr('readOnly')) { 235 return; 236 } 237 238 // set focus and place cursor at the start 239 var sel = DWgetSelection($edit_text[0]); 240 sel.start = 0; 241 sel.end = 0; 242 DWsetSelection(sel); 243 $edit_text.trigger('focus'); 244 245 doku_edit_text_content = $edit_text.val(); 246 } 247 248 var changeHandler = function() { 249 doku_hasTextBeenModified(); 250 251 doku_summaryCheck(); 252 }; 253 254 $editform.change(changeHandler); 255 $editform.keydown(changeHandler); 256 257 window.onbeforeunload = function(){ 258 if(window.textChanged) { 259 return LANG.notsavedyet; 260 } 261 }; 262 window.onunload = deleteDraft; 263 264 // reset change memory var on submit 265 jQuery('#edbtn__save').on('click', 266 function() { 267 window.onbeforeunload = ''; 268 textChanged = false; 269 } 270 ); 271 jQuery('#edbtn__preview').on('click', 272 function() { 273 window.onbeforeunload = ''; 274 textChanged = false; 275 window.keepDraft = true; // needed to keep draft on page unload 276 } 277 ); 278 279 var $summary = jQuery('#edit__summary'); 280 $summary.on('change keyup', doku_summaryCheck); 281 282 if (textChanged) doku_summaryCheck(); 283}); 284 285/** 286 * Updates textChanged variable if content of the editor has been modified 287 */ 288function doku_hasTextBeenModified() { 289 if (!textChanged) { 290 var $edit_text = jQuery('#wiki__text'); 291 292 if ($edit_text.length > 0) { 293 textChanged = doku_edit_text_content != $edit_text.val(); 294 } else { 295 textChanged = true; 296 } 297 } 298} 299 300/** 301 * Checks if a summary was entered - if not the style is changed 302 * 303 * @author Andreas Gohr <andi@splitbrain.org> 304 */ 305function doku_summaryCheck(){ 306 var $sum = jQuery('#edit__summary'), 307 missing = $sum.val() === ''; 308 $sum.toggleClass('missing', missing).toggleClass('edit', !missing); 309} 310