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 .appendTo($makebutton(key)); 107 }else if (typeof item == 'string'){ 108 // a list of text -> treat as text picker 109 $makebutton(item).text(item); 110 }else{ 111 // a list of lists -> treat it as subtoolbar 112 initToolbar($picker,edid,props.list); 113 return false; // all buttons handled already 114 } 115 116 }); 117 jQuery('body').append($picker); 118 119 // we have to return a DOM object (for compatibility reasons) 120 return $picker[0]; 121} 122 123/** 124 * Called by picker buttons to insert Text and close the picker again 125 * 126 * @author Andreas Gohr <andi@splitbrain.org> 127 */ 128function pickerInsert(text,edid){ 129 insertAtCarret(edid,text); 130 pickerClose(); 131} 132 133/** 134 * Add button action for signature button 135 * 136 * @param {jQuery} $btn Button element to add the action to 137 * @param {Array} props Associative array of button properties 138 * @param {string} edid ID of the editor textarea 139 * @return {string} picker id for aria-controls attribute 140 * @author Gabriel Birke <birke@d-scribe.de> 141 */ 142function addBtnActionSignature($btn, props, edid) { 143 if(typeof SIG != 'undefined' && SIG != ''){ 144 $btn.on('click', function (e) { 145 insertAtCarret(edid,SIG); 146 e.preventDefault(); 147 }); 148 return edid; 149 } 150 return ''; 151} 152 153/** 154 * Determine the current section level while editing 155 * 156 * @param {string} textboxId ID of the text field 157 * 158 * @author Andreas Gohr <gohr@cosmocode.de> 159 */ 160function currentHeadlineLevel(textboxId){ 161 var field = jQuery('#' + textboxId)[0], 162 s = false, 163 opts = [field.value.substr(0,DWgetSelection(field).start)]; 164 if (field.form && field.form.prefix) { 165 // we need to look in prefix context 166 opts.push(field.form.prefix.value); 167 } 168 169 jQuery.each(opts, function (_, opt) { 170 // Check whether there is a headline in the given string 171 var str = "\n" + opt, 172 lasthl = str.lastIndexOf("\n=="); 173 if (lasthl !== -1) { 174 s = str.substr(lasthl+1,6); 175 return false; 176 } 177 }); 178 if (s === false) { 179 return 0; 180 } 181 return 7 - s.match(/^={2,6}/)[0].length; 182} 183 184 185/** 186 * global var used for not saved yet warning 187 */ 188window.textChanged = false; 189 190/** 191 * global var which stores original editor content 192 */ 193window.doku_edit_text_content = ''; 194/** 195 * Delete the draft before leaving the page 196 */ 197function deleteDraft() { 198 if (is_opera || window.keepDraft) { 199 return; 200 } 201 202 var $dwform = jQuery('#dw__editform'); 203 204 if($dwform.length === 0) { 205 return; 206 } 207 208 // remove a possibly saved draft using ajax 209 jQuery.post(DOKU_BASE + 'lib/exe/ajax.php', 210 { 211 call: 'draftdel', 212 id: $dwform.find('input[name=id]').val() 213 } 214 ); 215} 216 217/** 218 * Activate "not saved" dialog, add draft deletion to page unload, 219 * add handlers to monitor changes 220 * Note: textChanged could be set by e.g. html_edit() as well 221 * 222 * Sets focus to the editbox as well 223 */ 224jQuery(function () { 225 var $editform = jQuery('#dw__editform'); 226 if ($editform.length == 0) { 227 return; 228 } 229 230 var $edit_text = jQuery('#wiki__text'); 231 if ($edit_text.length > 0) { 232 if($edit_text.attr('readOnly')) { 233 return; 234 } 235 236 // set focus and place cursor at the start 237 var sel = DWgetSelection($edit_text[0]); 238 sel.start = 0; 239 sel.end = 0; 240 DWsetSelection(sel); 241 $edit_text.trigger('focus'); 242 243 doku_edit_text_content = $edit_text.val(); 244 } 245 246 var changeHandler = function() { 247 doku_hasTextBeenModified(); 248 249 doku_summaryCheck(); 250 }; 251 252 $editform.change(changeHandler); 253 $editform.keydown(changeHandler); 254 255 window.onbeforeunload = function(){ 256 if(window.textChanged) { 257 return LANG.notsavedyet; 258 } 259 }; 260 window.onunload = deleteDraft; 261 262 // reset change memory var on submit 263 jQuery('#edbtn__save').on('click', 264 function() { 265 window.onbeforeunload = ''; 266 textChanged = false; 267 } 268 ); 269 jQuery('#edbtn__preview').on('click', 270 function() { 271 window.onbeforeunload = ''; 272 textChanged = false; 273 window.keepDraft = true; // needed to keep draft on page unload 274 } 275 ); 276 277 var $summary = jQuery('#edit__summary'); 278 $summary.on('change keyup', doku_summaryCheck); 279 280 if (textChanged) doku_summaryCheck(); 281}); 282 283/** 284 * Updates textChanged variable if content of the editor has been modified 285 */ 286function doku_hasTextBeenModified() { 287 if (!textChanged) { 288 var $edit_text = jQuery('#wiki__text'); 289 290 if ($edit_text.length > 0) { 291 textChanged = doku_edit_text_content != $edit_text.val(); 292 } else { 293 textChanged = true; 294 } 295 } 296} 297 298/** 299 * Checks if a summary was entered - if not the style is changed 300 * 301 * @author Andreas Gohr <andi@splitbrain.org> 302 */ 303function doku_summaryCheck(){ 304 var $sum = jQuery('#edit__summary'), 305 missing = $sum.val() === ''; 306 $sum.toggleClass('missing', missing).toggleClass('edit', !missing); 307} 308