linkwiz.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /**
  2. * The Link Wizard
  3. *
  4. * @author Andreas Gohr <gohr@cosmocode.de>
  5. * @author Pierre Spring <pierre.spring@caillou.ch>
  6. */
  7. var dw_linkwiz = {
  8. $wiz: null,
  9. $entry: null,
  10. result: null,
  11. timer: null,
  12. textArea: null,
  13. selected: null,
  14. selection: null,
  15. /**
  16. * Initialize the dw_linkwizard by creating the needed HTML
  17. * and attaching the eventhandlers
  18. */
  19. init: function($editor){
  20. // position relative to the text area
  21. var pos = $editor.position();
  22. // create HTML Structure
  23. if(dw_linkwiz.$wiz)
  24. return;
  25. dw_linkwiz.$wiz = jQuery(document.createElement('div'))
  26. .dialog({
  27. autoOpen: false,
  28. draggable: true,
  29. title: LANG.linkwiz,
  30. resizable: false
  31. })
  32. .html(
  33. '<div>'+LANG.linkto+' <input type="text" class="edit" id="link__wiz_entry" autocomplete="off" /></div>'+
  34. '<div id="link__wiz_result"></div>'
  35. )
  36. .parent()
  37. .attr('id','link__wiz')
  38. .css({
  39. 'position': 'absolute',
  40. 'top': (pos.top+20)+'px',
  41. 'left': (pos.left+80)+'px'
  42. })
  43. .hide()
  44. .appendTo('.dokuwiki:first');
  45. dw_linkwiz.textArea = $editor[0];
  46. dw_linkwiz.result = jQuery('#link__wiz_result')[0];
  47. // scrollview correction on arrow up/down gets easier
  48. jQuery(dw_linkwiz.result).css('position', 'relative');
  49. dw_linkwiz.$entry = jQuery('#link__wiz_entry');
  50. if(JSINFO.namespace){
  51. dw_linkwiz.$entry.val(JSINFO.namespace+':');
  52. }
  53. // attach event handlers
  54. jQuery('#link__wiz .ui-dialog-titlebar-close').on('click', dw_linkwiz.hide);
  55. dw_linkwiz.$entry.keyup(dw_linkwiz.onEntry);
  56. jQuery(dw_linkwiz.result).on('click', 'a', dw_linkwiz.onResultClick);
  57. },
  58. /**
  59. * handle all keyup events in the entry field
  60. */
  61. onEntry: function(e){
  62. if(e.keyCode == 37 || e.keyCode == 39){ //left/right
  63. return true; //ignore
  64. }
  65. if(e.keyCode == 27){ //Escape
  66. dw_linkwiz.hide();
  67. e.preventDefault();
  68. e.stopPropagation();
  69. return false;
  70. }
  71. if(e.keyCode == 38){ //Up
  72. dw_linkwiz.select(dw_linkwiz.selected -1);
  73. e.preventDefault();
  74. e.stopPropagation();
  75. return false;
  76. }
  77. if(e.keyCode == 40){ //Down
  78. dw_linkwiz.select(dw_linkwiz.selected +1);
  79. e.preventDefault();
  80. e.stopPropagation();
  81. return false;
  82. }
  83. if(e.keyCode == 13){ //Enter
  84. if(dw_linkwiz.selected > -1){
  85. var $obj = dw_linkwiz.$getResult(dw_linkwiz.selected);
  86. if($obj.length > 0){
  87. dw_linkwiz.resultClick($obj.find('a')[0]);
  88. }
  89. }else if(dw_linkwiz.$entry.val()){
  90. dw_linkwiz.insertLink(dw_linkwiz.$entry.val());
  91. }
  92. e.preventDefault();
  93. e.stopPropagation();
  94. return false;
  95. }
  96. dw_linkwiz.autocomplete();
  97. },
  98. /**
  99. * Get one of the results by index
  100. *
  101. * @param num int result div to return
  102. * @returns DOMObject or null
  103. */
  104. getResult: function(num){
  105. DEPRECATED('use dw_linkwiz.$getResult()[0] instead');
  106. return dw_linkwiz.$getResult()[0] || null;
  107. },
  108. /**
  109. * Get one of the results by index
  110. *
  111. * @param num int result div to return
  112. * @returns jQuery object
  113. */
  114. $getResult: function(num) {
  115. return jQuery(dw_linkwiz.result).find('div').eq(num);
  116. },
  117. /**
  118. * Select the given result
  119. */
  120. select: function(num){
  121. if(num < 0){
  122. dw_linkwiz.deselect();
  123. return;
  124. }
  125. var $obj = dw_linkwiz.$getResult(num);
  126. if ($obj.length === 0) {
  127. return;
  128. }
  129. dw_linkwiz.deselect();
  130. $obj.addClass('selected');
  131. // make sure the item is viewable in the scroll view
  132. //getting child position within the parent
  133. var childPos = $obj.position().top;
  134. //getting difference between the childs top and parents viewable area
  135. var yDiff = childPos + $obj.outerHeight() - jQuery(dw_linkwiz.result).innerHeight();
  136. if (childPos < 0) {
  137. //if childPos is above viewable area (that's why it goes negative)
  138. jQuery(dw_linkwiz.result)[0].scrollTop += childPos;
  139. } else if(yDiff > 0) {
  140. // if difference between childs top and parents viewable area is
  141. // greater than the height of a childDiv
  142. jQuery(dw_linkwiz.result)[0].scrollTop += yDiff;
  143. }
  144. dw_linkwiz.selected = num;
  145. },
  146. /**
  147. * deselect a result if any is selected
  148. */
  149. deselect: function(){
  150. if(dw_linkwiz.selected > -1){
  151. dw_linkwiz.$getResult(dw_linkwiz.selected).removeClass('selected');
  152. }
  153. dw_linkwiz.selected = -1;
  154. },
  155. /**
  156. * Handle clicks in the result set an dispatch them to
  157. * resultClick()
  158. */
  159. onResultClick: function(e){
  160. if(!jQuery(this).is('a')) {
  161. return;
  162. }
  163. e.stopPropagation();
  164. e.preventDefault();
  165. dw_linkwiz.resultClick(this);
  166. return false;
  167. },
  168. /**
  169. * Handles the "click" on a given result anchor
  170. */
  171. resultClick: function(a){
  172. dw_linkwiz.$entry.val(a.title);
  173. if(a.title == '' || a.title.substr(a.title.length-1) == ':'){
  174. dw_linkwiz.autocomplete_exec();
  175. }else{
  176. if (jQuery(a.nextSibling).is('span')) {
  177. dw_linkwiz.insertLink(a.nextSibling.innerHTML);
  178. }else{
  179. dw_linkwiz.insertLink('');
  180. }
  181. }
  182. },
  183. /**
  184. * Insert the id currently in the entry box to the textarea,
  185. * replacing the current selection or at the cursor position.
  186. * When no selection is available the given title will be used
  187. * as link title instead
  188. */
  189. insertLink: function(title){
  190. var link = dw_linkwiz.$entry.val(),
  191. sel, stxt;
  192. if(!link) {
  193. return;
  194. }
  195. sel = DWgetSelection(dw_linkwiz.textArea);
  196. if(sel.start == 0 && sel.end == 0) {
  197. sel = dw_linkwiz.selection;
  198. }
  199. stxt = sel.getText();
  200. // don't include trailing space in selection
  201. if(stxt.charAt(stxt.length - 1) == ' '){
  202. sel.end--;
  203. stxt = sel.getText();
  204. }
  205. if(!stxt && !DOKU_UHC) {
  206. stxt=title;
  207. }
  208. // prepend colon inside namespaces for non namespace pages
  209. if(dw_linkwiz.textArea.form.id.value.indexOf(':') != -1 &&
  210. link.indexOf(':') == -1){
  211. link = ':' + link;
  212. }
  213. var so = link.length;
  214. var eo = 0;
  215. if(dw_linkwiz.val){
  216. if(dw_linkwiz.val.open) {
  217. so += dw_linkwiz.val.open.length;
  218. link = dw_linkwiz.val.open+link;
  219. }
  220. link += '|';
  221. so += 1;
  222. if(stxt) {
  223. link += stxt;
  224. }
  225. if(dw_linkwiz.val.close) {
  226. link += dw_linkwiz.val.close;
  227. eo = dw_linkwiz.val.close.length;
  228. }
  229. }
  230. pasteText(sel,link,{startofs: so, endofs: eo});
  231. dw_linkwiz.hide();
  232. // reset the entry to the parent namespace
  233. var externallinkpattern = new RegExp('^((f|ht)tps?:)?//', 'i'),
  234. entry_value;
  235. if (externallinkpattern.test(dw_linkwiz.$entry.val())) {
  236. if (JSINFO.namespace) {
  237. entry_value = JSINFO.namespace + ':';
  238. } else {
  239. entry_value = ''; //reset whole external links
  240. }
  241. } else {
  242. entry_value = dw_linkwiz.$entry.val().replace(/[^:]*$/, '')
  243. }
  244. dw_linkwiz.$entry.val(entry_value);
  245. },
  246. /**
  247. * Start the page/namespace lookup timer
  248. *
  249. * Calls autocomplete_exec when the timer runs out
  250. */
  251. autocomplete: function(){
  252. if(dw_linkwiz.timer !== null){
  253. window.clearTimeout(dw_linkwiz.timer);
  254. dw_linkwiz.timer = null;
  255. }
  256. dw_linkwiz.timer = window.setTimeout(dw_linkwiz.autocomplete_exec,350);
  257. },
  258. /**
  259. * Executes the AJAX call for the page/namespace lookup
  260. */
  261. autocomplete_exec: function(){
  262. var $res = jQuery(dw_linkwiz.result);
  263. dw_linkwiz.deselect();
  264. $res.html('<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />')
  265. .load(
  266. DOKU_BASE + 'lib/exe/ajax.php',
  267. {
  268. call: 'linkwiz',
  269. q: dw_linkwiz.$entry.val()
  270. }
  271. );
  272. },
  273. /**
  274. * Show the link wizard
  275. */
  276. show: function(){
  277. dw_linkwiz.selection = DWgetSelection(dw_linkwiz.textArea);
  278. dw_linkwiz.$wiz.show();
  279. dw_linkwiz.$entry.focus();
  280. dw_linkwiz.autocomplete();
  281. // Move the cursor to the end of the input
  282. var temp = dw_linkwiz.$entry.val();
  283. dw_linkwiz.$entry.val('');
  284. dw_linkwiz.$entry.val(temp);
  285. },
  286. /**
  287. * Hide the link wizard
  288. */
  289. hide: function(){
  290. dw_linkwiz.$wiz.hide();
  291. dw_linkwiz.textArea.focus();
  292. },
  293. /**
  294. * Toggle the link wizard
  295. */
  296. toggle: function(){
  297. if(dw_linkwiz.$wiz.css('display') == 'none'){
  298. dw_linkwiz.show();
  299. }else{
  300. dw_linkwiz.hide();
  301. }
  302. }
  303. };