From f85c55f4a40548922e368188ba640abc1051b131 Mon Sep 17 00:00:00 2001 From: cdujeu Date: Fri, 1 May 2015 09:53:59 +0200 Subject: [PATCH] More JS modules async loading to avoid loading scripts when they are not necessary --- .../src/plugins/access.ajxp_user/manifest.xml | 4 +- .../plugins/action.cart/class.CartManager.js | 2 +- core/src/plugins/action.cart/manifest.xml | 2 +- .../plugins/action.share/class.ShareCenter.js | 32 +- core/src/plugins/action.share/manifest.xml | 56 +- .../plugins/authfront.keystore/manifest.xml | 2 +- core/src/plugins/core.mailer/manifest.xml | 3 - .../plugins/core.notifications/manifest.xml | 1 + core/src/plugins/editor.eml/manifest.xml | 2 +- core/src/plugins/editor.etherpad/manifest.xml | 2 +- core/src/plugins/editor.webodf/manifest.xml | 2 +- .../res/js/core/http/ResourcesManager.js | 750 ++++++++++-------- .../gui.ajax/res/js/core/model/Repository.js | 2 +- .../gui.ajax/res/js/core/util/XMLUtils.js | 17 +- .../res/js/es6/http/ResourcesManager.es6 | 111 ++- .../gui.ajax/res/js/es6/model/Repository.es6 | 2 +- .../gui.ajax/res/js/es6/util/XMLUtils.es6 | 13 +- .../res/js/ui/prototype/class.AjxpPane.js | 59 +- .../js/ui/prototype/class.AjxpTabulator.js | 33 +- .../res/js/ui/prototype/class.FilesList.js | 9 +- .../res/js/ui/prototype/class.InfoPanel.js | 16 +- .../res/js/ui/prototype/class.LogoWidget.js | 2 +- .../res/js/ui/prototype/class.PydioUI.js | 117 ++- .../res/js/ui/prototype/class.SearchEngine.js | 18 +- .../res/js/ui/prototype/util/ajxp_utils.js | 4 +- core/src/plugins/meta.comments/manifest.xml | 2 +- core/src/plugins/meta.exif/manifest.xml | 2 +- core/src/plugins/meta.git/manifest.xml | 24 +- core/src/plugins/meta.svn/manifest.xml | 4 +- core/src/plugins/meta.user/manifest.xml | 78 +- 30 files changed, 875 insertions(+), 496 deletions(-) diff --git a/core/src/plugins/access.ajxp_user/manifest.xml b/core/src/plugins/access.ajxp_user/manifest.xml index 86726823bf..28333201a9 100644 --- a/core/src/plugins/access.ajxp_user/manifest.xml +++ b/core/src/plugins/access.ajxp_user/manifest.xml @@ -4,8 +4,8 @@ - - + + diff --git a/core/src/plugins/action.cart/class.CartManager.js b/core/src/plugins/action.cart/class.CartManager.js index 20003639bd..0b77ef3394 100644 --- a/core/src/plugins/action.cart/class.CartManager.js +++ b/core/src/plugins/action.cart/class.CartManager.js @@ -284,6 +284,6 @@ Class.create("CartManager", FetchedResultPane, { if(window.ajxpMinisite){ document.observe("ajaxplorer:actions_loaded", function(){ - pydio.getController().actions.unset("send-selection-to-cart"); + pydio.getController().actions.delete("send-selection-to-cart"); }); } diff --git a/core/src/plugins/action.cart/manifest.xml b/core/src/plugins/action.cart/manifest.xml index 249b1c5375..b98c6a8e72 100644 --- a/core/src/plugins/action.cart/manifest.xml +++ b/core/src/plugins/action.cart/manifest.xml @@ -8,7 +8,7 @@ - + diff --git a/core/src/plugins/action.share/class.ShareCenter.js b/core/src/plugins/action.share/class.ShareCenter.js index 0410119f58..004c4b6216 100644 --- a/core/src/plugins/action.share/class.ShareCenter.js +++ b/core/src/plugins/action.share/class.ShareCenter.js @@ -1206,20 +1206,22 @@ Class.create("ShareCenter", { if(dialogButtonsOrRow.down('.share_qrcode') && dialogButtonsOrRow.down('.share_qrcode').visible() && dialogButtonsOrRow.down('.share_qrcode > img')){ message += "\n\n "+ MessageHash["share_center.108"] + "\n\n" + dialogButtonsOrRow.down('.share_qrcode').innerHTML; } - var mailer = new AjxpMailer(); var usersList = null; if(this.shareFolderMode == 'workspace' && oForm) { usersList = oForm.down(".editable_users_list"); } - modal.showSimpleModal( - mailerShower, - mailer.buildMailPane(MessageHash["share_center.44"].replace("%s", ajaxplorer.appTitle), message, usersList, MessageHash["share_center.45"], link), - function(){ - mailer.postEmail(); - return true; - },function(){ - return true; - }); + ResourcesManager.detectModuleToLoadAndApply('AjxpMailer', function(){ + var mailer = new AjxpMailer(); + modal.showSimpleModal( + mailerShower, + mailer.buildMailPane(MessageHash["share_center.44"].replace("%s", ajaxplorer.appTitle), message, usersList, MessageHash["share_center.45"], link), + function(){ + mailer.postEmail(); + return true; + },function(){ + return true; + }); + }); }.bind(this)); }else{ var subject = encodeURIComponent(MessageHash["share_center.44"].replace("%s", ajaxplorer.appTitle)); @@ -1269,11 +1271,13 @@ Class.create("ShareCenter", { }else{ url = this._currentRepositoryLink; } - var qrcode = new QRCode(randId, { - width: 128, - height: 128 + ResourcesManager.detectModuleToLoadAndApply('QRCode', function(){ + var qrcode = new QRCode(randId, { + width: 128, + height: 128 + }); + qrcode.makeCode(url); }); - qrcode.makeCode(url); } if(qrcodediv.visible()) qrcodediv.hide(); else qrcodediv.show(); diff --git a/core/src/plugins/action.share/manifest.xml b/core/src/plugins/action.share/manifest.xml index f49004176c..361b76725b 100644 --- a/core/src/plugins/action.share/manifest.xml +++ b/core/src/plugins/action.share/manifest.xml @@ -31,8 +31,8 @@ - - + + @@ -45,8 +45,12 @@ @@ -271,8 +275,12 @@ @@ -283,8 +291,12 @@ @@ -295,8 +307,12 @@ @@ -307,8 +323,12 @@ @@ -369,10 +389,14 @@ diff --git a/core/src/plugins/authfront.keystore/manifest.xml b/core/src/plugins/authfront.keystore/manifest.xml index 4f11daaafd..86938123c0 100644 --- a/core/src/plugins/authfront.keystore/manifest.xml +++ b/core/src/plugins/authfront.keystore/manifest.xml @@ -5,7 +5,7 @@ - + diff --git a/core/src/plugins/core.mailer/manifest.xml b/core/src/plugins/core.mailer/manifest.xml index 70b93c0a63..791a2d6e75 100755 --- a/core/src/plugins/core.mailer/manifest.xml +++ b/core/src/plugins/core.mailer/manifest.xml @@ -22,9 +22,6 @@ - diff --git a/core/src/plugins/core.notifications/manifest.xml b/core/src/plugins/core.notifications/manifest.xml index 9d49b9f930..2c3a526cc4 100644 --- a/core/src/plugins/core.notifications/manifest.xml +++ b/core/src/plugins/core.notifications/manifest.xml @@ -11,6 +11,7 @@ + diff --git a/core/src/plugins/editor.eml/manifest.xml b/core/src/plugins/editor.eml/manifest.xml index f3f94b5330..23c16f31fc 100644 --- a/core/src/plugins/editor.eml/manifest.xml +++ b/core/src/plugins/editor.eml/manifest.xml @@ -3,7 +3,7 @@ - + diff --git a/core/src/plugins/editor.etherpad/manifest.xml b/core/src/plugins/editor.etherpad/manifest.xml index 0e62537f03..67f617b7aa 100644 --- a/core/src/plugins/editor.etherpad/manifest.xml +++ b/core/src/plugins/editor.etherpad/manifest.xml @@ -5,7 +5,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:../core.ajaxplorer/ajxp_registry.xsd"> - + diff --git a/core/src/plugins/editor.webodf/manifest.xml b/core/src/plugins/editor.webodf/manifest.xml index cdf5606ca5..7423fdf35e 100644 --- a/core/src/plugins/editor.webodf/manifest.xml +++ b/core/src/plugins/editor.webodf/manifest.xml @@ -5,7 +5,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:../core.ajaxplorer/ajxp_registry.xsd"> - + diff --git a/core/src/plugins/gui.ajax/res/js/core/http/ResourcesManager.js b/core/src/plugins/gui.ajax/res/js/core/http/ResourcesManager.js index 306ca8f770..dd263f8910 100644 --- a/core/src/plugins/gui.ajax/res/js/core/http/ResourcesManager.js +++ b/core/src/plugins/gui.ajax/res/js/core/http/ResourcesManager.js @@ -29,316 +29,442 @@ var _createClass = (function () { function defineProperties(target, props) { for */ var ResourcesManager = (function () { - /** - * Constructor - */ - - function ResourcesManager() { - _classCallCheck(this, ResourcesManager); - - this.mainFormContainerId = 'all_forms'; - this.resources = {}; - this.loaded = false; - } - - _createClass(ResourcesManager, [{ - key: 'addJSResource', - - /** - * Adds a Javascript resource - * @param fileName String - * @param className String - */ - value: function addJSResource(fileName, className) { - if (!this.resources.js) { - this.resources.js = []; - } - this.resources.js.push({ fileName: fileName, className: className }); - } - }, { - key: 'addCSSResource', - - /** - * Adds a CSS resource - * @param fileName String - */ - value: function addCSSResource(fileName) { - if (!this.resources.css) { - this.resources.css = []; - } - this.resources.css.push(fileName); - } - }, { - key: 'addGuiForm', - - /** - * Adds a FORM from html snipper - * @param formId String - * @param htmlSnippet String - */ - value: function addGuiForm(formId, htmlSnippet) { - if (!this.resources.forms) { - this.resources.forms = new Map(); - } - this.resources.forms.set(formId, htmlSnippet); - } - }, { - key: 'addDependency', - - /** - * Add a dependency to another plugin - * @param data Object - */ - value: function addDependency(data) { - if (!this.resources.dependencies) { - this.resources.dependencies = []; - } - this.resources.dependencies.push(data); - } - }, { - key: 'hasDependencies', - - /** - * Check if some dependencies must be loaded before - * @returns Boolean - */ - value: function hasDependencies() { - return this.resources.dependencies || false; - } - }, { - key: 'load', - - /** - * Load resources - * @param resourcesRegistry $H Ajaxplorer ressources registry - */ - value: function load(resourcesRegistry) { - if (this.loaded) { - return; - }if (this.hasDependencies()) { - this.resources.dependencies.forEach((function (el) { - if (resourcesRegistry[el]) { - resourcesRegistry[el].load(resourcesRegistry); - } - }).bind(this)); - } - if (this.resources.forms) { - this.resources.forms.forEach((function (value, key) { - this.loadGuiForm(key, value); - }).bind(this)); - } - if (this.resources.js) { - this.resources.js.forEach((function (value) { - this.loadJSResource(value.fileName, value.className); - }).bind(this)); - } - if (this.resources.css) { - this.resources.css.forEach((function (value) { - this.loadCSSResource(value); - }).bind(this)); - } - this.loaded = true; - } - }, { - key: 'loadJSResource', - - /** - * Load a javascript file - * @param fileName String - * @param className String - * @param callback Function - * @param aSync Boolean - */ - value: function loadJSResource(fileName, className, callback, aSync) { - - if (!window[className]) { - if (typeof className != 'function' || typeof className.prototype != 'object') { - PydioApi.loadLibrary(fileName, callback, aSync); - } - } else if (callback) { - callback(); - } - } - }, { - key: 'loadCSSResource', - - /** - * Load a CSS file - * @param fileName String - */ - value: function loadCSSResource(fileName) { - - var head = document.getElementsByTagName('head')[0]; - if (head && head.down) { - if (pydio.Parameters.get('SERVER_PREFIX_URI')) { - fileName = pydio.Parameters.get('SERVER_PREFIX_URI') + fileName; - } - fileName = fileName + '?v=' + pydio.Parameters.get('ajxpVersion'); - // WARNING PROTOTYPE STUFF - var select = head.down('[href="' + fileName + '"]'); - if (!select) { - var cssNode = new Element('link', { - type: 'text/css', - rel: 'stylesheet', - href: fileName, - media: 'screen' - }); - head.insert(cssNode); - } - } - } - }, { - key: 'loadGuiForm', - - /** - * Insert the HTML snipper and evaluate scripts - * @param formId String - * @param htmlSnippet String - */ - value: function loadGuiForm(formId, htmlSnippet) { - if (!$(this.mainFormContainerId).select('[id="' + formId + '"]').length) { - // TODO - PROTOTYPE STUFF - htmlSnippet.evalScripts(); - $(this.mainFormContainerId).insert(htmlSnippet.stripScripts()); - } - } - }, { - key: 'loadFromXmlNode', - - /** - * Load the resources from XML - * @param node XMLNode - */ - value: function loadFromXmlNode(node) { - var clForm = {}; - var k; - if (node.nodeName == 'resources') { - for (k = 0; k < node.childNodes.length; k++) { - if (node.childNodes[k].nodeName == 'js') { - this.addJSResource(node.childNodes[k].getAttribute('file'), node.childNodes[k].getAttribute('className')); - } else if (node.childNodes[k].nodeName == 'css') { - this.addCSSResource(node.childNodes[k].getAttribute('file')); - } else if (node.childNodes[k].nodeName == 'img_library') { - ResourcesManager.addImageLibrary(node.childNodes[k].getAttribute('alias'), node.childNodes[k].getAttribute('path')); - } - } - } else if (node.nodeName == 'dependencies') { - for (k = 0; k < node.childNodes.length; k++) { - if (node.childNodes[k].nodeName == 'pluginResources') { - this.addDependency(node.childNodes[k].getAttribute('pluginName')); - } - } - } else if (node.nodeName == 'clientForm') { - if (!node.getAttribute('theme') || node.getAttribute('theme') == pydio.Parameters.get('theme')) { - clForm = { formId: node.getAttribute('id'), formCode: node.firstChild.nodeValue }; - } - } - if (clForm.formId) { - this.addGuiForm(clForm.formId, clForm.formCode); - } - } - }], [{ - key: 'addImageLibrary', - - /** - * - * @param aliasName - * @param aliasPath - * @todo MOVE OUTSIDE? - */ - value: function addImageLibrary(aliasName, aliasPath) { - if (!window.AjxpImageLibraries) window.AjxpImageLibraries = {}; - window.AjxpImageLibraries[aliasName] = aliasPath; - } - }, { - key: 'loadAutoLoadResources', - - /** - * Check if resources are tagged autoload and load them - * @param registry DOMDocument XML Registry - */ - value: function loadAutoLoadResources(registry) { - var manager = new ResourcesManager(); - var jsNodes = XMLUtils.XPathSelectNodes(registry, 'plugins/*/client_settings/resources/js[@autoload="true"]'); - var node; - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = jsNodes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - node = _step.value; - - manager.loadJSResource(node.getAttribute('file'), node.getAttribute('className')); - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator['return']) { - _iterator['return'](); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - - var imgNodes = XMLUtils.XPathSelectNodes(registry, 'plugins/*/client_settings/resources/img_library'); - var _iteratorNormalCompletion2 = true; - var _didIteratorError2 = false; - var _iteratorError2 = undefined; - - try { - for (var _iterator2 = imgNodes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { - node = _step2.value; - - ResourcesManager.addImageLibrary(node.getAttribute('alias'), node.getAttribute('path')); - } - } catch (err) { - _didIteratorError2 = true; - _iteratorError2 = err; - } finally { - try { - if (!_iteratorNormalCompletion2 && _iterator2['return']) { - _iterator2['return'](); - } - } finally { - if (_didIteratorError2) { - throw _iteratorError2; - } - } - } - - var cssNodes = XMLUtils.XPathSelectNodes(registry, 'plugins/*/client_settings/resources/css[@autoload="true"]'); - var _iteratorNormalCompletion3 = true; - var _didIteratorError3 = false; - var _iteratorError3 = undefined; - - try { - for (var _iterator3 = cssNodes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { - node = _step3.value; - - manager.loadCSSResource(node.getAttribute('file')); - } - } catch (err) { - _didIteratorError3 = true; - _iteratorError3 = err; - } finally { - try { - if (!_iteratorNormalCompletion3 && _iterator3['return']) { - _iterator3['return'](); - } - } finally { - if (_didIteratorError3) { - throw _iteratorError3; - } - } - } - } - }]); - - return ResourcesManager; + /** + * Constructor + */ + + function ResourcesManager() { + _classCallCheck(this, ResourcesManager); + + this.mainFormContainerId = 'all_forms'; + this.resources = {}; + this.loaded = false; + } + + _createClass(ResourcesManager, [{ + key: 'addJSResource', + + /** + * Adds a Javascript resource + * @param fileName String + * @param className String + */ + value: function addJSResource(fileName, className) { + var autoload = arguments[2] === undefined ? false : arguments[2]; + + if (!this.resources.js) { + this.resources.js = []; + } + this.resources.js.push({ + fileName: fileName, + className: className, + autoload: false + }); + } + }, { + key: 'addCSSResource', + + /** + * Adds a CSS resource + * @param fileName String + */ + value: function addCSSResource(fileName) { + if (!this.resources.css) { + this.resources.css = []; + } + this.resources.css.push(fileName); + } + }, { + key: 'addGuiForm', + + /** + * Adds a FORM from html snipper + * @param formId String + * @param htmlSnippet String + */ + value: function addGuiForm(formId, htmlSnippet) { + if (!this.resources.forms) { + this.resources.forms = new Map(); + } + this.resources.forms.set(formId, htmlSnippet); + } + }, { + key: 'addDependency', + + /** + * Add a dependency to another plugin + * @param data Object + */ + value: function addDependency(data) { + if (!this.resources.dependencies) { + this.resources.dependencies = []; + } + this.resources.dependencies.push(data); + } + }, { + key: 'hasDependencies', + + /** + * Check if some dependencies must be loaded before + * @returns Boolean + */ + value: function hasDependencies() { + return this.resources.dependencies || false; + } + }, { + key: 'load', + + /** + * Load resources + * @param resourcesRegistry $H Ajaxplorer ressources registry + */ + value: function load(resourcesRegistry) { + var jsAutoloadOnly = arguments[1] === undefined ? false : arguments[1]; + + if (this.loaded) { + return; + }if (this.hasDependencies()) { + this.resources.dependencies.forEach((function (el) { + if (resourcesRegistry[el]) { + resourcesRegistry[el].load(resourcesRegistry); + } + }).bind(this)); + } + if (this.resources.forms) { + this.resources.forms.forEach((function (value, key) { + this.loadGuiForm(key, value); + }).bind(this)); + } + if (this.resources.js) { + this.resources.js.forEach((function (value) { + if (jsAutoloadOnly && !value.autoload) return; + this.loadJSResource(value.fileName, value.className); + }).bind(this)); + } + if (this.resources.css) { + this.resources.css.forEach((function (value) { + this.loadCSSResource(value); + }).bind(this)); + } + this.loaded = true; + } + }, { + key: 'loadJSResource', + + /** + * Load a javascript file + * @param fileName String + * @param className String + * @param callback Function + * @param aSync Boolean + */ + value: function loadJSResource(fileName, className, callback, aSync) { + + if (!window[className]) { + if (typeof className != 'function' || typeof className.prototype != 'object') { + PydioApi.loadLibrary(fileName, callback, aSync); + } + } else if (callback) { + callback(); + } + } + }, { + key: 'loadWebComponents', + value: function loadWebComponents(fileNames, callback) { + if (!Polymer) { + throw Error('Cannot find Polymer library!'); + } + Polymer['import'](fileNames, callback); + } + }, { + key: 'loadCSSResource', + + /** + * Load a CSS file + * @param fileName String + */ + value: function loadCSSResource(fileName) { + + var head = document.getElementsByTagName('head')[0]; + if (head && head.down) { + if (pydio.Parameters.get('SERVER_PREFIX_URI')) { + fileName = pydio.Parameters.get('SERVER_PREFIX_URI') + fileName; + } + fileName = fileName + '?v=' + pydio.Parameters.get('ajxpVersion'); + // WARNING PROTOTYPE STUFF + var select = head.down('[href="' + fileName + '"]'); + if (!select) { + var cssNode = new Element('link', { + type: 'text/css', + rel: 'stylesheet', + href: fileName, + media: 'screen' + }); + head.insert(cssNode); + } + } + } + }, { + key: 'loadGuiForm', + + /** + * Insert the HTML snipper and evaluate scripts + * @param formId String + * @param htmlSnippet String + */ + value: function loadGuiForm(formId, htmlSnippet) { + if (!$(this.mainFormContainerId).select('[id="' + formId + '"]').length) { + // TODO - PROTOTYPE STUFF + htmlSnippet.evalScripts(); + $(this.mainFormContainerId).insert(htmlSnippet.stripScripts()); + } + } + }, { + key: 'loadFromXmlNode', + + /** + * Load the resources from XML + * @param node XMLNode + */ + value: function loadFromXmlNode(node) { + var clForm = {}; + var k; + if (node.nodeName == 'resources') { + for (k = 0; k < node.childNodes.length; k++) { + if (node.childNodes[k].nodeName == 'js') { + this.addJSResource(node.childNodes[k].getAttribute('file'), node.childNodes[k].getAttribute('className'), node.childNodes[k].getAttribute('autoload') === true); + } else if (node.childNodes[k].nodeName == 'css') { + this.addCSSResource(node.childNodes[k].getAttribute('file')); + } else if (node.childNodes[k].nodeName == 'img_library') { + ResourcesManager.addImageLibrary(node.childNodes[k].getAttribute('alias'), node.childNodes[k].getAttribute('path')); + } + } + } else if (node.nodeName == 'dependencies') { + for (k = 0; k < node.childNodes.length; k++) { + if (node.childNodes[k].nodeName == 'pluginResources') { + this.addDependency(node.childNodes[k].getAttribute('pluginName')); + } + } + } else if (node.nodeName == 'clientForm') { + if (!node.getAttribute('theme') || node.getAttribute('theme') == pydio.Parameters.get('theme')) { + clForm = { formId: node.getAttribute('id'), formCode: node.firstChild.nodeValue }; + } + } + if (clForm.formId) { + this.addGuiForm(clForm.formId, clForm.formCode); + } + } + }], [{ + key: 'addImageLibrary', + + /** + * + * @param aliasName + * @param aliasPath + * @todo MOVE OUTSIDE? + */ + value: function addImageLibrary(aliasName, aliasPath) { + if (!window.AjxpImageLibraries) window.AjxpImageLibraries = {}; + window.AjxpImageLibraries[aliasName] = aliasPath; + } + }, { + key: 'loadAutoLoadResources', + + /** + * Check if resources are tagged autoload and load them + * @param registry DOMDocument XML Registry + */ + value: function loadAutoLoadResources(registry) { + var manager = new ResourcesManager(); + var jsNodes = XMLUtils.XPathSelectNodes(registry, 'plugins/*/client_settings/resources/js'); + var node; + ResourcesManager.__modules = new Map(); + ResourcesManager.__components = new Map(); + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = jsNodes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + node = _step.value; + + ResourcesManager.__modules.set(node.getAttribute('className'), node.getAttribute('file')); + if (node.getAttribute('autoload') === 'true') { + manager.loadJSResource(node.getAttribute('file'), node.getAttribute('className'), null, false); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator['return']) { + _iterator['return'](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + var compNodes = XMLUtils.XPathSelectNodes(registry, 'plugins/*/client_settings/resources/component'); + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = compNodes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + node = _step2.value; + + ResourcesManager.__components.set(node.getAttribute('componentName'), node.getAttribute('file')); + if (node.getAttribute('autoload') === 'true') { + manager.loadWebComponents([node.getAttribute('file')]); + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2['return']) { + _iterator2['return'](); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + var imgNodes = XMLUtils.XPathSelectNodes(registry, 'plugins/*/client_settings/resources/img_library'); + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; + + try { + for (var _iterator3 = imgNodes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + node = _step3.value; + + ResourcesManager.addImageLibrary(node.getAttribute('alias'), node.getAttribute('path')); + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3['return']) { + _iterator3['return'](); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } + } + + var cssNodes = XMLUtils.XPathSelectNodes(registry, 'plugins/*/client_settings/resources/css[@autoload="true"]'); + var _iteratorNormalCompletion4 = true; + var _didIteratorError4 = false; + var _iteratorError4 = undefined; + + try { + for (var _iterator4 = cssNodes[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { + node = _step4.value; + + manager.loadCSSResource(node.getAttribute('file')); + } + } catch (err) { + _didIteratorError4 = true; + _iteratorError4 = err; + } finally { + try { + if (!_iteratorNormalCompletion4 && _iterator4['return']) { + _iterator4['return'](); + } + } finally { + if (_didIteratorError4) { + throw _iteratorError4; + } + } + } + } + }, { + key: 'loadClassesAndApply', + value: function loadClassesAndApply(classNames, callbackFunc) { + if (!ResourcesManager.__modules) { + ResourcesManager.loadAutoLoadResources(pydio.Registry.getXML()); + } + var modules = []; + classNames.forEach(function (c) { + if (!window[c] && ResourcesManager.__modules.has(c)) { + modules.push({ + className: c, + fileName: ResourcesManager.__modules.get(c), + require: ResourcesManager.__modules.get(c).replace('.js', '') + }); + } + }); + if (!modules.length) { + callbackFunc(); + return; + } + if (modules.length == 1) { + ResourcesManager.detectModuleToLoadAndApply(modules[0].className, callbackFunc); + return; + } + if (window.requirejs) { + // Let require handle multiple async + var requires = []; + modules.forEach(function (e) { + requires.push(e.require); + }); + requirejs(requires, callbackFunc); + } else { + // Load sync and apply the callback manually + var loader = new ResourcesManager(); + modules.forEach(function (element) { + loader.loadJSResource(element.fileName, element.className, null, false); + }); + callbackFunc(); + } + } + }, { + key: 'detectModuleToLoadAndApply', + value: function detectModuleToLoadAndApply(callbackString, callbackFunc) { + if (!ResourcesManager.__modules) { + ResourcesManager.loadAutoLoadResources(pydio.Registry.getXML()); + } + var className = callbackString.split('.', 1).shift(); + if (!window[className] && ResourcesManager.__modules.has(className)) { + if (window.requirejs) { + requirejs([ResourcesManager.__modules.get(className).replace('.js', '')], callbackFunc); + } else { + var loader = new ResourcesManager(); + loader.loadJSResource(ResourcesManager.__modules.get(className), className, callbackFunc, true); + } + } else { + callbackFunc(); + } + } + }, { + key: 'loadWebComponentsAndApply', + value: function loadWebComponentsAndApply(componentsList, callbackFunc) { + if (!ResourcesManager.__modules) { + ResourcesManager.loadAutoLoadResources(pydio.Registry.getXML()); + } + var files = []; + componentsList.forEach(function (v) { + if (ResourcesManager.__components.has(v)) { + files.push(ResourcesManager.__components.get(v)); + } + }); + if (files.length) { + var manager = new ResourcesManager(); + manager.loadWebComponents(files, callbackFunc); + } + } + }]); + + return ResourcesManager; })(); \ No newline at end of file diff --git a/core/src/plugins/gui.ajax/res/js/core/model/Repository.js b/core/src/plugins/gui.ajax/res/js/core/model/Repository.js index a513e8371c..da6ee3f5fd 100644 --- a/core/src/plugins/gui.ajax/res/js/core/model/Repository.js +++ b/core/src/plugins/gui.ajax/res/js/core/model/Repository.js @@ -156,7 +156,7 @@ var Repository = (function () { * Triggers ResourcesManager.load */ value: function loadResources() { - this.resourcesManager.load(); + this.resourcesManager.load(null, true); } }, { key: 'getNodeProviderDef', diff --git a/core/src/plugins/gui.ajax/res/js/core/util/XMLUtils.js b/core/src/plugins/gui.ajax/res/js/core/util/XMLUtils.js index 27f0d4cbd0..c1599ef285 100644 --- a/core/src/plugins/gui.ajax/res/js/core/util/XMLUtils.js +++ b/core/src/plugins/gui.ajax/res/js/core/util/XMLUtils.js @@ -94,7 +94,11 @@ var XMLUtils = (function () { result = query.evaluate(element, 7, null); nodes = []; for (i = 0; i < result.snapshotLength; i++) { - nodes[i] = Element.extend(result.snapshotItem(i)); + if (Element.extend) { + nodes[i] = Element.extend(result.snapshotItem(i)); + } else { + nodes[i] = result.snapshotItem(i); + } } return nodes; } @@ -129,6 +133,8 @@ var XMLUtils = (function () { }, { key: "getDomNodeText", value: function getDomNodeText(node) { + var includeCData = arguments[1] === undefined ? false : arguments[1]; + if (!node || !node.nodeType) { return null; } @@ -141,7 +147,7 @@ var XMLUtils = (function () { nodes = node.childNodes, length = nodes.length; for (i = 0; i < length; i++) { - a[i] = XMLUtils.getDomNodeText(nodes[i]); + a[i] = XMLUtils.getDomNodeText(nodes[i], includeCData); } return a.join(""); @@ -155,7 +161,12 @@ var XMLUtils = (function () { // NODE_TEXT return node.nodeValue; break; - } + + case 4: + // CDATA + if (includeCData) { + return node.nodeValue; + }} return null; } diff --git a/core/src/plugins/gui.ajax/res/js/es6/http/ResourcesManager.es6 b/core/src/plugins/gui.ajax/res/js/es6/http/ResourcesManager.es6 index 41c0848c29..6230277533 100644 --- a/core/src/plugins/gui.ajax/res/js/es6/http/ResourcesManager.es6 +++ b/core/src/plugins/gui.ajax/res/js/es6/http/ResourcesManager.es6 @@ -35,11 +35,15 @@ class ResourcesManager{ * @param fileName String * @param className String */ - addJSResource(fileName, className){ + addJSResource(fileName, className, autoload=false){ if(!this.resources.js){ this.resources.js = []; } - this.resources.js.push({fileName:fileName,className:className}); + this.resources.js.push({ + fileName:fileName, + className:className, + autoload:false + }); } /** * Adds a CSS resource @@ -83,7 +87,7 @@ class ResourcesManager{ * Load resources * @param resourcesRegistry $H Ajaxplorer ressources registry */ - load(resourcesRegistry){ + load(resourcesRegistry, jsAutoloadOnly=false){ if(this.loaded) return; if(this.hasDependencies()){ this.resources.dependencies.forEach(function(el){ @@ -99,6 +103,7 @@ class ResourcesManager{ } if(this.resources.js){ this.resources.js.forEach(function(value){ + if(jsAutoloadOnly && !value.autoload) return; this.loadJSResource(value.fileName, value.className); }.bind(this)); } @@ -126,6 +131,14 @@ class ResourcesManager{ callback(); } } + + loadWebComponents(fileNames, callback){ + if(!Polymer){ + throw Error('Cannot find Polymer library!'); + } + Polymer.import(fileNames, callback); + } + /** * Load a CSS file * @param fileName String @@ -173,7 +186,11 @@ class ResourcesManager{ if(node.nodeName == "resources"){ for(k=0;k parseInt(this.htmlElement.getHeight())){ + if(this.htmlElement.getHeight() && imgH > parseInt(this.htmlElement.getHeight())){ var elPadding = parseInt(this.htmlElement.getStyle('paddingTop')) + (imgH - parseInt(this.htmlElement.getHeight())); if(ajxpBootstrap.parameters.get("theme") == 'orbit'){ elPadding += 9; diff --git a/core/src/plugins/gui.ajax/res/js/ui/prototype/class.PydioUI.js b/core/src/plugins/gui.ajax/res/js/ui/prototype/class.PydioUI.js index 0b6fd58212..5fcb41f062 100644 --- a/core/src/plugins/gui.ajax/res/js/ui/prototype/class.PydioUI.js +++ b/core/src/plugins/gui.ajax/res/js/ui/prototype/class.PydioUI.js @@ -19,6 +19,8 @@ */ Class.create("PydioUI", { + __ajxpClassRegexp: new RegExp(/ajxpClass="([0-9a-zA-Z]+)"/g), + initialize: function(pydioObject){ this._pydio = pydioObject; @@ -178,29 +180,38 @@ Class.create("PydioUI", { /*********************/ this.guiLoaded = false; var mainElement = $(ajxpBootstrap.parameters.get('MAIN_ELEMENT')); - this.buildGUI(mainElement); - document.fire("ajaxplorer:before_gui_load"); - // Rewind components creation! - if(this._guiCompRegistry){ - this.initAjxpWidgets(this._guiCompRegistry); - } - this.guiLoaded = true; - document.fire("ajaxplorer:gui_loaded"); - document.observe("ajaxplorer:repository_list_refreshed", function(e){ - mainElement.classNames().findAll(function(c){ return c.startsWith('ajxp_ws-'); }).each(function(cName){ - mainElement.removeClassName(cName); - }); - if(e.memo.active && e.memo.list){ - mainElement.addClassName('ajxp_ws-' + e.memo.list.get(e.memo.active).getSlug()); - } + var classNames = $H(); + mainElement.select('[ajxpClass]').each(function(el){ + classNames.set(el.readAttribute('ajxpClass'), true); }); - modal.updateLoadingProgress('GUI Initialized'); - this.initTabNavigation(); - this.blockShortcuts = false; - this.blockNavigation = false; - // TODO : ADD TO XML TEMPLATES INSTEAD - this.bgManagerPane = new BackgroundManagerPane(); - modal.updateLoadingProgress('Navigation loaded'); + classNames = classNames.keys(); + + ResourcesManager.loadClassesAndApply(classNames, function(){ + this.buildGUI(mainElement); + document.fire("ajaxplorer:before_gui_load"); + // Rewind components creation! + if(this._guiCompRegistry){ + this.initAjxpWidgets(this._guiCompRegistry); + } + this.guiLoaded = true; + document.fire("ajaxplorer:gui_loaded"); + document.observe("ajaxplorer:repository_list_refreshed", function(e){ + mainElement.classNames().findAll(function(c){ return c.startsWith('ajxp_ws-'); }).each(function(cName){ + mainElement.removeClassName(cName); + }); + if(e.memo.active && e.memo.list){ + mainElement.addClassName('ajxp_ws-' + e.memo.list.get(e.memo.active).getSlug()); + } + }); + modal.updateLoadingProgress('GUI Initialized'); + this.initTabNavigation(); + this.blockShortcuts = false; + this.blockNavigation = false; + // TODO : ADD TO XML TEMPLATES INSTEAD + this.bgManagerPane = new BackgroundManagerPane(); + modal.updateLoadingProgress('Navigation loaded'); + }.bind(this)); + }, @@ -264,6 +275,16 @@ Class.create("PydioUI", { }.bind(this) ); }, + findAjxpClassesInText: function(cdataContent){ + var match = this.__ajxpClassRegexp.exec(cdataContent); + var requiredClasses = $H(); + while(match !=null){ + requiredClasses.set(match[1], true); + match = this.__ajxpClassRegexp.exec(cdataContent); + } + return requiredClasses; + }, + /** * Parses a client_configs/template_part node */ @@ -272,6 +293,9 @@ Class.create("PydioUI", { var toUpdate = {}; var restoreUpdate = {}; + var requiredClasses = $H(); + var requiredComponents = $H(); + if(!this.templatePartsToRestore){ this.templatePartsToRestore = $A(); } @@ -282,37 +306,52 @@ Class.create("PydioUI", { var ajxpId = parts[i].getAttribute("ajxpId"); var ajxpClassName = parts[i].getAttribute("ajxpClass"); var ajxpOptionsString = parts[i].getAttribute("ajxpOptions"); + if(parts[i].getAttribute("components")){ + parts[i].getAttribute("components").split(",").forEach(function(v){ + requiredComponents.set(v, true); + }); + } var cdataContent = ""; if(parts[i].firstChild && parts[i].firstChild.nodeType == 4 && parts[i].firstChild.nodeValue != ""){ cdataContent = parts[i].firstChild.nodeValue; } - - var ajxpClass = Class.getByName(ajxpClassName); - if(ajxpClass && ajxpId && Class.objectImplements(ajxpClass, "IAjxpWidget")){ - toUpdate[ajxpId] = [ajxpClass, ajxpClassName, ajxpOptionsString, cdataContent]; -// this.templatePartsToRestore = this.templatePartsToRestore.without(ajxpId); + if(ajxpId){ + toUpdate[ajxpId] = [ajxpClassName, ajxpOptionsString, cdataContent]; + requiredClasses = requiredClasses.merge(this.findAjxpClassesInText(cdataContent)); } } - var futurePartsToRestore = $A(Object.keys(toUpdate)); this.templatePartsToRestore.each(function(key){ var part = this.findOriginalTemplatePart(key); if(part){ var ajxpClassName = part.getAttribute("ajxpClass"); var ajxpOptionsString = part.getAttribute("ajxpOptions"); var cdataContent = part.innerHTML; - var ajxpClass = Class.getByName(ajxpClassName); - restoreUpdate[key] = [ajxpClass, ajxpClassName, ajxpOptionsString, cdataContent]; + restoreUpdate[key] = [ajxpClassName, ajxpOptionsString, cdataContent]; } }.bind(this)); - for(var id in restoreUpdate){ - this.refreshGuiComponent(id, restoreUpdate[id][0], restoreUpdate[id][1], restoreUpdate[id][2], restoreUpdate[id][3]); - } - for(id in toUpdate){ - this.refreshGuiComponent(id, toUpdate[id][0], toUpdate[id][1], toUpdate[id][2], toUpdate[id][3]); + var callback = function(){ + var futurePartsToRestore = $A();// $A(Object.keys(toUpdate)); + for(var id in restoreUpdate){ + this.refreshGuiComponent(id, restoreUpdate[id][0], restoreUpdate[id][1], restoreUpdate[id][2]); + } + for(id in toUpdate){ + var ajxpClass = Class.getByName(toUpdate[id][0]); + if(ajxpClass && Class.objectImplements(ajxpClass, "IAjxpWidget")){ + this.refreshGuiComponent(id, toUpdate[id][0], toUpdate[id][1], toUpdate[id][2]); + futurePartsToRestore.push(id); + } + } + this.templatePartsToRestore = futurePartsToRestore; + this.refreshGuiComponentConfigs(); + }.bind(this); + + if(requiredComponents.keys().size()){ + ResourcesManager.loadWebComponentsAndApply(requiredComponents.keys(), callback); + }else{ + ResourcesManager.loadClassesAndApply(requiredClasses.keys(), callback); } - this.templatePartsToRestore = futurePartsToRestore; - this.refreshGuiComponentConfigs(); + }, /** @@ -324,8 +363,9 @@ Class.create("PydioUI", { * @param ajxpOptionsString * @param cdataContent */ - refreshGuiComponent:function(ajxpId, ajxpClass, ajxpClassName, ajxpOptionsString, cdataContent){ + refreshGuiComponent:function(ajxpId, ajxpClassName, ajxpOptionsString, cdataContent){ if(!this._instancesCache.get(ajxpId)) return; + // First destroy current component, unregister actions, etc. var oldObj = this._instancesCache.get(ajxpId); if(!oldObj.__className) { @@ -355,6 +395,7 @@ Class.create("PydioUI", { }.bind(this)); if(compReg.length) this.initAjxpWidgets(compReg); } + var ajxpClass = Class.getByName(ajxpClassName); var obj = new ajxpClass($(ajxpId), ajxpOptions); if(Class.objectImplements(obj, "IFocusable")){ diff --git a/core/src/plugins/gui.ajax/res/js/ui/prototype/class.SearchEngine.js b/core/src/plugins/gui.ajax/res/js/ui/prototype/class.SearchEngine.js index bf96659e08..2d3c893c11 100644 --- a/core/src/plugins/gui.ajax/res/js/ui/prototype/class.SearchEngine.js +++ b/core/src/plugins/gui.ajax/res/js/ui/prototype/class.SearchEngine.js @@ -140,12 +140,26 @@ Class.create("SearchEngine", AjxpPane, { this.htmlElement.down('#search_form').insert({bottom:'
'}); } if(this.htmlElement.down('#search_meta')){ - this.initMetadataForm(this.htmlElement.down('#search_meta'), this._ajxpOptions.metaColumns); + this.loadRenderersAndInitMetadataForm(this.htmlElement.down('#search_meta'), this._ajxpOptions.metaColumns); } } }, + loadRenderersAndInitMetadataForm: function(formPanel, metadataColumns){ + if(!this._ajxpOptions.metaColumnsRenderers) { + this.initMetadataForm(formPanel, metadataColumns); + return; + } + var collectClasses = $H(); + $H(this._ajxpOptions.metaColumnsRenderers).each(function(pair){ + collectClasses.set(pair.value.split('.', 1).shift(), true); + }.bind(this)); + ResourcesManager.loadClassesAndApply(collectClasses.keys(), function(){ + this.initMetadataForm(formPanel, metadataColumns); + }.bind(this)); + }, + initMetadataForm: function(formPanel, metadataColumns){ var searchChooser = new Element('div',{id:"basic_search"}).update(' '+MessageHash[486]+'' + ' '+MessageHash[487]+' ' + @@ -329,7 +343,7 @@ Class.create("SearchEngine", AjxpPane, { var searchMeta = this.htmlElement.down('#search_meta'); if(searchMeta){ - this.initMetadataForm(searchMeta, this._ajxpOptions.metaColumns); + this.loadRenderersAndInitMetadataForm(searchMeta, this._ajxpOptions.metaColumns); } }else{ diff --git a/core/src/plugins/gui.ajax/res/js/ui/prototype/util/ajxp_utils.js b/core/src/plugins/gui.ajax/res/js/ui/prototype/util/ajxp_utils.js index 3cf48d7ad2..f533088495 100644 --- a/core/src/plugins/gui.ajax/res/js/ui/prototype/util/ajxp_utils.js +++ b/core/src/plugins/gui.ajax/res/js/ui/prototype/util/ajxp_utils.js @@ -305,6 +305,8 @@ function fitHeightToBottom(element, parentElement, addMarginBottom, listen, minO parentElement = Position.offsetParent($(element)); }else if(parentElement == "window") { parentElement = window; + }else if(typeof parentElement == "string"){ + parentElement = element.up('#' + parentElement); }else{ parentElement = $(parentElement); } @@ -354,7 +356,7 @@ function fitHeightToBottom(element, parentElement, addMarginBottom, listen, minO if(element.ajxpPaneObject && listen){ element.ajxpPaneObject.resize(); } - element.fire("resize"); + element.fire("resize", null, null, false); }; observer(); diff --git a/core/src/plugins/meta.comments/manifest.xml b/core/src/plugins/meta.comments/manifest.xml index 47b65621d8..eef44ed476 100644 --- a/core/src/plugins/meta.comments/manifest.xml +++ b/core/src/plugins/meta.comments/manifest.xml @@ -5,7 +5,7 @@ - +
diff --git a/core/src/plugins/meta.exif/manifest.xml b/core/src/plugins/meta.exif/manifest.xml index 09e5d59477..6c85eee2ff 100644 --- a/core/src/plugins/meta.exif/manifest.xml +++ b/core/src/plugins/meta.exif/manifest.xml @@ -4,7 +4,7 @@ - + diff --git a/core/src/plugins/meta.git/manifest.xml b/core/src/plugins/meta.git/manifest.xml index cd8c3fe2e4..96bf2cf8e2 100644 --- a/core/src/plugins/meta.git/manifest.xml +++ b/core/src/plugins/meta.git/manifest.xml @@ -9,7 +9,7 @@ - + @@ -28,16 +28,18 @@ diff --git a/core/src/plugins/meta.svn/manifest.xml b/core/src/plugins/meta.svn/manifest.xml index 9705df01b7..e82ea5be69 100644 --- a/core/src/plugins/meta.svn/manifest.xml +++ b/core/src/plugins/meta.svn/manifest.xml @@ -4,7 +4,7 @@ - + @@ -59,6 +59,7 @@ diff --git a/core/src/plugins/meta.user/manifest.xml b/core/src/plugins/meta.user/manifest.xml index 43edb20988..6ea147c6d1 100644 --- a/core/src/plugins/meta.user/manifest.xml +++ b/core/src/plugins/meta.user/manifest.xml @@ -4,7 +4,7 @@ - + @@ -66,44 +66,46 @@
'+pair.value.label+' :
'); + var element = form.down('input[name="'+pair.key+'"]'); + var fieldType = pair.value.type; + if(fieldType == 'stars_rate'){ + MetaCellRenderer.prototype.formPanelStars(element, form); + }else if(fieldType == 'css_label'){ + MetaCellRenderer.prototype.formPanelCssLabels(element, form); + }else if(fieldType == 'textarea'){ + MetaCellRenderer.prototype.formTextarea(element, form); + }else if(fieldType == 'choice'){ + MetaCellRenderer.prototype.formPanelSelectorFilter(element, form); + }else if(fieldType == 'tags'){ + MetaCellRenderer.prototype.formPanelTags(element, form); + }else if(fieldType == 'updater' || fieldType == 'creator'){ + element.disabled = true; + } + }); } - metaConfigs.each(function(pair){ - var value = nodeMeta.get(pair.key) || ''; - form.insert('
'+pair.value.label+' :
'); - var element = form.down('input[name="'+pair.key+'"]'); - var fieldType = pair.value.type; - if(fieldType == 'stars_rate'){ - MetaCellRenderer.prototype.formPanelStars(element, form); - }else if(fieldType == 'css_label'){ - MetaCellRenderer.prototype.formPanelCssLabels(element, form); - }else if(fieldType == 'textarea'){ - MetaCellRenderer.prototype.formTextarea(element, form); - }else if(fieldType == 'choice'){ - MetaCellRenderer.prototype.formPanelSelectorFilter(element, form); - }else if(fieldType == 'tags'){ - MetaCellRenderer.prototype.formPanelTags(element, form); - }else if(fieldType == 'updater' || fieldType == 'creator'){ - element.disabled = true; - } - }); - } - var closeFunc = function(){ - var oForm = $(modal.getForm()); - userSelection.updateFormOrUrl(modal.getForm()); - pydio.getController().submitForm(oForm); - hideLightBox(true); - return false; - }; - modal.showDialogForm('Meta Edit', 'user_meta_form', loadFunc, closeFunc); - ]]>
+ var closeFunc = function(){ + var oForm = $(modal.getForm()); + userSelection.updateFormOrUrl(modal.getForm()); + pydio.getController().submitForm(oForm); + hideLightBox(true); + return false; + }; + modal.showDialogForm('Meta Edit', 'user_meta_form', loadFunc, closeFunc); + }); + ]]> ]]>