diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml index 25aaaea0ffe2b98a6e46a7e133c53b743603530e..284f241b3ba54f834f2d5057bf1a67458645aa03 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml @@ -10,7 +10,7 @@ <?php $_divId = 'tree-div_' . time() ?> <div id="<?php /* @escapeNotVerified */ echo $_divId ?>" class="tree"></div> -<script id="ie-deferred-loader" defer="defer" src=""></script> +<script id="ie-deferred-loader" defer="defer" src="//:"></script> <script> require([ 'jquery', diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml index 9e303fe921dfd328453c0f736a107fa4be70e6fa..ae65d2658978a3f2e74dc04278ce9d803dda471a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml @@ -11,7 +11,7 @@ <?php $_divId = 'tree' . $block->getId() ?> <div id="<?php /* @escapeNotVerified */ echo $_divId ?>" class="tree"></div> <!--[if IE]> -<script id="ie-deferred-loader" defer="defer" src=""></script> +<script id="ie-deferred-loader" defer="defer" src="//:"></script> <![endif]--> <script> require(['jquery', "prototype", "extjs/ext-tree-checkbox"], function(jQuery){ diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml index f1bba6cc3c07ca0603e149afc8f0ad24b342a467..b3a7002ef417ecb3cc14006aa34b6844a32d8a67 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml @@ -38,7 +38,7 @@ <span class="title"><?php /* @escapeNotVerified */ echo __('Unassigned Attributes') ?></span> </div> <div id="tree-div2" class="attribute-set-tree"></div> - <script id="ie-deferred-loader" defer="defer" src=""></script> + <script id="ie-deferred-loader" defer="defer" src="//:"></script> <script> define("tree-panel", [ diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/button.js b/app/code/Magento/Ui/view/base/web/js/form/components/button.js new file mode 100644 index 0000000000000000000000000000000000000000..b5373ff1648b7f2c3db97e8333551781c09b09e1 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/form/components/button.js @@ -0,0 +1,103 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'uiElement', + 'uiRegistry', + 'uiLayout', + 'mageUtils' +], function (Element, registry, layout, utils) { + 'use strict'; + + return Element.extend({ + defaults: { + additionalClasses: {}, + displayArea: 'outsideGroup', + displayAsLink: false, + elementTmpl: 'ui/form/element/button', + template: 'ui/form/components/button/simple' + }, + + /** + * Initializes component. + * + * @returns {Object} Chainable. + */ + initialize: function () { + return this._super() + ._setClasses(); + }, + + /** + * Performs configured actions + */ + action: function () { + this.actions.forEach(this.applyAction, this); + }, + + /** + * Apply action on target component, + * but previously create this component from template if it is not existed + * + * @param {Object} action - action configuration + */ + applyAction: function (action) { + var targetName = action.targetName, + params = action.params, + actionName = action.actionName, + target; + + if (!registry.has(targetName)) { + this.getFromTemplate(targetName); + } + target = registry.async(targetName); + + if (target && typeof target === 'function' && actionName) { + target(actionName, params); + } + }, + + /** + * Create target component from template + * + * @param {Object} targetName - name of component, + * that supposed to be a template and need to be initialized + */ + getFromTemplate: function (targetName) { + var parentName = targetName.split('.'), + index = parentName.pop(), + child; + + parentName = parentName.join('.'); + child = utils.template({ + parent: parentName, + name: index, + nodeTemplate: targetName + }); + layout([child]); + }, + + /** + * Extends 'additionalClasses' object. + * + * @returns {Object} Chainable. + */ + _setClasses: function () { + if (typeof this.additionalClasses === 'string') { + this.additionalClasses = this.additionalClasses + .trim() + .split(' ') + .reduce(function (classes, name) { + classes[name] = true; + + return classes; + }, {} + ); + } + + return this; + } + }); +}); diff --git a/app/code/Magento/Ui/view/base/web/js/lib/core/element/links.js b/app/code/Magento/Ui/view/base/web/js/lib/core/element/links.js index c769be3775f46ddba47d5d9110974fc2bd5782da..1ac9b45a46e86a9df232db8993932a886c3bcd8c 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/core/element/links.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/core/element/links.js @@ -51,7 +51,7 @@ define([ } if (owner.component !== target.component) { - value = utils.copy(value); + value = data.inversionValue ? !utils.copy(value) : utils.copy(value); } component.set(property, value); @@ -149,6 +149,11 @@ define([ function transfer(owner, data) { var args = _.toArray(arguments); + if (data.target.substr(0,1) === '!') { + data.target = data.target.substr(1); + data.inversionValue = true; + } + if (owner.name === data.target) { args.unshift(owner); diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/outer_click.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/outer_click.js index 839a87481a38631267f295c9908abf2873d74f4d..7fa3ff22af8975b62d23b3f9c07ff3ab8d813c39 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/outer_click.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/outer_click.js @@ -15,6 +15,30 @@ define([ onlyIfVisible: true }; + /** + * Checks if element sis visible. + * + * @param {Element} el + * @returns {Boolean} + */ + function isVisible(el) { + var style = window.getComputedStyle(el), + visibility = { + display: 'none', + visibility: 'hidden', + opacity: '0' + }, + visible = true; + + _.each(visibility, function (val, key) { + if (style[key] === val) { + visible = false; + } + }); + + return visible; + } + /** * Document click handler which in case if event target is not * a descendant of provided container element, @@ -33,7 +57,7 @@ define([ } if (config.onlyIfVisible) { - if (!_.isNull(container.offsetParent)) { + if (!_.isNull(container.offsetParent) && isVisible(container)) { callback(); } } else { diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js b/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js new file mode 100644 index 0000000000000000000000000000000000000000..cccf95e00bf54f2a667e85fab4d5f12b435728a5 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js @@ -0,0 +1,315 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/lib/view/utils/async', + 'uiCollection', + 'uiRegistry', + 'underscore', + './modal' +], function ($, Collection, registry, _) { + 'use strict'; + + return Collection.extend({ + defaults: { + template: 'ui/modal/modal-component', + options: { + title: '', + buttons: [], + keyEventHandlers: {} + }, + valid: true, + listens: { + state: 'onState' + }, + modalClass: 'modal-component' + }, + + /** + * Initializes component. + * + * @returns {Object} Chainable. + */ + initialize: function () { + this._super(); + _.bindAll(this, + 'initModal', + 'openModal', + 'closeModal', + 'toggleModal', + 'setPrevValues', + 'actionCancel', + 'validate'); + this.initializeContent(); + + return this; + }, + + /** + * Initializes modal configuration + * + * @returns {Object} Chainable. + */ + initConfig: function () { + return this._super() + .initSelector() + .initModalEvents(); + }, + + /** + * Configure modal selector + * + * @returns {Object} Chainable. + */ + initSelector: function () { + this.contentSelector = '.' + this.modalClass; + this.options.modalClass = this.name.replace(/\./g, '_'); + this.rootSelector = '.' + this.options.modalClass; + + return this; + }, + + /** + * Configure modal keyboard handlers + * and outer click + * + * @returns {Object} Chainable. + */ + initModalEvents: function () { + this.options.keyEventHandlers.escapeKey = this.options.outerClickHandler = this.actionCancel.bind(this); + + return this; + }, + + /** + * Initialize modal's content components + */ + initializeContent: function () { + $.async(this.contentSelector, this, this.initModal); + }, + + /** + * Init toolbar section so other components will be able to place something in it + */ + initToolbarSection: function () { + this.set('toolbarSection', this.modal.data('modal').modal.find('header').get(0)); + }, + + /** + * Initializes observable properties. + * + * @returns {Object} Chainable. + */ + initObservable: function () { + this._super(); + this.observe('state'); + + return this; + }, + + /** + * Wrap content in a modal of certain type + * + * @param {HTMLElement} element + * @returns {Object} Chainable. + */ + initModal: function (element) { + if (!this.modal) { + this.overrideModalButtonCallback(); + this.options.modalCloseBtnHandler = this.actionCancel; + this.modal = $(element).modal(this.options); + this.initToolbarSection(); + + if (this.waitCbk) { + this.waitCbk(); + this.waitCbk = null; + } + } + + return this; + }, + + /** + * Open modal + */ + openModal: function () { + if (this.modal) { + this.state(true); + } else { + this.waitCbk = this.openModal; + } + }, + + /** + * Close modal + */ + closeModal: function () { + if (this.modal) { + this.state(false); + } else { + this.waitCbk = this.closeModal; + } + }, + + /** + * Toggle modal + */ + toggleModal: function () { + if (this.modal) { + this.state(!this.state()); + } else { + this.waitCbk = this.toggleModal; + } + }, + + /** + * Wrap content in a modal of certain type + * + * @param {Boolean} state + */ + onState: function (state) { + if (state) { + this.modal.modal('openModal'); + this.applyData(); + } else { + this.modal.modal('closeModal'); + } + }, + + /** + * Validate everything validatable in modal + */ + validate: function (elem) { + if (typeof elem.validate === 'function') { + this.valid = this.valid & elem.validate().valid; + } else if (elem.elems) { + elem.elems().forEach(this.validate, this); + } + }, + + /** + * Reset data from provider + */ + resetData: function () { + this.elems().forEach(this.resetValue, this); + }, + + /** + * Update 'applied' property with data from modal content + */ + applyData: function () { + var applied = {}; + + this.elems().forEach(this.gatherValues.bind(this, applied), this); + this.applied = applied; + }, + + /** + * Gather values from modal content + * + * @param {Array} applied + * @param {HTMLElement} elem + */ + gatherValues: function (applied, elem) { + if (typeof elem.value === 'function') { + applied[elem.index] = elem.value(); + } else if (elem.elems) { + elem.elems().forEach(this.gatherValues.bind(this, applied), this); + } + }, + + /** + * Set to previous values from modal content + * + * @param {HTMLElement} elem + */ + setPrevValues: function (elem) { + if (typeof elem.value === 'function') { + this.modal.focus(); + elem.value(this.applied[elem.index]); + } else if (elem.elems) { + elem.elems().forEach(this.setPrevValues, this); + } + }, + + /** + * Triggers some method in every modal child elem, if this method is defined + * + * @param {Object} action - action configuration, + * must contain actionName and targetName and + * can contain params + */ + triggerAction: function (action) { + var targetName = action.targetName, + params = action.params, + actionName = action.actionName, + target; + + target = registry.async(targetName); + + if (target && typeof target === 'function' && actionName) { + target(actionName, params); + } + }, + + /** + * Override modal buttons callback placeholders with real callbacks + */ + overrideModalButtonCallback: function () { + var buttons = this.options.buttons; + + if (buttons && buttons.length) { + buttons.forEach(function (button) { + button.click = this.getButtonClickHandler(button.actions); + }, this); + } + }, + + /** + * Generate button click handler based on button's 'actions' configuration + */ + getButtonClickHandler: function (actionsConfig) { + var actions = actionsConfig.map( + function (actionConfig) { + if (_.isObject(actionConfig)) { + return this.triggerAction.bind(this, actionConfig); + } + + return this[actionConfig] ? this[actionConfig].bind(this) : function () {}; + }, this); + + return function () { + actions.forEach( + function (action) { + action(); + } + ); + }; + }, + + /** + * Cancels changes in modal: + * returning elems values to the previous state, + * and close modal + */ + actionCancel: function () { + this.elems().forEach(this.setPrevValues, this); + this.closeModal(); + }, + + /** + * Accept changes in modal by not preventing them. + * Can be extended by exporting 'gatherValues' result somewhere + */ + actionDone: function () { + this.valid = true; + this.elems().forEach(this.validate, this); + + if (this.valid) { + this.closeModal(); + } + } + }); +}); diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js index 23011d607cdadc301ee6cf4cebeb148b400c0260..5dcfc53459baad6607edce1f13be49e708ead692 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js @@ -118,13 +118,14 @@ define([ 'closeModal' ); + _.extend(this.keyEventHandlers, this.options.keyEventHandlers); this.options.transitionEvent = transitionEvent; this._createWrapper(); this._renderModal(); this._createButtons(); $(this.options.trigger).on('click', _.bind(this.toggleModal, this)); this._on(this.modal.find(this.options.modalCloseBtn), { - 'click': this.closeModal + 'click': this.options.modalCloseBtnHandler ? this.options.modalCloseBtnHandler : this.closeModal }); this._on(this.element, { 'openModal': this.openModal, @@ -374,7 +375,8 @@ define([ * Creates overlay, append it to wrapper, set previous click event on overlay. */ _createOverlay: function () { - var events; + var events, + outerClickHandler = this.options.outerClickHandler || this.closeModal; this.overlay = $('.' + this.options.overlayClass); @@ -386,7 +388,7 @@ define([ } events = $._data(this.overlay.get(0), 'events'); events ? this.prevOverlayHandler = events.click[0].handler : false; - this.options.clickableOverlay ? this.overlay.unbind().on('click', this.closeModal) : false; + this.options.clickableOverlay ? this.overlay.unbind().on('click', outerClickHandler) : false; }, /** diff --git a/app/code/Magento/Ui/view/base/web/templates/form/components/button/simple.html b/app/code/Magento/Ui/view/base/web/templates/form/components/button/simple.html new file mode 100644 index 0000000000000000000000000000000000000000..d562e699c843a584fabd5f35905ca5e92ef8ad96 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/form/components/button/simple.html @@ -0,0 +1,7 @@ +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<render args="elementTmpl"/> \ No newline at end of file diff --git a/app/code/Magento/Ui/view/base/web/templates/form/element/button.html b/app/code/Magento/Ui/view/base/web/templates/form/element/button.html new file mode 100644 index 0000000000000000000000000000000000000000..8f337ce7acb966b57b511b1535ceedb6117cdc03 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/form/element/button.html @@ -0,0 +1,14 @@ +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<button type="button" + css=" + 'action-advanced': $data.displayAsLink, + 'action-secondary': !$data.displayAsLink + " + click="action" + text="title"> +</button> \ No newline at end of file diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html new file mode 100644 index 0000000000000000000000000000000000000000..e39c27e0741232496319e1385a667b181ee623f7 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html @@ -0,0 +1,12 @@ +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<div data-bind="css: modalClass"> + <!-- ko foreach: { data: elems, as: 'element' } --> + <!-- ko template: element.getTemplate() --><!-- /ko --> + <!-- /ko --> +</div> \ No newline at end of file diff --git a/app/code/Magento/Widget/view/adminhtml/templates/catalog/category/widget/tree.phtml b/app/code/Magento/Widget/view/adminhtml/templates/catalog/category/widget/tree.phtml index 135880f5f314214efdc4d95540f489a5fc7bd8fe..89d2b75a3b882322a91f953208242673130ae5b9 100644 --- a/app/code/Magento/Widget/view/adminhtml/templates/catalog/category/widget/tree.phtml +++ b/app/code/Magento/Widget/view/adminhtml/templates/catalog/category/widget/tree.phtml @@ -10,7 +10,7 @@ <?php $_divId = 'tree' . $block->getId() ?> <div id="<?php /* @escapeNotVerified */ echo $_divId ?>" class="tree"></div> -<script id="ie-deferred-loader" defer="defer" src=""></script> +<script id="ie-deferred-loader" defer="defer" src="//:"></script> <script> require(['jquery', "prototype", "extjs/ext-tree-checkbox"], function(jQuery){ diff --git a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml index 6831ca32a007b1c5abe9f67b322e76fe5ee38749..a98b8df1fd5deb0e3b50ee395f4a9102cca68083 100644 --- a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml +++ b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml @@ -15,7 +15,7 @@ <div class="actions"><?php echo $block->getAddLayoutButtonHtml() ?></div> </div> </fieldset> -<script id="ie-deferred-loader" defer="defer" src=""></script> +<script id="ie-deferred-loader" defer="defer" src="//:"></script> <script> require([ 'jquery',