diff --git a/bundle/Resources/public/admin/js/init.js b/bundle/Resources/public/admin/js/init.js
index 43d5d65..a4a588d 100644
--- a/bundle/Resources/public/admin/js/init.js
+++ b/bundle/Resources/public/admin/js/init.js
@@ -1,41 +1,54 @@
-(function($) {
- 'use strict';
- $('.multientry').multientry();
+const init = () => {
+ window.initaliseMultientries();
- const saveButton = document.getElementById('content_type_edit__sidebar_right__save-tab');
+ const observer = new MutationObserver(() => {
+ window.initaliseMultientries();
+ });
- const observer = new MutationObserver(e => {
- $('.multientry').multientry();
+ // enables observer when element is dropped
+ document.addEventListener('drop', () => {
+ document.querySelectorAll('.ibexa-collapse').forEach((element) => {
+ observer.observe(element, { childList: true, subtree: true });
});
+ });
- // enables observer when elements is dropped
- document.addEventListener("drop", e => {
- document.querySelectorAll('.ibexa-collapse').forEach(el => {
- observer.observe(el, { childList: true, subtree: true });
- });
+ // disable observer while dragging to reduce function firing
+ document.addEventListener('drag', () => {
+ document.querySelectorAll('.ibexa-collapse').forEach(() => {
+ observer.disconnect();
});
+ });
- // disable observer while dragging to reduce function firing
- document.addEventListener("drag", e => {
- document.querySelectorAll('.ibexa-collapse').forEach(el => {
- observer.disconnect();
- });
- });
+ // checks if any multientry inputs are empty and expands the field type
+ const saveButton = document.getElementById('content_type_edit__sidebar_right__save-tab');
+
+ saveButton && saveButton.addEventListener('click', (event) => {
+ document.querySelectorAll('.multientry input').forEach((input) => {
+ if (input.value.length > 0) {
+ return;
+ }
+
+ event.preventDefault();
+
+ const collapseWrapper = input.closest('.ibexa-collapse');
+ const collapseBody = collapseWrapper.querySelector('.ibexa-collapse__body');
+ const collapseToggle = collapseWrapper.querySelector('.ibexa-collapse__toggle-btn');
- // checks if any multientry inputs are empty and expands the field type
- saveButton && saveButton.addEventListener("click", e => {
- document.querySelectorAll('.multientry input').forEach(el => {
- if (el.value.length === 0) {
- e.preventDefault();
- const fullElement = el.closest('.ibexa-collapse');
- const elementBody = fullElement.querySelector('.ibexa-collapse__body');
- const collapseToggle = fullElement.querySelector('.ibexa-collapse__toggle-btn');
- fullElement.classList.contains('multientry-error') ? null : fullElement.classList.add('multientry-error');
- fullElement.classList.contains('ibexa-collapse--collapsed') ? fullElement.classList.remove('ibexa-collapse--collapsed') : null;
- elementBody.classList.contains('show') ? null : elementBody.classList.add('show');
- collapseToggle.classList.contains('collapsed') ? elementBody.classList.remove('collapsed') : null;
- fullElement.dataset.collapsed ? fullElement.dataset.collapsed = false : null;
- }
- })
+ collapseWrapper.classList.add('multientry-error');
+ collapseWrapper.classList.remove('ibexa-collapse--collapsed');
+ collapseWrapper.dataset.collapsed = false;
+
+ collapseBody.classList.add('show');
+
+ if (collapseToggle.classList.contains('collapsed')) {
+ collapseBody.classList.remove('collapsed');
+ }
});
-})(jQuery);
+ });
+};
+
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+} else {
+ init();
+}
diff --git a/bundle/Resources/public/admin/js/multientry.js b/bundle/Resources/public/admin/js/multientry.js
index e05ff1a..32c707f 100644
--- a/bundle/Resources/public/admin/js/multientry.js
+++ b/bundle/Resources/public/admin/js/multientry.js
@@ -1,129 +1,199 @@
-(function($) {
- 'use strict';
-
- //initialize
- var MultiEntry = function(element, options){
- this.opts = $.extend({
- insert_first: true,
- last_item_can_be_removed: false,
- limit: null,
- show_errors: true,
- }, options || {});
- this.id = 0;
- this.$el = $(element);
- $.extend(this.opts, this.$el.data() || {});
- this.$items_container = this.$el.find('.multientry-items');
- this.items_exist = this.$items_container.find('.multientry-item').length ? true : false;
- this.$add_button = this.$el.find('.multientry_add');
- this.item_template = this.$el.data('prototype');
- this.close_element = '';
- this.error_message = this.opts.error_message || ('Max number of items: '+ this.opts.limit);
- this.$error = $('
'+ this.error_message +'
');
- this.setup_dom();
- this.setup_events();
- setTimeout($.proxy(function(){
- this.opts.insert_first && !(this.items_exist) && this.add();
- }, this), 0);
+const SELECTORS = {
+ root: '.multientry',
+ items_container: '.multientry-items',
+ item: '.multientry-item',
+ add_button: '.multientry_add',
+ remove_button: '.icon-close',
+};
+
+const EVENTS = {
+ PREFIX: 'multientry:',
+ add: {
+ on: 'add',
+ before: 'before:add',
+ },
+ remove: {
+ on: 'remove',
+ before: 'before:remove',
+ },
+ limit: {
+ reached: 'limit:reached',
+ valid: 'limit:valid',
+ },
+};
+
+class MultiEntry {
+ constructor(element, options = {}) {
+ this.parser = new DOMParser();
+ this.id = 0;
+
+ this.$element = element;
+ this.$element.dataset.instanceId = options.instance_id;
+
+ const externalOptions = {
+ ...options,
+ ...this.$element.dataset,
};
-
- MultiEntry.prototype.next_id = function(){
- var timestamp = +new Date();
- return timestamp+''+(this.id++);
- };
-
- //instance methods
- MultiEntry.prototype.render_item_template = function() {
- var $template = $(this.item_template.replace(/__name__/g, this.next_id() ));
- $template.addClass('multientry-item new');
- $template.append(this.close_element);
- return $template;
- };
-
- MultiEntry.prototype.setup_dom = function() {
- this.$el.find('.multientry-item').append(this.close_element);
- };
-
- MultiEntry.prototype.setup_events = function() {
- var self = this;
-
- this.$add_button.on('click', function(e){
- e.preventDefault();
- self.add();
- });
-
- this.$items_container.on('click', '.icon-close', function(e){
- e.preventDefault();
- self.remove($(this).closest('.multientry-item'));
- });
- };
-
- MultiEntry.prototype.add = function() {
- if(this.opts.limit && this.limit_reached()) {return;}
- var $item = this.render_item_template();
- this.trigger('before:add', {item: $item});
- this.$items_container.append($item);
- this.trigger('add', {item: $item});
- this.opts.limit && this.limit_check();
- };
-
- MultiEntry.prototype.remove = function($item) {
- if(this.items_count() === 1 && !this.opts.last_item_can_be_removed){return;}
- this.trigger('before:remove', {item: $item});
- $item.remove();
- this.opts.limit && this.limit_check();
- this.trigger('remove', {item: $item});
- };
-
- MultiEntry.prototype.items_count = function() {
- return this.$items_container.find('.multientry-item').length;
- };
-
- MultiEntry.prototype.limit_check = function() {
- this.limit_reached() ? this.on_limit_reached() : this.on_limit_valid();
- };
-
- MultiEntry.prototype.limit_reached = function() {
- return this.items_count() >= this.opts.limit;
- };
-
- MultiEntry.prototype.on_limit_valid = function() {
- this.opts.show_errors && this.$error.remove();
- this.$add_button.removeClass('disabled');
- this.trigger('limit:valid');
+ this.options = {
+ insert_first: true,
+ last_item_can_be_removed: false,
+ limit: null,
+ show_errors: true,
+ error_message: `Max number of items: ${externalOptions.limit}`,
+ ...externalOptions,
};
- MultiEntry.prototype.on_limit_reached = function() {
- this.opts.show_errors && this.$items_container.append(this.$error);
- this.$add_button.addClass('disabled');
- this.trigger('limit:reached');
+ this.$add_button = this.$element.querySelector(SELECTORS.add_button);
+ this.$items_container = this.$element.querySelector(SELECTORS.items_container);
+ this.items_exist = !!this.$items_container.querySelector(SELECTORS.item);
+
+ this.item_template = this.$element.dataset.prototype;
+ this.remove_button_template = '';
+
+ this.$error = MultiEntry.create_element_from_string(
+ `${this.options.error_message}
`
+ );
+
+ this.setup_dom();
+ this.setup_events();
+ setTimeout(() => {
+ if (this.options.insert_first && !this.items_exist) {
+ this.add();
+ }
+ }, 0);
+ }
+
+ next_id() {
+ const timestamp = new Date();
+
+ return `${timestamp}${this.id++}`;
+ }
+
+ render_item_template() {
+ const $template = MultiEntry.create_element_from_string(
+ this.item_template.replace(/__name__/g, this.next_id())
+ );
+ $template.classList.add('multientry-item', 'new');
+ $template.append(MultiEntry.create_element_from_string(this.remove_button_template));
+
+ $template.querySelector(SELECTORS.remove_button).addEventListener('click', () => {
+ this.remove($template);
+ });
+
+ return $template;
+ }
+
+ setup_dom() {
+ this.$element.querySelectorAll(SELECTORS.item).forEach(($item) => {
+ $item.append(MultiEntry.create_element_from_string(this.remove_button_template));
+ });
+ }
+
+ setup_events() {
+ this.$add_button.addEventListener('click', (event) => {
+ event.preventDefault();
+ this.add();
+ });
+ }
+
+ add() {
+ if (this.options.limit && this.limit_reached()) {
+ return;
+ }
+
+ const $newItem = this.render_item_template();
+ this.trigger(EVENTS.add.before, { item: $newItem });
+ this.$items_container.append($newItem);
+ this.trigger(EVENTS.add.on, { item: $newItem });
+ this.options.limit && this.limit_check();
+ }
+
+ remove($item) {
+ if (this.items_count() === 1 && !this.options.last_item_can_be_removed) {
+ return;
+ }
+
+ this.trigger(EVENTS.remove.before, { item: $item });
+ $item.remove();
+ this.trigger(EVENTS.remove.on, { item: $item });
+ this.options.limit && this.limit_check();
+ }
+
+ limit_check() {
+ if (this.limit_reached()) {
+ this.on_limit_reached();
+ } else {
+ this.on_limit_valid();
+ }
+ }
+
+ limit_reached() {
+ return this.items_count() >= this.options.limit;
+ }
+
+ on_limit_reached() {
+ if (this.options.show_errors) {
+ this.$items_container.append(this.$error);
+ }
+
+ this.$add_button.classList.add('disabled');
+ this.trigger(EVENTS.limit.reached);
+ }
+
+ on_limit_valid() {
+ if (this.options.show_errors) {
+ this.$error.remove();
+ }
+
+ this.$add_button.classList.remove('disabled');
+ this.trigger(EVENTS.limit.valid);
+ }
+
+ trigger(suffix, data) {
+ const eventName = `${EVENTS.PREFIX}${suffix}`;
+ const detail = {
+ ...data,
+ instance: this,
};
-
- MultiEntry.prototype.trigger = function(event, data){
- var prefix = 'multientry:';
- data = $.extend({}, data, {instance: this});
- this.$el.trigger(prefix+event, data);
- $(document.body).trigger(prefix+event, data);
- };
-
- //Expose as jquery plugin
- $.fn.multientry = function(options){
- var method = typeof options === 'string' && options;
- $(this).each(function(){
- var $this = $(this);
- var instance = $this.data('multientry');
- if(instance){
- method && instance[method]();
- return;
- }
- instance = new MultiEntry(this, options);
- $this.data('multientry', instance);
- });
- return this;
- };
-
- //Expose class
- $.MultiEntry = MultiEntry;
-
-})(jQuery);
-
+ const event = new CustomEvent(eventName, { detail });
+
+ this.$element.dispatchEvent(event);
+ document.body.dispatchEvent(event);
+ }
+
+ items_count() {
+ return this.$items_container.querySelectorAll(SELECTORS.item).length;
+ }
+
+ static create_element_from_string(elementString) {
+ const template = document.createElement('template');
+ template.innerHTML = elementString.trim();
+
+ return template.content.firstChild;
+ }
+}
+
+const instances = [];
+window.initaliseMultientries = function (options = {}) {
+ document.querySelectorAll(SELECTORS.root).forEach(($multientry) => {
+ let instance = instances[$multientry.dataset.instanceId];
+ if (instance) {
+ return;
+ }
+
+ instance = new MultiEntry($multientry, {
+ ...options,
+ instance_id: instances.length,
+ });
+ instances.push(instance);
+ });
+
+ return [...instances];
+};
+
+window.runMethodOnAllMultientries = function (methodName) {
+ instances.forEach((instance) => {
+ instance[methodName] && instance[methodName]();
+ });
+};