Wiki source code of Attachment Picker Module
Last modified by Eleni Cojocariu on 2026/10/01 09:49
Show last authors
| author | version | line-number | content |
|---|---|---|---|
| 1 | The RequireJS module ##xwiki-attachment-picker##, one of the two modules of the [[Attachment Picker WebJar>>doc:documentation.xs.dev.attachments.attachment-picker-webjar.WebHome]], turns markup into a working picker: it builds the search field, fills the grid of results, and reports what the reader picks. | ||
| 2 | |||
| 3 | == Markup of a Picker == | ||
| 4 | |||
| 5 | A picker is an element carrying the class ##attachmentGalleryPicker##, holding four blocks that the JavaScript looks up inside it by class: | ||
| 6 | |||
| 7 | |=Block class|=Holds | ||
| 8 | |##attachmentPickerSearch##|The search field, which the JavaScript builds into it, and the class ##xform##, which it adds. The field is a Bootstrap ##input-group##: a text input and a pair of radio buttons named ##scope##, of values ##local## and ##global##, with ##local## checked. | ||
| 9 | |##attachmentPickerResults##|The grid of results, filled and emptied at every search. | ||
| 10 | |##attachmentPickerNoResults##|The message shown when a search returns nothing. Rendered with the class ##hidden##, which the JavaScript removes and adds back. | ||
| 11 | |##attachmentPickerGlobalSelection##|The warning shown while the selected attachment belongs to another Page. Rendered ##hidden## too. | ||
| 12 | |||
| 13 | Three optional attributes on the root element configure it. The JavaScript only reads them, and they are the three parameters of the ##attachmentGalleryPicker## macro: | ||
| 14 | |||
| 15 | |=Attribute|=Passed to ##SolrSearch## as|=Default | ||
| 16 | |##data-xwiki-attachment-picker-limit##|##limit##, parsed as an integer|##20##, both for an absent attribute and for a value that does not parse | ||
| 17 | |##data-xwiki-attachment-picker-target##|##target##|The Page the picker is displayed on | ||
| 18 | |##data-xwiki-attachment-picker-filter##|##filter## of the Solr options, where it becomes a media type clause of every query|No filter | ||
| 19 | |||
| 20 | The macro writes exactly that markup, but any markup carrying those classes works, once the module is loaded on the Page as described below: | ||
| 21 | |||
| 22 | {{code language="html"}} | ||
| 23 | <div class="attachmentGalleryPicker" | ||
| 24 | data-xwiki-attachment-picker-filter="image/*" | ||
| 25 | data-xwiki-attachment-picker-limit="10" | ||
| 26 | data-xwiki-attachment-picker-target="Sandbox.WebHome"> | ||
| 27 | <div class="attachmentPickerSearch"></div> | ||
| 28 | <div class="attachmentPickerResults"></div> | ||
| 29 | <div class="attachmentPickerNoResults hidden">No attachment matches your search.</div> | ||
| 30 | <div class="attachmentPickerGlobalSelection hidden">The attachment you picked belongs to another | ||
| 31 | page.</div> | ||
| 32 | </div> | ||
| 33 | {{/code}} | ||
| 34 | |||
| 35 | == Initialisation == | ||
| 36 | |||
| 37 | The module registers the jQuery plugin ##$.fn.attachmentGalleryPicker()##: | ||
| 38 | |||
| 39 | {{code language="javascript"}} | ||
| 40 | require(['jquery', 'xwiki-attachment-picker'], function ($) { | ||
| 41 | // The module exports nothing: requiring it is what registers the plugin. | ||
| 42 | $('#my-picker').attachmentGalleryPicker(); | ||
| 43 | }); | ||
| 44 | {{/code}} | ||
| 45 | |||
| 46 | |=Trigger|=What happens | ||
| 47 | |A call of the plugin|One picker is built and initialised per matched element. | ||
| 48 | |The document being ready, then every ##xwiki:dom:updated## event|The module initialises every element of class ##attachmentGalleryPicker## by itself, among the elements the event carries or in the whole document, so a picker inserted by an asynchronous request needs no call of your own. | ||
| 49 | |Initialising a picker|It is marked with the class ##initialized##, and a first search runs with an empty query, so the grid is filled before the reader types anything. | ||
| 50 | |Initialising a picker that already carries ##initialized##|Nothing, which is what makes the automatic initialisation safe. | ||
| 51 | |Typing in the search field, or switching scope|A new search, debounced by 500 milliseconds on both. | ||
| 52 | |||
| 53 | == Results == | ||
| 54 | |||
| 55 | Every result of a search is one ##attachmentGroup## element: | ||
| 56 | |||
| 57 | {{code language="html"}} | ||
| 58 | <span class="attachmentGroup localAttachment"> <!-- jQuery data "id": the attachment reference --> | ||
| 59 | <a title="logo.png" href="/xwiki/bin/download/Sandbox/WebHome/logo.png"> | ||
| 60 | <span class="previewWrapper"> | ||
| 61 | <img loading="lazy" alt="logo.png" | ||
| 62 | src="/xwiki/bin/download/Sandbox/WebHome/logo.png?width=150&height=150&keepAspectRatio=true"> | ||
| 63 | </span> | ||
| 64 | <span class="attachmentTitle" title="logo.png">logo.png</span> | ||
| 65 | </a> | ||
| 66 | </span> | ||
| 67 | {{/code}} | ||
| 68 | |||
| 69 | |=Carried by a result|=Value | ||
| 70 | |The class ##localAttachment## or ##globalAttachment##|Whether the attachment comes from the Page being searched or from another one | ||
| 71 | |The class ##selected##|Set on the selected result | ||
| 72 | |The jQuery data ##id##, on the group|The reference of the attachment, held as data rather than as an attribute | ||
| 73 | |The jQuery data ##index##, on the link|The rank of the result in the grid, held the same way | ||
| 74 | |The ##attachmentTitle## span, and the ##title## of both the span and the link|The file name | ||
| 75 | |||
| 76 | |=Preview|=Shown for | ||
| 77 | |The attachment itself, in an ##img## whose address carries ##?width=150&height=150&keepAspectRatio=true##, so the wiki resizes it into a 150 by 150 box while preserving its aspect ratio|A media type starting with ##image/## | ||
| 78 | |A span carrying the class of the media type's icon, taken from ##xwiki-attachments-icon##, plus the class ##attachmentIcon##|Any other media type, with a font icon theme | ||
| 79 | |An image element pointing at the address of that icon|Any other media type, with a theme that is not a font one | ||
| 80 | |||
| 81 | While a search runs, the results block is emptied and carries the class ##loading##. A search that fails is logged to the browser console and reported to the reader as an error notification. | ||
| 82 | |||
| 83 | == Selection == | ||
| 84 | |||
| 85 | A picker reports what the reader picks through two events fired on its root element, documented with an example on the [[attachmentGalleryPicker macro>>doc:documentation.xs.dev.attachments.attachmentgallerypicker-macro.WebHome]] page. | ||
| 86 | |||
| 87 | |=Rule|=Behaviour | ||
| 88 | |Selection is single|Selecting a result unselects every other. | ||
| 89 | |Clicking the selected result|Clears the selection. | ||
| 90 | |Double click|Only the first click of a chain selects, so the second click does not undo the selection the first one made. | ||
| 91 | |A new search|Keeps the selection while the attachment is still among the results, and clears it otherwise. | ||
| 92 | |The ##attachmentPickerGlobalSelection## block|Shown exactly while the selected result carries ##globalAttachment##, and hidden again when the selection is cleared or a new search runs. | ||
| 93 | |||
| 94 | == Messages == | ||
| 95 | |||
| 96 | The four messages of the picker are loaded through ##xwiki-l10n!##, under the prefix ##attachment.picker.##, and none of them is defined in this WebJar: | ||
| 97 | |||
| 98 | |=Key|=Labels|=Defined by | ||
| 99 | |##solrSearch.query.errorMessage##|The notification shown when a search fails|The ##attachmentGalleryPicker## macro module | ||
| 100 | |##searchField.placeholder##|The search field|The ##Attachment.Picker.Code.Translations## page of the Attachment Picker Application | ||
| 101 | |##searchField.scope.currentPage##|The scope button of the current Page|The same page | ||
| 102 | |##searchField.scope.allPages##|The scope button of every Page|The same page | ||
| 103 | |||
| 104 | The texts of the two blocks the JavaScript only shows and hides, ##attachment.picker.macro.notResult.message## and ##attachment.picker.macro.globalSelection.message##, belong to the macro module as well. |