From 15b5cf97504dfecbae82d45a1f3307c6ead8a563 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 16 Mar 2021 22:23:35 +0200 Subject: [PATCH] using cache api --- client/src/app.js | 227 ++++++++++++++++--------- client/src/cache.js | 22 ++- client/src/loader.js | 257 ++++++++++++++++++++--------- client/src/views/fields/barcode.js | 49 +++++- 4 files changed, 385 insertions(+), 170 deletions(-) diff --git a/client/src/app.js b/client/src/app.js index 414a8c2094..be26c69718 100644 --- a/client/src/app.js +++ b/client/src/app.js @@ -92,86 +92,15 @@ define( this.useCache = options.useCache || this.useCache; this.apiUrl = options.apiUrl || this.apiUrl; this.basePath = options.basePath || ''; - this.ajaxTimeout = options.ajaxTimeout || 0; - this.appParams = {}; - - this.loader = Espo.loader; - - this.loader.basePath = this.basePath; - - this.controllers = {}; - - if (this.useCache) { - this.cache = new Cache(options.cacheTimestamp); - if (options.cacheTimestamp) { - this.cache.handleActuality(options.cacheTimestamp); - } else { - this.cache.storeTimestamp(); - } - } - - this.storage = new Storage(); - this.sessionStorage = new SessionStorage(); - - this.loader.cache = this.cache; - - if (this.useCache && !this.loader.cacheTimestamp && options.cacheTimestamp) { - this.loader.cacheTimestamp = options.cacheTimestamp; - } - - this.setupAjax(); - - this.settings = new Settings(null); - this.language = new Language(this.cache); - this.metadata = new Metadata(this.cache); - this.fieldManager = new FieldManager(); - - Promise.all([ - new Promise(function (resolve) { - this.settings.load(function () { - resolve(); - }); - }.bind(this)), - new Promise(function (resolve) { - this.language.load(function () { - resolve(); - }, false, true); - }.bind(this)) - ]).then(function () { - this.loader.addLibsConfig(this.settings.get('jsLibs') || {}); - - this.user = new User(); - - this.preferences = new Preferences(); - - this.preferences.settings = this.settings; - - this.acl = this.createAclManager(); - - this.fieldManager.acl = this.acl; - - this.themeManager = new ThemeManager(this.settings, this.preferences, this.metadata); - - this.modelFactory = new ModelFactory(this.loader, this.metadata, this.user); - - this.collectionFactory = new CollectionFactory(this.loader, this.modelFactory); - - if (this.settings.get('useWebSocket')) { - this.webSocketManager = new WebSocketManager(this.settings); - } - - this.initUtils(); - this.initView(); - this.initBaseController(); - - this.preLoader = new PreLoader(this.cache, this.viewFactory, this.basePath); - - this.preLoad(function () { - callback.call(this, this); - }); - }.bind(this)); + this + .initCache(options) + .then( + function () { + this.init(options, callback); + }.bind(this) + ); } _.extend(App.prototype, { @@ -216,12 +145,152 @@ define( masterView: 'views/site/master', + responseCache: null, + + initCache: function (options) { + var cacheTimestamp = options.cacheTimestamp || null; + var storedCacheTimestamp = null; + + if (this.useCache) { + this.cache = new Cache(cacheTimestamp); + + storedCacheTimestamp = this.cache.getCacheTimestamp(); + + if (cacheTimestamp) { + this.cache.handleActuality(cacheTimestamp); + } + else { + this.cache.storeTimestamp(); + } + } + + var handleActuality = function () { + if ( + !cacheTimestamp || + !storedCacheTimestamp || + cacheTimestamp !== storedCacheTimestamp + ) { + return caches.delete('espo'); + } + + return new Promise( + function (resolve) { + resolve(); + } + ); + }; + + return new Promise( + function (resolve) { + if (!this.useCache) { + resolve(); + } + + if (!window.caches) { + resolve(); + } + + handleActuality() + .then( + function () { + return caches.open('espo'); + } + ) + .then( + function (responseCache) { + this.responseCache = responseCache; + } + .bind(this) + ) + .then( + function () { + resolve(); + } + ) + } + .bind(this) + ); + }, + + init: function (options, callback) { + this.appParams = {}; + this.controllers = {}; + + this.loader = Espo.loader; + + this.loader.responseCache = this.responseCache; + + this.loader.basePath = this.basePath; + + this.storage = new Storage(); + this.sessionStorage = new SessionStorage(); + + this.loader.cache = this.cache; + + if (this.useCache && !this.loader.cacheTimestamp && options.cacheTimestamp) { + this.loader.cacheTimestamp = options.cacheTimestamp; + } + + this.setupAjax(); + + this.settings = new Settings(null); + this.language = new Language(this.cache); + this.metadata = new Metadata(this.cache); + this.fieldManager = new FieldManager(); + + Promise.all([ + new Promise(function (resolve) { + this.settings.load(function () { + resolve(); + }); + }.bind(this)), + new Promise(function (resolve) { + this.language.load(function () { + resolve(); + }, false, true); + }.bind(this)) + ]).then(function () { + this.loader.addLibsConfig(this.settings.get('jsLibs') || {}); + + this.user = new User(); + + this.preferences = new Preferences(); + + this.preferences.settings = this.settings; + + this.acl = this.createAclManager(); + + this.fieldManager.acl = this.acl; + + this.themeManager = new ThemeManager(this.settings, this.preferences, this.metadata); + + this.modelFactory = new ModelFactory(this.loader, this.metadata, this.user); + + this.collectionFactory = new CollectionFactory(this.loader, this.modelFactory); + + if (this.settings.get('useWebSocket')) { + this.webSocketManager = new WebSocketManager(this.settings); + } + + this.initUtils(); + this.initView(); + this.initBaseController(); + + this.preLoader = new PreLoader(this.cache, this.viewFactory, this.basePath); + + this.preLoad(function () { + callback.call(this, this); + }); + }.bind(this)); + }, + start: function () { this.initAuth(); if (!this.auth) { this.baseController.login(); - } else { + } + else { this.initUserData(null, function () { this.onAuth.call(this); }.bind(this)); diff --git a/client/src/cache.js b/client/src/cache.js index 1da6499b40..37b19bc95b 100644 --- a/client/src/cache.js +++ b/client/src/cache.js @@ -45,23 +45,29 @@ define('cache', [], function () { prefix: 'cache', handleActuality: function (cacheTimestamp) { - var stored = parseInt(this.get('app', 'cacheTimestamp')); + var storedTimestamp = this.getCacheTimestamp(); - if (stored) { - if (stored !== cacheTimestamp) { + if (storedTimestamp) { + if (storedTimestamp !== cacheTimestamp) { this.clear(); this.set('app', 'cacheTimestamp', cacheTimestamp); this.storeTimestamp(); } - } else { - this.clear(); - this.set('app', 'cacheTimestamp', cacheTimestamp); - - this.storeTimestamp(); + return; } + + this.clear(); + + this.set('app', 'cacheTimestamp', cacheTimestamp); + + this.storeTimestamp(); + }, + + getCacheTimestamp: function () { + return parseInt(this.get('app', 'cacheTimestamp') || 0); }, storeTimestamp: function () { diff --git a/client/src/loader.js b/client/src/loader.js index 0f4c94493e..f8fc1302d2 100644 --- a/client/src/loader.js +++ b/client/src/loader.js @@ -51,6 +51,8 @@ var Espo = Espo || {classMap:{}}; cache: null, + responseCache: null, + data: null, classMap: Espo, @@ -81,10 +83,12 @@ var Espo = Espo || {classMap:{}}; if (modulePart == 'custom') { path = 'client/custom/src/' + namePart; - } else { + } + else { path = 'client/modules/' + modulePart + '/src/' + namePart; } - } else { + } + else { path = 'client/src/' + name; } @@ -111,12 +115,14 @@ var Espo = Espo || {classMap:{}}; if (subject) { subject = this.normalizeClassName(subject); } + if (this.loadingSubject) { subject = subject || this.loadingSubject; this.loadingSubject = null; } var self = this; + var proceed = function (relObj) { var o = callback.apply(this, arguments); @@ -134,7 +140,8 @@ var Espo = Espo || {classMap:{}}; if (!dependency) { proceed(); - } else { + } + else { this.require(dependency, function () { proceed.apply(this, arguments); }); @@ -146,6 +153,7 @@ var Espo = Espo || {classMap:{}}; if (Object.prototype.toString.call(subject) === '[object Array]') { list = subject; + list.forEach(function (item, i) { list[i] = this.normalizeClassName(list[i]); }, this); @@ -162,20 +170,26 @@ var Espo = Espo || {classMap:{}}; if (totalCount === 1) { this.load(list[0], callback, errorCallback); + return; } else if (totalCount) { var readyCount = 0; var loaded = {}; + list.forEach(function (name) { this.load(name, function (c) { loaded[name] = c; + readyCount++; + if (readyCount == totalCount) { var args = []; + for (var i in list) { args.push(loaded[list[i]]); } + callback.apply(this, args); } }); @@ -206,7 +220,9 @@ var Espo = Espo || {classMap:{}}; var arr = name.split(':'); var modulePart = arr[0]; var namePart = arr[1]; - normalizedName = this.convertCamelCaseToHyphen(modulePart) + ':' + this.convertCamelCaseToHyphen(namePart).split('.').join('/'); + + normalizedName = this.convertCamelCaseToHyphen(modulePart) + ':' + + this.convertCamelCaseToHyphen(namePart).split('.').join('/'); } else { normalizedName = this.convertCamelCaseToHyphen(name).split('.').join('/'); } @@ -226,7 +242,8 @@ var Espo = Espo || {classMap:{}}; dataLoaded: {}, load: function (name, callback, errorCallback) { - var dataType, type, path, fetchObject; + var dataType, type, path, fetchObjectFunction; + var realName = name; var noAppCache = false; @@ -247,17 +264,17 @@ var Espo = Espo || {classMap:{}}; path = libData.path || path; exportsTo = libData.exportsTo || exportsTo; exportsAs = libData.exportsAs || exportsAs; - //noAppCache = libData.noAppCache || noAppCache; } noAppCache = true; - fetchObject = function () { + fetchObjectFunction = function () { var from = root; - if (exportsTo == 'window') { + if (exportsTo === 'window') { from = root; - } else { + } + else { exportsTo.split('.').forEach(function (item) { from = from[item]; }); @@ -268,7 +285,7 @@ var Espo = Espo || {classMap:{}}; } } - var obj = fetchObject(); + var obj = fetchObjectFunction(); if (obj) { callback(obj); @@ -308,40 +325,24 @@ var Espo = Espo || {classMap:{}}; return; } - if (this.cache) { + var dtObject = { + name: name, + type: type, + dataType: dataType, + realName: realName, + fetchObjectFunction: fetchObjectFunction, + noAppCache: noAppCache, + useCache: useCache, + path: path, + callback: callback, + errorCallback: errorCallback, + }; + + if (this.cache && !this.responseCache) { var cached = this.cache.get('a', name); if (cached) { - if (type == 'class') { - this.loadingSubject = name; - } - - if (dataType == 'script') { - this._execute(cached); - } - - if (type == 'class') { - var c = this._getClass(name); - - if (c) { - callback(c); - - return; - } - - this._addLoadCallback(name, callback); - } - else { - var d = cached; - - if (typeof fetchObject == 'function') { - d = fetchObject(realName, cached); - } - - this.dataLoaded[name] = d; - - callback(d); - } + this._processCached(dtObject, cached); return; } @@ -361,53 +362,114 @@ var Espo = Espo || {classMap:{}}; useCache = true; var sep = (path.indexOf('?') > -1) ? '&' : '?'; + path += sep + 'r=' + this.cacheTimestamp; } + var url = this.basePath + path; + + dtObject.path = path; + dtObject.url = url; + + if (!this.responseCache) { + this._processRequest(dtObject); + + return; + } + + this.responseCache + .match(new Request(url)) + .then( + function (response) { + if (!response) { + this._processRequest(dtObject); + + return + } + + response + .text() + .then( + function (cached) { + this._handleResponse(dtObject, cached); + } + .bind(this) + ); + } + .bind(this) + ); + }, + + _processCached: function (dtObject, cached) { + + var name = dtObject.name; + var callback = dtObject.callback; + var type = dtObject.type; + var dataType = dtObject.dataType; + var realName = dtObject.realName; + var fetchObjectFunction = dtObject.fetchObjectFunction; + + + if (type === 'class') { + this.loadingSubject = name; + } + + if (dataType === 'script') { + this._execute(cached); + } + + if (type === 'class') { + var classObj = this._getClass(name); + + if (classObj) { + callback(classObj); + + return; + } + + this._addLoadCallback(name, callback); + + return; + } + + var data = cached; + + if (typeof fetchObjectFunction === 'function') { + data = fetchObjectFunction(realName, cached); + } + + this.dataLoaded[name] = data; + + callback(data); + }, + + _processRequest: function (dtObject) { + + var name = dtObject.name; + var url = dtObject.url; + var errorCallback = dtObject.errorCallback; + var path = dtObject.path; + var useCache = dtObject.useCache; + var noAppCache = dtObject.noAppCache; + $.ajax({ type: 'GET', cache: useCache, dataType: 'text', mimeType: 'text/plain', local: true, - url: this.basePath + path, + url: url, success: function (response) { - if (this.cache && !noAppCache) { + if (this.cache && !noAppCache && !this.responseCache) { this.cache.set('a', name, response); } - this._addLoadCallback(name, callback); - - if (type == 'class') { - this.loadingSubject = name; + if (this.responseCache) { + this.responseCache.put(url, new Response(response)); } - if (dataType == 'script') { - this._execute(response); - } - - var data; - - if (type == 'class') { - data = this._getClass(name); - - if (data && typeof data === 'function') { - this._executeLoadCallback(name, data); - } - } - else { - data = response; - - if (typeof fetchObject == 'function') { - data = fetchObject(realName, response); - } - - this.dataLoaded[name] = data; - this._executeLoadCallback(name, data); - } - - return; + this._handleResponse(dtObject, response); } .bind(this), @@ -423,6 +485,47 @@ var Espo = Espo || {classMap:{}}; }); }, + _handleResponse: function (dtObject, response) { + + var name = dtObject.name; + var callback = dtObject.callback; + var type = dtObject.type; + var dataType = dtObject.dataType; + var realName = dtObject.realName; + var fetchObjectFunction = dtObject.fetchObjectFunction; + + this._addLoadCallback(name, callback); + + if (type === 'class') { + this.loadingSubject = name; + } + + if (dataType === 'script') { + this._execute(response); + } + + var data; + + if (type === 'class') { + data = this._getClass(name); + + if (data && typeof data === 'function') { + this._executeLoadCallback(name, data); + } + + return; + } + + data = response; + + if (typeof fetchObjectFunction === 'function') { + data = fetchObjectFunction(realName, response); + } + + this.dataLoaded[name] = data; + + this._executeLoadCallback(name, data); + }, loadLib: function (url, callback) { if (this.cache) { @@ -430,9 +533,11 @@ var Espo = Espo || {classMap:{}}; if (script) { this._execute(script); + if (typeof callback == 'function') { callback(); } + return; } } @@ -484,16 +589,20 @@ var Espo = Espo || {classMap:{}}; var subject = null; var dependency = null; var callback = null; + if (typeof arg1 === 'function') { callback = arg1; - } else if (typeof arg1 !== 'undefined' && typeof arg2 === 'function') { + } + else if (typeof arg1 !== 'undefined' && typeof arg2 === 'function') { dependency = arg1; callback = arg2; - } else { + } + else { subject = arg1; dependency = arg2; callback = arg3; } + Espo.loader.define(subject, dependency, callback); } diff --git a/client/src/views/fields/barcode.js b/client/src/views/fields/barcode.js index 45df7ce3e6..f8251b47a8 100644 --- a/client/src/views/fields/barcode.js +++ b/client/src/views/fields/barcode.js @@ -70,7 +70,10 @@ define('views/fields/barcode', [ Dep.prototype.setup.call(this); $(window).on('resize.' + this.cid, function () { - if (!this.isRendered()) return; + if (!this.isRendered()) { + return; + } + this.controlWidth(); }.bind(this)); }, @@ -84,16 +87,21 @@ define('views/fields/barcode', [ if (this.mode === 'list' || this.mode === 'detail') { var value = this.model.get(this.name); + if (value) { if (this.params.codeType === 'QRcode') { var size = 128; + if (this.isListMode()) { size = 64; } + var containerWidth = this.$el.width() ; + if (containerWidth < size && containerWidth) { size = containerWidth; } + var qrCode = new QRCode(this.$el.find('.barcode').get(0), { text: value, width: size, @@ -102,20 +110,43 @@ define('views/fields/barcode', [ colorLight : '#ffffff', correctLevel : QRCode.CorrectLevel.H, }); - } else { - JsBarcode(this.getSelector() + ' .barcode', value, { - format: this.params.codeType, - height: 50, - fontSize: 14, - margin: 0, - lastChar: this.params.lastChar, - }); + } + else { + var $barcode = $(this.getSelector() + ' .barcode'); + + if ($barcode.length) { + this.initBarcode(value); + } + else { + // SVG may be not available yet (in webkit). + setTimeout( + function () { + this.initBarcode(value); + + this.controlWidth(); + } + .bind(this), + 100 + ); + } + } } + this.controlWidth(); } }, + initBarcode: function (value) { + JsBarcode(this.getSelector() + ' .barcode', value, { + format: this.params.codeType, + height: 50, + fontSize: 14, + margin: 0, + lastChar: this.params.lastChar, + }); + }, + controlWidth: function () { this.$el.find('.barcode').css('max-width', this.$el.width() + 'px'); },