Skip to content
Snippets Groups Projects
Commit 7c01f81f authored by Manuel Leduc's avatar Manuel Leduc
Browse files

XWIKI-20455: Image dialog from several editor instances should be isolated

- Introduce a target parameter on the attachmentGalleryPicker macro
- Create an image selector modal instance per editor
- Use the source document of the current editor instance to resolve and search for the images
parent cad681e1
No related branches found
No related tags found
No related merge requests found
Showing
with 358 additions and 205 deletions
......@@ -23,7 +23,9 @@
import java.util.List;
import org.xwiki.attachment.picker.internal.AttachmentGalleryPickerMacro;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.properties.annotation.PropertyDescription;
import org.xwiki.properties.annotation.PropertyDisplayType;
import org.xwiki.properties.annotation.PropertyName;
import org.xwiki.stability.Unstable;
......@@ -42,6 +44,8 @@ public class AttachmentGalleryPickerMacroParameters
private Integer limit = 20;
private String targetDocumentReference;
/**
* @return the id of the attachment picker macro
*/
......@@ -95,4 +99,28 @@ public void setLimit(Integer limit)
{
this.limit = limit;
}
/**
* @return the reference to the document that will be set as the current document to display the macro content
* @since 14.10.2
* @since 15.0RC1
*/
@Unstable
public String getTarget()
{
return this.targetDocumentReference;
}
/**
* @param targetDocumentReference refer to {@link #getTarget()}
* @since 14.10.2
* @since 15.0RC1
*/
@PropertyDescription("The reference to the document serving as the current document")
@PropertyDisplayType(DocumentReference.class)
@Unstable
public void setTarget(String targetDocumentReference)
{
this.targetDocumentReference = targetDocumentReference;
}
}
......@@ -110,6 +110,9 @@ public List<Block> execute(AttachmentGalleryPickerMacroParameters parameters, St
if (parameters.getLimit() != null) {
attachmentPickerParameters.put("data-xwiki-attachment-picker-limit", String.valueOf(parameters.getLimit()));
}
if (parameters.getTarget() != null) {
attachmentPickerParameters.put("data-xwiki-attachment-picker-target", parameters.getTarget());
}
return List.of(new GroupBlock(List.of(
// Search block.
......
......@@ -134,4 +134,13 @@ void executeWithFilter()
this.attachmentGalleryPickerMacro.execute(params, null, this.macroTransformationContext);
assertEquals("image/*,image/jpeg", actual.get(0).getParameter("data-xwiki-attachment-picker-filter"));
}
@Test
void executeWithTarget()
{
AttachmentGalleryPickerMacroParameters params = new AttachmentGalleryPickerMacroParameters();
params.setTarget("xwiki:Space.Page");
List<Block> actual = this.attachmentGalleryPickerMacro.execute(params, null, this.macroTransformationContext);
assertEquals("xwiki:Space.Page", actual.get(0).getParameter("data-xwiki-attachment-picker-target"));
}
}
......@@ -125,13 +125,21 @@
</property>
<property>
<code>require(['jquery', 'imageSelector'], function($, imageSelector) {
const picker = $('#attachmentPickerCKEditor')
picker.on('xwiki:attachmentGalleryPicker:selected', function(_target, attachmentReference) {
imageSelector.updateSelectedImageReferences([attachmentReference]);
});
function init(element) {
const picker = element.find('.attachmentPickerCKEditor');
picker.on('xwiki:attachmentGalleryPicker:selected', function(_target, attachmentReference) {
imageSelector.updateSelectedImageReferences([attachmentReference], $(this));
});
picker.on('xwiki:attachmentGalleryPicker:unselected', function() {
imageSelector.updateSelectedImageReferences([]);
picker.on('xwiki:attachmentGalleryPicker:unselected', function() {
imageSelector.updateSelectedImageReferences([], $(this));
});
}
init($(document).find('.image-selector'));
$(document).on('xwiki:dom:updated', (event, data) =&gt; {
data.elements.forEach(element =&gt; init($(element)));
});
});</code>
</property>
......@@ -273,7 +281,11 @@
#set ($discard = $xwiki.jsx.use('Attachment.Picker.Code.CKEditorImagePlugin'))
{{/velocity}}
{{attachmentGalleryPicker id='attachmentPickerCKEditor' filter="image/*"/}}</content>
{{velocity}}
(% class='attachmentPickerCKEditor' %)(((
{{attachmentGalleryPicker filter="image/*" target="$services.rendering.escape($request.documentReference, 'xwiki/2.1')"/}}
)))
{{/velocity}}</content>
</property>
<property>
<extensionPointId>org.xwiki.contrib.ckeditor.plugins.imageSelector</extensionPointId>
......
......@@ -60,15 +60,17 @@ define('xwiki-attachment-picker',
this.sugestSolrServiceURL = doc.getURL('get');
this.options = options || {};
this.limit = options.limit || 20;
this.target = options.target;
this.solrOptions = this.options.solrOptions || {};
}
search(query, isGlobal) {
const {currentWiki, currentSpace, currentPage} = this.resolveTargetFqs();
const localDocumentOnly = this.searchSolr(query, Object.assign({}, this.solrOptions, {
fqs: [
`wiki:${XWiki.currentWiki}`,
`space:"${XWiki.currentSpace}"`,
`name:"${XWiki.currentPage}"`
`wiki:${currentWiki}`,
`space:"${currentSpace}"`,
`name:"${currentPage}"`
]
}));
......@@ -100,6 +102,21 @@ define('xwiki-attachment-picker',
});
}
resolveTargetFqs() {
let currentWiki = XWiki.currentWiki;
let currentSpace = XWiki.currentSpace;
let currentPage = XWiki.currentPage;
if (this.target) {
const target = new XWiki.Document(XWiki.Model.resolve(this.target, XWiki.EntityType.DOCUMENT));
if (target.wiki) {
currentWiki = target.wiki;
}
currentSpace = target.space;
currentPage = target.page;
}
return {currentWiki, currentSpace, currentPage};
}
searchSolr(input, options) {
options = options || {};
const optionsFqs = options.fqs || [];
......@@ -183,6 +200,7 @@ define('xwiki-attachment-picker',
this.searchBlock.append(inputGroup);
this.solrSearch = new SolrSearch({
limit: parseInt(this.rootBlock.data('xwiki-attachment-picker-limit')),
target: this.rootBlock.data('xwiki-attachment-picker-target'),
solrOptions: {
filter: this.rootBlock.data('xwiki-attachment-picker-filter')
}
......
......@@ -31,78 +31,90 @@ define('imageSelectorTranslationKeys', [], [
define('imageSelector', ['jquery', 'modal', 'resource', 'l10n!imageSelector'],
function($, $modal, resource, translations) {
'use strict';
// Counter of the image selector instances. Each new instantiation increments this counter.
var counter = 0;
function scopedImageSelector(documentReference) {
var index = counter;
counter = counter + 1;
function getEntityReference(referenceStr) {
var reference;
if (referenceStr.startsWith("attachment:")) {
var separatorIndex = referenceStr.indexOf(':');
reference = referenceStr.substr(separatorIndex + 1);
} else {
reference = referenceStr;
}
function getEntityReference(referenceStr) {
var reference;
if (referenceStr.startsWith("attachment:")) {
var separatorIndex = referenceStr.indexOf(':');
reference = referenceStr.substr(separatorIndex + 1);
} else {
reference = referenceStr;
return XWiki.Model.resolve(reference, XWiki.EntityType.ATTACHMENT);
}
return XWiki.Model.resolve(reference, XWiki.EntityType.ATTACHMENT);
}
function getCurrentTabId() {
return $(".image-selector .tab-pane.active").attr('id');
}
// Internal map of the tab ids and their corresponding image references.
var mapTabReference = {};
function setImageReferenceValue(value) {
if (value) {
modal.data('imageReference', {
value: value
});
$('.image-selector-modal button.btn-primary').prop('disabled', false);
} else {
modal.data('imageReference', {});
$('.image-selector-modal button.btn-primary').prop('disabled', true);
function getCurrentTabId() {
return modal.find(".image-selector .tab-pane.active").attr('id');
}
}
/**
* Can be called by the image selector tab UIXs. Indicates the list of selected images. Passing an empty array
* indicates that not images are currently selected. The images can either be a string that will be parsed to a
* resource reference, or a reference object (e.g., "{type: 'icon', reference: 'accept'}");
* Note: Currently, only the first image of the list is taken into account.
*
* @param imageReferences the selected image references
*/
function updateSelectedImageReferences(imageReferences) {
imageReferences = imageReferences || [];
var imageReferenceValue;
if(imageReferences.length > 0) {
// TODO: Support the selection of several images (see CKEDITOR-445).
var imageReference = imageReferences[0];
var value;
if (typeof imageReference === 'string') {
value = resource.convertEntityReferenceToResourceReference(getEntityReference(imageReference));
// Internal map of the tab ids and their corresponding image references.
var mapTabReference = {};
function setImageReferenceValue(value) {
if (value) {
modal.data('imageReference', {
value: value
});
$('.image-selector-modal button.btn-primary').prop('disabled', false);
} else {
value = imageReference;
value.typed = true;
modal.data('imageReference', {});
$('.image-selector-modal button.btn-primary').prop('disabled', true);
}
imageReferenceValue = value;
}
// Save the value in a map to be able to retrieve it later in case of tab change.
mapTabReference[getCurrentTabId()] = imageReferenceValue;
/**
* Can be called by the image selector tab UIXs. Indicates the list of selected images. Passing an empty array
* indicates that not images are currently selected. The images can either be a string that will be parsed to a
* resource reference, or a reference object (e.g., "{type: 'icon', reference: 'accept'}");
* Note: Currently, only the first image of the list is taken into account.
*
* @param imageReferences the selected image references
*/
function updateSelectedImageReferences(imageReferences) {
imageReferences = imageReferences || [];
var imageReferenceValue;
if (imageReferences.length > 0) {
// TODO: Support the selection of several images (see CKEDITOR-445).
var imageReference = imageReferences[0];
var value;
if (typeof imageReference === 'string') {
value = resource.convertEntityReferenceToResourceReference(getEntityReference(imageReference));
} else {
value = imageReference;
value.typed = true;
}
imageReferenceValue = value;
}
setImageReferenceValue(imageReferenceValue);
}
// Save the value in a map to be able to retrieve it later in case of tab change.
mapTabReference[getCurrentTabId()] = imageReferenceValue;
function initialize(modal) {
if (!modal.data('initialized')) {
var url = new XWiki.Document(XWiki.Model.resolve('CKEditor.ImageSelectorService', XWiki.EntityType.DOCUMENT))
.getURL('get');
$.get(url, $.param({language: $('html').attr('lang')}))
.done(function(html, textState, jqXHR) {
var imageSelector = $('.image-selector');
setImageReferenceValue(imageReferenceValue);
}
function initialize(modal) {
if (!modal.data('initialized')) {
var url = new XWiki.Document(XWiki.Model.resolve('CKEditor.ImageSelectorService', XWiki.EntityType.DOCUMENT))
.getURL('get');
$.get(url, $.param({
language: $('html').attr('lang'),
index: index,
documentReference: documentReference
})).done(function (html, textState, jqXHR) {
var imageSelector = modal.find('.image-selector');
var requiredSkinExtensions = jqXHR.getResponseHeader('X-XWIKI-HTML-HEAD');
$(document).loadRequiredSkinExtensions(requiredSkinExtensions);
imageSelector.html(html);
$(document).trigger('xwiki:dom:updated', {'elements': imageSelector.toArray()});
imageSelector.removeClass('loading');
// Update the selection with the value of the current tab on tab change.
......@@ -111,83 +123,113 @@ define('imageSelector', ['jquery', 'modal', 'resource', 'l10n!imageSelector'],
});
modal.data('initialized', true);
}).fail(function(error) {
console.log('Failed to retrieve the image selection form.', error);
new XWiki.widgets.Notification(translations.get('modal.initialization.fail'), 'error');
modal.data('initialized', true);
});
}).fail(function (error) {
console.log('Failed to retrieve the image selection form.', error);
new XWiki.widgets.Notification(translations.get('modal.initialization.fail'), 'error');
modal.data('initialized', true);
});
}
}
}
// Defined once the modal is initialized.
var modal;
// Initialize the modal.
var createModal = $modal.createModalStep({
'class': 'image-selector-modal',
title: translations.get('modal.title'),
acceptLabel: translations.get('modal.selectButton'),
content: '<div class="image-selector loading"></div>',
onLoad: function() {
modal = this;
var selectButton = modal.find('.modal-footer .btn-primary');
// Make the modal larger.
modal.find('.modal-dialog').addClass('modal-lg');
modal.on('shown.bs.modal', function() {
initialize(modal);
// Defined once the modal is initialized.
var modal;
// Initialize the modal.
var createModal = $modal.createModalStep({
'class': 'image-selector-modal',
title: translations.get('modal.title'),
acceptLabel: translations.get('modal.selectButton'),
content: '<div class="image-selector loading"></div>',
onLoad: function () {
modal = this;
var selectButton = modal.find('.modal-footer .btn-primary');
// Make the modal larger.
modal.find('.modal-dialog').addClass('modal-lg');
modal.on('shown.bs.modal', function () {
initialize(modal);
});
selectButton.on('click', function () {
var imageData = modal.data('input').imageData || {};
imageData.resourceReference = modal.data('imageReference').value;
var output = {
imageData: imageData,
editor: modal.data('input').editor,
newImage: modal.data('input').newImage
};
modal.data('output', output).modal('hide');
});
}
});
/**
* Initialize a loader for the provided upload field. Three optional callbacks can be provided in the options
* object:
* - onSuccess is called when the upload is successful, the entity reference is passed as argument.
* - onError is called when the upload fails
* - onAbort is called when the upload is aborted
* @param uploadField the upload field to create the loader for
* @param options an option object with callback actions
*/
function createLoader(uploadField, options) {
options = options || {};
var editor = modal.data('input').editor;
var loader = editor.uploadRepository.create(uploadField);
loader.on('uploaded', function (evt) {
var resourceReference = evt.sender.responseData.message.resourceReference;
var entityReference = resource.convertResourceReferenceToEntityReference(resourceReference);
if (options.onSuccess) {
options.onSuccess(entityReference);
}
});
loader.on('error', function (error) {
console.log('Failed to upload a file', error);
if (options.onError) {
options.onError();
}
});
selectButton.on('click', function() {
var imageData = modal.data('input').imageData || {};
imageData.resourceReference = modal.data('imageReference').value;
var output = {
imageData: imageData,
editor: modal.data('input').editor,
newImage: modal.data('input').newImage
};
modal.data('output', output).modal('hide');
loader.on('abort', function (error) {
console.log('Failed to upload a file', error);
if (options.onAbort) {
options.onAbort();
}
});
loader.loadAndUpload(editor.config.filebrowserUploadUrl);
}
});
/**
* Initialize a loader for the provided upload field. Three optional callbacks can be provided in the options
* object:
* - onSuccess is called when the upload is successful, the entity reference is passed as argument.
* - onError is called when the upload fails
* - onAbort is called when the upload is aborted
* @param uploadField the upload field to create the loader for
* @param options an option object with callback actions
*/
function createLoader(uploadField, options) {
options = options || {};
var editor = modal.data('input').editor;
var loader = editor.uploadRepository.create(uploadField);
loader.on('uploaded', function(evt) {
var resourceReference = evt.sender.responseData.message.resourceReference;
var entityReference = resource.convertResourceReferenceToEntityReference(resourceReference);
if (options.onSuccess) {
options.onSuccess(entityReference);
}
});
loader.on('error', function(error) {
console.log('Failed to upload a file', error);
if (options.onError) {
options.onError();
}
});
loader.on('abort', function(error) {
console.log('Failed to upload a file', error);
if (options.onAbort) {
options.onAbort();
}
});
return {
open: createModal,
updateSelectedImageReferences: updateSelectedImageReferences,
createLoader: createLoader
};
}
loader.loadAndUpload(editor.config.filebrowserUploadUrl);
function getDocumentReference(params) {
return XWiki.Model.serialize(params.editor.config.sourceDocument.documentReference);
}
var mapScopes = {};
function createModal(params) {
var documentReference = getDocumentReference(params);
if (!mapScopes[documentReference]) {
mapScopes[documentReference] = scopedImageSelector(documentReference);
}
return mapScopes[documentReference].open(params);
}
function updateSelectedImageReferences(imageReferences, element) {
var documentReference = getDocumentReference(element.parents('.image-selector-modal').data('input'));
mapScopes[documentReference].updateSelectedImageReferences(imageReferences);
}
function createLoader(uploadField, options, element) {
var documentReference = getDocumentReference(element.parents('.image-selector-modal').data('input'));
mapScopes[documentReference].createLoader(uploadField, options);
}
return {
open: createModal,
updateSelectedImageReferences: updateSelectedImageReferences,
......
......@@ -43,6 +43,9 @@ define('imageWizard', ['imageSelector', 'imageEditor'], function(imageSelector,
}
return function(params) {
if (CKEDITOR.currentInstance) {
params.currentDocument = CKEDITOR.currentInstance.config.sourceDocument.documentReference;
}
if (params.isInsert === false) {
return editOnly(params);
} else {
......
......@@ -53,9 +53,10 @@
&lt;ul class="nav nav-tabs" role="tablist"&gt;
&lt;!-- Nav tabs --&gt;
#foreach ($uix in $selectorUIXs)
#set ($tabId = "${uix.parameters.id}-$request.index")
&lt;li role="presentation" #if($foreach.index == 0)class="active"#end&gt;
&lt;a href="#$escapetool.url($uix.parameters.id)"
aria-controls="$escapetool.xml($uix.parameters.id)"
&lt;a href="#$escapetool.url($tabId)"
aria-controls="$escapetool.xml($tabId)"
role="tab" data-toggle="tab"&gt;
$escapetool.xml($services.localization.render($uix.parameters.title))
&lt;/a&gt;
......@@ -66,7 +67,10 @@
&lt;!-- Tab panes --&gt;
&lt;div class="tab-content"&gt;
#foreach ($uix in $selectorUIXs)
&lt;div role="tabpanel" class="tab-pane#if($foreach.index == 0) active#end" id="$escapetool.url($uix.parameters.id)"&gt;
#set ($tabId = "${uix.parameters.id}-$request.index")
&lt;div role="tabpanel"
class="$escapetool.xml(${uix.parameters.id}) tab-pane#if($foreach.index == 0) active#end"
id="$escapetool.xml($tabId)"&gt;
$services.rendering.render($uix.execute(), 'xhtml/1.0')
&lt;/div&gt;
#end
......
......@@ -129,26 +129,35 @@
return selected &amp;&amp; selected.startsWith("attachment:");
}
$('.attachments-tree').xtree()
.one('ready.jstree', function (event, data) {
data.instance.openTo("document:" + XWiki.Model.serialize(XWiki.currentDocument.getDocumentReference()));
})
.on('changed.jstree', function (event, data) {
if (validateSelection(data.instance)) {
imageSelector.updateSelectedImageReferences([getSelected(data.instance)]);
} else {
imageSelector.updateSelectedImageReferences([]);
}
})
.on('load_node.jstree', function (node, status) {
for (var child of status.node.children) {
var childNode = status.instance.get_node(child);
if (childNode.data.mimetype &amp;&amp; !childNode.data.mimetype.startsWith('image/')) {
// Disable the nodes instead of hiding them because they still can be search in the search field.
status.instance.disable_node(childNode);
function init(element) {
element.find('.attachments-tree').xtree()
.one('ready.jstree', function (event, data) {
const documentReference = element.find('input[name="documentReference"]').val()
data.instance.openTo(documentReference);
})
.on('changed.jstree', function (event, data) {
if (validateSelection(data.instance)) {
imageSelector.updateSelectedImageReferences([getSelected(data.instance)], $(this));
} else {
imageSelector.updateSelectedImageReferences([], $(this));
}
}
});
})
.on('load_node.jstree', function (node, status) {
for (var child of status.node.children) {
var childNode = status.instance.get_node(child);
if (childNode.data.mimetype &amp;&amp; !childNode.data.mimetype.startsWith('image/')) {
// Disable the nodes instead of hiding them because they still can be search in the search field.
status.instance.disable_node(childNode);
}
}
});
}
init($(document).find('.image-selector'));
$(document).on('xwiki:dom:updated', (event, data) =&gt; {
data.elements.forEach(element =&gt; init($(element)));
});
});</code>
</property>
<property>
......@@ -236,12 +245,12 @@
<property>
<content>{{velocity}}
#set ($discard = $xwiki.jsx.use('CKEditor.ImageSelectorServiceUIX.DocumentTree'))
{{html clean="false"}}
{{html clean="false" wiki="false"}}
&lt;input type='hidden' name='documentReference' value='$escapetool.xml("document:$services.model.serialize($request.documentReference, 'default')")' /&gt;
#template('documentTree_macros.vm')
#documentTree({
'class': 'attachments-tree',
'finder': true,
'openTo': "document:$services.model.serialize($doc.documentReference, 'default')",
'showWikis': true,
'showTranslations': false
})
......
......@@ -120,12 +120,20 @@
</property>
<property>
<code>require(['jquery', 'imageSelector'], function($, imageSelector) {
$('#iconTab input[type="text"]').on('input', function () {
if ($(this).val() === '') {
imageSelector.updateSelectedImageReferences([]);
} else {
imageSelector.updateSelectedImageReferences([{type: 'icon', reference: $(this).val()}]);
}
function init(element) {
element.find('.iconTab input[type="text"]').on('input', function () {
if ($(this).val() === '') {
imageSelector.updateSelectedImageReferences([], $(this));
} else {
imageSelector.updateSelectedImageReferences([{type: 'icon', reference: $(this).val()}], $(this));
}
});
}
init($(document).find('.image-selector'));
$(document).on('xwiki:dom:updated', (event, data) =&gt; {
data.elements.forEach(element =&gt; init($(element)));
});
});</code>
</property>
......
......@@ -124,48 +124,57 @@
<cache>long</cache>
</property>
<property>
<code>require(['jquery', 'imageSelector', 'l10n!imageSelector', 'resource'], function($, imageSelector, translations, resource) {
const imageSelectorElement = $('.image-selector');
const uploadButton = $("#upload form input.button");
<code>require(['jquery', 'imageSelector', 'l10n!imageSelector', 'resource'],
function ($, imageSelector, translations, resource) {
function init(element) {
const uploadButton = element.find(".upload form input.button");
uploadButton.prop("disabled", true);
imageSelectorElement.on('change', function() {
if($("#fileUploadField").prop('files').length &gt; 0) {
uploadButton.prop("disabled", false);
} else {
uploadButton.prop("disabled", true);
}
});
uploadButton.on('click', function(event) {
event.preventDefault();
element.on('change', function () {
if ($(this).find("input[type='file']").prop('files').length &gt; 0) {
uploadButton.prop("disabled", false);
} else {
uploadButton.prop("disabled", true);
}
});
uploadButton.on('click', function (event) {
event.preventDefault();
const uploadedFile = $("#fileUploadField").prop('files')[0];
const uploadedFile = element.find("input[type='file']").prop('files')[0];
const beforeUploadEvent = $.Event("xwiki:actions:beforeUpload");
$(document).trigger(beforeUploadEvent, {file: uploadedFile});
const beforeUploadEvent = $.Event("xwiki:actions:beforeUpload");
$(document).trigger(beforeUploadEvent, {file: uploadedFile});
if (!beforeUploadEvent.isDefaultPrevented()) {
imageSelectorElement.addClass('loading');
imageSelector.createLoader(uploadedFile, {
onSuccess: function(entityReference) {
imageSelector.updateSelectedImageReferences([XWiki.Model.serialize(entityReference)]);
imageSelectorElement.removeClass('loading');
new XWiki.widgets.Notification(translations.get('modal.fileUpload.success'), 'done');
},
onError: function() {
new XWiki.widgets.Notification(translations.get('modal.fileUpload.fail'), 'error');
imageSelectorElement.removeClass('loading');
},
onAbort: function() {
new XWiki.widgets.Notification(translations.get('modal.fileUpload.abort'), 'error');
imageSelectorElement.removeClass('loading');
if (!beforeUploadEvent.isDefaultPrevented()) {
element.addClass('loading');
imageSelector.createLoader(uploadedFile, {
onSuccess: function (entityReference) {
imageSelector.updateSelectedImageReferences([XWiki.Model.serialize(entityReference)],
element);
element.removeClass('loading');
new XWiki.widgets.Notification(translations.get('modal.fileUpload.success'), 'done');
},
onError: function () {
new XWiki.widgets.Notification(translations.get('modal.fileUpload.fail'), 'error');
element.removeClass('loading');
},
onAbort: function () {
new XWiki.widgets.Notification(translations.get('modal.fileUpload.abort'), 'error');
element.removeClass('loading');
}
}, element);
}
});
}
init($(document).find('.image-selector'));
$(document).on('xwiki:dom:updated', (event, data) =&gt; {
data.elements.forEach(element =&gt; init($(element)));
});
});
});
</code>
</property>
<property>
......
......@@ -120,12 +120,20 @@
</property>
<property>
<code>require(['jquery', 'imageSelector'], function($, imageSelector) {
$('#urlTab input[type="text"]').on('input', function () {
if ($(this).val() === '') {
imageSelector.updateSelectedImageReferences([]);
} else {
imageSelector.updateSelectedImageReferences([{type: 'url', reference: $(this).val()}]);
}
function init(element) {
element.find('.urlTab input[type="text"]').on('input', function () {
if ($(this).val() === '') {
imageSelector.updateSelectedImageReferences([], $(this));
} else {
imageSelector.updateSelectedImageReferences([{type: 'url', reference: $(this).val()}], $(this));
}
});
}
init($(document).find('.image-selector'));
$(document).on('xwiki:dom:updated', (event, data) =&gt; {
data.elements.forEach(element =&gt; init($(element)));
});
});</code>
</property>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment