diff --git a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/htmlheader.vm b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/htmlheader.vm index 1b559100b168..dca697e262fb 100644 --- a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/htmlheader.vm +++ b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/htmlheader.vm @@ -60,6 +60,7 @@ #if ("$!xcontext.userReference" != '') #addMetaAttribute($metaAttributes, 'user-reference', $!services.model.serialize($xcontext.userReference, 'default')) #end + #addMetaAttribute($metaAttributes, 'action', "$xcontext.action") diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/js/xwiki/editors/dataeditors.js b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/js/xwiki/editors/dataeditors.js index 9b3b0b38196a..560cda850572 100644 --- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/js/xwiki/editors/dataeditors.js +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/js/xwiki/editors/dataeditors.js @@ -17,825 +17,757 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -var XWiki = (function(XWiki) { -// Start XWiki augmentation. -var editors = XWiki.editors = XWiki.editors || {}; -editors.XDataEditors = Class.create({ +/*! +## Velocity code here. +#set ($l10nKeys = [ + 'core.editors.object.add.inProgress', + 'core.editors.object.add.done', + 'core.editors.object.add.failed', + 'core.editors.object.delete.confirmJS', + 'core.editors.object.removeDeprecatedProperties.inProgress', + 'core.editors.object.removeDeprecatedProperties.done', + 'core.editors.object.removeDeprecatedProperties.failed', + 'core.editors.class.addProperty.inProgress', + 'core.editors.class.addProperty.done', + 'core.editors.class.addProperty.failed', + 'core.editors.class.deleteProperty.confirm', + 'core.editors.class.deleteProperty.inProgress', + 'core.editors.class.deleteProperty.done', + 'core.editors.class.deleteProperty.failed', + 'core.editors.object.loadObject.inProgress', + 'core.editors.object.loadObject.done', + 'core.editors.object.loadObject.failed', + 'core.editors.class.moveProperty.handle.label', + 'core.editors.class.switchClass.confirm' +]) +#set ($l10n = {}) +#foreach ($key in $l10nKeys) + #set ($params = $key.subList(1, $key.size())) + #if ($params) + #set ($discard = $l10n.put($key[0], $services.localization.render($key[0], $params))) + #else + #set ($discard = $l10n.put($key, $services.localization.render($key))) + #end +#end +#set ($icons = {'reposition': $services.icon.renderHTML('reposition')}) +#[[*/ +// Start JavaScript-only code. +(function(l10n, icons) { + "use strict"; + require(['jquery','xwiki-meta','xwiki-events-bridge','scriptaculous/dragdrop'], function ($, xm) { + let XDataEditors = Class.create({ + // Maintain informations about actions performed on the editor. + // Data abouts xobjects are map whom keys are xclass names and values are list of sorted xobjects ids + editorStatus : { + savedXObjects: {}, // already saved objects + addedXObjects: {}, // objects added but not saved yet + deletedXObjects: {} // objects deleted but not removed yet + }, - // Maintain informations about actions performed on the editor. - // Data abouts xobjects are map whom keys are xclass names and values are list of sorted xobjects ids - editorStatus : { - savedXObjects: {}, // already saved objects - addedXObjects: {}, // objects added but not saved yet - deletedXObjects: {} // objects deleted but not removed yet - }, + initialize : function() { + let self = this; + this.editedDocument = XWiki.currentDocument; - initialize : function() { - this.editedDocument = XWiki.currentDocument; + $('.xclass').each(function() { + self.enhanceClassUX($(this), true); + }); + this.ajaxObjectAdd($('#add_xobject')); + this.ajaxPropertyAdd(); + this.makeSortable($('#xwikiclassproperties')); + this.ajaxRemoveDeprecatedProperties($("body"), ".syncAllProperties"); + + // everytime the document is saved we restore the current state to be in sync with the server + $(document).on('xwiki:document:saved', function () { + // Remove fields about deleted and added objects + $('input[name=deletedObjects]').each(function() { + $(this).remove(); + }); + $('input[name=addedObjects]').each(function() { + $(this).remove(); + }); - $$('.xclass').each(function(item) { - this.enhanceClassUX(item, true); - }.bind(this)); - this.ajaxObjectAdd($('add_xobject')); - this.ajaxPropertyAdd(); - this.makeSortable($('xwikiclassproperties')); - this.ajaxRemoveDeprecatedProperties($("body"), ".syncAllProperties"); + // Show hidden links about edit and delete + // Edit link is hidden on not-saved objects since it needs to be saved on the server. + // Delete links are only shown for latest added object of an xclass to keep a good numbering. + $('.xobject-action.edit').show(); + $('.xobject').each(function () { + $(this).find('a.delete').show(); + }); - // everytime the document is saved we restore the current state to be in sync with the server - document.observe('xwiki:document:saved', function (event) { - // Remove fields about deleted and added objects - $$('input[name=deletedObjects]').each(function(item) { - item.remove(); - }); - $$('input[name=addedObjects]').each(function(item) { - item.remove(); + // We need to put all info about added objects in the saved objects collection. + // We don't need to do that for deleted objects since we cannot revert a deletion in the UI except by canceling + // the changes. So we directly manipulate the savedXObjects collection when performing a deletion. + for (let xclassName in self.editorStatus.addedXObjects) { + if (self.editorStatus.savedXObjects[xclassName] === undefined) { + self.editorStatus.savedXObjects[xclassName] = self.editorStatus.addedXObjects[xclassName]; + } else { + self.editorStatus.savedXObjects[xclassName].concat(self.editorStatus.addedXObjects[xclassName]); + self.editorStatus.savedXObjects[xclassName].sort(self.numberSort); + } + } + self.editorStatus.addedXObjects = {}; + self.editorStatus.deletedXObjects = {}; }); - // Show hidden links about edit and delete - // Edit link is hidden on not-saved objects since it needs to be saved on the server. - // Delete links are only shown for latest added object of an xclass to keep a good numbering. - $$('.xobject-action.edit').invoke('show'); - $$('.xobject').each(function (item) { - item.down('a.delete').show(); + // in case of cancel we just clean everything so that we don't get any warnings for leaving the page without saving. + $(document).on('xwiki:actions:cancel', function () { + // Remove fields about deleted and added objects + $('input[name=deletedObjects]').each(function() { + $(this).remove(); + }); + $('input[name=addedObjects]').each(function() { + $(this).remove(); + }); + self.editorStatus.addedXObjects = {}; + self.editorStatus.deletedXObjects = {}; }); - // We need to put all info about added objects in the saved objects collection. - // We don't need to do that for deleted objects since we cannot revert a deletion in the UI except by canceling - // the changes. So we directly manipulate the savedXObjects collection when performing a deletion. - for (var xclassName in this.editorStatus.addedXObjects) { - if (this.editorStatus.savedXObjects[xclassName] === undefined) { - this.editorStatus.savedXObjects[xclassName] = this.editorStatus.addedXObjects[xclassName]; + // We want to the user to be prevented if he tries to leave the editor before saving. + $(document).on("beforeunload", function(event) { + if (Object.keys(this.editorStatus.addedXObjects).length > 0 + || Object.keys(this.editorStatus.deletedXObjects).length > 0) { + event.preventDefault(); + event.returnValue = ""; } else { - this.editorStatus.savedXObjects[xclassName].concat(this.editorStatus.addedXObjects[xclassName]); - this.editorStatus.savedXObjects[xclassName].sort(this.numberSort); + return; } - } - this.editorStatus.addedXObjects = {}; - this.editorStatus.deletedXObjects = {}; - }.bindAsEventListener(this)); - - // in case of cancel we just clean everything so that we don't get any warnings for leaving the page without saving. - document.observe('xwiki:actions:cancel', function (event) { - // Remove fields about deleted and added objects - $$('input[name=deletedObjects]').each(function(item) { - item.remove(); - }); - $$('input[name=addedObjects]').each(function(item) { - item.remove(); }); - this.editorStatus.addedXObjects = {}; - this.editorStatus.deletedXObjects = {}; - }.bindAsEventListener(this)); + }, + /** + * Sort function to allow sorting an array of integer. + * Using [3, 0, 1].sort(numberSort) produces [0, 1, 3]. + */ + numberSort : function (a, b) { + return a - b; + }, + /** + * Extract the xclass name from the XDOM id of the xclass. + */ + getXClassNameFromXClassId : function (xclassId) { + return xclassId.substring("xclass_".length); + }, + /** + * Extract the xclass name from the XDOM id of an xobject. + */ + getXClassNameFromXObjectId : function (xobjectId) { + return xobjectId.substring("xobject_".length, xobjectId.lastIndexOf('_')); + }, + /** + * Extract the xobject number from the XDOM id of an xobject. + */ + getXObjectNumberFromXObjectId : function (xobjectId) { + return xobjectId.substring(xobjectId.lastIndexOf('_') + 1); + }, + /** + * Returns true if an object exists with the given classname and number. + */ + xObjectAlreadyExist : function (xclassName, objectNumber) { + return (this.editorStatus.savedXObjects[xclassName] !== undefined + && this.editorStatus.savedXObjects[xclassName].indexOf(objectNumber) !== -1) + || (this.editorStatus.addedXObjects[xclassName] !== undefined + && this.editorStatus.addedXObjects[xclassName].indexOf(objectNumber) !== -1); + }, + /** + * Returns the xobject DOM element for the given class and number. + */ + getXObject : function (xclassName, objectNumber) { + let expectedId = 'xobject_' + xclassName + '_' + objectNumber; + // We cannot use # + expectedId because of the dots to escape. + return $("[id='"+expectedId+"']"); + }, - // We want to the user to be prevented if he tries to leave the editor before saving. - window.addEventListener("beforeunload", function(event) { - if (Object.keys(this.editorStatus.addedXObjects).length > 0 - || Object.keys(this.editorStatus.deletedXObjects).length > 0) { - event.preventDefault(); - event.returnValue = ""; - } else { - return; + getDeletedXObject : function (xclassName, objectNumber) { + let expectedId = 'deletedObject_' + xclassName + '_' + objectNumber; + let possibleObjects = $('input[name=deletedObjects]'); + for (let i = 0; i < possibleObjects.length; i++) { + if (possibleObjects[i].attr('id') === expectedId) { + return possibleObjects[i]; + } } - }.bind(this)); - }, - /** - * Sort function to allow sorting an array of integer. - * Using [3, 0, 1].sort(numberSort) produces [0, 1, 3]. - */ - numberSort : function (a, b) { - return a - b; - }, - /** - * Extract the xclass name from the XDOM id of the xclass. - */ - getXClassNameFromXClassId : function (xclassId) { - return xclassId.substring("xclass_".length); - }, - /** - * Extract the xclass name from the XDOM id of an xobject. - */ - getXClassNameFromXObjectId : function (xobjectId) { - return xobjectId.substring("xobject_".length, xobjectId.lastIndexOf('_')); - }, - /** - * Extract the xobject number from the XDOM id of an xobject. - */ - getXObjectNumberFromXObjectId : function (xobjectId) { - return xobjectId.substring(xobjectId.lastIndexOf('_') + 1); - }, - /** - * Returns true if an object exists with the given classname and number. - */ - xObjectAlreadyExist : function (xclassName, objectNumber) { - return (this.editorStatus.savedXObjects[xclassName] !== undefined - && this.editorStatus.savedXObjects[xclassName].indexOf(objectNumber) !== -1) - || (this.editorStatus.addedXObjects[xclassName] !== undefined - && this.editorStatus.addedXObjects[xclassName].indexOf(objectNumber) !== -1); - }, - /** - * Returns the xobject DOM element for the given class and number. - */ - getXObject : function (xclassName, objectNumber) { - var expectedId = 'xobject_' + xclassName + '_' + objectNumber; - var possibleObjects = $$('.xobject'); - for (var i = 0; i < possibleObjects.length; i++) { - if (possibleObjects[i].id === expectedId) { - return possibleObjects[i]; + }, + /** + * Helper to remove an element from an array if and only if it was already in the array. + */ + removeElementFromArray : function (array, element) { + if (array.indexOf(element) !== -1) { + array.splice(array.indexOf(element), 1); } - } - }, - - getDeletedXObject : function (xclassName, objectNumber) { - var expectedId = 'deletedObject_' + xclassName + '_' + objectNumber; - var possibleObjects = $$('input[name=deletedObjects]'); - for (var i = 0; i < possibleObjects.length; i++) { - if (possibleObjects[i].id === expectedId) { - return possibleObjects[i]; + }, + /** + * Compute the new object number for a class name given the information we already have about savedXObjects and + * addedXObjects. + */ + getNewObjectNumber : function (xclassName) { + let objectNumberVal; + // if we already added xobjects of this type, the last number has to be taken there + if (this.editorStatus.addedXObjects[xclassName] !== undefined) { + objectNumberVal = Number(this.editorStatus.addedXObjects[xclassName].last()) + 1; + // if we don't have added xobjects yet, but some are already saved, then we take the last number there + } else if (this.editorStatus.savedXObjects[xclassName] !== undefined) { + objectNumberVal = Number(this.editorStatus.savedXObjects[xclassName].last()) + 1; + } else { + objectNumberVal = 0; } - } - }, - /** - * Helper to remove an element from an array if and only if it was already in the array. - */ - removeElementFromArray : function (array, element) { - if (array.indexOf(element) !== -1) { - array.splice(array.indexOf(element), 1); - } - }, - /** - * Compute the new object number for a class name given the information we already have about savedXObjects and - * addedXObjects. - */ - getNewObjectNumber : function (xclassName) { - var objectNumberVal; - // if we already added xobjects of this type, the last number has to be taken there - if (this.editorStatus.addedXObjects[xclassName] !== undefined) { - objectNumberVal = Number(this.editorStatus.addedXObjects[xclassName].last()) + 1; - // if we don't have added xobjects yet, but some are already saved, then we take the last number there - } else if (this.editorStatus.savedXObjects[xclassName] !== undefined) { - objectNumberVal = Number(this.editorStatus.savedXObjects[xclassName].last()) + 1; - } else { - objectNumberVal = 0; - } - objectNumberVal = objectNumberVal + ""; + objectNumberVal = objectNumberVal + ""; - // if an object with this number was already deleted, we remove the info from deleted objects. - var deletedArray = this.editorStatus.deletedXObjects[xclassName]; - if (deletedArray !== undefined && deletedArray.indexOf(objectNumberVal) !== -1) { - this.getDeletedXObject(xclassName, objectNumberVal).remove(); - this.removeElementFromArray(deletedArray, objectNumberVal); - if (deletedArray.length === 0) { - delete this.editorStatus.deletedXObjects[xclassName]; + // if an object with this number was already deleted, we remove the info from deleted objects. + let deletedArray = this.editorStatus.deletedXObjects[xclassName]; + if (deletedArray !== undefined && deletedArray.indexOf(objectNumberVal) !== -1) { + this.getDeletedXObject(xclassName, objectNumberVal).remove(); + this.removeElementFromArray(deletedArray, objectNumberVal); + if (deletedArray.length === 0) { + delete this.editorStatus.deletedXObjects[xclassName]; + } } - } - return objectNumberVal; - }, - /** - * Enhance xclass for the JS behaviours and iterate over inner xobjects or xproperties to enhance them. - * - * @param xclass the xclass DOM element - * @param init true if it's the call performed during the script initialization. - */ - enhanceClassUX : function(xclass, init) { - this.ajaxObjectAdd(xclass); - this.expandCollapseClass(xclass); + return objectNumberVal; + }, + /** + * Enhance xclass for the JS behaviours and iterate over inner xobjects or xproperties to enhance them. + * + * @param xclass the xclass DOM element + * @param init true if it's the call performed during the script initialization. + */ + enhanceClassUX : function(xclass, init) { + this.ajaxObjectAdd(xclass); + this.expandCollapseClass(xclass); - xclass.select('.xproperty').each(function(item) { - this.expandCollapseMetaProperty(item); - this.ajaxPropertyDeletion(item); - this.makeDisableVisible(item); - }.bind(this)); + let self = this; + xclass.find('.xproperty').each(function() { + let item = $(this); + self.expandCollapseMetaProperty(item); + self.ajaxPropertyDeletion(item); + self.makeDisableVisible(item); + }); - // We always iterate on the xobjects of the xclass to enhance them. - xclass.select('.xobject').each(function(item) { - this.enhanceObjectUX(item, init); - }.bind(this)); - }, - enhanceObjectUX : function(object, init) { - var xclassName = this.getXClassNameFromXObjectId(object.id); - var objectNumber = this.getXObjectNumberFromXObjectId(object.id); - var listObjects; - // Initialize the xobjects collections. - if (init) { - if (this.editorStatus.savedXObjects[xclassName] === undefined) { - this.editorStatus.savedXObjects[xclassName] = []; - } - listObjects = this.editorStatus.savedXObjects[xclassName]; - } else { - if (this.editorStatus.addedXObjects[xclassName] === undefined) { - this.editorStatus.addedXObjects[xclassName] = []; + // We always iterate on the xobjects of the xclass to enhance them. + xclass.find('.xobject').each(function() { + self.enhanceObjectUX($(this), init); + }); + }, + enhanceObjectUX : function(object, init) { + let xclassName = this.getXClassNameFromXObjectId(object.attr('id')); + let objectNumber = this.getXObjectNumberFromXObjectId(object.attr('id')); + let listObjects; + + // Initialize the xobjects collections. + if (init) { + if (this.editorStatus.savedXObjects[xclassName] === undefined) { + this.editorStatus.savedXObjects[xclassName] = []; + } + listObjects = this.editorStatus.savedXObjects[xclassName]; + } else { + if (this.editorStatus.addedXObjects[xclassName] === undefined) { + this.editorStatus.addedXObjects[xclassName] = []; + } + listObjects = this.editorStatus.addedXObjects[xclassName]; } - listObjects = this.editorStatus.addedXObjects[xclassName]; - } - if (!this.xObjectAlreadyExist(xclassName, objectNumber)) { - // we push the object number and we always sort the array. - listObjects.push(objectNumber); - listObjects.sort(this.numberSort); + if (!this.xObjectAlreadyExist(xclassName, objectNumber)) { + // we push the object number and we always sort the array. + listObjects.push(objectNumber); + listObjects.sort(this.numberSort); - // We keep record of the added objects after init by adding a form input about them - // The idea is to be able to submit to the server all the object added, even if they don't have any property - // such as for AWM Content type. - // We put those input directly with the xobject DOM element since we want it to be removed if the object is - // removed later. - if (!init) { - var addedObject = new Element('input', { - 'type': 'hidden', - 'name': 'addedObjects', - 'id': 'addedObject_' + xclassName + '_' + objectNumber, - 'value': xclassName + '_' + objectNumber - }); - object.appendChild(addedObject); + // We keep record of the added objects after init by adding a form input about them + // The idea is to be able to submit to the server all the object added, even if they don't have any property + // such as for AWM Content type. + // We put those input directly with the xobject DOM element since we want it to be removed if the object is + // removed later. + if (!init) { + let addedObject = $('', { + 'type': 'hidden', + 'name': 'addedObjects', + 'id': 'addedObject_' + xclassName + '_' + objectNumber, + 'value': xclassName + '_' + objectNumber + }); + object.append(addedObject); + } + this.ajaxObjectDeletion(object); + this.editButtonBehavior(object); + this.expandCollapseObject(object); + this.ajaxRemoveDeprecatedProperties(object, ".syncProperties"); } - this.ajaxObjectDeletion(object); - this.editButtonBehavior(object); - this.expandCollapseObject(object); - this.ajaxRemoveDeprecatedProperties(object, ".syncProperties"); - } - }, - // ----------------------------------------------- - /* AJAX object add */ - ajaxObjectAdd : function(element) { - if (!element) { - return; - } - element.select('.xobject-add-control').each(function(item) { - item.observe('click', function(event) { - item.blur(); - event.stop(); - var url, validClassName, classNameVal, objectNumberVal; - if (item.href) { - url = item.href.replace(/[?&]xredirect=[^&]*/, ''); - validClassName = true; + }, + // ----------------------------------------------- + /* AJAX object add */ + ajaxObjectAdd : function(element) { + if (!element) { + return; + } + let self = this; + element.find('.xobject-add-control').each(function() { + let item = $(this); + item.on('click', function(event) { + item.blur(); + event.preventDefault(); + let url, validClassName, classNameVal, objectNumberVal; + if (item.attr('href')) { + url = item.attr('href').replace(/[?&]xredirect=[^&]*/, ''); + validClassName = true; - // We compute the object number based on the information we have in our recorded objects array. - classNameVal = this.getXClassNameFromXClassId(item.up('.xclass').id); - url += "&objectNumber=" + this.getNewObjectNumber(classNameVal); - } else { - var classNameElt = element.down('select'); - if (classNameElt && classNameElt.selectedIndex >= 0) { - classNameVal = classNameElt.options[classNameElt.selectedIndex].value; + // We compute the object number based on the information we have in our recorded objects array. + classNameVal = self.getXClassNameFromXClassId(item.parents('.xclass').attr('id')); + url += "&objectNumber=" + self.getNewObjectNumber(classNameVal); + } else { + let classNameElt = element.find('select'); + classNameVal = classNameElt.val(); + validClassName = classNameVal && classNameVal !== '-'; + url = self.editedDocument.getURL(xm.action, Object.toQueryString({ + xpage: 'editobject', + xaction: 'addObject', + classname: classNameVal, + objectNumber: self.getNewObjectNumber(classNameVal), + form_token: xm.form_token + })); } - validClassName = classNameVal && classNameVal != '-'; - url = this.editedDocument.getURL('edit', Object.toQueryString({ - xpage: 'editobject', - xaction: 'addObject', - classname: classNameVal, - objectNumber: this.getNewObjectNumber(classNameVal), - form_token: $$('input[name=form_token]')[0].value - })); - } - if (!item.disabled && validClassName) { - new Ajax.Request( - /* Ajax request URL */ - url, - /* Ajax request parameters */ - { - onCreate : function() { - item.disabled = true; - item.notification = new XWiki.widgets.Notification("$services.localization.render('core.editors.object.add.inProgress')", "inprogress"); - }, - onSuccess : function(response) { - var activator = item.up('.add_xobject'); - if(activator) { - var insertedElement; - // We don't use Prototype API here because we wan't to move the CSS/JavaScript includes to the page head. - var responseDocumentFragment = this._parseHTMLResponse(response.responseText); - - if (activator.up('.xclass')) { - // Using plain JavaScript here because Prototype doesn't know how to insert a document fragment.. - activator.up().insertBefore(responseDocumentFragment, activator); - insertedElement = activator.previous(); - } else { - activator.up().insertBefore(responseDocumentFragment, activator.nextSibling); - insertedElement = activator.next(); - } + let notification; + if (!item.prop('disabled') && validClassName) { + item.prop('disabled', true); + notification = new XWiki.widgets.Notification(l10n['core.editors.object.add.inProgress'], "inprogress"); + $.post(url).done(function(data) { + let activator = item.parents('.add_xobject'); + if (activator.length > 0) { + let insertedElement = $(data); + if (activator.parents('.xclass')) { + activator.before(insertedElement); + } else { + activator.next().append(insertedElement); + } - if (insertedElement) { - // Notify the listeners that the DOM has been updated. This is needed in order to have pickers. - document.fire('xwiki:dom:updated', {elements: [insertedElement]}); - var insertedObject; - if (insertedElement.hasClassName('xclass')) { - this.enhanceClassUX(insertedElement, false); - insertedObject = insertedElement.down('.xobject'); - } else if (insertedElement.hasClassName('xobject')) { - var classId = insertedElement.id.replace(/^xobject_/, "xclass_").replace(/_\d+$/, ""); + if (insertedElement) { + // Notify the listeners that the DOM has been updated. This is needed in order to have pickers. + $(document).trigger('xwiki:dom:updated', {elements: insertedElement.toArray()}); + let insertedObject; + if (insertedElement.hasClass('xclass')) { + self.enhanceClassUX(insertedElement, false); + insertedObject = insertedElement.find('.xobject'); + } else if (insertedElement.hasClass('xobject')) { + let classId = insertedElement.attr('id').replace(/^xobject_/, "xclass_").replace(/_\d+$/, ""); - // clean up the deletion array if we add back a deleted object. - var xclassName = this.getXClassNameFromXObjectId(insertedElement.id); - if (this.editorStatus.deletedXObjects[xclassName] !== undefined) { - var deletionArray = this.editorStatus.deletedXObjects[xclassName]; - // be sure to remove the requested object number from the array first. - if (deletionArray.indexOf(objectNumberVal) !== -1) { - this.removeElementFromArray(deletionArray, objectNumberVal); - this.getDeletedXObject(xclassName, objectNumberVal).remove(); - } - if (deletionArray.length === 0) { - delete this.editorStatus.deletedXObjects[xclassName]; - } + // clean up the deletion array if we add back a deleted object. + let xclassName = self.getXClassNameFromXObjectId(insertedElement.attr('id')); + if (self.editorStatus.deletedXObjects[xclassName] !== undefined) { + let deletionArray = self.editorStatus.deletedXObjects[xclassName]; + // be sure to remove the requested object number from the array first. + if (deletionArray.indexOf(objectNumberVal) !== -1) { + self.removeElementFromArray(deletionArray, objectNumberVal); + self.getDeletedXObject(xclassName, objectNumberVal).remove(); } - this.enhanceObjectUX(insertedElement, false); - var xclass = $(classId); - if(xclass) { - xclass.down('.add_xobject').insert({before: insertedElement}); - this.updateXObjectCount(xclass); + if (deletionArray.length === 0) { + delete self.editorStatus.deletedXObjects[xclassName]; } - insertedObject = insertedElement; } - // Expand the newly inserted object, since the user will probably want to edit it once it was added - insertedObject.removeClassName('collapsed'); - insertedObject.up('.xclass').removeClassName('collapsed'); - // We don't display the edit link on newly added object until they have been saved. - insertedObject.down('.xobject-action.edit').hide(); + self.enhanceObjectUX(insertedElement, false); + let xclass = $(classId); + if (xclass) { + xclass.find('.add_xobject').prev().before(insertedElement); + self.updateXObjectCount(xclass); + } + insertedObject = insertedElement; } + // Expand the newly inserted object, since the user will probably want to edit it once it was added + insertedObject.removeClass('collapsed'); + insertedObject.parents('.xclass').removeClass('collapsed'); + // // We don't display the edit link on newly added object until they have been saved. + insertedObject.find('.xobject-action.edit').hide(); } - item.notification.replace(new XWiki.widgets.Notification("$services.localization.render('core.editors.object.add.done')", "done")); - }.bind(this), - onFailure : function(response) { - var failureReason = response.statusText || 'Server not responding'; - item.notification.replace(new XWiki.widgets.Notification("$services.localization.render('core.editors.object.add.failed')" + failureReason, "error")); - }, - onComplete : function() { - item.disabled = false; - require(['xwiki-meta'], function (xm) { - xm.refreshVersion(); - }); - document.fire('xwiki:dom:refresh'); - }, - // 0 is returned for network failures. - on0 : function(response) { - response.request.options.onFailure(response); } - } - ); - } - }.bindAsEventListener(this)); - }.bind(this)); - }, - /** - * Parses the given HTML, moving the CSS/JavaScript includes in the page head. - * - * @param html the HTML to parse - * @return the document fragment corresponding to the given HTML - */ - _parseHTMLResponse : function(html) { - // We don't use Element#update() because it doesn't move external scripts and sheets into HEAD and also because we - // don't want to support in-line scripts in property displayers. - var container = new Element('div'); - container.innerHTML = html; - var head = document.body.previous('head'); - head && container.select('link').each(function(link) { - head.insert(link); - }); - head && container.select('script').each(function(script) { - if (script.src) { - // The script is not fetched if we simply move the script element. - head.insert(new Element('script', {type: script.type, src: script.readAttribute('src')})); - } - script.remove(); - }); - return this._extractContents(container); - }, - /** - * Extracts the children of the given element into a document fragment. - * - * @param element a DOM element - * @return a document fragment containing all the children of the given element, including text nodes - */ - _extractContents : function(element) { - var documentFragment = element.ownerDocument.createDocumentFragment(); - for(; element.firstChild; documentFragment.appendChild(element.firstChild)) {}; - return documentFragment; - }, - // ------------------------------------ - // Ajax object deletion - ajaxObjectDeletion : function(object) { - var item = object.down('a.delete'); - var xclassName = this.getXClassNameFromXObjectId(object.id); - var addedObjects = this.editorStatus.addedXObjects[xclassName]; + notification.replace(new XWiki.widgets.Notification(l10n['core.editors.object.add.done'], "done")); + xm.refreshVersion(); + $(document).trigger('xwiki:dom:refresh'); + }).fail(function (error) { + let failureReason = error || 'Server not responding'; + notification.replace(new XWiki.widgets.Notification(l10n['core.editors.object.add.failed'] + failureReason, "error")); + }).always(function () { + item.prop('disabled',false); + }); + } + }); + }); + }, + // ------------------------------------ + // Ajax object deletion + ajaxObjectDeletion : function(object) { + let item = object.find('a.delete'); + let xclassName = this.getXClassNameFromXObjectId(object.attr('id')); + let addedObjects = this.editorStatus.addedXObjects[xclassName]; - // if we have more than one object of the same type added, we always want the latest want to be deleted - // so we hide the delete link for previous one. - if (addedObjects !== undefined && addedObjects.length > 1) { - var previousObjectNumber = addedObjects[addedObjects.length - 2]; - this.getXObject(xclassName, previousObjectNumber).down('a.delete').hide(); - } + // if we have more than one object of the same type added, we always want the latest want to be deleted + // so we hide the delete link for previous one. + if (addedObjects !== undefined && addedObjects.length > 1) { + let previousObjectNumber = addedObjects[addedObjects.length - 2]; + this.getXObject(xclassName, previousObjectNumber).find('a.delete').hide(); + } - item.observe('click', function(event) { - item.blur(); - event.stop(); - new XWiki.widgets.ConfirmationBox({ - onYes: function () { - if (!item.disabled) { - var form = item.up('form'); - var xobjectElement = item.up('.xobject'); - var xclassName = this.getXClassNameFromXObjectId(xobjectElement.id); - var xObjectNumber = this.getXObjectNumberFromXObjectId(xobjectElement.id); - var addedObjects = this.editorStatus.addedXObjects[xclassName]; + let self = this; + item.on('click', function(event) { + item.blur(); + event.preventDefault(); + new XWiki.widgets.ConfirmationBox({ + onYes: function () { + if (!item.disabled) { + let form = item.parents('form'); + let xobjectElement = item.parents('.xobject'); + let xclassName = self.getXClassNameFromXObjectId(xobjectElement.attr('id')); + let xObjectNumber = self.getXObjectNumberFromXObjectId(xobjectElement.attr('id')); + let addedObjects = self.editorStatus.addedXObjects[xclassName]; - // if the object was already saved, then we need to add the right form information to delete it on server - if (addedObjects === undefined || addedObjects.indexOf(xObjectNumber) === -1) { - if (this.editorStatus.deletedXObjects[xclassName] === undefined) { - this.editorStatus.deletedXObjects[xclassName] = []; - } - this.editorStatus.deletedXObjects[xclassName].push(xObjectNumber); - this.editorStatus.deletedXObjects[xclassName].sort(this.numberSort); - var deletedObject = new Element('input', { - 'type': 'hidden', - 'name': 'deletedObjects', - 'id': 'deletedObject_' + xclassName + '_' + xObjectNumber, - 'value': xclassName + '_' + xObjectNumber - }); - form.appendChild(deletedObject); - this.removeElementFromArray(this.editorStatus.savedXObjects[xclassName], xObjectNumber); - if (this.editorStatus.savedXObjects[xclassName].length === 0) { - delete this.editorStatus.savedXObjects[xclassName]; - } - // if the object wasn't already saved, then we need to enable back the delete link on the previous added element - } else { - this.removeElementFromArray(addedObjects, xObjectNumber); - if (addedObjects.length === 0) { - delete this.editorStatus.addedXObjects[xclassName]; + // if the object was already saved, then we need to add the right form information to delete it on server + if (addedObjects === undefined || addedObjects.indexOf(xObjectNumber) === -1) { + if (self.editorStatus.deletedXObjects[xclassName] === undefined) { + self.editorStatus.deletedXObjects[xclassName] = []; + } + self.editorStatus.deletedXObjects[xclassName].push(xObjectNumber); + self.editorStatus.deletedXObjects[xclassName].sort(self.numberSort); + let deletedObject = $('', { + 'type': 'hidden', + 'name': 'deletedObjects', + 'id': 'deletedObject_' + xclassName + '_' + xObjectNumber, + 'value': xclassName + '_' + xObjectNumber + }); + form.append(deletedObject); + self.removeElementFromArray(self.editorStatus.savedXObjects[xclassName], xObjectNumber); + if (self.editorStatus.savedXObjects[xclassName].length === 0) { + delete self.editorStatus.savedXObjects[xclassName]; + } + // if the object wasn't already saved, then we need to enable back the delete link on the previous added element } else { - this.getXObject(xclassName, addedObjects.last()).down('a.delete').show(); + self.removeElementFromArray(addedObjects, xObjectNumber); + if (addedObjects.length === 0) { + delete self.editorStatus.addedXObjects[xclassName]; + } else { + self.getXObject(xclassName, addedObjects.last()).find('a.delete').show(); + } } - } - var xclassElement = xobjectElement.up('.xclass'); - xobjectElement.remove(); + let xclassElement = xobjectElement.parents('.xclass'); + xobjectElement.remove(); - this.updateXObjectCount(xclassElement); + self.updateXObjectCount(xclassElement); + } + }, + }, { + confirmationText: l10n['core.editors.object.delete.confirmJS'], + // Allow the users to cancel the switch. + showCancelButton: true + }); + }); + }, + // ----------------------------------------------- + /* AJAX removal of deprecated properties */ + ajaxRemoveDeprecatedProperties : function(container, triggerSelector) { + // Should never happen, but helpful for tests. + if (!container) { + return; + } + container.find(triggerSelector).each(function() { + let item = $(this); + item.on("click", function(event) { + item.blur(); + event.stopPropagation(); + if (!item.disabled) { + item.prop('disabled', true); + let notification = new XWiki.widgets.Notification(l10n['core.editors.object.removeDeprecatedProperties.inProgress'], "inprogress"); + $.post(item.href).done(function(data) { + // Remove deprecated properties box + container.find(".deprecatedProperties").remove(); + notification.replace(new XWiki.widgets.Notification(l10n['core.editors.object.removeDeprecatedProperties.done'], "done")); + }).fail(function (error) { + let failureReason = response.statusText || 'Server not responding'; + notification.replace(new XWiki.widgets.Notification(l10n['core.editors.object.removeDeprecatedProperties.failed'] + failureReason, "error")); + }).always(function () { + item.prop('disabled', false); + }); } - }.bind(this), - }, { - confirmationText: "$services.localization.render('core.editors.object.delete.confirmJS')", - // Allow the users to cancel the switch. - showCancelButton: true + }); }); - }.bindAsEventListener(this)); - }, - // ----------------------------------------------- - /* AJAX removal of deprecated properties */ - ajaxRemoveDeprecatedProperties : function(container, triggerSelector) { - // Should never happen, but helpful for tests. - if (!container) { - return; - } - container.select(triggerSelector).each(function(item) { - item.observe("click", function(event) { + }, + // ----------------------------------------------- + /* AJAX property add */ + ajaxPropertyAdd : function() { + let self = this; + $('input[name=action_propadd]').each(function(){ + let item = $(this); + item.on('click', function(event) { + let propname = $('#propname').val(); + let proptype = $('#proptype').val(); + item.blur(); + event.stopPropagation(); + if (!item.prop('disabled') && propname !== '' && proptype !== '') { + let editURL = self.editedDocument.getURL(xm.action, Object.toQueryString({ + xpage: 'editclass', + xaction: 'displayProperty', + propName: propname + })); + let ref = self.editedDocument.getURL('propadd', Object.toQueryString({ + propname: propname, + proptype: proptype, + xredirect: editURL, + form_token: xm.form_token + })); + item.prop('disabled', true); + let notification = new XWiki.widgets.Notification(l10n['core.editors.class.addProperty.inProgress'], "inprogress"); + $.post(ref).done(function(data) { + $('#xclassContent').append(data); + let insertedPropertyElt = $('#xclassContent :last-child'); + // Expand the newly inserted property, since the user will probably want to edit it once it was added + self.expandCollapseMetaProperty(insertedPropertyElt); + // Make teh newly added property sortable + self.makeSortable(insertedPropertyElt); + self.ajaxPropertyDeletion(insertedPropertyElt); + self.makeDisableVisible(insertedPropertyElt); + notification.replace(new XWiki.widgets.Notification(l10n['core.editors.class.addProperty.done'], "done")); + }).fail(function (error) { + let failureReason = error || 'Server not responding'; + notification.replace(new XWiki.widgets.Notification(l10n['core.editors.class.addProperty.failed'] + failureReason, "error")); + }).always(function () { + item.prop('disabled', false); + }); + } + }); + }); + }, + // ------------------------------------ + // Ajax property deletion + ajaxPropertyDeletion : function(property) { + let item = property.find('a.delete'); + item.on('click', function(event) { item.blur(); - event.stop(); + event.stopPropagation(); if (!item.disabled) { - new Ajax.Request( + new XWiki.widgets.ConfirmedAjaxRequest( /* Ajax request URL */ item.href, /* Ajax request parameters */ { onCreate : function() { - item.disabled = true; - item.notification = new XWiki.widgets.Notification("$services.localization.render('core.editors.object.removeDeprecatedProperties.inProgress')", "inprogress"); - }, - onSuccess : function(response) { - // Remove deprecated properties box - container.select(".deprecatedProperties").invoke("remove"); - item.notification.replace(new XWiki.widgets.Notification("$services.localization.render('core.editors.object.removeDeprecatedProperties.done')", "done")); + item.prop('disabled', true); }, - onFailure : function(response) { - var failureReason = response.statusText || 'Server not responding'; - item.notification.replace(new XWiki.widgets.Notification("$services.localization.render('core.editors.object.removeDeprecatedProperties.failed')" + failureReason, "error")); + onSuccess : function() { + property.remove(); }, onComplete : function() { - item.disabled = false; - }, - // 0 is returned for network failures. - on0 : function(response) { - response.request.options.onFailure(response); + item.prop('disabled', false); } + }, + /* Interaction parameters */ + { + confirmationText: l10n['core.editors.class.deleteProperty.confirm'], + progressMessageText : l10n['core.editors.class.deleteProperty.inProgress'], + successMessageText : l10n['core.editors.class.deleteProperty.done'], + failureMessageText : l10n['core.editors.class.deleteProperty.failed'] } ); } }); - }); - }, - // ----------------------------------------------- - /* AJAX property add */ - ajaxPropertyAdd : function() { - $$('input[name=action_propadd]').each(function(item){ - item._x_propnameElt = $('propname'); - item._x_proptypeElt = $('proptype'); - item._x_form_tokenElt = $('form_token'); - item.observe('click', function(event) { + }, + // ------------------------------------ + // + makeDisableVisible : function(property) { + property.find('.disabletool input').on("click", function() { + property.toggleClass('disabled'); + }) + }, + // ------------------------------------ + // Edit button behavior + // Prevent from collapsing the object subtree when clicking on edit + editButtonBehavior : function(object) { + let item = object.find('a.edit'); + if (!item) { + return; + } + item.on('click', function(event) { item.blur(); - event.stop(); - if (!item.disabled && item._x_propnameElt.value != '' && item._x_proptypeElt.selectedIndex >= 0) { - var editURL = this.editedDocument.getURL('edit', Object.toQueryString({ - xpage: 'editclass', - xaction: 'displayProperty', - propName: item._x_propnameElt.value - })); - var ref = this.editedDocument.getURL('propadd', Object.toQueryString({ - propname: item._x_propnameElt.value, - proptype: item._x_proptypeElt.options[item._x_proptypeElt.selectedIndex].value, - xredirect: editURL, - form_token: item._x_form_tokenElt.value + event.stopPropagation(); + window.location = item.href; + }); + }, + // Update the number of objects displayed in the class group title, when objects are added or deleted + updateXObjectCount: function(xclass) { + let xobjectCount = xclass.find('.xobject').length; + if (xobjectCount == 0) { + xclass.remove(); + } else { + let xobjectCountElement = xclass.find('.xclass_xobject_nb'); + if (typeof(xobjectCountElement) != 'undefined') { + xobjectCountElement.text('(' + xobjectCount + ')'); + } + } + }, + // ------------------------------------ + // Expand/collapse objects and object properties + expandCollapseObject : function(object) { + let self = this; + object.addClass('collapsable'); + let objectContent = object.find('.xobject-content'); + + if (objectContent.children().length === 0) { + object.addClass('collapsed'); + } + let objectTitle = object.find('.xobject-title'); + let xclassName = this.getXClassNameFromXObjectId(object.attr('id')); + let xObjectNumber = this.getXObjectNumberFromXObjectId(object.attr('id')); + objectTitle.on('click', function(event) { + let isAlreadyLoaded = objectContent.children().length > 0; + if (!isAlreadyLoaded && !object.hasClass('loading')) { + object.addClass('loading'); + let editURL = self.editedDocument.getURL(xm.action, Object.toQueryString({ + xpage: 'editobject', + xaction: 'loadObject', + classname: xclassName, + objectNumber: xObjectNumber, + form_token: xm.form_token })); - new Ajax.Request( - /* Ajax request URL */ - ref, - /* Ajax request parameters */ - { - onCreate : function() { - item.disabled = true; - item.notification = new XWiki.widgets.Notification("$services.localization.render('core.editors.class.addProperty.inProgress')", "inprogress"); - }, - onSuccess : function(response) { - $('xclassContent').insert({bottom : response.responseText}); - var insertedPropertyElt = $('xclassContent').lastChild; - // Expand the newly inserted property, since the user will probably want to edit it once it was added - this.expandCollapseMetaProperty(insertedPropertyElt); - // Make teh newly added property sortable - this.makeSortable(insertedPropertyElt); - this.ajaxPropertyDeletion(insertedPropertyElt); - this.makeDisableVisible(insertedPropertyElt); - item.notification.replace(new XWiki.widgets.Notification("$services.localization.render('core.editors.class.addProperty.done')", "done")); - }.bind(this), - onFailure : function(response) { - var failureReason = response.responseText; - item.notification.replace(new XWiki.widgets.Notification("$services.localization.render('core.editors.class.addProperty.failed') " + failureReason, "error")); - }, - onComplete : function() { - item.disabled = false; - }, - // 0 is returned for network failures. - on0 : function(response) { - response.request.options.onFailure(response); - } - } - ); + let notification = new XWiki.widgets.Notification(l10n['core.editors.object.loadObject.inProgress'], "inprogress"); + $.post(editURL).done(function(data) { + objectContent.append(data); + // display the elements before firing the event to be sure they are visible. + object.toggleClass('collapsed'); + $(document).trigger('xwiki:dom:updated', {elements: objectContent.toArray()}); + notification.replace(new XWiki.widgets.Notification(l10n['core.editors.object.loadObject.done'], "done")); + }).fail(function (error) { + let failureReason = response.statusText || 'Server not responding'; + notification.replace(new XWiki.widgets.Notification(l10n['core.editors.object.loadObject.failed'] + failureReason, "error")); + }).always(function () { + object.removeClass('loading'); + }); + } else { + object.toggleClass('collapsed'); } - }.bindAsEventListener(this)); - }.bind(this)); - }, - // ------------------------------------ - // Ajax property deletion - ajaxPropertyDeletion : function(property) { - var item = property.down('a.delete'); - item.observe('click', function(event) { - item.blur(); - event.stop(); - if (!item.disabled) { - new XWiki.widgets.ConfirmedAjaxRequest( - /* Ajax request URL */ - item.href, - /* Ajax request parameters */ - { - onCreate : function() { - item.disabled = true; - }, - onSuccess : function() { - property.remove(); - }, - onComplete : function() { - item.disabled = false; - } - }, - /* Interaction parameters */ - { - confirmationText: "$services.localization.render('core.editors.class.deleteProperty.confirm')", - progressMessageText : "$services.localization.render('core.editors.class.deleteProperty.inProgress')", - successMessageText : "$services.localization.render('core.editors.class.deleteProperty.done')", - failureMessageText : "$services.localization.render('core.editors.class.deleteProperty.failed')" - } - ); + }); + }, + // ------------------------------------ + // Expand/collapse classes + expandCollapseClass : function(xclass) { + // Classes are expanded by default + let xclassTitle = xclass.find('.xclass-title'); + if (!xclassTitle) { + // No objects... + return; } - }); - }, - // ------------------------------------ - // - makeDisableVisible : function(property) { - property.down('.disabletool input').observe("click", function(event) { - property.toggleClassName('disabled'); - }) - }, - // ------------------------------------ - // Edit button behavior - // Prevent from collapsing the object subtree when clicking on edit - editButtonBehavior : function(object) { - var item = object.down('a.edit'); - if (!item) { - return; - } - item.observe('click', function(event) { - item.blur(); - event.stop(); - window.location = item.href; - }.bindAsEventListener()); - }, - // Update the number of objects displayed in the class group title, when objects are added or deleted - updateXObjectCount: function(xclass) { - var xobjectCount = xclass.select('.xobject').length; - if (xobjectCount == 0) { - xclass.remove(); - } else { - var xobjectCountElement = xclass.down('.xclass_xobject_nb'); - if (typeof(xobjectCountElement) != 'undefined') { - xobjectCountElement.update('(' + xobjectCount + ')'); + xclass.addClass('collapsable'); + xclassTitle.on('click', function() { + xclassTitle.parent().toggleClass('collapsed'); + }); + }, + // ------------------------------------ + // Class editor: expand-collapse meta properties + expandCollapseMetaProperty : function(property) { + let propertyTitle = property.find('.xproperty-title'); + if (!propertyTitle) { + // No such object... + return; } - } - }, - // ------------------------------------ - // Expand/collapse objects and object properties - expandCollapseObject : function(object) { - object.addClassName('collapsable'); - var objectContent = object.down('.xobject-content'); - - if (objectContent.childElementCount === 0) { - object.addClassName('collapsed'); - } - var objectTitle = object.down('.xobject-title'); - var xclassName = this.getXClassNameFromXObjectId(object.id); - var xObjectNumber = this.getXObjectNumberFromXObjectId(object.id); - objectTitle.observe('click', function(event) { - var isAlreadyLoaded = objectContent.childElementCount > 0; - if (!isAlreadyLoaded && !object.hasClassName('loading')) { - object.addClassName('loading'); - var editURL = this.editedDocument.getURL('edit', Object.toQueryString({ - xpage: 'editobject', - xaction: 'loadObject', - classname: xclassName, - objectNumber: xObjectNumber, - form_token: $$('input[name=form_token]')[0].value - })); - new Ajax.Request( - /* Ajax request URL */ - editURL, - /* Ajax request parameters */ - { - onCreate : function() { - object.notification = new XWiki.widgets.Notification("$services.localization.render('core.editors.object.loadObject.inProgress')", "inprogress"); - }, - onSuccess : function(response) { - // We don't use Prototype API here because we wan't to move the CSS/JavaScript includes to the page head. - var responseDocumentFragment = this._parseHTMLResponse(response.responseText); - // Using plain JavaScript here because Prototype doesn't know how to insert a document fragment. - objectContent.insertBefore(responseDocumentFragment, null); - - // display the elements before firing the event to be sure they are visible. - object.toggleClassName('collapsed'); - document.fire('xwiki:dom:updated', {elements: [objectContent]}); - object.removeClassName('loading'); - object.notification.replace(new XWiki.widgets.Notification("$services.localization.render('core.editors.object.loadObject.done')", "done")); - }.bind(this), - onFailure : function(response) { - var failureReason = response.statusText || 'Server not responding'; - object.removeClassName('loading'); - object.notification.replace(new XWiki.widgets.Notification("$services.localization.render('core.editors.object.loadObject.failed') " + failureReason, "error")); - }, - // 0 is returned for network failures. - on0 : function(response) { - response.request.options.onFailure(response); + property.addClass('collapsable'); + property.addClass('collapsed'); + propertyTitle.on('click', function() { + propertyTitle.parent().toggleClass('collapsed'); + }); + }, + //--------------------------------------------------- + /* Class editor: xproperty ordering */ + makeSortable : function(element) { + if (element.length > 0) { + // Hide the property number, as ordering can be done by drag and drop + element.find('.xproperty-content').each(function () { + let item = $(this); + item.find("input").each(function () { + let input = $(this); + if (input.attr('id') && input.attr('id').endsWith("_number")) { + item.data('numberProperty', input); + input.parent().hide(); + if (input.parent().prev('dt')) { + input.parent().prev('dt').hide(); + } } - } - ); - } else { - object.toggleClassName('collapsed'); + }); + }); + // Create and insert move button + element.find('.xproperty-title .tools').each(function () { + let item = $(this); + let movebutton = $('', { + 'class': 'tool move', + title: l10n['core.editors.class.moveProperty.handle.label'] + }).html(icons['reposition']); + item.css('position', 'relative'); + item.append(movebutton); + movebutton.on('click', function (event) { + event.stopPropagation(); + }); + }); + let self = this; + Sortable.create('xclassContent', { + tag: 'div', + only: 'xproperty', + handle: 'move', + starteffect: self.startDrag, + endeffect: self.endDrag, + onUpdate: self.updateOrder + }); } - }.bindAsEventListener(this)); - }, - // ------------------------------------ - // Expand/collapse classes - expandCollapseClass : function(xclass) { - // Classes are expanded by default - var xclassTitle = xclass.down('.xclass-title'); - if (!xclassTitle) { - // No objects... - return; - } - xclass.addClassName('collapsable'); - xclassTitle.observe('click', function(event) { - xclassTitle.up().toggleClassName('collapsed'); - }.bindAsEventListener()); - }, - // ------------------------------------ - // Class editor: expand-collapse meta properties - expandCollapseMetaProperty : function(property) { - var propertyTitle = property.down('.xproperty-title'); - if (!propertyTitle) { - // No such object... - return; - } - property.addClassName('collapsable'); - property.addClassName('collapsed'); - propertyTitle.observe('click', function(event) { - propertyTitle.up().toggleClassName('collapsed'); - }.bindAsEventListener()); - }, - //--------------------------------------------------- - /* Class editor: xproperty ordering */ - makeSortable : function(element) { - if (!element) { - return; - } - // Hide the property number, as ordering can be done by drag and drop - element.select('.xproperty-content').each(function(item) { - item.select("input").each(function(input) { - if (input.id.endsWith("_number")) { - item.numberProperty = input; - input.up().hide(); - if (input.up().previous('dt')) { - input.up().previous('dt').hide(); - } + }, + updateOrder : function(container) { + let i = 0; + $(container).children().each(function () { + $(this).find(".xproperty-content").data('numberProperty', i++); + }); + }, + startDrag : function(dragged) { + $(dragged).addClass('dragged'); + $('#xclassContent').children().each(function() { + let item = $(this); + item.data('_expandedBeforeDrag', !item.hasClass('collapsed')); + item.addClass('collapsed'); + }); + }, + endDrag : function(dragged) { + $(dragged).removeClass('dragged'); + $('#xclassContent').children().each(function() { + let item = $(this); + if (item.data('_expandedBeforeDrag')) { + item.removeClass('collapsed'); } }); - }); - // Create and insert move button - element.select('.xproperty-title .tools').each(function(item) { - var movebutton = new Element('span', { - 'class': 'tool move', - title: $jsontool.serialize($services.localization.render('core.editors.class.moveProperty.handle.label')) - }).update($jsontool.serialize($services.icon.renderHTML('reposition'))); - item.makePositioned(); - item.appendChild(movebutton); - movebutton.observe('click', function(event) { - event.stop(); - }.bindAsEventListener()); - }); - // Attach behavior to the move buttons - Sortable.create($('xclassContent'), { - tag : 'div', - only : 'xproperty', - handle : 'move', - starteffect : this.startDrag.bind(this), - endeffect : this.endDrag.bind(this), - onUpdate : this.updateOrder.bind(this) - }); - }, - updateOrder : function(container) { - var children = container.childElements(); - for (var i = 0; i < children.length; ++i) { - var child = children[i].down(".xproperty-content"); - child.numberProperty.value = i+1; } - }, - startDrag : function(dragged) { - dragged.addClassName('dragged'); - $('xclassContent').childElements().each(function(item) { - item._expandedBeforeDrag = !item.hasClassName('collapsed'); - item.addClassName('collapsed'); - }); - }, - endDrag : function(dragged) { - dragged.removeClassName('dragged'); - $('xclassContent').childElements().each(function(item) { - if (item._expandedBeforeDrag) { - item.removeClassName('collapsed'); - } - }); - } -}); - -function init() { - require(['scriptaculous/dragdrop'], function() { - new editors.XDataEditors() }); - return true; -} - -// When the document is loaded, create the Autosave control -(XWiki.domIsLoaded && init()) -|| document.observe('xwiki:dom:loaded', init); - -// End XWiki augmentation. -return XWiki; -}(XWiki || {})); -// Class Switcher -require(['jquery', 'xwiki-events-bridge'], function($) { - $('#switch-xclass').on('change', function(event) { - var selectedClass = $(event.target).val(); - if (selectedClass) { - var selectedClassReference = XWiki.Model.resolve(selectedClass, XWiki.EntityType.DOCUMENT, - XWiki.currentDocument.documentReference); - var selectedClassURL = new XWiki.Document(selectedClassReference).getURL('edit', 'editor=class'); - var switchClass = function() { - window.self.location = selectedClassURL; - }; - new XWiki.widgets.ConfirmationBox({ - onYes: function() { - // Save the current class before switching. - $(document).trigger('xwiki:actions:save', { - 'continue': true, - 'form': $('#propupdate')[0] + let initSwitchClassListener = function () { + $('#switch-xclass').on('change', function (event) { + let selectedClass = $(event.target).val(); + if (selectedClass) { + let selectedClassReference = XWiki.Model.resolve(selectedClass, XWiki.EntityType.DOCUMENT, + XWiki.currentDocument.documentReference); + let selectedClassURL = new XWiki.Document(selectedClassReference).getURL(xm.action, 'editor=class'); + let switchClass = function () { + window.self.location = selectedClassURL; + }; + new XWiki.widgets.ConfirmationBox({ + onYes: function () { + // Save the current class before switching. + $(document).trigger('xwiki:actions:save', { + 'continue': true, + 'form': $('#propupdate')[0] + }); + $(document).on('xwiki:document:saved', switchClass); + }, + // Switch without saving the current class. + onNo: switchClass + }, { + confirmationText: l10n['core.editors.class.switchClass.confirm'], + // Allow the users to cancel the switch. + showCancelButton: true }); - $(document).on('xwiki:document:saved', switchClass); - }, - // Switch without saving the current class. - onNo: switchClass - }, { - confirmationText: "$services.localization.render('core.editors.class.switchClass.confirm')", - // Allow the users to cancel the switch. - showCancelButton: true + } }); } + + function init() { + ((XWiki || {}).editors || {}).XDataEditors = new XDataEditors(); + initSwitchClassListener(); + } + + // When the document is loaded, create the Autosave control + $(init); }); -}); + +// End JavaScript-only code. +}).apply(']]#', $jsontool.serialize([$l10n, $icons])); \ No newline at end of file diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/js/xwiki/meta.js b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/js/xwiki/meta.js index 9f029a955285..ca4b8d924534 100644 --- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/js/xwiki/meta.js +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/js/xwiki/meta.js @@ -57,6 +57,7 @@ define(['jquery', 'xwiki-entityReference', 'xwiki-events-bridge'], function($, X // Since 12.3RC1 // Note that the 'data-xwiki-locale' attribute is set since XWiki 10.4RC1 but it hasn't been exposed here. self.locale = html.data('xwiki-locale'); + self.action = html.data('xwiki-action'); } else { // Case 2: meta information are stored in deprecated tags // (in colibri)