edit.js 8.5 KB

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