Repository: MahoCommerce/maho
Pull request #136: Rewrote mage/adminhtml/sales.js without prototypejs
Author: login: justinbeaty
Created: 2025-03-17T21:33:26Z | Merged: 2025-03-17T22:27:06Z

I believe this was the largest JS file still with prototype, and obviously a file that is very crucial to Maho. I've a lot of testing and can't find any more issues.

### Testing

This JS file is only loaded on two pages: https://example.com/admin/sales_order_create/, and https://example.com/admin/sales_order_edit/ (i.e. you press the Edit Order button). So those should be the only pages to test, and the edit page is actually handled by the same controller action, so they should be identical.

To find all places to test, I ran the following grep to find calls to the window's order object, and made sure each re-written method was called.
```sh
git grep -E 'order\.\w+' -- '*.phtml' '*.php' '*.js'
```

Things to test:
- Create admin order for existing customer, new customer, and guest
- Add products from grid
- Change prices and qty for quote items
- Move items between quote, wishlist, cart, etc.
- Apply coupons
- Change addresses, shipping same as billing, etc.
- Validate VAT
- Change payment method
- Change shipping method
- Add gift message
- Add order comment

### Improvements

1\. Change item quantity, don't press Update Items & Qty's, then click Submit Order.
Before you get error message: "You have item changes [OK] [CANCEL]". I had no idea what that meant, but if you press OK then the order is submitted without updating the quantity.

Now, the message is "You have unsaved item changes. Discard and proceed with checkout? [OK] [CANCEL]"

2\. Validate VAT
I never used it, but I still found some bugs around API error handling and customer group switching.

3\. ControlButton JS Class
If you look at the bottom of the old sales.js, you'll see this prototype class that is used in exactly one place in the entire code.

Before, the "Add Products" button disappears when you press it, and showed up again once you're done. Maybe because people were pressing that button instead of the "Add Selected Product(s) to Order" button. Leaving it visible doesn't cause any problems, but if we want to re-hide it we can do with CSS instead of creating, destroying, and recreating the button.

As a side note I also added a "Cancel" button to the add products grid so if you accidentally hit "Add Products", you can cancel without the only option being "Add Selected Product(s)".

4\. Made disabled overlay CSS only
Do you know how if you have a virtual order the shipping address and shipping method boxes are greyed out? Mage devs did that by adding a window resize event observer and absolutely positioning the div on top. I replaced it with a few lines on CSS...

### Misc

Trying to get a rough estimation on how much more prototypejs we have:

```
git grep Class.create -- ':!public/js/prototype' | wc -l
```

| This PR | Adminhtml Theme | OM |
|--------|--------|--------|
| 27 | 21 | 109 |


Diff:
diff --git a/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Items.php b/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Items.php
index 7db3d2d8c4..b6590744ea 100644
--- a/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Items.php
+++ b/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Items.php
@@ -6,7 +6,7 @@
  * @package    Mage_Adminhtml
  * @copyright  Copyright (c) 2006-2020 Magento, Inc. (https://magento.com)
  * @copyright  Copyright (c) 2022-2024 The OpenMage Contributors (https://openmage.org)
- * @copyright  Copyright (c) 2024 Maho (https://mahocommerce.com)
+ * @copyright  Copyright (c) 2024-2025 Maho (https://mahocommerce.com)
  * @license    https://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
@@ -19,17 +19,18 @@ class Mage_Adminhtml_Block_Sales_Order_Create_Items extends Mage_Adminhtml_Block
 {
     /**
      * Contains button descriptions to be shown at the top of accordion
-     * @var array
+     * @var list<array>
      */
     protected $_buttons = [];
 
-    /**
-     * Define block ID
-     */
     public function __construct()
     {
         parent::__construct();
         $this->setId('sales_order_create_items');
+        $this->addButton([
+            'label' => Mage::helper('sales')->__('Add Products'),
+            'onclick' => 'order.productGridShow()',
+        ]);
     }
 
     /**
diff --git a/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Search.php b/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Search.php
index 48be363614..d159481354 100644
--- a/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Search.php
+++ b/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Search.php
@@ -6,6 +6,7 @@
  * @package    Mage_Adminhtml
  * @copyright  Copyright (c) 2006-2020 Magento, Inc. (https://magento.com)
  * @copyright  Copyright (c) 2022-2024 The OpenMage Contributors (https://openmage.org)
+ * @copyright  Copyright (c) 2025 Maho (https://mahocommerce.com)
  * @license    https://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
@@ -16,10 +17,25 @@
  */
 class Mage_Adminhtml_Block_Sales_Order_Create_Search extends Mage_Adminhtml_Block_Sales_Order_Create_Abstract
 {
+    /**
+     * Contains button descriptions to be shown at the top of accordion
+     * @var list<array>
+     */
+    protected $_buttons = [];
+
     public function __construct()
     {
         parent::__construct();
         $this->setId('sales_order_create_search');
+        $this->addButton([
+            'label' => Mage::helper('sales')->__('Add Selected Product(s) to Order'),
+            'onclick' => 'order.productGridAddSelected()',
+            'class' => 'add',
+        ]);
+        $this->addButton([
+            'label' => Mage::helper('sales')->__('Cancel'),
+            'onclick' => 'order.productGridHide()',
+        ]);
     }
 
     /**
@@ -31,16 +47,30 @@ public function getHeaderText()
     }
 
     /**
+     * Add button to the items header
+     *
+     * @param array $args
+     */
+    public function addButton($args)
+    {
+        $this->_buttons[] = $args;
+    }
+
+    /**
+     * Render buttons and return HTML code
+     *
      * @return string
      */
     public function getButtonsHtml()
     {
-        $addButtonData = [
-            'label' => Mage::helper('sales')->__('Add Selected Product(s) to Order'),
-            'onclick' => 'order.productGridAddSelected()',
-            'class' => 'add',
-        ];
-        return $this->getLayout()->createBlock('adminhtml/widget_button')->setData($addButtonData)->toHtml();
+        $html = '';
+        // Make buttons to be rendered in opposite order of addition. This makes "Add products" the last one.
+        $this->_buttons = array_reverse($this->_buttons);
+        foreach ($this->_buttons as $buttonData) {
+            $html .= $this->getLayout()->createBlock('adminhtml/widget_button')->setData($buttonData)->toHtml();
+        }
+
+        return $html;
     }
 
     /**
diff --git a/app/code/core/Mage/Adminhtml/controllers/Customer/System/Config/ValidatevatController.php b/app/code/core/Mage/Adminhtml/controllers/Customer/System/Config/ValidatevatController.php
index 237738037e..7e87a7ebc9 100644
--- a/app/code/core/Mage/Adminhtml/controllers/Customer/System/Config/ValidatevatController.php
+++ b/app/code/core/Mage/Adminhtml/controllers/Customer/System/Config/ValidatevatController.php
@@ -6,6 +6,7 @@
  * @package    Mage_Adminhtml
  * @copyright  Copyright (c) 2006-2020 Magento, Inc. (https://magento.com)
  * @copyright  Copyright (c) 2022-2024 The OpenMage Contributors (https://openmage.org)
+ * @copyright  Copyright (c) 2025 Maho (https://mahocommerce.com)
  * @license    https://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
@@ -68,11 +69,10 @@ public function validateAdvancedAction()
             $storeId,
         );
 
-        $body = $coreHelper->jsonEncode([
+        $this->getResponse()->setBodyJson([
             'valid' => $valid,
             'group' => $groupId,
             'success' => $success,
         ]);
-        $this->getResponse()->setBody($body);
     }
 }
diff --git a/app/code/core/Mage/Adminhtml/etc/jstranslator.xml b/app/code/core/Mage/Adminhtml/etc/jstranslator.xml
index f38cb65962..8a1e4b9f3a 100644
--- a/app/code/core/Mage/Adminhtml/etc/jstranslator.xml
+++ b/app/code/core/Mage/Adminhtml/etc/jstranslator.xml
@@ -62,6 +62,15 @@
         </passkey-deleted>
     </script>
 
+    <script path="mage/adminhtml/sales.js" area="adminhtml" module="sales">
+        <submit-order-unsaved-items translate="message">
+            <message>You have unsaved item changes. Discard and proceed with checkout?</message>
+        </submit-order-unsaved-items>
+        <clear-giftmessage translate="message">
+            <message>First, clean the Message field in Gift Message form</message>
+        </clear-giftmessage>
+    </script>
+
     <script path="mage/adminhtml/sales/packaging.js" area="adminhtml" module="sales">
         <invalid-url translate="message" module="core">
             <message>Invalid URL</message>
diff --git a/app/design/adminhtml/default/default/template/sales/order/create/abstract.phtml b/app/design/adminhtml/default/default/template/sales/order/create/abstract.phtml
index 411574e39e..69ca9fe5a3 100644
--- a/app/design/adminhtml/default/default/template/sales/order/create/abstract.phtml
+++ b/app/design/adminhtml/default/default/template/sales/order/create/abstract.phtml
@@ -5,7 +5,7 @@
  * @package     default_default
  * @copyright   Copyright (c) 2006-2020 Magento, Inc. (https://magento.com)
  * @copyright   Copyright (c) 2022-2024 The OpenMage Contributors (https://openmage.org)
- * @copyright   Copyright (c) 2024 Maho (https://mahocommerce.com)
+ * @copyright   Copyright (c) 2024-2025 Maho (https://mahocommerce.com)
  * @license     https://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
  */
 
@@ -13,8 +13,8 @@
 ?>
 <div class="entry-edit">
     <div class="entry-edit-head">
-        <div style="float: right;"><?= $this->getButtonsHtml() ?></div>
-        <h4 class="fieldset-legend <?= ($this->getHeaderCssClass()) ? $this->getHeaderCssClass().' icon-head' : '' ?>"><?= $this->getHeaderText() ?></h4>
+        <h4 class="fieldset-legend <?= $this->getHeaderCssClass() ?: '' ?>"><?= $this->getHeaderText() ?></h4>
+        <div class="form-buttons"><?= $this->getButtonsHtml() ?></div>
     </div>
     <div class="fieldset">
         <?= $this->getChildHtml('', true, true) ?>
diff --git a/public/js/mage/adminhtml/giftmessage.js b/public/js/mage/adminhtml/giftmessage.js
index d15c8385a2..bdd6760216 100644
--- a/public/js/mage/adminhtml/giftmessage.js
+++ b/public/js/mage/adminhtml/giftmessage.js
@@ -4,6 +4,7 @@
  * @package     Mage_Adminhtml
  * @copyright   Copyright (c) 2006-2020 Magento, Inc. (https://magento.com)
  * @copyright   Copyright (c) 2022-2023 The OpenMage Contributors (https://openmage.org)
+ * @copyright   Copyright (c) 2025 Maho (https://mahocommerce.com)
  * @license     https://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
  */
 
@@ -247,7 +248,10 @@ GiftMessageSet.prototype = {
             $(this.sourcePrefix + this.id + '_form').request();
         } else if (typeof(order) != 'undefined') {
             var data = order.serializeData('gift_options_data_' + this.id);
-            order.loadArea(['items'], true, data.toObject());
+            if (typeof data?.toObject === 'function') {
+                data = data.toObject();
+            }
+            order.loadArea(['items'], true, data);
         }
     }
 };
diff --git a/public/js/mage/adminhtml/loader.js b/public/js/mage/adminhtml/loader.js
index ae3670ed25..e805cc60ba 100644
--- a/public/js/mage/adminhtml/loader.js
+++ b/public/js/mage/adminhtml/loader.js
@@ -4,6 +4,7 @@
  * @package     Mage_Adminhtml
  * @copyright   Copyright (c) 2006-2020 Magento, Inc. (https://magento.com)
  * @copyright   Copyright (c) 2022-2023 The OpenMage Contributors (https://openmage.org)
+ * @copyright   Copyright (c) 2025 Maho (https://mahocommerce.com)
  * @license     https://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
  */
 
@@ -189,9 +190,13 @@ varienLoaderHandler.handler = {
 var loaderTimeout = null;
 
 function showLoader(loaderArea) {
-    if($(loaderArea) === undefined) {
-        loaderArea = $$('#html-body .wrapper')[0]; // Blocks all page
+    if (typeof loaderArea === 'string') {
+        loaderArea = document.getElementById(loaderArea);
     }
+    if (!(loaderArea instanceof Element)) {
+        loaderArea = document.body;
+    }
+
     var loadingMask = $('loading-mask');
     if(Element.visible(loadingMask)) {
         return;
diff --git a/public/js/mage/adminhtml/product/composite/configure.js b/public/js/mage/adminhtml/product/composite/configure.js
index 0ecb5830e0..9c5174ddec 100644
--- a/public/js/mage/adminhtml/product/composite/configure.js
+++ b/public/js/mage/adminhtml/product/composite/configure.js
@@ -491,9 +491,16 @@ class ProductConfigure // Maho.Admin.Controller.ProductConfigurePopup
     /**
      * Helper to find qty of currently confirmed item
      */
-    getCurrentConfirmedQtyElement() {
+    getCurrentConfirmedBlock() {
         const { listType, itemId } = this.current;
-        return this._getConfirmedBlock(listType, itemId).querySelector('input[name=qty]');
+        return this._getConfirmedBlock(listType, itemId);
+    }
+
+    /**
+     * Helper to find qty of currently confirmed item
+     */
+    getCurrentConfirmedQtyElement() {
+        return this.getCurrentConfirmedBlock().querySelector('input[name=qty]');
     }
 
     /**
diff --git a/public/js/mage/adminhtml/sales.js b/public/js/mage/adminhtml/sales.js
index fd38433b42..e970113cb3 100644
--- a/public/js/mage/adminhtml/sales.js
+++ b/public/js/mage/adminhtml/sales.js
@@ -7,202 +7,187 @@
  * @copyright   Copyright (c) 2025 Maho (https://mahocommerce.com)
  * @license     https://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
  */
-var AdminOrder = new Class.create();
-AdminOrder.prototype = {
-    initialize : function(data){
-        if(!data) data = {};
-        this.loadBaseUrl    = false;
-        this.customerId     = data.customer_id ? data.customer_id : false;
-        this.isGuest        = data.is_guest ? true : false;
-        this.storeId        = data.store_id ? data.store_id : false;
-        this.currencyId     = false;
-        this.currencySymbol = data.currency_symbol ? data.currency_symbol : '';
-        this.addresses      = data.addresses ? data.addresses : $H({});
-        this.shippingAsBilling = data.shippingAsBilling ? data.shippingAsBilling : false;
-        this.gridProducts   = $H({});
-        this.gridProductsGift = $H({});
-        this.billingAddressContainer = '';
-        this.shippingAddressContainer= '';
-        this.isShippingMethodReseted = data.shipping_method_reseted ? data.shipping_method_reseted : false;
-        this.overlayData = $H({});
-        this.giftMessageDataChanged = false;
-        this.productConfigureAddFields = {};
-        this.productPriceBase = {};
-        this.collectElementsValue = true;
-        Event.observe(window, 'load',  (function(){
-            this.dataArea = new OrderFormArea('data', $(this.getAreaId('data')), this);
-            this.itemsArea = Object.extend(new OrderFormArea('items', $(this.getAreaId('items')), this), {
-                addControlButton: function(button){
-                    var controlButtonArea = $(this.node).select('.form-buttons')[0];
-                    if (typeof controlButtonArea != 'undefined') {
-                        var buttons = controlButtonArea.childElements();
-                        for (var i = 0; i < buttons.length; i++) {
-                            if (buttons[i].innerHTML.include(button.label)) {
-                                return ;
-                            }
-                        }
-                        button.insertIn(controlButtonArea, 'top');
-                    }
-                }
-            });
 
-            var searchButton = new ControlButton(Translator.translate('Add Products')),
-                searchAreaId = this.getAreaId('search');
-            searchButton.onClick = function() {
-                $(searchAreaId).show();
-                var el = this;
-                window.setTimeout(function () {
-                    el.remove();
-                }, 10);
-            };
-
-            this.dataArea.onLoad = this.dataArea.onLoad.wrap(function(proceed) {
-                proceed();
-                this._parent.itemsArea.setNode($(this._parent.getAreaId('items')));
-                this._parent.itemsArea.onLoad();
-            });
+class AdminOrder
+{
+    gridProducts = new Map();
+    overlayData = new Map();
+    productConfigureAddFields = {};
+    productPriceBase = {};
+    dataArea = null;
+    itemsArea = null;
+    billingAddressContainer = '';
+    shippingAddressContainer= '';
+    collectElementsValue = true;
+    giftMessageDataChanged = false;
+
+    constructor() {
+        this.initialize(...arguments);
+    }
 
-            this.itemsArea.onLoad = this.itemsArea.onLoad.wrap(function(proceed) {
-                proceed();
-                if (!$(searchAreaId).visible()) {
-                    this.addControlButton(searchButton);
-                }
-            });
-            this.areasLoaded();
+    initialize(data) {
+        data = {
+            customer_id: false,
+            is_guest: false,
+            store_id: false,
+            currency_symbol: '',
+            addresses: {},
+            shippingAsBilling: false,
+            shipping_method_reseted: false,
+            ...data,
+        }
+
+        this.loadBaseUrl             = false;
+        this.customerId              = data.customer_id;
+        this.isGuest                 = data.is_guest;
+        this.storeId                 = data.store_id;
+        this.currencyId              = false;
+        this.currencySymbol          = data.currency_symbol;
+        this.addresses               = data.addresses;
+        this.shippingAsBilling       = data.shippingAsBilling;
+        this.isShippingMethodReseted = data.shipping_method_reseted;
+
+        document.addEventListener('DOMContentLoaded', this.initAreas.bind(this));
+    }
+
+    initAreas() {
+        this.dataArea = new OrderFormArea('data', this.getAreaEl('data'), this);
+        this.itemsArea = new OrderFormArea('items', this.getAreaEl('items'), this);
+
+        this.dataArea.onLoad = wrapFunction(this.dataArea.onLoad, (proceed) => {
+            proceed();
+            this.itemsArea.setNode(this.getAreaEl('items'));
             this.itemsArea.onLoad();
-        }).bind(this));
-    },
+        });
+
+        this.areasLoaded();
+        this.itemsArea.onLoad();
+    }
 
-    areasLoaded: function(){
-    },
+    areasLoaded() {
+    }
 
-    itemsLoaded: function(){
-    },
+    itemsLoaded() {
+    }
 
-    dataLoaded: function(){
+    dataLoaded() {
         this.dataShow();
-    },
+    }
 
-    setLoadBaseUrl : function(url){
+    setLoadBaseUrl(url) {
         this.loadBaseUrl = url;
-    },
+    }
 
-    setAddresses : function(addresses){
+    setAddresses(addresses) {
         this.addresses = addresses;
-    },
+    }
 
-    setCustomerIsGuest : function(){
+    setCustomerIsGuest() {
         this.isGuest = true;
         this.setCustomerId(false);
-    },
+    }
 
-    setCustomerId : function(id){
+    setCustomerId(id) {
         this.customerId = id;
         this.loadArea('header', true);
-        $(this.getAreaId('header')).callback = 'setCustomerAfter';
-        $('back_order_top_button').hide();
-        $('reset_order_top_button').show();
-    },
+        this.getAreaEl('header').callback = 'setCustomerAfter';
+        toggleVis('back_order_top_button', false);
+        toggleVis('reset_order_top_button', true);
+    }
 
-    setCustomerAfter : function () {
+    setCustomerAfter() {
         this.customerSelectorHide();
         if (this.storeId) {
-            $(this.getAreaId('data')).callback = 'dataLoaded';
+            this.getAreaEl('data').callback = 'dataLoaded';
             this.loadArea(['data'], true);
-        }
-        else {
+        } else {
             this.storeSelectorShow();
         }
-    },
+    }
 
-    setStoreId : function(id){
+    setStoreId(id) {
         this.storeId = id;
         this.storeSelectorHide();
         this.sidebarShow();
-        //this.loadArea(['header', 'sidebar','data'], true);
         this.dataShow();
         this.loadArea(['header', 'data'], true);
-    },
+    }
 
-    setCurrencyId : function(id){
+    setCurrencyId(id) {
         this.currencyId = id;
-        //this.loadArea(['sidebar', 'data'], true);
         this.loadArea(['data'], true);
-    },
+    }
 
-    setCurrencySymbol : function(symbol){
+    setCurrencySymbol(symbol) {
         this.currencySymbol = symbol;
-    },
+    }
 
-    selectAddress : function(el, container){
-        id = el.value;
-        if (id.length == 0) {
-            id = '0';
-        }
-        if(this.addresses[id]){
-            this.fillAddressFields(container, this.addresses[id]);
-        }
-        else{
-            this.fillAddressFields(container, {});
+    selectAddress(el, container) {
+        const addressId = el.value || '0';
+        const address = this.addresses[addressId] ?? {};
+
+        this.fillAddressFields(container, address);
+
+        if (this.isBillingField(el.id) && this.shippingAsBilling) {
+            this.fillAddressFields(this.shippingAddressContainer, address);
         }
 
-        var data = this.serializeData(container);
-        data[el.name] = id;
-        if(this.isShippingField(container) && !this.isShippingMethodReseted){
+        const data = {
+            ...this.serializeData(container),
+            [el.name]: addressId,
+        };
+
+        if (this.isShippingField(container) && !this.isShippingMethodReseted) {
             this.resetShippingMethod(data);
-        }
-        else{
+        } else {
             this.saveData(data);
         }
-    },
+    }
 
-    isShippingField : function(fieldId){
-        if(this.shippingAsBilling){
-            return fieldId.include('billing');
+    isShippingField(fieldId) {
+        if (this.shippingAsBilling) {
+            return fieldId.includes('billing');
         }
-        return fieldId.include('shipping');
-    },
+        return fieldId.includes('shipping');
+    }
 
-    isBillingField : function(fieldId){
-        return fieldId.include('billing');
-    },
+    isBillingField(fieldId) {
+        return fieldId.includes('billing');
+    }
 
-    bindAddressFields : function(container) {
-        var fields = $(container).select('input', 'select', 'textarea');
-        for(var i=0;i<fields.length;i++){
-            Event.observe(fields[i], 'change', this.changeAddressField.bind(this));
+    bindAddressFields(containerId) {
+        const container = this.getContainerEl(containerId);
+        for (const field of container.querySelectorAll('input, select, textarea')) {
+            field.addEventListener('change', this.changeAddressField.bind(this));
         }
-    },
+    }
 
-    changeAddressField : function(event){
-        var field = Event.element(event);
-        var re = /[^\[]*\[([^\]]*)_address\]\[([^\]]*)\](\[(\d)\])?/;
-        var matchRes = field.name.match(re);
+    changeAddressField(event) {
+        const field = event.target;
+        const pattern = /[^\[]*\[([^\]]*)_address\]\[([^\]]*)\](\[(\d)\])?/;
 
-        if (!matchRes) {
+        const matchRes = field.name.match(pattern);
+        if (matchRes === null) {
             return;
         }
 
-        var type = matchRes[1];
-        var name = matchRes[2];
-        var data;
+        const type = matchRes[1];
+        const name = matchRes[2];
+        const data = this.isBillingField(field.id)
+              ? this.serializeData(this.billingAddressContainer)
+              : this.serializeData(this.shippingAddressContainer);
 
-        if(this.isBillingField(field.id)){
-            data = this.serializeData(this.billingAddressContainer);
-        }
-        else{
-            data = this.serializeData(this.shippingAddressContainer);
+        if (type === 'billing' && this.shippingAsBilling && !this.isShippingMethodReseted) {
+            data['reset_shipping'] = true;
         }
-        data = data.toObject();
-
-        if( (type == 'billing' && this.shippingAsBilling && !this.isShippingMethodReseted)
-            || (type == 'shipping' && !this.shippingAsBilling && !this.isShippingMethodReseted) ) {
+        if (type === 'shipping' && !this.shippingAsBilling && !this.isShippingMethodReseted) {
             data['reset_shipping'] = true;
         }
 
-        data['order['+type+'_address][customer_address_id]'] = $('order-'+type+'_address_customer_address_id').value;
+        const addressId = document.getElementById(`order-${type}_address_customer_address_id`).value;
+        data[`order[${type}_address][customer_address_id]`] = addressId;
 
-        if (type == 'billing' && this.shippingAsBilling) {
+        if (type === 'billing' && this.shippingAsBilling) {
             this.copyDataFromBillingToShipping(field);
         }
 
@@ -210,594 +195,628 @@ AdminOrder.prototype = {
             this.resetShippingMethod(data);
         } else {
             this.saveData(data);
-            if (!this.isShippingMethodReseted && (name == 'country_id' || name == 'customer_address_id')) {
+            if (!this.isShippingMethodReseted && ['country_id', 'customer_address_id'].includes(name)) {
                 this.loadArea(['shipping_method', 'billing_method', 'totals', 'items'], true, data);
             }
         }
-    },
+    }
 
-    copyDataFromBillingToShipping : function(field) {
-        var shippingId = $(field).identify().replace('-billing_', '-shipping_');
-        var inputField = $(shippingId);
-        if (inputField) {
-            inputField.setValue($(field).getValue());
-            if (inputField.changeUpdater) {
-                inputField.changeUpdater();
-            }
-            $(this.shippingAddressContainer).select('select').each(function(el){
-                el.disable();
-            });
+    copyDataFromBillingToShipping(fromEl) {
+        const toEl = document.getElementById(fromEl.id.replace('-billing_', '-shipping_'));
+        if (!toEl) {
+            return;
         }
-    },
 
-    fillAddressFields : function(container, data){
-        var regionIdElem = false;
-        var regionIdElemValue = false;
+        toEl.value = fromEl.value;
+        if (typeof toEl.changeUpdater === 'function') {
+            toEl.changeUpdater();
+        }
+
+        const container = this.getContainerEl(this.shippingAddressContainer);
+        for (const field of container.getElementsByTagName('select')) {
+            setElementDisable(field, true);
+        }
+    }
+
+    fillAddressFields(containerId, data) {
+        const pattern = /[^\[]*\[[^\]]*\]\[([^\]]*)\](\[(\d)\])?/;
+
+        const container = this.getContainerEl(containerId);
+        for (const field of container.querySelectorAll('input, select, textarea')) {
 
-        var fields = $(container).select('input', 'select', 'textarea');
-        var re = /[^\[]*\[[^\]]*\]\[([^\]]*)\](\[(\d)\])?/;
-        for(var i=0;i<fields.length;i++){
             // skip input type file @Security error code: 1000
-            if (fields[i].tagName.toLowerCase() == 'input' && fields[i].type.toLowerCase() == 'file') {
+            if (field.tagName === 'INPUT' && field.type.toLowerCase() === 'file') {
                 continue;
             }
-            var matchRes = fields[i].name.match(re);
+
+            const matchRes = field.name.match(pattern);
             if (matchRes === null) {
                 continue;
             }
-            var name = matchRes[1];
-            var index = matchRes[3];
-
-            if (index){
-                // multiply line
-                if (data[name]){
-                    var values = data[name].split("\n");
-                    fields[i].value = values[index] ? values[index] : '';
-                } else {
-                    fields[i].value = '';
-                }
-            } else if (fields[i].tagName.toLowerCase() == 'select' && fields[i].multiple) {
+
+            const name = matchRes[1];
+            const index = matchRes[3];
+            const value = data[name] ?? '';
+
+            if (index) {
+                // multiple line
+                field.value = value.split("\n")[index] ?? '';
+            } else if (field.tagName === 'SELECT' && field.multiple) {
                 // multiselect
-                if (data[name]) {
-                    values = [''];
-                    if (Object.isString(data[name])) {
-                        values = data[name].split(',');
-                    } else if (Object.isArray(data[name])) {
-                        values = data[name];
-                    }
-                    fields[i].setValue(values);
+                let values = [''];
+                if (Array.isArray(value)) {
+                    values = value;
+                } else if (typeof value === 'string' || value instanceof String) {
+                    values = value.split(',');
+                }
+                for (const optionEl of field.options) {
+                    optionEl.selected = values.inclues(option.value);
                 }
             } else {
-                fields[i].setValue(data[name] ? data[name] : '');
+                field.value = value;
             }
-
-            if (fields[i].changeUpdater) fields[i].changeUpdater();
-            if (name == 'region' && data['region_id'] > 0 && !data['region']){
-                fields[i].value = data['region_id'];
+            if (typeof field.changeUpdater === 'function') {
+                field.changeUpdater();
+            }
+            if (name === 'region' && data['region_id'] > 0 && !data['region']) {
+                field.value = data['region_id'];
             }
         }
-    },
+    }
 
-    disableShippingAddress : function(flag) {
+    disableShippingAddress(flag) {
         this.shippingAsBilling = flag;
-        if ($('order-shipping_address_customer_address_id')) {
-            $('order-shipping_address_customer_address_id').disabled = flag;
+
+        const addressSelectEl = document.getElementById('order-shipping_address_customer_address_id');
+        if (addressSelectEl) {
+            addressSelectEl.disabled = flag;
         }
 
-        if ($(this.shippingAddressContainer)) {
-            var dataFields = $(this.shippingAddressContainer).select('input', 'select', 'textarea');
-            for (var i = 0; i < dataFields.length; i++) {
-                dataFields[i].disabled = flag;
-            }
-            var buttons = $(this.shippingAddressContainer).select('button');
-            // Add corresponding class to buttons while disabling them
-            for (i = 0; i < buttons.length; i++) {
-                buttons[i].disabled = flag;
-                if (flag) {
-                    buttons[i].addClassName('disabled');
-                } else {
-                    buttons[i].removeClassName('disabled');
-                }
-            }
+        const container = this.getContainerEl(this.shippingAddressContainer);
+        if (!container) {
+            return;
         }
-    },
 
-    turnOffShippingFields : function() {
-        if ($(this.shippingAddressContainer)) {
-            var dataFields = $(this.shippingAddressContainer).select('input', 'select', 'textarea', 'button');
-            for (var i = 0; i < dataFields.length; i++) {
-                dataFields[i].removeAttribute('name');
-                dataFields[i].removeAttribute('id');
-                dataFields[i].readOnly = true;
-            }
+        for (const field of container.querySelectorAll('input, select, textarea')) {
+            field.disabled = flag;
+        }
+        for (const button of container.querySelectorAll('button')) {
+            button.disabled = flag;
+            button.classList.toggle('disabled', flag);
         }
-    },
+    }
 
-    setShippingAsBilling : function(flag){
-        this.disableShippingAddress(flag);
-        if(flag){
-            var data = this.serializeData(this.billingAddressContainer);
+    turnOffShippingFields() {
+        const container = this.getContainerEl(this.shippingAddressContainer);
+        if (!container) {
+            return;
         }
-        else{
-            var data = this.serializeData(this.shippingAddressContainer);
+
+        for (const field of container.querySelectorAll('input, select, textarea, button')) {
+            field.removeAttribute('name');
+            field.removeAttribute('id');
+            field.readOnly = true;
         }
-        data = data.toObject();
-        data['shipping_as_billing'] = flag ? 1 : 0;
-        data['reset_shipping'] = 1;
-        this.loadArea(['shipping_method', 'billing_method', 'shipping_address', 'totals', 'giftmessage'], true, data);
-    },
+    }
+
+    setShippingAsBilling(flag) {
+        this.disableShippingAddress(flag);
+        this.loadArea(['shipping_method', 'billing_method', 'shipping_address', 'totals', 'giftmessage'], true, {
+            ...this.serializeData(flag ? this.billingAddressContainer : this.shippingAddressContainer),
+            shipping_as_billing: flag ? 1 : 0,
+            reset_shipping: 1,
+        });
+    }
 
-    resetShippingMethod : function(data){
-        data['reset_shipping'] = 1;
+    resetShippingMethod(data) {
         this.isShippingMethodReseted = true;
-        this.loadArea(['shipping_method', 'billing_method', 'totals', 'giftmessage', 'items'], true, data);
-    },
+        this.loadArea(['shipping_method', 'billing_method', 'totals', 'giftmessage', 'items'], true, {
+            ...data,
+            reset_shipping: 1,
+        });
+    }
 
-    loadShippingRates : function(){
+    loadShippingRates() {
         this.isShippingMethodReseted = false;
-        this.loadArea(['shipping_method', 'totals'], true, {collect_shipping_rates: 1});
-    },
+        this.loadArea(['shipping_method', 'totals'], true, {
+            collect_shipping_rates: 1,
+        });
+    }
 
-    setShippingMethod : function(method){
-        var data = {};
-        data['order[shipping_method]'] = method;
-        data['shipping_as_billing'] = this.shippingAsBilling ? 1 : 0;
-        this.loadArea(['shipping_method', 'totals', 'billing_method'], true, data);
-    },
+    setShippingMethod(method) {
+        this.loadArea(['shipping_method', 'totals', 'billing_method'], true, {
+            'order[shipping_method]': method,
+            shipping_as_billing: this.shippingAsBilling ? 1 : 0,
+        });
+    }
 
-    switchPaymentMethod : function(method){
+    switchPaymentMethod(method) {
         this.setPaymentMethod(method);
-        var data = {};
-        data['order[payment_method]'] = method;
-        this.loadArea(['card_validation'], true, data);
-    },
-
-    setPaymentMethod : function(method){
-        if (this.paymentMethod && $('payment_form_'+this.paymentMethod)) {
-            var form = 'payment_form_'+this.paymentMethod;
-            [form + '_before', form, form + '_after'].each(function(el) {
-                var block = $(el);
-                if (block) {
-                    block.hide();
-                    block.select('input', 'select', 'textarea').each(function(field) {
-                        field.disabled = true;
-                    });
+        this.loadArea(['card_validation'], true, {
+            'order[payment_method]': method,
+        });
+    }
+
+    setPaymentMethod(method) {
+        if (this.paymentMethod && document.getElementById(`payment_form_${this.paymentMethod}`)) {
+            const form = `payment_form_${this.paymentMethod}`;
+            for (const blockId of [`${form}_before`, form, `${form}_after`]) {
+                const blockEl = document.getElementById(blockId);
+                if (!blockEl) {
+                    continue;
                 }
-            });
+                toggleVis(blockEl, false);
+                for (const field of blockEl.querySelectorAll('input, select, textarea')) {
+                    field.disabled = true;
+                }
+            }
         }
 
-        if(!this.paymentMethod || method){
-            $('order-billing_method_form').select('input', 'select', 'textarea').each(function(elem){
-                if(elem.type != 'radio') elem.disabled = true;
-            });
+        if (!this.paymentMethod || method) {
+            const billingMethodForm = document.getElementById('order-billing_method_form');
+            for (const field of billingMethodForm.querySelectorAll('input, select, textarea')) {
+                if (field.type !== 'radio') {
+                    field.disabled = true;
+                }
+            }
         }
 
-        if ($('payment_form_'+method)){
-            this.paymentMethod = method;
-            var form = 'payment_form_'+method;
-            [form + '_before', form, form + '_after'].each(function(el) {
-                var block = $(el);
-                if (block) {
-                   block.show();
-                   block.select('input', 'select', 'textarea').each(function(field) {
-                       field.disabled = false;
-                       if (!el.include('_before') && !el.include('_after') && !field.bindChange) {
-                           field.bindChange = true;
-                           field.paymentContainer = form; /** @deprecated after 1.4.0.0-rc1 */
-                           field.method = method;
-                           field.observe('change', this.changePaymentData.bind(this));
-                        }
-                    },this);
+        if (document.getElementById(`payment_form_${method}`)) {
+            const form = `payment_form_${method}`;
+            for (const blockId of [`${form}_before`, form, `${form}_after`]) {
+                const blockEl = document.getElementById(blockId);
+                if (!blockEl) {
+                    continue;
+                }
+                toggleVis(blockEl, true);
+                for (const field of blockEl.querySelectorAll('input, select, textarea')) {
+                    field.disabled = false;
+                    if (!blockId.endsWith('_before') && !blockId.endsWith('_after') && !field.bindChange) {
+                        field.bindChange = true;
+                        field.method = method;
+                        field.addEventListener('change', this.changePaymentData.bind(this));
+                    }
                 }
-            },this);
+            }
+            this.paymentMethod = method;
         }
-    },
+    }
 
-    changePaymentData : function(event){
-        var elem = Event.element(event);
-        if(elem && elem.method){
-            var data = this.getPaymentData(elem.method);
-            if (data) {
-                 this.loadArea(['card_validation'], true, data);
-            } else {
-                return;
-            }
+    changePaymentData(event) {
+        const method = event.target.method;
+        if (!method) {
+            return;
+        }
+
+        const data = this.getPaymentData(method);
+        if (data) {
+            this.loadArea(['card_validation'], true, data);
         }
-    },
+    }
 
-    getPaymentData : function(currentMethod){
-        if (typeof(currentMethod) == 'undefined') {
+    getPaymentData(currentMethod) {
+        if (typeof currentMethod === 'undefined') {
             if (this.paymentMethod) {
                 currentMethod = this.paymentMethod;
             } else {
                 return false;
             }
         }
-        var data = {};
-        var fields = $('payment_form_' + currentMethod).select('input', 'select');
-        for(var i=0;i<fields.length;i++){
-            data[fields[i].name] = fields[i].getValue();
+
+        const data = {};
+
+        const paymentForm = document.getElementById(`payment_form_${currentMethod}`);
+        for (const field of paymentForm.querySelectorAll('input, select')) {
+            data[field.name] = field.value;
         }
-        if ((typeof data['payment[cc_type]']) != 'undefined' && (!data['payment[cc_type]'] || !data['payment[cc_number]'])) {
+
+        if (typeof data['payment[cc_type]'] !== 'undefined' && (!data['payment[cc_type]'] || !data['payment[cc_number]'])) {
             return false;
         }
         return data;
-    },
+    }
 
-    applyCoupon : function(code){
-        this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {'order[coupon][code]':code, reset_shipping: true});
-    },
+    applyCoupon(code) {
+        this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {
+            'order[coupon][code]': code,
+            reset_shipping: true,
+        });
+    }
 
-    addProduct : function(id){
-        this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {add_product:id, reset_shipping: true});
-    },
+    addProduct(id) {
+        this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {
+            add_product: id,
+            reset_shipping: true,
+        });
+    }
 
-    removeQuoteItem : function(id){
-        this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true,
-            {remove_item:id, from:'quote',reset_shipping: true});
-    },
+    removeQuoteItem(id) {
+        this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {
+            remove_item: id,
+            from: 'quote',
+            reset_shipping: true,
+        });
+    }
 
-    moveQuoteItem : function(id, to){
-        this.loadArea(['sidebar_'+to, 'items', 'shipping_method', 'totals', 'billing_method'], this.getAreaId('items'),
-            {move_item:id, to:to, reset_shipping: true});
-    },
+    moveQuoteItem(id, to) {
+        this.loadArea([`sidebar_${to}`, 'items', 'shipping_method', 'totals', 'billing_method'], this.getAreaId('items'), {
+            move_item: id,
+            to,
+            reset_shipping: true,
+        });
+    }
 
-    productGridShow : function(buttonElement){
-        this.productGridShowButton = buttonElement;
-        Element.hide(buttonElement);
+    productGridShow() {
         this.showArea('search');
-    },
-
-    productGridRowInit : function(grid, row){
-        var checkbox = $(row).select('.checkbox')[0];
-        var inputs = $(row).select('.input-text');
-        if (checkbox && inputs.length > 0) {
-            checkbox.inputElements = inputs;
-            for (var i = 0; i < inputs.length; i++) {
-                var input = inputs[i];
-                input.checkboxElement = checkbox;
-
-                var product = this.gridProducts.get(checkbox.value);
-                if (product) {
-                    var defaultValue = product[input.name];
-                    if (defaultValue) {
-                        if (input.name == 'giftmessage') {
-                            input.checked = true;
-                        } else {
-                            input.value = defaultValue;
-                        }
-                    }
-                }
+    }
 
-                input.disabled = !checkbox.checked || input.hasClassName('input-inactive');
+    productGridHide() {
+        this.hideArea('search');
+        this.gridProducts.clear();
+        productConfigure.clean('quote_items');
+        sales_order_create_search_gridJsObject?.resetFilter();
+    }
 
-                Event.observe(input,'keyup', this.productGridRowInputChange.bind(this));
-                Event.observe(input,'change',this.productGridRowInputChange.bind(this));
-            }
+    productGridRowInit(grid, row) {
+        const checkbox = row.querySelector('.checkbox');
+        const inputs = row.querySelectorAll('.input-text');
+        if (!checkbox || inputs.length === 0) {
+            return;
         }
-    },
 
-    productGridRowInputChange : function(event){
-        var element = Event.element(event);
-        if (element && element.checkboxElement && element.checkboxElement.checked){
-            if (element.name!='giftmessage' || element.checked) {
-                this.gridProducts.get(element.checkboxElement.value)[element.name] = element.value;
-            } else if (element.name=='giftmessage' && this.gridProducts.get(element.checkboxElement.value)[element.name]) {
-                delete(this.gridProducts.get(element.checkboxElement.value)[element.name]);
+        checkbox.inputElements = inputs;
+        for (const input of inputs) {
+            input.checkboxElement = checkbox;
+
+            const defaultValue = this.gridProducts.get(checkbox.value)?.[input.name];
+            if (defaultValue) {
+                if (input.name === 'giftmessage') {
+                    input.checked = true;
+                } else {
+                    input.value = defaultValue;
+                }
             }
+
+            input.disabled = !checkbox.checked || input.classList.contains('input-inactive');
+            input.addEventListener('keyup', this.productGridRowInputChange.bind(this));
+            input.addEventListener('change',this.productGridRowInputChange.bind(this));
         }
-    },
-
-    productGridRowClick : function(grid, event){
-        var trElement = Event.findElement(event, 'tr');
-        var qtyElement = trElement.select('input[name="qty"]')[0];
-        var eventElement = Event.element(event);
-        var isInputCheckbox = eventElement.tagName == 'INPUT' && eventElement.type == 'checkbox';
-        var isInputQty = eventElement.tagName == 'INPUT' && eventElement.name == 'qty';
-        if (trElement && !isInputQty) {
-            var checkbox = Element.select(trElement, 'input[type="checkbox"]')[0];
-            var confLink = Element.select(trElement, 'a')[0];
-            var priceColl = Element.select(trElement, '.price')[0];
-            if (checkbox) {
-                // processing non composite product
-                if (confLink.readAttribute('disabled')) {
-                    var checked = isInputCheckbox ? checkbox.checked : !checkbox.checked;
-                    grid.setCheckboxChecked(checkbox, checked);
-                // processing composite product
-                } else if (isInputCheckbox && !checkbox.checked) {
-                    grid.setCheckboxChecked(checkbox, false);
-                // processing composite product
-                } else if (!isInputCheckbox || (isInputCheckbox && checkbox.checked)) {
-                    var listType = confLink.readAttribute('list_type');
-                    var productId = confLink.readAttribute('product_id');
-                    if (typeof this.productPriceBase[productId] == 'undefined') {
-                        var priceBase = priceColl.innerHTML.match(/.*?([\d,]+\.?\d*)/);
-                        if (!priceBase) {
-                            this.productPriceBase[productId] = 0;
-                        } else {
-                            this.productPriceBase[productId] = parseFloat(priceBase[1].replace(/,/g,''));
-                        }
-                    }
-                    productConfigure.setConfirmCallback(listType, function() {
-                        // sync qty of popup and qty of grid
-                        var confirmedCurrentQty = productConfigure.getCurrentConfirmedQtyElement();
-                        if (qtyElement && confirmedCurrentQty && !isNaN(confirmedCurrentQty.value)) {
-                            qtyElement.value = confirmedCurrentQty.value;
-                        }
-                        // calc and set product price
-                        var productPrice = parseFloat(this._calcProductPrice() + this.productPriceBase[productId]);
-                        priceColl.innerHTML = this.currencySymbol + productPrice.toFixed(2);
-                        // and set checkbox checked
-                        grid.setCheckboxChecked(checkbox, true);
-                    }.bind(this));
-                    productConfigure.setCancelCallback(listType, function() {
-                        if (!productConfigure.itemConfigured(listType, productId)) {
-                            grid.setCheckboxChecked(checkbox, false);
-                        }
-                    });
-                    productConfigure.setShowWindowCallback(listType, function() {
-                        // sync qty of grid and qty of popup
-                        var formCurrentQty = productConfigure.getCurrentFormQtyElement();
-                        if (formCurrentQty && qtyElement && qtyElement.value && !isNaN(qtyElement.value)) {
-                            formCurrentQty.value = qtyElement.value;
-                        }
-                    }.bind(this));
-                    productConfigure.showItemConfiguration(listType, productId);
+    }
+
+    productGridRowInputChange(event) {
+        const inputEl = event.target;
+        const checkboxEl = inputEl.checkboxElement;
+        if (!checkboxEl?.checked) {
+            return;
+        }
+
+        const product = this.gridProducts.get(checkboxEl.value);
+        if (inputEl.name === 'giftmessage') {
+            delete product[inputEl.name];
+        } else if (inputEl.checked) {
+            product[inputEl.name] = inputEl.value;
+        }
+    }
+
+    productGridRowClick(grid, event) {
+        const trElement = event.target.closest('tr');
+        const qtyElement = trElement.querySelector('input[name=qty]');
+        const isInputCheckbox = event.target.tagName === 'INPUT' && event.target.type === 'checkbox';
+        const isInputQty = event.target.tagName === 'INPUT' && event.target.name === 'qty';
+        if (!trElement || isInputQty) {
+            return;
+        }
+
+        const checkbox = trElement.querySelector('input[type=checkbox]');
+        const confLink = trElement.querySelector('a');
+        const priceCol = trElement.querySelector('.price');
+        if (!checkbox) {
+            return;
+        }
+
+        // processing non composite product
+        if (confLink.readAttribute('disabled')) {
+            const checked = isInputCheckbox ? checkbox.checked : !checkbox.checked;
+            grid.setCheckboxChecked(checkbox, checked);
+            return;
+        }
+
+        // processing composite product
+        if (isInputCheckbox && !checkbox.checked) {
+            grid.setCheckboxChecked(checkbox, false);
+            return;
+        }
+
+        // processing composite product
+        if (!isInputCheckbox || (isInputCheckbox && checkbox.checked)) {
+            const listType = confLink.getAttribute('list_type');
+            const productId = confLink.getAttribute('product_id');
+            if (typeof this.productPriceBase[productId] === 'undefined') {
+                const priceBase = priceCol.textContent.match(/.*?([\d,]+\.?\d*)/);
+                if (!priceBase) {
+                    this.productPriceBase[productId] = 0;
+                } else {
+                    this.productPriceBase[productId] = parseFloat(priceBase[1].replace(/,/g, ''));
                 }
             }
+
+            productConfigure.setConfirmCallback(listType, () => {
+                // sync qty of popup and qty of grid
+                this._syncQuantityElements(productConfigure.getCurrentConfirmedQtyElement(), qtyElement);
+
+                // calc and set product price
+                const productPrice = parseFloat(this._calcProductPrice() + this.productPriceBase[productId]);
+                priceCol.textContent = this.currencySymbol + productPrice.toFixed(2);
+
+                // set checkbox checked
+                grid.setCheckboxChecked(checkbox, true);
+            });
+
+            productConfigure.setCancelCallback(listType, () => {
+                if (!productConfigure.itemConfigured(listType, productId)) {
+                    grid.setCheckboxChecked(checkbox, false);
+                }
+            });
+
+            productConfigure.setShowWindowCallback(listType, () => {
+                // sync qty of grid and qty of popup
+                this._syncQuantityElements(qtyElement, productConfigure.getCurrentFormQtyElement());
+            });
+
+            // Show product configure pop up window
+            productConfigure.showItemConfiguration(listType, productId);
+        }
+    }
+
+    _syncQuantityElements(fromEl, toEl) {
+        if (!(toEl instanceof HTMLInputElement)) {
+            return;
+        }
+        if (fromEl?.value && !isNaN(fromEl.value)) {
+            toEl.value = fromEl.value;
+        }
+        if (toEl.value < 1) {
+            toEl.value = 1;
         }
-    },
+    }
 
     /**
      * Calc product price through its options
      */
-    _calcProductPrice: function () {
-        var productPrice = 0;
-        var getPriceFields = function (elms) {
-            var productPrice = 0;
-            var getPrice = function (elm) {
-                var optQty = 1;
-                if (elm.hasAttribute('qtyId')) {
-                    if (!$(elm.getAttribute('qtyId')).value) {
-                        return 0;
-                    } else {
-                        optQty = parseFloat($(elm.getAttribute('qtyId')).value);
-                    }
-                }
-                if (elm.hasAttribute('price') && !elm.disabled) {
-                    return parseFloat(elm.readAttribute('price')) * optQty;
-                }
-                return 0;
-            };
-            for(var i = 0; i < elms.length; i++) {
-                if (elms[i].type == 'select-one' || elms[i].type == 'select-multiple') {
-                    for(var ii = 0; ii < elms[i].options.length; ii++) {
-                        if (elms[i].options[ii].selected) {
-                            productPrice += getPrice(elms[i].options[ii]);
-                        }
-                    }
-                }
-                else if (((elms[i].type == 'checkbox' || elms[i].type == 'radio') && elms[i].checked)
-                        || ((elms[i].type == 'file' || elms[i].type == 'text' || elms[i].type == 'textarea' || elms[i].type == 'hidden')
-                            && Form.Element.getValue(elms[i]))
-                ) {
-                    productPrice += getPrice(elms[i]);
+    _calcProductPrice() {
+        let productPrice = 0;
+        for (const el of productConfigure.getCurrentConfirmedBlock().querySelectorAll('input, select, textarea')) {
+            if (['select-one', 'select-multiple'].includes(el.type)) {
+                for (const option of el.selectedOptions) {
+                    productPrice += this._calcOptionPrice(option);
                 }
             }
-            return productPrice;
-        }.bind(this);
-        productPrice += getPriceFields($(productConfigure.confirmedCurrentId).getElementsByTagName('input'));
-        productPrice += getPriceFields($(productConfigure.confirmedCurrentId).getElementsByTagName('select'));
-        productPrice += getPriceFields($(productConfigure.confirmedCurrentId).getElementsByTagName('textarea'));
+            if (['checkbox', 'radio'].includes(el.type) && el.checked) {
+                productPrice += this._calcOptionPrice(el);
+            }
+            if (['file', 'text', 'textarea', 'hidden'].includes(el.type) && el.value) {
+                productPrice += this._calcOptionPrice(el);
+            }
+        }
         return productPrice;
-    },
+    }
 
-    productGridCheckboxCheck : function(grid, element, checked){
+    _calcOptionPrice(inputEl) {
+        let optQty = 1;
+        if (inputEl.hasAttribute('qtyId')) {
+            const qtyEl = document.getElementById(inputEl.getAttribute('qtyId'));
+            if (!qtyEl.value) {
+                return 0;
+            }
+            optQty = parseFloat(qtyEl.value);
+        }
+        if (inputEl.hasAttribute('price') && !inputEl.disabled) {
+            return parseFloat(inputEl.getAttribute('price')) * optQty;
+        }
+        return 0;
+    }
+
+    productGridCheckboxCheck(grid, element, checked) {
         if (checked) {
-            if(element.inputElements) {
-                this.gridProducts.set(element.value, {});
-                var product = this.gridProducts.get(element.value);
-                for (var i = 0; i < element.inputElements.length; i++) {
-                    var input = element.inputElements[i];
-                    if (!input.hasClassName('input-inactive')) {
-                        input.disabled = false;
-                        if (input.name == 'qty' && !input.value) {
-                            input.value = 1;
+            if (element.inputElements) {
+                const product = {};
+                for (const inputEl of element.inputElements) {
+                    if (!inputEl.classList.contains('input-inactive')) {
+                        inputEl.disabled = false;
+                        if (inputEl.name === 'qty' && !inputEl.value) {
+                            inputEl.value = 1;
                         }
                     }
-
-                    if (input.checked || input.name != 'giftmessage') {
-                        product[input.name] = input.value;
-                    } else if (product[input.name]) {
-                        delete(product[input.name]);
+                    if (inputEl.checked || inputEl.name !== 'giftmessage') {
+                        product[inputEl.name] = inputEl.value;
                     }
                 }
+                this.gridProducts.set(element.value, product);
             }
         } else {
-            if(element.inputElements){
-                for(var i = 0; i < element.inputElements.length; i++) {
-                    element.inputElements[i].disabled = true;
-                }
+            for (const inputEl of element.inputElements ?? []) {
+                inputEl.disabled = true;
             }
-            this.gridProducts.unset(element.value);
+            this.gridProducts.delete(element.value);
         }
-        grid.reloadParams = {'products[]':this.gridProducts.keys()};
-    },
+        grid.reloadParams = {'products[]': this.gridProducts.keys()};
+    }
 
     /**
      * Submit configured products to quote
      */
-    productGridAddSelected : function(){
-        if(this.productGridShowButton) Element.show(this.productGridShowButton);
-        var area = ['search', 'items', 'shipping_method', 'totals', 'giftmessage','billing_method'];
+    productGridAddSelected() {
+        const area = ['search', 'items', 'shipping_method', 'totals', 'giftmessage','billing_method'];
+
         // prepare additional fields and filtered items of products
-        var fieldsPrepare = {};
-        var itemsFilter = [];
-        var products = this.gridProducts.toObject();
-        for (var productId in products) {
-            itemsFilter.push(productId);
-            var paramKey = 'item['+productId+']';
-            for (var productParamKey in products[productId]) {
-                paramKey += '['+productParamKey+']';
-                fieldsPrepare[paramKey] = products[productId][productParamKey];
+        const fieldsPrepare = {};
+        const itemsFilter = [];
+        for (const [productId, product] of this.gridProducts) {
+            let paramKey = `item[${productId}]`;
+            for (const [key, value] of Object.entries(product)) {
+                paramKey += `[${key}]`;
+                fieldsPrepare[paramKey] = value;
             }
+            itemsFilter.push(productId);
         }
+
         this.productConfigureSubmit('product_to_add', area, fieldsPrepare, itemsFilter);
-        productConfigure.clean('quote_items');
-        this.hideArea('search');
-        this.gridProducts = $H({});
-    },
+        this.productGridHide();
+    }
 
-    selectCustomer : function(grid, event){
-        var element = Event.findElement(event, 'tr');
-        if (element.title){
+    selectCustomer(grid, event) {
+        const element = event.target.closest('tr');
+        if (element?.title) {
             this.setCustomerId(element.title);
         }
-    },
+    }
 
-    customerSelectorHide : function(){
+    customerSelectorHide() {
         this.hideArea('customer-selector');
-    },
+    }
 
-    customerSelectorShow : function(){
+    customerSelectorShow() {
         this.showArea('customer-selector');
-    },
+    }
 
-    storeSelectorHide : function(){
+    storeSelectorHide() {
         this.hideArea('store-selector');
-    },
+    }
 
-    storeSelectorShow : function(){
+    storeSelectorShow() {
         this.showArea('store-selector');
-    },
+    }
 
-    dataHide : function(){
+    dataHide() {
         this.hideArea('data');
-    },
+    }
 
-    dataShow : function(){
-        if ($('submit_order_top_button')) {
-            $('submit_order_top_button').show();
+    dataShow() {
+        const submitButton = document.getElementById('submit_order_top_button');
+        if (submitButton) {
+            toggleVis(submitButton, true);
         }
         this.showArea('data');
-    },
-
-    clearShoppingCart : function(confirmMessage){
-        if (confirm(confirmMessage)) {
-            this.collectElementsValue = false;
-            order.sidebarApplyChanges({'sidebar[empty_customer_cart]': 1});
-        }
-    },
-
-    sidebarApplyChanges : function(auxiliaryParams) {
-        if ($(this.getAreaId('sidebar'))) {
-            var data = {};
-            if (this.collectElementsValue) {
-                var elems = $(this.getAreaId('sidebar')).select('input');
-                for (var i=0; i < elems.length; i++) {
-                    if (elems[i].getValue()) {
-                        data[elems[i].name] = elems[i].getValue();
-                    }
-                }
-            }
-            if (auxiliaryParams instanceof Object) {
-                for (var paramName in auxiliaryParams) {
-                    data[paramName] = String(auxiliaryParams[paramName]);
-                }
-            }
-            data.reset_shipping = true;
-            this.loadArea(['sidebar', 'items', 'shipping_method', 'billing_method','totals', 'giftmessage'], true, data);
+    }
+
+    clearShoppingCart(confirmMessage) {
+        if (!confirm(confirmMessage)) {
+            return;
         }
-    },
 
-    sidebarHide : function(){
-        if(this.storeId === false && $('page:left') && $('page:container')){
-            $('page:left').hide();
-            $('page:container').removeClassName('container');
-            $('page:container').addClassName('container-collapsed');
+        this.collectElementsValue = false;
+        order.sidebarApplyChanges({
+            'sidebar[empty_customer_cart]': 1,
+        });
+    }
+
+    sidebarApplyChanges(auxiliaryParams) {
+        const sidebarEl = this.getAreaEl('sidebar');
+        if (!sidebarEl) {
+            return;
         }
-    },
 
-    sidebarShow : function(){
-        if($('page:left') && $('page:container')){
-            $('page:left').show();
-            $('page:container').removeClassName('container-collapsed');
-            $('page:container').addClassName('container');
+        const data = {};
+        if (this.collectElementsValue) {
+            var elems = $(this.getAreaId('sidebar')).select('input');
+            for (var i=0; i < elems.length; i++) {
+                if (elems[i].getValue()) {
+                    data[elems[i].name] = elems[i].getValue();
+                }
+            }
+        }
+        if (typeof auxiliaryParams === 'object' && auxiliaryParams !== null) {
+            Object.assign(data, auxiliaryParams);
         }
-    },
+        data.reset_shipping = true;
+        this.loadArea(['sidebar', 'items', 'shipping_method', 'billing_method','totals', 'giftmessage'], true, data);
+    }
+
+    sidebarHide() {
+        return;
+    }
+
+    sidebarShow() {
+        return;
+    }
 
     /**
      * Show configuration of product and add handlers on submit form
      *
      * @param productId
      */
-    sidebarConfigureProduct: function (listType, productId, itemId) {
+    sidebarConfigureProduct(listType, productId, itemId) {
         // create additional fields
-        var params = {};
-        params.reset_shipping = true;
-        params.add_product = productId;
-        this.prepareParams(params);
-        for (var i in params) {
-            if (params[i] === null) {
-                unset(params[i]);
-            } else if (typeof(params[i]) == 'boolean') {
-                params[i] = params[i] ? 1 : 0;
+        const params = this.prepareParams({
+            reset_shipping: true,
+            add_product: productId,
+        });
+
+        const fields = [];
+        for (const [name, value] of Object.entries(params)) {
+            if (value === null) {
+                delete params[name];
+                continue;
             }
+            if (typeof value === 'boolean') {
+                params[name] = value ? 1 : 0;
+            }
+            fields.push(new Element('input', {type: 'hidden', name, value}));
         }
-        var fields = [];
-        for (var name in params) {
-            fields.push(new Element('input', {type: 'hidden', name: name, value: params[name]}));
-        }
+
         // add additional fields before triggered submit
-        productConfigure.setBeforeSubmitCallback(listType, function() {
+        productConfigure.setBeforeSubmitCallback(listType, () => {
             productConfigure.addFields(fields);
-        }.bind(this));
+        });
+
         // response handler
-        productConfigure.setOnLoadIFrameCallback(listType, function(response) {
-            if (!response.ok) {
-                return;
-            }
+        productConfigure.setOnLoadIFrameCallback(listType, () => {
             this.loadArea(['items', 'shipping_method', 'billing_method','totals', 'giftmessage'], true);
-        }.bind(this));
+        });
+
         // show item configuration
-        itemId = itemId ? itemId : productId;
-        productConfigure.showItemConfiguration(listType, itemId);
+        productConfigure.showItemConfiguration(listType, itemId || productId);
         return false;
-    },
+    }
 
-    removeSidebarItem : function(id, from){
-        this.loadArea(['sidebar_'+from], 'sidebar_data_'+from, {remove_item:id, from:from});
-    },
+    removeSidebarItem(id, from) {
+        this.loadArea([`sidebar_${from}`], `sidebar_data_${from}`, {remove_item:id, from});
+    }
+
+    itemsUpdate() {
+        const area = ['sidebar', 'items', 'shipping_method', 'billing_method', 'totals', 'giftmessage'];
 
-    itemsUpdate : function(){
-        var area = ['sidebar', 'items', 'shipping_method', 'billing_method','totals', 'giftmessage'];
         // prepare additional fields
-        var fieldsPrepare = {update_items: 1};
-        var info = $('order-items_grid').select('input', 'select', 'textarea');
-        for(var i=0; i<info.length; i++){
-            if(!info[i].disabled && (info[i].type != 'checkbox' || info[i].checked)) {
-                fieldsPrepare[info[i].name] = info[i].getValue();
+        const fieldsPrepare = {
+            update_items: 1,
+        };
+
+        const gridEl = document.getElementById('order-items_grid');
+        for (const field of gridEl.querySelectorAll('input, select, textarea')) {
+            if (!field.disabled && (field.type !== 'checkbox' || field.checked)) {
+                fieldsPrepare[field.name] = field.value;
             }
         }
-        fieldsPrepare = Object.extend(fieldsPrepare, this.productConfigureAddFields);
+
+        Object.extend(fieldsPrepare, this.productConfigureAddFields);
         this.productConfigureSubmit('quote_items', area, fieldsPrepare);
         this.orderItemChanged = false;
-    },
-
-    itemsOnchangeBind : function(){
-        var elems = $('order-items_grid').select('input', 'select', 'textarea');
-        for(var i=0; i<elems.length; i++){
-            if(!elems[i].bindOnchange){
-                elems[i].bindOnchange = true;
-                elems[i].observe('change', this.itemChange.bind(this));
+    }
+
+    itemsOnchangeBind() {
+        const gridEl = document.getElementById('order-items_grid');
+        for (const field of gridEl.querySelectorAll('input, select, textarea')) {
+            if (!field.bindOnchange) {
+                field.bindOnchange = true;
+                field.addEventListener('change', this.itemChange.bind(this));
             }
         }
-    },
+    }
 
-    itemChange : function(event){
+    itemChange(event) {
         this.giftmessageOnItemChange(event);
         this.orderItemChanged = true;
-    },
+    }
 
     /**
      * Submit batch of configured products
@@ -807,21 +826,20 @@ AdminOrder.prototype = {
      * @param fieldsPrepare
      * @param itemsFilter
      */
-    productConfigureSubmit : function(listType, area, fieldsPrepare, itemsFilter) {
-        // prepare loading areas and build url
-        area = this.prepareArea(area);
-        this.loadingAreas = area;
-        var url = this.loadBaseUrl + 'block/' + area + '?isAjax=true';
+    productConfigureSubmit(listType, area, fieldsPrepare, itemsFilter) {
+        this.loadingAreas = this.prepareArea(area);
 
         // prepare additional fields
-        fieldsPrepare = this.prepareParams(fieldsPrepare);
-        fieldsPrepare.reset_shipping = 1;
-        fieldsPrepare.json = 1;
+        const params = this.prepareParams({
+            ...fieldsPrepare,
+            reset_shipping: true,
+        });
+        params.json = 1;
 
         // create fields
-        var fields = [];
-        for (var name in fieldsPrepare) {
-            fields.push(new Element('input', {type: 'hidden', name: name, value: fieldsPrepare[name]}));
+        const fields = [];
+        for (const [name, value] of Object.entries(params)) {
+            fields.push(new Element('input', {type: 'hidden', name, value}));
         }
         productConfigure.addFields(fields);
 
@@ -831,204 +849,217 @@ AdminOrder.prototype = {
         }
 
         // prepare and do submit
-        productConfigure.addListType(listType, {urlSubmit: url});
-        productConfigure.setOnLoadIFrameCallback(listType, function(response){
-            this.loadAreaResponseHandler(response);
-        }.bind(this));
+        productConfigure.addListType(listType, {
+            urlSubmit: setRouteParams(this.loadBaseUrl, { block: this.loadingAreas }),
+        });
+
+        // Submit
+        productConfigure.setOnLoadIFrameCallback(listType, this.loadAreaResponseHandler.bind(this));
         productConfigure.submit(listType);
+
         // clean
         this.productConfigureAddFields = {};
-    },
+    }
 
     /**
      * Show configuration of quote item
      *
      * @param itemId
      */
-    showQuoteItemConfiguration: function(itemId){
-        var listType = 'quote_items';
-        var qtyElement = $('order-items_grid').select('input[name="item\['+itemId+'\]\[qty\]"]')[0];
-        productConfigure.setConfirmCallback(listType, function() {
+    showQuoteItemConfiguration(itemId) {
+        const listType = 'quote_items';
+        const qtyElement = document.querySelector(`#order-items_grid input[name="item[${itemId}][qty]"]`);
+
+        productConfigure.setConfirmCallback(listType, () => {
             // sync qty of popup and qty of grid
-            var confirmedCurrentQty = productConfigure.getCurrentConfirmedQtyElement();
-            if (qtyElement && confirmedCurrentQty && !isNaN(confirmedCurrentQty.value)) {
-                qtyElement.value = confirmedCurrentQty.value;
-            }
-            this.productConfigureAddFields['item['+itemId+'][configured]'] = 1;
+            this._syncQuantityElements(productConfigure.getCurrentConfirmedQtyElement(), qtyElement);
+            this.productConfigureAddFields[`item[${itemId}][configured]`] = 1;
+        });
 
-        }.bind(this));
-        productConfigure.setShowWindowCallback(listType, function() {
+        productConfigure.setShowWindowCallback(listType, () => {
             // sync qty of grid and qty of popup
-            var formCurrentQty = productConfigure.getCurrentFormQtyElement();
-            if (formCurrentQty && qtyElement && !isNaN(qtyElement.value)) {
-                formCurrentQty.value = qtyElement.value;
-            }
-        }.bind(this));
+            this._syncQuantityElements(qtyElement, productConfigure.getCurrentFormQtyElement());
+        });
+
+        // Show product configure pop up window
         productConfigure.showItemConfiguration(listType, itemId);
-    },
-
-    accountFieldsBind : function(container){
-        if($(container)){
-            var fields = $(container).select('input', 'select', 'textarea');
-            for(var i=0; i<fields.length; i++){
-                if(fields[i].id == 'group_id'){
-                    fields[i].observe('change', this.accountGroupChange.bind(this));
-                }
-                else{
-                    fields[i].observe('change', this.accountFieldChange.bind(this));
-                }
+    }
+
+    accountFieldsBind(containerId) {
+        const container = this.getContainerEl(containerId);
+        if (!container) {
+            return;
+        }
+        for (const field of container.querySelectorAll('input, select, textarea')) {
+            if (field.id === 'group_id') {
+                field.addEventListener('change', this.accountGroupChange.bind(this));
+            } else {
+                field.addEventListener('change', this.accountFieldChange.bind(this));
             }
         }
-    },
+    }
 
-    accountGroupChange : function(){
-        this.loadArea(['data'], true, this.serializeData('order-form_account').toObject());
-    },
+    accountGroupChange() {
+        this.loadArea(['data'], true, this.serializeData('order-form_account'));
+    }
 
-    accountFieldChange : function(){
+    accountFieldChange() {
         this.saveData(this.serializeData('order-form_account'));
-    },
+    }
 
-    commentFieldsBind : function(container){
-        if($(container)){
-            var fields = $(container).select('input', 'textarea');
-            for(var i=0; i<fields.length; i++)
-                fields[i].observe('change', this.commentFieldChange.bind(this));
+    commentFieldsBind(containerId) {
+        const container = this.getContainerEl(containerId);
+        if (!container) {
+            return;
+        }
+        for (const field of container.querySelectorAll('input, textarea')) {
+            field.addEventListener('change', this.commentFieldChange.bind(this));
         }
-    },
+    }
 
-    commentFieldChange : function(){
+    commentFieldChange() {
         this.saveData(this.serializeData('order-comment'));
-    },
+    }
 
-    giftmessageFieldsBind : function(container){
-        if($(container)){
-            var fields = $(container).select('input', 'textarea');
-            for(var i=0; i<fields.length; i++)
-                fields[i].observe('change', this.giftmessageFieldChange.bind(this));
+    giftmessageFieldsBind(containerId) {
+        const container = this.getContainerEl(containerId);
+        if (!container) {
+            return;
         }
-    },
+        for (const field of container.querySelectorAll('input, textarea')) {
+            field.addEventListener('change', this.giftmessageFieldChange.bind(this));
+        }
+    }
 
-    giftmessageFieldChange : function(){
+    giftmessageFieldChange() {
         this.giftMessageDataChanged = true;
-    },
-
-    giftmessageOnItemChange : function(event) {
-        var element = Event.element(event);
-        if(element.name.indexOf("giftmessage") != -1 && element.type == "checkbox" && !element.checked) {
-            var messages = $("order-giftmessage").select('textarea');
-            var name;
-            for(var i=0; i<messages.length; i++) {
-                name = messages[i].id.split("_");
-                if(name.length < 2) continue;
-                if (element.name.indexOf("[" + name[1] + "]") != -1 && messages[i].value != "") {
-                    alert("First, clean the Message field in Gift Message form");
-                    element.checked = true;
-                }
+    }
+
+    giftmessageOnItemChange(event) {
+        if (!event.target.name.includes('giftmessage') || event.target.type !== 'checkbox' || !event.target.checked) {
+            return;
+        }
+        for (const message of document.getElementById('order-giftmessage').querySelectorAll('textarea')) {
+            const name = message.id.split('_');
+            if (name.length > 1 && event.target.name.includes(`[${name[1]}]`) && message.value !== '') {
+                alert(Translator.translate('First, clean the Message field in Gift Message form'));
+                event.target.checked = true;
             }
         }
-    },
+    }
 
-    loadArea : function(area, indicator, params){
-        var url = this.loadBaseUrl;
-        if (area) {
-            area = this.prepareArea(area);
-            url += 'block/' + area;
-        }
-        if (indicator === true) indicator = 'html-body';
+    async loadArea(area, loaderArea, params) {
+        area = this.prepareArea(area);
         params = this.prepareParams(params);
         params.json = true;
-        if (!this.loadingAreas) this.loadingAreas = [];
-        if (indicator) {
+
+        const url = setRouteParams(this.loadBaseUrl, { block: area });
+
+        if (loaderArea) {
             this.loadingAreas = area;
-            new Ajax.Request(url, {
-                parameters:params,
-                loaderArea: indicator,
-                onSuccess: function(transport) {
-                    var response = transport.responseText.evalJSON();
-                    this.loadAreaResponseHandler(response);
-                }.bind(this)
-            });
+        } else if (!this.loadingAreas) {
+            this.loadingAreas = [];
         }
-        else {
-            new Ajax.Request(url, {parameters:params,loaderArea: indicator});
+
+        try {
+            const result = await mahoFetch(url, {
+                method: 'POST',
+                body: new URLSearchParams(params),
+                loaderArea,
+            });
+            if (loaderArea) {
+                this.loadAreaResponseHandler(result);
+            }
+        } catch (error) {
+            console.error(error);
+            alert(error.message);
         }
-        if (typeof productConfigure != 'undefined' && area instanceof Array && area.indexOf('items') != -1) {
+        if (typeof productConfigure !== 'undefined' && Array.isArray(area) && area.includes('items')) {
             productConfigure.clean('quote_items');
         }
-    },
+    }
 
-    loadAreaResponseHandler : function (response){
-        if (response.error) {
-            alert(response.message);
-        }
-        if(response.ajaxExpired && response.ajaxRedirect) {
-            setLocation(response.ajaxRedirect);
-        }
-        if(!this.loadingAreas){
+    loadAreaResponseHandler(response) {
+        if (!this.loadingAreas) {
             this.loadingAreas = [];
         }
-        if(typeof this.loadingAreas == 'string'){
+        if (typeof this.loadingAreas === 'string') {
             this.loadingAreas = [this.loadingAreas];
         }
-        if(this.loadingAreas.indexOf('message') == -1) {
+        if (!this.loadingAreas.includes('message')) {
             this.loadingAreas.push('message');
         }
 
-        for(var i=0; i<this.loadingAreas.length; i++){
-            var id = this.loadingAreas[i];
-            if($(this.getAreaId(id))){
-                if ('message' != id || response[id]) {
-                    var wrapper = new Element('div');
-                    wrapper.update(response[id] ? response[id] : '');
-                    $(this.getAreaId(id)).update(wrapper);
-                }
-                if ($(this.getAreaId(id)).callback) {
-                    this[$(this.getAreaId(id)).callback]();
-                }
+        for (const area of this.loadingAreas) {
+            const areaEl = document.getElementById(this.getAreaId(area));
+            if (!areaEl) {
+                continue;
+            }
+            if (area !== 'message' || response[area]) {
+                updateElementHtmlAndExecuteScripts(areaEl, response[area] ?? '');
+            }
+            if (typeof this[areaEl.callback] === 'function') {
+                this[areaEl.callback]();
             }
         }
-    },
+    }
 
-    prepareArea : function(area){
-        if (this.giftMessageDataChanged) {
-            return area.without('giftmessage');
+    prepareArea(area) {
+        if (Array.isArray(area) && this.giftMessageDataChanged) {
+            return area.filter(val => val !=='giftmessage');
         }
         return area;
-    },
+    }
 
-    saveData : function(data){
+    saveData(data) {
         this.loadArea(false, false, data);
-    },
+    }
 
-    showArea : function(area){
-        var id = this.getAreaId(area);
-        if($(id)) {
-            $(id).show();
+    showArea(area) {
+        const areaId = this.getAreaId(area);
+        const areaEl = document.getElementById(areaId);
+        if (areaEl) {
+            toggleVis(areaEl, true);
             this.areaOverlay();
         }
-    },
+    }
 
-    hideArea : function(area){
-        var id = this.getAreaId(area);
-        if($(id)) {
-            $(id).hide();
+    hideArea(area) {
+        const areaId = this.getAreaId(area);
+        const areaEl = document.getElementById(areaId);
+        if (areaEl) {
+            toggleVis(areaEl, false);
             this.areaOverlay();
         }
-    },
+    }
 
-    areaOverlay : function()
-    {
-        $H(order.overlayData).each(function(e){
-            e.value.fx();
-        });
-    },
+    areaOverlay() {
+        for (const overlay of Object.values(this.overlayData)) {
+            overlay.fx();
+        }
+    }
 
-    getAreaId : function(area){
-        return 'order-'+area;
-    },
+    getAreaId(area) {
+        return `order-${area}`;
+    }
+
+    getAreaEl(area) {
+        return document.getElementById(this.getAreaId(area));
+    }
+
+    getContainerEl(container) {
+        if (typeof container === 'string' || container instanceof String) {
+            container = document.getElementById(container);
+        }
+        if (container instanceof Element) {
+            return container;
+        }
+    }
 
-    prepareParams : function(params){
+    prepareParams(params) {
+        if (typeof params?.toObject === 'function') {
+            params = params.toObject();
+        }
         if (!params) {
             params = {};
         }
@@ -1044,232 +1075,167 @@ AdminOrder.prototype = {
         if (!params.currency_id) {
             params.currency_id = this.currencyId;
         }
-        if (!params.form_key) {
-            params.form_key = FORM_KEY;
-        }
-        var data = this.serializeData('order-billing_method');
-        if (data) {
-            data.each(function(value) {
-                params[value[0]] = value[1];
-            });
+        for (const [name, value] of Object.entries(this.serializeData('order-billing_method'))) {
+            params[name] = value;
         }
         return params;
-    },
-
-    serializeData : function(container){
-        var fields = $(container).select('input', 'select', 'textarea');
-        var data = Form.serializeElements(fields, true);
-
-        return $H(data);
-    },
-
-    toggleCustomPrice: function(checkbox, elemId, tierBlock) {
-        if (checkbox.checked) {
-            $(elemId).disabled = false;
-            $(elemId).show();
-            if($(tierBlock)) $(tierBlock).hide();
-        }
-        else {
-            $(elemId).disabled = true;
-            $(elemId).hide();
-            if($(tierBlock)) $(tierBlock).show();
-        }
-    },
-
-    submit : function()
-    {
-        if (this.orderItemChanged) {
-            if (confirm('You have item changes')) {
-                if (editForm.submit()) {
-                    disableElements('save');
-                }
-            } else {
-                this.itemsUpdate();
-            }
-        } else {
-            if (editForm.submit()) {
-                disableElements('save');
-            }
+    }
+
+    serializeData(containerId) {
+        const container = this.getContainerEl(containerId);
+        if (!container) {
+            return {};
+        }
+        const data = {};
+        for (const field of container.querySelectorAll('input, select, textarea')) {
+            data[field.name] = field.value;
+        }
+        return data;
+    }
+
+    toggleCustomPrice(checkbox, priceInputId, tierBlockId) {
+        const priceInput = document.getElementById(priceInputId);
+        if (priceInput) {
+            priceInput.disabled = !checkbox.checked;
+            toggleVis(priceInput, checkbox.checked)
+        }
+
+        const tierBlock = document.getElementById(tierBlockId);
+        if (tierBlock) {
+            toggleVis(tierBlock, !checkbox.checked)
         }
-    },
+    }
 
-    overlay : function(elId, show, observe)
-    {
-        if (typeof(show) == 'undefined') { show = true; }
+    submit() {
+        const confirmText = Translator.translate('You have unsaved item changes. Discard and proceed with checkout?');
+        if (this.orderItemChanged && !confirm(confirmText)) {
+            this.itemsUpdate();
+            return;
+        }
+        if (editForm.submit()) {
+            disableElements('save');
+        }
+    }
 
-        var orderObj = this;
-        var obj = this.overlayData.get(elId);
-        if (!obj) {
-            obj = {
-                show: show,
+    overlay(elId, show = true) {
+        if (!this.overlayData.has(elId)) {
+            this.overlayData.set(elId, {
                 el: elId,
-                order: orderObj,
-                fx: function(event) {
+                order: this,
+                fx(event) {
                     this.order.processOverlay(this.el, this.show);
                 }
-            };
-            obj.bfx = obj.fx.bindAsEventListener(obj);
-            this.overlayData.set(elId, obj);
-        }
-        else {
-            obj.show = show;
-            Event.stopObserving(window, 'resize', obj.bfx);
+            });
         }
 
-        Event.observe(window, 'resize', obj.bfx);
-
+        this.overlayData.get(elId).show = show;
         this.processOverlay(elId, show);
-    },
-
-    processOverlay : function(elId, show)
-    {
-        var el = $(elId);
+    }
 
+    processOverlay(elId, show) {
+        const el = document.getElementById(elId);
         if (!el) {
             return false;
         }
 
-        var parentEl = el.up(1);
-        if (show) {
-            parentEl.removeClassName('ignore-validate');
-        }
-        else {
-            parentEl.addClassName('ignore-validate');
-        }
-
-        parentEl.setStyle({position: 'relative'});
-        el.setStyle({
-            display: show ? 'none' : '',
-            position: 'absolute',
-            backgroundColor: '#999999',
-            opacity: 0.8,
-            width: parentEl.getWidth() + 'px',
-            height: parentEl.getHeight() + 'px',
-            top: 0,
-            left: 0
-        });
-    },
+        toggleVis(el, !show);
+        el.parentElement.classList.toggle('ignore-validate', !show);
+    }
 
-    validateVat: function(parameters)
-    {
-        var params = {
-            country: $(parameters.countryElementId).value,
-            vat: $(parameters.vatElementId).value
+    async validateVat(parameters) {
+        const params = {
+            country: document.getElementById(parameters.countryElementId).value,
+            vat: document.getElementById(parameters.vatElementId).value
         };
 
         if (this.storeId !== false) {
             params.store_id = this.storeId;
         }
 
-        var currentCustomerGroupId = $(parameters.groupIdHtmlId).value;
-
-        new Ajax.Request(parameters.validateUrl, {
-            parameters: params,
-            onSuccess: function(response) {
-                var message = '';
-                var groupChangeRequired = false;
-                try {
-                    response = response.responseText.evalJSON();
+        let message = '';
+        let groupChangeRequired = false;
+        const currentCustomerGroupId = document.getElementById(parameters.groupIdHtmlId).value;
 
-                    if (true === response.valid) {
-                        message = parameters.vatValidMessage;
-                        if (currentCustomerGroupId != response.group) {
-                            message = parameters.vatValidAndGroupChangeMessage;
-                            groupChangeRequired = true;
-                        }
-                    } else if (response.success) {
-                        message = parameters.vatInvalidMessage.replace(/%s/, params.vat);
-                        groupChangeRequired = true;
-                    } else {
-                        message = parameters.vatValidationFailedMessage;
-                        groupChangeRequired = true;
-                    }
+        try {
+            const result = await mahoFetch(parameters.validateUrl, {
+                method: 'POST',
+                body: new URLSearchParams(params),
+            });
 
-                } catch (e) {
-                    message = parameters.vatErrorMessage;
-                }
-                if (!groupChangeRequired) {
-                    alert(message);
-                }
-                else {
-                    this.processCustomerGroupChange(parameters.groupIdHtmlId, message, response.group);
+            if (result.valid === true) {
+                if (currentCustomerGroupId == result.group || !result.group) {
+                    message = parameters.vatValidMessage;
+                } else {
+                    message = parameters.vatValidAndGroupChangeMessage;
+                    groupChangeRequired = true;
                 }
-            }.bind(this)
-        });
-    },
-
-    processCustomerGroupChange: function(groupIdHtmlId, message, groupId)
-    {
-        var currentCustomerGroupId = $(groupIdHtmlId).value;
-        var currentCustomerGroupTitle = $$('#' + groupIdHtmlId + ' > option[value=' + currentCustomerGroupId + ']')[0].text;
-        var customerGroupOption = $$('#' + groupIdHtmlId + ' > option[value=' + groupId + ']')[0];
-        var confirmText = message.replace(/%s/, customerGroupOption.text);
-        confirmText = confirmText.replace(/%s/, currentCustomerGroupTitle);
-        if (confirm(confirmText)) {
-            $$('#' + groupIdHtmlId + ' option').each(function(o) {
-                o.selected = o.readAttribute('value') == groupId;
-            });
-            this.accountGroupChange();
+            } else if (result.success === true) {
+                message = parameters.vatInvalidMessage.replace(/%s/, params.vat);
+                groupChangeRequired = true;
+            } else {
+                message = parameters.vatValidationFailedMessage;
+                groupChangeRequired = true;
+            }
+            if (groupChangeRequired && !result.group) {
+                message = parameters.vatErrorMessage;
+                groupChangeRequired = false;
+            }
+            if (groupChangeRequired) {
+                this.processCustomerGroupChange(parameters.groupIdHtmlId, message, result.group);
+            } else {
+                alert(message);
+            }
+        } catch (error) {
+            console.error(error);
+            alert(parameters.vatErrorMessage)
+        }
+    }
+
+    processCustomerGroupChange(groupIdHtmlId, message, groupId) {
+        const groupSelectEl = document.getElementById(groupIdHtmlId);
+        const oldGroup = groupSelectEl.querySelector(`option[value="${groupSelectEl.value}"]`).textContent;
+        const newGroup = groupSelectEl.querySelector(`option[value="${groupId}"]`).textContent;
+
+        const confirmText = message.replace('%s', newGroup).replace('%s', oldGroup);
+        if (!confirm(confirmText)) {
+            return;
+        }
+
+        for (const option of groupSelectEl.options) {
+            option.selected = option.value == groupId;
         }
+        this.accountGroupChange();
     }
 };
 
-var OrderFormArea = Class.create();
-OrderFormArea.prototype = {
-    _name: null,
-    _node: null,
-    _parent: null,
-    _callbackName: null,
+class OrderFormArea {
+    _name = null;
+    _node = null;
+    _parent =  null;
+    _callbackName = null;
+
+    constructor() {
+        this.initialize(...arguments);
+    }
 
-    initialize: function(name, node, parent){
+    initialize(name, node, parent) {
         this._name = name;
         this._parent = parent;
-        this._callbackName = node.callback;
-        if (typeof this._callbackName == 'undefined') {
-            this._callbackName = name + 'Loaded';
-            node.callback = this._callbackName;
-        }
-        parent[this._callbackName] = parent[this._callbackName].wrap((function (proceed){
+        this._callbackName = node.callback ?? `${name}Loaded`;
+
+        parent[this._callbackName] = wrapFunction(parent[this._callbackName].bind(parent), (proceed) => {
             proceed();
             this.onLoad();
-        }).bind(this));
+        });
 
         this.setNode(node);
-    },
+    }
 
-    setNode: function(node){
-        if (!node.callback) {
-            node.callback = this._callbackName;
-        }
+    setNode(node) {
         this.node = node;
-    },
-
-    onLoad: function(){
+        this.node.callback ??= this._callbackName;
     }
-};
-
-var ControlButton = Class.create();
-ControlButton.prototype = {
-    _label: '',
-    _node: null,
 
-    initialize: function(label){
-        this._label = label;
-        this._node = new Element('button', {
-            'class': 'scalable add',
-            'type':  'button'
-        });
-    },
-
-    onClick: function(){
-    },
-
-    insertIn: function(element, position){
-        var node = Object.extend(this._node),
-            content = {};
-        node.observe('click', this.onClick);
-        node.update('<span>' + this._label + '</span>');
-        content[position] = node;
-        Element.insert(element, content);
+    onLoad() {
     }
 };
diff --git a/public/js/mage/adminhtml/tools.js b/public/js/mage/adminhtml/tools.js
index 8f85c4da8e..9e7df35718 100644
--- a/public/js/mage/adminhtml/tools.js
+++ b/public/js/mage/adminhtml/tools.js
@@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
diff --git a/public/skin/adminhtml/default/default/boxes.css b/public/skin/adminhtml/default/default/boxes.css
index e9531a1750..2bcc0b38e4 100644
--- a/public/skin/adminhtml/default/default/boxes.css
+++ b/public/skin/adminhtml/default/default/boxes.css
@@ -768,6 +768,14 @@ ul.item-options li { padding-left:.7em; }
 .entry-edit .order-address .validate-vat { text-align:right; padding:10px 0 0; width:96%; }
 .order-search-items .entry-edit .grid    { height:610px; overflow:auto; }
 .order-search-items .entry-edit .grid table { width:99.9%; }
+#order-data .ignore-validate:has(> .overlay) {
+    position: relative;
+    .overlay {
+        display: flex; flex-direction: column; align-items: center; justify-content: center;
+        position: absolute; z-index: 1; top: 0; bottom: 0; left: 0; right: 0;
+        background: rgba(255,255,255,0.8);
+    }
+}
 /* .create-order-totals                 { background:url(images/bg_create_order_totals.gif) repeat-y 50% 0 !important; } */
 
 /* Product Configuration Popup */


Reviews:
user: login: justinbeaty
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T21:36:58Z
user: login: fballiano
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T21:41:24Z
user: login: justinbeaty
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T21:48:07Z
user: login: fballiano
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T21:49:10Z
user: login: fballiano
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T21:50:40Z
user: login: justinbeaty
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T22:08:33Z
user: login: fballiano
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T22:13:35Z
user: login: justinbeaty
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T22:21:08Z
user: login: fballiano
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T22:22:43Z
user: login: justinbeaty
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T22:23:51Z
user: login: fballiano
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T22:26:05Z
user: login: justinbeaty
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T22:27:02Z

Review comments:
diff_hunk: @@ -16,10 +17,25 @@
  */
 class Mage_Adminhtml_Block_Sales_Order_Create_Search extends Mage_Adminhtml_Block_Sales_Order_Create_Abstract
 {
+    /**
+     * Contains button descriptions to be shown at the top of accordion
+     * @var list<array>
+     */
+    protected $_buttons = [];
path: app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Search.php
user: login: justinbeaty
body: In this class I didn't add native PHP types to methods and properties and I used the underscore.

If we add them, then this block would work differently than the other `Mage_Adminhtml_Block_Sales_Order_Create_*` blocks, and I'm not sure that's great.

Any thoughts?
created_at: 2025-03-17T21:36:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -16,10 +17,25 @@
  */
 class Mage_Adminhtml_Block_Sales_Order_Create_Search extends Mage_Adminhtml_Block_Sales_Order_Create_Abstract
 {
+    /**
+     * Contains button descriptions to be shown at the top of accordion
+     * @var list<array>
+     */
+    protected $_buttons = [];
path: app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Search.php
user: login: fballiano
body: mmm tricky situation but I think coherence makes more sense in this case, so I'd live it as you implement it already. If in the future we mass add types then at least all those classes will still be similar between each other.
created_at: 2025-03-17T21:41:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -16,10 +17,25 @@
  */
 class Mage_Adminhtml_Block_Sales_Order_Create_Search extends Mage_Adminhtml_Block_Sales_Order_Create_Abstract
 {
+    /**
+     * Contains button descriptions to be shown at the top of accordion
+     * @var list<array>
+     */
+    protected $_buttons = [];
path: app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Search.php
user: login: justinbeaty
body: > If in the future we mass add types then at least all those classes will still be similar between each other.

That's what I was thinking too. Maybe someday... :)
created_at: 2025-03-17T21:48:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -16,10 +17,25 @@
  */
 class Mage_Adminhtml_Block_Sales_Order_Create_Search extends Mage_Adminhtml_Block_Sales_Order_Create_Abstract
 {
+    /**
+     * Contains button descriptions to be shown at the top of accordion
+     * @var list<array>
+     */
+    protected $_buttons = [];
path: app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Search.php
user: login: fballiano
body: maybe someday
created_at: 2025-03-17T21:49:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
path: public/js/mage/adminhtml/tools.js
user: login: fballiano
body: do you think this wrapfunction is absolutely necessary?

my worry is that we replace prototype but add a series of functions (really we didn't do that, but I say that just to explain the thought) that, at the end of the day, do the same thing.

since the wrapFunction is used only 2 times, maybe...


created_at: 2025-03-17T21:50:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
path: public/js/mage/adminhtml/tools.js
user: login: justinbeaty
body: I definitely understand the concern, but I'm a bit worried about breaking admin checkout modules. Any other place in the code and I wouldn't hesitate to remove it.

A module would use it something like this without overriding the parent method:

```js
window.order.dataArea.onLoad = wrapFunction(window.order.dataArea.onLoad, (proceed) => {
    // Some custom logic

    // Call parent method, which calls its parent method, etc
    proceed();

    // More custom logic
});
```

Which seems more elegant than code that does stuff like this:
```js
    // stop Magento's card_validation ajax from running                                                                            
    AdminOrder.prototype._getPaymentDataOriginal = AdminOrder.prototype.getPaymentData;
    AdminOrder.prototype.getPaymentData = function(currentMethod) {
        return (currentMethod == 'gene_braintree_creditcard' || currentMethod == 'gene_braintree_paypal') ? false : this._getPaymentDataOriginal(currentMethod);
    };
```

The above is from the Gene Braintree module, but there was [similar code](https://github.com/MahoCommerce/maho/pull/68/files#diff-8e79ab312dd6720c6ed65cb6ca2fc3de5016d021faf6c304cfb452e959d7ff51) in core, so the pattern is likely common in modules.
created_at: 2025-03-17T22:08:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
path: public/js/mage/adminhtml/tools.js
user: login: fballiano
body: but they would break anyway the moment we remove prototype if they were using `$something.wrap()` right? maybe i'm missing something

```javascript                                                                   
    AdminOrder.prototype._getPaymentDataOriginal = AdminOrder.prototype.getPaymentData;
    AdminOrder.prototype.getPaymentData = function(currentMethod) {
        return (currentMethod == 'gene_braintree_creditcard' || currentMethod == 'gene_braintree_paypal') ? false : this._getPaymentDataOriginal(currentMethod);
    };
```

I kinda like this one more 😅 just personal preference, it seems easier to read, but as you wish
created_at: 2025-03-17T22:13:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
path: public/js/mage/adminhtml/tools.js
user: login: justinbeaty
body: > but they would break anyway the moment we remove prototype if they were using $something.wrap() right? maybe i'm missing something

It would break, but the fix would be simply changing `myFunc.wrap(myCallable)` with `wrapFunction(myFunc, myCallable)`.

If we are going to break it more than that, then maybe we could create some sort of events that modules could hook into so that they don't need to overwrite methods on the prototype chain?
created_at: 2025-03-17T22:21:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
path: public/js/mage/adminhtml/tools.js
user: login: fballiano
body: I see your point, although it would be difficult for people to know that this new function exist, I'm ok with it :-)
created_at: 2025-03-17T22:22:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
path: public/js/mage/adminhtml/tools.js
user: login: justinbeaty
body: Also, just a thought, but `Function.wrap` could be mapped to `wrapFunction` when we finally tackle #84, which would be nice that nothing would break.
created_at: 2025-03-17T22:23:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
path: public/js/mage/adminhtml/tools.js
user: login: fballiano
body: I tried to forget about that discussion 😅 cause it scares me a bit, let's link this comment there not to forget, will do now
created_at: 2025-03-17T22:26:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -832,3 +832,15 @@ function setQueryParams(url, params = {}) {
     }
     return url.toString();
 }
+
+/**
+ * Alternative to PrototypeJS's Function.wrap() method
+ */
+function wrapFunction(originalFn, wrapperFn) {
+    if (typeof originalFn !== 'function' || typeof wrapperFn !== 'function') {
+        throw new TypeError('Arguments must be functions');
+    }
+    return function() {
+        return wrapperFn(originalFn, ...arguments);
+    };
+}
path: public/js/mage/adminhtml/tools.js
user: login: justinbeaty
body: It's still on the back of my mind, I've just been trying to knock out some more of those `Class.create` instances beforehand.
created_at: 2025-03-17T22:27:02Z
author_association: CONTRIBUTOR

PR comments:
user: login: fballiano
created_at: 2025-03-17T22:04:56Z
body: I've tested order modification and couldn't find any problem (adding products, modifying, modifying options, configuring configurables, moving to wishlist/cart and back, changing address and syncing it with the billing address, shipping options, gift messages)
author_association: CONTRIBUTOR
user: login: fballiano
created_at: 2025-03-17T22:14:03Z
body: tested also order creation for the same things, couldn't find any problem
author_association: CONTRIBUTOR
user: login: fballiano
created_at: 2025-03-17T22:23:11Z
body: @justinbeaty I'm ok merging already, you tell me :-)
author_association: CONTRIBUTOR
user: login: justinbeaty
created_at: 2025-03-17T22:25:53Z
body: > @justinbeaty I'm ok merging already, you tell me :-)

I'm not against improving it in the future, but I think it's ready to merge now if you couldn't find any bugs. :smiley:
author_association: CONTRIBUTOR
user: login: fballiano
created_at: 2025-03-17T22:27:35Z
body: > > @justinbeaty I'm ok merging already, you tell me :-)
> 
> I'm not against improving it in the future, but I think it's ready to merge now if you couldn't find any bugs. 😃

this is already a wonderful effort and we've so many things going on that's totally ok as it is :-) :-)
author_association: CONTRIBUTOR