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