1define('confluence/autocomplete-content', [ 2 'jquery', 3 'document', 4 'ajs', 5 'confluence-rest/confluence-rest' 6], function( 7 $, 8 document, 9 AJS, 10 ConfluenceRest 11) { 12 'use strict'; 13 14 var makeRestMatrixFromData = function(response) { 15 if (!response || !response.group) { 16 throw new Error('Invalid JSON format'); 17 } 18 19 var matrix = []; 20 21 for (var i = 0, ii = response.group.length; i < ii; i++) { 22 matrix.push(response.group[i].result); 23 } 24 return matrix; 25 }; 26 27 var bind = function(input, contentTypes, valueTemplate, displayHandler) { 28 var $input = $(input); 29 var boundKey = 'data-autocomplete-content-bound'; 30 31 if ($input.attr(boundKey)) { 32 return; 33 } 34 35 if (typeof contentTypes === 'string') { 36 contentTypes = [contentTypes]; 37 } 38 39 $input.attr(boundKey, 'true').attr('autocomplete', 'off'); 40 var typesString = contentTypes.join(','); 41 var maxResults = $input.attr('data-max') || 10; 42 var alignment = $input.attr('data-alignment') || 'left'; 43 var spaceKey = $input.attr('data-spacekey'); 44 var noResults = $input.attr('data-none-message'); 45 var searchLinkMessage = $input.attr('data-search-link-message'); 46 var template = $input.attr('data-template') || valueTemplate; 47 var targetSelector = $input.attr('data-target'); 48 var dropDownTarget = $input.attr('data-dropdown-target'); 49 var dropDownPosition = null; 50 51 if (dropDownTarget) { 52 dropDownPosition = $(dropDownTarget); 53 } else { 54 dropDownPosition = $('<div></div>'); 55 $input.after(dropDownPosition); 56 } 57 dropDownPosition.addClass('aui-dd-parent autocomplete'); 58 59 $input.focus(function() { 60 // Some places like the Link Browser allow a select element to change the space. 61 // The autocomplete should re-cache the spaceKey on focus. 62 spaceKey = $input.attr('data-spacekey'); 63 }); 64 65 $input.quicksearch(AJS.REST.getBaseUrl() + 'search/name.json', null, { 66 onShow: function() { 67 $input.trigger('open.autocomplete-content', { contentTypes: contentTypes }); 68 }, 69 makeParams: function(val) { 70 var params = { 71 'max-results': maxResults, 72 pageSize: maxResults, 73 type: typesString, 74 query: val 75 }; 76 if (spaceKey) { 77 params.spaceKey = spaceKey; 78 } 79 return params; 80 }, 81 dropdownPlacement: function(dd) { 82 dropDownPosition.append(dd); 83 }, 84 makeRestMatrixFromData: makeRestMatrixFromData, 85 addDropdownData: function(matrix) { 86 if (!matrix.length) { 87 if (noResults) { 88 matrix.push([ 89 { 90 name: noResults, 91 className: 'no-results', 92 href: '#' 93 } 94 ]); 95 } 96 } 97 if (searchLinkMessage) { 98 var value = AJS.escapeHtml($input.val()); 99 var searchPrompt = AJS.format(searchLinkMessage, value); 100 matrix.push([ 101 { 102 className: 'search-for', 103 name: searchPrompt, 104 href: '#' 105 } 106 ]); 107 } 108 return matrix; 109 }, 110 ajsDropDownOptions: { 111 alignment: alignment, 112 displayHandler: displayHandler, 113 selectionHandler: function(e, selection) { 114 AJS.debug('autocomplete-content: ajsDropDownOptions.selectionHandler'); 115 if (selection.find('.search-for').length) { 116 $input.trigger('selected.autocomplete-content', { 117 contentTypes: contentTypes, 118 searchFor: $input.val() 119 }); 120 return; 121 } 122 if (selection.find('.no-results').length) { 123 AJS.debug('no results selected'); 124 this.hide(); 125 e.preventDefault(); 126 return; 127 } 128 129 var contentProps = selection.data('properties'); 130 131 $input.val(AJS.template(template).fillHtml(contentProps.restObj)); 132
133 if (targetSelector) { 134 var value = AJS.template(valueTemplate).fillHtml(contentProps.restObj); 135 $(targetSelector).val(value); 136 } 137 138 $input.trigger('selected.autocomplete-content', { 139 contentTypes: contentTypes, 140 content: contentProps.restObj, 141 selection: selection 142 }); 143 this.hide(); 144 e.preventDefault(); 145 } 146 } 147 }); 148 }; 149 150 /** 151 * @exports confluence/autocomplete-content 152 */ 153 var autocompleteContent = {}; 154 155 /** 156 * Space Autocomplete Binder Component. 157 * <br> 158 * Example markup: 159 * <input class="autocomplete-space" data-max="10" data-none-message="No results" data-template="{name}"> 160 * 161 * Events Triggered: 162 * open.autocomplete-content, selected.autocomplete-content 163 * 164 * @since 3.4 165 */ 166 autocompleteContent.autocompleteSpace = function(scope) { 167 scope = scope || document.body; 168 $('input.autocomplete-space', scope).each(function() { 169 bind(this, ['spacedesc', 'personalspacedesc'], '{name}', function(obj) { 170 return obj.name; 171 }); 172 }); 173 }; 174 175 /** 176 * Attachment Autocomplete Binder Component. 177 * <br> 178 * Example markup: 179 * <input class="autocomplete-attachment" data-max="10" data-none-message="No results" data-template="{fileName}"> 180 * 181 * Events Triggered: 182 * open.autocomplete-content, selected.autocomplete-content 183 * 184 * @since 3.4 185 */ 186 autocompleteContent.autocompleteAttachment = function(scope) { 187 scope = scope || document.body; 188 $('input.autocomplete-attachment', scope).each(function() { 189 bind(this, 'attachment', '{fileName}', 190 function(obj) { 191 var str = (obj.restObj && obj.restObj.fileName) || obj.name; 192 if (obj.restObj && obj.restObj.space && obj.restObj.space.title) { 193 str += ' (' + obj.restObj.space.title + ')'; 194 } 195 return ConfluenceRest.safeHTML(str); 196 }); 197 }); 198 }; 199 200 var contentDisplayHandler = function(obj) { 201 return (obj.restObj && obj.restObj.title && AJS.escapeHtml(obj.restObj.title)) || obj.name; 202 }; 203 204 /** 205 * Page Autocomplete Binder Component. 206 * <br> 207 * Example markup: 208 * <input class="autocomplete-page" data-max="10" data-none-message="No results" data-template="{title}"> 209 * 210 * Events Triggered: 211 * open.autocomplete-content, selected.autocomplete-content 212 * 213 * @since 3.4 214 */ 215 autocompleteContent.autocompletePage = function(scope) { 216 scope = scope || document.body; 217 $('input.autocomplete-page', scope).each(function() { 218 bind(this, 'page', '{title}', contentDisplayHandler); 219 }); 220 }; 221 222 /** 223 * Blog post Autocomplete Binder Component. 224 * <br> 225 * Example markup: 226 * <input class="autocomplete-blogpost" data-max="10" data-none-message="No results" data-template="{title}"> 227 * 228 * Events Triggered: 229 * open.autocomplete-content, selected.autocomplete-content 230 * 231 * @since 3.4 232 */ 233 autocompleteContent.autocompleteBlogpost = function(scope) { 234 scope = scope || document.body; 235 $('input.autocomplete-blogpost', scope).each(function() { 236 bind(this, 'blogpost', '{title}', contentDisplayHandler); 237 }); 238 }; 239 240 /** 241 * Confluence content Autocomplete Binder Component i.e. Pages, Blog posts 242 * <br> 243 * Example markup: 244 * <input class="autocomplete-confluence-content" data-max="10" data-none-message="No results" data-template="{title}"> 245 * 246 * Events Triggered: 247 * open.autocomplete-content, selected.autocomplete-content 248 * 249 * @since 3.4 250 */ 251 autocompleteContent.autocompleteConfluenceContent = function(scope) { 252 scope = scope || document.body; 253 $('input.autocomplete-confluence-content', scope).each(function() { 254 bind(this, ['page', 'blogpost'], '{title}', contentDisplayHandler); 255 }); 256 }; 257 258 /** 259 * General Confluence content Autocomplete Binder Component - pages, blogs, space
259s, users, attachments 260 * <br> 261 * Example markup: 262 * <input class="autocomplete-search" data-max="10" data-none-message="No results" data-template="{title}"> 263 * 264 * Events Triggered: 265 * open.autocomplete-search, selected.autocomplete-search 266 * 267 * @since 4.0 268 */ 269 autocompleteContent.autocompleteSearch = function(scope) { 270 scope = scope || document.body; 271 $('input.autocomplete-search', scope).each(function() { 272 // Empty contentTypes array uses ResultTemplate.DEFAULT at the back end for grouping and max-results. 273 bind(this, [], '{title}', contentDisplayHandler); 274 }); 275 }; 276 277 return autocompleteContent; 278}); 279 280require('confluence/module-exporter').safeRequire('confluence/autocomplete-content', function( 281 autocompleteContent 282) { 283 'use strict'; 284 285 var Confluence = require('confluence/legacy'); 286 287 Confluence.Binder.autocompleteSpace = autocompleteContent.autocompleteSpace; 288 Confluence.Binder.autocompleteAttachment = autocompleteContent.autocompleteAttachment; 289 Confluence.Binder.autocompletePage = autocompleteContent.autocompletePage; 290 Confluence.Binder.autocompleteBlogpost = autocompleteContent.autocompleteBlogpost; 291 Confluence.Binder.autocompleteConfluenceContent = 292 autocompleteContent.autocompleteConfluenceContent; 293 Confluence.Binder.autocompleteSearch = autocompleteContent.autocompleteSearch; 294});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.