Xeditor v6.0

General information

Xeditor version 6.0 contains many improvements for performance, copy/paste, search/replace, etc., as well as completely new features like simultaneous authoring (BETA). Some of these changes are not backwards compatible with v5.x. This guide provides the necessary background and steps to upgrade from v5.x to v6.0.

Why update?

Our recommendation is to upgrade to v6 as soon as possible. A few good reasons:

  • Xeditor v5.x will only recieve critical bug fixes for a limited time.
  • No new features will be added to Xeditor v5.x and all the good features will be added to v6.
  • There are significant performance updates in v6 that will not be available in v5.

Highlevel description of major changes

We performed a lot of highlevel changes, that not only improve the performance and the general behaviour of Xeditor, but also make it more convenient for developers to work with it. We separated a lot of logic into separate classes, refactored existing methods and created completely new methods.

  • We changed the whole initialization process of Xeditor. We adapted a lot of new technologies that were not supported by all browsers previously but now offer good enough support to be used. This offers a great performance boost, meaning Xeditor loads up much faster than it did in old versions. Additionally, the initialization process is now asynchronous.
  • New plugin Ext.ux.xeditor.plugin.AttributesList has been added. It builds the attributes panel (with updated UI). Along with it, we changed the way attributes are handled. They are not either empty or filled, they now also support a state of being null which means they are not set on the element. Previously attributes were either filled or not, but there was no distinction between existing and empty attributes, and attributes that do not exist at all. Due to this change, all code handling the attributes panel has been moved from Ext.ux.xeditor.Editor to the new plugin.
  • Added new class Ext.ux.xeditor.MenuManager. This class is responsible for building the context menu, the insert menu and the breadcrumb menus. Therefore, all logic responsible for those menus was refactored and moved from Ext.ux.xeditor.Editor to the new class.
  • The copy paste handling was completely redesigned. The internal clipboard of Xeditor was removed and we use the default browser clipboard instead. Now, copying a selection/elements using CTRL + C will write the required information into the clipboard, to be used from Xeditor, but also from other applications like Word. CTRL + V will always insert the copied content as elements (will also insert missing elements, e.g. when you copied a list item, and the content gets pasted into a paragraph, the required list element will be inserted as well). Additionaly, using CTRL + Shift + V will paste the copied content as plain text.
  • Added new class Ext.ux.xeditor.Range which will handle all kinds of ranges. It can be used in order to store the currently selected range, but also manually created ranges. It offers methods for selecting a given range, unselecting it, as well as removing a given range from the document.
  • Added provider classes that simplify the integration of Xeditor. More detailed description of how to work with them can be found here.
  • Added beta version of simultanous authoring. This is handled by a new plugin Ext.ux.xeditor.plugin.Collaboration. This enables multiple users to edit the same document at the same time.
  • Added new plugin Ext.ux.xeditor.plugin.Searchreplace which offers a new search and replace window with updated UI. In addition, it now also supports to search for XML element tags as well as attributes in combination with certain values. This plugin replaces the class Ext.ux.xeditor.FindReplace which is now no longer available.
  • Method fixEmptyTextElements has been completely refactored and was moved from Ext.ux.xeditor.Document to Ext.ux.xeditor.Frame. Due to the refactoring, this step is now way faster than it was in the old version.
  • Added new class Ext.ux.xeditor.TextLengthCache which builds the text lengths cache (used for e.g. offset calculations) instead of having this process within Ext.ux.xeditor.Document. Due to this, the whole text length processing has been refactored.
  • Added new class Ext.ux.xeditor.ConfigProcessor which will handle config related processing as well as schema related handling of the validator.
  • Xeditor will now save proper XHTML instead of HTML. This makes the parsing as XHTML on the backend before the content could be transformed obsolete.
  • Added new method Ext.ux.xeditor.Editor.boot() which is new starting point for starting up a new Xeditor instance.
  • Xeditor toolbar button objects/configurations can now contain a property raw which can be used to set ExtJS button configs into the Xeditor toolbar button.
  • Introduced new role properties enableCopyElementItem, enableCopyElementTextItem, enableCutElementItem, enableElementDeleteItem and enableElementRemoveItem definiting whether those elements should have corresponding items in the breadcrumb/context menu. This replaces the adding of those items using an event listener on the updatemenus event.
  • The properties editable, removable, preserveSelection and frameonly now also offer inheritance. In order to use it, just set their value to undefined.
  • The way custom shortcuts are defined and handles has been changed. Instead of using listener with custom logic, Xeditor now uses keybindings to be defined in keybindings.js. The demo packages already contain samples on how to use them.

Cleaning up your package

Before upgrading the latest version of Xeditor, we recommend taking some time to clean up your previous version. Specifically, it is a good idea to compare any custom code to the new release and identify any redundancies. This will make future updates much easier and also reduce the amount of code you need to maintain.

Downloading a new package and adding your features to it

We recommend re-downloading a new Xeditor package and adding your custom files to that rather than using your existing package. Based on our own experience, this will be faster and easier. Depending on the configuration you are using, there are different packages available for you:

  • For custom schemas, you can download our clean package, which has no schema conigured to it. The package is called xeditor/xeditor. You can download it by running

    npm pack @xeditor/xeditor --registry https://npm.xeditor.com:4873

    in your console.

  • For our configurations of DITA or JATS, you can download the corresponding package by running

    npm pack @xeditor/xeditor-dita --registry https://npm.xeditor.com:4873

    or

    npm pack @xeditor/xeditor-jats --registry https://npm.xeditor.com:4873

    in your console.

  • Install all the plugins you used in your old package and you want to still use in the new Xeditor version. In addition, you can install all new plugins that have beend added along with Xeditor v6.

You now have a clean package to start with.

The next step would be, to add your custom files/changes to the newly downloaded, clean package. Usually, It's only required to change the following files:

  • Any JS file included in /js/config/. Most files your old package uses should already be existing in here (e.g. listeners.js) so you can simply update them. If they do not exist in the new package, just copy them in there. If you added any new JS files, make sure to also include them in /js/index.js.
  • Any phrases files within /static/phrases/
  • Any CSS files within /static/css/. If you added new files that didn't exist already, make sure to either add them to /static/index.html or /static/iframe.html
  • Compare the transformation files:
    1. contenttoeditor.xsl
    2. editortocontent.xsl
    3. pastetoeditorfirst.xsl
    4. pastetoeditorsecond.xsl
  • of your old and your new project, and adapt the new one accordingly. This basically means that you have to add your elements to the corresponding templates of the new files

Generate new types configuration

Since we adjusted the way attributes are handled, default attribute values will be defined differently within the types configuration of Xeditor (types.js).

In order to incorporate the new types definition, you simply have to regenerate the types definition file.

in order to do so, just follow the steps described here: Generate types definition.

If you already created a info.xml file for your previous configuration, it can be reused for this generation process.

Code changes

Changed API methods

This table will list changed API methods, and describe how they differ between version 5.9 and version 6.0. If you used any of the listed methos, make sure to adapt them to their new behaviour.

Note

Please note that this table does not contain methods that are marked as @protected and/or @private since they are not meant to be used in custom projects.

Changed Code Old behaviour New behaviour
Changed result of Ext.ux.xeditor.Element.getPlainText() Returned object with properties text and spaceOffsets Returns text directly
Changed result of Ext.ux.xeditor.Element.getPlainTextLength() Returned object with properties length and spacesCount Returns text length directly
Changed behaviour and result of Ext.ux.xeditor.Element.getAttributes() Returned default values for attributes that were not populated Attributes that do not exist are not returned. If attribute exist but is empty, empty string will be returned. Basically only existing attributes are returned with their populated values.
Changed behaviour and result of Ext.ux.xeditor.Element.removeAttribute() Populated attribute with it's default value, which might have been an empty string if none existed in the schema Method will now only populate attribute with its default value if it's required or the default value is not an empty string
Changed behaviour and result of Ext.ux.xeditor.Element.removeAttributes() See removeAttribute() See removeAttribute()
Changed behaviour of Ext.ux.xeditor.BasicElement.setData() Given data name was set as is Given data name will be cleaned before setting it (only lower case, HTML valid)
Removed Ext.ux.xeditor.Editor.localStorageKey Key used for local storage save Removed since now IndexedDB will be used instead of local storage
Changed Ext.ux.xeditor.BasicElement.addClass(), removeClass() and hasClass() Used regular expressions in order to set/remove/read classes Uses native DOM functionality for setting/removing/reading classes. However, this also means that you can't set/remove multiple classes at once. If you want to do so, use newly introduced methods addClasses or removeClasses instead
Implemented addition/replacement for Ext.ux.xeditor.selectionManager.getElements() This was used for recieving the current selection (and its elements), and also for removing the current selection New class Ext.ux.xeditor.Range can store a range and also remove it. In addition, it's not limited to the current selection, but any range can be used. It also offers helper methods for e.g. selecting a given range.
Renamed role properties for setting elements editable and/or removable Previously role properties were called isEditable and isRemoveable Now they are called editable and removable
Removed fixEmptyTextElements from document class and moved it to frame class instead Previously this was used in Ext.ux.xeditor.Document.fixEmptyTextElements Now it's used in Ext.ux.xeditor.Frame.fixEmptyTextElements along with some refacotring for improved performance
Reviewbar is now enabled by default Flag Ext.ux.xeditor.Editor.enableReviewbar was set to false by default Flag Ext.ux.xeditor.Editor.enableReviewbar is now set to true by default
Renamed anchorable mixin Anchorable mixin was called Ext.ux.xeditor.plugin.Searchreplace Anchorable mixin got renamed to Ext.ux.xeditor.Anchorable
Editable and removable are now use dom attributes instead of "Xeditor data attributes" Previously they were set as data-datavalue-xe-editable and/or data-datavalue-xe-removable They are now set on the HTML element as data-editable and/or data-removable
Changed params of Ext.ux.xeditor.Editor.openDocumentByString Previously, the params of this method were content , {successCallback} and {failureCallback} Now the params are documentId and content. In addition, the method now returns a Promise
Changed initialization method name of all classes for more constistency Depnding on the class, initialization method might have been called init, initComponent or something else All initialization methods are now called initalize

Promises vs Callbacks

With Xeditor v6 we moved away from using Callbacks, and use Promises instead.

Note

We moved to Promises even though they are not supported by IE. However, by using the ExtJS Framework and adding Polyfills, they work just fine in IE.

Note

If you are unfamiliar with Promises and how to work with them, here's a good guide from MDN explaining how to work with Promises: Using Promises.

Promises offer a way easier handling of asynchronous code, and are also way more handy than callbacks. Since Promises are a JS standard, we won't explain their usage here. Any API method Xeditor offers, will be highlighted using a @async marker.

In order to update your package, you have to adapt any method that previously used a successCallback and a failureCallback to the new Promises instead. For this, see following example using the saveDocument function (see version 5.9 and 6.0 in our API for more details).

Old code:

editor.saveDocument(function(), {
    // do something when saving was successfull
}, function() {
    // do something when saving failed
});

becomes:

var savePromise = editor.saveDocument();
savePromise.then(function(savedContent) {
    // do something when saving was successfull
}).catch(function(error) {
    // do something when saving failed
});

or (both are identical, you can use whatever you prefer):

editor.saveDocument().then(function(savedContent) {
    // do something when saving was successfull
}).catch(function(error) {
    // do something when saving failed
});

Adapt to usage of provider

With version 6, all communication goes directly via the provider API of Xeditor instead of AJAX requests. This has the advantage that the communication itself can be changed and it is also possible to communicate via other interfaces (Offline/Pure JS, Sockets etc) and hence, is way more flexible.

With Middleware:

If you are using our middleware server you can simply use a static constructor function on the AjaxProvider class to create your provider instance:

var provider = Ext.ux.xeditor.provider.AjaxProvider.forMiddleware(BLACKBOX_URL);

Here you can also add or override routes using the second parameter of it. This provider is automatically preconfigured for the middleware.

Without Middleware:

If the middleware is not used but the communication also happens via AJAX, the static function forHost can be used here. You have to pass a map as a second argument, which maps the services with the suburls of the server.

// define routes
var routes = {};
routes[Ext.ux.xeditor.provider.AjaxProvider.SAVE_URL] = "/editor/save";
routes[Ext.ux.xeditor.provider.AjaxProvider.LOAD_URL] = "/editor/load";
routes[Ext.ux.xeditor.provider.AjaxProvider.META_URL] = "/editor/meta";
routes[Ext.ux.xeditor.provider.AjaxProvider.TRANSFORM_URL] = "/editor/transform";
routes[Ext.ux.xeditor.provider.AjaxProvider.VALIDATE_URL] = "/editor/validate";
routes[Ext.ux.xeditor.provider.AjaxProvider.OFFICE_URL] = "/editor/pasteofficetransform";

// create provider using the routes
var provider = Ext.ux.xeditor.provider.AjaxProvider.forMiddleware(SERVER_URL, routes);

Register default parameter:

If all requests previously had a default parameter such as IDs or similar in the URL, they can be set using the function setDefaultParameter. This is also possible for headers by using the function setDefaultHeader.

Register the provider:

The provider is a mandatory configuration argument of the editor. This must always be specified when creating the editor.

var editor = Ext.create('Ext.ux.xeditor.Editor', {
    provider: provider /*previously created*/,
}); 

(optional) Enabling multi user editing

Note

Please be aware that multi user editing is still in beta and it's codebase as well it's functionality is still subject to change. Therefore, keep in mind that the features of this as well as it's bevahiour might change until it's final release. Also, we do not offer high priority support for this. Even though we will fix issues that you might find on this, it may take some time until they will be fixed.

With this release, we introduces a new plugin that offers support for multiple users editing the same document at the same time.

Even though this is currently in beta state, you can already give it a try and report any issues you find to us.

Note

Please note that multi user editing requires a lot of backend logic. Therefore, we currently only offer support for multi user editing in combination with usage of the blackbox.

Note

In order to avoid conflicts while editing XML documents, an element gets locked for all users as soon as one user selects it. Meaning only one user can edit the same element at a time.

In order to enable it, just do the following:

  1. Install the plugin in your package using npm i -S @xeditor/plugin-collaboration --registry https://npm.xeditor.com:4873
  2. Add the following plugin configuration to your file plugins.js within your Xeditor package:

    { pluginId: 'collaboration', ptype: 'uxxeditorcollaboration', socketURL: BLACKBOX_URL, wholeElementTypesToLock: ['tab_table'], authOptions: { token: 'xeditor-token' } }

  3. As already stated, an element can only be edited by one user at a time. In order to enforce this, the config wholeElementTypesToLock has to be filled with elements that are bigger blocks that should behave the same way (e.g. tables). This config has to be adjusted to your schema

  4. The plugin also offers a button indicating who's currently editing the document. You might want to it to your toolbar.js file by adding 'xeditor.collaboration'