diff --git a/README.md b/README.md index 67b8857..e6b48ce 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,150 @@ # Form Masks for Elementor -Form Masks for Elementor create a custom control in field advanced tab for your customize your fields with masks. This plugin require the Elementor Pro (Form Widget). +The Form Masks for Elementor plugin adds a custom control in the Advanced Tab of form fields, allowing you to easily apply input masks. This plugin requires Elementor Pro (Form Widget). -The masks work with filed types 'text'. +Masks help users input structured data like phone numbers, dates, and more. +📽️ **Watch how it works:** https://www.youtube.com/watch?v=NYoykAUl4QE -#### Masks Available #### +## Features (FREE VERSION) -* Phone - '0000-0000' or '(000) 000-0000' or '(00) 0000-0000' +Ready-to-Use Masks +- **Phone**: + - 0000-0000 + - (000) 000-0000 + - (00) 0000-0000 + - (00) 0.0000-0000 +- **CPF (Brazilian ID)**: 000.000.000-00 +- **CNPJ (Brazilian Company ID)**: 00.000.000/0000-00 +- **Date**: 00/00/0000 +- **Time**: 00:00:00 +- **Date and Time**: 00/00/0000 00:00:00 +- **Money**: 000.000.000.000.000,00 +- **Postal Code (CEP)**: 00000-000 +- **Credit Card**: 0000-0000-0000-0000 +- **Credit Card Expiry Date**: 00/00 -* CPF - '000.000.000-00' +## Exclusive PRO Features +- **Custom Masks**: Create your own masks for maximum flexibility. Tailor your forms to any requirement. -* CNPJ - '00.000.000/0000-00' +🚀 **Coming Soon to the PRO Version** +- **Minimum character limits** to prevent incomplete submissions. +- **Prefix and suffix** options for masks. +- Built-in **validation for CPF, CNPJ**, and other formats. +- And much more… -* Date - '00/00/0000' +**PRO VERSION** +[Check the PRO version here](https://codecanyon.net/item/form-masks-for-elementor/25872641) -* Time - '00:00:00' +📽️ **Watch how it works:** +https://www.youtube.com/watch?v=XAuL43HFh8I -* Date and Time - '00/00/0000 00:00:00' +## Why Choose Form Masks for Elementor? +- Simple to set up and use. +- Increases form usability and data accuracy. +- Seamless integration with Elementor Pro. +- Regular updates with new features and improvements. -* Money - '000.000.000.000.000,00' +## More Plugins by Us -* CEP - '00000-000' +Discover other plugins to enhance your WordPress experience:: [WordPress Plugins](https://eduardovillao.me/wordpress-plugins/) -#### Masks Available #### +## Learn WordPress Tips -Check the [Pro version](https://codecanyon.net/item/form-masks-for-elementor/25872641) +Visit our blog for expert WordPress insights: [WordPress Tips](https://eduardovillao.me/blog/) -#### CREDITS #### +## Plugins Suggestions? -This plugin use the jQuery Mask library plugin. [jQuery Mask Plugin](https://github.com/igorescobar/jQuery-Mask-Plugin) +We’d love to hear from you! [plugins@eduardovillao.me](mailto:plugins@eduardovillao.me) + +## Installation + +1. Upload the plugin files to the WordPress +2. Activate the plugin in WordPress +3. Go to Widget Elementor Pro in your page +4. Select the custom mask for field in advanced field tab **(works only on text field)** +5. Go to the page for see the results (outside the Elementor editor) + +## Frequently Asked Questions + +1. Does the plugin work without Elementor Pro? + +No, the Form Masks for Elementor plugin requires Elementor Pro, as it relies on the Form Widget, which is only available in Elementor Pro. The plugin is not compatible with alternatives like Pro Elements or similar Elementor-like versions. + +2. Does the mask work on all field types? + +The plugin supports masks only for fields of type text. This means that masks will not work on fields like textarea, number, or other custom field types. + +3. Can I create my own masks? + +Yes, but this feature is available only in the PRO version. With the PRO version, you can define custom masks tailored to your specific needs. + +## Changelog +``` += 2.0 = +* New: add inputmode to open keybord with input context. Ex.: input with tel mask will open numeric keybord. +* New: remove jQuery mask library to use our own custom library. +* New: support to masks on popup improved. +* Changed: minimum PHP version changed to 7.4. +* Changed: improve build proccess to assets. + += 1.6.5 = +* Changed: compatibility with WordPress 6.7. +* Changed: code improvements. + += 1.6.4 = +* Changed: compatibility with WordPress 6.5. +* Changed: code improvements. + += 1.6.3 = +* Changed: compatibility with WordPress 6.2. + += 1.6.2 = +* Changed: compatibility with WordPress 6.1. + += 1.6.1 = +* Changed: compatibility with WordPress 6.0. + += 1.6 = +* Changed: support to string translations. +* Changed: code improvements. +* Changed: plugin header requirements improved. + += 1.5.3 = +* Tweak: conflict with jQuery old versions. + += 1.5.2 = +* Tweak: add hooks to pro version new features. +* Tweak: code improvements. + += 1.5.1 = +* Tweak: mask with 9 digits improved. +* Tweak: update and minify jQuery mask lib. + += 1.5 = +* New: support for Pro version. +* Tweak: improve JS mask files. +* Tweak: improve plugin code structure. + += 1.4.2 = +* Suppor to WordPress 5.8. + += 1.4.1 = +* Fix: active mask on popup is called by link, button and others. + += 1.4 = +* Support for Elementor Popup. + += 1.3 = +* Add new mask - Credit card and Credit card date. + += 1.2 = +* Add new mask - Phone 9 digits. + += 1.1 = +* Update the mask control. + += 1.0 = +* Initial release. +``` diff --git a/assets/js/elementor-mask.js b/assets/js/elementor-mask.js deleted file mode 100644 index c2d2c66..0000000 --- a/assets/js/elementor-mask.js +++ /dev/null @@ -1,43 +0,0 @@ -(function($) { - var fmeMasks = { - 'ev-tel': '0000-0000', - 'ev-tel-ddd': '(00) 0000-0000', - 'ev-tel-ddd9': '(00) 00000-0000', - 'ev-tel-us': '(000) 000-0000', - 'ev-cpf': '000.000.000-00', - 'ev-cnpj': '00.000.000/0000-00', - 'ev-money': '000.000.000.000.000,00', - 'ev-ccard': '0000-0000-0000-0000', - 'ev-ccard-valid': '00/00', - 'ev-cep': '00000-000', - 'ev-time': '00:00:00', - 'ev-date': '00/00/0000', - 'ev-date_time': '00/00/0000 00:00:00' - }; - - $(window).on( 'load', function() { - "use strict"; - $('.fme-mask-input').each(function() { - if($( this ).data('fme-mask') !== undefined) { - var inputMask = $( this ).data("fme-mask"); - if(inputMask == 'ev-cpf' || inputMask == 'ev-cnpj' || inputMask == 'ev-money') { - $( this ).mask( fmeMasks[inputMask], {reverse: true} ); - } else { - $( this ).mask( fmeMasks[inputMask] ); - } - } - }); - jQuery(document).on( 'elementor/popup/show', () => { - $('.fme-mask-input').each(function() { - if($( this ).data("fme-mask") !== undefined) { - var inputMask = $( this ).data("fme-mask"); - if(inputMask == 'ev-cpf' || inputMask == 'ev-cnpj' || inputMask == 'ev-money') { - $( this ).mask( fmeMasks[inputMask], {reverse: true} ); - } else { - $( this ).mask( fmeMasks[inputMask] ); - } - } - }); - }); - }); -})(jQuery); \ No newline at end of file diff --git a/assets/js/input-mask.min.js b/assets/js/input-mask.min.js new file mode 100644 index 0000000..d4ce81d --- /dev/null +++ b/assets/js/input-mask.min.js @@ -0,0 +1 @@ +new class{constructor(e){this.inputs=null,this.init(),this.mappedMasks=e||null,this.tokens={0:{validateRule:/\d/}}}init(){"complete"===document.readyState?(this.initModalListener(),this.getInput()):window.addEventListener("load",this.init.bind(this))}initModalListener(){const e=document.body;new MutationObserver((e=>{e.forEach((e=>{"childList"===e.type&&e.addedNodes.forEach((e=>{e.classList&&e.classList.contains("elementor-popup-modal")&&this.getInput()}))}))})).observe(e,{childList:!0})}getInput(){this.inputs=document.querySelectorAll("input[data-mask]"),this.inputs.forEach((e=>{this.handleMappedMasks(e),e.addEventListener("input",this.maskInput.bind(this)),e.addEventListener("keydown",this.handleBackspace.bind(this))}))}handleMappedMasks(e){if(!this.mappedMasks)return;const t=this.mappedMasks[e.dataset.mask];t&&(e.dataset.mask=t.mask,e.dataset.maskReverse=t.reverse,e.inputmode=t.inputmode)}maskInput(e){const t=e.target,s=t.dataset.mask,i=t.value,a=this.removeMask(s,i),n="true"===t.dataset.maskReverse,r=this.applyMask(a,s,n);t.value=r}removeMask(e,t){if(!e||!t)return t;const s=Object.keys(this.tokens),i=new RegExp(`[${s.join("")}]`,"g"),a=e.replace(i,"");return t.replace(new RegExp(`[${a}]`,"g"),"")}handleBackspace(e){const t=e.target;if("Backspace"===e.key&&t.selectionStart===t.selectionEnd){const s=t.selectionStart;if(s>0){const i=t.value;/\d/.test(i[s-1])||(e.preventDefault(),t.value=i.slice(0,s-1)+i.slice(s),t.setSelectionRange(s-1,s-1))}}}applyMask(e,t,s){if(!e)return e;let i="",a=0,n=t.split("");s&&(e=e.split("").reverse().join(""),n=n.reverse());for(let t=0;tg?h=10*d:e>=h&&e!==g?c.maskDigitPosMapOld[h]||(e=h,h=h-(k-l)-a,c.maskDigitPosMap[h]&&(h=e)):h>e&&(h=h+(l-k)+f)}return h},behaviour:function(d){d= -d||window.event;c.invalid=[];var e=b.data("mask-keycode");if(-1===a.inArray(e,l.byPassKeys)){e=c.getMasked();var h=c.getCaret(),g=b.data("mask-previus-value")||"";setTimeout(function(){c.setCaret(c.calculateCaretPosition(g))},a.jMaskGlobals.keyStrokeCompensation);c.val(e);c.setCaret(h);return c.callbacks(d)}},getMasked:function(a,b){var h=[],f=void 0===b?c.val():b+"",g=0,k=d.length,n=0,p=f.length,m=1,r="push",u=-1,w=0;b=[];if(e.reverse){r="unshift";m=-1;var x=0;g=k-1;n=p-1;var A=function(){return-1< -g&&-1=' ) ) { +if ( ! version_compare( PHP_VERSION, FME_PHP_MINIMUM_VERSION, '>=' ) ) { add_action( 'admin_notices', 'fme_admin_notice_php_version_fail' ); -} elseif( ! version_compare( get_bloginfo( 'version' ), FME_WP_MINIMUM_VERSION, '>=' ) ) { +} elseif ( ! version_compare( get_bloginfo( 'version' ), FME_WP_MINIMUM_VERSION, '>=' ) ) { add_action( 'admin_notices', 'fme_admin_notice_wp_version_fail' ); } else { diff --git a/includes/class-elementor-mask-control.php b/includes/class-elementor-mask-control.php index 0e5c237..8f99126 100644 --- a/includes/class-elementor-mask-control.php +++ b/includes/class-elementor-mask-control.php @@ -7,30 +7,28 @@ use \Elementor\Repeater as ElementorRepeater; if ( ! defined( 'ABSPATH' ) ) { - exit; // Exit if accessed directly + exit; } class FME_Elementor_Forms_Mask { public $allowed_fields = [ - 'text' + 'text', ]; public function __construct() { - add_action( 'elementor/element/form/section_form_fields/before_section_end', [ $this, 'add_mask_control' ], 100, 2 ); add_filter( 'elementor_pro/forms/render/item', [ $this, 'add_mask_atributes' ], 10, 3 ); } /** * Add mask control - * + * * @since 1.0 * @param $element * @param $args */ public function add_mask_control( $element, $args ) { - $elementor = ElementorPlugin::instance(); $control_data = $elementor->controls_manager->get_control_from_stack( $element->get_name(), 'form_fields' ); @@ -74,17 +72,17 @@ public function add_mask_control( $element, $args ) { /** * Filter to pro version change control. - * + * * @since 1.5 */ $new_control = apply_filters( 'fme_after_mask_control_created', $new_control ); - + $mask_control = new ElementorRepeater(); $mask_control->add_control( 'fme_mask_control', $new_control ); /** * Action to insert more controls. - * + * * @since 1.5.2 */ do_action( 'fme_after_mask_control_added', $mask_control ); @@ -93,11 +91,11 @@ public function add_mask_control( $element, $args ) { /** * Register control in form advanced tab. - * + * * @since 1.5.2 */ $this->register_control_in_form_advanced_tab( $element, $control_data, $pattern_field ); - + } /** @@ -107,18 +105,17 @@ public function add_mask_control( $element, $args ) { * @param array $control_data * @param array $pattern_field * @return void - * + * * @since 1.5.2 */ public function register_control_in_form_advanced_tab( $element, $control_data, $pattern_field ) { - foreach( $pattern_field as $key => $control ) { - + if( $key !== '_id' ) { $new_order = []; foreach ( $control_data['fields'] as $field_key => $field ) { - + if ( 'field_value' === $field['name'] ) { $new_order[$key] = $control; } @@ -141,22 +138,20 @@ public function register_control_in_form_advanced_tab( $element, $control_data, * @return void */ public function add_mask_atributes( $field, $field_index, $form_widget ) { - if ( ! empty( $field['fme_mask_control'] ) && in_array( $field['field_type'], $this->allowed_fields ) && $field['fme_mask_control'] != 'sel' ) { - - $form_widget->add_render_attribute( 'input' . $field_index, 'data-fme-mask', $field['fme_mask_control'] ); + $form_widget->add_render_attribute( 'input' . $field_index, 'data-mask', $field['fme_mask_control'] ); $form_widget->add_render_attribute( 'input' . $field_index, 'class', 'fme-mask-input' ); } /** * After mask atribute added - * + * * Action fired to pro version add custom atributes. - * + * * @since 1.5.2 */ do_action( 'fme_aftere_mask_atribute_added', $field, $field_index, $form_widget ); return $field; } -} \ No newline at end of file +} diff --git a/includes/class-fme-plugin.php b/includes/class-fme-plugin.php index 8327dc0..9b63aec 100644 --- a/includes/class-fme-plugin.php +++ b/includes/class-fme-plugin.php @@ -3,7 +3,7 @@ namespace FME\Includes; if ( ! defined( 'ABSPATH' ) ) { - exit; // Exit if accessed directly + exit; } /** @@ -14,7 +14,6 @@ * @since 1.4 */ final class FME_Plugin { - /** * Instance * @@ -76,7 +75,7 @@ public function __wakeup() { * Constructor * * Private method for prevent instance outsite the class. - * + * * @since 1.4 * * @access private @@ -97,9 +96,9 @@ private function __construct() { */ public function init() { - /** - * Check Elementor Por is actived - */ + /** + * Check Elementor Por is actived + */ if( ! $this->plugin_is_active( 'elementor-pro/elementor-pro.php' ) ) { add_action( 'admin_notices', [ $this, 'notice_elementor_pro_inactive' ] ); @@ -111,9 +110,9 @@ public function init() { // required files require_once FME_PLUGIN_PATH . '/includes/class-elementor-mask-control.php'; - - // instanciate mask control class - new FME_Elementor_Forms_Mask; + + // instanciate mask control class + new FME_Elementor_Forms_Mask; // register and enqueue scripts add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_plugin_js' ] ); @@ -129,18 +128,15 @@ public function init() { * @access public */ public function enqueue_plugin_js() { - - wp_register_script( 'fme-jquery-mask', FME_PLUGN_URL . 'assets/lib/jquery.mask.js', array( 'jquery' ), FME_VERSION, true ); - wp_register_script( 'fme-mask', FME_PLUGN_URL . 'assets/js/elementor-mask.js', array( 'jquery' ), FME_VERSION, true ); - wp_enqueue_script( 'fme-jquery-mask' ); - wp_enqueue_script( 'fme-mask' ); + \wp_register_script( 'fme-input-mask', FME_PLUGN_URL . 'assets/js/input-mask.min.js', array(), FME_VERSION, true ); + \wp_enqueue_script( 'fme-input-mask' ); /** * Action for enqueue more scripts or remove current scripts - * + * * @since 1.5 */ - do_action( 'fme_after_enqueue_scripts' ); + \do_action( 'fme_after_enqueue_scripts' ); } /** @@ -161,12 +157,12 @@ public function notice_elementor_pro_inactive() { ); $html_message = sprintf( '

%1$s

', $message ); - echo wp_kses_post( $html_message ); + echo wp_kses_post( $html_message ); } /** * Check plugin is activated - * + * * @since 1.5 * @return boolean * @param string $plugin @@ -177,4 +173,4 @@ public function plugin_is_active( $plugin ) { } } -FME_Plugin::instance(); \ No newline at end of file +FME_Plugin::instance(); diff --git a/package.json b/package.json index 005a560..d13d12a 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,12 @@ }, "homepage": "https://github.com/eduardovillao/myd-delivery-pro#readme", "devDependencies": { - "@wordpress/eslint-plugin": "18.0.0", - "css-loader": "7.1.2", - "css-minimizer-webpack-plugin": "7.0.0", - "esbuild-loader": "4.1.0", - "mini-css-extract-plugin": "2.9.0", - "webpack": "5.91.0", - "webpack-cli": "5.1.4" + "@wordpress/eslint-plugin": "^22.1.1", + "css-loader": "^7.1.2", + "css-minimizer-webpack-plugin": "^7.0.0", + "esbuild-loader": "^4.2.2", + "mini-css-extract-plugin": "^2.9.2", + "webpack": "^5.97.1", + "webpack-cli": "^6.0.1" } } diff --git a/readme.txt b/readme.txt index 608e5b7..a7d4ebb 100644 --- a/readme.txt +++ b/readme.txt @@ -1,94 +1,114 @@ === Form Masks for Elementor === Contributors: evcode Donate link: https://eduardovillao.me/ -Tags: elementor, form, mask, elementor mask, formulario, form mask +Tags: elementor, elementor form, form mask Requires at least: 5.5 Tested up to: 6.7 -Stable tag: 1.6.5 -Requires PHP: 7.0 +Stable tag: 2.0 +Requires PHP: 7.4 License: GPLv2License URI:https://www.gnu.org/licenses/gpl-2.0.html -Form Masks for Elementor add custom masks to fields in Elementor Pro Form. +Add custom masks to fields in Elementor Pro Forms with ease. Enhance your forms with flexible mask options and ensure better data input control. == Description == -#### => Now the plugin support masks on popups forms. #### +The Form Masks for Elementor plugin adds a custom control in the Advanced Tab of form fields, allowing you to easily apply input masks. This plugin requires Elementor Pro (Form Widget). -Form Masks for Elementor create a custom control in field advanced tab for your customize your fields with masks. This plugin require the Elementor Pro (Form Widget). - -The masks function with filed types 'text'. +Masks help users input structured data like phone numbers, dates, and more. +📽️ **Watch how it works:** https://www.youtube.com/watch?v=NYoykAUl4QE -#### Masks Available (FREE VERSION) #### - -* Phone - '0000-0000' or '(000) 000-0000' or '(00) 0000-0000' or '(00) 0.0000-0000' - -* CPF - '000.000.000-00' - -* CNPJ - '00.000.000/0000-00' - -* Date - '00/00/0000' - -* Time - '00:00:00' - -* Date and Time - '00/00/0000 00:00:00' - -* Money - '000.000.000.000.000,00' - -* CEP - '00000-000' - -* Credit Card - 0000-0000-0000-0000 - -* Credit Card Date - 00/00 - -#### PRO VERSION #### - -[Check the PRO version](https://codecanyon.net/item/form-masks-for-elementor/25872641) - -In PRO version you can WRITE YOUR CUSTOM MASKS. Create the mask you want for your inputs! - +**Features (FREE VERSION)** + +Ready-to-Use Masks +* **Phone**: + 0000-0000 + (000) 000-0000 + (00) 0000-0000 + (00) 0.0000-0000 +* **CPF (Brazilian ID)**: 000.000.000-00 +* **CNPJ (Brazilian Company ID)**: 00.000.000/0000-00 +* **Date**: 00/00/0000 +* **Time**: 00:00:00 +* **Date and Time**: 00/00/0000 00:00:00 +* **Money**: 000.000.000.000.000,00 +* **Postal Code (CEP)**: 00000-000 +* **Credit Card**: 0000-0000-0000-0000 +* **Credit Card Expiry Date**: 00/00 + +**Exclusive PRO Features** +* **Custom Masks**: Create your own masks for maximum flexibility. Tailor your forms to any requirement. + +🚀 **Coming Soon to the PRO Version** +* **Minimum character limits** to prevent incomplete submissions. +* **Prefix and suffix** options for masks. +* Built-in **validation for CPF, CNPJ**, and other formats. +* And much more… + +**PRO VERSION** +[Check the PRO version here](https://codecanyon.net/item/form-masks-for-elementor/25872641) + +📽️ **Watch how it works:** https://www.youtube.com/watch?v=XAuL43HFh8I -#### CREDITS #### +**Why Choose Form Masks for Elementor?** +* Simple to set up and use. +* Increases form usability and data accuracy. +* Seamless integration with Elementor Pro. +* Regular updates with new features and improvements. -This plugin use the jQuery Mask library plugin. [jQuery Mask Plugin](https://github.com/igorescobar/jQuery-Mask-Plugin) +== More Plugins by Us == -== More plugins? == +Discover other plugins to enhance your WordPress experience:: [WordPress Plugins](https://eduardovillao.me/wordpress-plugins/) -Check my other plugins: [WordPress Plugins](https://eduardovillao.me/wordpress-plugins/) +== Learn WordPress Tips == -== WordPress Tips? == - -Check my blog: [WordPress Tips](https://eduardovillao.me/blog/) +Visit our blog for expert WordPress insights: [WordPress Tips](https://eduardovillao.me/blog/) == Plugins Suggestions? == -Please, send to me: [plugins@eduardovillao.me](mailto:plugins@eduardovillao.me) +We’d love to hear from you! [plugins@eduardovillao.me](mailto:plugins@eduardovillao.me) == Installation == 1. Upload the plugin files to the WordPress - 2. Activate the plugin in WordPress - 3. Go to Widget Elementor Pro in your page - -4. Select the custom mask for field in advanced field tab - +4. Select the custom mask for field in advanced field tab **(works only on text field)** 5. Go to the page for see the results (outside the Elementor editor) == Frequently Asked Questions == += Does the plugin work without Elementor Pro? = + +No, the Form Masks for Elementor plugin requires Elementor Pro, as it relies on the Form Widget, which is only available in Elementor Pro. The plugin is not compatible with alternatives like Pro Elements or similar Elementor-like versions. + += Does the mask work on all field types? = + +The plugin supports masks only for fields of type text. This means that masks will not work on fields like textarea, number, or other custom field types. + += Can I create my own masks? = + +Yes, but this feature is available only in the PRO version. With the PRO version, you can define custom masks tailored to your specific needs. + == Screenshots == == Changelog == += 2.0 = +* New: add inputmode to open keybord with input context. Ex.: input with tel mask will open numeric keybord. +* New: remove jQuery mask library to use our own custom library. +* New: support to masks on popup improved. +* Changed: minimum PHP version changed to 7.4. +* Changed: improve build proccess to assets. + += 1.6.5 = * Changed: compatibility with WordPress 6.7. * Changed: code improvements. -= 1.6.5 = += 1.6.4 = * Changed: compatibility with WordPress 6.5. * Changed: code improvements. diff --git a/src/js/input-mask.js b/src/js/input-mask.js new file mode 100644 index 0000000..2c5bc08 --- /dev/null +++ b/src/js/input-mask.js @@ -0,0 +1,215 @@ +class inputMask { + constructor(mappedMasks) { + this.inputs = null; + this.init(); + this.mappedMasks = mappedMasks || null; + this.tokens = { + '0': { + validateRule: /\d/, + }, + }; + } + + init() { + if (document.readyState !== 'complete') { + window.addEventListener('load', this.init.bind(this)); + return; + } + + this.initModalListener(); + this.getInput(); + } + + initModalListener() { + const body = document.body; + + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === 'childList') { + mutation.addedNodes.forEach((node) => { + if ( + node.classList && + node.classList.contains('elementor-popup-modal') + ) { + this.getInput(); + } + }); + } + }); + }); + + observer.observe(body, { childList: true }); + } + + getInput() { + this.inputs = document.querySelectorAll('input[data-mask]'); + this.inputs.forEach((input) => { + this.handleMappedMasks(input); + input.addEventListener('input', this.maskInput.bind(this)); + input.addEventListener('keydown', this.handleBackspace.bind(this)); + }); + } + + handleMappedMasks(input) { + if (!this.mappedMasks) { + return; + } + + const replaceMaks = this.mappedMasks[input.dataset.mask]; + if (!replaceMaks) { + return; + } + + input.dataset.mask = replaceMaks.mask; + input.dataset.maskReverse = replaceMaks.reverse; + input.inputmode = replaceMaks.inputmode; + } + + maskInput(event) { + const input = event.target; + const mask = input.dataset.mask; + const value = input.value; + const unmaskedValue = this.removeMask(mask, value); + const isReverse = input.dataset.maskReverse === 'true'; + const maskedValue = this.applyMask(unmaskedValue, mask, isReverse); + + input.value = maskedValue; + } + + removeMask(mask, value) { + if (!mask || !value) { + return value; + } + + const allowTokens = Object.keys(this.tokens); + const allowTokensRegex = new RegExp(`[${allowTokens.join('')}]`, 'g'); + const maskLiteralsToRemove = mask.replace(allowTokensRegex, ''); + return value.replace(new RegExp(`[${maskLiteralsToRemove}]`, 'g'), ''); + } + + handleBackspace(event) { + const input = event.target; + if ( + event.key === 'Backspace' && + input.selectionStart === input.selectionEnd + ) { + const pos = input.selectionStart; + if (pos > 0) { + const value = input.value; + if (!/\d/.test(value[pos - 1])) { + event.preventDefault(); + input.value = value.slice(0, pos - 1) + value.slice(pos); + input.setSelectionRange(pos - 1, pos - 1); + } + } + } + } + + applyMask(unmaskedValue, mask, isReverse) { + if (!unmaskedValue) { + return unmaskedValue; + } + + let maskedValue = ''; + let valueIndex = 0; + let maskChars = mask.split(''); + + if (isReverse) { + unmaskedValue = unmaskedValue.split('').reverse().join(''); + maskChars = maskChars.reverse(); + } + + for (let i = 0; i < maskChars.length; i++) { + if (maskChars[i] === '0') { + if ( + new RegExp(this.tokens['0'].validateRule).test( + unmaskedValue[valueIndex] + ) + ) { + maskedValue += unmaskedValue[valueIndex]; + valueIndex++; + } else { + break; + } + } else { + maskedValue += maskChars[i]; + } + } + + if (isReverse) { + maskedValue = maskedValue.split('').reverse().join(''); + maskedValue = maskedValue.startsWith('.') + ? maskedValue.substring(1) + : maskedValue; + } + + return maskedValue; + } +} + +new inputMask({ + 'ev-tel': { + mask: '0000-0000', + reverse: false, + inputmode: 'tel', + }, + 'ev-tel-ddd': { + mask: '(00) 0000-0000', + reverse: false, + inputmode: 'tel', + }, + 'ev-tel-ddd9': { + mask: '(00) 00000-0000', + reverse: false, + }, + 'ev-tel-us': { + mask: '(000) 000-0000', + reverse: false, + inputmode: 'tel', + }, + 'ev-cpf': { + mask: '000.000.000-00', + reverse: false, + inputmode: 'numeric', + }, + 'ev-cnpj': { + mask: '00.000.000/0000-00', + reverse: false, + inputmode: 'numeric', + }, + 'ev-money': { + mask: '000.000.000.000.000,00', + reverse: true, + inputmode: 'numeric', + }, + 'ev-ccard': { + mask: '0000-0000-0000-0000', + reverse: false, + inputmode: 'numeric', + }, + 'ev-ccard-valid': { + mask: '00/00', + reverse: false, + inputmode: 'numeric', + }, + 'ev-cep': { + mask: '00000-000', + reverse: false, + inputmode: 'numeric', + }, + 'ev-time': { + mask: '00:00:00', + reverse: false, + inputmode: 'numeric', + }, + 'ev-date': { + mask: '00/00/0000', + reverse: false, + inputmode: 'numeric', + }, + 'ev-date_time': { + mask: '00/00/0000 00:00:00', + reverse: false, + inputmode: 'numeric', + }, +}); diff --git a/webpack.config.js b/webpack.config.js index e405779..dae258b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,244 +1,37 @@ -// const path = require('path'); -// const fs = require('fs'); -// const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); -// const MiniCssExtractPlugin = require("mini-css-extract-plugin"); - -// const orderJs = { -// mode: 'production', -// entry: './src/js/order/controller.js', -// output: { -// filename: 'order.min.js', -// path: path.resolve(__dirname, 'assets/js'), -// }, -// module: { -// rules: [ -// { -// test: /\.js$/, -// exclude: /node_modules/, -// use: { -// loader: 'esbuild-loader', -// options: { -// loader: 'js', -// target: 'es2016', -// }, -// }, -// }, -// ], -// }, -// }; - -// const srcJsAdminDirectory = path.resolve(__dirname, 'src/js/admin'); -// const adminEntries = fs.readdirSync(srcJsAdminDirectory).reduce((entries, file) => { -// if (file.endsWith('.js')) { -// const entryKey = file.replace('.js', ''); -// entries[entryKey] = path.resolve(srcJsAdminDirectory, file); -// } -// return entries; -// }, {}); - -// const adminJs = { -// mode: 'production', -// entry: adminEntries, -// output: { -// filename: '[name].min.js', -// path: path.resolve(__dirname, 'assets/js/admin'), -// }, -// module: { -// rules: [ -// { -// test: /\.js$/, -// exclude: /node_modules/, -// use: { -// loader: 'esbuild-loader', -// options: { -// loader: 'js', -// target: 'es2016', -// }, -// }, -// }, -// ], -// }, -// }; - -// const srcJsCustomFieldsDirectory = path.resolve(__dirname, 'src/js/admin/custom-fields'); -// const customFieldsEntries = fs.readdirSync(srcJsCustomFieldsDirectory).reduce((entries, file) => { -// if (file.endsWith('.js')) { -// const entryKey = file.replace('.js', ''); -// entries[entryKey] = path.resolve(srcJsCustomFieldsDirectory, file); -// } -// return entries; -// }, {}); - -// const customFieldsJs = { -// mode: 'production', -// entry: customFieldsEntries, -// output: { -// filename: '[name].min.js', -// path: path.resolve(__dirname, 'assets/js/admin/custom-fields'), -// }, -// module: { -// rules: [ -// { -// test: /\.js$/, -// exclude: /node_modules/, -// use: { -// loader: 'esbuild-loader', -// options: { -// loader: 'js', -// target: 'es2016', -// }, -// }, -// }, -// ], -// }, -// }; - -// const srcJsGeneralDirectory = path.resolve(__dirname, 'src/js/'); -// const generalEntries = fs.readdirSync(srcJsGeneralDirectory).reduce((entries, file) => { -// if (file.endsWith('.js')) { -// const entryKey = file.replace('.js', ''); -// entries[entryKey] = path.resolve(srcJsGeneralDirectory, file); -// } -// return entries; -// }, {}); - -// const generalJs = { -// mode: 'production', -// entry: generalEntries, -// output: { -// filename: '[name].min.js', -// path: path.resolve(__dirname, 'assets/js'), -// }, -// module: { -// rules: [ -// { -// test: /\.js$/, -// exclude: /node_modules/, -// use: { -// loader: 'esbuild-loader', -// options: { -// loader: 'js', -// target: 'es2016', -// }, -// }, -// }, -// ], -// }, -// }; - -// const srcJsOrdersPanelDirectory = path.resolve(__dirname, 'src/js/orders-panel'); -// const ordersPanelEntries = fs.readdirSync(srcJsOrdersPanelDirectory).map((file) => { -// if (file.endsWith('.js')) { -// return path.resolve(srcJsOrdersPanelDirectory, file); -// } -// }, []); - -// const ordersPanelJs = { -// mode: 'production', -// entry: ordersPanelEntries, -// output: { -// filename: 'frontend.min.js', -// path: path.resolve(__dirname, 'assets/js/orders-panel'), -// }, -// module: { -// rules: [ -// { -// test: /\.js$/, -// exclude: /node_modules/, -// use: { -// loader: 'esbuild-loader', -// options: { -// loader: 'js', -// target: 'es2016', -// }, -// }, -// }, -// ], -// }, -// }; - -// const deliveryPageCSS = { -// mode: 'production', -// entry: './src/css/delivery-page/style.css', -// output: { -// filename: 'style.bundle.js', -// path: path.resolve(__dirname, 'assets/css/'), -// }, -// module: { -// rules: [ -// { -// test: /\.css$/, -// use: [MiniCssExtractPlugin.loader, 'css-loader'], -// }, -// ], -// }, -// optimization: { -// minimizer: [new CssMinimizerPlugin()], -// }, -// plugins: [ -// new MiniCssExtractPlugin({ -// filename: 'delivery-frontend.min.css', -// }), -// ], -// }; - -// const orderPanelPageCSS = { -// mode: 'production', -// entry: './src/css/order-panel/style.css', -// output: { -// filename: 'style.bundle.js', -// path: path.resolve(__dirname, 'assets/css/'), -// }, -// module: { -// rules: [ -// { -// test: /\.css$/, -// use: [MiniCssExtractPlugin.loader, 'css-loader'], -// }, -// ], -// }, -// optimization: { -// minimizer: [new CssMinimizerPlugin()], -// }, -// plugins: [ -// new MiniCssExtractPlugin({ -// filename: 'order-panel-frontend.min.css', -// }), -// ], -// }; - -// const trackOrderPageCSS = { -// mode: 'production', -// entry: './src/css/track-order/style.css', -// output: { -// filename: 'style.bundle.js', -// path: path.resolve(__dirname, 'assets/css/'), -// }, -// module: { -// rules: [ -// { -// test: /\.css$/, -// use: [MiniCssExtractPlugin.loader, 'css-loader'], -// }, -// ], -// }, -// optimization: { -// minimizer: [new CssMinimizerPlugin()], -// }, -// plugins: [ -// new MiniCssExtractPlugin({ -// filename: 'track-order-frontend.min.css', -// }), -// ], -// }; - -// module.exports = [ -// orderJs, -// adminJs, -// customFieldsJs, -// generalJs, -// ordersPanelJs, -// deliveryPageCSS, -// orderPanelPageCSS, -// trackOrderPageCSS, -// ]; +const path = require('path'); +const fs = require('fs'); + +const srcJsGeneralDirectory = path.resolve(__dirname, 'src/js/'); +const generalEntries = fs.readdirSync(srcJsGeneralDirectory).reduce((entries, file) => { + if (file.endsWith('.js')) { + const entryKey = file.replace('.js', ''); + entries[entryKey] = path.resolve(srcJsGeneralDirectory, file); + } + return entries; + }, {}); + +const generalJs = { + mode: 'production', + entry: generalEntries, + output: { + filename: '[name].min.js', + path: path.resolve(__dirname, 'assets/js'), + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'esbuild-loader', + options: { + loader: 'js', + target: 'es2016', + }, + }, + }, + ], + }, +}; + +module.exports = [generalJs];