diff --git a/Gruntfile.js b/Gruntfile.js
index 83745e0ef5..fa5da7f769 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -42,6 +42,7 @@ module.exports = grunt => {
'node_modules/es6-promise/dist/es6-promise.js',
'node_modules/backbone/backbone.js',
'node_modules/handlebars/dist/handlebars.js',
+ 'node_modules/bullbone/dist/bullbone.js',
'node_modules/base-64/base64.js',
'node_modules/moment/moment.js',
'node_modules/moment-timezone/moment-timezone.js',
@@ -58,7 +59,6 @@ module.exports = grunt => {
'client/lib/jquery.ui.touch-punch.min.js',
'client/lib/moment-timezone-data.js',
'client/lib/autobahn.js',
- 'client/lib/bullbone.js',
'client/src/namespace.js',
'client/src/exceptions.js',
diff --git a/application/Espo/Resources/metadata/app/client.json b/application/Espo/Resources/metadata/app/client.json
index dd9c2f80b3..3e6f4e47a4 100644
--- a/application/Espo/Resources/metadata/app/client.json
+++ b/application/Espo/Resources/metadata/app/client.json
@@ -8,6 +8,7 @@
"node_modules/es6-promise/dist/es6-promise.js",
"node_modules/backbone/backbone.js",
"node_modules/handlebars/dist/handlebars.js",
+ "node_modules/bullbone/dist/bullbone.js",
"node_modules/base-64/base64.js",
"node_modules/moment/moment.js",
"node_modules/moment-timezone/moment-timezone.js",
@@ -23,8 +24,6 @@
"client/lib/jquery-ui.min.js",
"client/lib/jquery.ui.touch-punch.min.js",
"client/lib/autobahn.js",
-
- "client/lib/bullbone.js",
"client/lib/moment-timezone-data.js",
"client/src/loader.js",
diff --git a/client/lib/bullbone.js b/client/lib/bullbone.js
deleted file mode 100644
index 17aab7f47d..0000000000
--- a/client/lib/bullbone.js
+++ /dev/null
@@ -1,1468 +0,0 @@
-/*! bullbone 1.0.2 2021-06-19 */
-var Bull = Bull || {};
-
-(function (Bull, _) {
-
- var root = this;
-
- /**
- * Bull.Factory is a factory for views.
- * It has hard dependency from Backbone.js and uses Handlebars.js templating system by default.
- *
- */
-
- /**
- * @constructor
- * @param {Object} options Configuration options.
- *
- * - defaultViewName: {String} Default name for views when it is not defined.
- * - viewLoader: {Function} Function that loads view class ({Function} in javascript) by the given view name and callback function as parameters. Here you can load js code using sync XHR request. If not defined it will lookup classes in window object.
- * - helper: {Object} View Helper that will be injected into all views.
- * - resources: {Object} Resources loading options: paths, exts, loaders. Example:
- * {
- * paths: { // Custom paths for resource files.
- * layout: 'resources/layouts',
- * templates: 'resources/templates',
- * layoutTemplate: 'resources/templates/layouts',
- * },
- * exts: { // Custom extensions of resource files.
- * layout: 'json',
- * templates: 'tpl',
- * },
- * loaders: { // Custom resources loading functions. Define it if some type of resources needs to be loaded via REST rather than from file.
- * layout: function (layoutName, callback) {
- * callback(layoutManager.getLayout(layoutName));
- * }
- * },
- * path: function (type, name) {} // Custom path function. Should return path to the needed resource.
- * }
- *
- * - rendering: {Object} Rendering options: method (Method is the custom function for a rendering. Define it if you want to use another templating engine. Function (template, data)).
- * - templating: {Object} Templating options: {bool} compilable (If templates are compilable (like Handlebars). True by default.)
- *
- */
- Bull.Factory = function (options) {
- var options = options || {};
-
- this.defaultViewName = options.defaultViewName || this.defaultViewName;
-
- this._loader = options.customLoader || new Bull.Loader(options.resources || {});
- this._renderer = options.customRenderer || new Bull.Renderer(options.rendering || {});
- this._layouter = options.customLayouter || new Bull.Layouter(_.extend(options.layouting || {}, {
- loader: this._loader,
- }));
- this._templator = options.customTemplator || new Bull.Templator(_.extend(options.templating || {}, {
- loader: this._loader,
- layouter: this._layouter,
- }));
-
- this._helper = options.helper || null;
-
- this._viewClassHash = {};
- this._getViewClassFunction = options.viewLoader || this._getViewClassFunction;
- this._viewLoader = this._getViewClassFunction;
- };
-
- _.extend(Bull.Factory.prototype, {
-
- defaultViewName: 'View',
-
- _layouter: null,
-
- _templator: null,
-
- _renderer: null,
-
- _loader: null,
-
- _helper: null,
-
- _viewClassHash: null,
-
- _viewLoader: null,
-
- _getViewClassFunction: function (viewName, callback) {
- var viewClass = root[viewName];
- if (typeof viewClass !== "function") {
- throw new Error("function \"" + viewClass + "\" not found.");
- }
- callback(viewClass);
- },
-
- _getViewClass: function (viewName, callback) {
- if (viewName in this._viewClassHash) {
- callback(this._viewClassHash[viewName]);
- return;
- }
- this._getViewClassFunction(viewName, function (viewClass) {
- this._viewClassHash[viewName] = viewClass;
- callback(viewClass);
- }.bind(this));
- },
-
- /**
- * Create view.
- * @param viewName
- * @param {Object} options
- * @param {Function} callback Will be invoked once view gets ready and view will be passed as an argument.
- * @return {Bull.View}
- */
- create: function (viewName, options, callback) {
- this._getViewClass(viewName, function (viewClass) {
- if (typeof viewClass === 'undefined') {
- throw new Error("Class for view \"" + viewName + "\" not found.");
- }
- var view = new viewClass(_.extend(options || {}, {
- factory: this,
- layouter: this._layouter,
- templator: this._templator,
- renderer: this._renderer,
- helper: this._helper,
- onReady: callback
- }));
- }.bind(this));
- },
- });
-
-}).call(this, Bull, _);
-
-(function (Bull, Backbone, _) {
-
- Bull.View = Backbone.View.extend({
-
- /**
- * @property {String} Template name.
- */
- template: null,
-
- /**
- * @property {String} Layout name. Used if template is not specified to build template.
- */
- layout: null,
-
- /**
- * @property {String} Name of View. If template name is not defined it will be used to cache built template and layout. Otherwise they won't be cached. Name it unique.
- */
- name: null,
-
- /**
- * @property {Object} or {function} Data that will be passed into template.
- */
- data: null,
-
- /**
- * @property {bool} Not to use cache for layouts. Use it if layouts are dynamic.
- */
- noCache: false,
-
- /**
- * @property {bool} Not to rended view automatical when build view tree. Afterwards it can be rendered manually.
- */
- notToRender: false,
-
- /**
- * @property {String} Template itself.
- */
- _template: null,
-
- /**
- * @property {Object} Layout itself.
- */
- _layout: null,
-
- layoutData: null,
-
- isReady: false,
-
- /**
- * @property {Object} Definitions for nested views that should be automaticaly created. Example: {body: {view: 'Body', selector: '> .body'}}.
- */
- views: null,
-
- nestedViews: null,
-
- _nestedViewDefs: null,
-
- _factory: null,
-
- factory: null,
-
- _templator: null,
-
- _renderer: null,
-
- _layouter: null,
-
- _helper: null,
-
- _templateCompiled: null,
-
- _parentView: null,
-
- _path: '',
-
- _wait: false,
-
- _waitViewList: null,
-
- optionsToPass: null,
-
- _nestedViewsFromLayoutLoaded: false,
-
- _readyConditionList: null,
-
- _isRendered: false,
-
- _isFullyRendered: false,
-
- _isBeingRendered: false,
-
- _isRemoved: false,
-
- _isRenderCanceled: false,
-
- initialize: function (options) {
- this.options = options || {};
-
- this._factory = this.factory = this.options.factory || null;
- this._renderer = this.options.renderer || null;
- this._templator = this.options.templator || null;
- this._layouter = this.options.layouter || null;
-
- this._helper = this.options.helper || null;
-
- if ('noCache' in this.options) {
- this.noCache = this.options.noCache;
- }
-
- this.name = this.options.name || this.name;
-
- this.notToRender = ('notToRender' in this.options) ? this.options.notToRender : this.notToRender;
-
- this.data = this.options.data || this.data;
-
- this.nestedViews = {};
- this._nestedViewDefs = {};
-
- if (this._waitViewList == null) {
- this._waitViewList = [];
- }
-
- this._waitPromiseCount = 0;
-
- if (this._readyConditionList == null) {
- this._readyConditionList = [];
- }
-
- this.optionsToPass = this.options.optionsToPass || this.optionsToPass || [];
-
- var merge = function (target, source) {
- for (var prop in source) {
- if (typeof target == 'object') {
- if (prop in target) {
- merge(target[prop], source[prop]);
- } else {
- target[prop] = source[prop];
- }
- }
- }
- return target;
- }
-
- if (this.views || this.options.views) {
- this.views = merge(this.options.views || {}, this.views || {});
- }
-
- this.init();
- this.setup();
- this.setupFinal();
-
- this.template = this.options.template || this.template;
- this.layout = this.options.layout || this.layout;
- this._layout = this.options._layout || this._layout;
- this.layoutData = this.options.layoutData || this.layoutData;
-
- this._template = this.templateContent || this.options.templateContent || this._template;
-
- if (this._template != null && this._templator.compilable) {
- this._templateCompiled = this._templator.compileTemplate(this._template);
- }
-
- if (this.options.el) {
- this.setElementInAdvance(this.options.el);
- }
-
- var _layout = this._getLayout();
-
- var loadNestedViews = function () {
- this._loadNestedViews(function () {
- this._nestedViewsFromLayoutLoaded = true;
- this._tryReady();
- }.bind(this));
- }.bind(this);
-
- if (this.layout != null || _layout !== null) {
- if (_layout === null) {
- this._layouter.getLayout(this.layout, function (_layout) {
- this._layout = _layout;
- loadNestedViews();
- }.bind(this));
- return;
- }
- loadNestedViews();
- return;
- } else {
- if (this.views != null) {
- loadNestedViews();
- return;
- }
- }
- this._nestedViewsFromLayoutLoaded = true;
-
- this._tryReady();
- },
-
- /**
- * Init view. Empty method by default. Is run before #setup.
- */
- init: function () {},
-
- /**
- * Setup view. Empty method by default. Is run after #init.
- */
- setup: function () {},
-
- setupFinal: function () {},
-
- /**
- * Set view container element if doesn't exist yet. It will call setElement after render.
- */
- setElementInAdvance: function (el) {
- if (this._setElementInAdvancedInProcess) return;
- this._setElementInAdvancedInProcess = true;
-
- this.on("after:render-internal", function () {
- this.setElement(el);
- this._setElementInAdvancedInProcess = false;
- }.bind(this));
- },
-
- getSelector: function () {
- return this.options.el || null;
- },
-
- setSelector: function (selector) {
- this.options.el = selector;
- },
-
- /**
- * Checks whether view has been already rendered.
- * @return {Bool}
- */
- isRendered: function () {
- return this._isRendered;
- },
-
- /**
- * Checks whether view has been fully rendered (afterRender has been executed).
- * @return {Bool}
- */
- isFullyRendered: function () {
- return this._isFullyRendered
- },
-
- isBeingRendered: function () {
- return this._isBeingRendered;
- },
-
- isRemoved: function () {
- return this._isRemoved;
- },
-
- /**
- * Get HTML of view but don't render it.
- */
- getHtml: function (callback) {
- this._getHtml(callback);
- },
-
- cancelRender: function () {
- if (this.isBeingRendered()) {
- this._isRenderCanceled = true;
- }
- if (this._renderPromise) {
- this._renderPromise._isToReject = true;
- }
- },
-
- uncancelRender: function () {
- this._isRenderCanceled = false;
- },
-
- /**
- * Render view.
- */
- render: function (callback) {
- this._isRendered = false;
- this._isFullyRendered = false;
-
- var self = this;
-
- this._renderPromise = new Promise(function (resolve, reject) {
- var promise = this;
- self._getHtml(function (html) {
- if (this._isRenderCanceled || promise._isToReject) {
- this._isRenderCanceled = false;
- this._isBeingRendered = false;
- reject();
- delete this._renderPromise;
- return;
- }
- if (this.$el.length) {
- this.$el.html(html);
- } else {
- if (this.options.el) {
- this.setElement(this.options.el);
- }
- this.$el.html(html);
- }
- this._afterRender();
- if (typeof callback === 'function') {
- callback();
- }
- delete this._renderPromise;
- resolve(this);
- }.bind(self));
- });
-
- return this._renderPromise;
- },
-
- /**
- * Re-render view.
- */
- reRender: function (force) {
- if (this.isRendered()) {
- return this.render();
- } else if (this.isBeingRendered()) {
- return new Promise(function (resolve, reject) {
- this.once('after:render', function () {
- this.render().then(resolve).catch(reject);
- }, this);
- }.bind(this));
- } else {
- if (force) {
- return this.render();
- }
- }
- },
-
- _afterRender: function () {
- this._isBeingRendered = false;
- this._isRendered = true;
- this.trigger("after:render-internal", this);
- for (var key in this.nestedViews) {
- var nestedView = this.nestedViews[key];
- if (!nestedView.notToRender) {
- nestedView._afterRender();
- }
- }
- this.afterRender();
- this.trigger("after:render", this);
- this._isFullyRendered = true;
- },
-
- /**
- * Executed after render. Empty method by default.
- */
- afterRender: function () {},
-
- _tryReady: function () {
- if (this.isReady) return;
-
- if (this._wait) return;
-
- if (!this._nestedViewsFromLayoutLoaded) return;
-
- for (var i = 0; i < this._waitViewList.length; i++) {
- if (!this.hasView(this._waitViewList[i])) return;
- }
-
- if (this._waitPromiseCount) return;
-
- for (var i = 0; i < this._readyConditionList.length; i++) {
- if (typeof this._readyConditionList[i] === 'function') {
- if (!this._readyConditionList[i]()) return;
- } else {
- if (!this._readyConditionList) return;
- }
- }
-
- this._makeReady();
- },
-
- /**
- * Run checking for view is ready.
- */
- tryReady: function () {
- this._tryReady();
- },
-
- _makeReady: function () {
- this.isReady = true;
- this.trigger('ready');
- if (typeof this.options.onReady === 'function') {
- this.options.onReady(this);
- }
- },
-
- _addDefinedNestedViewDefs: function (list) {
- for (var name in this.views) {
- var o = _.clone(this.views[name]);
- o.name = name;
- list.push(o);
- this._nestedViewDefs[name] = o;
- }
- return list
- },
-
- _getNestedViewsFromLayout: function () {
- var nestedViewDefs = this._layouter.findNestedViews(this._getLayoutName(), this._getLayout() || null, this.noCache);
-
- if (Object.prototype.toString.call(nestedViewDefs) !== '[object Array]') {
- throw new Error("Bad layout. It should be an Array.");
- }
-
- var nestedViewDefsFiltered = [];
- for (var i in nestedViewDefs) {
- var key = nestedViewDefs[i].name;
-
- this._nestedViewDefs[key] = nestedViewDefs[i];
-
- if ('view' in nestedViewDefs[i] && nestedViewDefs[i].view === true) {
- if (!('layout' in nestedViewDefs[i] || 'template' in nestedViewDefs[i])) {
- continue;
- }
- }
- nestedViewDefsFiltered.push(nestedViewDefs[i]);
- }
-
- return nestedViewDefsFiltered;
- },
-
-
- _loadNestedViews: function (callback) {
- var nestedViewDefs = [];
- if (this._layout != null) {
- nestedViewDefs = this._getNestedViewsFromLayout();
- }
-
- this._addDefinedNestedViewDefs(nestedViewDefs);
-
- var count = nestedViewDefs.length;
- var loaded = 0;
-
- var tryReady = function () {
- if (loaded == count) {
- callback();
- return true
- }
- };
-
- tryReady();
- nestedViewDefs.forEach(function (def, i) {
- var key = nestedViewDefs[i].name;
- var viewName = this._factory.defaultViewName;
- if ('view' in nestedViewDefs[i]) {
- viewName = nestedViewDefs[i].view;
- }
-
- if (viewName === false) {
- loaded++;
- tryReady();
- } else {
- var options = {};
- if ('layout' in nestedViewDefs[i]) {
- options.layout = nestedViewDefs[i].layout;
- }
- if ('template' in nestedViewDefs[i]) {
- options.template = nestedViewDefs[i].template;
- }
-
- if ('el' in nestedViewDefs[i]) {
- options.el = nestedViewDefs[i].el;
- }
-
- if ('options' in nestedViewDefs[i]) {
- options = _.extend(options, nestedViewDefs[i].options);
- }
- if (this.model) {
- options.model = this.model;
- }
- if (this.collection) {
- options.collection = this.collection;
- }
-
- for (var k in this.optionsToPass) {
- var name = this.optionsToPass[k];
- options[name] = this.options[name];
- }
- this._factory.create(viewName, options, function (view) {
- if ('notToRender' in nestedViewDefs[i]) {
- view.notToRender = nestedViewDefs[i].notToRender;
- }
- this.setView(key, view);
- loaded++;
- tryReady();
- }.bind(this));
- }
- }, this);
- },
-
- _getData: function () {
- if (typeof this.data === 'function') {
- return this.data();
- }
- return this.data;
- },
-
- _getNestedViewsAsArray: function (nestedViews) {
- var nestedViewsArray = [];
- var i = 0;
- for (var key in this.nestedViews) {
- nestedViewsArray.push({
- key: key,
- view: this.nestedViews[key]
- });
- i++;
- }
- return nestedViewsArray;
-
- },
-
- _getNestedViewsHtmlList: function (callback) {
- var data = {};
- var nestedViewsArray = this._getNestedViewsAsArray();
-
-
- var loaded = 0;
- var count = nestedViewsArray.length;
-
- var tryReady = function () {
- if (loaded == count) {
- callback(data);
- return true
- }
- };
-
- tryReady();
- nestedViewsArray.forEach(function (d, i) {
- var key = nestedViewsArray[i].key;
- var view = nestedViewsArray[i].view;
-
- if (!view.notToRender) {
- view.getHtml(function (html) {
- data[key] = html;
- loaded++;
- tryReady();
- });
- } else {
- loaded++;
- tryReady();
- }
- }, this);
- },
-
- handleDataBeforeRender: function (data) {},
-
- _getHtml: function (callback) {
- this._isBeingRendered = true;
- this.trigger("render", this);
- this._getNestedViewsHtmlList(function (nestedViewsHtmlList) {
- var data = _.extend(this._getData() || {}, nestedViewsHtmlList);
- if (this.collection || null) {
- data.collection = this.collection;
- }
- if (this.model || null) {
- data.model = this.model;
- }
- data.viewObject = this;
- this.handleDataBeforeRender(data);
- this._getTemplate(function (template) {
- var html = this._renderer.render(template, data);
- callback(html);
- }.bind(this));
- }.bind(this));
- },
-
- _getTemplateName: function () {
- return this.template || null;
- },
-
- _getLayoutName: function () {
- return this.layout || this.name || null;
- },
-
- _getLayoutData: function () {
- return this.layoutData;
- },
-
- _getLayout: function () {
- if (typeof this._layout === 'function') {
- return this._layout();
- }
- return this._layout;
- },
-
- _getTemplate: function (callback) {
- if (this._templator.compilable && this._templateCompiled !== null) {
- callback(this._templateCompiled);
- return;
- }
-
- var _template = this._template || null;
-
- if (_template !== null) {
- callback(_template);
- return;
- }
-
- var templateName = this._getTemplateName();
-
- var noCache = false;
- var layoutOptions = {};
-
- if (!templateName) {
- noCache = this.noCache;
-
- var layoutName = this._getLayoutName();
-
- if (!layoutName) {
- noCache = true;
- } else {
- if (layoutName) {
- templateName = 'built-' + layoutName;
- } else {
- templateName = null;
- }
- }
- layoutOptions = {
- name: layoutName,
- data: this._getLayoutData(),
- layout: this._getLayout(),
- }
- }
-
- this._templator.getTemplate(templateName, layoutOptions, noCache, callback);
- },
-
- _updatePath: function (parentPath, viewKey) {
- this._path = parentPath + '/' + viewKey;
- for (var key in this.nestedViews) {
- this.nestedViews[key]._updatePath(this._path, key);
- }
- },
-
- _getSelectorForNestedView: function (key) {
- var el = false;
-
- if (key in this._nestedViewDefs) {
- if ('id' in this._nestedViewDefs[key]) {
- el = '#' + this._nestedViewDefs[key].id;
- } else {
- if ('el' in this._nestedViewDefs[key]) {
- el = this._nestedViewDefs[key].el;
- } else if ('selector' in this._nestedViewDefs[key]) {
- var currentEl = this.getSelector();
- if (currentEl) {
- el = currentEl + ' ' + this._nestedViewDefs[key].selector;
- }
- } else {
- var currentEl = this.getSelector();
- if (currentEl) {
- el = currentEl + ' [data-view="'+key+'"]';
- }
- }
- }
- }
- return el;
- },
-
- /**
- * Whether this view has nested view.
- * @param {String} key
- * @return {Bool}
- */
- hasView: function (key) {
- if (key in this.nestedViews) {
- return true;
- }
- return false;
- },
-
- /**
- * Get nested view.
- * @param {String} key
- * @return {Bull.View}
- */
- getView: function (key) {
- if (key in this.nestedViews) {
- return this.nestedViews[key];
- }
- },
-
- /**
- * Create nested view. The important method.
- * @param {String} key Key.
- * @param {String} viewName View name.
- * @param {Object} options View options.
- * @param {Function} callback Callback function. Will be invoiked once nested view is ready (loaded).
- * @param {Bool} wait True be default. Set false if no need parent view wait for nested view loaded.
- */
- createView: function (key, viewName, options, callback, wait) {
- this.clearView(key);
-
- this._viewPromiseHash = this._viewPromiseHash || {};
-
- var self = this;
-
- var promise = this._viewPromiseHash[key] = new Promise(function (resolve, reject) {
- var promise = this;
- var context = self;
-
- wait = (typeof wait === 'undefined') ? true : wait;
-
- if (wait) {
- self.waitForView(key);
- }
-
- options = options || {};
- if (!options.el) {
- options.el = self.getSelector() + ' [data-view="'+key+'"]';
- }
-
- self._factory.create(viewName, options, function (view) {
- var previusView = this.getView(key);
- if (previusView) {
- previusView.cancelRender();
- }
-
- delete this._viewPromiseHash[key];
-
- if (promise._isToReject) {
- reject();
- return;
- }
-
- var isSet = false;
- if (this._isRendered || options.setViewBeforeCallback) {
- this.setView(key, view);
- isSet = true;
- }
-
- if (typeof callback === 'function') {
- callback.call(context, view);
- }
-
- resolve(view);
-
- if (!this._isRendered && !options.setViewBeforeCallback && !isSet) {
- this.setView(key, view);
- }
- }.bind(self));
- });
-
- return promise;
- },
-
- /**
- * Set nested view.
- * @param {String} key
- * @param {Bull.View} view
- * @param {String} el Selector for view container.
- */
- setView: function (key, view, el) {
- var el = el || this._getSelectorForNestedView(key) || view.options.el || false;
-
- if (el) {
- if (this.isRendered()) {
- view.setElement(el);
- } else {
- view.setElementInAdvance(el);
- }
- }
-
- if (key in this.nestedViews) {
- this.clearView(key);
- }
- this.nestedViews[key] = view;
- view._parentView = this;
- view._updatePath(this._path, key);
-
- this._tryReady();
- },
-
- /**
- * Clear nested view.
- * @param {String} key
- */
- clearView: function (key) {
- if (key in this.nestedViews) {
- this.nestedViews[key].remove();
- delete this.nestedViews[key];
- }
-
- this._viewPromiseHash = this._viewPromiseHash || {};
- var previousPromise = this._viewPromiseHash[key];
- if (previousPromise) {
- previousPromise._isToReject = true;
- }
- },
-
- /**
- * Removes nested view but supposed that this view can be re-used in future.
- * @param {String} key
- */
- unchainView: function (key) {
- if (key in this.nestedViews) {
- this.nestedViews[key]._parentView = null;
- this.nestedViews[key].undelegateEvents();
- delete this.nestedViews[key];
- }
- },
-
- /**
- * Get parent view.
- * @return {Bull.View}
- */
- getParentView: function () {
- return this._parentView;
- },
-
- /**
- * Has parent view.
- * @return {bool}
- */
- hasParentView: function () {
- return !!this._parentView;
- },
-
- /**
- * Add condition for view getting ready.
- * @param {Function} or {Bool}
- */
- addReadyCondition: function (condition) {
- this._readyConditionList.push(condition);
- },
-
- /**
- * Wait for nested view.
- * @param {String} key
- */
- waitForView: function (key) {
- this._waitViewList.push(key);
- },
-
- /**
- * Make view wait for promise if Promise is passed as a parameter.
- * Add wait condition if true is passed. Remove wait condition if false.
- * @param wait Promise | Function | {Bool}
- */
- wait: function (wait) {
- if (typeof wait === 'object' && (wait instanceof Promise || typeof wait.then === 'function')) {
- this._waitPromiseCount++;
- wait.then(function () {
- this._waitPromiseCount--;
- this._tryReady();
- }.bind(this));
- return;
- }
- if (typeof wait == 'function') {
- this._waitPromiseCount++;
- var promise = new Promise(function (resolve) {
- resolve(wait.call(this));
- }.bind(this))
- promise.then(function () {
- this._waitPromiseCount--;
- this._tryReady();
- }.bind(this));
- return promise;
- }
- if (wait) {
- this._wait = true;
- } else {
- this._wait = false;
- this._tryReady();
- }
- },
-
- /**
- * Remove view and all nested tree. Removes contents of el. Triggers 'remove' event.
- */
- remove: function (dontEmpty) {
- this.cancelRender();
- for (var key in this.nestedViews) {
- this.clearView(key);
- }
- this.trigger('remove');
- this.onRemove();
- this.off();
- if (!dontEmpty) {
- this.$el.empty();
- }
- this.stopListening();
- this.undelegateEvents();
- if (this.model) {
- this.model.off(null, null, this);
- }
- if (this.collection) {
- this.collection.off(null, null, this);
- }
- this._isRendered = false;
- this._isFullyRendered = false;
- this._isBeingRendered = false;
- this._isRemoved = true;
- return this;
- },
-
- onRemove: function () {},
-
- _ensureElement: function () {
- this.$el = $();
- },
-
- _setElement: function (el) {
- if (typeof el === 'string') {
- var parentView = this.getParentView();
- if (parentView && parentView.isRendered()) {
- if (parentView.$el && parentView.$el.length && parentView.getSelector()) {
- if (el.indexOf(parentView.getSelector()) === 0) {
- var subEl = el.substr(parentView.getSelector().length, el.length - 1);
- this.$el = $(subEl, parentView.$el).eq(0);
- this.el = this.$el[0];
- return;
- }
- }
- }
- }
-
- this.$el = $(el).eq(0);
- this.el = this.$el[0];
- },
-
- propagateEvent: function () {
- this.trigger.apply(this, arguments);
-
- for (var key in this.nestedViews) {
- var view = this.nestedViews[key];
- view.propagateEvent.apply(view, arguments);
- }
- },
- });
-
-}).call(this, Bull, Backbone, _);
-
-(function (Bull, _) {
-
- Bull.Loader = function (options) {
- var options = options || {};
- this._paths = _.extend(this._paths, options.paths || {});
- this._exts = _.extend(this._exts, options.exts || {});
- this._normalize = _.extend(this._normalize, options.normalize || {});
- this._isJson = _.extend(this._isJson, options.isJson || {});
-
- this._externalLoaders = _.extend(this._externalLoaders, options.loaders || {});
-
- this._externalPathFunction = options.path || null;
-
- };
-
- _.extend(Bull.Loader.prototype, {
-
- _exts: {
- layout: 'json',
- template: 'tpl',
- layoutTemplate: 'tpl',
- },
-
- _paths: {
- layout: 'layouts',
- template: 'templates',
- layoutTemplate: 'templates/layouts',
- },
-
- _isJson: {
- layout: true,
- },
-
- _externalLoaders: {
- layout: null,
- template: null,
- layoutTemplate: null,
- },
-
- _externalPathFunction: null,
-
- _normalize: {
- layouts: function (name) {
- return name;
- },
- templates: function (name) {
- return name;
- },
- layoutTemplates: function (name) {
- return name;
- },
- },
-
- getFilePath: function (type, name) {
- if (!(type in this._paths) || !(type in this._exts)) {
- throw new TypeError("Unknown resource type \"" + type + "\" requested in Bull.Loader.");
- }
-
- var namePart = name;
- if (type in this._normalize) {
- namePart = this._normalize[type](name);
- }
-
- var pathPart = this._paths[type];
- if (pathPart.substr(-1) == '/') {
- pathPart = pathPart.substr(0, pathPart.length - 1);
- }
-
- return pathPart + '/' + namePart + '.' + this._exts[type];
- },
-
- _callExternalLoader: function (type, name, callback) {
- if (type in this._externalLoaders && this._externalLoaders[type] !== null) {
- if (typeof this._externalLoaders[type] === 'function') {
- this._externalLoaders[type](name, callback);
- return true;
- } else {
- throw new Error("Loader for \"" + type + "\" in not a Function.");
- }
- }
- return null;
- },
-
- load: function (type, name, callback) {
- var customCalled = this._callExternalLoader(type, name, callback);
- if (customCalled) {
- return;
- }
-
- var response, filePath ;
-
- if (this._externalPathFunction != null) {
- filePath = this._externalPathFunction.call(this, type, name);
- } else {
- filePath = this.getFilePath(type, name);
- }
-
- filePath += '?_=' + new Date().getTime();
-
- var xhr = new XMLHttpRequest();
- xhr.open('GET', filePath, true);
- xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
-
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) {
- response = xhr.responseText;
- if (type in this._isJson) {
- if (this._isJson[type]) {
- var obj;
- if (xhr.status == 404 || xhr.status == 403) {
- throw new Error("Could not load " + type + " \"" + name + "\".");
- }
-
- try {
- obj = JSON.parse(String(response));
- } catch (e) {
- throw new SyntaxError("Error while parsing " + type + " \"" + name + "\": (" + e.message + ").");
- }
- callback(obj);
- return;
- }
- }
- callback(response);
- }
- }.bind(this);
-
- xhr.send(null);
- },
- });
-
-}).call(this, Bull, _);
-
-(function (Bull, _, Handlebars) {
-
- Bull.Templator = function (data) {
- var data = data || {};
- this._templates = {};
- this._layoutTemplates = {};
- this._loader = data.loader || null;
- this._layouter = data.layouter || null;
- if ('compilable' in data) {
- this.compilable = data.compilable;
- }
- };
-
- _.extend(Bull.Templator.prototype, {
-
- compilable: true,
-
- _templates: null,
-
- _layoutTemplates: null,
-
- _loader: null,
-
- _layouter: null,
-
- addTemplate: function (name, template) {
- this._templates[name] = template;
- },
-
- getTemplate: function (name, layoutOptions, noCache, callback) {
- var layoutOptions = layoutOptions || {};
- var template = null;
-
- if (!layoutOptions.name && !layoutOptions.layout && !name) {
- throw new Error("Can not get template. Not enough data passed.");
- }
-
- if (!noCache && name) {
- template = this._getCachedTemplate(name);
- if (template) {
- callback(template);
- return;
- }
- }
-
- var layout = layoutOptions.layout || null;
-
- var then = function (template) {
- if (this.compilable) {
- template = this.compileTemplate(template);
- }
- this._templates[name] = template;
- callback(template);
- }.bind(this);
-
- var proceedWithLayout = function (layout) {
- if (layout == null) {
- throw new Error("Could not get layout '" + layoutOptions.name + "'.");
- }
- this._buildTemplate(layout, layoutOptions.data, then);
- }.bind(this);
-
- if (!template) {
- if (!layoutOptions.name && !layoutOptions.layout) {
- this._loader.load('template', name, then);
- } else {
- if (!layout) {
- this._layouter.getLayout(layoutOptions.name, proceedWithLayout);
- } else {
- proceedWithLayout(layout);
- }
- }
- }
- },
-
- compileTemplate: function (template) {
- if (typeof Handlebars !== 'undefined') {
- return Handlebars.compile(template);
- }
- return template;
- },
-
- _getCachedTemplate: function (templateName) {
- if (templateName in this._templates) {
- return this._templates[templateName];
- }
- return false;
- },
-
-
- _getCachedLayoutTemplate: function (layoutType) {
- if (layoutType in this._layoutTemplates) {
- return this._layoutTemplates[layoutType];
- }
- return false;
- },
-
- _cacheLayoutTemplate: function (layoutType, layoutTemplate) {
- this._layoutTemplates[layoutType] = layoutTemplate;
- },
-
- _buildTemplate: function (layoutDefs, data, callback) {
- var layoutType = layoutDefs.type || 'default';
-
- var proceed = function (layoutTemplate) {
- var injection = _.extend(layoutDefs, data || {});
- var template = _.template(layoutTemplate, injection);
- if (typeof template === 'function') {
- template = template(injection);
- }
- callback(template);
- }.bind(this);
-
- var layoutTemplate = this._getCachedLayoutTemplate(layoutType);
- if (!layoutTemplate) {
- this._loader.load('layoutTemplate', layoutType, function (layoutTemplate) {
- this._cacheLayoutTemplate(layoutType, layoutTemplate);
- proceed(layoutTemplate);
- }.bind(this));
- return;
- }
- proceed(layoutTemplate);
- },
- });
-
-}).call(this, Bull, _, Handlebars);
-
-(function (Bull, _) {
-
- Bull.Layouter = function (data) {
- var data = data || {};
- this._layouts = {};
- this._loader = data.loader || null;
- this._cachedNestedViews = {};
- };
-
- _.extend(Bull.Layouter.prototype, {
-
- _layouts: null,
-
- _loader: null,
-
- _cachedNestedViews: null,
-
- addLayout: function (layoutName, layout) {
- this._layouts[layoutName] = layout;
- },
-
- getLayout: function (layoutName, callback) {
- if (layoutName in this._layouts) {
- callback(this._layouts[layoutName]);
- return;
- }
-
- if (!layout) {
- this._loader.load('layout', layoutName, function (layout) {
- this.addLayout(layoutName, layout);
- callback(layout);
- }.bind(this));
- return;
- }
- },
-
- _getCachedNestedViews: function (layoutName) {
- if (layoutName in this._cachedNestedViews) {
- return this._cachedNestedViews[layoutName];
- }
- return false;
- },
-
- _cacheNestedViews: function (layoutName, nestedViews) {
- if (!(layoutName in this._cachedNestedViews)) {
- this._cachedNestedViews[layoutName] = nestedViews;
- }
- },
-
- findNestedViews: function (layoutName, layoutDefs, noCache) {
- if (!layoutName && !layoutDefs) {
- throw new Error("Can not find nested views. No layout data and name.");
- }
-
- if (layoutName && !noCache) {
- var cached = this._getCachedNestedViews(layoutName);
- if (cached) {
- return cached;
- }
- }
-
- if (typeof layoutDefs == 'undefined') {
- if (layoutName in this._layouts) {
- layoutDefs = this._layouts[layoutName];
- }
- if (!('layout' in layoutDefs)) {
- throw new Error("Layout \"" + layoutName + "\"" + " is bad.");
- }
- }
- var layout = layoutDefs.layout;
- var viewPathList = [];
-
- var uniqName = function (name, count) {
- var modName = name;
- if (typeof count !== 'undefined') {
- modName = modName + '_' + count;
- } else {
- var count = 0;
- }
- for (var i in viewPathList) {
- if (viewPathList[i].name == modName) {
- return uniqName(name, count + 1);
- }
- }
- return modName;
- }
-
- var getDefsForNestedView = function (defsInLayout) {
- var defs = {};
- var params = ['view', 'layout', 'notToRender', 'options', 'template', 'id', 'selector', 'el'];
- for (var i in params) {
- var param = params[i];
- if (param in defsInLayout) {
- defs[param] = defsInLayout[param];
- }
- }
- if ('name' in defsInLayout) {
- defs.name = uniqName(defsInLayout.name);
- }
- return defs;
- }
-
- var seekForViews = function (tree) {
- for (var key in tree) {
- if (tree[key] != null && typeof tree[key] === 'object') {
- if ('view' in tree[key] || 'layout' in tree[key] || 'template' in tree[key]) {
- var def = getDefsForNestedView(tree[key]);
- if ('name' in def) {
- viewPathList.push(def);
- }
- } else {
- seekForViews(tree[key]);
- }
- }
- }
- }
- seekForViews(layout);
- if (layoutName && !noCache) {
- this._cacheNestedViews(layoutName, viewPathList);
- }
- return viewPathList;
- }
- });
-
-}).call(this, Bull, _);
-
-(function (Bull, _) {
-
- Bull.Renderer = function (options) {
- var options = options || {};
- this._render = options.method || this._render;
- };
-
- _.extend(Bull.Renderer.prototype, {
-
- _render: function (template, data) {
- return template(data, {allowProtoPropertiesByDefault: true});
- },
-
- render: function (template, data) {
- return this._render.call(this, template, data);
- },
- });
-
-}).call(this, Bull, _);
diff --git a/package-lock.json b/package-lock.json
index 1499aeffa6..e4d1830986 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -421,6 +421,10 @@
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
+ "bullbone": {
+ "version": "git+https://github.com/yurikuzn/bull.git#ce23e221456aca079cee34ce48cf5c4720730610",
+ "from": "git+https://github.com/yurikuzn/bull.git#1.0.3"
+ },
"cache-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
@@ -1971,9 +1975,9 @@
"dev": true
},
"jquery": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz",
- "integrity": "sha1-LInWiJterFIqfuoywUUhVZxsvwI="
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
+ "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
},
"jquery-textcomplete": {
"version": "1.8.5",
diff --git a/package.json b/package.json
index a2ea2873aa..31249995f8 100644
--- a/package.json
+++ b/package.json
@@ -34,11 +34,12 @@
"base-64": "^1.0.0",
"bootstrap": "^3.4.1",
"bootstrap-datepicker": "^1.9.0",
+ "bullbone": "git+https://github.com/yurikuzn/bull.git#1.0.3",
"devbridge-autocomplete": "^1.4.9",
"es6-promise": "^3.0.2",
"gridstack": "^1.2.1",
"handlebars": "^4.7.7",
- "jquery": "^2.1.4",
+ "jquery": "^3.6.0",
"jquery-textcomplete": "^1.8.5",
"marked": "^0.8.2",
"moment": "^2.24.0",