} el
+ * @return {void}
+ */
+ showEditForm( el ) {
+ const bulk_wrap = el.closest( '.ppom-bulk-quantity-wrapper' );
+ bulk_wrap
+ .find( 'table' )
+ .find( 'tbody tr td' )
+ .each( function ( index, el ) {
+ const class_name = $( el ).attr( 'id' );
+ const td_wrap = $( this );
+ const cross_icon =
+ '';
+ if ( class_name == 'ppom-bulkqty-adjust-cross' ) {
+ var input =
+ '' +
+ cross_icon +
+ '';
+ } else {
+ var input =
+ '';
+ }
- td_wrap.closest('td').html(input);
- });
+ td_wrap.closest( 'td' ).html( input );
+ } );
// show action
- $(this).hide();
- bulk_wrap.find('.ppom-bulk-action-wrap').show();
- bulk_wrap.find('.ppom-save-bulk-json').show();
+ $( this ).hide();
+ bulk_wrap.find( '.ppom-bulk-action-wrap' ).show();
+ bulk_wrap.find( '.ppom-save-bulk-json' ).show();
this.setMaskRangeInput();
- }
- }
+ },
+ };
- body.ready(function(){
+ body.ready( function () {
ppomBQ.setMaskRangeInput();
- });
+ } );
- $(document).on('ppom_new_field_created', (e, newField, fieldNo, fieldType)=>{
- if( fieldType !== 'bulkquantity' ) {
- return;
+ $( document ).on(
+ 'ppom_new_field_created',
+ ( e, newField, fieldNo, fieldType ) => {
+ if ( fieldType !== 'bulkquantity' ) {
+ return;
+ }
+
+ ppomBQ.setMaskRangeInput();
}
+ );
+
+ body.on( 'click', 'button.ppom-add-bulk-qty-row', function ( e ) {
+ e.preventDefault();
+
+ const main_wrapper = $( this ).closest( '.ppom-slider' );
+ const field_index = main_wrapper
+ .find( '.ppom-fields-actions' )
+ .attr( 'data-field-no' );
+ const bulk_div = $( this ).closest( 'div' );
+ const bulk_qty_val = bulk_div.find( '.ppom-bulk-qty-val' ).val();
+ const table = $( this ).closest( 'div.table-content' ),
+ tbody = table.find( 'tbody' ),
+ thead = table.find( 'thead' );
+
+ // Clone the last visible row so merchants keep the same number of
+ // variation columns while defining the next quantity interval.
+ const clon_qty_section = tbody.find( 'tr:last-child' ).clone();
+ clon_qty_section
+ .find( '.ppom-bulk-qty-val-picker' )
+ .val( bulk_qty_val );
+ clon_qty_section.appendTo( tbody );
ppomBQ.setMaskRangeInput();
- });
-
- /**
- 2- Add New Quantity Row
- **/
- body.on('click', 'button.ppom-add-bulk-qty-row', function (e) {
- e.preventDefault();
-
- var main_wrapper = $(this).closest('.ppom-slider');
- var field_index = main_wrapper.find('.ppom-fields-actions').attr('data-field-no');
- var bulk_div = $(this).closest('div');
- var bulk_qty_val = bulk_div.find('.ppom-bulk-qty-val').val();
- var table = $(this).closest('div.table-content'),
- tbody = table.find('tbody'),
- thead = table.find('thead');
-
- var clon_qty_section = tbody.find('tr:last-child').clone();
- clon_qty_section.find('.ppom-bulk-qty-val-picker').val(bulk_qty_val);
- clon_qty_section.appendTo(tbody);
+ } );
- ppomBQ.setMaskRangeInput();
- });
-
-
- /**
- 3- Remove Quantity Row
- **/
- body.on('click', 'span.ppom-rm-bulk-qty', function (e) {
- e.preventDefault();
-
- var count = $(this).closest('tbody').find('tr').length;
- if ( count < 2 ) {
- alert('sorry! you can not remove more textbox');
- return;
- }
- $(this).closest('tr').remove();
- });
-
-
- /**
- 4- Remove Variation Colunm
- **/
- body.on('click', 'span.ppom-rm-bulk-variation', function (e) {
- e.preventDefault();
-
- var cell = $(this).closest('th'),
- index = cell.index() + 1;
- cell.closest('table').find('th, td').filter(':nth-child(' + index + ')').remove();
- });
-
-
- /**
- 5- Add Bulk Variation Colunm
- **/
- body.on('click', 'button.ppom-add-bulk-variation-col', function (e) {
- e.preventDefault();
-
- var buk_div = $(this).closest('div');
- var bulk_variation_val = buk_div.find('.ppom-bulk-variation-val').val();
- // console.log(bulk_variation_val);
- var table = $(this).closest('div.table-content').find('table'),
- thead = table.find('thead'),
- lastTheadRow = thead.find('tr:last-child'),
- tbody = table.find('tbody');
- var closest_td = tbody.find('td:last-child');
-
- $('| ', {
- 'html': ' '+bulk_variation_val+' '
- }).appendTo(lastTheadRow);
- $(' | ', {
- 'html': ''
- }).insertAfter(closest_td);
- });
-
-
- /**
- 6- Save Bulk Quantity Meta
- **/
- $('body').on('click', '.ppom-save-bulk-json', function(event) {
- event.preventDefault();
-
- const bulk_wrap = $(this).closest('.ppom-bulk-quantity-wrapper');
- bulk_wrap.find('table').find('input').each(function(index, el) {
- const td_wrap = $(this);
- td_wrap.closest('td').html(td_wrap.val());
- });
- const bulkData = bulk_wrap.find('table').tableToJSON();
-
- if( ! ppomBQ.formValidation(bulkData) ) {
- ppomBQ.showEditForm($(this));
+ body.on( 'click', 'span.ppom-rm-bulk-qty', function ( e ) {
+ e.preventDefault();
+
+ const count = $( this ).closest( 'tbody' ).find( 'tr' ).length;
+ if ( count < 2 ) {
+ alert( 'sorry! you can not remove more textbox' );
+ return;
+ }
+ $( this ).closest( 'tr' ).remove();
+ } );
+
+ body.on( 'click', 'span.ppom-rm-bulk-variation', function ( e ) {
+ e.preventDefault();
+
+ const cell = $( this ).closest( 'th' ),
+ index = cell.index() + 1;
+ cell.closest( 'table' )
+ .find( 'th, td' )
+ .filter( ':nth-child(' + index + ')' )
+ .remove();
+ } );
+
+ body.on( 'click', 'button.ppom-add-bulk-variation-col', function ( e ) {
+ e.preventDefault();
+
+ const buk_div = $( this ).closest( 'div' );
+ const bulk_variation_val = buk_div
+ .find( '.ppom-bulk-variation-val' )
+ .val();
+ // console.log(bulk_variation_val);
+ const table = $( this ).closest( 'div.table-content' ).find( 'table' ),
+ thead = table.find( 'thead' ),
+ lastTheadRow = thead.find( 'tr:last-child' ),
+ tbody = table.find( 'tbody' );
+ const closest_td = tbody.find( 'td:last-child' );
+
+ // Every added column becomes a new object key when `tableToJSON()`
+ // serializes the matrix, so the header text is the persisted identifier.
+ $( ' | ', {
+ html:
+ ' ' +
+ bulk_variation_val +
+ ' ',
+ } ).appendTo( lastTheadRow );
+ $( ' | ', {
+ html: '',
+ } ).insertAfter( closest_td );
+ } );
+
+ // Convert the editable grid back into the JSON blob later consumed by
+ // `ppom_bulkquantity_price_manager()` on the product page.
+ $( 'body' ).on( 'click', '.ppom-save-bulk-json', function ( event ) {
+ event.preventDefault();
+
+ const bulk_wrap = $( this ).closest( '.ppom-bulk-quantity-wrapper' );
+ bulk_wrap
+ .find( 'table' )
+ .find( 'input' )
+ .each( function ( index, el ) {
+ const td_wrap = $( this );
+ td_wrap.closest( 'td' ).html( td_wrap.val() );
+ } );
+ const bulkData = bulk_wrap.find( 'table' ).tableToJSON();
+
+ if ( ! ppomBQ.formValidation( bulkData ) ) {
+ ppomBQ.showEditForm( $( this ) );
return;
}
- bulk_wrap.find('.ppom-saved-bulk-data').val(JSON.stringify(bulkData));
-
- // hide action
- $(this).hide();
- bulk_wrap.find('.ppom-bulk-action-wrap').hide();
- bulk_wrap.find('.ppom-edit-bulk-json').show();
- });
-
-
- /**
- 7- Edit Bulk Quantity Meta
- **/
- $('body').on('click', '.ppom-edit-bulk-json', function(event) {
- event.preventDefault();
- ppomBQ.showEditForm($(this));
- });
-
-});
\ No newline at end of file
+ bulk_wrap
+ .find( '.ppom-saved-bulk-data' )
+ .val( JSON.stringify( bulkData ) );
+
+ // hide action
+ $( this ).hide();
+ bulk_wrap.find( '.ppom-bulk-action-wrap' ).hide();
+ bulk_wrap.find( '.ppom-edit-bulk-json' ).show();
+ } );
+
+ // Rehydrate the read-only table into inputs so an existing matrix can be edited.
+ $( 'body' ).on( 'click', '.ppom-edit-bulk-json', function ( event ) {
+ event.preventDefault();
+ ppomBQ.showEditForm( $( this ) );
+ } );
+} );
diff --git a/js/admin/ppom-deactivate.js b/js/admin/ppom-deactivate.js
index 97e756e4..23526f4a 100644
--- a/js/admin/ppom-deactivate.js
+++ b/js/admin/ppom-deactivate.js
@@ -1,70 +1,91 @@
/**
- * Getting user response when deactivate plugin
- * */
-"use strict"
-jQuery(function($){
-
- var modal = $('#ppom-deactivate-modal');
- var deactivateLink = '';
+ * Deactivation survey modal for the plugin list screen.
+ *
+ * The collected reason is optional telemetry; the saved redirect target still
+ * drives the final plugin deactivation link after the modal flow completes.
+ */
+'use strict';
+jQuery( function ( $ ) {
+ const modal = $( '#ppom-deactivate-modal' );
+ let deactivateLink = '';
+ $( '#the-list' ).on( 'click', 'a.ppom-deactivate-link', function ( e ) {
+ e.preventDefault();
+ modal.addClass( 'modal-active' );
+ deactivateLink = $( this ).attr( 'href' );
+ modal
+ .find( 'a.dont-bother-me' )
+ .attr( 'href', deactivateLink )
+ .css( 'float', 'left' );
+ } );
- $('#the-list').on('click', 'a.ppom-deactivate-link', function (e) {
- e.preventDefault();
- modal.addClass('modal-active');
- deactivateLink = $(this).attr('href');
- modal.find('a.dont-bother-me').attr('href', deactivateLink).css('float', 'left');
- });
+ $( '#ppom-deactivate-modal' ).on(
+ 'click',
+ 'a.review-and-deactivate',
+ function ( e ) {
+ e.preventDefault();
+ window.open(
+ 'https://wordpress.org/support/plugin/woocommerce-product-addon/reviews/#new-post'
+ );
+ window.location.href = deactivateLink;
+ }
+ );
+ modal.on( 'click', 'button.pipe-model-cancel', function ( e ) {
+ e.preventDefault();
+ modal.removeClass( 'modal-active' );
+ } );
+ modal.on( 'click', 'input[type="radio"]', function () {
+ const parent = $( this ).parents( 'li:first' );
+ modal.find( '.reason-input' ).remove();
+ const inputType = parent.data( 'type' ),
+ inputPlaceholder = parent.data( 'placeholder' );
+ if ( 'reviewhtml' === inputType ) {
+ var reasonInputHtml =
+ '';
+ } else {
+ var reasonInputHtml =
+ ' ' +
+ ( 'text' === inputType
+ ? ''
+ : '' ) +
+ ' ';
+ }
+ if ( inputType !== '' ) {
+ parent.append( $( reasonInputHtml ) );
+ parent
+ .find( 'input, textarea' )
+ .attr( 'placeholder', inputPlaceholder )
+ .focus();
+ }
+ } );
- $('#ppom-deactivate-modal').on('click', 'a.review-and-deactivate', function (e) {
- e.preventDefault();
- window.open("https://wordpress.org/support/plugin/woocommerce-product-addon/reviews/#new-post");
- window.location.href = deactivateLink;
- });
- modal.on('click', 'button.pipe-model-cancel', function (e) {
- e.preventDefault();
- modal.removeClass('modal-active');
- });
- modal.on('click', 'input[type="radio"]', function () {
- var parent = $(this).parents('li:first');
- modal.find('.reason-input').remove();
- var inputType = parent.data('type'),
- inputPlaceholder = parent.data('placeholder');
- if ('reviewhtml' === inputType) {
- var reasonInputHtml = '';
- } else {
- var reasonInputHtml = '' + (('text' === inputType) ? '' : '') + ' ';
- }
- if (inputType !== '') {
- parent.append($(reasonInputHtml));
- parent.find('input, textarea').attr('placeholder', inputPlaceholder).focus();
- }
- });
+ modal.on( 'click', 'button.pipe-model-submit', function ( e ) {
+ e.preventDefault();
+ // Submit the reason asynchronously, then continue to the original
+ // deactivation URL regardless of whether the response body is useful.
+ const button = $( this );
+ if ( button.hasClass( 'disabled' ) ) {
+ return;
+ }
+ const $radio = $( 'input[type="radio"]:checked', modal );
+ const $selected_reason = $radio.parents( 'li:first' ),
+ $input = $selected_reason.find( 'textarea, input[type="text"]' );
- modal.on('click', 'button.pipe-model-submit', function (e) {
- e.preventDefault();
- var button = $(this);
- if (button.hasClass('disabled')) {
- return;
- }
- var $radio = $('input[type="radio"]:checked', modal);
- var $selected_reason = $radio.parents('li:first'),
- $input = $selected_reason.find('textarea, input[type="text"]');
-
- $.ajax({
- url: ajaxurl,
- type: 'POST',
- data: {
- action: 'pipe_submit_uninstall_reason',
- reason_id: (0 === $radio.length) ? 'none' : $radio.val(),
- reason_info: (0 !== $input.length) ? $input.val().trim() : ''
- },
- beforeSend: function () {
- button.addClass('disabled');
- button.text('Processing...');
- },
- complete: function (resp) {
- window.location.href = deactivateLink;
- }
- });
- });
-});
\ No newline at end of file
+ $.ajax( {
+ url: ajaxurl,
+ type: 'POST',
+ data: {
+ action: 'pipe_submit_uninstall_reason',
+ reason_id: 0 === $radio.length ? 'none' : $radio.val(),
+ reason_info: 0 !== $input.length ? $input.val().trim() : '',
+ },
+ beforeSend() {
+ button.addClass( 'disabled' );
+ button.text( 'Processing...' );
+ },
+ complete( resp ) {
+ window.location.href = deactivateLink;
+ },
+ } );
+ } );
+} );
diff --git a/js/admin/ppom-meta-table.js b/js/admin/ppom-meta-table.js
index 663627fa..e598b0fd 100644
--- a/js/admin/ppom-meta-table.js
+++ b/js/admin/ppom-meta-table.js
@@ -1,199 +1,249 @@
-"use strict";
-jQuery(function($){
-
- /*********************************
- * PPOM Existing Table Meta JS *
- **********************************/
-
- /*-------------------------------------------------------
-
- ------ Its Include Following Function -----
-
- 1- Apply DataTable JS Library To PPOM Meta List
- 2- Delete Selected Products
- 3- Check And Uncheck All Existing Product Meta List
- 4- Loading Products In Modal DataTable
- 5- Delete Single Product Meta
- --------------------------------------------------------*/
-
-
+'use strict';
+
+/**
+ * Admin list screen actions for saved PPOM field groups.
+ *
+ * This file wires DataTables, bulk actions, and the "attach to products" modal
+ * around the server-side group list rendered by PHP.
+ *
+ * @see window.ppomPopup in js/popup.js
+ */
+jQuery( function ( $ ) {
/**
- 1- Apply DataTable JS Library To PPOM Meta List
- **/
- $('#ppom-meta-table').DataTable({
+ * Initialize Select2 on the product search dropdowns in the "attach to products" modal.
+ *
+ * @return {void}
+ */
+ function initAttachSelects() {
+ const attachSelects = $( '.ppom-attach-container-item select' );
+
+ if ( typeof $.fn.select2 === 'function' ) {
+ attachSelects.select2();
+ }
+ }
+
+ // DataTables provides the searchable/sortable shell, while PPOM injects its
+ // own action toolbar into the custom `ppom-toolbar` slot defined in `dom`.
+ $( '#ppom-meta-table' ).DataTable( {
pageLength: 50,
dom: 'f<"ppom-toolbar"><"top">rt<"bottom">lpi',
- });
- var append_overly_model = ("");
-
- /**
- 2- Delete Selected Products
- **/
- function deleteSelectedProducts(checkedProducts_ids) {
- window?.ppomPopup?.open({
- title: window?.ppom_vars?.i18n.popup.confirmTitle,
+ } );
+ const append_overlay_modal =
+ "";
+
+ // Bulk delete is confirmation-driven because it removes saved field groups,
+ // not just the rows in the current DataTable view.
+ /**
+ * Delete multiple saved PPOM groups after an explicit confirmation step.
+ *
+ * @param {number[]} checkedProducts_ids
+ * @param {string} checkedProducts_names
+ * @return {void}
+ */
+ function deleteSelectedProducts( checkedProducts_ids, checkedProducts_names = '' ) {
+ let title = window?.ppom_vars?.i18n.popup.deleteGroup;
+ title = checkedProducts_names ? title.replace( '%s', checkedProducts_names ) : window?.ppom_vars?.i18n.popup.confirmTitle;
+ window?.ppomPopup?.open( {
+ title: title,
onConfirmation: () => {
- $('#ppom_delete_selected_products_btn').html('Deleting...');
-
+ $( '#ppom_delete_selected_products_btn' ).html( 'Deleting...' );
+
const data = {
- action : 'ppom_delete_selected_meta',
- productmeta_ids : checkedProducts_ids,
- ppom_meta_nonce : $("#ppom_meta_nonce").val()
+ action: 'ppom_delete_selected_meta',
+ productmeta_ids: checkedProducts_ids,
+ ppom_meta_nonce: $( '#ppom_meta_nonce' ).val(),
};
- $.post(ajaxurl, data, function(resp){
- $('#ppom_delete_selected_products_btn').html('Delete');
- if (resp) {
- window?.ppomPopup?.open({
+ $.post( ajaxurl, data, function ( resp ) {
+ $( '#ppom_delete_selected_products_btn' ).html( 'Delete' );
+ if ( resp ) {
+ window?.ppomPopup?.open( {
title: window?.ppom_vars?.i18n.popup.finishTitle,
hideCloseBtn: true,
onConfirmation: () => location.reload(),
- onClose: () => location.reload()
- });
+ onClose: () => location.reload(),
+ } );
} else {
- window?.ppomPopup?.open({
+ window?.ppomPopup?.open( {
title: window.ppom_vars.i18n.popup.errorTitle,
text: resp,
- hideCloseBtn: true
- });
+ hideCloseBtn: true,
+ } );
}
- });
- }
- })
+ } );
+ },
+ } );
}
+ $( '.ppom_product_checkbox' ).on( 'click', function ( event ) {
+ const checkboxProducts = $( '.ppom_product_checkbox' )
+ .map( function () {
+ return this.value;
+ } )
+ .get();
+
+ const checkedProducts = $( '.ppom_product_checkbox:checked' )
+ .map( function () {
+ return this.value;
+ } )
+ .get();
+
+ if ( checkboxProducts.length == checkedProducts.length ) {
+ $(
+ '#ppom-all-select-products-head-btn, #ppom-all-select-products-foot-btn'
+ ).prop( 'checked', true );
+ } else {
+ $(
+ '#ppom-all-select-products-head-btn, #ppom-all-select-products-foot-btn'
+ ).prop( 'checked', false );
+ }
- /**
- 3- Check And Uncheck All Existing Product Meta List
- **/
- $('.ppom_product_checkbox').on('click', function(event){
-
- var checkboxProducts = $('.ppom_product_checkbox').map(function() {
- return this.value;
- }).get();
-
- var checkedProducts = $('.ppom_product_checkbox:checked').map(function() {
- return this.value;
- }).get();
-
- if (checkboxProducts.length == checkedProducts.length ) {
- $('#ppom-all-select-products-head-btn, #ppom-all-select-products-foot-btn').prop('checked', true);
- }else{
- $('#ppom-all-select-products-head-btn, #ppom-all-select-products-foot-btn').prop('checked', false);
- };
-
- $('#selected_products_count').html();
- $('#selected_products_count').html(checkedProducts.length);
- });
- $('#ppom-all-select-products-head-btn, #ppom-all-select-products-foot-btn').on('click', function(event){
-
- $('#ppom-meta-table input:checkbox').not(this).prop('checked', this.checked);
- var checkedProducts = $('.ppom_product_checkbox:checked').map(function() {
- return this.value;
- }).get();
- $('#selected_products_count').html();
- $('#selected_products_count').html(checkedProducts.length);
- });
-
+ $( '#selected_products_count' ).html();
+ $( '#selected_products_count' ).html( checkedProducts.length );
+ } );
+ $(
+ '#ppom-all-select-products-head-btn, #ppom-all-select-products-foot-btn'
+ ).on( 'click', function ( event ) {
+ $( '#ppom-meta-table input:checkbox' )
+ .not( this )
+ .prop( 'checked', this.checked );
+ const checkedProducts = $( '.ppom_product_checkbox:checked' )
+ .map( function () {
+ return this.value;
+ } )
+ .get();
+ $( '#selected_products_count' ).html();
+ $( '#selected_products_count' ).html( checkedProducts.length );
+ } );
+
+ // Load the product-assignment UI lazily so the heavy modal table is fetched
+ // only when the merchant asks to attach a group to products.
+ $( '#ppom-meta-table_wrapper, .ppom-basic-setting-section' ).on(
+ 'click',
+ 'a.ppom-products-modal',
+ function ( e ) {
+ e.preventDefault();
+
+ $( '.ppom-table' ).DataTable();
+ const ppom_id = $( this ).data( 'ppom_id' );
+ const get_url =
+ ajaxurl + '?action=ppom_get_products&ppom_id=' + ppom_id;
+ const model_id = $( this ).attr( 'data-formmodal-id' );
+
+ $.get( get_url, function ( html ) {
+ $( '#ppom-product-modal .ppom-modal-body' ).html( html );
+ initAttachSelects();
+ $( '#ppom_id' ).val( ppom_id );
+ $( 'body' ).append( append_overlay_modal );
+ $( '#' + model_id ).fadeIn();
+ $( '#attach-to-products input' ).focus();
+ } );
+ }
+ );
- /**
- 4- Loading Products In Modal DataTable
- **/
- $('#ppom-meta-table_wrapper, .ppom-basic-setting-section').on('click','a.ppom-products-modal', function(e){
-
- e.preventDefault();
-
- $(".ppom-table").DataTable();
- var ppom_id = $(this).data('ppom_id');
- var get_url = ajaxurl+'?action=ppom_get_products&ppom_id='+ppom_id;
- var model_id = $(this).attr('data-formmodal-id');
-
- $.get( get_url, function(html){
- $('#ppom-product-modal .ppom-modal-body').html(html);
- $('.ppom-attach-container-item select')?.select2();
- $("#ppom_id").val(ppom_id);
- $("body").append(append_overly_model);
- $('#'+model_id).fadeIn();
- $("#attach-to-products input").focus();
- });
- });
-
-
- /**
- 5- Delete Single Product Meta
- **/
- $('body').on('click','a.ppom-delete-single-product', function(e){
+ $( 'body' ).on( 'click', 'a.ppom-delete-single-product', function ( e ) {
e.preventDefault();
- const productmeta_id = $(this).attr('data-product-id');
+ const productmeta_id = $( this ).attr( 'data-product-id' );
+ let title = window?.ppom_vars?.i18n.popup.deleteGroup;
+ const productName = $( this ).data( 'name' );
+ title = productName ? title.replace( '%s', productName ) : window?.ppom_vars?.i18n.popup.confirmTitle;
- window?.ppomPopup?.open({
- title: window?.ppom_vars?.i18n.popup.confirmTitle,
+ window?.ppomPopup?.open( {
+ title: title,
onConfirmation: () => {
- $("#del-file-" + productmeta_id).html(' ');
+ $( '#del-file-' + productmeta_id ).html(
+ ' '
+ );
const data = {
- action : 'ppom_delete_meta',
- productmeta_id : productmeta_id,
- ppom_meta_nonce : $("#ppom_meta_nonce").val()
+ action: 'ppom_delete_meta',
+ productmeta_id,
+ ppom_meta_nonce: $( '#ppom_meta_nonce' ).val(),
};
- $.post( ajaxurl, data, function(resp){
- $("#del-file-" + productmeta_id).html('');
- if ( resp.status === 'success' ) {
- window?.ppomPopup?.open({
+ $.post( ajaxurl, data, function ( resp ) {
+ $( '#del-file-' + productmeta_id ).html(
+ ''
+ );
+ if ( resp.status === 'success' ) {
+ window?.ppomPopup?.open( {
title: window?.ppom_vars?.i18n.popup.finishTitle,
hideCloseBtn: true,
onConfirmation: () => location.reload(),
- onClose: () => location.reload()
- });
- } else {
- window?.ppomPopup?.open({
+ onClose: () => location.reload(),
+ } );
+ } else {
+ window?.ppomPopup?.open( {
title: window.ppom_vars.i18n.popup.errorTitle,
text: resp.message,
hideCloseBtn: true,
- });
- }
- });
- }
- })
- });
+ } );
+ }
+ } );
+ },
+ } );
+ } );
- $(document).on( 'change', '#ppom-bulk-actions', function(){
- const type = $(this).val();
+ $( document ).on( 'change', '#ppom-bulk-actions', function () {
+ const type = $( this ).val();
- const checkedProducts_ids = $('.ppom_product_checkbox:checked').map(function() {
- return parseInt(this.value);
- }).get();
+ const checkedProducts_ids = $( '.ppom_product_checkbox:checked' )
+ .map( function () {
+ return parseInt( this.value );
+ } )
+ .get();
if ( ! ( checkedProducts_ids.length > 0 ) ) {
- window?.ppomPopup?.open({
+ window?.ppomPopup?.open( {
title: window?.ppom_vars?.i18n.popup.confirmTitle,
type: 'error',
- hideCloseBtn: true
- });
+ hideCloseBtn: true,
+ } );
return;
}
- if( 'delete' === type ) {
- deleteSelectedProducts(checkedProducts_ids);
- }else if( 'export' === type ) {
- $('#ppom-groups-export-form').submit();
+ const checkedProductsNames = $( '.ppom_product_checkbox:checked' )
+ .map( function () {
+ return this.dataset.name && this.dataset.name.trim() !== ''
+ ? this.dataset.name
+ : this.value;
+ } )
+ .get()
+ .join( ', ' );
+ // Only one action runs per selection. Resetting the select afterwards
+ // prevents DataTables redraws from accidentally replaying the last action.
+ if ( 'delete' === type ) {
+ deleteSelectedProducts( checkedProducts_ids, checkedProductsNames );
+ } else if ( 'export' === type ) {
+ $( '#ppom-groups-export-form' ).submit();
}
- $(this).val(-1);
- });
+ $( this ).val( -1 );
+ } );
- const exportOption = ppom_vars.ppomProActivated === 'yes' ? `` : ``;
+ // Import/export are always surfaced in the toolbar so the locked Pro state
+ // is visible even on Free installs; the markup changes between enabled and
+ // disabled variants based on the localized license flag.
+ const exportOption =
+ ppom_vars.ppomProActivated === 'yes'
+ ? ``
+ : ``;
- const importBtn = `${ppom_vars.i18n.importLabel}`;
+ const importBtn = `${ ppom_vars.i18n.importLabel }`;
const bulkActions = ``;
- const btn = `${ppom_vars.i18n.addGroupLabel}`;
+ const btn = `${ ppom_vars.i18n.addGroupLabel }`;
- $('div.ppom-toolbar').html(`${bulkActions} ${importBtn} ${btn} `);
-});
+ // DataTables creates the placeholder container, then PPOM injects the
+ // toolbar HTML after initialization so the controls stay inside the table UI.
+ $( 'div.ppom-toolbar' ).html(
+ `${ bulkActions } ${ importBtn } ${ btn } `
+ );
+} );
diff --git a/js/admin/pre-load.js b/js/admin/pre-load.js
index 489cfe83..85c7e79e 100644
--- a/js/admin/pre-load.js
+++ b/js/admin/pre-load.js
@@ -1,37 +1,49 @@
- /* Image loader */
-function addListener(element, type, expression, bubbling) {
- bubbling = bubbling || false;
- if (window.addEventListener) { // Standard
- element.addEventListener(type, expression, bubbling);
- return true;
- } else if (window.attachEvent) { // IE
- element.attachEvent('on' + type, expression);
- return true;
- } else return false;
+/**
+ * Small image preloader used by the admin field builder shell.
+ *
+ * The main admin UI stays hidden until the loader asset is available so the
+ * screen does not flash half-rendered styles while the builder initializes.
+ */
+function addListener( element, type, expression, bubbling ) {
+ bubbling = bubbling || false;
+ if ( window.addEventListener ) {
+ // Standard
+ element.addEventListener( type, expression, bubbling );
+ return true;
+ } else if ( window.attachEvent ) {
+ // IE
+ element.attachEvent( 'on' + type, expression );
+ return true;
+ }
+ return false;
}
-
-var ImageLoader = function (url) {
- this.url = url;
- this.image = null;
- this.loadEvent = null;
+const ImageLoader = function ( url ) {
+ this.url = url;
+ this.image = null;
+ this.loadEvent = null;
};
ImageLoader.prototype = {
- load: function () {
- this.image = document.createElement('img');
- var url = this.url;
- var image = this.image;
- var loadEvent = this.loadEvent;
- addListener(this.image, 'load', function (e) {
- if (loadEvent != null) {
- loadEvent(url, image);
- }
- }, false);
- this.image.src = this.url;
- },
- getImage: function () {
- return this.image;
- }
+ load() {
+ this.image = document.createElement( 'img' );
+ const url = this.url;
+ const image = this.image;
+ const loadEvent = this.loadEvent;
+ addListener(
+ this.image,
+ 'load',
+ function ( e ) {
+ if ( loadEvent != null ) {
+ loadEvent( url, image );
+ }
+ },
+ false
+ );
+ this.image.src = this.url;
+ },
+ getImage() {
+ return this.image;
+ },
};
-/* End of image loader */
\ No newline at end of file
+/* End of image loader */
diff --git a/js/admin/serializejson.js b/js/admin/serializejson.js
index 22eb2074..6161367c 100644
--- a/js/admin/serializejson.js
+++ b/js/admin/serializejson.js
@@ -7,4 +7,288 @@
Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
*/
-!function(e){if("function"==typeof define&&define.amd)define(["jquery"],e);else if("object"==typeof exports){var n=require("jquery");module.exports=e(n)}else e(window.jQuery||window.Zepto||window.$)}(function(e){"use strict";var n=/\r?\n/g,r=/^(?:submit|button|image|reset|file)$/i,t=/^(?:input|select|textarea|keygen)/i,i=/^(?:checkbox|radio)$/i;e.fn.serializeJSON=function(n){var r=e.serializeJSON,t=r.setupOpts(n),i=e.extend({},t.defaultTypes,t.customTypes),a=r.serializeArray(this,t),u={};return e.each(a,function(n,a){var s=a.name,l=e(a.el).attr("data-value-type");if(!l&&!t.disableColonTypes){var o=r.splitType(a.name);s=o[0],l=o[1]}if("skip"!==l){l||(l=t.defaultType);var p=r.applyTypeFunc(a.name,a.value,l,a.el,i);if(p||!r.shouldSkipFalsy(a.name,s,l,a.el,t)){var f=r.splitInputNameIntoKeysArray(s);r.deepSet(u,f,p,t)}}}),u},e.serializeJSON={defaultOptions:{},defaultBaseOptions:{checkboxUncheckedValue:void 0,useIntKeysAsArrayIndex:!1,skipFalsyValuesForTypes:[],skipFalsyValuesForFields:[],disableColonTypes:!1,customTypes:{},defaultTypes:{string:function(e){return String(e)},number:function(e){return Number(e)},boolean:function(e){return-1===["false","null","undefined","","0"].indexOf(e)},null:function(e){return-1===["false","null","undefined","","0"].indexOf(e)?e:null},array:function(e){return JSON.parse(e)},object:function(e){return JSON.parse(e)},skip:null},defaultType:"string"},setupOpts:function(n){null==n&&(n={});var r=e.serializeJSON,t=["checkboxUncheckedValue","useIntKeysAsArrayIndex","skipFalsyValuesForTypes","skipFalsyValuesForFields","disableColonTypes","customTypes","defaultTypes","defaultType"];for(var i in n)if(-1===t.indexOf(i))throw new Error("serializeJSON ERROR: invalid option '"+i+"'. Please use one of "+t.join(", "));return e.extend({},r.defaultBaseOptions,r.defaultOptions,n)},serializeArray:function(a,u){null==u&&(u={});var s=e.serializeJSON;return a.map(function(){var n=e.prop(this,"elements");return n?e.makeArray(n):this}).filter(function(){var n=e(this),a=this.type;return this.name&&!n.is(":disabled")&&t.test(this.nodeName)&&!r.test(a)&&(this.checked||!i.test(a)||null!=s.getCheckboxUncheckedValue(n,u))}).map(function(r,t){var a=e(this),l=a.val(),p=this.type;return null==l?null:(i.test(p)&&!this.checked&&(l=s.getCheckboxUncheckedValue(a,u)),o(l)?e.map(l,function(e){return{name:t.name,value:e.replace(n,"\r\n"),el:t}}):{name:t.name,value:l.replace(n,"\r\n"),el:t})}).get()},getCheckboxUncheckedValue:function(e,n){var r=e.attr("data-unchecked-value");return null==r&&(r=n.checkboxUncheckedValue),r},applyTypeFunc:function(e,n,r,t,i){var u=i[r];if(!u)throw new Error("serializeJSON ERROR: Invalid type "+r+" found in input name '"+e+"', please use one of "+a(i).join(", "));return u(n,t)},splitType:function(e){var n=e.split(":");if(n.length>1){var r=n.pop();return[n.join(":"),r]}return[e,""]},shouldSkipFalsy:function(n,r,t,i,a){var u=e(i).attr("data-skip-falsy");if(null!=u)return"false"!==u;var s=a.skipFalsyValuesForFields;if(s&&(-1!==s.indexOf(r)||-1!==s.indexOf(n)))return!0;var l=a.skipFalsyValuesForTypes;return!(!l||-1===l.indexOf(t))},splitInputNameIntoKeysArray:function(n){var r=n.split("[");return""===(r=e.map(r,function(e){return e.replace(/\]/g,"")}))[0]&&r.shift(),r},deepSet:function(n,r,t,i){null==i&&(i={});var a=e.serializeJSON;if(s(n))throw new Error("ArgumentError: param 'o' expected to be an object or array, found undefined");if(!r||0===r.length)throw new Error("ArgumentError: param 'keys' expected to be an array with least one element");var p=r[0];if(1!==r.length){var f=r[1],c=r.slice(1);if(""===p){var d=n.length-1,y=n[d];p=u(y)&&s(a.deepGet(y,c))?d:d+1}""===f?!s(n[p])&&o(n[p])||(n[p]=[]):i.useIntKeysAsArrayIndex&&l(f)?!s(n[p])&&o(n[p])||(n[p]=[]):!s(n[p])&&u(n[p])||(n[p]={}),a.deepSet(n[p],c,t,i)}else""===p?n.push(t):n[p]=t},deepGet:function(n,r){var t=e.serializeJSON;if(s(n)||s(r)||0===r.length||!u(n)&&!o(n))return n;var i=r[0];if(""!==i){if(1===r.length)return n[i];var a=r.slice(1);return t.deepGet(n[i],a)}}};var a=function(e){if(Object.keys)return Object.keys(e);var n,r=[];for(n in e)r.push(n);return r},u=function(e){return e===Object(e)},s=function(e){return void 0===e},l=function(e){return/^[0-9]+$/.test(String(e))},o=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}});
\ No newline at end of file
+! ( function ( e ) {
+ if ( 'function' === typeof define && define.amd ) {
+ define( [ 'jquery' ], e );
+ } else if ( 'object' === typeof exports ) {
+ const n = require( 'jquery' );
+ module.exports = e( n );
+ } else {
+ e( window.jQuery || window.Zepto || window.$ );
+ }
+} )( function ( e ) {
+ 'use strict';
+ const n = /\r?\n/g,
+ r = /^(?:submit|button|image|reset|file)$/i,
+ t = /^(?:input|select|textarea|keygen)/i,
+ i = /^(?:checkbox|radio)$/i;
+ ( e.fn.serializeJSON = function ( n ) {
+ const r = e.serializeJSON,
+ t = r.setupOpts( n ),
+ i = e.extend( {}, t.defaultTypes, t.customTypes ),
+ a = r.serializeArray( this, t ),
+ u = {};
+ return (
+ e.each( a, function ( n, a ) {
+ let s = a.name,
+ l = e( a.el ).attr( 'data-value-type' );
+ if ( ! l && ! t.disableColonTypes ) {
+ const o = r.splitType( a.name );
+ ( s = o[ 0 ] ), ( l = o[ 1 ] );
+ }
+ if ( 'skip' !== l ) {
+ l || ( l = t.defaultType );
+ const p = r.applyTypeFunc( a.name, a.value, l, a.el, i );
+ if ( p || ! r.shouldSkipFalsy( a.name, s, l, a.el, t ) ) {
+ const f = r.splitInputNameIntoKeysArray( s );
+ r.deepSet( u, f, p, t );
+ }
+ }
+ } ),
+ u
+ );
+ } ),
+ ( e.serializeJSON = {
+ defaultOptions: {},
+ defaultBaseOptions: {
+ checkboxUncheckedValue: void 0,
+ useIntKeysAsArrayIndex: ! 1,
+ skipFalsyValuesForTypes: [],
+ skipFalsyValuesForFields: [],
+ disableColonTypes: ! 1,
+ customTypes: {},
+ defaultTypes: {
+ string( e ) {
+ return String( e );
+ },
+ number( e ) {
+ return Number( e );
+ },
+ boolean( e ) {
+ return (
+ -1 ===
+ [ 'false', 'null', 'undefined', '', '0' ].indexOf(
+ e
+ )
+ );
+ },
+ null( e ) {
+ return -1 ===
+ [ 'false', 'null', 'undefined', '', '0' ].indexOf(
+ e
+ )
+ ? e
+ : null;
+ },
+ array( e ) {
+ return JSON.parse( e );
+ },
+ object( e ) {
+ return JSON.parse( e );
+ },
+ skip: null,
+ },
+ defaultType: 'string',
+ },
+ setupOpts( n ) {
+ null == n && ( n = {} );
+ const r = e.serializeJSON,
+ t = [
+ 'checkboxUncheckedValue',
+ 'useIntKeysAsArrayIndex',
+ 'skipFalsyValuesForTypes',
+ 'skipFalsyValuesForFields',
+ 'disableColonTypes',
+ 'customTypes',
+ 'defaultTypes',
+ 'defaultType',
+ ];
+ for ( const i in n ) {
+ if ( -1 === t.indexOf( i ) ) {
+ throw new Error(
+ "serializeJSON ERROR: invalid option '" +
+ i +
+ "'. Please use one of " +
+ t.join( ', ' )
+ );
+ }
+ }
+ return e.extend(
+ {},
+ r.defaultBaseOptions,
+ r.defaultOptions,
+ n
+ );
+ },
+ serializeArray( a, u ) {
+ null == u && ( u = {} );
+ const s = e.serializeJSON;
+ return a
+ .map( function () {
+ const n = e.prop( this, 'elements' );
+ return n ? e.makeArray( n ) : this;
+ } )
+ .filter( function () {
+ const n = e( this ),
+ a = this.type;
+ return (
+ this.name &&
+ ! n.is( ':disabled' ) &&
+ t.test( this.nodeName ) &&
+ ! r.test( a ) &&
+ ( this.checked ||
+ ! i.test( a ) ||
+ null != s.getCheckboxUncheckedValue( n, u ) )
+ );
+ } )
+ .map( function ( r, t ) {
+ let a = e( this ),
+ l = a.val(),
+ p = this.type;
+ return null == l
+ ? null
+ : ( i.test( p ) &&
+ ! this.checked &&
+ ( l = s.getCheckboxUncheckedValue( a, u ) ),
+ o( l )
+ ? e.map( l, function ( e ) {
+ return {
+ name: t.name,
+ value: e.replace( n, '\r\n' ),
+ el: t,
+ };
+ } )
+ : {
+ name: t.name,
+ value: l.replace( n, '\r\n' ),
+ el: t,
+ } );
+ } )
+ .get();
+ },
+ getCheckboxUncheckedValue( e, n ) {
+ let r = e.attr( 'data-unchecked-value' );
+ return null == r && ( r = n.checkboxUncheckedValue ), r;
+ },
+ applyTypeFunc( e, n, r, t, i ) {
+ const u = i[ r ];
+ if ( ! u ) {
+ throw new Error(
+ 'serializeJSON ERROR: Invalid type ' +
+ r +
+ " found in input name '" +
+ e +
+ "', please use one of " +
+ a( i ).join( ', ' )
+ );
+ }
+ return u( n, t );
+ },
+ splitType( e ) {
+ const n = e.split( ':' );
+ if ( n.length > 1 ) {
+ const r = n.pop();
+ return [ n.join( ':' ), r ];
+ }
+ return [ e, '' ];
+ },
+ shouldSkipFalsy( n, r, t, i, a ) {
+ const u = e( i ).attr( 'data-skip-falsy' );
+ if ( null != u ) {
+ return 'false' !== u;
+ }
+ const s = a.skipFalsyValuesForFields;
+ if ( s && ( -1 !== s.indexOf( r ) || -1 !== s.indexOf( n ) ) ) {
+ return ! 0;
+ }
+ const l = a.skipFalsyValuesForTypes;
+ return ! ( ! l || -1 === l.indexOf( t ) );
+ },
+ splitInputNameIntoKeysArray( n ) {
+ let r = n.split( '[' );
+ return (
+ '' ===
+ ( r = e.map( r, function ( e ) {
+ return e.replace( /\]/g, '' );
+ } ) )[ 0 ] && r.shift(),
+ r
+ );
+ },
+ deepSet( n, r, t, i ) {
+ null == i && ( i = {} );
+ const a = e.serializeJSON;
+ if ( s( n ) ) {
+ throw new Error(
+ "ArgumentError: param 'o' expected to be an object or array, found undefined"
+ );
+ }
+ if ( ! r || 0 === r.length ) {
+ throw new Error(
+ "ArgumentError: param 'keys' expected to be an array with least one element"
+ );
+ }
+ let p = r[ 0 ];
+ if ( 1 !== r.length ) {
+ const f = r[ 1 ],
+ c = r.slice( 1 );
+ if ( '' === p ) {
+ const d = n.length - 1,
+ y = n[ d ];
+ p = u( y ) && s( a.deepGet( y, c ) ) ? d : d + 1;
+ }
+ '' === f
+ ? ( ! s( n[ p ] ) && o( n[ p ] ) ) || ( n[ p ] = [] )
+ : i.useIntKeysAsArrayIndex && l( f )
+ ? ( ! s( n[ p ] ) && o( n[ p ] ) ) || ( n[ p ] = [] )
+ : ( ! s( n[ p ] ) && u( n[ p ] ) ) || ( n[ p ] = {} ),
+ a.deepSet( n[ p ], c, t, i );
+ } else {
+ '' === p ? n.push( t ) : ( n[ p ] = t );
+ }
+ },
+ deepGet( n, r ) {
+ const t = e.serializeJSON;
+ if (
+ s( n ) ||
+ s( r ) ||
+ 0 === r.length ||
+ ( ! u( n ) && ! o( n ) )
+ ) {
+ return n;
+ }
+ const i = r[ 0 ];
+ if ( '' !== i ) {
+ if ( 1 === r.length ) {
+ return n[ i ];
+ }
+ const a = r.slice( 1 );
+ return t.deepGet( n[ i ], a );
+ }
+ },
+ } );
+ var a = function ( e ) {
+ if ( Object.keys ) {
+ return Object.keys( e );
+ }
+ let n,
+ r = [];
+ for ( n in e ) {
+ r.push( n );
+ }
+ return r;
+ },
+ u = function ( e ) {
+ return e === Object( e );
+ },
+ s = function ( e ) {
+ return void 0 === e;
+ },
+ l = function ( e ) {
+ return /^[0-9]+$/.test( String( e ) );
+ },
+ o =
+ Array.isArray ||
+ function ( e ) {
+ return '[object Array]' === Object.prototype.toString.call( e );
+ };
+} );
diff --git a/js/exif.js b/js/exif.js
index dfcb08a2..3f937a20 100644
--- a/js/exif.js
+++ b/js/exif.js
@@ -1,1058 +1,1229 @@
-(function() {
-
- var debug = false;
-
- var root = this;
-
- var EXIF = function(obj) {
- if (obj instanceof EXIF) return obj;
- if (!(this instanceof EXIF)) return new EXIF(obj);
- this.EXIFwrapped = obj;
- };
-
- if (typeof exports !== 'undefined') {
- if (typeof module !== 'undefined' && module.exports) {
- exports = module.exports = EXIF;
- }
- exports.EXIF = EXIF;
- } else {
- root.EXIF = EXIF;
- }
-
- var ExifTags = EXIF.Tags = {
-
- // version tags
- 0x9000 : "ExifVersion", // EXIF version
- 0xA000 : "FlashpixVersion", // Flashpix format version
-
- // colorspace tags
- 0xA001 : "ColorSpace", // Color space information tag
-
- // image configuration
- 0xA002 : "PixelXDimension", // Valid width of meaningful image
- 0xA003 : "PixelYDimension", // Valid height of meaningful image
- 0x9101 : "ComponentsConfiguration", // Information about channels
- 0x9102 : "CompressedBitsPerPixel", // Compressed bits per pixel
-
- // user information
- 0x927C : "MakerNote", // Any desired information written by the manufacturer
- 0x9286 : "UserComment", // Comments by user
-
- // related file
- 0xA004 : "RelatedSoundFile", // Name of related sound file
-
- // date and time
- 0x9003 : "DateTimeOriginal", // Date and time when the original image was generated
- 0x9004 : "DateTimeDigitized", // Date and time when the image was stored digitally
- 0x9290 : "SubsecTime", // Fractions of seconds for DateTime
- 0x9291 : "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
- 0x9292 : "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
-
- // picture-taking conditions
- 0x829A : "ExposureTime", // Exposure time (in seconds)
- 0x829D : "FNumber", // F number
- 0x8822 : "ExposureProgram", // Exposure program
- 0x8824 : "SpectralSensitivity", // Spectral sensitivity
- 0x8827 : "ISOSpeedRatings", // ISO speed rating
- 0x8828 : "OECF", // Optoelectric conversion factor
- 0x9201 : "ShutterSpeedValue", // Shutter speed
- 0x9202 : "ApertureValue", // Lens aperture
- 0x9203 : "BrightnessValue", // Value of brightness
- 0x9204 : "ExposureBias", // Exposure bias
- 0x9205 : "MaxApertureValue", // Smallest F number of lens
- 0x9206 : "SubjectDistance", // Distance to subject in meters
- 0x9207 : "MeteringMode", // Metering mode
- 0x9208 : "LightSource", // Kind of light source
- 0x9209 : "Flash", // Flash status
- 0x9214 : "SubjectArea", // Location and area of main subject
- 0x920A : "FocalLength", // Focal length of the lens in mm
- 0xA20B : "FlashEnergy", // Strobe energy in BCPS
- 0xA20C : "SpatialFrequencyResponse", //
- 0xA20E : "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
- 0xA20F : "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
- 0xA210 : "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
- 0xA214 : "SubjectLocation", // Location of subject in image
- 0xA215 : "ExposureIndex", // Exposure index selected on camera
- 0xA217 : "SensingMethod", // Image sensor type
- 0xA300 : "FileSource", // Image source (3 == DSC)
- 0xA301 : "SceneType", // Scene type (1 == directly photographed)
- 0xA302 : "CFAPattern", // Color filter array geometric pattern
- 0xA401 : "CustomRendered", // Special processing
- 0xA402 : "ExposureMode", // Exposure mode
- 0xA403 : "WhiteBalance", // 1 = auto white balance, 2 = manual
- 0xA404 : "DigitalZoomRation", // Digital zoom ratio
- 0xA405 : "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
- 0xA406 : "SceneCaptureType", // Type of scene
- 0xA407 : "GainControl", // Degree of overall image gain adjustment
- 0xA408 : "Contrast", // Direction of contrast processing applied by camera
- 0xA409 : "Saturation", // Direction of saturation processing applied by camera
- 0xA40A : "Sharpness", // Direction of sharpness processing applied by camera
- 0xA40B : "DeviceSettingDescription", //
- 0xA40C : "SubjectDistanceRange", // Distance to subject
-
- // other tags
- 0xA005 : "InteroperabilityIFDPointer",
- 0xA420 : "ImageUniqueID" // Identifier assigned uniquely to each image
- };
-
- var TiffTags = EXIF.TiffTags = {
- 0x0100 : "ImageWidth",
- 0x0101 : "ImageHeight",
- 0x8769 : "ExifIFDPointer",
- 0x8825 : "GPSInfoIFDPointer",
- 0xA005 : "InteroperabilityIFDPointer",
- 0x0102 : "BitsPerSample",
- 0x0103 : "Compression",
- 0x0106 : "PhotometricInterpretation",
- 0x0112 : "Orientation",
- 0x0115 : "SamplesPerPixel",
- 0x011C : "PlanarConfiguration",
- 0x0212 : "YCbCrSubSampling",
- 0x0213 : "YCbCrPositioning",
- 0x011A : "XResolution",
- 0x011B : "YResolution",
- 0x0128 : "ResolutionUnit",
- 0x0111 : "StripOffsets",
- 0x0116 : "RowsPerStrip",
- 0x0117 : "StripByteCounts",
- 0x0201 : "JPEGInterchangeFormat",
- 0x0202 : "JPEGInterchangeFormatLength",
- 0x012D : "TransferFunction",
- 0x013E : "WhitePoint",
- 0x013F : "PrimaryChromaticities",
- 0x0211 : "YCbCrCoefficients",
- 0x0214 : "ReferenceBlackWhite",
- 0x0132 : "DateTime",
- 0x010E : "ImageDescription",
- 0x010F : "Make",
- 0x0110 : "Model",
- 0x0131 : "Software",
- 0x013B : "Artist",
- 0x8298 : "Copyright"
- };
-
- var GPSTags = EXIF.GPSTags = {
- 0x0000 : "GPSVersionID",
- 0x0001 : "GPSLatitudeRef",
- 0x0002 : "GPSLatitude",
- 0x0003 : "GPSLongitudeRef",
- 0x0004 : "GPSLongitude",
- 0x0005 : "GPSAltitudeRef",
- 0x0006 : "GPSAltitude",
- 0x0007 : "GPSTimeStamp",
- 0x0008 : "GPSSatellites",
- 0x0009 : "GPSStatus",
- 0x000A : "GPSMeasureMode",
- 0x000B : "GPSDOP",
- 0x000C : "GPSSpeedRef",
- 0x000D : "GPSSpeed",
- 0x000E : "GPSTrackRef",
- 0x000F : "GPSTrack",
- 0x0010 : "GPSImgDirectionRef",
- 0x0011 : "GPSImgDirection",
- 0x0012 : "GPSMapDatum",
- 0x0013 : "GPSDestLatitudeRef",
- 0x0014 : "GPSDestLatitude",
- 0x0015 : "GPSDestLongitudeRef",
- 0x0016 : "GPSDestLongitude",
- 0x0017 : "GPSDestBearingRef",
- 0x0018 : "GPSDestBearing",
- 0x0019 : "GPSDestDistanceRef",
- 0x001A : "GPSDestDistance",
- 0x001B : "GPSProcessingMethod",
- 0x001C : "GPSAreaInformation",
- 0x001D : "GPSDateStamp",
- 0x001E : "GPSDifferential"
- };
-
- // EXIF 2.3 Spec
- var IFD1Tags = EXIF.IFD1Tags = {
- 0x0100: "ImageWidth",
- 0x0101: "ImageHeight",
- 0x0102: "BitsPerSample",
- 0x0103: "Compression",
- 0x0106: "PhotometricInterpretation",
- 0x0111: "StripOffsets",
- 0x0112: "Orientation",
- 0x0115: "SamplesPerPixel",
- 0x0116: "RowsPerStrip",
- 0x0117: "StripByteCounts",
- 0x011A: "XResolution",
- 0x011B: "YResolution",
- 0x011C: "PlanarConfiguration",
- 0x0128: "ResolutionUnit",
- 0x0201: "JpegIFOffset", // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat")
- 0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength")
- 0x0211: "YCbCrCoefficients",
- 0x0212: "YCbCrSubSampling",
- 0x0213: "YCbCrPositioning",
- 0x0214: "ReferenceBlackWhite"
- };
-
- var StringValues = EXIF.StringValues = {
- ExposureProgram : {
- 0 : "Not defined",
- 1 : "Manual",
- 2 : "Normal program",
- 3 : "Aperture priority",
- 4 : "Shutter priority",
- 5 : "Creative program",
- 6 : "Action program",
- 7 : "Portrait mode",
- 8 : "Landscape mode"
- },
- MeteringMode : {
- 0 : "Unknown",
- 1 : "Average",
- 2 : "CenterWeightedAverage",
- 3 : "Spot",
- 4 : "MultiSpot",
- 5 : "Pattern",
- 6 : "Partial",
- 255 : "Other"
- },
- LightSource : {
- 0 : "Unknown",
- 1 : "Daylight",
- 2 : "Fluorescent",
- 3 : "Tungsten (incandescent light)",
- 4 : "Flash",
- 9 : "Fine weather",
- 10 : "Cloudy weather",
- 11 : "Shade",
- 12 : "Daylight fluorescent (D 5700 - 7100K)",
- 13 : "Day white fluorescent (N 4600 - 5400K)",
- 14 : "Cool white fluorescent (W 3900 - 4500K)",
- 15 : "White fluorescent (WW 3200 - 3700K)",
- 17 : "Standard light A",
- 18 : "Standard light B",
- 19 : "Standard light C",
- 20 : "D55",
- 21 : "D65",
- 22 : "D75",
- 23 : "D50",
- 24 : "ISO studio tungsten",
- 255 : "Other"
- },
- Flash : {
- 0x0000 : "Flash did not fire",
- 0x0001 : "Flash fired",
- 0x0005 : "Strobe return light not detected",
- 0x0007 : "Strobe return light detected",
- 0x0009 : "Flash fired, compulsory flash mode",
- 0x000D : "Flash fired, compulsory flash mode, return light not detected",
- 0x000F : "Flash fired, compulsory flash mode, return light detected",
- 0x0010 : "Flash did not fire, compulsory flash mode",
- 0x0018 : "Flash did not fire, auto mode",
- 0x0019 : "Flash fired, auto mode",
- 0x001D : "Flash fired, auto mode, return light not detected",
- 0x001F : "Flash fired, auto mode, return light detected",
- 0x0020 : "No flash function",
- 0x0041 : "Flash fired, red-eye reduction mode",
- 0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
- 0x0047 : "Flash fired, red-eye reduction mode, return light detected",
- 0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
- 0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
- 0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
- 0x0059 : "Flash fired, auto mode, red-eye reduction mode",
- 0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
- 0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
- },
- SensingMethod : {
- 1 : "Not defined",
- 2 : "One-chip color area sensor",
- 3 : "Two-chip color area sensor",
- 4 : "Three-chip color area sensor",
- 5 : "Color sequential area sensor",
- 7 : "Trilinear sensor",
- 8 : "Color sequential linear sensor"
- },
- SceneCaptureType : {
- 0 : "Standard",
- 1 : "Landscape",
- 2 : "Portrait",
- 3 : "Night scene"
- },
- SceneType : {
- 1 : "Directly photographed"
- },
- CustomRendered : {
- 0 : "Normal process",
- 1 : "Custom process"
- },
- WhiteBalance : {
- 0 : "Auto white balance",
- 1 : "Manual white balance"
- },
- GainControl : {
- 0 : "None",
- 1 : "Low gain up",
- 2 : "High gain up",
- 3 : "Low gain down",
- 4 : "High gain down"
- },
- Contrast : {
- 0 : "Normal",
- 1 : "Soft",
- 2 : "Hard"
- },
- Saturation : {
- 0 : "Normal",
- 1 : "Low saturation",
- 2 : "High saturation"
- },
- Sharpness : {
- 0 : "Normal",
- 1 : "Soft",
- 2 : "Hard"
- },
- SubjectDistanceRange : {
- 0 : "Unknown",
- 1 : "Macro",
- 2 : "Close view",
- 3 : "Distant view"
- },
- FileSource : {
- 3 : "DSC"
- },
-
- Components : {
- 0 : "",
- 1 : "Y",
- 2 : "Cb",
- 3 : "Cr",
- 4 : "R",
- 5 : "G",
- 6 : "B"
- }
- };
-
- function addEvent(element, event, handler) {
- if (element.addEventListener) {
- element.addEventListener(event, handler, false);
- } else if (element.attachEvent) {
- element.attachEvent("on" + event, handler);
- }
- }
-
- function imageHasData(img) {
- return !!(img.exifdata);
- }
-
-
- function base64ToArrayBuffer(base64, contentType) {
- contentType = contentType || base64.match(/^data\:([^\;]+)\;base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
- base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
- var binary = atob(base64);
- var len = binary.length;
- var buffer = new ArrayBuffer(len);
- var view = new Uint8Array(buffer);
- for (var i = 0; i < len; i++) {
- view[i] = binary.charCodeAt(i);
- }
- return buffer;
- }
-
- function objectURLToBlob(url, callback) {
- var http = new XMLHttpRequest();
- http.open("GET", url, true);
- http.responseType = "blob";
- http.onload = function(e) {
- if (this.status == 200 || this.status === 0) {
- callback(this.response);
- }
- };
- http.send();
- }
-
- function getImageData(img, callback) {
- function handleBinaryFile(binFile) {
- var data = findEXIFinJPEG(binFile);
- img.exifdata = data || {};
- var iptcdata = findIPTCinJPEG(binFile);
- img.iptcdata = iptcdata || {};
- if (EXIF.isXmpEnabled) {
- var xmpdata= findXMPinJPEG(binFile);
- img.xmpdata = xmpdata || {};
- }
- if (callback) {
- callback.call(img);
- }
- }
-
- if (img.src) {
- if (/^data\:/i.test(img.src)) { // Data URI
- var arrayBuffer = base64ToArrayBuffer(img.src);
- handleBinaryFile(arrayBuffer);
-
- } else if (/^blob\:/i.test(img.src)) { // Object URL
- var fileReader = new FileReader();
- fileReader.onload = function(e) {
- handleBinaryFile(e.target.result);
- };
- objectURLToBlob(img.src, function (blob) {
- fileReader.readAsArrayBuffer(blob);
- });
- } else {
- var http = new XMLHttpRequest();
- http.onload = function() {
- if (this.status == 200 || this.status === 0) {
- handleBinaryFile(http.response);
- } else {
- throw "Could not load image";
- }
- http = null;
- };
- http.open("GET", img.src, true);
- http.responseType = "arraybuffer";
- http.send(null);
- }
- } else if (self.FileReader && (img instanceof self.Blob || img instanceof self.File)) {
- var fileReader = new FileReader();
- fileReader.onload = function(e) {
- if (debug) console.log("Got file of length " + e.target.result.byteLength);
- handleBinaryFile(e.target.result);
- };
-
- fileReader.readAsArrayBuffer(img);
- }
- }
-
- function findEXIFinJPEG(file) {
- var dataView = new DataView(file);
-
- if (debug) console.log("Got file of length " + file.byteLength);
- if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
- if (debug) console.log("Not a valid JPEG");
- return false; // not a valid jpeg
- }
-
- var offset = 2,
- length = file.byteLength,
- marker;
-
- while (offset < length) {
- if (dataView.getUint8(offset) != 0xFF) {
- if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset));
- return false; // not a valid marker, something is wrong
- }
-
- marker = dataView.getUint8(offset + 1);
- if (debug) console.log(marker);
-
- // we could implement handling for other markers here,
- // but we're only looking for 0xFFE1 for EXIF data
-
- if (marker == 225) {
- if (debug) console.log("Found 0xFFE1 marker");
-
- return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
-
- // offset += 2 + file.getShortAt(offset+2, true);
-
- } else {
- offset += 2 + dataView.getUint16(offset+2);
- }
-
- }
-
- }
-
- function findIPTCinJPEG(file) {
- var dataView = new DataView(file);
-
- if (debug) console.log("Got file of length " + file.byteLength);
- if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
- if (debug) console.log("Not a valid JPEG");
- return false; // not a valid jpeg
- }
-
- var offset = 2,
- length = file.byteLength;
-
-
- var isFieldSegmentStart = function(dataView, offset){
- return (
- dataView.getUint8(offset) === 0x38 &&
- dataView.getUint8(offset+1) === 0x42 &&
- dataView.getUint8(offset+2) === 0x49 &&
- dataView.getUint8(offset+3) === 0x4D &&
- dataView.getUint8(offset+4) === 0x04 &&
- dataView.getUint8(offset+5) === 0x04
- );
- };
-
- while (offset < length) {
-
- if ( isFieldSegmentStart(dataView, offset )){
-
- // Get the length of the name header (which is padded to an even number of bytes)
- var nameHeaderLength = dataView.getUint8(offset+7);
- if(nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
- // Check for pre photoshop 6 format
- if(nameHeaderLength === 0) {
- // Always 4
- nameHeaderLength = 4;
- }
-
- var startOffset = offset + 8 + nameHeaderLength;
- var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);
-
- return readIPTCData(file, startOffset, sectionLength);
-
- break;
-
- }
-
-
- // Not the marker, continue searching
- offset++;
-
- }
-
- }
- var IptcFieldMap = {
- 0x78 : 'caption',
- 0x6E : 'credit',
- 0x19 : 'keywords',
- 0x37 : 'dateCreated',
- 0x50 : 'byline',
- 0x55 : 'bylineTitle',
- 0x7A : 'captionWriter',
- 0x69 : 'headline',
- 0x74 : 'copyright',
- 0x0F : 'category'
- };
- function readIPTCData(file, startOffset, sectionLength){
- var dataView = new DataView(file);
- var data = {};
- var fieldValue, fieldName, dataSize, segmentType, segmentSize;
- var segmentStartPos = startOffset;
- while(segmentStartPos < startOffset+sectionLength) {
- if(dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos+1) === 0x02){
- segmentType = dataView.getUint8(segmentStartPos+2);
- if(segmentType in IptcFieldMap) {
- dataSize = dataView.getInt16(segmentStartPos+3);
- segmentSize = dataSize + 5;
- fieldName = IptcFieldMap[segmentType];
- fieldValue = getStringFromDB(dataView, segmentStartPos+5, dataSize);
- // Check if we already stored a value with this name
- if(data.hasOwnProperty(fieldName)) {
- // Value already stored with this name, create multivalue field
- if(data[fieldName] instanceof Array) {
- data[fieldName].push(fieldValue);
- }
- else {
- data[fieldName] = [data[fieldName], fieldValue];
- }
- }
- else {
- data[fieldName] = fieldValue;
- }
- }
-
- }
- segmentStartPos++;
- }
- return data;
- }
-
-
-
- function readTags(file, tiffStart, dirStart, strings, bigEnd) {
- var entries = file.getUint16(dirStart, !bigEnd),
- tags = {},
- entryOffset, tag,
- i;
-
- for (i=0;i 4 ? valueOffset : (entryOffset + 8);
- vals = [];
- for (n=0;n 4 ? valueOffset : (entryOffset + 8);
- return getStringFromDB(file, offset, numValues-1);
-
- case 3: // short, 16 bit int
- if (numValues == 1) {
- return file.getUint16(entryOffset + 8, !bigEnd);
- } else {
- offset = numValues > 2 ? valueOffset : (entryOffset + 8);
- vals = [];
- for (n=0;n dataView.byteLength) { // this should not happen
- // console.log('******** IFD1Offset is outside the bounds of the DataView ********');
- return {};
- }
- // console.log('******* thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer);
-
- var thumbTags = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd)
-
- // EXIF 2.3 specification for JPEG format thumbnail
-
- // If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG.
- // Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail
- // by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag.
- // Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that
- // JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later.
-
- if (thumbTags['Compression']) {
- // console.log('Thumbnail image found!');
-
- switch (thumbTags['Compression']) {
- case 6:
- // console.log('Thumbnail image format is JPEG');
- if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) {
- // extract the thumbnail
- var tOffset = tiffStart + thumbTags.JpegIFOffset;
- var tLength = thumbTags.JpegIFByteCount;
- thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], {
- type: 'image/jpeg'
- });
- }
- break;
-
- case 1:
- console.log("Thumbnail image format is TIFF, which is not implemented.");
- break;
- default:
- console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']);
- }
- }
- else if (thumbTags['PhotometricInterpretation'] == 2) {
- console.log("Thumbnail image format is RGB, which is not implemented.");
- }
- return thumbTags;
- }
-
- function getStringFromDB(buffer, start, length) {
- var outstr = "";
- for (var n = start; n < start+length; n++) {
- outstr += String.fromCharCode(buffer.getUint8(n));
- }
- return outstr;
- }
-
- function readEXIFData(file, start) {
- if (getStringFromDB(file, start, 4) != "Exif") {
- if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
- return false;
- }
-
- var bigEnd,
- tags, tag,
- exifData, gpsData,
- tiffOffset = start + 6;
-
- // test for TIFF validity and endianness
- if (file.getUint16(tiffOffset) == 0x4949) {
- bigEnd = false;
- } else if (file.getUint16(tiffOffset) == 0x4D4D) {
- bigEnd = true;
- } else {
- if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
- return false;
- }
-
- if (file.getUint16(tiffOffset+2, !bigEnd) != 0x002A) {
- if (debug) console.log("Not valid TIFF data! (no 0x002A)");
- return false;
- }
-
- var firstIFDOffset = file.getUint32(tiffOffset+4, !bigEnd);
-
- if (firstIFDOffset < 0x00000008) {
- if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset+4, !bigEnd));
- return false;
- }
-
- tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
-
- if (tags.ExifIFDPointer) {
- exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
- for (tag in exifData) {
- switch (tag) {
- case "LightSource" :
- case "Flash" :
- case "MeteringMode" :
- case "ExposureProgram" :
- case "SensingMethod" :
- case "SceneCaptureType" :
- case "SceneType" :
- case "CustomRendered" :
- case "WhiteBalance" :
- case "GainControl" :
- case "Contrast" :
- case "Saturation" :
- case "Sharpness" :
- case "SubjectDistanceRange" :
- case "FileSource" :
- exifData[tag] = StringValues[tag][exifData[tag]];
- break;
-
- case "ExifVersion" :
- case "FlashpixVersion" :
- exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
- break;
-
- case "ComponentsConfiguration" :
- exifData[tag] =
- StringValues.Components[exifData[tag][0]] +
- StringValues.Components[exifData[tag][1]] +
- StringValues.Components[exifData[tag][2]] +
- StringValues.Components[exifData[tag][3]];
- break;
- }
- tags[tag] = exifData[tag];
- }
- }
-
- if (tags.GPSInfoIFDPointer) {
- gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
- for (tag in gpsData) {
- switch (tag) {
- case "GPSVersionID" :
- gpsData[tag] = gpsData[tag][0] +
- "." + gpsData[tag][1] +
- "." + gpsData[tag][2] +
- "." + gpsData[tag][3];
- break;
- }
- tags[tag] = gpsData[tag];
- }
- }
-
- // extract thumbnail
- tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd);
-
- return tags;
- }
-
- function findXMPinJPEG(file) {
-
- if (!('DOMParser' in self)) {
- // console.warn('XML parsing not supported without DOMParser');
- return;
- }
- var dataView = new DataView(file);
-
- if (debug) console.log("Got file of length " + file.byteLength);
- if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
- if (debug) console.log("Not a valid JPEG");
- return false; // not a valid jpeg
- }
-
- var offset = 2,
- length = file.byteLength,
- dom = new DOMParser();
-
- while (offset < (length-4)) {
- if (getStringFromDB(dataView, offset, 4) == "http") {
- var startOffset = offset - 1;
- var sectionLength = dataView.getUint16(offset - 2) - 1;
- var xmpString = getStringFromDB(dataView, startOffset, sectionLength)
- var xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8;
- xmpString = xmpString.substring( xmpString.indexOf( ' 0) {
- json['@attributes'] = {};
- for (var j = 0; j < xml.attributes.length; j++) {
- var attribute = xml.attributes.item(j);
- json['@attributes'][attribute.nodeName] = attribute.nodeValue;
- }
- }
- } else if (xml.nodeType == 3) { // text node
- return xml.nodeValue;
- }
-
- // deal with children
- if (xml.hasChildNodes()) {
- for(var i = 0; i < xml.childNodes.length; i++) {
- var child = xml.childNodes.item(i);
- var nodeName = child.nodeName;
- if (json[nodeName] == null) {
- json[nodeName] = xml2json(child);
- } else {
- if (json[nodeName].push == null) {
- var old = json[nodeName];
- json[nodeName] = [];
- json[nodeName].push(old);
- }
- json[nodeName].push(xml2json(child));
- }
- }
- }
-
- return json;
- }
-
- function xml2Object(xml) {
- try {
- var obj = {};
- if (xml.children.length > 0) {
- for (var i = 0; i < xml.children.length; i++) {
- var item = xml.children.item(i);
- var attributes = item.attributes;
- for(var idx in attributes) {
- var itemAtt = attributes[idx];
- var dataKey = itemAtt.nodeName;
- var dataValue = itemAtt.nodeValue;
-
- if(dataKey !== undefined) {
- obj[dataKey] = dataValue;
- }
- }
- var nodeName = item.nodeName;
-
- if (typeof (obj[nodeName]) == "undefined") {
- obj[nodeName] = xml2json(item);
- } else {
- if (typeof (obj[nodeName].push) == "undefined") {
- var old = obj[nodeName];
-
- obj[nodeName] = [];
- obj[nodeName].push(old);
- }
- obj[nodeName].push(xml2json(item));
- }
- }
- } else {
- obj = xml.textContent;
- }
- return obj;
- } catch (e) {
- console.log(e.message);
- }
- }
-
- EXIF.enableXmp = function() {
- EXIF.isXmpEnabled = true;
- }
-
- EXIF.disableXmp = function() {
- EXIF.isXmpEnabled = false;
- }
-
- EXIF.getData = function(img, callback) {
- if (((self.Image && img instanceof self.Image)
- || (self.HTMLImageElement && img instanceof self.HTMLImageElement))
- && !img.complete)
- return false;
-
- if (!imageHasData(img)) {
- getImageData(img, callback);
- } else {
- if (callback) {
- callback.call(img);
- }
- }
- return true;
- }
-
- EXIF.getTag = function(img, tag) {
- if (!imageHasData(img)) return;
- return img.exifdata[tag];
- }
-
- EXIF.getIptcTag = function(img, tag) {
- if (!imageHasData(img)) return;
- return img.iptcdata[tag];
- }
-
- EXIF.getAllTags = function(img) {
- if (!imageHasData(img)) return {};
- var a,
- data = img.exifdata,
- tags = {};
- for (a in data) {
- if (data.hasOwnProperty(a)) {
- tags[a] = data[a];
- }
- }
- return tags;
- }
-
- EXIF.getAllIptcTags = function(img) {
- if (!imageHasData(img)) return {};
- var a,
- data = img.iptcdata,
- tags = {};
- for (a in data) {
- if (data.hasOwnProperty(a)) {
- tags[a] = data[a];
- }
- }
- return tags;
- }
-
- EXIF.pretty = function(img) {
- if (!imageHasData(img)) return "";
- var a,
- data = img.exifdata,
- strPretty = "";
- for (a in data) {
- if (data.hasOwnProperty(a)) {
- if (typeof data[a] == "object") {
- if (data[a] instanceof Number) {
- strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
- } else {
- strPretty += a + " : [" + data[a].length + " values]\r\n";
- }
- } else {
- strPretty += a + " : " + data[a] + "\r\n";
- }
- }
- }
- return strPretty;
- }
-
- EXIF.readFromBinaryFile = function(file) {
- return findEXIFinJPEG(file);
- }
-
- if (typeof define === 'function' && define.amd) {
- define('exif-js', [], function() {
- return EXIF;
- });
- }
-}.call(this));
+( function () {
+ const debug = false;
+
+ const root = this;
+
+ const EXIF = function ( obj ) {
+ if ( obj instanceof EXIF ) {
+ return obj;
+ }
+ if ( ! ( this instanceof EXIF ) ) {
+ return new EXIF( obj );
+ }
+ this.EXIFwrapped = obj;
+ };
+
+ if ( typeof exports !== 'undefined' ) {
+ if ( typeof module !== 'undefined' && module.exports ) {
+ exports = module.exports = EXIF;
+ }
+ exports.EXIF = EXIF;
+ } else {
+ root.EXIF = EXIF;
+ }
+
+ const ExifTags = ( EXIF.Tags = {
+ // version tags
+ 0x9000: 'ExifVersion', // EXIF version
+ 0xa000: 'FlashpixVersion', // Flashpix format version
+
+ // colorspace tags
+ 0xa001: 'ColorSpace', // Color space information tag
+
+ // image configuration
+ 0xa002: 'PixelXDimension', // Valid width of meaningful image
+ 0xa003: 'PixelYDimension', // Valid height of meaningful image
+ 0x9101: 'ComponentsConfiguration', // Information about channels
+ 0x9102: 'CompressedBitsPerPixel', // Compressed bits per pixel
+
+ // user information
+ 0x927c: 'MakerNote', // Any desired information written by the manufacturer
+ 0x9286: 'UserComment', // Comments by user
+
+ // related file
+ 0xa004: 'RelatedSoundFile', // Name of related sound file
+
+ // date and time
+ 0x9003: 'DateTimeOriginal', // Date and time when the original image was generated
+ 0x9004: 'DateTimeDigitized', // Date and time when the image was stored digitally
+ 0x9290: 'SubsecTime', // Fractions of seconds for DateTime
+ 0x9291: 'SubsecTimeOriginal', // Fractions of seconds for DateTimeOriginal
+ 0x9292: 'SubsecTimeDigitized', // Fractions of seconds for DateTimeDigitized
+
+ // picture-taking conditions
+ 0x829a: 'ExposureTime', // Exposure time (in seconds)
+ 0x829d: 'FNumber', // F number
+ 0x8822: 'ExposureProgram', // Exposure program
+ 0x8824: 'SpectralSensitivity', // Spectral sensitivity
+ 0x8827: 'ISOSpeedRatings', // ISO speed rating
+ 0x8828: 'OECF', // Optoelectric conversion factor
+ 0x9201: 'ShutterSpeedValue', // Shutter speed
+ 0x9202: 'ApertureValue', // Lens aperture
+ 0x9203: 'BrightnessValue', // Value of brightness
+ 0x9204: 'ExposureBias', // Exposure bias
+ 0x9205: 'MaxApertureValue', // Smallest F number of lens
+ 0x9206: 'SubjectDistance', // Distance to subject in meters
+ 0x9207: 'MeteringMode', // Metering mode
+ 0x9208: 'LightSource', // Kind of light source
+ 0x9209: 'Flash', // Flash status
+ 0x9214: 'SubjectArea', // Location and area of main subject
+ 0x920a: 'FocalLength', // Focal length of the lens in mm
+ 0xa20b: 'FlashEnergy', // Strobe energy in BCPS
+ 0xa20c: 'SpatialFrequencyResponse', //
+ 0xa20e: 'FocalPlaneXResolution', // Number of pixels in width direction per FocalPlaneResolutionUnit
+ 0xa20f: 'FocalPlaneYResolution', // Number of pixels in height direction per FocalPlaneResolutionUnit
+ 0xa210: 'FocalPlaneResolutionUnit', // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
+ 0xa214: 'SubjectLocation', // Location of subject in image
+ 0xa215: 'ExposureIndex', // Exposure index selected on camera
+ 0xa217: 'SensingMethod', // Image sensor type
+ 0xa300: 'FileSource', // Image source (3 == DSC)
+ 0xa301: 'SceneType', // Scene type (1 == directly photographed)
+ 0xa302: 'CFAPattern', // Color filter array geometric pattern
+ 0xa401: 'CustomRendered', // Special processing
+ 0xa402: 'ExposureMode', // Exposure mode
+ 0xa403: 'WhiteBalance', // 1 = auto white balance, 2 = manual
+ 0xa404: 'DigitalZoomRation', // Digital zoom ratio
+ 0xa405: 'FocalLengthIn35mmFilm', // Equivalent foacl length assuming 35mm film camera (in mm)
+ 0xa406: 'SceneCaptureType', // Type of scene
+ 0xa407: 'GainControl', // Degree of overall image gain adjustment
+ 0xa408: 'Contrast', // Direction of contrast processing applied by camera
+ 0xa409: 'Saturation', // Direction of saturation processing applied by camera
+ 0xa40a: 'Sharpness', // Direction of sharpness processing applied by camera
+ 0xa40b: 'DeviceSettingDescription', //
+ 0xa40c: 'SubjectDistanceRange', // Distance to subject
+
+ // other tags
+ 0xa005: 'InteroperabilityIFDPointer',
+ 0xa420: 'ImageUniqueID', // Identifier assigned uniquely to each image
+ } );
+
+ const TiffTags = ( EXIF.TiffTags = {
+ 0x0100: 'ImageWidth',
+ 0x0101: 'ImageHeight',
+ 0x8769: 'ExifIFDPointer',
+ 0x8825: 'GPSInfoIFDPointer',
+ 0xa005: 'InteroperabilityIFDPointer',
+ 0x0102: 'BitsPerSample',
+ 0x0103: 'Compression',
+ 0x0106: 'PhotometricInterpretation',
+ 0x0112: 'Orientation',
+ 0x0115: 'SamplesPerPixel',
+ 0x011c: 'PlanarConfiguration',
+ 0x0212: 'YCbCrSubSampling',
+ 0x0213: 'YCbCrPositioning',
+ 0x011a: 'XResolution',
+ 0x011b: 'YResolution',
+ 0x0128: 'ResolutionUnit',
+ 0x0111: 'StripOffsets',
+ 0x0116: 'RowsPerStrip',
+ 0x0117: 'StripByteCounts',
+ 0x0201: 'JPEGInterchangeFormat',
+ 0x0202: 'JPEGInterchangeFormatLength',
+ 0x012d: 'TransferFunction',
+ 0x013e: 'WhitePoint',
+ 0x013f: 'PrimaryChromaticities',
+ 0x0211: 'YCbCrCoefficients',
+ 0x0214: 'ReferenceBlackWhite',
+ 0x0132: 'DateTime',
+ 0x010e: 'ImageDescription',
+ 0x010f: 'Make',
+ 0x0110: 'Model',
+ 0x0131: 'Software',
+ 0x013b: 'Artist',
+ 0x8298: 'Copyright',
+ } );
+
+ const GPSTags = ( EXIF.GPSTags = {
+ 0x0000: 'GPSVersionID',
+ 0x0001: 'GPSLatitudeRef',
+ 0x0002: 'GPSLatitude',
+ 0x0003: 'GPSLongitudeRef',
+ 0x0004: 'GPSLongitude',
+ 0x0005: 'GPSAltitudeRef',
+ 0x0006: 'GPSAltitude',
+ 0x0007: 'GPSTimeStamp',
+ 0x0008: 'GPSSatellites',
+ 0x0009: 'GPSStatus',
+ 0x000a: 'GPSMeasureMode',
+ 0x000b: 'GPSDOP',
+ 0x000c: 'GPSSpeedRef',
+ 0x000d: 'GPSSpeed',
+ 0x000e: 'GPSTrackRef',
+ 0x000f: 'GPSTrack',
+ 0x0010: 'GPSImgDirectionRef',
+ 0x0011: 'GPSImgDirection',
+ 0x0012: 'GPSMapDatum',
+ 0x0013: 'GPSDestLatitudeRef',
+ 0x0014: 'GPSDestLatitude',
+ 0x0015: 'GPSDestLongitudeRef',
+ 0x0016: 'GPSDestLongitude',
+ 0x0017: 'GPSDestBearingRef',
+ 0x0018: 'GPSDestBearing',
+ 0x0019: 'GPSDestDistanceRef',
+ 0x001a: 'GPSDestDistance',
+ 0x001b: 'GPSProcessingMethod',
+ 0x001c: 'GPSAreaInformation',
+ 0x001d: 'GPSDateStamp',
+ 0x001e: 'GPSDifferential',
+ } );
+
+ // EXIF 2.3 Spec
+ const IFD1Tags = ( EXIF.IFD1Tags = {
+ 0x0100: 'ImageWidth',
+ 0x0101: 'ImageHeight',
+ 0x0102: 'BitsPerSample',
+ 0x0103: 'Compression',
+ 0x0106: 'PhotometricInterpretation',
+ 0x0111: 'StripOffsets',
+ 0x0112: 'Orientation',
+ 0x0115: 'SamplesPerPixel',
+ 0x0116: 'RowsPerStrip',
+ 0x0117: 'StripByteCounts',
+ 0x011a: 'XResolution',
+ 0x011b: 'YResolution',
+ 0x011c: 'PlanarConfiguration',
+ 0x0128: 'ResolutionUnit',
+ 0x0201: 'JpegIFOffset', // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat")
+ 0x0202: 'JpegIFByteCount', // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength")
+ 0x0211: 'YCbCrCoefficients',
+ 0x0212: 'YCbCrSubSampling',
+ 0x0213: 'YCbCrPositioning',
+ 0x0214: 'ReferenceBlackWhite',
+ } );
+
+ const StringValues = ( EXIF.StringValues = {
+ ExposureProgram: {
+ 0: 'Not defined',
+ 1: 'Manual',
+ 2: 'Normal program',
+ 3: 'Aperture priority',
+ 4: 'Shutter priority',
+ 5: 'Creative program',
+ 6: 'Action program',
+ 7: 'Portrait mode',
+ 8: 'Landscape mode',
+ },
+ MeteringMode: {
+ 0: 'Unknown',
+ 1: 'Average',
+ 2: 'CenterWeightedAverage',
+ 3: 'Spot',
+ 4: 'MultiSpot',
+ 5: 'Pattern',
+ 6: 'Partial',
+ 255: 'Other',
+ },
+ LightSource: {
+ 0: 'Unknown',
+ 1: 'Daylight',
+ 2: 'Fluorescent',
+ 3: 'Tungsten (incandescent light)',
+ 4: 'Flash',
+ 9: 'Fine weather',
+ 10: 'Cloudy weather',
+ 11: 'Shade',
+ 12: 'Daylight fluorescent (D 5700 - 7100K)',
+ 13: 'Day white fluorescent (N 4600 - 5400K)',
+ 14: 'Cool white fluorescent (W 3900 - 4500K)',
+ 15: 'White fluorescent (WW 3200 - 3700K)',
+ 17: 'Standard light A',
+ 18: 'Standard light B',
+ 19: 'Standard light C',
+ 20: 'D55',
+ 21: 'D65',
+ 22: 'D75',
+ 23: 'D50',
+ 24: 'ISO studio tungsten',
+ 255: 'Other',
+ },
+ Flash: {
+ 0x0000: 'Flash did not fire',
+ 0x0001: 'Flash fired',
+ 0x0005: 'Strobe return light not detected',
+ 0x0007: 'Strobe return light detected',
+ 0x0009: 'Flash fired, compulsory flash mode',
+ 0x000d: 'Flash fired, compulsory flash mode, return light not detected',
+ 0x000f: 'Flash fired, compulsory flash mode, return light detected',
+ 0x0010: 'Flash did not fire, compulsory flash mode',
+ 0x0018: 'Flash did not fire, auto mode',
+ 0x0019: 'Flash fired, auto mode',
+ 0x001d: 'Flash fired, auto mode, return light not detected',
+ 0x001f: 'Flash fired, auto mode, return light detected',
+ 0x0020: 'No flash function',
+ 0x0041: 'Flash fired, red-eye reduction mode',
+ 0x0045: 'Flash fired, red-eye reduction mode, return light not detected',
+ 0x0047: 'Flash fired, red-eye reduction mode, return light detected',
+ 0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode',
+ 0x004d: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
+ 0x004f: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
+ 0x0059: 'Flash fired, auto mode, red-eye reduction mode',
+ 0x005d: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
+ 0x005f: 'Flash fired, auto mode, return light detected, red-eye reduction mode',
+ },
+ SensingMethod: {
+ 1: 'Not defined',
+ 2: 'One-chip color area sensor',
+ 3: 'Two-chip color area sensor',
+ 4: 'Three-chip color area sensor',
+ 5: 'Color sequential area sensor',
+ 7: 'Trilinear sensor',
+ 8: 'Color sequential linear sensor',
+ },
+ SceneCaptureType: {
+ 0: 'Standard',
+ 1: 'Landscape',
+ 2: 'Portrait',
+ 3: 'Night scene',
+ },
+ SceneType: {
+ 1: 'Directly photographed',
+ },
+ CustomRendered: {
+ 0: 'Normal process',
+ 1: 'Custom process',
+ },
+ WhiteBalance: {
+ 0: 'Auto white balance',
+ 1: 'Manual white balance',
+ },
+ GainControl: {
+ 0: 'None',
+ 1: 'Low gain up',
+ 2: 'High gain up',
+ 3: 'Low gain down',
+ 4: 'High gain down',
+ },
+ Contrast: {
+ 0: 'Normal',
+ 1: 'Soft',
+ 2: 'Hard',
+ },
+ Saturation: {
+ 0: 'Normal',
+ 1: 'Low saturation',
+ 2: 'High saturation',
+ },
+ Sharpness: {
+ 0: 'Normal',
+ 1: 'Soft',
+ 2: 'Hard',
+ },
+ SubjectDistanceRange: {
+ 0: 'Unknown',
+ 1: 'Macro',
+ 2: 'Close view',
+ 3: 'Distant view',
+ },
+ FileSource: {
+ 3: 'DSC',
+ },
+
+ Components: {
+ 0: '',
+ 1: 'Y',
+ 2: 'Cb',
+ 3: 'Cr',
+ 4: 'R',
+ 5: 'G',
+ 6: 'B',
+ },
+ } );
+
+ function addEvent( element, event, handler ) {
+ if ( element.addEventListener ) {
+ element.addEventListener( event, handler, false );
+ } else if ( element.attachEvent ) {
+ element.attachEvent( 'on' + event, handler );
+ }
+ }
+
+ function imageHasData( img ) {
+ return !! img.exifdata;
+ }
+
+ function base64ToArrayBuffer( base64, contentType ) {
+ contentType =
+ contentType ||
+ base64.match( /^data\:([^\;]+)\;base64,/im )[ 1 ] ||
+ ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
+ base64 = base64.replace( /^data\:([^\;]+)\;base64,/gim, '' );
+ const binary = atob( base64 );
+ const len = binary.length;
+ const buffer = new ArrayBuffer( len );
+ const view = new Uint8Array( buffer );
+ for ( let i = 0; i < len; i++ ) {
+ view[ i ] = binary.charCodeAt( i );
+ }
+ return buffer;
+ }
+
+ function objectURLToBlob( url, callback ) {
+ const http = new XMLHttpRequest();
+ http.open( 'GET', url, true );
+ http.responseType = 'blob';
+ http.onload = function ( e ) {
+ if ( this.status == 200 || this.status === 0 ) {
+ callback( this.response );
+ }
+ };
+ http.send();
+ }
+
+ function getImageData( img, callback ) {
+ function handleBinaryFile( binFile ) {
+ const data = findEXIFinJPEG( binFile );
+ img.exifdata = data || {};
+ const iptcdata = findIPTCinJPEG( binFile );
+ img.iptcdata = iptcdata || {};
+ if ( EXIF.isXmpEnabled ) {
+ const xmpdata = findXMPinJPEG( binFile );
+ img.xmpdata = xmpdata || {};
+ }
+ if ( callback ) {
+ callback.call( img );
+ }
+ }
+
+ if ( img.src ) {
+ if ( /^data\:/i.test( img.src ) ) {
+ // Data URI
+ const arrayBuffer = base64ToArrayBuffer( img.src );
+ handleBinaryFile( arrayBuffer );
+ } else if ( /^blob\:/i.test( img.src ) ) {
+ // Object URL
+ var fileReader = new FileReader();
+ fileReader.onload = function ( e ) {
+ handleBinaryFile( e.target.result );
+ };
+ objectURLToBlob( img.src, function ( blob ) {
+ fileReader.readAsArrayBuffer( blob );
+ } );
+ } else {
+ let http = new XMLHttpRequest();
+ http.onload = function () {
+ if ( this.status == 200 || this.status === 0 ) {
+ handleBinaryFile( http.response );
+ } else {
+ throw 'Could not load image';
+ }
+ http = null;
+ };
+ http.open( 'GET', img.src, true );
+ http.responseType = 'arraybuffer';
+ http.send( null );
+ }
+ } else if (
+ self.FileReader &&
+ ( img instanceof self.Blob || img instanceof self.File )
+ ) {
+ var fileReader = new FileReader();
+ fileReader.onload = function ( e ) {
+ if ( debug ) {
+ console.log(
+ 'Got file of length ' + e.target.result.byteLength
+ );
+ }
+ handleBinaryFile( e.target.result );
+ };
+
+ fileReader.readAsArrayBuffer( img );
+ }
+ }
+
+ function findEXIFinJPEG( file ) {
+ const dataView = new DataView( file );
+
+ if ( debug ) {
+ console.log( 'Got file of length ' + file.byteLength );
+ }
+ if (
+ dataView.getUint8( 0 ) != 0xff ||
+ dataView.getUint8( 1 ) != 0xd8
+ ) {
+ if ( debug ) {
+ console.log( 'Not a valid JPEG' );
+ }
+ return false; // not a valid jpeg
+ }
+
+ let offset = 2,
+ length = file.byteLength,
+ marker;
+
+ while ( offset < length ) {
+ if ( dataView.getUint8( offset ) != 0xff ) {
+ if ( debug ) {
+ console.log(
+ 'Not a valid marker at offset ' +
+ offset +
+ ', found: ' +
+ dataView.getUint8( offset )
+ );
+ }
+ return false; // not a valid marker, something is wrong
+ }
+
+ marker = dataView.getUint8( offset + 1 );
+ if ( debug ) {
+ console.log( marker );
+ }
+
+ // we could implement handling for other markers here,
+ // but we're only looking for 0xFFE1 for EXIF data
+
+ if ( marker == 225 ) {
+ if ( debug ) {
+ console.log( 'Found 0xFFE1 marker' );
+ }
+
+ return readEXIFData(
+ dataView,
+ offset + 4,
+ dataView.getUint16( offset + 2 ) - 2
+ );
+
+ // offset += 2 + file.getShortAt(offset+2, true);
+ }
+ offset += 2 + dataView.getUint16( offset + 2 );
+ }
+ }
+
+ function findIPTCinJPEG( file ) {
+ const dataView = new DataView( file );
+
+ if ( debug ) {
+ console.log( 'Got file of length ' + file.byteLength );
+ }
+ if (
+ dataView.getUint8( 0 ) != 0xff ||
+ dataView.getUint8( 1 ) != 0xd8
+ ) {
+ if ( debug ) {
+ console.log( 'Not a valid JPEG' );
+ }
+ return false; // not a valid jpeg
+ }
+
+ let offset = 2,
+ length = file.byteLength;
+
+ const isFieldSegmentStart = function ( dataView, offset ) {
+ return (
+ dataView.getUint8( offset ) === 0x38 &&
+ dataView.getUint8( offset + 1 ) === 0x42 &&
+ dataView.getUint8( offset + 2 ) === 0x49 &&
+ dataView.getUint8( offset + 3 ) === 0x4d &&
+ dataView.getUint8( offset + 4 ) === 0x04 &&
+ dataView.getUint8( offset + 5 ) === 0x04
+ );
+ };
+
+ while ( offset < length ) {
+ if ( isFieldSegmentStart( dataView, offset ) ) {
+ // Get the length of the name header (which is padded to an even number of bytes)
+ let nameHeaderLength = dataView.getUint8( offset + 7 );
+ if ( nameHeaderLength % 2 !== 0 ) {
+ nameHeaderLength += 1;
+ }
+ // Check for pre photoshop 6 format
+ if ( nameHeaderLength === 0 ) {
+ // Always 4
+ nameHeaderLength = 4;
+ }
+
+ const startOffset = offset + 8 + nameHeaderLength;
+ const sectionLength = dataView.getUint16(
+ offset + 6 + nameHeaderLength
+ );
+
+ return readIPTCData( file, startOffset, sectionLength );
+
+ break;
+ }
+
+ // Not the marker, continue searching
+ offset++;
+ }
+ }
+ const IptcFieldMap = {
+ 0x78: 'caption',
+ 0x6e: 'credit',
+ 0x19: 'keywords',
+ 0x37: 'dateCreated',
+ 0x50: 'byline',
+ 0x55: 'bylineTitle',
+ 0x7a: 'captionWriter',
+ 0x69: 'headline',
+ 0x74: 'copyright',
+ 0x0f: 'category',
+ };
+ function readIPTCData( file, startOffset, sectionLength ) {
+ const dataView = new DataView( file );
+ const data = {};
+ let fieldValue, fieldName, dataSize, segmentType, segmentSize;
+ let segmentStartPos = startOffset;
+ while ( segmentStartPos < startOffset + sectionLength ) {
+ if (
+ dataView.getUint8( segmentStartPos ) === 0x1c &&
+ dataView.getUint8( segmentStartPos + 1 ) === 0x02
+ ) {
+ segmentType = dataView.getUint8( segmentStartPos + 2 );
+ if ( segmentType in IptcFieldMap ) {
+ dataSize = dataView.getInt16( segmentStartPos + 3 );
+ segmentSize = dataSize + 5;
+ fieldName = IptcFieldMap[ segmentType ];
+ fieldValue = getStringFromDB(
+ dataView,
+ segmentStartPos + 5,
+ dataSize
+ );
+ // Check if we already stored a value with this name
+ if ( data.hasOwnProperty( fieldName ) ) {
+ // Value already stored with this name, create multivalue field
+ if ( data[ fieldName ] instanceof Array ) {
+ data[ fieldName ].push( fieldValue );
+ } else {
+ data[ fieldName ] = [
+ data[ fieldName ],
+ fieldValue,
+ ];
+ }
+ } else {
+ data[ fieldName ] = fieldValue;
+ }
+ }
+ }
+ segmentStartPos++;
+ }
+ return data;
+ }
+
+ function readTags( file, tiffStart, dirStart, strings, bigEnd ) {
+ let entries = file.getUint16( dirStart, ! bigEnd ),
+ tags = {},
+ entryOffset,
+ tag,
+ i;
+
+ for ( i = 0; i < entries; i++ ) {
+ entryOffset = dirStart + i * 12 + 2;
+ tag = strings[ file.getUint16( entryOffset, ! bigEnd ) ];
+ if ( ! tag && debug ) {
+ console.log(
+ 'Unknown tag: ' + file.getUint16( entryOffset, ! bigEnd )
+ );
+ }
+ tags[ tag ] = readTagValue(
+ file,
+ entryOffset,
+ tiffStart,
+ dirStart,
+ bigEnd
+ );
+ }
+ return tags;
+ }
+
+ function readTagValue( file, entryOffset, tiffStart, dirStart, bigEnd ) {
+ let type = file.getUint16( entryOffset + 2, ! bigEnd ),
+ numValues = file.getUint32( entryOffset + 4, ! bigEnd ),
+ valueOffset =
+ file.getUint32( entryOffset + 8, ! bigEnd ) + tiffStart,
+ offset,
+ vals,
+ val,
+ n,
+ numerator,
+ denominator;
+
+ switch ( type ) {
+ case 1: // byte, 8-bit unsigned int
+ case 7: // undefined, 8-bit byte, value depending on field
+ if ( numValues == 1 ) {
+ return file.getUint8( entryOffset + 8, ! bigEnd );
+ }
+ offset = numValues > 4 ? valueOffset : entryOffset + 8;
+ vals = [];
+ for ( n = 0; n < numValues; n++ ) {
+ vals[ n ] = file.getUint8( offset + n );
+ }
+ return vals;
+
+ case 2: // ascii, 8-bit byte
+ offset = numValues > 4 ? valueOffset : entryOffset + 8;
+ return getStringFromDB( file, offset, numValues - 1 );
+
+ case 3: // short, 16 bit int
+ if ( numValues == 1 ) {
+ return file.getUint16( entryOffset + 8, ! bigEnd );
+ }
+ offset = numValues > 2 ? valueOffset : entryOffset + 8;
+ vals = [];
+ for ( n = 0; n < numValues; n++ ) {
+ vals[ n ] = file.getUint16( offset + 2 * n, ! bigEnd );
+ }
+ return vals;
+
+ case 4: // long, 32 bit int
+ if ( numValues == 1 ) {
+ return file.getUint32( entryOffset + 8, ! bigEnd );
+ }
+ vals = [];
+ for ( n = 0; n < numValues; n++ ) {
+ vals[ n ] = file.getUint32( valueOffset + 4 * n, ! bigEnd );
+ }
+ return vals;
+
+ case 5: // rational = two long values, first is numerator, second is denominator
+ if ( numValues == 1 ) {
+ numerator = file.getUint32( valueOffset, ! bigEnd );
+ denominator = file.getUint32( valueOffset + 4, ! bigEnd );
+ val = new Number( numerator / denominator );
+ val.numerator = numerator;
+ val.denominator = denominator;
+ return val;
+ }
+ vals = [];
+ for ( n = 0; n < numValues; n++ ) {
+ numerator = file.getUint32( valueOffset + 8 * n, ! bigEnd );
+ denominator = file.getUint32(
+ valueOffset + 4 + 8 * n,
+ ! bigEnd
+ );
+ vals[ n ] = new Number( numerator / denominator );
+ vals[ n ].numerator = numerator;
+ vals[ n ].denominator = denominator;
+ }
+ return vals;
+
+ case 9: // slong, 32 bit signed int
+ if ( numValues == 1 ) {
+ return file.getInt32( entryOffset + 8, ! bigEnd );
+ }
+ vals = [];
+ for ( n = 0; n < numValues; n++ ) {
+ vals[ n ] = file.getInt32( valueOffset + 4 * n, ! bigEnd );
+ }
+ return vals;
+
+ case 10: // signed rational, two slongs, first is numerator, second is denominator
+ if ( numValues == 1 ) {
+ return (
+ file.getInt32( valueOffset, ! bigEnd ) /
+ file.getInt32( valueOffset + 4, ! bigEnd )
+ );
+ }
+ vals = [];
+ for ( n = 0; n < numValues; n++ ) {
+ vals[ n ] =
+ file.getInt32( valueOffset + 8 * n, ! bigEnd ) /
+ file.getInt32( valueOffset + 4 + 8 * n, ! bigEnd );
+ }
+ return vals;
+ }
+ }
+
+ /**
+ * Given an IFD (Image File Directory) start offset
+ * returns an offset to next IFD or 0 if it's the last IFD.
+ * @param dataView
+ * @param dirStart
+ * @param bigEnd
+ */
+ function getNextIFDOffset( dataView, dirStart, bigEnd ) {
+ //the first 2bytes means the number of directory entries contains in this IFD
+ const entries = dataView.getUint16( dirStart, ! bigEnd );
+
+ // After last directory entry, there is a 4bytes of data,
+ // it means an offset to next IFD.
+ // If its value is '0x00000000', it means this is the last IFD and there is no linked IFD.
+
+ return dataView.getUint32( dirStart + 2 + entries * 12, ! bigEnd ); // each entry is 12 bytes long
+ }
+
+ function readThumbnailImage( dataView, tiffStart, firstIFDOffset, bigEnd ) {
+ // get the IFD1 offset
+ const IFD1OffsetPointer = getNextIFDOffset(
+ dataView,
+ tiffStart + firstIFDOffset,
+ bigEnd
+ );
+
+ if ( ! IFD1OffsetPointer ) {
+ // console.log('******** IFD1Offset is empty, image thumb not found ********');
+ return {};
+ } else if ( IFD1OffsetPointer > dataView.byteLength ) {
+ // this should not happen
+ // console.log('******** IFD1Offset is outside the bounds of the DataView ********');
+ return {};
+ }
+ // console.log('******* thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer);
+
+ const thumbTags = readTags(
+ dataView,
+ tiffStart,
+ tiffStart + IFD1OffsetPointer,
+ IFD1Tags,
+ bigEnd
+ );
+
+ // EXIF 2.3 specification for JPEG format thumbnail
+
+ // If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG.
+ // Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail
+ // by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag.
+ // Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that
+ // JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later.
+
+ if ( thumbTags.Compression ) {
+ // console.log('Thumbnail image found!');
+
+ switch ( thumbTags.Compression ) {
+ case 6:
+ // console.log('Thumbnail image format is JPEG');
+ if ( thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount ) {
+ // extract the thumbnail
+ const tOffset = tiffStart + thumbTags.JpegIFOffset;
+ const tLength = thumbTags.JpegIFByteCount;
+ thumbTags.blob = new Blob(
+ [
+ new Uint8Array(
+ dataView.buffer,
+ tOffset,
+ tLength
+ ),
+ ],
+ {
+ type: 'image/jpeg',
+ }
+ );
+ }
+ break;
+
+ case 1:
+ console.log(
+ 'Thumbnail image format is TIFF, which is not implemented.'
+ );
+ break;
+ default:
+ console.log(
+ "Unknown thumbnail image format '%s'",
+ thumbTags.Compression
+ );
+ }
+ } else if ( thumbTags.PhotometricInterpretation == 2 ) {
+ console.log(
+ 'Thumbnail image format is RGB, which is not implemented.'
+ );
+ }
+ return thumbTags;
+ }
+
+ function getStringFromDB( buffer, start, length ) {
+ let outstr = '';
+ for ( let n = start; n < start + length; n++ ) {
+ outstr += String.fromCharCode( buffer.getUint8( n ) );
+ }
+ return outstr;
+ }
+
+ function readEXIFData( file, start ) {
+ if ( getStringFromDB( file, start, 4 ) != 'Exif' ) {
+ if ( debug ) {
+ console.log(
+ 'Not valid EXIF data! ' + getStringFromDB( file, start, 4 )
+ );
+ }
+ return false;
+ }
+
+ let bigEnd,
+ tags,
+ tag,
+ exifData,
+ gpsData,
+ tiffOffset = start + 6;
+
+ // test for TIFF validity and endianness
+ if ( file.getUint16( tiffOffset ) == 0x4949 ) {
+ bigEnd = false;
+ } else if ( file.getUint16( tiffOffset ) == 0x4d4d ) {
+ bigEnd = true;
+ } else {
+ if ( debug ) {
+ console.log( 'Not valid TIFF data! (no 0x4949 or 0x4D4D)' );
+ }
+ return false;
+ }
+
+ if ( file.getUint16( tiffOffset + 2, ! bigEnd ) != 0x002a ) {
+ if ( debug ) {
+ console.log( 'Not valid TIFF data! (no 0x002A)' );
+ }
+ return false;
+ }
+
+ const firstIFDOffset = file.getUint32( tiffOffset + 4, ! bigEnd );
+
+ if ( firstIFDOffset < 0x00000008 ) {
+ if ( debug ) {
+ console.log(
+ 'Not valid TIFF data! (First offset less than 8)',
+ file.getUint32( tiffOffset + 4, ! bigEnd )
+ );
+ }
+ return false;
+ }
+
+ tags = readTags(
+ file,
+ tiffOffset,
+ tiffOffset + firstIFDOffset,
+ TiffTags,
+ bigEnd
+ );
+
+ if ( tags.ExifIFDPointer ) {
+ exifData = readTags(
+ file,
+ tiffOffset,
+ tiffOffset + tags.ExifIFDPointer,
+ ExifTags,
+ bigEnd
+ );
+ for ( tag in exifData ) {
+ switch ( tag ) {
+ case 'LightSource':
+ case 'Flash':
+ case 'MeteringMode':
+ case 'ExposureProgram':
+ case 'SensingMethod':
+ case 'SceneCaptureType':
+ case 'SceneType':
+ case 'CustomRendered':
+ case 'WhiteBalance':
+ case 'GainControl':
+ case 'Contrast':
+ case 'Saturation':
+ case 'Sharpness':
+ case 'SubjectDistanceRange':
+ case 'FileSource':
+ exifData[ tag ] =
+ StringValues[ tag ][ exifData[ tag ] ];
+ break;
+
+ case 'ExifVersion':
+ case 'FlashpixVersion':
+ exifData[ tag ] = String.fromCharCode(
+ exifData[ tag ][ 0 ],
+ exifData[ tag ][ 1 ],
+ exifData[ tag ][ 2 ],
+ exifData[ tag ][ 3 ]
+ );
+ break;
+
+ case 'ComponentsConfiguration':
+ exifData[ tag ] =
+ StringValues.Components[ exifData[ tag ][ 0 ] ] +
+ StringValues.Components[ exifData[ tag ][ 1 ] ] +
+ StringValues.Components[ exifData[ tag ][ 2 ] ] +
+ StringValues.Components[ exifData[ tag ][ 3 ] ];
+ break;
+ }
+ tags[ tag ] = exifData[ tag ];
+ }
+ }
+
+ if ( tags.GPSInfoIFDPointer ) {
+ gpsData = readTags(
+ file,
+ tiffOffset,
+ tiffOffset + tags.GPSInfoIFDPointer,
+ GPSTags,
+ bigEnd
+ );
+ for ( tag in gpsData ) {
+ switch ( tag ) {
+ case 'GPSVersionID':
+ gpsData[ tag ] =
+ gpsData[ tag ][ 0 ] +
+ '.' +
+ gpsData[ tag ][ 1 ] +
+ '.' +
+ gpsData[ tag ][ 2 ] +
+ '.' +
+ gpsData[ tag ][ 3 ];
+ break;
+ }
+ tags[ tag ] = gpsData[ tag ];
+ }
+ }
+
+ // extract thumbnail
+ tags.thumbnail = readThumbnailImage(
+ file,
+ tiffOffset,
+ firstIFDOffset,
+ bigEnd
+ );
+
+ return tags;
+ }
+
+ function findXMPinJPEG( file ) {
+ if ( ! ( 'DOMParser' in self ) ) {
+ // console.warn('XML parsing not supported without DOMParser');
+ return;
+ }
+ const dataView = new DataView( file );
+
+ if ( debug ) {
+ console.log( 'Got file of length ' + file.byteLength );
+ }
+ if (
+ dataView.getUint8( 0 ) != 0xff ||
+ dataView.getUint8( 1 ) != 0xd8
+ ) {
+ if ( debug ) {
+ console.log( 'Not a valid JPEG' );
+ }
+ return false; // not a valid jpeg
+ }
+
+ let offset = 2,
+ length = file.byteLength,
+ dom = new DOMParser();
+
+ while ( offset < length - 4 ) {
+ if ( getStringFromDB( dataView, offset, 4 ) == 'http' ) {
+ const startOffset = offset - 1;
+ const sectionLength = dataView.getUint16( offset - 2 ) - 1;
+ let xmpString = getStringFromDB(
+ dataView,
+ startOffset,
+ sectionLength
+ );
+ const xmpEndIndex = xmpString.indexOf( 'xmpmeta>' ) + 8;
+ xmpString = xmpString.substring(
+ xmpString.indexOf( ' 0 ) {
+ json[ '@attributes' ] = {};
+ for ( let j = 0; j < xml.attributes.length; j++ ) {
+ const attribute = xml.attributes.item( j );
+ json[ '@attributes' ][ attribute.nodeName ] =
+ attribute.nodeValue;
+ }
+ }
+ } else if ( xml.nodeType == 3 ) {
+ // text node
+ return xml.nodeValue;
+ }
+
+ // deal with children
+ if ( xml.hasChildNodes() ) {
+ for ( let i = 0; i < xml.childNodes.length; i++ ) {
+ const child = xml.childNodes.item( i );
+ const nodeName = child.nodeName;
+ if ( json[ nodeName ] == null ) {
+ json[ nodeName ] = xml2json( child );
+ } else {
+ if ( json[ nodeName ].push == null ) {
+ const old = json[ nodeName ];
+ json[ nodeName ] = [];
+ json[ nodeName ].push( old );
+ }
+ json[ nodeName ].push( xml2json( child ) );
+ }
+ }
+ }
+
+ return json;
+ }
+
+ function xml2Object( xml ) {
+ try {
+ let obj = {};
+ if ( xml.children.length > 0 ) {
+ for ( let i = 0; i < xml.children.length; i++ ) {
+ const item = xml.children.item( i );
+ const attributes = item.attributes;
+ for ( const idx in attributes ) {
+ const itemAtt = attributes[ idx ];
+ const dataKey = itemAtt.nodeName;
+ const dataValue = itemAtt.nodeValue;
+
+ if ( dataKey !== undefined ) {
+ obj[ dataKey ] = dataValue;
+ }
+ }
+ const nodeName = item.nodeName;
+
+ if ( typeof obj[ nodeName ] === 'undefined' ) {
+ obj[ nodeName ] = xml2json( item );
+ } else {
+ if ( typeof obj[ nodeName ].push === 'undefined' ) {
+ const old = obj[ nodeName ];
+
+ obj[ nodeName ] = [];
+ obj[ nodeName ].push( old );
+ }
+ obj[ nodeName ].push( xml2json( item ) );
+ }
+ }
+ } else {
+ obj = xml.textContent;
+ }
+ return obj;
+ } catch ( e ) {
+ console.log( e.message );
+ }
+ }
+
+ EXIF.enableXmp = function () {
+ EXIF.isXmpEnabled = true;
+ };
+
+ EXIF.disableXmp = function () {
+ EXIF.isXmpEnabled = false;
+ };
+
+ EXIF.getData = function ( img, callback ) {
+ if (
+ ( ( self.Image && img instanceof self.Image ) ||
+ ( self.HTMLImageElement &&
+ img instanceof self.HTMLImageElement ) ) &&
+ ! img.complete
+ ) {
+ return false;
+ }
+
+ if ( ! imageHasData( img ) ) {
+ getImageData( img, callback );
+ } else if ( callback ) {
+ callback.call( img );
+ }
+ return true;
+ };
+
+ EXIF.getTag = function ( img, tag ) {
+ if ( ! imageHasData( img ) ) {
+ return;
+ }
+ return img.exifdata[ tag ];
+ };
+
+ EXIF.getIptcTag = function ( img, tag ) {
+ if ( ! imageHasData( img ) ) {
+ return;
+ }
+ return img.iptcdata[ tag ];
+ };
+
+ EXIF.getAllTags = function ( img ) {
+ if ( ! imageHasData( img ) ) {
+ return {};
+ }
+ let a,
+ data = img.exifdata,
+ tags = {};
+ for ( a in data ) {
+ if ( data.hasOwnProperty( a ) ) {
+ tags[ a ] = data[ a ];
+ }
+ }
+ return tags;
+ };
+
+ EXIF.getAllIptcTags = function ( img ) {
+ if ( ! imageHasData( img ) ) {
+ return {};
+ }
+ let a,
+ data = img.iptcdata,
+ tags = {};
+ for ( a in data ) {
+ if ( data.hasOwnProperty( a ) ) {
+ tags[ a ] = data[ a ];
+ }
+ }
+ return tags;
+ };
+
+ EXIF.pretty = function ( img ) {
+ if ( ! imageHasData( img ) ) {
+ return '';
+ }
+ let a,
+ data = img.exifdata,
+ strPretty = '';
+ for ( a in data ) {
+ if ( data.hasOwnProperty( a ) ) {
+ if ( typeof data[ a ] === 'object' ) {
+ if ( data[ a ] instanceof Number ) {
+ strPretty +=
+ a +
+ ' : ' +
+ data[ a ] +
+ ' [' +
+ data[ a ].numerator +
+ '/' +
+ data[ a ].denominator +
+ ']\r\n';
+ } else {
+ strPretty +=
+ a + ' : [' + data[ a ].length + ' values]\r\n';
+ }
+ } else {
+ strPretty += a + ' : ' + data[ a ] + '\r\n';
+ }
+ }
+ }
+ return strPretty;
+ };
+
+ EXIF.readFromBinaryFile = function ( file ) {
+ return findEXIFinJPEG( file );
+ };
+
+ if ( typeof define === 'function' && define.amd ) {
+ define( 'exif-js', [], function () {
+ return EXIF;
+ } );
+ }
+} ).call( this );
diff --git a/js/file-upload.js b/js/file-upload.js
index 0524ffc4..5da7e2bf 100644
--- a/js/file-upload.js
+++ b/js/file-upload.js
@@ -1,703 +1,980 @@
/**
- * file upload js
- * @since 8.4
- **/
+ * File and cropper workflow for PPOM frontend fields.
+ *
+ * Each file/cropper field gets a dedicated Plupload instance. This script owns
+ * the upload UI, preview/croppie state, and the hidden inputs that eventually
+ * travel through add-to-cart, cart restore, and order meta persistence.
+ *
+ * @see ppom_get_field_meta_by_id in js/ppom.inputs.js
+ * @see ppom_update_option_prices in js/price/ppom-price.js
+ * @see ppom_generate_cropper_data_for_cart
+ */
+
+/**
+ * Minimal localized metadata used by the upload/cropper bootstrap.
+ *
+ * @typedef {{
+ * data_name: string,
+ * type: 'file'|'cropper',
+ * file_size: string,
+ * files_allowed: string,
+ * file_types: string,
+ * title?: string,
+ * required?: string,
+ * file_cost?: string,
+ * onetime?: string,
+ * max_img_w?: string,
+ * min_img_w?: string,
+ * max_img_h?: string,
+ * min_img_h?: string
+ * }} PPOMUploadFieldMeta
+ */
let isCartBlock = false;
+// Runtime registries keyed by PPOM field data_name.
const plupload_instances = Array();
const field_file_count = Array();
const file_list_preview_containers = Array();
-var ppom_file_progress = '';
-var featherEditor = '';
+const ppom_file_progress = '';
+const featherEditor = '';
const uploaderInstances = {};
-var Cropped_Data_Captured = false;
-
-jQuery(function($) {
-
- // If cropper input found in fields
- // if (ppom_get_field_meta_by_type('cropper').length > 0) {
-
- // var wc_cart_form = $('form.cart');
- // $(wc_cart_form).on('submit', function(e) {
-
- // // e.preventDefault();
- // var cropper_fields = ppom_get_field_meta_by_type('cropper');
- // $.each(cropper_fields, function(i, cropper) {
-
- // if (cropper.legacy_cropper !== undefined) return;
-
- // var cropper_name = cropper.data_name;
- // ppom_generate_cropper_data_for_cart(cropper.data_name);
-
- // });
- // });
- // }
-
- $(document).on('ppom_image_ready', function(e) {
-
- var image_url = e.image_url;
- var image_id = e.image.id;
- var data_name = e.data_name;
- var input_type = e.input_type;
-
- if (input_type === 'cropper') {
-
- field_meta = ppom_get_field_meta_by_id(data_name);
- // console.log('ppom',field_meta)
- if (field_meta.legacy_cropper === undefined) {
- ppom_show_cropped_preview(data_name, image_url, image_id);
- // hiding the filelist-{data_name} when preview enabled
- $(`#filelist-${data_name}`).hide();
- // hide the file upload area too
- $(`.ppom-file-container.${data_name}`).hide();
- // also hide the crop ratio if only one option is provided
- if( $(`#crop-size-${data_name} option`).length === 1){
- $(`#crop-size-${data_name}`).hide();
- }
- }
- }
-
- // moving modal to body end
- $('.ppom-modals').appendTo('body');
- });
-
- // On file removed
- $(document).on('ppom_uploaded_file_removed', function(e) {
-
- var field_name = e.field_name;
- // var fileid = e.fileid;
-
- ppom_reset_cropping_preview(field_name);
- ppom_update_option_prices();
- });
-
-
- // Croppie update size
- $('.ppom-croppie-preview').on('change', '.ppom-cropping-size', function(e) {
-
- var data_name = $(this).data('field_name');
- var cropp_preview_container = jQuery(".ppom-croppie-wrapper-" + data_name);
- var v_width = $('option:selected', this).data('width');
- var v_height = $('option:selected', this).data('height');
-
- cropp_preview_container.find('.croppie-container').each(function(i, croppie_dom) {
-
- var image_id = jQuery(croppie_dom).attr('data-image_id');
- $(croppie_dom).croppie('destroy');
- const viewport = {'width': v_width, 'height': v_height};
- ppom_set_croppie_options(data_name, viewport, image_id);
- });
-
- });
-
- // Deleting File
- document.querySelector('.ppom-wrapper')?.addEventListener('click', async function(e) {
- if (
- ! e.target.classList.contains('u_i_c_tools_del') ||
- ! plupload_instances
- ) {
- return;
- }
-
- e.preventDefault();
-
- const delMessage = ppom_file_vars.delete_file_msg;
- if ( ! confirm( delMessage ) ) return;
-
- const ppomFileWrapper = e.target.closest('.ppom-file-wrapper');
- const fileId = ppomFileWrapper?.getAttribute("data-fileid");
- const ppomFieldWrapper = e.target.closest('div.ppom-field-wrapper');
- const fileDataName = ppomFieldWrapper?.getAttribute("data-data_name");
-
- if ( !fileId || !fileDataName ) return;
-
- field_file_count[fileDataName] = 0;
-
- const uploaderInstance = plupload_instances[fileDataName];
- if ( uploaderInstance ) {
- uploaderInstance.removeFile(fileId);
- }
-
- const checkbox = document.querySelector(`input[name="ppom[fields][${fileDataName}][${fileId}][org]"]`);
- const fileName = checkbox?.value;
-
- if ( ! fileName ) return;
-
- // Delete animation.
- const imageElement = document.querySelector(`#u_i_c_${fileId} img`);
- if ( imageElement ) {
- imageElement.src = `${ppom_file_vars.plugin_url}/images/loading.gif`;
- }
-
- const data = new URLSearchParams({
- action: 'ppom_delete_file',
- file_name: fileName,
- ppom_nonce: ppom_file_vars.ppom_file_delete_nonce
- });
-
- try {
- const response = await fetch(ppom_file_vars.ajaxurl, {
- method: 'POST',
- body: data,
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'X-Requested-With': 'XMLHttpRequest'
- }
- });
-
- const responseText = await response.text();
- if ( ! response.ok ) {
- confirm(`Error: ${responseText}`);
- return;
- }
-
- // Update UI
- const fileContainer = document.querySelector(`#u_i_c_${fileId}`);
- if ( fileContainer ) {
- fileContainer.remove();
- }
-
- if ( checkbox ) {
- checkbox.remove();
- }
-
- const parentBox = e.target.closest('.u_i_c_box');
- if ( parentBox ) {
- parentBox.remove();
- }
-
- const croppiePreview = document.querySelector(`.ppom-croppie-preview-${fileId}`);
- if ( croppiePreview ) {
- croppiePreview.remove();
- }
-
- // Send action to PPOM_Validate
- document.dispatchEvent(new CustomEvent("ppom_uploaded_file_removed", {
- detail: {
- field_name: fileDataName,
- fileid: fileId,
- time: new Date()
- }
- }));
-
- // Decrease file count
- field_file_count[fileDataName] -= 1;
-
- } catch (error) {
- confirm(`Error: ${error.message}`);
- }
- });
-
- $.each(ppom_input_vars.ppom_inputs, function(index, file_input) {
-
-
- if (file_input.type === 'file' || file_input.type === 'cropper') {
-
- var file_data_name = file_input.data_name;
- ppom_setup_file_upload_input(file_input);
- }
-
- }); // $.each(ppom_file_vars
-
-
-}); // jQuery(function($){});
-
-// generate thumbbox
-function add_thumb_box(file, $filelist_DIV) {
-
- let inner_html = '(' + plupload.formatSize(file.size) + ') ';
- inner_html += '' + file.name + ' ';
-
- jQuery('', {
- 'id': 'u_i_c_' + file.id,
- 'class': 'uk-text-center ppom-file-wrapper',
- 'data-fileid': file.id,
- 'html': inner_html,
-
- }).appendTo($filelist_DIV);
-
- // clearfix
- // 1- removing last clearfix first
- $filelist_DIV.find('.u_i_c_box_clearfix').remove();
-
- jQuery('', {
- 'class': 'u_i_c_box_clearfix',
- }).appendTo($filelist_DIV);
-
+const Cropped_Data_Captured = false;
+
+jQuery( function ( $ ) {
+ // Keep cropper previews, price recalculation, and modal placement aligned
+ // with the rest of the PPOM product form lifecycle.
+ // If cropper input found in fields
+ // if (ppom_get_field_meta_by_type('cropper').length > 0) {
+
+ // var wc_cart_form = $('form.cart');
+ // $(wc_cart_form).on('submit', function(e) {
+
+ // // e.preventDefault();
+ // var cropper_fields = ppom_get_field_meta_by_type('cropper');
+ // $.each(cropper_fields, function(i, cropper) {
+
+ // if (cropper.legacy_cropper !== undefined) return;
+
+ // var cropper_name = cropper.data_name;
+ // ppom_generate_cropper_data_for_cart(cropper.data_name);
+
+ // });
+ // });
+ // }
+
+ $( document ).on( 'ppom_image_ready', function ( e ) {
+ const image_url = e.image_url;
+ const image_id = e.image.id;
+ const data_name = e.data_name;
+ const input_type = e.input_type;
+ const file_input = e.file_input;
+
+ if ( input_type === 'cropper' ) {
+ field_meta = ppom_get_field_meta_by_id( data_name );
+ // console.log('ppom',field_meta)
+ if ( field_meta.legacy_cropper === undefined ) {
+ ppom_show_cropped_preview(
+ data_name,
+ image_url,
+ image_id,
+ file_input
+ );
+ // hiding the filelist-{data_name} when preview enabled
+ $( `#filelist-${ data_name }` ).hide();
+ // hide the file upload area too
+ $( `.ppom-file-container.${ data_name }` ).hide();
+ // also hide the crop ratio if only one option is provided
+ if ( $( `#crop-size-${ data_name } option` ).length === 1 ) {
+ $( `#crop-size-${ data_name }` ).hide();
+ }
+ }
+ }
+
+ // moving modal to body end
+ $( '.ppom-modals' ).appendTo( 'body' );
+ } );
+
+ // On file removed
+ $( document ).on( 'ppom_uploaded_file_removed', function ( e ) {
+ const field_name = e.field_name;
+ // var fileid = e.fileid;
+
+ ppom_reset_cropping_preview( field_name );
+ ppom_update_option_prices();
+ } );
+
+ // Croppie update size
+ $( '.ppom-croppie-preview' ).on(
+ 'change',
+ '.ppom-cropping-size',
+ function ( e ) {
+ const data_name = $( this ).data( 'field_name' );
+ const cropp_preview_container = jQuery(
+ '.ppom-croppie-wrapper-' + data_name
+ );
+ const v_width = $( 'option:selected', this ).data( 'width' );
+ const v_height = $( 'option:selected', this ).data( 'height' );
+
+ cropp_preview_container
+ .find( '.croppie-container' )
+ .each( function ( i, croppie_dom ) {
+ const image_id = jQuery( croppie_dom ).attr(
+ 'data-image_id'
+ );
+ const croppie_container = jQuery(
+ '.ppom-croppie-preview-' + image_id
+ );
+ const image_url = jQuery( croppie_dom )
+ .find( 'img' )
+ .attr( 'src' );
+ $( croppie_dom ).croppie( 'destroy' );
+ const viewport = { width: v_width, height: v_height };
+
+ file_list_preview_containers[ data_name ].croppie[
+ image_id
+ ] = croppie_container;
+ file_list_preview_containers[ data_name ].image_id = image_id;
+ file_list_preview_containers[ data_name ].image_url = image_url;
+
+ ppom_set_croppie_options( data_name, viewport, image_id );
+ } );
+ }
+ );
+
+ // Deleting File
+ document
+ .querySelector( '.ppom-wrapper' )
+ ?.addEventListener( 'click', async function ( e ) {
+ if (
+ ! e.target.classList.contains( 'u_i_c_tools_del' ) ||
+ ! plupload_instances
+ ) {
+ return;
+ }
+
+ e.preventDefault();
+
+ const delMessage = ppom_file_vars.delete_file_msg;
+ if ( ! confirm( delMessage ) ) {
+ return;
+ }
+
+ const ppomFileWrapper = e.target.closest( '.ppom-file-wrapper' );
+ const fileId = ppomFileWrapper?.getAttribute( 'data-fileid' );
+ const ppomFieldWrapper = e.target.closest(
+ 'div.ppom-field-wrapper'
+ );
+ const fileDataName =
+ ppomFieldWrapper?.getAttribute( 'data-data_name' );
+
+ if ( ! fileId || ! fileDataName ) {
+ return;
+ }
+
+ field_file_count[ fileDataName ] = 0;
+
+ const uploaderInstance = plupload_instances[ fileDataName ];
+ if ( uploaderInstance ) {
+ uploaderInstance.removeFile( fileId );
+ }
+
+ const checkbox = document.querySelector(
+ `input[name="ppom[fields][${ fileDataName }][${ fileId }][org]"]`
+ );
+ const fileName = checkbox?.value;
+
+ if ( ! fileName ) {
+ return;
+ }
+
+ // Delete animation.
+ const imageElement = document.querySelector(
+ `#u_i_c_${ fileId } img`
+ );
+ if ( imageElement ) {
+ imageElement.src = `${ ppom_file_vars.plugin_url }/images/loading.gif`;
+ }
+
+ const data = new URLSearchParams( {
+ action: 'ppom_delete_file',
+ file_name: fileName,
+ ppom_nonce: ppom_file_vars.ppom_file_delete_nonce,
+ } );
+
+ try {
+ const response = await fetch( ppom_file_vars.ajaxurl, {
+ method: 'POST',
+ body: data,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ } );
+
+ const responseText = await response.text();
+ if ( ! response.ok ) {
+ confirm( `Error: ${ responseText }` );
+ return;
+ }
+
+ // Update UI
+ const fileContainer = document.querySelector(
+ `#u_i_c_${ fileId }`
+ );
+ if ( fileContainer ) {
+ fileContainer.remove();
+ }
+
+ if ( checkbox ) {
+ checkbox.remove();
+ }
+
+ const parentBox = e.target.closest( '.u_i_c_box' );
+ if ( parentBox ) {
+ parentBox.remove();
+ }
+
+ const croppiePreview = document.querySelector(
+ `.ppom-croppie-preview-${ fileId }`
+ );
+ if ( croppiePreview ) {
+ croppiePreview.remove();
+ }
+
+ // Send action to PPOM_Validate
+ document.dispatchEvent(
+ new CustomEvent( 'ppom_uploaded_file_removed', {
+ detail: {
+ field_name: fileDataName,
+ fileid: fileId,
+ time: new Date(),
+ },
+ } )
+ );
+
+ // Decrease file count
+ field_file_count[ fileDataName ] -= 1;
+ } catch ( error ) {
+ confirm( `Error: ${ error.message }` );
+ }
+ } );
+
+ $.each( ppom_input_vars.ppom_inputs, function ( index, file_input ) {
+ if ( file_input.type === 'file' || file_input.type === 'cropper' ) {
+ const file_data_name = file_input.data_name;
+ ppom_setup_file_upload_input( file_input );
+ }
+ } ); // $.each(ppom_file_vars
+} ); // jQuery(function($){});
+
+// Build the temporary thumbnail shell shown while a file is uploading.
+function add_thumb_box( file, $filelist_DIV ) {
+ let inner_html =
+ '(' +
+ plupload.formatSize( file.size ) +
+ ') ';
+ inner_html +=
+ '' + file.name + ' ';
+
+ jQuery( '', {
+ id: 'u_i_c_' + file.id,
+ class: 'uk-text-center ppom-file-wrapper',
+ 'data-fileid': file.id,
+ html: inner_html,
+ } ).appendTo( $filelist_DIV );
+
+ // clearfix
+ // 1- removing last clearfix first
+ $filelist_DIV.find( '.u_i_c_box_clearfix' ).remove();
+
+ jQuery( '', {
+ class: 'u_i_c_box_clearfix',
+ } ).appendTo( $filelist_DIV );
}
-
// save croped/edited photo
-function save_edited_photo(img_id, photo_url) {
-
- //console.log(img_id);
-
- //setting new image width to 75
- jQuery('#' + img_id).attr('width', 75);
-
- //disabling add to cart button for a while
- jQuery('form.cart').block({
- message: null,
- overlayCSS: {
- background: "#fff",
- opacity: .6
- }
- });
- var post_data = {
- action: 'ppom_save_edited_photo',
- image_url: photo_url,
- filename: jQuery('#' + img_id).attr('data-filename')
- };
-
- jQuery.post(ppom_file_vars.ajaxurl, post_data, function(resp) {
-
- //console.log( resp );
- jQuery('form.cart').unblock();
-
- });
+function save_edited_photo( img_id, photo_url ) {
+ //console.log(img_id);
+
+ //setting new image width to 75
+ jQuery( '#' + img_id ).attr( 'width', 75 );
+
+ //disabling add to cart button for a while
+ jQuery( 'form.cart' ).block( {
+ message: null,
+ overlayCSS: {
+ background: '#fff',
+ opacity: 0.6,
+ },
+ } );
+ const post_data = {
+ action: 'ppom_save_edited_photo',
+ image_url: photo_url,
+ filename: jQuery( '#' + img_id ).attr( 'data-filename' ),
+ };
+
+ jQuery.post( ppom_file_vars.ajaxurl, post_data, function ( resp ) {
+ //console.log( resp );
+ jQuery( 'form.cart' ).unblock();
+ } );
}
-// Cropping image with Croppie
-function ppom_show_cropped_preview(file_name, image_url, image_id) {
-
- var cropp_preview_container = jQuery(".ppom-croppie-wrapper-" + file_name);
- // Enable size option
- cropp_preview_container.find('.ppom-cropping-size').prop('disabled', false);
- cropp_preview_container.find('.ppom-cropping-size').show();
-
- const croppie_container = jQuery('')
- .addClass('ppom-croppie-preview-' + image_id)
- .attr('data-image_id', image_id)
- .appendTo(cropp_preview_container);
-
- // Change preview image
- jQuery('')
- .addClass('btn ' + image_id)
- .attr('href', '#')
- .html('Change image')
- .appendTo(cropp_preview_container)
- .click(function(e){
- e.preventDefault();
- location.reload();
- });
-
-
- // file_list_preview_containers[file_name]['croppie'] = cropp_preview_container.find('.ppom-croppie-preview');
-
- jQuery(croppie_container).on('update.croppie', function(ev, cropData) {
- // console.log(cropData);
- // croppie_container.croppie('result', 'rawcanvas').then(function(canvas) {
- // console.log(canvas);
-
- ppom_generate_cropper_data_for_cart(file_name);
-
- jQuery.event.trigger({
- type: 'ppom_croppie_update',
- img_id: image_id,
- croppie_obj: croppie_container,
- crop_data: cropData,
- dataname: file_name,
- time: new Date()
- });
-
- });
-
- file_list_preview_containers[file_name]['croppie'][image_id] = croppie_container;
- file_list_preview_containers[file_name]['image_id'] = image_id;
- file_list_preview_containers[file_name]['image_url'] = image_url;
-
- ppom_set_croppie_options(file_name, undefined, image_id);
+// Once an upload finishes, create the Croppie preview that feeds the hidden
+// cropped-image payload later submitted with the add-to-cart request.
+function ppom_show_cropped_preview(
+ file_name,
+ image_url,
+ image_id,
+ file_input
+) {
+ const cropp_preview_container = jQuery(
+ '.ppom-croppie-wrapper-' + file_name
+ );
+ // Enable size option
+ cropp_preview_container
+ .find( '.ppom-cropping-size' )
+ .prop( 'disabled', false );
+ cropp_preview_container.find( '.ppom-cropping-size' ).show();
+
+ const container_width = jQuery(
+ '#ppom-file-container-' + file_name
+ ).width();
+ const croppie_container = jQuery( '' )
+ .addClass( 'ppom-croppie-preview-' + image_id )
+ .attr( 'data-image_id', image_id )
+ .appendTo( cropp_preview_container );
+
+ // Change preview image
+ jQuery( '' )
+ .addClass( 'btn ' + image_id )
+ .attr( 'href', 'javascript:;' )
+ .attr( 'id', 'selectfiles-' + file_name + '-' + image_id )
+ .attr( 'data-field-name', file_name )
+ .attr( 'data-image-id', image_id )
+ .html( 'Change image' )
+ .appendTo( cropp_preview_container )
+ .click( function ( e ) {
+ e.preventDefault();
+ } );
+
+ const file_inputs = {
+ ...file_input,
+ data_name: file_name + '-' + image_id,
+ is_change_image: true,
+ original_data_name: file_name,
+ };
+ ppom_setup_file_upload_input( file_inputs );
+
+ // file_list_preview_containers[file_name]['croppie'] = cropp_preview_container.find('.ppom-croppie-preview');
+
+ jQuery( croppie_container ).on(
+ 'update.croppie',
+ function ( ev, cropData ) {
+ // console.log(cropData);
+ // croppie_container.croppie('result', 'rawcanvas').then(function(canvas) {
+ // console.log(canvas);
+
+ ppom_generate_cropper_data_for_cart( file_name );
+
+ jQuery.event.trigger( {
+ type: 'ppom_croppie_update',
+ img_id: image_id,
+ croppie_obj: croppie_container,
+ crop_data: cropData,
+ dataname: file_name,
+ time: new Date(),
+ } );
+ }
+ );
+
+ file_list_preview_containers[ file_name ].croppie[ image_id ] =
+ croppie_container;
+ file_list_preview_containers[ file_name ].image_id = image_id;
+ file_list_preview_containers[ file_name ].image_url = image_url;
+
+ file_list_preview_containers[ file_name ].container_width =
+ container_width;
+ ppom_set_croppie_options( file_name, undefined, image_id );
}
-function ppom_set_croppie_options(file_name, viewport, image_id) {
-
- const croppie_options = ppom_file_vars.croppie_options;
- jQuery.each(croppie_options, function(field_name, option) {
-
- if (file_name === field_name) {
-
- option.url = file_list_preview_containers[file_name]['image_url'];
- if (viewport !== undefined) {
- viewport.type = option.viewport.type;
- option.viewport = viewport;
- }
-
- // console.log($filelist_DIV[file_name]['croppie'][image_id]);
- file_list_preview_containers[file_name]['croppie'][image_id].croppie(option);
- }
- });
+function ppom_set_croppie_options( file_name, viewport, image_id ) {
+ const croppie_options = ppom_file_vars.croppie_options;
+ jQuery.each( croppie_options, function ( field_name, option ) {
+ if ( file_name === field_name ) {
+ option.url = file_list_preview_containers[ file_name ].image_url;
+ if ( viewport !== undefined ) {
+ viewport.type = option.viewport.type;
+ option.viewport = viewport;
+ }
+
+ const preview = file_list_preview_containers[ file_name ];
+ let applied = option;
+ if (
+ preview.container_width !== undefined &&
+ preview.container_width !== null
+ ) {
+ applied = get_responsive_croppie_options(
+ option,
+ preview.container_width
+ );
+ }
+ // console.log($filelist_DIV[file_name]['croppie'][image_id]);
+ preview.croppie[ image_id ].croppie( applied );
+ }
+ } );
}
// Reset cropping when image removed
-function ppom_reset_cropping_preview(file_name) {
-
- var cropp_preview_container = jQuery(".ppom-croppie-wrapper-" + file_name);
- // Reseting preview DOM
- cropp_preview_container.find('.ppom-croppie-preview').html('');
+function ppom_reset_cropping_preview( file_name ) {
+ const cropp_preview_container = jQuery(
+ '.ppom-croppie-wrapper-' + file_name
+ );
+ // Reseting preview DOM
+ cropp_preview_container.find( '.ppom-croppie-preview' ).html( '' );
}
-// Attach FILE API with DOM
-function ppom_setup_file_upload_input(file_input) {
-
- const file_data_name = file_input.data_name;
- if ( plupload_instances[file_data_name] !== undefined ) {
- return;
- }
-
- field_file_count[file_data_name] = 0;
- file_list_preview_containers[file_data_name] = jQuery('#filelist-' + file_data_name);
-
- // Energy pack
- const bar = window.document.getElementById(`ppom-progressbar-${file_data_name}`);
-
- const ppom_file_data = {
- 'action': 'ppom_upload_file',
- 'data_name': file_data_name,
- 'ppom_nonce': ppom_file_vars.ppom_file_upload_nonce,
- 'product_id': ppom_file_vars.product_id,
- };
-
- let img_dim_errormsg = 'Please upload correct image dimension';
- if (file_input.img_dimension_error) {
- img_dim_errormsg = file_input.img_dimension_error;
- }
-
- plupload_instances[file_data_name] = new plupload.Uploader({
- runtimes: ppom_file_vars.plupload_runtime,
- browse_button: 'selectfiles-' + file_data_name, // you can pass in id...
- container: 'ppom-file-container-' + file_data_name, // ... or DOM Element itself
- drop_element: 'ppom-file-container-' + file_data_name,
- url: ppom_file_vars.ajaxurl,
- multipart_params: ppom_file_data,
- max_file_size: file_input.file_size,
- max_file_count: parseInt(file_input.files_allowed),
- unique_names: ppom_file_vars.enable_file_rename,
- chunk_size: '2mb',
- unique_names: false,
-
- filters: {
- mime_types: [
- { title: "Filetypes", extensions: file_input.file_types }
- ]
- },
-
- init: {
- PostInit: function() {
-
- // file_list_preview_containers[file_data_name].html('');
- if ( ! file_list_preview_containers[file_data_name].is(':visible') ) {
- jQuery(document).on('ppom_field_shown', function() {
-
- jQuery.each(ppom_input_vars.ppom_inputs, function(index, file_input) {
- if (file_input && (file_input.type === 'file' || file_input.type === 'cropper')) {
- if (
- file_input.data_name &&
- file_input.files_allowed &&
- file_input.file_size &&
- file_input.files_allowed
- ) {
- ppom_setup_file_upload_input(file_input);
- }
- }
-
- });
- } );
- }
- /*$('#uploadfiles-'+file_data_name).bind('click', function() {
+/**
+ * Attach one Plupload instance per PPOM file/cropper field and keep the
+ * resulting hidden checkbox inputs compatible with the price/validation stack.
+ *
+ * @param {PPOMUploadFieldMeta} file_input
+ * @return {void}
+ */
+function ppom_setup_file_upload_input( file_input ) {
+ const file_inputs = file_input;
+ const parts = file_input.data_name.split( '-' );
+ const [ file_data_name, file_id ] = parts;
+ let data_name = file_data_name;
+
+ if ( file_id !== undefined ) {
+ data_name = file_data_name + '-' + file_id;
+ }
+
+ if ( plupload_instances[ data_name ] !== undefined ) {
+ return;
+ }
+
+ if ( ! Object.prototype.hasOwnProperty.call( field_file_count, file_data_name ) ) {
+ field_file_count[ file_data_name ] = 0;
+ }
+ file_list_preview_containers[ file_data_name ] = jQuery(
+ '#filelist-' + file_data_name
+ );
+
+ // Energy pack
+ const bar = window.document.getElementById(
+ `ppom-progressbar-${ file_data_name }`
+ );
+
+ const ppom_file_data = {
+ action: 'ppom_upload_file',
+ data_name: file_data_name,
+ ppom_nonce: ppom_file_vars.ppom_file_upload_nonce,
+ product_id: ppom_file_vars.product_id,
+ };
+
+ let img_dim_errormsg = 'Please upload correct image dimension';
+ if ( file_input.img_dimension_error ) {
+ img_dim_errormsg = file_input.img_dimension_error;
+ }
+
+ plupload_instances[ file_data_name ] = new plupload.Uploader( {
+ runtimes: ppom_file_vars.plupload_runtime,
+ browse_button: 'selectfiles-' + data_name, // you can pass in id...
+ container: 'ppom-file-container-' + file_data_name, // ... or DOM Element itself
+ drop_element: 'ppom-file-container-' + file_data_name,
+ url: ppom_file_vars.ajaxurl,
+ multipart_params: ppom_file_data,
+ max_file_size: file_input.file_size,
+ max_file_count: parseInt( file_input.files_allowed ),
+ unique_names: ppom_file_vars.enable_file_rename,
+ chunk_size: '2mb',
+ unique_names: false,
+
+ filters: {
+ mime_types: [
+ { title: 'Filetypes', extensions: file_input.file_types },
+ ],
+ },
+
+ init: {
+ PostInit() {
+ // file_list_preview_containers[file_data_name].html('');
+ if (
+ ! file_list_preview_containers[ file_data_name ].is(
+ ':visible'
+ )
+ ) {
+ jQuery( document ).on( 'ppom_field_shown', function () {
+ jQuery.each(
+ ppom_input_vars.ppom_inputs,
+ function ( index, file_input ) {
+ if (
+ file_input &&
+ ( file_input.type === 'file' ||
+ file_input.type === 'cropper' )
+ ) {
+ if (
+ file_input.data_name &&
+ file_input.files_allowed &&
+ file_input.file_size &&
+ file_input.files_allowed
+ ) {
+ ppom_setup_file_upload_input(
+ file_input
+ );
+ }
+ }
+ }
+ );
+ } );
+ }
+ /*$('#uploadfiles-'+file_data_name).bind('click', function() {
upload_instance[file_data_name].start();
return false;
});*/
- },
-
- FilesAdded: function(up, files) {
-
- // Adding progress bar
- const file_pb = jQuery('')
- .addClass('progress')
- .css('width', '100%')
- .css('clear', 'both')
- .css('margin', '5px auto')
- .appendTo(file_list_preview_containers[file_data_name]);
- const file_pb_runner = jQuery('')
- .addClass('progress-bar')
- .attr('role', 'progressbar')
- .attr('aria-valuenow', 0)
- .attr('aria-valuemin', 0)
- .attr('aria-valuemax', 100)
- .css('height', '15px')
- .css('width', 0)
- .appendTo(file_pb);
-
- const files_added = files.length;
- // return;
-
- // console.log('image w bac', files);
- // plupload.each(files, function(file, i) {
- // var img = new mOxie.Image;
- // img.onload = function() {
- // var img_height = this.height;
- // var img_width = this.width;
- // // if ((img_height >= 1024 || img_height <= 1100) && (img_width >= 750 || img_width <= 800)) {
- // if ((img_width >= parseFloat(file_input.max_img_w) || img_width <= parseFloat(file_input.min_img_w))) {
- // alert("Height and Width must not exceed 1100*800.");
- // return false;
- // }
- // console.log('image h', parseFloat(file_input.max_img_w));
- // // access image size here using this.width and this.height
- // };
- // img.load(file.getSource());
- // });
-
- if ((field_file_count[file_data_name] + files_added) > plupload_instances[file_data_name].settings.max_file_count) {
- alert(plupload_instances[file_data_name].settings.max_file_count + ppom_file_vars.mesage_max_files_limit);
- }
- else {
-
- plupload.each(files, function(file) {
-
- if (file.type.indexOf("image") !== -1 && file.type !== 'image/photoshop') {
- const img = new moxie.image.Image();
- img.load = function() {
-
- const img_height = this.height;
- const img_width = this.width;
-
- let aspect_ratio = Math.max(img_width, img_height) / Math.min(img_width, img_height);
-
- if (img_width >= parseFloat(file_input.max_img_w) || img_width <= parseFloat(file_input.min_img_w)) {
- plupload_instances[file_data_name].stop();
- plupload_instances[file_data_name].removeFile(file);
- alert(img_dim_errormsg);
- }
- else if (img_height >= parseFloat(file_input.max_img_h) || img_height <= parseFloat(file_input.min_img_h)) {
- plupload_instances[file_data_name].stop();
- plupload_instances[file_data_name].removeFile(file);
- alert(img_dim_errormsg);
- }
- else {
- field_file_count[file_data_name]++;
- // Code to add pending file details, if you want
- add_thumb_box(file, file_list_preview_containers[file_data_name], up);
- setTimeout('plupload_instances[\'' + file_data_name + '\'].start()', 100);
- }
- };
- img.load(file.getSource());
- }
- else {
- field_file_count[file_data_name]++;
- // Code to add pending file details, if you want
- add_thumb_box(file, file_list_preview_containers[file_data_name], up);
- setTimeout('plupload_instances[\'' + file_data_name + '\'].start()', 100);
- }
-
-
- // Energy pack
- if ( bar ) {
- bar.removeAttribute('hidden');
- bar.max = file.size;
- bar.value = file.loaded;
- }
- });
- }
-
-
- },
-
- FileUploaded: function(up, file, info) {
-
-
- const obj_resp = jQuery.parseJSON(info.response);
-
- if (obj_resp.file_name === 'ThumbNotFound') {
-
- plupload_instances[file_data_name].removeFile(file.id);
- jQuery("#u_i_c_" + file.id).hide(500).remove();
- field_file_count[file_data_name]--;
-
- alert('There is some error please try again');
- return;
-
- }
- else if (obj_resp.status === 'error') {
-
- plupload_instances[file_data_name].removeFile(file.id);
-
- jQuery("#u_i_c_" + file.id).hide(500).remove();
-
- field_file_count[file_data_name]--;
- alert(obj_resp.message);
- return;
- };
-
- // var img_w = obj_resp.file_w
- // var img_h = obj_resp.file_h
- // if (img_w > parseFloat(file_input.max_img_w)) {
- // upload_instance[file_data_name].removeFile(file.id);
- // jQuery("#u_i_c_" + file.id).hide(500).remove();
- // file_count[file_data_name]--;
- // alert('Image Dimension Error');
- // jQuery('form.cart').unblock();
- // return;
- // }
-
- let file_thumb = '';
-
- /*if( file_input.file_cost != "" ) {
+ },
+
+ FilesAdded( up, files ) {
+ // Adding progress bar
+ const file_pb = jQuery( '' )
+ .addClass( 'progress' )
+ .css( 'width', '100%' )
+ .css( 'clear', 'both' )
+ .css( 'margin', '5px auto' )
+ .appendTo( file_list_preview_containers[ file_data_name ] );
+ const file_pb_runner = jQuery( '' )
+ .addClass( 'progress-bar' )
+ .attr( 'role', 'progressbar' )
+ .attr( 'aria-valuenow', 0 )
+ .attr( 'aria-valuemin', 0 )
+ .attr( 'aria-valuemax', 100 )
+ .css( 'height', '15px' )
+ .css( 'width', 0 )
+ .appendTo( file_pb );
+
+ const files_added = files.length;
+ // return;
+
+ // console.log('image w bac', files);
+ // plupload.each(files, function(file, i) {
+ // var img = new mOxie.Image;
+ // img.onload = function() {
+ // var img_height = this.height;
+ // var img_width = this.width;
+ // // if ((img_height >= 1024 || img_height <= 1100) && (img_width >= 750 || img_width <= 800)) {
+ // if ((img_width >= parseFloat(file_input.max_img_w) || img_width <= parseFloat(file_input.min_img_w))) {
+ // alert("Height and Width must not exceed 1100*800.");
+ // return false;
+ // }
+ // console.log('image h', parseFloat(file_input.max_img_w));
+ // // access image size here using this.width and this.height
+ // };
+ // img.load(file.getSource());
+ // });
+
+ if ( file_id !== undefined ) {
+ --field_file_count[ file_data_name ];
+ }
+
+ if (
+ field_file_count[ file_data_name ] + files_added >
+ plupload_instances[ file_data_name ].settings.max_file_count
+ ) {
+ alert(
+ plupload_instances[ file_data_name ].settings
+ .max_file_count +
+ ppom_file_vars.mesage_max_files_limit
+ );
+ } else {
+ if ( file_id !== undefined ) {
+ jQuery( '.ppom-croppie-preview-' + file_id )
+ .hide( 500 )
+ .remove();
+ jQuery( `.btn.${ file_id }` ).hide( 500 ).remove();
+ jQuery( '#u_i_c_' + file_id ).hide( 500 ).remove();
+ jQuery(
+ `input[name="ppom[fields][${ file_data_name }][${ file_data_name }][cropped]"]`
+ )
+ .hide( 500 )
+ .remove();
+ }
+
+ plupload.each( files, function ( file ) {
+ if (
+ file.type.indexOf( 'image' ) !== -1 &&
+ file.type !== 'image/photoshop'
+ ) {
+ const img = new moxie.image.Image();
+ img.load = function () {
+ const img_height = this.height;
+ const img_width = this.width;
+
+ const aspect_ratio =
+ Math.max( img_width, img_height ) /
+ Math.min( img_width, img_height );
+
+ if (
+ img_width >=
+ parseFloat( file_input.max_img_w ) ||
+ img_width <=
+ parseFloat( file_input.min_img_w ) ||
+ img_height >=
+ parseFloat( file_input.max_img_h ) ||
+ img_height <=
+ parseFloat( file_input.min_img_h )
+ ) {
+ up.removeFile( file );
+ alert( img_dim_errormsg );
+ } else {
+ field_file_count[ file_data_name ]++;
+ // Code to add pending file details, if you want
+ add_thumb_box(
+ file,
+ file_list_preview_containers[
+ file_data_name
+ ],
+ up
+ );
+ up.start();
+ }
+ };
+ img.load( file.getSource() );
+ } else {
+ field_file_count[ file_data_name ]++;
+ // Code to add pending file details, if you want
+ add_thumb_box(
+ file,
+ file_list_preview_containers[ file_data_name ],
+ up
+ );
+ up.start();
+ }
+
+ // Energy pack
+ if ( bar ) {
+ bar.removeAttribute( 'hidden' );
+ bar.max = file.size;
+ bar.value = file.loaded;
+ }
+ } );
+ }
+ },
+
+ FileUploaded( up, file, info ) {
+ const obj_resp = jQuery.parseJSON( info.response );
+
+ if ( obj_resp.file_name === 'ThumbNotFound' ) {
+ plupload_instances[ file_data_name ].removeFile( file.id );
+ jQuery( '#u_i_c_' + file.id )
+ .hide( 500 )
+ .remove();
+ field_file_count[ file_data_name ]--;
+
+ alert( 'There is some error please try again' );
+ return;
+ } else if ( obj_resp.status === 'error' ) {
+ plupload_instances[ file_data_name ].removeFile( file.id );
+
+ jQuery( '#u_i_c_' + file.id )
+ .hide( 500 )
+ .remove();
+
+ field_file_count[ file_data_name ]--;
+ alert( obj_resp.message );
+ return;
+ }
+
+ // var img_w = obj_resp.file_w
+ // var img_h = obj_resp.file_h
+ // if (img_w > parseFloat(file_input.max_img_w)) {
+ // upload_instance[file_data_name].removeFile(file.id);
+ // jQuery("#u_i_c_" + file.id).hide(500).remove();
+ // file_count[file_data_name]--;
+ // alert('Image Dimension Error');
+ // jQuery('form.cart').unblock();
+ // return;
+ // }
+
+ let file_thumb = '';
+
+ /*if( file_input.file_cost != "" ) {
jQuery('input[name="woo_file_cost"]').val( file_input.file_cost );
}*/
- file_list_preview_containers[file_data_name].find('#u_i_c_' + file.id).html(obj_resp.html)
- .trigger({
- type: "ppom_image_ready",
- image: file,
- data_name: file_data_name,
- input_type: file_input.type,
- image_url: obj_resp.file_url,
- image_resp: obj_resp,
- time: new Date()
- });
-
-
- // checking if uploaded file is thumb
- const ext = obj_resp.file_name.substring(obj_resp.file_name.lastIndexOf('.') + 1).toLowerCase();
-
- if (
- ext === 'png' ||
- ext === 'gif' ||
- ext === 'jpg' ||
- ext === 'jpeg'
- ) {
- const file_full = ppom_file_vars.file_upload_path + obj_resp.file_name;
- // thumb thickbox only shown if it is image
- file_list_preview_containers[file_data_name]
- .find('#u_i_c_' + file.id)
- .find('.u_i_c_thumb')
- .append('');
-
- // Aviary editing tools
- if (file_input.photo_editing === 'on' && ppom_file_vars.aviary_api_key !== '') {
- const editing_tools = file_input.editing_tools;
- file_list_preview_containers[file_data_name]
- .find('#u_i_c_' + file.id)
- .find('.u_i_c_tools_edit')
- .append(' ');
- }
- } else {
- file_thumb = ppom_file_vars.plugin_url + '/images/file.png';
- file_list_preview_containers[file_data_name].find('#u_i_c_' + file.id)
- .find('.u_i_c_thumb')
- .html(' ')
- }
-
- // adding checkbox input to Hold uploaded file name as array
- const file_container = file_list_preview_containers[file_data_name].find('#u_i_c_' + file.id);
- let input_class = 'ppom-input';
- input_class += file_input.required === 'on' ? ' ppom-required' : '';
-
- // Add file check
- jQuery('')
- .attr('data-price', file_input.file_cost)
- .attr('data-label', obj_resp.file_name)
- .attr('data-data_name', file_input.data_name)
- .attr('data-title', file_input.title)
- .attr('data-onetime', file_input.onetime)
- .val(obj_resp.file_name)
- .css('display', 'none')
- .addClass('ppom-file-cb-' + file_data_name)
- .addClass('ppom-file-cb')
- .addClass(input_class)
- .appendTo(file_container)
- .trigger('change');
-
- ppom_update_option_prices();
-
- jQuery('form.cart').unblock();
- isCartBlock = false;
-
- // Removing progressbar
- file_list_preview_containers[file_data_name].find('.progress').remove();
-
- if ( bar ) {
- setTimeout(function() {
- bar.setAttribute('hidden', 'hidden');
- }, 1000);
- bar.max = file.size;
- bar.value = file.loaded;
- }
-
- // Trigger
- jQuery.event.trigger({
- type: "ppom_file_uploaded",
- file: file,
- file_meta: file_input,
- file_resp: obj_resp,
- time: new Date()
- });
- },
-
- UploadProgress: function(up, file) {
-
- // Energy pack
- if ( bar ) {
- bar.max = file.size;
- bar.value = file.loaded;
- }
-
- file_list_preview_containers[file_data_name].find('.progress-bar').css('width', file.percent + '%');
-
- //disabling add to cart button for a while
- if (!isCartBlock) {
- jQuery('form.cart').block({
- message: null,
- overlayCSS: {
- background: "#fff",
- opacity: .6,
- onBlock: function() {
- isCartBlock = true;
- }
- }
- });
- }
- },
-
- Error: function(up, err) {
- //document.getElementById('console').innerHTML += "\nError #" + err.code + ": " + err.message;
- alert("\nError #" + err.code + ": " + err.message);
- }
- }
-
-
- });
-
- // console.log('running file', upload_instance[file_data_name]);
- plupload_instances[file_data_name].init();
- uploaderInstances[file_data_name] = plupload_instances[file_data_name];
+ file_list_preview_containers[ file_data_name ]
+ .find( '#u_i_c_' + file.id )
+ .html( obj_resp.html )
+ .trigger( {
+ type: 'ppom_image_ready',
+ image: file,
+ data_name: file_data_name,
+ input_type: file_input.type,
+ image_url: obj_resp.file_url,
+ image_resp: obj_resp,
+ file_input: file_inputs,
+ time: new Date(),
+ } );
+
+ // checking if uploaded file is thumb
+ const ext = obj_resp.file_name
+ .substring( obj_resp.file_name.lastIndexOf( '.' ) + 1 )
+ .toLowerCase();
+
+ if (
+ ext === 'png' ||
+ ext === 'gif' ||
+ ext === 'jpg' ||
+ ext === 'jpeg'
+ ) {
+ const file_full =
+ ppom_file_vars.file_upload_path + obj_resp.file_name;
+ // thumb thickbox only shown if it is image
+ file_list_preview_containers[ file_data_name ]
+ .find( '#u_i_c_' + file.id )
+ .find( '.u_i_c_thumb' )
+ .append(
+ ''
+ );
+
+ // Aviary editing tools
+ if (
+ file_input.photo_editing === 'on' &&
+ ppom_file_vars.aviary_api_key !== ''
+ ) {
+ const editing_tools = file_input.editing_tools;
+ file_list_preview_containers[ file_data_name ]
+ .find( '#u_i_c_' + file.id )
+ .find( '.u_i_c_tools_edit' )
+ .append(
+ ' '
+ );
+ }
+ } else {
+ file_thumb = ppom_file_vars.plugin_url + '/images/file.png';
+ file_list_preview_containers[ file_data_name ]
+ .find( '#u_i_c_' + file.id )
+ .find( '.u_i_c_thumb' )
+ .html(
+ ' '
+ );
+ }
+
+ // adding checkbox input to Hold uploaded file name as array
+ const file_container = file_list_preview_containers[
+ file_data_name
+ ].find( '#u_i_c_' + file.id );
+ let input_class = 'ppom-input';
+ input_class +=
+ file_input.required === 'on' ? ' ppom-required' : '';
+
+ // Add file check
+ jQuery(
+ ''
+ )
+ .attr( 'data-price', file_input.file_cost )
+ .attr( 'data-label', obj_resp.file_name )
+ .attr( 'data-data_name', file_input.data_name )
+ .attr( 'data-title', file_input.title )
+ .attr( 'data-onetime', file_input.onetime )
+ .val( obj_resp.file_name )
+ .css( 'display', 'none' )
+ .addClass( 'ppom-file-cb-' + file_data_name )
+ .addClass( 'ppom-file-cb' )
+ .addClass( input_class )
+ .appendTo( file_container )
+ .trigger( 'change' );
+
+ ppom_update_option_prices();
+
+ jQuery( 'form.cart' ).unblock();
+ isCartBlock = false;
+
+ // Removing progressbar
+ file_list_preview_containers[ file_data_name ]
+ .find( '.progress' )
+ .remove();
+
+ if ( bar ) {
+ setTimeout( function () {
+ bar.setAttribute( 'hidden', 'hidden' );
+ }, 1000 );
+ bar.max = file.size;
+ bar.value = file.loaded;
+ }
+
+ // Trigger
+ jQuery.event.trigger( {
+ type: 'ppom_file_uploaded',
+ file,
+ file_meta: file_input,
+ file_resp: obj_resp,
+ time: new Date(),
+ } );
+ },
+
+ UploadProgress( up, file ) {
+ // Energy pack
+ if ( bar ) {
+ bar.max = file.size;
+ bar.value = file.loaded;
+ }
+
+ file_list_preview_containers[ file_data_name ]
+ .find( '.progress-bar' )
+ .css( 'width', file.percent + '%' );
+
+ //disabling add to cart button for a while
+ if ( ! isCartBlock ) {
+ jQuery( 'form.cart' ).block( {
+ message: null,
+ overlayCSS: {
+ background: '#fff',
+ opacity: 0.6,
+ onBlock() {
+ isCartBlock = true;
+ },
+ },
+ } );
+ }
+ },
+
+ Error( up, err ) {
+ if ( -600 === err.code && file_input.file_size ) {
+ alert( ppom_file_vars.max_file_size.replace( '%s', file_input.file_size.toUpperCase() ) );
+ } else if ( -601 === err.code && file_input.file_types ) {
+ alert( ppom_file_vars.invalid_file_type);
+ } else if ( -602 === err.code ) {
+ alert( ppom_file_vars.duplicate_file );
+ } else {
+ alert( 'Error #' + err.code + ': ' + err.message );
+ }
+ },
+ },
+ } );
+
+ // console.log('running file', upload_instance[file_data_name]);
+ plupload_instances[ file_data_name ].init();
+ uploaderInstances[ file_data_name ] = plupload_instances[ file_data_name ];
+}
+
+// Persist the Croppie canvas output into hidden inputs so PHP can rebuild the
+// edited image from the same request payload used for normal uploaded files.
+function ppom_generate_cropper_data_for_cart( field_name ) {
+ const cropp_preview_container = jQuery(
+ '.ppom-croppie-wrapper-' + field_name
+ );
+
+ cropp_preview_container
+ .find( '.croppie-container' )
+ .each( function ( i, croppie_dom ) {
+ const image_id = jQuery( croppie_dom ).attr( 'data-image_id' );
+ jQuery( croppie_dom )
+ .croppie( 'result', {
+ type: 'rawcanvas',
+ // size: { width: 300, height: 300 },
+ size: 'original',
+ format: 'png',
+ } )
+ .then( function ( canvas ) {
+ const image_url = canvas.toDataURL();
+ //console.log(image_url);
+ // remove first
+ jQuery(
+ `input[name="ppom[fields][${ field_name }][${ image_id }][cropped]"]`
+ ).remove();
+
+ // Add file check
+ jQuery(
+ ''
+ )
+ .val( image_url )
+ .css( 'display', 'none' )
+ .appendTo( file_list_preview_containers[ field_name ] );
+ } );
+ } );
}
-// Generate Cropped image data for cart
-function ppom_generate_cropper_data_for_cart(field_name) {
-
- const cropp_preview_container = jQuery(".ppom-croppie-wrapper-" + field_name);
-
- cropp_preview_container.find('.croppie-container').each(function(i, croppie_dom) {
-
- const image_id = jQuery(croppie_dom).attr('data-image_id');
- jQuery(croppie_dom).croppie('result', {
- type: 'rawcanvas',
- // size: { width: 300, height: 300 },
- size: 'original',
- format: 'png'
- }).then(function(canvas) {
- const image_url = canvas.toDataURL();
- //console.log(image_url);
- // remove first
- jQuery(`input[name="ppom[fields][${field_name}][${image_id}][cropped]"`).remove();
-
- // Add file check
- jQuery('')
- .val(image_url)
- .css('display', 'none')
- .appendTo(file_list_preview_containers[field_name]);
-
- });
- });
+/**
+ * Scale Croppie boundary/viewport when the field container is narrower than
+ * configured dimensions (responsive cropper).
+ *
+ * @param {Object} baseOptions Croppie options object for the field.
+ * @param {number} max_width Container width in pixels.
+ * @return {Object} Options safe to pass to .croppie() without mutating globals.
+ */
+function get_responsive_croppie_options( baseOptions, max_width ) {
+ const boundary = baseOptions.boundary || {};
+
+ const boundaryWidthOriginal = Number( boundary.width );
+ const boundaryHeightOriginal = Number( boundary.height );
+ if (
+ ! Number.isFinite( boundaryWidthOriginal ) ||
+ ! Number.isFinite( boundaryHeightOriginal ) ||
+ boundaryWidthOriginal <= 0 ||
+ boundaryHeightOriginal <= 0
+ ) {
+ return baseOptions;
+ }
+ if ( max_width >= boundaryWidthOriginal ) {
+ return baseOptions;
+ }
+ if ( ! Number.isFinite( max_width ) ) {
+ return baseOptions;
+ }
+ const aspectRatio = boundaryHeightOriginal / boundaryWidthOriginal || 1;
+ const boundaryWidth = Math.floor( max_width );
+ const boundaryHeight = Math.floor( boundaryWidth * aspectRatio );
+
+ const result = {
+ ...baseOptions,
+ boundary: {
+ ...baseOptions.boundary,
+ width: boundaryWidth,
+ height: boundaryHeight,
+ },
+ };
+
+ if (
+ baseOptions.viewport &&
+ baseOptions.viewport.width &&
+ baseOptions.viewport.height
+ ) {
+ const scale = boundaryWidth / boundaryWidthOriginal;
+ const viewportWidth = Math.floor( baseOptions.viewport.width * scale );
+ const viewportHeight = Math.floor( baseOptions.viewport.height * scale );
+
+ result.viewport = {
+ ...baseOptions.viewport,
+ width: Math.min( viewportWidth, boundaryWidth - 2 ),
+ height: Math.min( viewportHeight, boundaryHeight - 2 ),
+ };
+ }
+
+ return result;
}
diff --git a/js/image-tooltip.js b/js/image-tooltip.js
index 19b30542..623f0865 100644
--- a/js/image-tooltip.js
+++ b/js/image-tooltip.js
@@ -1,67 +1,91 @@
-(function ($) {
- $.fn.imageTooltip = function (options) {
+/**
+ * Lightweight hover preview used by PPOM image inputs.
+ *
+ * Product-page image choices can carry a larger image URL in
+ * `data-image-tooltip`; this plugin renders that preview near the cursor
+ * without opening the full media modal flow.
+ */
+( function ( $ ) {
+ $.fn.imageTooltip = function ( options ) {
+ const defaults = {
+ imgWidth: 'initial',
+ backgroundColor: '#fff',
+ };
- var defaults = {
- imgWidth: 'initial',
- backgroundColor: '#fff'
- };
+ if ( typeof options === 'object' ) {
+ options = $.extend( defaults, options );
+ } else {
+ const tempOptions = {};
+ tempOptions.imgWidth = arguments[ 0 ] || defaults.imgWidth;
+ tempOptions.backgroundColor =
+ arguments[ 1 ] || defaults.backgroundColor;
+ options = tempOptions;
+ }
- if (typeof (options) === 'object') {
- options = $.extend(defaults, options);
- } else {
- var tempOptions = {};
- tempOptions.imgWidth = arguments[0] || defaults.imgWidth;
- tempOptions.backgroundColor = arguments[1] || defaults.backgroundColor;
- options = tempOptions;
- }
+ function calLeft( x, imgWidth ) {
+ return window.innerWidth - x > imgWidth ? x : x - imgWidth;
+ }
- function calLeft(x, imgWidth) {
- return window.innerWidth - x > imgWidth ? x : x - imgWidth;
- }
+ function calTop( y, imgHeight ) {
+ return window.innerHeight - y > imgHeight
+ ? y + 25
+ : y - imgHeight - 25;
+ }
- function calTop(y, imgHeight) {
- return window.innerHeight - y > imgHeight ? y + 25 : y - imgHeight - 25;
- }
+ return this.each( function () {
+ const imgContainer = $( '', {
+ css: {
+ display: 'none',
+ backgroundColor: options.backgroundColor,
+ padding: '5px',
+ position: 'fixed',
+ 'max-width': '350px',
+ 'z-index': '9999',
+ },
+ } );
- return this.each(function () {
+ const img = $( ' ', {
+ src:
+ $( this ).data( 'image-tooltip' ) ||
+ $( this ).attr( 'src' ),
+ alt: 'Image Not Available',
+ width: options.imgWidth,
+ } );
- var imgContainer = $(' ', {
- css: {
- display: 'none',
- backgroundColor: options.backgroundColor,
- padding: '5px',
- position: 'fixed',
- 'max-width': '350px',
- 'z-index': '9999'
- }
- });
+ imgContainer.append( img );
- var img = $(' ', {
- src: $(this).data('image-tooltip') || $(this).attr('src'),
- alt: 'Image Not Available',
- width: options.imgWidth
- });
-
- imgContainer.append(img);
-
- $(this).hover(
- function (e) {
- imgContainer.css({
- left: calLeft(e.clientX, imgContainer.outerWidth()) + 'px',
- top: calTop(e.clientY, imgContainer.outerHeight()) + 'px'
- });
- $('body').append(imgContainer);
- imgContainer.fadeIn('fast');
- },
- function () {
- imgContainer.remove();
- }
- ).mousemove(function (e) {
- imgContainer.css({
- left: calLeft(e.clientX, imgContainer.outerWidth()) + 'px',
- top: calTop(e.clientY, imgContainer.outerHeight()) + 'px'
- });
- });
- });
- };
-}(jQuery));
\ No newline at end of file
+ $( this )
+ .hover(
+ function ( e ) {
+ imgContainer.css( {
+ left:
+ calLeft(
+ e.clientX,
+ imgContainer.outerWidth()
+ ) + 'px',
+ top:
+ calTop(
+ e.clientY,
+ imgContainer.outerHeight()
+ ) + 'px',
+ } );
+ $( 'body' ).append( imgContainer );
+ imgContainer.fadeIn( 'fast' );
+ },
+ function () {
+ imgContainer.remove();
+ }
+ )
+ .mousemove( function ( e ) {
+ imgContainer.css( {
+ left:
+ calLeft( e.clientX, imgContainer.outerWidth() ) +
+ 'px',
+ top:
+ calTop( e.clientY, imgContainer.outerHeight() ) +
+ 'px',
+ } );
+ } );
+ } );
+ };
+} )( jQuery );
diff --git a/js/popup.js b/js/popup.js
index b778cbae..4e977933 100644
--- a/js/popup.js
+++ b/js/popup.js
@@ -1,102 +1,140 @@
// @ts-check
+/**
+ * Shared confirmation/error popup used by PPOM admin screens.
+ *
+ * Unlike the field-builder inline modals, this is a single programmatic popup
+ * instance exposed on `window.ppomPopup` so list-table and settings actions can
+ * reuse the same confirmation UX.
+ *
+ * @see js/admin/ppom-meta-table.js
+ * @see js/admin/ppom-admin.js
+ */
+
+/**
+ * @typedef {{
+ * title?: string,
+ * text?: string,
+ * hideCloseBtn?: boolean,
+ * type?: string,
+ * onConfirmation?: () => void,
+ * onClose?: () => void
+ * }} PpomPopupOptions
+ */
class PpomPopup {
- constructor() {
- this.overlay = document.createElement('div');
- this.overlay.classList.add('ppom-popup-overlay');
-
- // Close on outside click.
- this.overlay.addEventListener('click', (event) => {
- if ( event.target === this.overlay ) {
- this.close();
- }
- });
-
- this.popup = document.createElement('div');
- this.popup.classList.add('ppom-popup');
-
- this.container = document.createElement('div');
- this.container.classList.add('ppom-popup-container');
-
- this.title = document.createElement('h2');
- this.title.classList.add('ppom-popup-title');
-
- this.text = document.createElement('p');
- this.text.classList.add('ppom-popup-text');
-
- const containerActions = document.createElement('div');
- containerActions.classList.add('ppom-popup-actions');
-
- this.confirmButton = document.createElement('button');
- this.confirmButton.classList.add('ppom-btn-confirm')
- this.confirmButton.textContent = window.ppom_vars.i18n.popup.confirmationBtn;
- this.confirmButton.addEventListener('click', this.confirm.bind(this));
-
- this.cancelButton = document.createElement('button');
- this.cancelButton.classList.add('ppom-btn-cancel')
- this.cancelButton.textContent = window.ppom_vars.i18n.popup.cancelBtn;
- this.cancelButton.addEventListener('click', this.close.bind(this));
-
- containerActions.appendChild(this.cancelButton);
- containerActions.appendChild(this.confirmButton);
-
- this.container.appendChild(this.title);
- this.container.appendChild(this.text);
- this.container.appendChild(containerActions);
- this.popup.appendChild(this.container);
- this.overlay.appendChild(this.popup);
-
- this.onConfirmation = () => {}
- this.onClose = () => {}
- }
-
- open( options = {} ) {
-
- if ( options?.title ) {
- this.title.innerHTML = options.title;
- }
-
- if ( options?.text ) {
- this.text.innerHTML = options.text;
- }
-
- if ( options?.onConfirmation ) {
- this.onConfirmation = options.onConfirmation;
- }
-
- if ( options?.onClose ) {
- this.onClose = options.onClose;
- }
-
- this.cancelButton.classList.toggle('ppom-hide', Boolean( options?.hideCloseBtn ) );
- this.text.classList.toggle('ppom-hide', Boolean( options?.text?.length ) );
- this.popup.classList.toggle('ppom-error', 'error' === options?.type );
-
- this.show();
- }
-
- close() {
- this.hide();
- this.onClose?.();
- }
-
- confirm() {
- this.hide();
- this.onConfirmation?.();
- }
-
- show() {
- document.body.appendChild(this.overlay);
- }
-
- hide() {
- document.body.removeChild(this.overlay);
- }
+ constructor() {
+ this.overlay = document.createElement( 'div' );
+ this.overlay.classList.add( 'ppom-popup-overlay' );
+
+ // Close on outside click.
+ this.overlay.addEventListener( 'click', ( event ) => {
+ if ( event.target === this.overlay ) {
+ this.close();
+ }
+ } );
+
+ this.popup = document.createElement( 'div' );
+ this.popup.classList.add( 'ppom-popup' );
+
+ this.container = document.createElement( 'div' );
+ this.container.classList.add( 'ppom-popup-container' );
+
+ this.title = document.createElement( 'h2' );
+ this.title.classList.add( 'ppom-popup-title' );
+
+ this.text = document.createElement( 'p' );
+ this.text.classList.add( 'ppom-popup-text' );
+
+ const containerActions = document.createElement( 'div' );
+ containerActions.classList.add( 'ppom-popup-actions' );
+
+ this.confirmButton = document.createElement( 'button' );
+ this.confirmButton.classList.add( 'ppom-btn-confirm' );
+ this.confirmButton.textContent =
+ window.ppom_vars.i18n.popup.confirmationBtn;
+ this.confirmButton.addEventListener(
+ 'click',
+ this.confirm.bind( this )
+ );
+
+ this.cancelButton = document.createElement( 'button' );
+ this.cancelButton.classList.add( 'ppom-btn-cancel' );
+ this.cancelButton.textContent = window.ppom_vars.i18n.popup.cancelBtn;
+ this.cancelButton.addEventListener( 'click', this.close.bind( this ) );
+
+ containerActions.appendChild( this.cancelButton );
+ containerActions.appendChild( this.confirmButton );
+
+ this.container.appendChild( this.title );
+ this.container.appendChild( this.text );
+ this.container.appendChild( containerActions );
+ this.popup.appendChild( this.container );
+ this.overlay.appendChild( this.popup );
+
+ /** @type {() => void} */
+ this.onConfirmation = () => {};
+ /** @type {() => void} */
+ this.onClose = () => {};
+ }
+
+ /**
+ * Open the shared admin popup with optional callbacks and copy.
+ *
+ * @param {PpomPopupOptions} [options]
+ * @return {void}
+ */
+ open( options = {} ) {
+ if ( options?.title ) {
+ this.title.innerHTML = options.title;
+ }
+
+ if ( options?.text ) {
+ this.text.innerHTML = options.text;
+ }
+
+ if ( options?.onConfirmation ) {
+ this.onConfirmation = options.onConfirmation;
+ }
+
+ if ( options?.onClose ) {
+ this.onClose = options.onClose;
+ }
+
+ this.cancelButton.classList.toggle(
+ 'ppom-hide',
+ Boolean( options?.hideCloseBtn )
+ );
+ this.text.classList.toggle(
+ 'ppom-hide',
+ Boolean( options?.text?.length )
+ );
+ this.popup.classList.toggle( 'ppom-error', 'error' === options?.type );
+
+ this.show();
+ }
+
+ close() {
+ this.hide();
+ this.onClose?.();
+ }
+
+ confirm() {
+ this.hide();
+ this.onConfirmation?.();
+ }
+
+ show() {
+ document.body.appendChild( this.overlay );
+ }
+
+ hide() {
+ document.body.removeChild( this.overlay );
+ }
}
-window.addEventListener('DOMContentLoaded', () => {
- /**
- * @type {import('../global.d.ts').Popup}
- */
- window.ppomPopup = new PpomPopup();
-});
\ No newline at end of file
+window.addEventListener( 'DOMContentLoaded', () => {
+ /**
+ * @type {import('../global.d.ts').Popup}
+ */
+ window.ppomPopup = new PpomPopup();
+} );
diff --git a/js/ppom-conditions-v2.js b/js/ppom-conditions-v2.js
index 5dee86ab..611dccbe 100644
--- a/js/ppom-conditions-v2.js
+++ b/js/ppom-conditions-v2.js
@@ -2,601 +2,775 @@
/**
* PPOM Conditional Version 2
- * More Fast and Optimized
- * April, 2020 in LockedDown (CORVID-19)
- * */
-
-var ppom_hidden_fields = [];
-
-jQuery(function($) {
-
- setTimeout(function() {
- $('form.cart').find('select option:selected, input[type="radio"]:checked, input[type="checkbox"]:checked').each(function(i, field) {
-
- if ($(field).closest('div.ppom-field-wrapper').hasClass('ppom-c-hide')) return;
-
- const data_name = $(field).data('data_name');
- ppom_check_conditions(data_name, function(element_dataname, event_type) {
- // console.log(data_name, event_type);
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
- });
-
- $('form.cart').find('div.ppom-c-show').each(function(i, field) {
-
- const data_name = $(field).data('data_name');
- ppom_check_conditions(data_name, function(element_dataname, event_type) {
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
- });
-
- $('form.cart').find('div.ppom-c-hide').each(function(i, field) {
- const data_name = $(field).data('data_name');
- $.event.trigger({
- type: 'ppom_field_hidden',
- field: data_name,
- time: new Date()
- });
- });
-
- }, 100);
-
- // $('form.cart').on('change', 'select, input[type="radio"], input[type="checkbox"]', function(ev) {
-
- function trigger_check_conditions( modifiedElement ) {
- let value = null;
- if (modifiedElement.type === 'radio' || modifiedElement.type === 'checkbox') {
- value = modifiedElement.checked ? modifiedElement.value : null;
- } else {
- value = modifiedElement.value;
- }
-
- const data_name = modifiedElement.dataset?.data_name;
- ppom_check_conditions(data_name, (element_dataname, event_type) => {
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
- }
-
- $(".ppom-wrapper").on('change', 'select, input:radio, input:checkbox, input[type="date"]', function(_e) {
- trigger_check_conditions( this );
- });
-
- $(".ppom-wrapper").on('keyup', 'input:text, input[type="number"], input[type="email"]', function(_e) {
- trigger_check_conditions( this );
- });
-
- $(document).on('ppom_hidden_fields_updated', function(e) {
- ppom_fields_hidden_conditionally();
- });
-
-
- $(document).on('ppom_field_hidden', function(e) {
-
- // console.log(e.field)
-
- var element_type = ppom_get_field_type_by_id(e.field);
- switch (element_type) {
-
- case 'select':
- $('select[name="ppom[fields][' + e.field + ']"]').val('');
- break;
-
- case 'multiple_select':
-
- var selector = $('select[name="ppom[fields][' + e.field + '][]"]');
- var selected_value = selector.val();
- var selected_options = selector.find('option:selected');
-
- jQuery.each(selected_options, function(index, default_selected) {
-
- var option_id = jQuery(default_selected).attr('data-option_id');
- var the_id = 'ppom-multipleselect-' + e.field + '-' + option_id;
-
- $("#" + the_id).remove();
- });
-
- if (selected_value) {
-
- $('select[name="ppom[fields][' + e.field + '][]"]').val(null).trigger("change");
- }
-
- break;
-
- case 'checkbox':
- $('input[name="ppom[fields][' + e.field + '][]"]').prop('checked', false);
- break;
-
- case 'radio':
- $('input[name="ppom[fields][' + e.field + ']"]').prop('checked', false);
- break;
-
- case 'file':
- $('#filelist-' + e.field).find('.u_i_c_box').remove();
- break;
-
- case 'palettes':
- case 'image':
- $('input[name="ppom[fields][' + e.field + '][]"]').prop('checked', false);
- break;
-
- case 'imageselect':
- var the_id = 'ppom-imageselect' + e.field;
- $("#" + the_id).remove();
- break;
-
- case 'quantityoption':
- $('#' + e.field).val('');
- var the_id = 'ppom-quantityoption-rm' + e.field;
- $("#" + the_id).remove();
- break;
-
- case 'pricematrix':
- $(`input[data-dataname="ppom[fields][${e.field}]"]`).removeClass('active');
- break;
-
- case 'quantities':
- $(`input[name^="ppom[fields][${e.field}]"]`).val('');
- break;
-
- case 'fixedprice':
- // if select type is radio
- $('input[name="ppom[fields][' + e.field + ']"]').prop('checked', false);
- // if select type is select
- $('select[name="ppom[fields][' + e.field + ']"]').val('');
- break;
-
-
- default:
- // Reset text/textarea/date/email etc types
- $('#' + e.field).val('');
- break;
- }
+ *
+ * This is the current conditional-logic engine used on the product page. PHP
+ * renders rule metadata into `data-cond-*` attributes; this file evaluates
+ * those rules and emits the shared `ppom_field_hidden` / `ppom_field_shown`
+ * events that pricing, uploads, validation, and default restoration rely on.
+ *
+ * @see populate_conditional_elements in js/admin/ppom-admin.js
+ * @see ppom_fields_hidden_conditionally
+ * @see ppom_update_option_prices in js/price/ppom-price.js
+ */
- $.event.trigger({
- type: "ppom_hidden_fields_updated",
- field: e.field,
- time: new Date()
- });
+/** @type {string[]} */
+let ppom_hidden_fields = [];
- ppom_check_conditions(e.field, function(element_dataname, event_type) {
- // console.log(`${element_dataname} ===> ${event_type}`);
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
- });
+/**
+ * @typedef {Object} PPOMConditionCompareArgs
+ * @property {string|string[]|undefined|null} valueToCompare
+ * @property {string|undefined} selectOptionToCompare
+ * @property {string|undefined} constantValueToCompare
+ * @property {{to: string, from: string}} betweenValueInterval
+ * @property {string} operator
+ */
- /*$(document).on('ppom_field_shown', function(e) {
+jQuery( function ( $ ) {
+ // Replay the initial field state after the product form has rendered so
+ // defaults, preselected options, and conditionally hidden fields line up.
+ setTimeout( function () {
+ $( 'form.cart' )
+ .find(
+ 'select option:selected, input[type="radio"]:checked, input[type="checkbox"]:checked'
+ )
+ .each( function ( i, field ) {
+ if (
+ $( field )
+ .closest( 'div.ppom-field-wrapper' )
+ .hasClass( 'ppom-c-hide' )
+ ) {
+ return;
+ }
+
+ const data_name = $( field ).data( 'data_name' );
+ ppom_check_conditions(
+ data_name,
+ function ( element_dataname, event_type ) {
+ // console.log(data_name, event_type);
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+ } );
+
+ $( 'form.cart' )
+ .find( 'div.ppom-c-show' )
+ .each( function ( i, field ) {
+ const data_name = $( field ).data( 'data_name' );
+ ppom_check_conditions(
+ data_name,
+ function ( element_dataname, event_type ) {
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+ } );
+
+ $( 'form.cart' )
+ .find( 'div.ppom-c-hide' )
+ .each( function ( i, field ) {
+ const data_name = $( field ).data( 'data_name' );
+ $.event.trigger( {
+ type: 'ppom_field_hidden',
+ field: data_name,
+ time: new Date(),
+ } );
+ } );
+ }, 100 );
+
+ // $('form.cart').on('change', 'select, input[type="radio"], input[type="checkbox"]', function(ev) {
+
+ /**
+ * Re-evaluate any condition tree that depends on the changed form control.
+ *
+ * @param {HTMLInputElement|HTMLSelectElement} modifiedElement
+ * @return {void}
+ */
+ function trigger_check_conditions( modifiedElement ) {
+ let value = null;
+ if (
+ modifiedElement.type === 'radio' ||
+ modifiedElement.type === 'checkbox'
+ ) {
+ value = modifiedElement.checked ? modifiedElement.value : null;
+ } else {
+ value = modifiedElement.value;
+ }
+
+ const data_name = modifiedElement.dataset?.data_name;
+ ppom_check_conditions( data_name, ( element_dataname, event_type ) => {
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ } );
+ }
+
+ $( '.ppom-wrapper' ).on(
+ 'change',
+ 'select, input:radio, input:checkbox, input[type="date"]',
+ function ( _e ) {
+ trigger_check_conditions( this );
+ }
+ );
+
+ $( '.ppom-wrapper' ).on(
+ 'keyup',
+ 'input:text, input[type="number"], input[type="email"]',
+ function ( _e ) {
+ trigger_check_conditions( this );
+ }
+ );
+
+ $( document ).on( 'ppom_hidden_fields_updated', function ( e ) {
+ ppom_fields_hidden_conditionally();
+ } );
+
+ $( document ).on( 'ppom_field_hidden', function ( e ) {
+ // console.log(e.field)
+
+ const element_type = ppom_get_field_type_by_id( e.field );
+ switch ( element_type ) {
+ case 'select':
+ $( 'select[name="ppom[fields][' + e.field + ']"]' ).val( '' );
+ break;
+
+ case 'multiple_select':
+ var selector = $(
+ 'select[name="ppom[fields][' + e.field + '][]"]'
+ );
+ var selected_value = selector.val();
+ var selected_options = selector.find( 'option:selected' );
+
+ jQuery.each(
+ selected_options,
+ function ( index, default_selected ) {
+ const option_id =
+ jQuery( default_selected ).attr( 'data-option_id' );
+ const the_id =
+ 'ppom-multipleselect-' + e.field + '-' + option_id;
+
+ $( '#' + the_id ).remove();
+ }
+ );
+
+ if ( selected_value ) {
+ $( 'select[name="ppom[fields][' + e.field + '][]"]' )
+ .val( null )
+ .trigger( 'change' );
+ }
+
+ break;
+
+ case 'checkbox':
+ $( 'input[name="ppom[fields][' + e.field + '][]"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+
+ case 'radio':
+ $( 'input[name="ppom[fields][' + e.field + ']"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+
+ case 'file':
+ $( '#filelist-' + e.field )
+ .find( '.u_i_c_box' )
+ .remove();
+ break;
+
+ case 'palettes':
+ case 'image':
+ $( 'input[name="ppom[fields][' + e.field + '][]"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+
+ case 'imageselect':
+ var the_id = 'ppom-imageselect' + e.field;
+ $( '#' + the_id ).remove();
+ break;
+
+ case 'quantityoption':
+ $( '#' + e.field ).val( '' );
+ var the_id = 'ppom-quantityoption-rm' + e.field;
+ $( '#' + the_id ).remove();
+ break;
+
+ case 'pricematrix':
+ $(
+ `input[data-dataname="ppom[fields][${ e.field }]"]`
+ ).removeClass( 'active' );
+ break;
+
+ case 'quantities':
+ $( `input[name^="ppom[fields][${ e.field }]"]` ).val( '' );
+ break;
+
+ case 'fixedprice':
+ // if select type is radio
+ $( 'input[name="ppom[fields][' + e.field + ']"]' ).prop(
+ 'checked',
+ false
+ );
+ // if select type is select
+ $( 'select[name="ppom[fields][' + e.field + ']"]' ).val( '' );
+ break;
+
+ default:
+ // Reset text/textarea/date/email etc types
+ $( '#' + e.field ).val( '' );
+ break;
+ }
+
+ $.event.trigger( {
+ type: 'ppom_hidden_fields_updated',
+ field: e.field,
+ time: new Date(),
+ } );
+
+ ppom_check_conditions(
+ e.field,
+ function ( element_dataname, event_type ) {
+ // console.log(`${element_dataname} ===> ${event_type}`);
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+ } );
+
+ /*$(document).on('ppom_field_shown', function(e) {
console.log(`shown event ${e.field}`);
ppom_check_conditions(e.field);
});*/
- $(document).on('ppom_field_shown', function(e) {
-
- ppom_fields_hidden_conditionally();
-
- // Set checked/selected again
- ppom_set_default_option(e.field);
-
- ppom_check_conditions(e.field, function(element_dataname, event_type) {
- // console.log(`${element_dataname} ===> ${event_type}`);
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
-
-
- var field_meta = ppom_get_field_meta_by_id(e.field);
-
- // Apply FileAPI to DOM
- // PPOM version 22.0 has issue, commenting it so far by Najeeb April 4, 2021
- // if (field_meta.type === 'file' || field_meta.type === 'cropper') {
- // ppom_setup_file_upload_input(field_meta);
- // }
-
- // Price Matrix
- if (field_meta.type == 'pricematrix') {
- // Resettin
- $(".ppom_pricematrix").removeClass('active');
-
- // Set Active
- var classname = "." + field_meta.data_name;
- // console.log(field_meta.data_name, jQuery(`input[data-dataname="ppom[fields][${field_meta.data_name}]"]`));
- jQuery(`input[data-dataname="ppom[fields][${field_meta.data_name}]"]`).addClass('active')
- // $(classname).find('.ppom_pricematrix').addClass('active')
- }
-
- //Imageselect (Image dropdown)
- if (field_meta.type === 'imageselect') {
-
- var dd_selector = 'ppom_imageselect_' + field_meta.data_name;
- var ddData = $('#' + dd_selector).data('ppom_ddslick');
- var image_replace = field_meta.image_replace ? field_meta.image_replace : 'off';
-
- ppom_create_hidden_input(ddData);
- ppom_update_option_prices();
- setTimeout(function() {
- ppom_image_selection(ddData, image_replace);
- }, 100);
- // $('#'+dd_selector).ddslick('select', {index: 0 });
- }
-
-
- // Multiple Select Addon
- if (field_meta.type === 'multiple_select') {
-
- var selector = jQuery('select[name="ppom[fields][' + field_meta.data_name + '][]"]');
- var selected_value = selector.val();
- var default_value = field_meta.selected;
-
- if (selected_value === null && default_value) {
-
- var selected_opt_arr = default_value.split(',');
-
- selector.val(selected_opt_arr).trigger('change');
-
- var selected_options = selector.find('option:selected');
- jQuery.each(selected_options, function(index, default_selected) {
-
- var option_id = jQuery(default_selected).attr('data-option_id');
- var option_label = jQuery(default_selected).attr('data-optionlabel');
- var option_price = jQuery(default_selected).attr('data-optionprice');
-
- ppom_multiple_select_create_hidden_input(field_meta.data_name, option_id, option_price, option_label, field_meta.title);
- });
- }
- }
-
- });
-
- ppom_fields_hidden_conditionally();
-
-});
-
-function ppom_check_conditions(data_name, callback) {
-
- let is_matched = false;
- let event_type, element_data_name;
-
- jQuery(`div.ppom-cond-${data_name}`).each(function() {
- // return this.data('cond-val1').match(/\w*-Back/);
- // console.log(jQuery(this));
- const total_cond = parseInt(jQuery(this).data('cond-total'));
- const binding = jQuery(this).data(`cond-bind`);
- const visibility = jQuery(this).data(`cond-visibility`);
- element_data_name = jQuery(this).data('data_name');
-
- let matched = 0;
- var matched_conditions = [];
- let cond_elements = [];
- for (var t = 1; t <= total_cond; t++) {
- const targetFieldToCompare = jQuery(this).data(`cond-input${t}`)?.toString()?.toLowerCase()
- const targetFieldValue = ppom_get_element_value(targetFieldToCompare);
-
- const selectOptionValue = jQuery(this).data(`cond-val${t}`)?.toString();
- const operator = jQuery(this).data(`cond-operator${t}`);
- const constantValue = jQuery(this).data(`cond-constant-val-${t}`)?.toString();
- const betweenValueTo = jQuery(this).data(`cond-between-to-${t}`);
- const betweenValueFrom = jQuery(this).data(`cond-between-from-${t}`);
-
-
- is_matched = ppom_compare_values({
- valueToCompare: targetFieldValue,
- selectOptionToCompare: selectOptionValue,
- constantValueToCompare: constantValue,
- betweenValueInterval: {
- from: betweenValueFrom,
- to: betweenValueTo
- },
- operator
- });
-
- if ( is_matched ) {
- matched = ++matched;
- cond_elements.push(targetFieldToCompare);
- }
-
- matched_conditions[element_data_name] = matched;
-
- event_type = visibility === 'hide' ? 'ppom_field_hidden' : 'ppom_field_shown';
- // console.log(`${t} ***** ${element_data_name} total_cond ${total_cond} == matched ${matched} ==> ${matched_conditions[element_data_name]} ==> visibility ${event_type}`);
-
- if ( (matched_conditions[element_data_name] > 0 && binding === 'Any') ||
- (matched_conditions[element_data_name] == total_cond && binding === 'All')
- ) {
-
- // remove/add locked classes for all dependent fields
- cond_elements.forEach(cond_dataname => {
- if( visibility === 'hide' ){
- jQuery(this).addClass(`ppom-locked-${cond_dataname} ppom-c-hide`).removeClass('ppom-c-show');
- }else{
- jQuery(this).removeClass(`ppom-locked-${cond_dataname} ppom-c-hide`);
- }
- });
-
- if ( typeof callback == "function" ) {
- callback(element_data_name, event_type);
- }
- }
- else if ( ! is_matched || matched_conditions[element_data_name] !== total_cond) {
-
- if( visibility === 'hide' ){
- event_type = 'ppom_field_shown';
- jQuery(this).removeClass(`ppom-locked-${data_name} ppom-c-hide`);
- }else{
- event_type = 'ppom_field_hidden';
- jQuery(this).addClass(`ppom-locked-${data_name} ppom-c-hide`);
- }
-
- if ( typeof callback == "function" )
- callback(element_data_name, event_type);
- } else {
-
- jQuery(this).removeClass(`ppom-locked-${data_name} ppom-c-hide`);
- // console.log('event_type', event_type);
-
- if ( typeof callback == "function" )
- callback(element_data_name, event_type);
- }
- }
- });
+ $( document ).on( 'ppom_field_shown', function ( e ) {
+ ppom_fields_hidden_conditionally();
+
+ // Set checked/selected again
+ ppom_set_default_option( e.field );
+
+ ppom_check_conditions(
+ e.field,
+ function ( element_dataname, event_type ) {
+ // console.log(`${element_dataname} ===> ${event_type}`);
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+
+ const field_meta = ppom_get_field_meta_by_id( e.field );
+
+ // Apply FileAPI to DOM
+ // PPOM version 22.0 has issue, commenting it so far by Najeeb April 4, 2021
+ // if (field_meta.type === 'file' || field_meta.type === 'cropper') {
+ // ppom_setup_file_upload_input(field_meta);
+ // }
+
+ // Price Matrix
+ if ( field_meta.type == 'pricematrix' ) {
+ // Resettin
+ $( '.ppom_pricematrix' ).removeClass( 'active' );
+
+ // Set Active
+ const classname = '.' + field_meta.data_name;
+ // console.log(field_meta.data_name, jQuery(`input[data-dataname="ppom[fields][${field_meta.data_name}]"]`));
+ jQuery(
+ `input[data-dataname="ppom[fields][${ field_meta.data_name }]"]`
+ ).addClass( 'active' );
+ // $(classname).find('.ppom_pricematrix').addClass('active')
+ }
+
+ //Imageselect (Image dropdown)
+ if ( field_meta.type === 'imageselect' ) {
+ const dd_selector = 'ppom_imageselect_' + field_meta.data_name;
+ const ddData = $( '#' + dd_selector ).data( 'ppom_ddslick' );
+ const image_replace = field_meta.image_replace
+ ? field_meta.image_replace
+ : 'off';
+
+ ppom_create_hidden_input( ddData );
+ ppom_update_option_prices();
+ setTimeout( function () {
+ ppom_image_selection( ddData, image_replace );
+ }, 100 );
+ // $('#'+dd_selector).ddslick('select', {index: 0 });
+ }
+
+ // Multiple Select Addon
+ if ( field_meta.type === 'multiple_select' ) {
+ const selector = jQuery(
+ 'select[name="ppom[fields][' + field_meta.data_name + '][]"]'
+ );
+ const selected_value = selector.val();
+ const default_value = field_meta.selected;
+
+ if ( selected_value === null && default_value ) {
+ const selected_opt_arr = default_value.split( ',' );
+
+ selector.val( selected_opt_arr ).trigger( 'change' );
+
+ const selected_options = selector.find( 'option:selected' );
+ jQuery.each(
+ selected_options,
+ function ( index, default_selected ) {
+ const option_id =
+ jQuery( default_selected ).attr( 'data-option_id' );
+ const option_label =
+ jQuery( default_selected ).attr(
+ 'data-optionlabel'
+ );
+ const option_price =
+ jQuery( default_selected ).attr(
+ 'data-optionprice'
+ );
+
+ ppom_multiple_select_create_hidden_input(
+ field_meta.data_name,
+ option_id,
+ option_price,
+ option_label,
+ field_meta.title
+ );
+ }
+ );
+ }
+ }
+ } );
+
+ ppom_fields_hidden_conditionally();
+} );
+
+function ppom_check_conditions( data_name, callback ) {
+ // Each `.ppom-cond-*` node describes one target field and its dependencies.
+ // We evaluate all rules for that target, then notify the rest of the stack
+ // through shared PPOM events instead of mutating unrelated features directly.
+ let is_matched = false;
+ let event_type, element_data_name;
+
+ jQuery( `div.ppom-cond-${ data_name }` ).each( function () {
+ // return this.data('cond-val1').match(/\w*-Back/);
+ // console.log(jQuery(this));
+ const total_cond = parseInt( jQuery( this ).data( 'cond-total' ) );
+ const binding = jQuery( this ).data( `cond-bind` );
+ const visibility = jQuery( this ).data( `cond-visibility` );
+ element_data_name = jQuery( this ).data( 'data_name' );
+
+ let matched = 0;
+ const matched_conditions = [];
+ const cond_elements = [];
+ for ( let t = 1; t <= total_cond; t++ ) {
+ const targetFieldToCompare = jQuery( this )
+ .data( `cond-input${ t }` )
+ ?.toString()
+ ?.toLowerCase();
+ const targetFieldValue =
+ ppom_get_element_value( targetFieldToCompare );
+
+ const selectOptionValue = jQuery( this )
+ .data( `cond-val${ t }` )
+ ?.toString();
+ const operator = jQuery( this ).data( `cond-operator${ t }` );
+ const constantValue = jQuery( this )
+ .data( `cond-constant-val-${ t }` )
+ ?.toString();
+ const betweenValueTo = jQuery( this ).data(
+ `cond-between-to-${ t }`
+ );
+ const betweenValueFrom = jQuery( this ).data(
+ `cond-between-from-${ t }`
+ );
+
+ is_matched = ppom_compare_values( {
+ valueToCompare: targetFieldValue,
+ selectOptionToCompare: selectOptionValue,
+ constantValueToCompare: constantValue,
+ betweenValueInterval: {
+ from: betweenValueFrom,
+ to: betweenValueTo,
+ },
+ operator,
+ } );
+
+ if ( is_matched ) {
+ matched = ++matched;
+ cond_elements.push( targetFieldToCompare );
+ }
+
+ matched_conditions[ element_data_name ] = matched;
+
+ event_type =
+ visibility === 'hide'
+ ? 'ppom_field_hidden'
+ : 'ppom_field_shown';
+ // console.log(`${t} ***** ${element_data_name} total_cond ${total_cond} == matched ${matched} ==> ${matched_conditions[element_data_name]} ==> visibility ${event_type}`);
+
+ if (
+ ( matched_conditions[ element_data_name ] > 0 &&
+ binding === 'Any' ) ||
+ ( matched_conditions[ element_data_name ] == total_cond &&
+ binding === 'All' )
+ ) {
+ // remove/add locked classes for all dependent fields
+ cond_elements.forEach( ( cond_dataname ) => {
+ if ( visibility === 'hide' ) {
+ jQuery( this )
+ .addClass(
+ `ppom-locked-${ cond_dataname } ppom-c-hide`
+ )
+ .removeClass( 'ppom-c-show' );
+ } else {
+ jQuery( this ).removeClass(
+ `ppom-locked-${ cond_dataname } ppom-c-hide`
+ );
+ }
+ } );
+
+ if ( typeof callback === 'function' ) {
+ callback( element_data_name, event_type );
+ }
+ } else if (
+ ! is_matched ||
+ matched_conditions[ element_data_name ] !== total_cond
+ ) {
+ if ( visibility === 'hide' ) {
+ event_type = 'ppom_field_shown';
+ jQuery( this ).removeClass(
+ `ppom-locked-${ data_name } ppom-c-hide`
+ );
+ } else {
+ event_type = 'ppom_field_hidden';
+ jQuery( this ).addClass(
+ `ppom-locked-${ data_name } ppom-c-hide`
+ );
+ }
+
+ if ( typeof callback === 'function' ) {
+ callback( element_data_name, event_type );
+ }
+ } else {
+ jQuery( this ).removeClass(
+ `ppom-locked-${ data_name } ppom-c-hide`
+ );
+ // console.log('event_type', event_type);
+
+ if ( typeof callback === 'function' ) {
+ callback( element_data_name, event_type );
+ }
+ }
+ }
+ } );
}
-function ppom_get_input_dom_type(data_name) {
- // const field_obj = jQuery(`input[name="ppom[fields][${data_name}]"], input[name="ppom[fields][${data_name}[]]"], select[name="ppom[fields][${data_name}]"]`);
- const field_obj = jQuery(`.ppom-input[data-data_name="${data_name}"]`);
- return field_obj.closest('.ppom-field-wrapper').data('type');
+function ppom_get_input_dom_type( data_name ) {
+ // const field_obj = jQuery(`input[name="ppom[fields][${data_name}]"], input[name="ppom[fields][${data_name}[]]"], select[name="ppom[fields][${data_name}]"]`);
+ const field_obj = jQuery( `.ppom-input[data-data_name="${ data_name }"]` );
+ return field_obj.closest( '.ppom-field-wrapper' ).data( 'type' );
}
-function ppom_get_element_value(data_name) {
-
- const ppom_type = ppom_get_input_dom_type(data_name);
- let element_value = '';
- var value_found_cb = [];
-
- switch (ppom_type) {
- case 'switcher':
- case 'radio':
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]:checked`).val();
- break;
- case 'palettes':
- case 'checkbox':
- jQuery('input[name="ppom[fields][' + data_name + '][]"]:checked').each(function(i) {
- value_found_cb[i] = jQuery(this).val();
- });
- break;
- case 'image':
- case 'conditional_meta':
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]:checked`).data('label');
- break;
- case 'imageselect':
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]:checked`).data('label');
- break;
- case 'fixedprice':
- var render_type = jQuery(`.ppom-input-${data_name}`).attr('data-input');
- if( render_type == 'radio' ){
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]:checked`).val();
- }else{
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]`).val();
- }
- break;
-
- default:
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]`).val();
- }
-
- if (ppom_type === 'checkbox' || ppom_type === 'palettes') {
- // console.log(value_found_cb);
- return value_found_cb;
- }
-
- return element_value;
+// Normalize values across PPOM field types so condition operators can stay
+// unaware of the exact DOM structure used by each input renderer.
+function ppom_get_element_value( data_name ) {
+ const ppom_type = ppom_get_input_dom_type( data_name );
+ let element_value = '';
+ const value_found_cb = [];
+
+ switch ( ppom_type ) {
+ case 'switcher':
+ case 'radio':
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]:checked`
+ ).val();
+ break;
+ case 'palettes':
+ case 'checkbox':
+ jQuery(
+ 'input[name="ppom[fields][' + data_name + '][]"]:checked'
+ ).each( function ( i ) {
+ value_found_cb[ i ] = jQuery( this ).val();
+ } );
+ break;
+ case 'image':
+ case 'conditional_meta':
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]:checked`
+ ).data( 'label' );
+ break;
+ case 'imageselect':
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]:checked`
+ ).data( 'label' );
+ break;
+ case 'fixedprice':
+ var render_type = jQuery( `.ppom-input-${ data_name }` ).attr(
+ 'data-input'
+ );
+ if ( render_type == 'radio' ) {
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]:checked`
+ ).val();
+ } else {
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]`
+ ).val();
+ }
+ break;
+
+ default:
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]`
+ ).val();
+ }
+
+ if ( ppom_type === 'checkbox' || ppom_type === 'palettes' ) {
+ // console.log(value_found_cb);
+ return value_found_cb;
+ }
+
+ return element_value;
}
/**
* Compares values based on the provided operator.
*
- * @param {Object} args - The arguments object containing comparison parameters.
- * @param {string} args.valueToCompare - The target value to compare.
- * @param {string} args.selectOptionToCompare - The select option value to compare.
- * @param {string} args.constantValueToCompare - The constant value to compare.
- * @param {{to: string, from: string}} args.betweenValueInterval - The between interval.
- * @param {string} args.operator - The operator used for comparison.
- * @returns {boolean} - The result of the comparison.
+ * @param {PPOMConditionCompareArgs} args - Comparison parameters taken from a
+ * rendered condition rule and the current state of its target field.
+ * @return {boolean} - The result of the comparison.
*/
function ppom_compare_values( args ) {
- const { valueToCompare, selectOptionToCompare, constantValueToCompare, operator, betweenValueInterval } = args;
- let result = false;
- switch (operator) {
- case 'is':
- if ( Array.isArray(valueToCompare) ) {
- result = valueToCompare.includes(selectOptionToCompare);;
- } else {
- result = valueToCompare === selectOptionToCompare;
- if ( !selectOptionToCompare && constantValueToCompare ) {
- result = valueToCompare === constantValueToCompare
- }
- }
- break;
-
- case 'not':
- if ( Array.isArray(valueToCompare) ) {
- result = !valueToCompare.includes(selectOptionToCompare);;
- } else {
- result = valueToCompare !== selectOptionToCompare;
- if ( !selectOptionToCompare && constantValueToCompare ) {
- result = valueToCompare !== constantValueToCompare
- }
- }
- break;
-
- case 'greater than':
- result = parseFloat(valueToCompare) > parseFloat(selectOptionToCompare);
- if ( !selectOptionToCompare && constantValueToCompare ) {
- result = parseFloat(valueToCompare) > parseFloat(constantValueToCompare)
- }
- break;
-
- case 'less than':
- result = parseFloat(valueToCompare) < parseFloat(selectOptionToCompare);
- if ( !selectOptionToCompare && constantValueToCompare ) {
- result = parseFloat(valueToCompare) < parseFloat(constantValueToCompare)
- }
- break;
-
- case 'any':
- result = valueToCompare !== undefined && valueToCompare !== null && valueToCompare !== '';
- break;
-
- case 'empty':
- result = valueToCompare === undefined || valueToCompare === null || valueToCompare === '';
- break;
-
- case 'between':
- result = (
- parseFloat(valueToCompare) >= parseFloat( betweenValueInterval.from ) &&
- parseFloat(valueToCompare) <= parseFloat( betweenValueInterval.to )
- );
- break;
-
- case 'number-multiplier':
- result = parseFloat(valueToCompare) % parseFloat(constantValueToCompare) === 0;
- break;
-
- case 'even-number':
- result = parseFloat(valueToCompare) % 2 === 0;
- break;
-
- case 'odd-number':
- result = parseFloat(valueToCompare) % 2 !== 0;
- break;
-
- case 'contains':
- result = valueToCompare?.includes(constantValueToCompare);
- break;
-
- case 'not contains':
- result = !valueToCompare?.includes(constantValueToCompare);
- break;
-
- case 'regex':
- if ( typeof constantValueToCompare === 'string' ) {
- const [_, pattern, flags] = constantValueToCompare.split('/');
- const regex = new RegExp(pattern || constantValueToCompare, flags);
- result = regex.test(valueToCompare);
- }
- break;
-
- default:
- // code
- }
-
- // console.log(`matching ${v1} ${operator} ${v2}`);
- return result;
+ const {
+ valueToCompare,
+ selectOptionToCompare,
+ constantValueToCompare,
+ operator,
+ betweenValueInterval,
+ } = args;
+ let result = false;
+ switch ( operator ) {
+ case 'is':
+ if ( Array.isArray( valueToCompare ) ) {
+ result = valueToCompare.includes( selectOptionToCompare );
+ } else {
+ result = valueToCompare === selectOptionToCompare;
+ if ( ! selectOptionToCompare && constantValueToCompare ) {
+ result = valueToCompare === constantValueToCompare;
+ }
+ }
+ break;
+
+ case 'not':
+ if ( Array.isArray( valueToCompare ) ) {
+ result = ! valueToCompare.includes( selectOptionToCompare );
+ } else {
+ result = valueToCompare !== selectOptionToCompare;
+ if ( ! selectOptionToCompare && constantValueToCompare ) {
+ result = valueToCompare !== constantValueToCompare;
+ }
+ }
+ break;
+
+ case 'greater than':
+ result =
+ parseFloat( valueToCompare ) >
+ parseFloat( selectOptionToCompare );
+ if ( ! selectOptionToCompare && constantValueToCompare ) {
+ result =
+ parseFloat( valueToCompare ) >
+ parseFloat( constantValueToCompare );
+ }
+ break;
+
+ case 'less than':
+ result =
+ parseFloat( valueToCompare ) <
+ parseFloat( selectOptionToCompare );
+ if ( ! selectOptionToCompare && constantValueToCompare ) {
+ result =
+ parseFloat( valueToCompare ) <
+ parseFloat( constantValueToCompare );
+ }
+ break;
+
+ case 'any':
+ result =
+ valueToCompare !== undefined &&
+ valueToCompare !== null &&
+ valueToCompare !== '';
+ break;
+
+ case 'empty':
+ result =
+ valueToCompare === undefined ||
+ valueToCompare === null ||
+ valueToCompare === '';
+ break;
+
+ case 'between':
+ result =
+ parseFloat( valueToCompare ) >=
+ parseFloat( betweenValueInterval.from ) &&
+ parseFloat( valueToCompare ) <=
+ parseFloat( betweenValueInterval.to );
+ break;
+
+ case 'number-multiplier':
+ result =
+ parseFloat( valueToCompare ) %
+ parseFloat( constantValueToCompare ) ===
+ 0;
+ break;
+
+ case 'even-number':
+ result = parseFloat( valueToCompare ) % 2 === 0;
+ break;
+
+ case 'odd-number':
+ result = parseFloat( valueToCompare ) % 2 !== 0;
+ break;
+
+ case 'contains':
+ result = valueToCompare?.includes( constantValueToCompare );
+ break;
+
+ case 'not contains':
+ result = ! valueToCompare?.includes( constantValueToCompare );
+ break;
+
+ case 'regex':
+ if ( typeof constantValueToCompare === 'string' ) {
+ const [ _, pattern, flags ] =
+ constantValueToCompare.split( '/' );
+ const regex = new RegExp(
+ pattern || constantValueToCompare,
+ flags
+ );
+ result = regex.test( valueToCompare );
+ }
+ break;
+
+ default:
+ // code
+ }
+
+ // console.log(`matching ${v1} ${operator} ${v2}`);
+ return result;
}
-function ppom_set_default_option(field_id) {
-
- // get product id
- var product_id = ppom_input_vars.product_id;
-
- var field = ppom_get_field_meta_by_id(field_id);
-
- switch (field.type) {
-
- // Check if field is
- case 'switcher':
- case 'radio':
- jQuery.each(field.options, function(label, options) {
- var opt_id = product_id + '-' + field.data_name + '-' + options.id;
- // console.log('optio nid ', opt_id);
-
- if (options.option == field.selected) {
- jQuery("#" + opt_id).prop('checked', true).trigger('change');
- }
- });
- break;
-
- case 'select':
- if ( '' === jQuery("#" + field.data_name).val() ) {
- jQuery("#" + field.data_name).val(field.selected);
- }
- break;
-
- case 'image':
- jQuery.each(field.images, function(index, img) {
-
- if (img.title == field.selected) {
- jQuery("#" + field.data_name + '-' + img.id).prop('checked', true);
- }
- });
- break;
-
- case 'checkbox':
- jQuery.each(field.options, function(label, options) {
- var opt_id = product_id + '-' + field.data_name + '-' + options.id;
-
- var default_checked = field.checked.split('\r\n');
- if (jQuery.inArray(options.option, default_checked) > -1) {
- jQuery("#" + opt_id).prop('checked', true);
-
- }
- });
- break;
-
- case 'quantities':
- jQuery.each(field.options, function(label, options) {
- //console.log(options);
- if( options.default === '' ) return;
- var opt_id = product_id + '-' + field.data_name + '-' + options.id;
- jQuery("#" + opt_id).val(options.default).trigger('change');
-
- });
- break;
-
- case 'text':
- case 'date':
- case 'number':
- if ( '' === jQuery("#" + field.data_name).val() ) {
- jQuery("#" + field.data_name).val(field.default_value);
- }
- break;
- }
+function ppom_set_default_option( field_id ) {
+ // When a field becomes visible again, restore its default state the same way
+ // the original PHP renderer would have populated it on first page load.
+ // get product id
+ const product_id = ppom_input_vars.product_id;
+
+ const field = ppom_get_field_meta_by_id( field_id );
+
+ switch ( field.type ) {
+ // Check if field is
+ case 'switcher':
+ case 'radio':
+ jQuery.each( field.options, function ( label, options ) {
+ const opt_id =
+ product_id + '-' + field.data_name + '-' + options.id;
+ // console.log('optio nid ', opt_id);
+
+ if ( options.option == field.selected ) {
+ jQuery( '#' + opt_id )
+ .prop( 'checked', true )
+ .trigger( 'change' );
+ }
+ } );
+ break;
+
+ case 'select':
+ if ( '' === jQuery( '#' + field.data_name ).val() ) {
+ jQuery( '#' + field.data_name ).val( field.selected );
+ }
+ break;
+
+ case 'image':
+ jQuery.each( field.images, function ( index, img ) {
+ if ( img.title == field.selected ) {
+ jQuery( '#' + field.data_name + '-' + img.id ).prop(
+ 'checked',
+ true
+ );
+ }
+ } );
+ break;
+
+ case 'checkbox':
+ jQuery.each( field.options, function ( label, options ) {
+ const opt_id =
+ product_id + '-' + field.data_name + '-' + options.id;
+
+ const default_checked = field.checked.split( '\r\n' );
+ if ( jQuery.inArray( options.option, default_checked ) > -1 ) {
+ jQuery( '#' + opt_id ).prop( 'checked', true );
+ }
+ } );
+ break;
+
+ case 'quantities':
+ jQuery.each( field.options, function ( label, options ) {
+ //console.log(options);
+ if ( options.default === '' ) {
+ return;
+ }
+ const opt_id =
+ product_id + '-' + field.data_name + '-' + options.id;
+ jQuery( '#' + opt_id )
+ .val( options.default )
+ .trigger( 'change' );
+ } );
+ break;
+
+ case 'text':
+ case 'date':
+ case 'number':
+ if ( '' === jQuery( '#' + field.data_name ).val() ) {
+ jQuery( '#' + field.data_name ).val( field.default_value );
+ }
+ break;
+ }
}
-// Updating conditionally hidden fields
+// Mirror the current hidden field list into the hidden input consumed by PHP.
function ppom_fields_hidden_conditionally() {
-
- // Reset
- ppom_hidden_fields = [];
- // jQuery(`.ppom-field-wrapper.ppom-c-hide`).filter(function() {
-
- // const data_name = jQuery(this).data('data_name');
- // jQuery(`#${data_name}`).prop('required', false);
- // // console.log(data_name);
- // ppom_hidden_fields.push(data_name);
- // });
- // console.log("Condionally Hidden", ppom_hidden_fields);
- // jQuery("#conditionally_hidden").val(ppom_hidden_fields);
-
- var datanames = jQuery(`.ppom-field-wrapper[class*="ppom-locked-"]`).map( (i,h) => ppom_hidden_fields.push(jQuery(h).data('data_name')) );
- jQuery("#conditionally_hidden").val(ppom_hidden_fields);
- // console.log(ppom_hidden_fields);
+ // Reset
+ ppom_hidden_fields = [];
+ // jQuery(`.ppom-field-wrapper.ppom-c-hide`).filter(function() {
+
+ // const data_name = jQuery(this).data('data_name');
+ // jQuery(`#${data_name}`).prop('required', false);
+ // // console.log(data_name);
+ // ppom_hidden_fields.push(data_name);
+ // });
+ // console.log("Condionally Hidden", ppom_hidden_fields);
+ // jQuery("#conditionally_hidden").val(ppom_hidden_fields);
+
+ const datanames = jQuery(
+ `.ppom-field-wrapper[class*="ppom-locked-"]`
+ ).map( ( i, h ) =>
+ ppom_hidden_fields.push( jQuery( h ).data( 'data_name' ) )
+ );
+ jQuery( '#conditionally_hidden' ).val( ppom_hidden_fields );
+ // console.log(ppom_hidden_fields);
}
diff --git a/js/ppom-conditions-v2bkp.js b/js/ppom-conditions-v2bkp.js
index f89d3781..d51e97d3 100644
--- a/js/ppom-conditions-v2bkp.js
+++ b/js/ppom-conditions-v2bkp.js
@@ -2,505 +2,597 @@
* PPOM Conditional Version 2
* More Fast and Optimized
* April, 2020 in LockedDown (CORVID-19)
- * */
-
-var ppom_hidden_fields = [];
-
-jQuery(function($) {
-
- setTimeout(function() {
- $('form.cart').find('select option:selected, input[type="radio"]:checked, input[type="checkbox"]:checked').each(function(i, field) {
-
- if ($(field).closest('div.ppom-field-wrapper').hasClass('ppom-c-hide')) return;
-
- const data_name = $(field).data('data_name');
- ppom_check_conditions(data_name, function(element_dataname, event_type) {
- // console.log(data_name, event_type);
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
- });
-
- $('form.cart').find('div.ppom-c-show').each(function(i, field) {
-
- const data_name = $(field).data('data_name');
- ppom_check_conditions(data_name, function(element_dataname, event_type) {
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
- });
-
- $('form.cart').find('div.ppom-c-hide').each(function(i, field) {
- const data_name = $(field).data('data_name');
- $.event.trigger({
- type: 'ppom_field_hidden',
- field: data_name,
- time: new Date()
- });
- });
-
- }, 100);
-
- // $('form.cart').on('change', 'select, input[type="radio"], input[type="checkbox"]', function(ev) {
-
- $(".ppom-wrapper").on('change', 'select,input:radio,input:checkbox', function(e) {
-
- let value = null;
- if (($(this).is(':radio') || $(this).is(':checkbox'))) {
- value = this.checked ? $(this).val() : null;
- }
- else {
-
- value = $(this).val();
- }
-
- const data_name = $(this).data('data_name');
- // console.log("Checking condition for ", data_name);
-
- ppom_check_conditions(data_name, function(element_dataname, event_type) {
- // console.log(`${element_dataname} ===> ${event_type}`);
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
- });
-
- $(document).on('ppom_hidden_fields_updated', function(e) {
- ppom_fields_hidden_conditionally();
-
- // $("#conditionally_hidden").val(ppom_hidden_fields);
- // console.log(` hiddend field updated ==> ${e.field}`);
- // $("#conditionally_hidden").val(ppom_hidden_fields);
- // ppom_update_option_prices();
- });
-
-
- $(document).on('ppom_field_hidden', function(e) {
-
- // console.log(e.field)
-
- var element_type = ppom_get_field_type_by_id(e.field);
- switch (element_type) {
-
- case 'select':
- $('select[name="ppom[fields][' + e.field + ']"]').val('');
- break;
-
- case 'multiple_select':
-
- var selector = $('select[name="ppom[fields][' + e.field + '][]"]');
- var selected_value = selector.val();
- var selected_options = selector.find('option:selected');
-
- jQuery.each(selected_options, function(index, default_selected) {
-
- var option_id = jQuery(default_selected).attr('data-option_id');
- var the_id = 'ppom-multipleselect-' + e.field + '-' + option_id;
-
- $("#" + the_id).remove();
- });
-
- if (selected_value) {
-
- $('select[name="ppom[fields][' + e.field + '][]"]').val(null).trigger("change");
- }
-
- break;
-
- case 'checkbox':
- $('input[name="ppom[fields][' + e.field + '][]"]').prop('checked', false);
- break;
-
- case 'radio':
- $('input[name="ppom[fields][' + e.field + ']"]').prop('checked', false);
- break;
-
- case 'file':
- $('#filelist-' + e.field).find('.u_i_c_box').remove();
- break;
-
- case 'palettes':
- case 'image':
- $('input[name="ppom[fields][' + e.field + '][]"]').prop('checked', false);
- break;
-
- case 'imageselect':
- var the_id = 'ppom-imageselect' + e.field;
- $("#" + the_id).remove();
- break;
-
- case 'quantityoption':
- $('#' + e.field).val('');
- var the_id = 'ppom-quantityoption-rm' + e.field;
- $("#" + the_id).remove();
- break;
-
- case 'pricematrix':
- $(`input[data-dataname="ppom[fields][${e.field}]"]`).removeClass('active');
- break;
-
- case 'quantities':
- $(`input[name^="ppom[fields][${e.field}]"]`).val('');
- break;
-
-
- default:
- // Reset text/textarea/date/email etc types
- $('#' + e.field).val('');
- break;
- }
-
- $.event.trigger({
- type: "ppom_hidden_fields_updated",
- field: e.field,
- time: new Date()
- });
-
- ppom_check_conditions(e.field, function(element_dataname, event_type) {
- // console.log(`${element_dataname} ===> ${event_type}`);
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
- });
-
- /*$(document).on('ppom_field_shown', function(e) {
+ */
+
+let ppom_hidden_fields = [];
+
+jQuery( function ( $ ) {
+ setTimeout( function () {
+ $( 'form.cart' )
+ .find(
+ 'select option:selected, input[type="radio"]:checked, input[type="checkbox"]:checked'
+ )
+ .each( function ( i, field ) {
+ if (
+ $( field )
+ .closest( 'div.ppom-field-wrapper' )
+ .hasClass( 'ppom-c-hide' )
+ ) {
+ return;
+ }
+
+ const data_name = $( field ).data( 'data_name' );
+ ppom_check_conditions(
+ data_name,
+ function ( element_dataname, event_type ) {
+ // console.log(data_name, event_type);
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+ } );
+
+ $( 'form.cart' )
+ .find( 'div.ppom-c-show' )
+ .each( function ( i, field ) {
+ const data_name = $( field ).data( 'data_name' );
+ ppom_check_conditions(
+ data_name,
+ function ( element_dataname, event_type ) {
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+ } );
+
+ $( 'form.cart' )
+ .find( 'div.ppom-c-hide' )
+ .each( function ( i, field ) {
+ const data_name = $( field ).data( 'data_name' );
+ $.event.trigger( {
+ type: 'ppom_field_hidden',
+ field: data_name,
+ time: new Date(),
+ } );
+ } );
+ }, 100 );
+
+ // $('form.cart').on('change', 'select, input[type="radio"], input[type="checkbox"]', function(ev) {
+
+ $( '.ppom-wrapper' ).on(
+ 'change',
+ 'select,input:radio,input:checkbox',
+ function ( e ) {
+ let value = null;
+ if ( $( this ).is( ':radio' ) || $( this ).is( ':checkbox' ) ) {
+ value = this.checked ? $( this ).val() : null;
+ } else {
+ value = $( this ).val();
+ }
+
+ const data_name = $( this ).data( 'data_name' );
+ // console.log("Checking condition for ", data_name);
+
+ ppom_check_conditions(
+ data_name,
+ function ( element_dataname, event_type ) {
+ // console.log(`${element_dataname} ===> ${event_type}`);
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+ }
+ );
+
+ $( document ).on( 'ppom_hidden_fields_updated', function ( e ) {
+ ppom_fields_hidden_conditionally();
+
+ // $("#conditionally_hidden").val(ppom_hidden_fields);
+ // console.log(` hiddend field updated ==> ${e.field}`);
+ // $("#conditionally_hidden").val(ppom_hidden_fields);
+ // ppom_update_option_prices();
+ } );
+
+ $( document ).on( 'ppom_field_hidden', function ( e ) {
+ // console.log(e.field)
+
+ const element_type = ppom_get_field_type_by_id( e.field );
+ switch ( element_type ) {
+ case 'select':
+ $( 'select[name="ppom[fields][' + e.field + ']"]' ).val( '' );
+ break;
+
+ case 'multiple_select':
+ var selector = $(
+ 'select[name="ppom[fields][' + e.field + '][]"]'
+ );
+ var selected_value = selector.val();
+ var selected_options = selector.find( 'option:selected' );
+
+ jQuery.each(
+ selected_options,
+ function ( index, default_selected ) {
+ const option_id =
+ jQuery( default_selected ).attr( 'data-option_id' );
+ const the_id =
+ 'ppom-multipleselect-' + e.field + '-' + option_id;
+
+ $( '#' + the_id ).remove();
+ }
+ );
+
+ if ( selected_value ) {
+ $( 'select[name="ppom[fields][' + e.field + '][]"]' )
+ .val( null )
+ .trigger( 'change' );
+ }
+
+ break;
+
+ case 'checkbox':
+ $( 'input[name="ppom[fields][' + e.field + '][]"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+
+ case 'radio':
+ $( 'input[name="ppom[fields][' + e.field + ']"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+
+ case 'file':
+ $( '#filelist-' + e.field )
+ .find( '.u_i_c_box' )
+ .remove();
+ break;
+
+ case 'palettes':
+ case 'image':
+ $( 'input[name="ppom[fields][' + e.field + '][]"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+
+ case 'imageselect':
+ var the_id = 'ppom-imageselect' + e.field;
+ $( '#' + the_id ).remove();
+ break;
+
+ case 'quantityoption':
+ $( '#' + e.field ).val( '' );
+ var the_id = 'ppom-quantityoption-rm' + e.field;
+ $( '#' + the_id ).remove();
+ break;
+
+ case 'pricematrix':
+ $(
+ `input[data-dataname="ppom[fields][${ e.field }]"]`
+ ).removeClass( 'active' );
+ break;
+
+ case 'quantities':
+ $( `input[name^="ppom[fields][${ e.field }]"]` ).val( '' );
+ break;
+
+ default:
+ // Reset text/textarea/date/email etc types
+ $( '#' + e.field ).val( '' );
+ break;
+ }
+
+ $.event.trigger( {
+ type: 'ppom_hidden_fields_updated',
+ field: e.field,
+ time: new Date(),
+ } );
+
+ ppom_check_conditions(
+ e.field,
+ function ( element_dataname, event_type ) {
+ // console.log(`${element_dataname} ===> ${event_type}`);
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+ } );
+
+ /*$(document).on('ppom_field_shown', function(e) {
console.log(`shown event ${e.field}`);
ppom_check_conditions(e.field);
});*/
- $(document).on('ppom_field_shown', function(e) {
-
- ppom_fields_hidden_conditionally();
-
- // Set checked/selected again
- ppom_set_default_option(e.field);
-
- ppom_check_conditions(e.field, function(element_dataname, event_type) {
- // console.log(`${element_dataname} ===> ${event_type}`);
- $.event.trigger({
- type: event_type,
- field: element_dataname,
- time: new Date()
- });
- });
-
-
- var field_meta = ppom_get_field_meta_by_id(e.field);
-
- // Apply FileAPI to DOM
- // PPOM version 22.0 has issue, commenting it so far by Najeeb April 4, 2021
- // if (field_meta.type === 'file' || field_meta.type === 'cropper') {
- // ppom_setup_file_upload_input(field_meta);
- // }
-
- // Price Matrix
- if (field_meta.type == 'pricematrix') {
- // Resettin
- $(".ppom_pricematrix").removeClass('active');
-
- // Set Active
- var classname = "." + field_meta.data_name;
- // console.log(field_meta.data_name, jQuery(`input[data-dataname="ppom[fields][${field_meta.data_name}]"]`));
- jQuery(`input[data-dataname="ppom[fields][${field_meta.data_name}]"]`).addClass('active')
- // $(classname).find('.ppom_pricematrix').addClass('active')
- }
-
- //Imageselect (Image dropdown)
- if (field_meta.type === 'imageselect') {
-
- var dd_selector = 'ppom_imageselect_' + field_meta.data_name;
- var ddData = $('#' + dd_selector).data('ddslick');
- var image_replace = $('#' + dd_selector).attr('data-enable-rpimg');
- ppom_create_hidden_input(ddData);
- ppom_update_option_prices();
- setTimeout(function() {
- ppom_image_selection(ddData, image_replace);
- }, 100);
- // $('#'+dd_selector).ddslick('select', {index: 0 });
- }
-
-
- // Multiple Select Addon
- if (field_meta.type === 'multiple_select') {
-
- var selector = jQuery('select[name="ppom[fields][' + field_meta.data_name + '][]"]');
- var selected_value = selector.val();
- var default_value = field_meta.selected;
-
- if (selected_value === null && default_value) {
-
- var selected_opt_arr = default_value.split(',');
-
- selector.val(selected_opt_arr).trigger('change');
-
- var selected_options = selector.find('option:selected');
- jQuery.each(selected_options, function(index, default_selected) {
-
- var option_id = jQuery(default_selected).attr('data-option_id');
- var option_label = jQuery(default_selected).attr('data-optionlabel');
- var option_price = jQuery(default_selected).attr('data-optionprice');
-
- ppom_multiple_select_create_hidden_input(field_meta.data_name, option_id, option_price, option_label, field_meta.title);
- });
- }
- }
-
- });
-
- ppom_fields_hidden_conditionally();
-
-});
-
-function ppom_check_conditions(data_name, callback) {
-
- let is_matched = false;
- const ppom_type = jQuery(`.ppom-input[data-data_name="${data_name}"]`).data('type');
- let event_type, element_data_name;
- const field_val = ppom_get_element_value(data_name);
- // console.log('data_name',data_name);
- jQuery(`div.ppom-cond-${data_name}`).each(function() {
- // return this.data('cond-val1').match(/\w*-Back/);
- // console.log(jQuery(this));
- const total_cond = parseInt(jQuery(this).data('cond-total'));
- const binding = jQuery(this).data(`cond-bind`);
- const visibility = jQuery(this).data(`cond-visibility`);
- element_data_name = jQuery(this).data('data_name');
-
- let matched = 0;
- var matched_conditions = [];
- for (var t = 1; t <= total_cond; t++) {
-
- const cond_element = jQuery(this).data(`cond-input${t}`);
- const cond_val = jQuery(this).data(`cond-val${t}`).toString();
- const operator = jQuery(this).data(`cond-operator${t}`);
-
- // const field_val = ppom_get_field_type(field_obj);
- if (cond_element !== data_name) continue;
- is_matched = ppom_compare_values(field_val, cond_val, operator);
- // console.log(`${data_name} TRIGGERS :: ${t} ***** ${element_data_name} ==> field value ${field_val} || cond_valu ${cond_val} || operator ${operator} || Binding ${binding} is_matched=>${is_matched}`);
- // console.log(field_val,cond_val);
- matched = is_matched ? ++matched : matched;
- matched_conditions[element_data_name] = matched;
-
- event_type = visibility === 'hide' ? 'ppom_field_hidden' : 'ppom_field_shown';
- // console.log(`${t} ***** ${element_data_name} total_cond ${total_cond} == matched ${matched} ==> ${matched_conditions[element_data_name]} ==> visibility ${event_type}`);
-
- if ( (matched_conditions[element_data_name] > 0 && binding === 'Any') ||
- (matched_conditions[element_data_name] == total_cond && binding === 'All')
- ) {
-
- if( visibility === 'hide' ){
- jQuery(this).addClass(`ppom-locked-${data_name} ppom-c-hide`).removeClass('ppom-c-show');
- }else{
- jQuery(this).removeClass(`ppom-locked-${data_name} ppom-c-hide`);
- }
- if (typeof callback == "function")
- callback(element_data_name, event_type);
- // return is_matched;
-
-
- }
- else if ( ! is_matched ) {
-
- if( visibility === 'hide' ){
- event_type = 'ppom_field_shown';
- jQuery(this).removeClass(`ppom-locked-${data_name} ppom-c-hide`);
- }else{
- event_type = 'ppom_field_hidden';
- jQuery(this).addClass(`ppom-locked-${data_name} ppom-c-hide`);
- }
-
- if (typeof callback == "function")
- callback(element_data_name, event_type);
- } else {
-
- jQuery(this).removeClass(`ppom-locked-${data_name} ppom-c-hide`);
- // console.log('event_type', event_type);
- if (typeof callback == "function")
- callback(element_data_name, event_type);
- }
- }
-
- // return is_matched;
- // return jQuery(this).data('cond-val1') === jQuery(this).val();
- });
+ $( document ).on( 'ppom_field_shown', function ( e ) {
+ ppom_fields_hidden_conditionally();
+
+ // Set checked/selected again
+ ppom_set_default_option( e.field );
+
+ ppom_check_conditions(
+ e.field,
+ function ( element_dataname, event_type ) {
+ // console.log(`${element_dataname} ===> ${event_type}`);
+ $.event.trigger( {
+ type: event_type,
+ field: element_dataname,
+ time: new Date(),
+ } );
+ }
+ );
+
+ const field_meta = ppom_get_field_meta_by_id( e.field );
+
+ // Apply FileAPI to DOM
+ // PPOM version 22.0 has issue, commenting it so far by Najeeb April 4, 2021
+ // if (field_meta.type === 'file' || field_meta.type === 'cropper') {
+ // ppom_setup_file_upload_input(field_meta);
+ // }
+
+ // Price Matrix
+ if ( field_meta.type == 'pricematrix' ) {
+ // Resettin
+ $( '.ppom_pricematrix' ).removeClass( 'active' );
+
+ // Set Active
+ const classname = '.' + field_meta.data_name;
+ // console.log(field_meta.data_name, jQuery(`input[data-dataname="ppom[fields][${field_meta.data_name}]"]`));
+ jQuery(
+ `input[data-dataname="ppom[fields][${ field_meta.data_name }]"]`
+ ).addClass( 'active' );
+ // $(classname).find('.ppom_pricematrix').addClass('active')
+ }
+
+ //Imageselect (Image dropdown)
+ if ( field_meta.type === 'imageselect' ) {
+ const dd_selector = 'ppom_imageselect_' + field_meta.data_name;
+ const ddData = $( '#' + dd_selector ).data( 'ddslick' );
+ const image_replace = $( '#' + dd_selector ).attr(
+ 'data-enable-rpimg'
+ );
+ ppom_create_hidden_input( ddData );
+ ppom_update_option_prices();
+ setTimeout( function () {
+ ppom_image_selection( ddData, image_replace );
+ }, 100 );
+ // $('#'+dd_selector).ddslick('select', {index: 0 });
+ }
+
+ // Multiple Select Addon
+ if ( field_meta.type === 'multiple_select' ) {
+ const selector = jQuery(
+ 'select[name="ppom[fields][' + field_meta.data_name + '][]"]'
+ );
+ const selected_value = selector.val();
+ const default_value = field_meta.selected;
+
+ if ( selected_value === null && default_value ) {
+ const selected_opt_arr = default_value.split( ',' );
+
+ selector.val( selected_opt_arr ).trigger( 'change' );
+
+ const selected_options = selector.find( 'option:selected' );
+ jQuery.each(
+ selected_options,
+ function ( index, default_selected ) {
+ const option_id =
+ jQuery( default_selected ).attr( 'data-option_id' );
+ const option_label =
+ jQuery( default_selected ).attr(
+ 'data-optionlabel'
+ );
+ const option_price =
+ jQuery( default_selected ).attr(
+ 'data-optionprice'
+ );
+
+ ppom_multiple_select_create_hidden_input(
+ field_meta.data_name,
+ option_id,
+ option_price,
+ option_label,
+ field_meta.title
+ );
+ }
+ );
+ }
+ }
+ } );
+
+ ppom_fields_hidden_conditionally();
+} );
+
+function ppom_check_conditions( data_name, callback ) {
+ let is_matched = false;
+ const ppom_type = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]`
+ ).data( 'type' );
+ let event_type, element_data_name;
+ const field_val = ppom_get_element_value( data_name );
+ // console.log('data_name',data_name);
+ jQuery( `div.ppom-cond-${ data_name }` ).each( function () {
+ // return this.data('cond-val1').match(/\w*-Back/);
+ // console.log(jQuery(this));
+ const total_cond = parseInt( jQuery( this ).data( 'cond-total' ) );
+ const binding = jQuery( this ).data( `cond-bind` );
+ const visibility = jQuery( this ).data( `cond-visibility` );
+ element_data_name = jQuery( this ).data( 'data_name' );
+
+ let matched = 0;
+ const matched_conditions = [];
+ for ( let t = 1; t <= total_cond; t++ ) {
+ const cond_element = jQuery( this ).data( `cond-input${ t }` );
+ const cond_val = jQuery( this ).data( `cond-val${ t }` ).toString();
+ const operator = jQuery( this ).data( `cond-operator${ t }` );
+
+ // const field_val = ppom_get_field_type(field_obj);
+ if ( cond_element !== data_name ) {
+ continue;
+ }
+ is_matched = ppom_compare_values( field_val, cond_val, operator );
+ // console.log(`${data_name} TRIGGERS :: ${t} ***** ${element_data_name} ==> field value ${field_val} || cond_valu ${cond_val} || operator ${operator} || Binding ${binding} is_matched=>${is_matched}`);
+ // console.log(field_val,cond_val);
+ matched = is_matched ? ++matched : matched;
+ matched_conditions[ element_data_name ] = matched;
+
+ event_type =
+ visibility === 'hide'
+ ? 'ppom_field_hidden'
+ : 'ppom_field_shown';
+ // console.log(`${t} ***** ${element_data_name} total_cond ${total_cond} == matched ${matched} ==> ${matched_conditions[element_data_name]} ==> visibility ${event_type}`);
+
+ if (
+ ( matched_conditions[ element_data_name ] > 0 &&
+ binding === 'Any' ) ||
+ ( matched_conditions[ element_data_name ] == total_cond &&
+ binding === 'All' )
+ ) {
+ if ( visibility === 'hide' ) {
+ jQuery( this )
+ .addClass( `ppom-locked-${ data_name } ppom-c-hide` )
+ .removeClass( 'ppom-c-show' );
+ } else {
+ jQuery( this ).removeClass(
+ `ppom-locked-${ data_name } ppom-c-hide`
+ );
+ }
+ if ( typeof callback === 'function' ) {
+ callback( element_data_name, event_type );
+ }
+ // return is_matched;
+ } else if ( ! is_matched ) {
+ if ( visibility === 'hide' ) {
+ event_type = 'ppom_field_shown';
+ jQuery( this ).removeClass(
+ `ppom-locked-${ data_name } ppom-c-hide`
+ );
+ } else {
+ event_type = 'ppom_field_hidden';
+ jQuery( this ).addClass(
+ `ppom-locked-${ data_name } ppom-c-hide`
+ );
+ }
+
+ if ( typeof callback === 'function' ) {
+ callback( element_data_name, event_type );
+ }
+ } else {
+ jQuery( this ).removeClass(
+ `ppom-locked-${ data_name } ppom-c-hide`
+ );
+ // console.log('event_type', event_type);
+ if ( typeof callback === 'function' ) {
+ callback( element_data_name, event_type );
+ }
+ }
+ }
+
+ // return is_matched;
+ // return jQuery(this).data('cond-val1') === jQuery(this).val();
+ } );
}
-function ppom_get_input_dom_type(data_name) {
-
- // const field_obj = jQuery(`input[name="ppom[fields][${data_name}]"], input[name="ppom[fields][${data_name}[]]"], select[name="ppom[fields][${data_name}]"]`);
- const field_obj = jQuery(`.ppom-input[data-data_name="${data_name}"]`);
- const ppom_type = field_obj.closest('.ppom-field-wrapper').data('type');
- return ppom_type;
+function ppom_get_input_dom_type( data_name ) {
+ // const field_obj = jQuery(`input[name="ppom[fields][${data_name}]"], input[name="ppom[fields][${data_name}[]]"], select[name="ppom[fields][${data_name}]"]`);
+ const field_obj = jQuery( `.ppom-input[data-data_name="${ data_name }"]` );
+ const ppom_type = field_obj.closest( '.ppom-field-wrapper' ).data( 'type' );
+ return ppom_type;
}
-function ppom_get_element_value(data_name) {
-
- const ppom_type = ppom_get_input_dom_type(data_name);
- let element_value = '';
- var value_found_cb = [];
-
- switch (ppom_type) {
- case 'switcher':
- case 'radio':
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]:checked`).val();
- break;
- case 'palettes':
- case 'checkbox':
- jQuery('input[name="ppom[fields][' + data_name + '][]"]:checked').each(function(i) {
- value_found_cb[i] = jQuery(this).val();
- });
- break;
- case 'image':
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]:checked`).data('label');
- break;
- case 'imageselect':
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]:checked`).data('label');
- break;
- case 'fixedprice':
- var render_type = jQuery(`.ppom-input-${data_name}`).attr('data-input');
- if( render_type == 'radio' ){
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]:checked`).val();
- }else{
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]`).val();
- }
- break;
-
- default:
- element_value = jQuery(`.ppom-input[data-data_name="${data_name}"]`).val();
- }
-
- if (ppom_type === 'checkbox' || ppom_type === 'palettes') {
- // console.log(value_found_cb);
- return value_found_cb;
- }
-
- return element_value;
+function ppom_get_element_value( data_name ) {
+ const ppom_type = ppom_get_input_dom_type( data_name );
+ let element_value = '';
+ const value_found_cb = [];
+
+ switch ( ppom_type ) {
+ case 'switcher':
+ case 'radio':
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]:checked`
+ ).val();
+ break;
+ case 'palettes':
+ case 'checkbox':
+ jQuery(
+ 'input[name="ppom[fields][' + data_name + '][]"]:checked'
+ ).each( function ( i ) {
+ value_found_cb[ i ] = jQuery( this ).val();
+ } );
+ break;
+ case 'image':
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]:checked`
+ ).data( 'label' );
+ break;
+ case 'imageselect':
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]:checked`
+ ).data( 'label' );
+ break;
+ case 'fixedprice':
+ var render_type = jQuery( `.ppom-input-${ data_name }` ).attr(
+ 'data-input'
+ );
+ if ( render_type == 'radio' ) {
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]:checked`
+ ).val();
+ } else {
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]`
+ ).val();
+ }
+ break;
+
+ default:
+ element_value = jQuery(
+ `.ppom-input[data-data_name="${ data_name }"]`
+ ).val();
+ }
+
+ if ( ppom_type === 'checkbox' || ppom_type === 'palettes' ) {
+ // console.log(value_found_cb);
+ return value_found_cb;
+ }
+
+ return element_value;
}
-function ppom_compare_values(v1, v2, operator) {
-
- let result = false;
- switch (operator) {
- case 'is':
- if( Array.isArray(v1) ) {
- result = jQuery.inArray(v2, v1) !== -1 ? true : false;
- }else{
- result = v1 === v2 ? true : false;
- }
- break;
- case 'not':
- result = v1 !== v2 ? true : false;
- break;
-
- case 'greater than':
- result = parseFloat(v1) > parseFloat(v2) ? true : false;
- break;
- case 'less than':
- result = parseFloat(v1) < parseFloat(v2) ? true : false;
- break;
-
- default:
- // code
- }
-
- // console.log(`matching ${v1} ${operator} ${v2}`);
- return result;
+function ppom_compare_values( v1, v2, operator ) {
+ let result = false;
+ switch ( operator ) {
+ case 'is':
+ if ( Array.isArray( v1 ) ) {
+ result = jQuery.inArray( v2, v1 ) !== -1 ? true : false;
+ } else {
+ result = v1 === v2 ? true : false;
+ }
+ break;
+ case 'not':
+ result = v1 !== v2 ? true : false;
+ break;
+
+ case 'greater than':
+ result = parseFloat( v1 ) > parseFloat( v2 ) ? true : false;
+ break;
+ case 'less than':
+ result = parseFloat( v1 ) < parseFloat( v2 ) ? true : false;
+ break;
+
+ default:
+ // code
+ }
+
+ // console.log(`matching ${v1} ${operator} ${v2}`);
+ return result;
}
-function ppom_set_default_option(field_id) {
-
- // get product id
- var product_id = ppom_input_vars.product_id;
-
- var field = ppom_get_field_meta_by_id(field_id);
-
- switch (field.type) {
-
- // Check if field is
- case 'switcher':
- case 'radio':
- jQuery.each(field.options, function(label, options) {
- var opt_id = product_id + '-' + field.data_name + '-' + options.id;
- // console.log('optio nid ', opt_id);
-
- if (options.option == field.selected) {
- jQuery("#" + opt_id).prop('checked', true).trigger('change');
- }
- });
- break;
-
- case 'select':
- jQuery("#" + field.data_name).val(field.selected);
- break;
-
- case 'image':
- jQuery.each(field.images, function(index, img) {
-
- if (img.title == field.selected) {
- jQuery("#" + field.data_name + '-' + img.id).prop('checked', true);
- }
- });
- break;
-
- case 'checkbox':
- jQuery.each(field.options, function(label, options) {
- var opt_id = product_id + '-' + field.data_name + '-' + options.id;
-
- var default_checked = field.checked.split('\r\n');
- if (jQuery.inArray(options.option, default_checked) > -1) {
- jQuery("#" + opt_id).prop('checked', true);
-
- }
- });
- break;
-
- case 'quantities':
- jQuery.each(field.options, function(label, options) {
- //console.log(options);
- if( options.default === '' ) return;
- var opt_id = product_id + '-' + field.data_name + '-' + options.id;
- jQuery("#" + opt_id).val(options.default).trigger('change');
-
- });
- break;
-
- case 'text':
- case 'date':
- case 'number':
- jQuery("#" + field.data_name).val(field.default_value);
- break;
- }
+function ppom_set_default_option( field_id ) {
+ // get product id
+ const product_id = ppom_input_vars.product_id;
+
+ const field = ppom_get_field_meta_by_id( field_id );
+
+ switch ( field.type ) {
+ // Check if field is
+ case 'switcher':
+ case 'radio':
+ jQuery.each( field.options, function ( label, options ) {
+ const opt_id =
+ product_id + '-' + field.data_name + '-' + options.id;
+ // console.log('optio nid ', opt_id);
+
+ if ( options.option == field.selected ) {
+ jQuery( '#' + opt_id )
+ .prop( 'checked', true )
+ .trigger( 'change' );
+ }
+ } );
+ break;
+
+ case 'select':
+ jQuery( '#' + field.data_name ).val( field.selected );
+ break;
+
+ case 'image':
+ jQuery.each( field.images, function ( index, img ) {
+ if ( img.title == field.selected ) {
+ jQuery( '#' + field.data_name + '-' + img.id ).prop(
+ 'checked',
+ true
+ );
+ }
+ } );
+ break;
+
+ case 'checkbox':
+ jQuery.each( field.options, function ( label, options ) {
+ const opt_id =
+ product_id + '-' + field.data_name + '-' + options.id;
+
+ const default_checked = field.checked.split( '\r\n' );
+ if ( jQuery.inArray( options.option, default_checked ) > -1 ) {
+ jQuery( '#' + opt_id ).prop( 'checked', true );
+ }
+ } );
+ break;
+
+ case 'quantities':
+ jQuery.each( field.options, function ( label, options ) {
+ //console.log(options);
+ if ( options.default === '' ) {
+ return;
+ }
+ const opt_id =
+ product_id + '-' + field.data_name + '-' + options.id;
+ jQuery( '#' + opt_id )
+ .val( options.default )
+ .trigger( 'change' );
+ } );
+ break;
+
+ case 'text':
+ case 'date':
+ case 'number':
+ jQuery( '#' + field.data_name ).val( field.default_value );
+ break;
+ }
}
// Updating conditionally hidden fields
function ppom_fields_hidden_conditionally() {
-
- // Reset
- ppom_hidden_fields = [];
- // jQuery(`.ppom-field-wrapper.ppom-c-hide`).filter(function() {
-
- // const data_name = jQuery(this).data('data_name');
- // jQuery(`#${data_name}`).prop('required', false);
- // // console.log(data_name);
- // ppom_hidden_fields.push(data_name);
- // });
- // console.log("Condionally Hidden", ppom_hidden_fields);
- // jQuery("#conditionally_hidden").val(ppom_hidden_fields);
-
- var datanames = jQuery(`.ppom-field-wrapper[class*="ppom-locked-"]`).map( (i,h) => ppom_hidden_fields.push(jQuery(h).data('data_name')) );
- jQuery("#conditionally_hidden").val(ppom_hidden_fields);
- // console.log(ppom_hidden_fields);
-}
\ No newline at end of file
+ // Reset
+ ppom_hidden_fields = [];
+ // jQuery(`.ppom-field-wrapper.ppom-c-hide`).filter(function() {
+
+ // const data_name = jQuery(this).data('data_name');
+ // jQuery(`#${data_name}`).prop('required', false);
+ // // console.log(data_name);
+ // ppom_hidden_fields.push(data_name);
+ // });
+ // console.log("Condionally Hidden", ppom_hidden_fields);
+ // jQuery("#conditionally_hidden").val(ppom_hidden_fields);
+
+ const datanames = jQuery(
+ `.ppom-field-wrapper[class*="ppom-locked-"]`
+ ).map( ( i, h ) =>
+ ppom_hidden_fields.push( jQuery( h ).data( 'data_name' ) )
+ );
+ jQuery( '#conditionally_hidden' ).val( ppom_hidden_fields );
+ // console.log(ppom_hidden_fields);
+}
diff --git a/js/ppom-conditions.js b/js/ppom-conditions.js
index bea969d9..379dbfe8 100644
--- a/js/ppom-conditions.js
+++ b/js/ppom-conditions.js
@@ -1,424 +1,479 @@
/**
- * PPOM Fields Conditions
- **/
-"use strict"
-
-var ppom_field_matched_rules = {};
-var ppom_hidden_fields = [];
-jQuery(function($) {
-
- $(".ppom-wrapper").on('change', 'select,input:radio,input:checkbox', function(e) {
-
- ppom_check_conditions();
- });
-
- $(document).on('ppom_field_shown', function(e) {
-
-
- // Remove from array
- $.each(ppom_hidden_fields, function(i, item) {
- if (item === e.field) {
-
-
- // Set checked/selected again
- ppom_set_default_option(item);
-
- ppom_hidden_fields.splice(i, 1);
- $.event.trigger({
- type: "ppom_hidden_fields_updated",
- field: e.field,
- time: new Date()
- });
-
- }
- });
-
- // Apply FileAPI to DOM
- var field_meta = ppom_get_field_meta_by_id(e.field);
- if (field_meta.type === 'file' || field_meta.type === 'cropper') {
-
- ppom_setup_file_upload_input(field_meta);
- }
-
- // Price Matrix
- if (field_meta.type == 'pricematrix') {
- // Resettin
- $(".ppom_pricematrix").removeClass('active');
-
- // Set Active
- var classname = "." + field_meta.data_name;
- // console.log(classname);
- $(classname).find('.ppom_pricematrix').addClass('active')
- }
-
- //Imageselect (Image dropdown)
- if (field_meta.type === 'imageselect') {
-
- var dd_selector = 'ppom_imageselect_' + field_meta.data_name;
- var ddData = $('#' + dd_selector).data('ddslick');
- var image_replace = $('#' + dd_selector).attr('data-enable-rpimg');
- ppom_create_hidden_input(ddData);
- ppom_update_option_prices();
- setTimeout(function() {
- ppom_image_selection(ddData, image_replace);
- }, 100);
- // $('#'+dd_selector).ddslick('select', {index: 0 });
- }
-
-
- // Multiple Select Addon
- if (field_meta.type === 'multiple_select') {
-
- var selector = jQuery('select[name="ppom[fields][' + field_meta.data_name + '][]"]');
- var selected_value = selector.val();
- var default_value = field_meta.selected;
-
- if (selected_value === null && default_value) {
-
- var selected_opt_arr = default_value.split(',');
-
- selector.val(selected_opt_arr).trigger('change');
-
- var selected_options = selector.find('option:selected');
- jQuery.each(selected_options, function(index, default_selected) {
-
- var option_id = jQuery(default_selected).attr('data-option_id');
- var option_label = jQuery(default_selected).attr('data-optionlabel');
- var option_price = jQuery(default_selected).attr('data-optionprice');
-
- ppom_multiple_select_create_hidden_input(field_meta.data_name, option_id, option_price, option_label, field_meta.title);
- });
- }
- }
-
- });
-
- $(document).on('ppom_hidden_fields_updated', function(e) {
-
-
- $("#conditionally_hidden").val(ppom_hidden_fields);
- ppom_update_option_prices();
- });
-
- $(document).on('ppom_field_hidden', function(e) {
-
- var element_type = ppom_get_field_type_by_id(e.field);
- switch (element_type) {
-
- case 'select':
- $('select[name="ppom[fields][' + e.field + ']"]').val('');
- break;
-
- case 'multiple_select':
-
- var selector = $('select[name="ppom[fields][' + e.field + '][]"]');
- var selected_value = selector.val();
- var selected_options = selector.find('option:selected');
-
- jQuery.each(selected_options, function(index, default_selected) {
-
- var option_id = jQuery(default_selected).attr('data-option_id');
- var the_id = 'ppom-multipleselect-' + e.field + '-' + option_id;
-
- $("#" + the_id).remove();
- });
-
- if (selected_value) {
-
- $('select[name="ppom[fields][' + e.field + '][]"]').val(null).trigger("change");
- }
-
- break;
-
- case 'checkbox':
- $('input[name="ppom[fields][' + e.field + '][]"]').prop('checked', false);
- break;
- case 'radio':
- $('input[name="ppom[fields][' + e.field + ']"]').prop('checked', false);
- break;
-
- case 'file':
- $('#filelist-' + e.field).find('.u_i_c_box').remove();
- break;
-
- case 'palettes':
- case 'image':
- $('input[name="ppom[fields][' + e.field + '][]"]').prop('checked', false);
- break;
-
- case 'imageselect':
- var the_id = 'ppom-imageselect' + e.field;
- $("#" + the_id).remove();
- break;
-
- default:
- // Reset text/textarea/date/email etc types
- $('#' + e.field).val('');
- break;
- }
-
- ppom_hidden_fields.push(e.field);
- $.event.trigger({
- type: "ppom_hidden_fields_updated",
- field: e.field,
- time: new Date()
- });
- });
-
-
- setTimeout(function() {
- ppom_check_conditions();
- }, 500);
-
-});
-
-function ppom_set_default_option(field_id) {
-
- // get product id
- var product_id = ppom_input_vars.product_id;
-
- var field = ppom_get_field_meta_by_id(field_id);
- switch (field.type) {
-
- // Check if field is
- case 'radio':
- jQuery.each(field.options, function(label, options) {
- var opt_id = product_id + '-' + field.data_name + '-' + options.id;
-
- if (options.option == field.selected) {
- jQuery("#" + opt_id).prop('checked', true);
- }
- });
-
- break;
-
- case 'select':
- jQuery("#" + field.data_name).val(field.selected);
- break;
-
- case 'image':
- jQuery.each(field.images, function(index, img) {
-
- if (img.title == field.selected) {
- jQuery("#" + field.data_name + '-' + img.id).prop('checked', true);
- }
- });
- break;
-
- case 'checkbox':
- jQuery.each(field.options, function(label, options) {
- var opt_id = product_id + '-' + field.data_name + '-' + options.id;
-
- var default_checked = field.checked.split('\r\n');
- if (jQuery.inArray(options.option, default_checked) > -1) {
- jQuery("#" + opt_id).prop('checked', true);
-
- }
- });
- break;
-
- case 'text':
- case 'date':
- case 'number':
- jQuery("#" + field.data_name).val(field.default_value);
- break;
- }
+ * Legacy PPOM conditional-logic engine.
+ *
+ * This version works with the older `ppom_input_vars.conditions` structure.
+ * It still emits the same hidden/shown events so the rest of the frontend and
+ * the hidden `conditionally_hidden` payload remain compatible with PHP.
+ */
+'use strict';
+
+const ppom_field_matched_rules = {};
+const ppom_hidden_fields = [];
+jQuery( function ( $ ) {
+ // Legacy mode recalculates all conditions after each relevant field change.
+ $( '.ppom-wrapper' ).on(
+ 'change',
+ 'select,input:radio,input:checkbox',
+ function ( e ) {
+ ppom_check_conditions();
+ }
+ );
+
+ $( document ).on( 'ppom_field_shown', function ( e ) {
+ // Remove from array
+ $.each( ppom_hidden_fields, function ( i, item ) {
+ if ( item === e.field ) {
+ // Set checked/selected again
+ ppom_set_default_option( item );
+
+ ppom_hidden_fields.splice( i, 1 );
+ $.event.trigger( {
+ type: 'ppom_hidden_fields_updated',
+ field: e.field,
+ time: new Date(),
+ } );
+ }
+ } );
+
+ // Apply FileAPI to DOM
+ const field_meta = ppom_get_field_meta_by_id( e.field );
+ if ( field_meta.type === 'file' || field_meta.type === 'cropper' ) {
+ ppom_setup_file_upload_input( field_meta );
+ }
+
+ // Price Matrix
+ if ( field_meta.type == 'pricematrix' ) {
+ // Resettin
+ $( '.ppom_pricematrix' ).removeClass( 'active' );
+
+ // Set Active
+ const classname = '.' + field_meta.data_name;
+ // console.log(classname);
+ $( classname ).find( '.ppom_pricematrix' ).addClass( 'active' );
+ }
+
+ //Imageselect (Image dropdown)
+ if ( field_meta.type === 'imageselect' ) {
+ const dd_selector = 'ppom_imageselect_' + field_meta.data_name;
+ const ddData = $( '#' + dd_selector ).data( 'ddslick' );
+ const image_replace = $( '#' + dd_selector ).attr(
+ 'data-enable-rpimg'
+ );
+ ppom_create_hidden_input( ddData );
+ ppom_update_option_prices();
+ setTimeout( function () {
+ ppom_image_selection( ddData, image_replace );
+ }, 100 );
+ // $('#'+dd_selector).ddslick('select', {index: 0 });
+ }
+
+ // Multiple Select Addon
+ if ( field_meta.type === 'multiple_select' ) {
+ const selector = jQuery(
+ 'select[name="ppom[fields][' + field_meta.data_name + '][]"]'
+ );
+ const selected_value = selector.val();
+ const default_value = field_meta.selected;
+
+ if ( selected_value === null && default_value ) {
+ const selected_opt_arr = default_value.split( ',' );
+
+ selector.val( selected_opt_arr ).trigger( 'change' );
+
+ const selected_options = selector.find( 'option:selected' );
+ jQuery.each(
+ selected_options,
+ function ( index, default_selected ) {
+ const option_id =
+ jQuery( default_selected ).attr( 'data-option_id' );
+ const option_label =
+ jQuery( default_selected ).attr(
+ 'data-optionlabel'
+ );
+ const option_price =
+ jQuery( default_selected ).attr(
+ 'data-optionprice'
+ );
+
+ ppom_multiple_select_create_hidden_input(
+ field_meta.data_name,
+ option_id,
+ option_price,
+ option_label,
+ field_meta.title
+ );
+ }
+ );
+ }
+ }
+ } );
+
+ $( document ).on( 'ppom_hidden_fields_updated', function ( e ) {
+ $( '#conditionally_hidden' ).val( ppom_hidden_fields );
+ ppom_update_option_prices();
+ } );
+
+ $( document ).on( 'ppom_field_hidden', function ( e ) {
+ const element_type = ppom_get_field_type_by_id( e.field );
+ switch ( element_type ) {
+ case 'select':
+ $( 'select[name="ppom[fields][' + e.field + ']"]' ).val( '' );
+ break;
+
+ case 'multiple_select':
+ var selector = $(
+ 'select[name="ppom[fields][' + e.field + '][]"]'
+ );
+ var selected_value = selector.val();
+ var selected_options = selector.find( 'option:selected' );
+
+ jQuery.each(
+ selected_options,
+ function ( index, default_selected ) {
+ const option_id =
+ jQuery( default_selected ).attr( 'data-option_id' );
+ const the_id =
+ 'ppom-multipleselect-' + e.field + '-' + option_id;
+
+ $( '#' + the_id ).remove();
+ }
+ );
+
+ if ( selected_value ) {
+ $( 'select[name="ppom[fields][' + e.field + '][]"]' )
+ .val( null )
+ .trigger( 'change' );
+ }
+
+ break;
+
+ case 'checkbox':
+ $( 'input[name="ppom[fields][' + e.field + '][]"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+ case 'radio':
+ $( 'input[name="ppom[fields][' + e.field + ']"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+
+ case 'file':
+ $( '#filelist-' + e.field )
+ .find( '.u_i_c_box' )
+ .remove();
+ break;
+
+ case 'palettes':
+ case 'image':
+ $( 'input[name="ppom[fields][' + e.field + '][]"]' ).prop(
+ 'checked',
+ false
+ );
+ break;
+
+ case 'imageselect':
+ var the_id = 'ppom-imageselect' + e.field;
+ $( '#' + the_id ).remove();
+ break;
+
+ default:
+ // Reset text/textarea/date/email etc types
+ $( '#' + e.field ).val( '' );
+ break;
+ }
+
+ ppom_hidden_fields.push( e.field );
+ $.event.trigger( {
+ type: 'ppom_hidden_fields_updated',
+ field: e.field,
+ time: new Date(),
+ } );
+ } );
+
+ setTimeout( function () {
+ ppom_check_conditions();
+ }, 500 );
+} );
+
+function ppom_set_default_option( field_id ) {
+ // get product id
+ const product_id = ppom_input_vars.product_id;
+
+ const field = ppom_get_field_meta_by_id( field_id );
+ switch ( field.type ) {
+ // Check if field is
+ case 'radio':
+ jQuery.each( field.options, function ( label, options ) {
+ const opt_id =
+ product_id + '-' + field.data_name + '-' + options.id;
+
+ if ( options.option == field.selected ) {
+ jQuery( '#' + opt_id ).prop( 'checked', true );
+ }
+ } );
+
+ break;
+
+ case 'select':
+ jQuery( '#' + field.data_name ).val( field.selected );
+ break;
+
+ case 'image':
+ jQuery.each( field.images, function ( index, img ) {
+ if ( img.title == field.selected ) {
+ jQuery( '#' + field.data_name + '-' + img.id ).prop(
+ 'checked',
+ true
+ );
+ }
+ } );
+ break;
+
+ case 'checkbox':
+ jQuery.each( field.options, function ( label, options ) {
+ const opt_id =
+ product_id + '-' + field.data_name + '-' + options.id;
+
+ const default_checked = field.checked.split( '\r\n' );
+ if ( jQuery.inArray( options.option, default_checked ) > -1 ) {
+ jQuery( '#' + opt_id ).prop( 'checked', true );
+ }
+ } );
+ break;
+
+ case 'text':
+ case 'date':
+ case 'number':
+ jQuery( '#' + field.data_name ).val( field.default_value );
+ break;
+ }
}
+// Evaluate every configured field condition and toggle the target wrappers.
function ppom_check_conditions() {
-
- jQuery.each(ppom_input_vars.conditions, function(field, condition) {
-
-
- // It will return rules array with True or False
- ppom_field_matched_rules[field] = ppom_get_field_rule_status(condition);
-
- // get length of condition
- var obj_length = Object.keys(condition.rules).length;
-
- // Now check if all rules are valid
- if (condition.bound === 'Any' && ppom_field_matched_rules[field] > 0) {
- ppom_unlock_field_from_condition(field, condition.visibility);
- }
- else if (condition.bound === 'All' && ppom_field_matched_rules[field] == obj_length) {
- ppom_unlock_field_from_condition(field, condition.visibility);
- }
- else {
- ppom_lock_field_from_condition(field, condition.visibility);
- }
-
- });
+ jQuery.each( ppom_input_vars.conditions, function ( field, condition ) {
+ // It will return rules array with True or False
+ ppom_field_matched_rules[ field ] =
+ ppom_get_field_rule_status( condition );
+
+ // get length of condition
+ const obj_length = Object.keys( condition.rules ).length;
+
+ // Now check if all rules are valid
+ if (
+ condition.bound === 'Any' &&
+ ppom_field_matched_rules[ field ] > 0
+ ) {
+ ppom_unlock_field_from_condition( field, condition.visibility );
+ } else if (
+ condition.bound === 'All' &&
+ ppom_field_matched_rules[ field ] == obj_length
+ ) {
+ ppom_unlock_field_from_condition( field, condition.visibility );
+ } else {
+ ppom_lock_field_from_condition( field, condition.visibility );
+ }
+ } );
}
-function ppom_unlock_field_from_condition(field, unlock) {
-
- var classname = '.ppom-input-' + field;
- if (unlock === 'Show') {
- jQuery(classname).show().removeClass('ppom-locked').addClass('ppom-unlocked')
- .trigger({
- type: "ppom_field_shown",
- field: field,
- time: new Date()
- });
- }
- else {
- jQuery(classname).hide().removeClass('ppom-locked').addClass('ppom-unlocked')
- .trigger({
- type: "ppom_field_hidden",
- field: field,
- time: new Date()
- });
- }
+// Showing/hiding a field also needs to broadcast the same lifecycle events
+// used by uploads, pricing, and validation to keep their state consistent.
+function ppom_unlock_field_from_condition( field, unlock ) {
+ const classname = '.ppom-input-' + field;
+ if ( unlock === 'Show' ) {
+ jQuery( classname )
+ .show()
+ .removeClass( 'ppom-locked' )
+ .addClass( 'ppom-unlocked' )
+ .trigger( {
+ type: 'ppom_field_shown',
+ field,
+ time: new Date(),
+ } );
+ } else {
+ jQuery( classname )
+ .hide()
+ .removeClass( 'ppom-locked' )
+ .addClass( 'ppom-unlocked' )
+ .trigger( {
+ type: 'ppom_field_hidden',
+ field,
+ time: new Date(),
+ } );
+ }
}
-function ppom_lock_field_from_condition(field, lock) {
-
- var classname = '.ppom-input-' + field;
- if (lock === 'Show') {
- jQuery(classname).hide().removeClass('ppom-unlocked').addClass('ppom-locked')
- .trigger({
- type: "ppom_field_hidden",
- field: field,
- time: new Date()
- });
- }
- else {
- jQuery(classname).show().removeClass('ppom-unlocked').addClass('ppom-locked')
- .trigger({
- type: "ppom_field_shown",
- field: field,
- time: new Date()
- });
- }
-
- jQuery.event.trigger({
- type: "ppom_field_locked",
- field: field,
- lock: lock,
- time: new Date()
- });
+function ppom_lock_field_from_condition( field, lock ) {
+ const classname = '.ppom-input-' + field;
+ if ( lock === 'Show' ) {
+ jQuery( classname )
+ .hide()
+ .removeClass( 'ppom-unlocked' )
+ .addClass( 'ppom-locked' )
+ .trigger( {
+ type: 'ppom_field_hidden',
+ field,
+ time: new Date(),
+ } );
+ } else {
+ jQuery( classname )
+ .show()
+ .removeClass( 'ppom-unlocked' )
+ .addClass( 'ppom-locked' )
+ .trigger( {
+ type: 'ppom_field_shown',
+ field,
+ time: new Date(),
+ } );
+ }
+
+ jQuery.event.trigger( {
+ type: 'ppom_field_locked',
+ field,
+ lock,
+ time: new Date(),
+ } );
}
-// It will return rules array with True or False
-function ppom_get_field_rule_status(condition) {
-
- var ppom_rules_matched = 0;
- jQuery.each(condition.rules, function(i, rule) {
-
- var element_type = ppom_get_field_type_by_id(rule.elements);
-
- // console.log(element_type);
- switch (rule.operators) {
- case 'is':
- if (element_type === 'checkbox') {
- var element_value = ppom_get_element_value(rule.elements);
- jQuery(element_value).each(function(i, item) {
- if (item === rule.element_values) {
- ppom_rules_matched++;
- }
- });
- }
- else if (element_type === 'image') {
- var element_value = ppom_get_element_value(rule.elements);
- jQuery(element_value).each(function(i, item) {
- if (item === rule.element_values) {
- ppom_rules_matched++;
- }
- });
- }
- else if (ppom_get_element_value(rule.elements) === rule.element_values) {
- ppom_rules_matched++;
- }
- break;
-
- case 'not':
- if (element_type === 'checkbox') {
- var element_value = ppom_get_element_value(rule.elements);
- jQuery(element_value).each(function(i, item) {
- if (item !== rule.element_values) {
- ppom_rules_matched++;
- }
- });
- }
- else if (ppom_get_element_value(rule.elements) !== rule.element_values) {
- ppom_rules_matched++;
- }
- break;
-
- case 'greater than':
- if (element_type === 'checkbox') {
- var element_value = ppom_get_element_value(rule.elements);
- jQuery(element_value).each(function(i, item) {
- if (parseFloat(item) > parseFloat(rule.element_values)) {
- ppom_rules_matched++;
- }
- });
- }
- else if (parseFloat(ppom_get_element_value(rule.elements)) > parseFloat(rule.element_values)) {
- ppom_rules_matched++;
- }
- break;
-
- case 'less than':
- if (element_type === 'checkbox') {
- var element_value = ppom_get_element_value(rule.elements);
- jQuery(element_value).each(function(i, item) {
- if (parseFloat(item) < parseFloat(rule.element_values)) {
- ppom_rules_matched++;
- }
- });
- }
- else if (parseFloat(ppom_get_element_value(rule.elements)) < parseFloat(rule.element_values)) {
- ppom_rules_matched++;
- }
- break;
-
-
- }
- });
-
- return ppom_rules_matched;
+// Count how many rules match for a target field in the legacy condition format.
+function ppom_get_field_rule_status( condition ) {
+ let ppom_rules_matched = 0;
+ jQuery.each( condition.rules, function ( i, rule ) {
+ const element_type = ppom_get_field_type_by_id( rule.elements );
+
+ // console.log(element_type);
+ switch ( rule.operators ) {
+ case 'is':
+ if ( element_type === 'checkbox' ) {
+ var element_value = ppom_get_element_value( rule.elements );
+ jQuery( element_value ).each( function ( i, item ) {
+ if ( item === rule.element_values ) {
+ ppom_rules_matched++;
+ }
+ } );
+ } else if ( element_type === 'image' ) {
+ var element_value = ppom_get_element_value( rule.elements );
+ jQuery( element_value ).each( function ( i, item ) {
+ if ( item === rule.element_values ) {
+ ppom_rules_matched++;
+ }
+ } );
+ } else if (
+ ppom_get_element_value( rule.elements ) ===
+ rule.element_values
+ ) {
+ ppom_rules_matched++;
+ }
+ break;
+
+ case 'not':
+ if ( element_type === 'checkbox' ) {
+ var element_value = ppom_get_element_value( rule.elements );
+ jQuery( element_value ).each( function ( i, item ) {
+ if ( item !== rule.element_values ) {
+ ppom_rules_matched++;
+ }
+ } );
+ } else if (
+ ppom_get_element_value( rule.elements ) !==
+ rule.element_values
+ ) {
+ ppom_rules_matched++;
+ }
+ break;
+
+ case 'greater than':
+ if ( element_type === 'checkbox' ) {
+ var element_value = ppom_get_element_value( rule.elements );
+ jQuery( element_value ).each( function ( i, item ) {
+ if (
+ parseFloat( item ) >
+ parseFloat( rule.element_values )
+ ) {
+ ppom_rules_matched++;
+ }
+ } );
+ } else if (
+ parseFloat( ppom_get_element_value( rule.elements ) ) >
+ parseFloat( rule.element_values )
+ ) {
+ ppom_rules_matched++;
+ }
+ break;
+
+ case 'less than':
+ if ( element_type === 'checkbox' ) {
+ var element_value = ppom_get_element_value( rule.elements );
+ jQuery( element_value ).each( function ( i, item ) {
+ if (
+ parseFloat( item ) <
+ parseFloat( rule.element_values )
+ ) {
+ ppom_rules_matched++;
+ }
+ } );
+ } else if (
+ parseFloat( ppom_get_element_value( rule.elements ) ) <
+ parseFloat( rule.element_values )
+ ) {
+ ppom_rules_matched++;
+ }
+ break;
+ }
+ } );
+
+ return ppom_rules_matched;
}
-// Getting rule element value
-function ppom_get_element_value(field_name) {
-
- var element_type = ppom_get_field_type_by_id(field_name);
- var value_found = '';
- var value_found_cb = [];
-
- switch (element_type) {
-
- case 'select':
- value_found = jQuery('select[name="ppom[fields][' + field_name + ']"]').val();
- break;
-
- case 'radio':
- value_found = jQuery('input[name="ppom[fields][' + field_name + ']"]:checked').val();
- break;
-
- case 'checkbox':
- jQuery('input[name="ppom[fields][' + field_name + '][]"]:checked').each(function(i) {
- value_found_cb[i] = jQuery(this).val();
- });
- break;
-
- case 'image':
- // value_found = jQuery('input[name="ppom[fields]['+field_name+'][]"]:checked').attr('data-label');
- jQuery('input[name="ppom[fields][' + field_name + '][]"]:checked').each(function(i) {
- value_found_cb[i] = jQuery(this).attr('data-label');
- });
- break;
-
- case 'imageselect':
- value_found = jQuery('input[name="ppom[fields][' + field_name + ']"]:checked').attr('data-title');
- break;
-
- }
-
- if (element_type === 'checkbox' || element_type === 'image') {
- return value_found_cb;
- }
-
- return value_found;
+// Normalize legacy field values before comparing them against rule operators.
+function ppom_get_element_value( field_name ) {
+ const element_type = ppom_get_field_type_by_id( field_name );
+ let value_found = '';
+ const value_found_cb = [];
+
+ switch ( element_type ) {
+ case 'select':
+ value_found = jQuery(
+ 'select[name="ppom[fields][' + field_name + ']"]'
+ ).val();
+ break;
+
+ case 'radio':
+ value_found = jQuery(
+ 'input[name="ppom[fields][' + field_name + ']"]:checked'
+ ).val();
+ break;
+
+ case 'checkbox':
+ jQuery(
+ 'input[name="ppom[fields][' + field_name + '][]"]:checked'
+ ).each( function ( i ) {
+ value_found_cb[ i ] = jQuery( this ).val();
+ } );
+ break;
+
+ case 'image':
+ // value_found = jQuery('input[name="ppom[fields]['+field_name+'][]"]:checked').attr('data-label');
+ jQuery(
+ 'input[name="ppom[fields][' + field_name + '][]"]:checked'
+ ).each( function ( i ) {
+ value_found_cb[ i ] = jQuery( this ).attr( 'data-label' );
+ } );
+ break;
+
+ case 'imageselect':
+ value_found = jQuery(
+ 'input[name="ppom[fields][' + field_name + ']"]:checked'
+ ).attr( 'data-title' );
+ break;
+ }
+
+ if ( element_type === 'checkbox' || element_type === 'image' ) {
+ return value_found_cb;
+ }
+
+ return value_found;
}
diff --git a/js/ppom-modal.js b/js/ppom-modal.js
index 8fd17c67..0a723785 100644
--- a/js/ppom-modal.js
+++ b/js/ppom-modal.js
@@ -7,787 +7,890 @@
* Under MIT License
*/
-!(function(root, factory) {
- if (typeof define === 'function' && define.amd) {
- define(['jquery'], function($) {
- return factory(root, $);
- });
- }
- else if (typeof exports === 'object') {
- factory(root, require('jquery'));
- }
- else {
- factory(root, root.jQuery || root.Zepto);
- }
-})(this, function(global, $) {
-
- 'use strict';
-
- /**
- * Name of the plugin
- * @private
- * @const
- * @type {String}
- */
- var PLUGIN_NAME = 'ppom_modal';
-
- /**
- * Namespace for CSS and events
- * @private
- * @const
- * @type {String}
- */
- var NAMESPACE = global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.NAMESPACE || PLUGIN_NAME;
-
- /**
- * Animationstart event with vendor prefixes
- * @private
- * @const
- * @type {String}
- */
- var ANIMATIONSTART_EVENTS = $.map(
- ['animationstart', 'webkitAnimationStart', 'MSAnimationStart', 'oAnimationStart'],
-
- function(eventName) {
- return eventName + '.' + NAMESPACE;
- }
-
- ).join(' ');
-
- /**
- * Animationend event with vendor prefixes
- * @private
- * @const
- * @type {String}
- */
- var ANIMATIONEND_EVENTS = $.map(
- ['animationend', 'webkitAnimationEnd', 'MSAnimationEnd', 'oAnimationEnd'],
-
- function(eventName) {
- return eventName + '.' + NAMESPACE;
- }
-
- ).join(' ');
-
- /**
- * Default settings
- * @private
- * @const
- * @type {Object}
- */
- var DEFAULTS = $.extend({
- hashTracking: true,
- closeOnConfirm: true,
- closeOnCancel: true,
- closeOnEscape: true,
- closeOnOutsideClick: true,
- modifier: '',
- appendTo: ''
- }, global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.DEFAULTS);
-
- /**
- * States of the Remodal
- * @private
- * @const
- * @enum {String}
- */
- var STATES = {
- CLOSING: 'closing',
- CLOSED: 'closed',
- OPENING: 'opening',
- OPENED: 'opened'
- };
-
- /**
- * Reasons of the state change.
- * @private
- * @const
- * @enum {String}
- */
- var STATE_CHANGE_REASONS = {
- CONFIRMATION: 'confirmation',
- CANCELLATION: 'cancellation'
- };
-
- /**
- * Is animation supported?
- * @private
- * @const
- * @type {Boolean}
- */
- var IS_ANIMATION = (function() {
- var style = document.createElement('div').style;
-
- return style.animationName !== undefined ||
- style.WebkitAnimationName !== undefined ||
- style.MozAnimationName !== undefined ||
- style.msAnimationName !== undefined ||
- style.OAnimationName !== undefined;
- })();
-
- /**
- * Is iOS?
- * @private
- * @const
- * @type {Boolean}
- */
- var IS_IOS = /iPad|iPhone|iPod/.test(navigator.platform);
-
- /**
- * Current modal
- * @private
- * @type {Remodal}
- */
- var current;
-
- /**
- * Scrollbar position
- * @private
- * @type {Number}
- */
- var scrollTop;
-
- /**
- * Returns an animation duration
- * @private
- * @param {jQuery} $elem
- * @returns {Number}
- */
- function getAnimationDuration($elem) {
- if (
- IS_ANIMATION &&
- $elem.css('animation-name') === 'none' &&
- $elem.css('-webkit-animation-name') === 'none' &&
- $elem.css('-moz-animation-name') === 'none' &&
- $elem.css('-o-animation-name') === 'none' &&
- $elem.css('-ms-animation-name') === 'none'
- ) {
- return 0;
- }
-
- var duration = $elem.css('animation-duration') ||
- $elem.css('-webkit-animation-duration') ||
- $elem.css('-moz-animation-duration') ||
- $elem.css('-o-animation-duration') ||
- $elem.css('-ms-animation-duration') ||
- '0s';
-
- var delay = $elem.css('animation-delay') ||
- $elem.css('-webkit-animation-delay') ||
- $elem.css('-moz-animation-delay') ||
- $elem.css('-o-animation-delay') ||
- $elem.css('-ms-animation-delay') ||
- '0s';
-
- var iterationCount = $elem.css('animation-iteration-count') ||
- $elem.css('-webkit-animation-iteration-count') ||
- $elem.css('-moz-animation-iteration-count') ||
- $elem.css('-o-animation-iteration-count') ||
- $elem.css('-ms-animation-iteration-count') ||
- '1';
-
- var max;
- var len;
- var num;
- var i;
-
- duration = duration.split(', ');
- delay = delay.split(', ');
- iterationCount = iterationCount.split(', ');
-
- // The 'duration' size is the same as the 'delay' size
- for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) {
- num = parseFloat(duration[i]) * parseInt(iterationCount[i], 10) + parseFloat(delay[i]);
-
- if (num > max) {
- max = num;
- }
- }
-
- return max;
- }
-
- /**
- * Returns a scrollbar width
- * @private
- * @returns {Number}
- */
- function getScrollbarWidth() {
- if ($(document).height() <= $(window).height()) {
- return 0;
- }
-
- var outer = document.createElement('div');
- var inner = document.createElement('div');
- var widthNoScroll;
- var widthWithScroll;
-
- outer.style.visibility = 'hidden';
- outer.style.width = '100px';
- document.body.appendChild(outer);
-
- widthNoScroll = outer.offsetWidth;
-
- // Force scrollbars
- outer.style.overflow = 'scroll';
-
- // Add inner div
- inner.style.width = '100%';
- outer.appendChild(inner);
-
- widthWithScroll = inner.offsetWidth;
-
- // Remove divs
- outer.parentNode.removeChild(outer);
-
- return widthNoScroll - widthWithScroll;
- }
-
- /**
- * Locks the screen
- * @private
- */
- function lockScreen() {
- if (IS_IOS) {
- return;
- }
-
- var $html = $('html');
- var lockedClass = namespacify('is-locked');
- var paddingRight;
- var $body;
-
- if (!$html.hasClass(lockedClass)) {
- $body = $(document.body);
-
- // Zepto does not support '-=', '+=' in the `css` method
- paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth();
-
- $body.css('padding-right', paddingRight + 'px');
- $html.addClass(lockedClass);
- }
- }
-
- /**
- * Unlocks the screen
- * @private
- */
- function unlockScreen() {
- if (IS_IOS) {
- return;
- }
-
- var $html = $('html');
- var lockedClass = namespacify('is-locked');
- var paddingRight;
- var $body;
-
- if ($html.hasClass(lockedClass)) {
- $body = $(document.body);
-
- // Zepto does not support '-=', '+=' in the `css` method
- paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth();
-
- $body.css('padding-right', paddingRight + 'px');
- $html.removeClass(lockedClass);
- }
- }
-
- /**
- * Sets a state for an instance
- * @private
- * @param {Remodal} instance
- * @param {STATES} state
- * @param {Boolean} isSilent If true, Remodal does not trigger events
- * @param {String} Reason of a state change.
- */
- function setState(instance, state, isSilent, reason) {
-
- var newState = namespacify('is', state);
- var allStates = [namespacify('is', STATES.CLOSING),
- namespacify('is', STATES.OPENING),
- namespacify('is', STATES.CLOSED),
- namespacify('is', STATES.OPENED)
- ].join(' ');
-
- instance.$bg
- .removeClass(allStates)
- .addClass(newState);
-
- instance.$overlay
- .removeClass(allStates)
- .addClass(newState);
-
- instance.$wrapper
- .removeClass(allStates)
- .addClass(newState);
-
- instance.$modal
- .removeClass(allStates)
- .addClass(newState);
-
- instance.state = state;
- !isSilent && instance.$modal.trigger({
- type: state,
- reason: reason
- }, [{ reason: reason }]);
- }
-
- /**
- * Synchronizes with the animation
- * @param {Function} doBeforeAnimation
- * @param {Function} doAfterAnimation
- * @param {Remodal} instance
- */
- function syncWithAnimation(doBeforeAnimation, doAfterAnimation, instance) {
- var runningAnimationsCount = 0;
-
- var handleAnimationStart = function(e) {
- if (e.target !== this) {
- return;
- }
-
- runningAnimationsCount++;
- };
-
- var handleAnimationEnd = function(e) {
- if (e.target !== this) {
- return;
- }
-
- if (--runningAnimationsCount === 0) {
-
- // Remove event listeners
- $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
- instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
- });
-
- doAfterAnimation();
- }
- };
-
- $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
- instance[elemName]
- .on(ANIMATIONSTART_EVENTS, handleAnimationStart)
- .on(ANIMATIONEND_EVENTS, handleAnimationEnd);
- });
-
- doBeforeAnimation();
-
- // If the animation is not supported by a browser or its duration is 0
- if (
- getAnimationDuration(instance.$bg) === 0 &&
- getAnimationDuration(instance.$overlay) === 0 &&
- getAnimationDuration(instance.$wrapper) === 0 &&
- getAnimationDuration(instance.$modal) === 0
- ) {
-
- // Remove event listeners
- $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
- instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
- });
-
- doAfterAnimation();
- }
- }
-
- /**
- * Closes immediately
- * @private
- * @param {Remodal} instance
- */
- function halt(instance) {
- if (instance.state === STATES.CLOSED) {
- return;
- }
-
- $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
- instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
- });
-
- instance.$bg.removeClass(instance.settings.modifier);
- instance.$overlay.removeClass(instance.settings.modifier).hide();
- instance.$wrapper.hide();
- unlockScreen();
- setState(instance, STATES.CLOSED, true);
- }
-
- /**
- * Parses a string with options
- * @private
- * @param str
- * @returns {Object}
- */
- function parseOptions(str) {
- var obj = {};
- var arr;
- var len;
- var val;
- var i;
-
- // Remove spaces before and after delimiters
- str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ',');
-
- // Parse a string
- arr = str.split(',');
- for (i = 0, len = arr.length; i < len; i++) {
- arr[i] = arr[i].split(':');
- val = arr[i][1];
-
- // Convert a string value if it is like a boolean
- if (typeof val === 'string' || val instanceof String) {
- val = val === 'true' || (val === 'false' ? false : val);
- }
-
- // Convert a string value if it is like a number
- if (typeof val === 'string' || val instanceof String) {
- val = !isNaN(val) ? +val : val;
- }
-
- obj[arr[i][0]] = val;
- }
-
- return obj;
- }
-
- /**
- * Generates a string separated by dashes and prefixed with NAMESPACE
- * @private
- * @param {...String}
- * @returns {String}
- */
- function namespacify() {
- var result = NAMESPACE;
-
- for (var i = 0; i < arguments.length; ++i) {
- result += '-' + arguments[i];
- }
-
- return result;
- }
-
- /**
- * Handles the hashchange event
- * @private
- * @listens hashchange
- */
- function handleHashChangeEvent() {
- var id = location.hash.replace('#', '');
- var instance;
- var $elem;
-
- if (!id) {
-
- // Check if we have currently opened modal and animation was completed
- if (current && current.state === STATES.OPENED && current.settings.hashTracking) {
- current.close();
- }
- }
- else {
-
- // Catch syntax error if your hash is bad
- try {
- $elem = $(
- '[data-' + PLUGIN_NAME + '-id="' + id + '"]'
- );
- }
- catch (err) {}
-
- if ($elem && $elem.length) {
- instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
-
- if (instance && instance.settings.hashTracking) {
- instance.open();
- }
- }
-
- }
- }
-
- /**
- * Remodal constructor
- * @constructor
- * @param {jQuery} $modal
- * @param {Object} options
- */
- function Remodal($modal, options) {
- // var $body = $(document.body);
- var $body = $modal.parent();
- var $appendTo = $body;
- var remodal = this;
-
- remodal.settings = $.extend({}, DEFAULTS, options);
- remodal.index = $[PLUGIN_NAME].lookup.push(remodal) - 1;
- remodal.state = STATES.CLOSED;
-
- remodal.$overlay = $('.' + namespacify('overlay'));
-
- if (remodal.settings.appendTo !== null && remodal.settings.appendTo.length) {
- $appendTo = $(remodal.settings.appendTo);
- }
-
- if (!remodal.$overlay.length) {
- remodal.$overlay = $(' ').addClass(namespacify('overlay') + ' ' + namespacify('is', STATES.CLOSED)).hide();
- $appendTo.append(remodal.$overlay);
- }
-
- remodal.$bg = $('.' + namespacify('bg')).addClass(namespacify('is', STATES.CLOSED));
-
- remodal.$modal = $modal
- .addClass(
- NAMESPACE + ' ' +
- namespacify('is-initialized') + ' ' +
- remodal.settings.modifier + ' ' +
- namespacify('is', STATES.CLOSED))
- .attr('tabindex', '-1');
-
- remodal.$wrapper = $(' ')
- .addClass(
- namespacify('wrapper') + ' ' +
- remodal.settings.modifier + ' ' +
- namespacify('is', STATES.CLOSED))
- .hide()
- .append(remodal.$modal);
- $appendTo.append(remodal.$wrapper);
-
- // Add the event listener for the close button
- remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="close"]', function(e) {
- e.preventDefault();
-
- remodal.close();
- });
-
- // Add the event listener for the cancel button
- remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="cancel"]', function(e) {
- e.preventDefault();
-
- remodal.$modal.trigger(STATE_CHANGE_REASONS.CANCELLATION);
-
- if (remodal.settings.closeOnCancel) {
- remodal.close(STATE_CHANGE_REASONS.CANCELLATION);
- }
- });
-
- // Add the event listener for the confirm button
- remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="confirm"]', function(e) {
- e.preventDefault();
-
- remodal.$modal.trigger(STATE_CHANGE_REASONS.CONFIRMATION);
-
- if (remodal.settings.closeOnConfirm) {
- remodal.close(STATE_CHANGE_REASONS.CONFIRMATION);
- }
- });
-
- // Add the event listener for the overlay
- remodal.$wrapper.on('click.' + NAMESPACE, function(e) {
- var $target = $(e.target);
-
- if (!$target.hasClass(namespacify('wrapper'))) {
- return;
- }
-
- if (remodal.settings.closeOnOutsideClick) {
- remodal.close();
- }
- });
- }
-
- /**
- * Opens a modal window
- * @public
- */
- Remodal.prototype.open = function() {
- var remodal = this;
- var id;
-
- // Check if the animation was completed
- if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) {
- return;
- }
-
- id = remodal.$modal.attr('data-' + PLUGIN_NAME + '-id');
-
- if (id && remodal.settings.hashTracking) {
- scrollTop = $(window).scrollTop();
- location.hash = id;
- }
-
- if (current && current !== remodal) {
- halt(current);
- }
-
- current = remodal;
- lockScreen();
- remodal.$bg.addClass(remodal.settings.modifier);
- remodal.$overlay.addClass(remodal.settings.modifier).show();
- remodal.$wrapper.show().scrollTop(0);
- remodal.$modal.focus();
-
- syncWithAnimation(
- function() {
- setState(remodal, STATES.OPENING);
- },
-
- function() {
- setState(remodal, STATES.OPENED);
- },
-
- remodal);
- };
-
- /**
- * Closes a modal window
- * @public
- * @param {String} reason
- */
- Remodal.prototype.close = function(reason) {
- var remodal = this;
-
- // Check if the animation was completed
- if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING || remodal.state === STATES.CLOSED) {
- return;
- }
-
- if (
- remodal.settings.hashTracking &&
- remodal.$modal.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1)
- ) {
- location.hash = '';
- $(window).scrollTop(scrollTop);
- }
-
- syncWithAnimation(
- function() {
- setState(remodal, STATES.CLOSING, false, reason);
- },
-
- function() {
- remodal.$bg.removeClass(remodal.settings.modifier);
- remodal.$overlay.removeClass(remodal.settings.modifier).hide();
- remodal.$wrapper.hide();
- unlockScreen();
-
- setState(remodal, STATES.CLOSED, false, reason);
- },
-
- remodal);
- };
-
- /**
- * Returns a current state of a modal
- * @public
- * @returns {STATES}
- */
- Remodal.prototype.getState = function() {
- return this.state;
- };
-
- /**
- * Destroys a modal
- * @public
- */
- Remodal.prototype.destroy = function() {
- var lookup = $[PLUGIN_NAME].lookup;
- var instanceCount;
-
- halt(this);
- this.$wrapper.remove();
-
- delete lookup[this.index];
- instanceCount = $.grep(lookup, function(instance) {
- return !!instance;
- }).length;
-
- if (instanceCount === 0) {
- this.$overlay.remove();
- this.$bg.removeClass(
- namespacify('is', STATES.CLOSING) + ' ' +
- namespacify('is', STATES.OPENING) + ' ' +
- namespacify('is', STATES.CLOSED) + ' ' +
- namespacify('is', STATES.OPENED));
- }
- };
-
- /**
- * Special plugin object for instances
- * @public
- * @type {Object}
- */
- $[PLUGIN_NAME] = {
- lookup: []
- };
-
- /**
- * Plugin constructor
- * @constructor
- * @param {Object} options
- * @returns {JQuery}
- */
- $.fn[PLUGIN_NAME] = function(opts) {
- var instance;
- var $elem;
-
- this.each(function(index, elem) {
- $elem = $(elem);
-
- if ($elem.data(PLUGIN_NAME) == null) {
- instance = new Remodal($elem, opts);
- $elem.data(PLUGIN_NAME, instance.index);
-
- if (
- instance.settings.hashTracking &&
- $elem.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1)
- ) {
- instance.open();
- }
- }
- else {
- instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
- }
- });
-
- return instance;
- };
-
- $(document).ready(function() {
-
- // data-remodal-target opens a modal window with the special Id
- $(document).on('click', '[data-' + PLUGIN_NAME + '-target]', function(e) {
- e.preventDefault();
-
- var elem = e.currentTarget;
- var id = elem.getAttribute('data-' + PLUGIN_NAME + '-target');
- var $target = $('[data-' + PLUGIN_NAME + '-id="' + id + '"]');
-
- $[PLUGIN_NAME].lookup[$target.data(PLUGIN_NAME)].open();
- });
-
- // Auto initialization of modal windows
- // They should have the 'remodal' class attribute
- // Also you can write the `data-remodal-options` attribute to pass params into the modal
- $(document).find('.' + NAMESPACE).each(function(i, container) {
- var $container = $(container);
- var options = $container.data(PLUGIN_NAME + '-options');
-
- if (!options) {
- options = {};
- }
- else if (typeof options === 'string' || options instanceof String) {
- options = parseOptions(options);
- }
-
- $container[PLUGIN_NAME](options);
- });
-
- // Handles the keydown event
- $(document).on('keydown.' + NAMESPACE, function(e) {
- if (current && current.settings.closeOnEscape && current.state === STATES.OPENED && e.keyCode === 27) {
- current.close();
- }
- });
-
- // Handles the hashchange event
- $(window).on('hashchange.' + NAMESPACE, handleHashChangeEvent);
- });
-});
+! ( function ( root, factory ) {
+ if ( typeof define === 'function' && define.amd ) {
+ define( [ 'jquery' ], function ( $ ) {
+ return factory( root, $ );
+ } );
+ } else if ( typeof exports === 'object' ) {
+ factory( root, require( 'jquery' ) );
+ } else {
+ factory( root, root.jQuery || root.Zepto );
+ }
+} )( this, function ( global, $ ) {
+ 'use strict';
+
+ /**
+ * Name of the plugin
+ * @private
+ * @constant
+ * @type {string}
+ */
+ const PLUGIN_NAME = 'ppom_modal';
+
+ /**
+ * Namespace for CSS and events
+ * @private
+ * @constant
+ * @type {string}
+ */
+ const NAMESPACE =
+ ( global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.NAMESPACE ) ||
+ PLUGIN_NAME;
+
+ /**
+ * Animationstart event with vendor prefixes
+ * @private
+ * @constant
+ * @type {string}
+ */
+ const ANIMATIONSTART_EVENTS = $.map(
+ [
+ 'animationstart',
+ 'webkitAnimationStart',
+ 'MSAnimationStart',
+ 'oAnimationStart',
+ ],
+
+ function ( eventName ) {
+ return eventName + '.' + NAMESPACE;
+ }
+ ).join( ' ' );
+
+ /**
+ * Animationend event with vendor prefixes
+ * @private
+ * @constant
+ * @type {string}
+ */
+ const ANIMATIONEND_EVENTS = $.map(
+ [
+ 'animationend',
+ 'webkitAnimationEnd',
+ 'MSAnimationEnd',
+ 'oAnimationEnd',
+ ],
+
+ function ( eventName ) {
+ return eventName + '.' + NAMESPACE;
+ }
+ ).join( ' ' );
+
+ /**
+ * Default settings
+ * @private
+ * @constant
+ * @type {Object}
+ */
+ const DEFAULTS = $.extend(
+ {
+ hashTracking: true,
+ closeOnConfirm: true,
+ closeOnCancel: true,
+ closeOnEscape: true,
+ closeOnOutsideClick: true,
+ modifier: '',
+ appendTo: '',
+ },
+ global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.DEFAULTS
+ );
+
+ /**
+ * States of the Remodal
+ * @private
+ * @constant
+ * @enum {string}
+ */
+ const STATES = {
+ CLOSING: 'closing',
+ CLOSED: 'closed',
+ OPENING: 'opening',
+ OPENED: 'opened',
+ };
+
+ /**
+ * Reasons of the state change.
+ * @private
+ * @constant
+ * @enum {string}
+ */
+ const STATE_CHANGE_REASONS = {
+ CONFIRMATION: 'confirmation',
+ CANCELLATION: 'cancellation',
+ };
+
+ /**
+ * Is animation supported?
+ * @private
+ * @constant
+ * @type {boolean}
+ */
+ const IS_ANIMATION = ( function () {
+ const style = document.createElement( 'div' ).style;
+
+ return (
+ style.animationName !== undefined ||
+ style.WebkitAnimationName !== undefined ||
+ style.MozAnimationName !== undefined ||
+ style.msAnimationName !== undefined ||
+ style.OAnimationName !== undefined
+ );
+ } )();
+
+ /**
+ * Is iOS?
+ * @private
+ * @constant
+ * @type {boolean}
+ */
+ const IS_IOS = /iPad|iPhone|iPod/.test( navigator.platform );
+
+ /**
+ * Current modal
+ * @private
+ * @type {Remodal}
+ */
+ let current;
+
+ /**
+ * Scrollbar position
+ * @private
+ * @type {number}
+ */
+ let scrollTop;
+
+ /**
+ * Returns an animation duration
+ * @private
+ * @param {jQuery} $elem
+ * @return {number}
+ */
+ function getAnimationDuration( $elem ) {
+ if (
+ IS_ANIMATION &&
+ $elem.css( 'animation-name' ) === 'none' &&
+ $elem.css( '-webkit-animation-name' ) === 'none' &&
+ $elem.css( '-moz-animation-name' ) === 'none' &&
+ $elem.css( '-o-animation-name' ) === 'none' &&
+ $elem.css( '-ms-animation-name' ) === 'none'
+ ) {
+ return 0;
+ }
+
+ let duration =
+ $elem.css( 'animation-duration' ) ||
+ $elem.css( '-webkit-animation-duration' ) ||
+ $elem.css( '-moz-animation-duration' ) ||
+ $elem.css( '-o-animation-duration' ) ||
+ $elem.css( '-ms-animation-duration' ) ||
+ '0s';
+
+ let delay =
+ $elem.css( 'animation-delay' ) ||
+ $elem.css( '-webkit-animation-delay' ) ||
+ $elem.css( '-moz-animation-delay' ) ||
+ $elem.css( '-o-animation-delay' ) ||
+ $elem.css( '-ms-animation-delay' ) ||
+ '0s';
+
+ let iterationCount =
+ $elem.css( 'animation-iteration-count' ) ||
+ $elem.css( '-webkit-animation-iteration-count' ) ||
+ $elem.css( '-moz-animation-iteration-count' ) ||
+ $elem.css( '-o-animation-iteration-count' ) ||
+ $elem.css( '-ms-animation-iteration-count' ) ||
+ '1';
+
+ let max;
+ let len;
+ let num;
+ let i;
+
+ duration = duration.split( ', ' );
+ delay = delay.split( ', ' );
+ iterationCount = iterationCount.split( ', ' );
+
+ // The 'duration' size is the same as the 'delay' size
+ for (
+ i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY;
+ i < len;
+ i++
+ ) {
+ num =
+ parseFloat( duration[ i ] ) *
+ parseInt( iterationCount[ i ], 10 ) +
+ parseFloat( delay[ i ] );
+
+ if ( num > max ) {
+ max = num;
+ }
+ }
+
+ return max;
+ }
+
+ /**
+ * Returns a scrollbar width
+ * @private
+ * @return {number}
+ */
+ function getScrollbarWidth() {
+ if ( $( document ).height() <= $( window ).height() ) {
+ return 0;
+ }
+
+ const outer = document.createElement( 'div' );
+ const inner = document.createElement( 'div' );
+ let widthNoScroll;
+ let widthWithScroll;
+
+ outer.style.visibility = 'hidden';
+ outer.style.width = '100px';
+ document.body.appendChild( outer );
+
+ widthNoScroll = outer.offsetWidth;
+
+ // Force scrollbars
+ outer.style.overflow = 'scroll';
+
+ // Add inner div
+ inner.style.width = '100%';
+ outer.appendChild( inner );
+
+ widthWithScroll = inner.offsetWidth;
+
+ // Remove divs
+ outer.parentNode.removeChild( outer );
+
+ return widthNoScroll - widthWithScroll;
+ }
+
+ /**
+ * Locks the screen
+ * @private
+ */
+ function lockScreen() {
+ if ( IS_IOS ) {
+ return;
+ }
+
+ const $html = $( 'html' );
+ const lockedClass = namespacify( 'is-locked' );
+ let paddingRight;
+ let $body;
+
+ if ( ! $html.hasClass( lockedClass ) ) {
+ $body = $( document.body );
+
+ // Zepto does not support '-=', '+=' in the `css` method
+ paddingRight =
+ parseInt( $body.css( 'padding-right' ), 10 ) +
+ getScrollbarWidth();
+
+ $body.css( 'padding-right', paddingRight + 'px' );
+ $html.addClass( lockedClass );
+ }
+ }
+
+ /**
+ * Unlocks the screen
+ * @private
+ */
+ function unlockScreen() {
+ if ( IS_IOS ) {
+ return;
+ }
+
+ const $html = $( 'html' );
+ const lockedClass = namespacify( 'is-locked' );
+ let paddingRight;
+ let $body;
+
+ if ( $html.hasClass( lockedClass ) ) {
+ $body = $( document.body );
+
+ // Zepto does not support '-=', '+=' in the `css` method
+ paddingRight =
+ parseInt( $body.css( 'padding-right' ), 10 ) -
+ getScrollbarWidth();
+
+ $body.css( 'padding-right', paddingRight + 'px' );
+ $html.removeClass( lockedClass );
+ }
+ }
+
+ /**
+ * Sets a state for an instance
+ * @private
+ * @param {Remodal} instance
+ * @param {STATES} state
+ * @param {boolean} isSilent If true, Remodal does not trigger events
+ * @param reason
+ * @param {string} Reason of a state change.
+ */
+ function setState( instance, state, isSilent, reason ) {
+ const newState = namespacify( 'is', state );
+ const allStates = [
+ namespacify( 'is', STATES.CLOSING ),
+ namespacify( 'is', STATES.OPENING ),
+ namespacify( 'is', STATES.CLOSED ),
+ namespacify( 'is', STATES.OPENED ),
+ ].join( ' ' );
+
+ instance.$bg.removeClass( allStates ).addClass( newState );
+
+ instance.$overlay.removeClass( allStates ).addClass( newState );
+
+ instance.$wrapper.removeClass( allStates ).addClass( newState );
+
+ instance.$modal.removeClass( allStates ).addClass( newState );
+
+ instance.state = state;
+ ! isSilent &&
+ instance.$modal.trigger(
+ {
+ type: state,
+ reason,
+ },
+ [ { reason } ]
+ );
+ }
+
+ /**
+ * Synchronizes with the animation
+ * @param {Function} doBeforeAnimation
+ * @param {Function} doAfterAnimation
+ * @param {Remodal} instance
+ */
+ function syncWithAnimation(
+ doBeforeAnimation,
+ doAfterAnimation,
+ instance
+ ) {
+ let runningAnimationsCount = 0;
+
+ const handleAnimationStart = function ( e ) {
+ if ( e.target !== this ) {
+ return;
+ }
+
+ runningAnimationsCount++;
+ };
+
+ const handleAnimationEnd = function ( e ) {
+ if ( e.target !== this ) {
+ return;
+ }
+
+ if ( --runningAnimationsCount === 0 ) {
+ // Remove event listeners
+ $.each(
+ [ '$bg', '$overlay', '$wrapper', '$modal' ],
+ function ( index, elemName ) {
+ instance[ elemName ].off(
+ ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS
+ );
+ }
+ );
+
+ doAfterAnimation();
+ }
+ };
+
+ $.each(
+ [ '$bg', '$overlay', '$wrapper', '$modal' ],
+ function ( index, elemName ) {
+ instance[ elemName ]
+ .on( ANIMATIONSTART_EVENTS, handleAnimationStart )
+ .on( ANIMATIONEND_EVENTS, handleAnimationEnd );
+ }
+ );
+
+ doBeforeAnimation();
+
+ // If the animation is not supported by a browser or its duration is 0
+ if (
+ getAnimationDuration( instance.$bg ) === 0 &&
+ getAnimationDuration( instance.$overlay ) === 0 &&
+ getAnimationDuration( instance.$wrapper ) === 0 &&
+ getAnimationDuration( instance.$modal ) === 0
+ ) {
+ // Remove event listeners
+ $.each(
+ [ '$bg', '$overlay', '$wrapper', '$modal' ],
+ function ( index, elemName ) {
+ instance[ elemName ].off(
+ ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS
+ );
+ }
+ );
+
+ doAfterAnimation();
+ }
+ }
+
+ /**
+ * Closes immediately
+ * @private
+ * @param {Remodal} instance
+ */
+ function halt( instance ) {
+ if ( instance.state === STATES.CLOSED ) {
+ return;
+ }
+
+ $.each(
+ [ '$bg', '$overlay', '$wrapper', '$modal' ],
+ function ( index, elemName ) {
+ instance[ elemName ].off(
+ ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS
+ );
+ }
+ );
+
+ instance.$bg.removeClass( instance.settings.modifier );
+ instance.$overlay.removeClass( instance.settings.modifier ).hide();
+ instance.$wrapper.hide();
+ unlockScreen();
+ setState( instance, STATES.CLOSED, true );
+ }
+
+ /**
+ * Parses a string with options
+ * @private
+ * @param str
+ * @return {Object}
+ */
+ function parseOptions( str ) {
+ const obj = {};
+ let arr;
+ let len;
+ let val;
+ let i;
+
+ // Remove spaces before and after delimiters
+ str = str.replace( /\s*:\s*/g, ':' ).replace( /\s*,\s*/g, ',' );
+
+ // Parse a string
+ arr = str.split( ',' );
+ for ( i = 0, len = arr.length; i < len; i++ ) {
+ arr[ i ] = arr[ i ].split( ':' );
+ val = arr[ i ][ 1 ];
+
+ // Convert a string value if it is like a boolean
+ if ( typeof val === 'string' || val instanceof String ) {
+ val = val === 'true' || ( val === 'false' ? false : val );
+ }
+
+ // Convert a string value if it is like a number
+ if ( typeof val === 'string' || val instanceof String ) {
+ val = ! isNaN( val ) ? +val : val;
+ }
+
+ obj[ arr[ i ][ 0 ] ] = val;
+ }
+
+ return obj;
+ }
+
+ /**
+ * Generates a string separated by dashes and prefixed with NAMESPACE
+ * @private
+ * @param {...string}
+ * @return {string}
+ */
+ function namespacify() {
+ let result = NAMESPACE;
+
+ for ( let i = 0; i < arguments.length; ++i ) {
+ result += '-' + arguments[ i ];
+ }
+
+ return result;
+ }
+
+ /**
+ * Handles the hashchange event
+ * @private
+ * @listens hashchange
+ */
+ function handleHashChangeEvent() {
+ const id = location.hash.replace( '#', '' );
+ let instance;
+ let $elem;
+
+ if ( ! id ) {
+ // Check if we have currently opened modal and animation was completed
+ if (
+ current &&
+ current.state === STATES.OPENED &&
+ current.settings.hashTracking
+ ) {
+ current.close();
+ }
+ } else {
+ // Catch syntax error if your hash is bad
+ try {
+ $elem = $( '[data-' + PLUGIN_NAME + '-id="' + id + '"]' );
+ } catch ( err ) {}
+
+ if ( $elem && $elem.length ) {
+ instance = $[ PLUGIN_NAME ].lookup[ $elem.data( PLUGIN_NAME ) ];
+
+ if ( instance && instance.settings.hashTracking ) {
+ instance.open();
+ }
+ }
+ }
+ }
+
+ /**
+ * Remodal constructor
+ * @class
+ * @param {jQuery} $modal
+ * @param {Object} options
+ */
+ function Remodal( $modal, options ) {
+ // var $body = $(document.body);
+ const $body = $modal.parent();
+ let $appendTo = $body;
+ const remodal = this;
+
+ remodal.settings = $.extend( {}, DEFAULTS, options );
+ remodal.index = $[ PLUGIN_NAME ].lookup.push( remodal ) - 1;
+ remodal.state = STATES.CLOSED;
+
+ remodal.$overlay = $( '.' + namespacify( 'overlay' ) );
+
+ if (
+ remodal.settings.appendTo !== null &&
+ remodal.settings.appendTo.length
+ ) {
+ $appendTo = $( remodal.settings.appendTo );
+ }
+
+ if ( ! remodal.$overlay.length ) {
+ remodal.$overlay = $( ' ' )
+ .addClass(
+ namespacify( 'overlay' ) +
+ ' ' +
+ namespacify( 'is', STATES.CLOSED )
+ )
+ .hide();
+ $appendTo.append( remodal.$overlay );
+ }
+
+ remodal.$bg = $( '.' + namespacify( 'bg' ) ).addClass(
+ namespacify( 'is', STATES.CLOSED )
+ );
+
+ remodal.$modal = $modal
+ .addClass(
+ NAMESPACE +
+ ' ' +
+ namespacify( 'is-initialized' ) +
+ ' ' +
+ remodal.settings.modifier +
+ ' ' +
+ namespacify( 'is', STATES.CLOSED )
+ )
+ .attr( 'tabindex', '-1' );
+
+ remodal.$wrapper = $( ' ' )
+ .addClass(
+ namespacify( 'wrapper' ) +
+ ' ' +
+ remodal.settings.modifier +
+ ' ' +
+ namespacify( 'is', STATES.CLOSED )
+ )
+ .hide()
+ .append( remodal.$modal );
+ $appendTo.append( remodal.$wrapper );
+
+ // Add the event listener for the close button
+ remodal.$wrapper.on(
+ 'click.' + NAMESPACE,
+ '[data-' + PLUGIN_NAME + '-action="close"]',
+ function ( e ) {
+ e.preventDefault();
+
+ remodal.close();
+ }
+ );
+
+ // Add the event listener for the cancel button
+ remodal.$wrapper.on(
+ 'click.' + NAMESPACE,
+ '[data-' + PLUGIN_NAME + '-action="cancel"]',
+ function ( e ) {
+ e.preventDefault();
+
+ remodal.$modal.trigger( STATE_CHANGE_REASONS.CANCELLATION );
+
+ if ( remodal.settings.closeOnCancel ) {
+ remodal.close( STATE_CHANGE_REASONS.CANCELLATION );
+ }
+ }
+ );
+
+ // Add the event listener for the confirm button
+ remodal.$wrapper.on(
+ 'click.' + NAMESPACE,
+ '[data-' + PLUGIN_NAME + '-action="confirm"]',
+ function ( e ) {
+ e.preventDefault();
+
+ remodal.$modal.trigger( STATE_CHANGE_REASONS.CONFIRMATION );
+
+ if ( remodal.settings.closeOnConfirm ) {
+ remodal.close( STATE_CHANGE_REASONS.CONFIRMATION );
+ }
+ }
+ );
+
+ // Add the event listener for the overlay
+ remodal.$wrapper.on( 'click.' + NAMESPACE, function ( e ) {
+ const $target = $( e.target );
+
+ if ( ! $target.hasClass( namespacify( 'wrapper' ) ) ) {
+ return;
+ }
+
+ if ( remodal.settings.closeOnOutsideClick ) {
+ remodal.close();
+ }
+ } );
+ }
+
+ /**
+ * Opens a modal window
+ * @public
+ */
+ Remodal.prototype.open = function () {
+ const remodal = this;
+ let id;
+
+ // Check if the animation was completed
+ if (
+ remodal.state === STATES.OPENING ||
+ remodal.state === STATES.CLOSING
+ ) {
+ return;
+ }
+
+ id = remodal.$modal.attr( 'data-' + PLUGIN_NAME + '-id' );
+
+ if ( id && remodal.settings.hashTracking ) {
+ scrollTop = $( window ).scrollTop();
+ location.hash = id;
+ }
+
+ if ( current && current !== remodal ) {
+ halt( current );
+ }
+
+ current = remodal;
+ lockScreen();
+ remodal.$bg.addClass( remodal.settings.modifier );
+ remodal.$overlay.addClass( remodal.settings.modifier ).show();
+ remodal.$wrapper.show().scrollTop( 0 );
+ remodal.$modal.focus();
+
+ syncWithAnimation(
+ function () {
+ setState( remodal, STATES.OPENING );
+ },
+
+ function () {
+ setState( remodal, STATES.OPENED );
+ },
+
+ remodal
+ );
+ };
+
+ /**
+ * Closes a modal window
+ * @public
+ * @param {string} reason
+ */
+ Remodal.prototype.close = function ( reason ) {
+ const remodal = this;
+
+ // Check if the animation was completed
+ if (
+ remodal.state === STATES.OPENING ||
+ remodal.state === STATES.CLOSING ||
+ remodal.state === STATES.CLOSED
+ ) {
+ return;
+ }
+
+ if (
+ remodal.settings.hashTracking &&
+ remodal.$modal.attr( 'data-' + PLUGIN_NAME + '-id' ) ===
+ location.hash.substr( 1 )
+ ) {
+ location.hash = '';
+ $( window ).scrollTop( scrollTop );
+ }
+
+ syncWithAnimation(
+ function () {
+ setState( remodal, STATES.CLOSING, false, reason );
+ },
+
+ function () {
+ remodal.$bg.removeClass( remodal.settings.modifier );
+ remodal.$overlay
+ .removeClass( remodal.settings.modifier )
+ .hide();
+ remodal.$wrapper.hide();
+ unlockScreen();
+
+ setState( remodal, STATES.CLOSED, false, reason );
+ },
+
+ remodal
+ );
+ };
+
+ /**
+ * Returns a current state of a modal
+ * @public
+ * @return {STATES}
+ */
+ Remodal.prototype.getState = function () {
+ return this.state;
+ };
+
+ /**
+ * Destroys a modal
+ * @public
+ */
+ Remodal.prototype.destroy = function () {
+ const lookup = $[ PLUGIN_NAME ].lookup;
+ let instanceCount;
+
+ halt( this );
+ this.$wrapper.remove();
+
+ delete lookup[ this.index ];
+ instanceCount = $.grep( lookup, function ( instance ) {
+ return !! instance;
+ } ).length;
+
+ if ( instanceCount === 0 ) {
+ this.$overlay.remove();
+ this.$bg.removeClass(
+ namespacify( 'is', STATES.CLOSING ) +
+ ' ' +
+ namespacify( 'is', STATES.OPENING ) +
+ ' ' +
+ namespacify( 'is', STATES.CLOSED ) +
+ ' ' +
+ namespacify( 'is', STATES.OPENED )
+ );
+ }
+ };
+
+ /**
+ * Special plugin object for instances
+ * @public
+ * @type {Object}
+ */
+ $[ PLUGIN_NAME ] = {
+ lookup: [],
+ };
+
+ /**
+ * Plugin constructor
+ * @class
+ * @param opts
+ * @param {Object} options
+ * @return {JQuery}
+ */
+ $.fn[ PLUGIN_NAME ] = function ( opts ) {
+ let instance;
+ let $elem;
+
+ this.each( function ( index, elem ) {
+ $elem = $( elem );
+
+ if ( $elem.data( PLUGIN_NAME ) == null ) {
+ instance = new Remodal( $elem, opts );
+ $elem.data( PLUGIN_NAME, instance.index );
+
+ if (
+ instance.settings.hashTracking &&
+ $elem.attr( 'data-' + PLUGIN_NAME + '-id' ) ===
+ location.hash.substr( 1 )
+ ) {
+ instance.open();
+ }
+ } else {
+ instance = $[ PLUGIN_NAME ].lookup[ $elem.data( PLUGIN_NAME ) ];
+ }
+ } );
+
+ return instance;
+ };
+
+ $( document ).ready( function () {
+ // data-remodal-target opens a modal window with the special Id
+ $( document ).on(
+ 'click',
+ '[data-' + PLUGIN_NAME + '-target]',
+ function ( e ) {
+ e.preventDefault();
+
+ const elem = e.currentTarget;
+ const id = elem.getAttribute(
+ 'data-' + PLUGIN_NAME + '-target'
+ );
+ const $target = $(
+ '[data-' + PLUGIN_NAME + '-id="' + id + '"]'
+ );
+
+ $[ PLUGIN_NAME ].lookup[ $target.data( PLUGIN_NAME ) ].open();
+ }
+ );
+
+ // Auto initialization of modal windows
+ // They should have the 'remodal' class attribute
+ // Also you can write the `data-remodal-options` attribute to pass params into the modal
+ $( document )
+ .find( '.' + NAMESPACE )
+ .each( function ( i, container ) {
+ const $container = $( container );
+ let options = $container.data( PLUGIN_NAME + '-options' );
+
+ if ( ! options ) {
+ options = {};
+ } else if (
+ typeof options === 'string' ||
+ options instanceof String
+ ) {
+ options = parseOptions( options );
+ }
+
+ $container[ PLUGIN_NAME ]( options );
+ } );
+
+ // Handles the keydown event
+ $( document ).on( 'keydown.' + NAMESPACE, function ( e ) {
+ if (
+ current &&
+ current.settings.closeOnEscape &&
+ current.state === STATES.OPENED &&
+ e.keyCode === 27
+ ) {
+ current.close();
+ }
+ } );
+
+ // Handles the hashchange event
+ $( window ).on( 'hashchange.' + NAMESPACE, handleHashChangeEvent );
+ } );
+} );
diff --git a/js/ppom-plusminus.js b/js/ppom-plusminus.js
index 12be5864..b632c92c 100644
--- a/js/ppom-plusminus.js
+++ b/js/ppom-plusminus.js
@@ -1,156 +1,163 @@
-(function ($) {
- $.fn.niceNumber = function(options) {
- var settings = $.extend({
- autoSize: true,
- autoSizeBuffer: 1,
- buttonDecrement: '-',
- buttonIncrement: "+",
- buttonPosition: 'around'
- }, options);
+( function ( $ ) {
+ $.fn.niceNumber = function ( options ) {
+ const settings = $.extend(
+ {
+ autoSize: true,
+ autoSizeBuffer: 1,
+ buttonDecrement: '-',
+ buttonIncrement: '+',
+ buttonPosition: 'around',
+ },
+ options
+ );
- return this.each(function(){
- var currentInput = this,
- $currentInput = $(currentInput),
- attrMax = null,
- attrMin = null;
+ return this.each( function () {
+ let currentInput = this,
+ $currentInput = $( currentInput ),
+ attrMax = null,
+ attrMin = null;
- // Handle max and min values
- if (
- typeof $currentInput.attr('max') !== typeof undefined
- && $currentInput.attr('max') !== false
- ) {
- attrMax = parseFloat($currentInput.attr('max'));
- }
+ // Handle max and min values
+ if (
+ typeof $currentInput.attr( 'max' ) !== typeof undefined &&
+ $currentInput.attr( 'max' ) !== false
+ ) {
+ attrMax = parseFloat( $currentInput.attr( 'max' ) );
+ }
- if (
- typeof $currentInput.attr('min') !== typeof undefined
- && $currentInput.attr('min') !== false
- ) {
- attrMin = parseFloat($currentInput.attr('min'));
- }
+ if (
+ typeof $currentInput.attr( 'min' ) !== typeof undefined &&
+ $currentInput.attr( 'min' ) !== false
+ ) {
+ attrMin = parseFloat( $currentInput.attr( 'min' ) );
+ }
- // if (isNaN(attrMax)) {
- // attrMax = 44;
- // console.log(attrMax);
- // }
+ // if (isNaN(attrMax)) {
+ // attrMax = 44;
+ // console.log(attrMax);
+ // }
- // if (isNaN(attrMin)) {
- // attrMin = 0;
- // console.log(attrMin);
- // }
+ // if (isNaN(attrMin)) {
+ // attrMin = 0;
+ // console.log(attrMin);
+ // }
- // Fix issue with initial value being < min
- //console.log(currentInput.value);
- if (
- attrMin
- && !currentInput.value
- ) {
- $currentInput.val(attrMin);
- }
+ // Fix issue with initial value being < min
+ //console.log(currentInput.value);
+ if ( attrMin && ! currentInput.value ) {
+ $currentInput.val( attrMin );
+ }
- // Generate container
- var $inputContainer = $(' ',{
- class: 'ppom-number-plusminus'
- })
- .insertAfter(currentInput);
+ // Generate container
+ const $inputContainer = $( ' ', {
+ class: 'ppom-number-plusminus',
+ } ).insertAfter( currentInput );
- // Generate interval (object so it is passed by reference)
- var interval = {};
+ // Generate interval (object so it is passed by reference)
+ const interval = {};
- // Generate buttons
- var $minusButton = $(' ')
- .attr('type', 'button')
- .html(settings.buttonDecrement)
- .on('mousedown mouseup mouseleave', function(event){
- changeInterval(event.type, interval, function(){
- if (
- attrMin == null || isNaN(attrMin)
- || attrMin < parseFloat(currentInput.value)
- ) {
- currentInput.value--;
- }
- });
+ // Generate buttons
+ const $minusButton = $( ' ' )
+ .attr( 'type', 'button' )
+ .html( settings.buttonDecrement )
+ .on( 'mousedown mouseup mouseleave', function ( event ) {
+ changeInterval( event.type, interval, function () {
+ if (
+ attrMin == null ||
+ isNaN( attrMin ) ||
+ attrMin < parseFloat( currentInput.value )
+ ) {
+ currentInput.value--;
+ }
+ } );
- // Trigger the input event here to avoid event spam
- if (
- event.type == 'mouseup'
- || event.type == 'mouseleave'
- ) {
- $currentInput.trigger('change');
- }
- });
+ // Trigger the input event here to avoid event spam
+ if (
+ event.type == 'mouseup' ||
+ event.type == 'mouseleave'
+ ) {
+ $currentInput.trigger( 'change' );
+ }
+ } );
- var $plusButton = $(' ')
- .attr('type', 'button')
- .html(settings.buttonIncrement)
- .on('mousedown mouseup mouseleave', function(event){
- changeInterval(event.type, interval, function(){
- if (
- attrMax == null || isNaN(attrMax)
- || attrMax > parseFloat(currentInput.value)
- ) {
- currentInput.value++;
- }
- });
+ const $plusButton = $( ' ' )
+ .attr( 'type', 'button' )
+ .html( settings.buttonIncrement )
+ .on( 'mousedown mouseup mouseleave', function ( event ) {
+ changeInterval( event.type, interval, function () {
+ if (
+ attrMax == null ||
+ isNaN( attrMax ) ||
+ attrMax > parseFloat( currentInput.value )
+ ) {
+ currentInput.value++;
+ }
+ } );
- // Trigger the input event here to avoid event spam
- if (
- event.type == 'mouseup'
- || event.type == 'mouseleave'
- ) {
- $currentInput.trigger('change');
- }
- });
+ // Trigger the input event here to avoid event spam
+ if (
+ event.type == 'mouseup' ||
+ event.type == 'mouseleave'
+ ) {
+ $currentInput.trigger( 'change' );
+ }
+ } );
- // Append elements
- switch (settings.buttonPosition) {
- case 'left':
- $minusButton.appendTo($inputContainer);
- $plusButton.appendTo($inputContainer);
- $currentInput.appendTo($inputContainer);
- break;
- case 'right':
- $currentInput.appendTo($inputContainer);
- $minusButton.appendTo($inputContainer);
- $plusButton.appendTo($inputContainer);
- break;
- case 'around':
- default:
- $minusButton.appendTo($inputContainer);
- $currentInput.appendTo($inputContainer);
- $plusButton.appendTo($inputContainer);
- break;
- }
+ // Append elements
+ switch ( settings.buttonPosition ) {
+ case 'left':
+ $minusButton.appendTo( $inputContainer );
+ $plusButton.appendTo( $inputContainer );
+ $currentInput.appendTo( $inputContainer );
+ break;
+ case 'right':
+ $currentInput.appendTo( $inputContainer );
+ $minusButton.appendTo( $inputContainer );
+ $plusButton.appendTo( $inputContainer );
+ break;
+ case 'around':
+ default:
+ $minusButton.appendTo( $inputContainer );
+ $currentInput.appendTo( $inputContainer );
+ $plusButton.appendTo( $inputContainer );
+ break;
+ }
- // Nicely size input
- if (settings.autoSize) {
- $currentInput.width(
- $currentInput.val().length+settings.autoSizeBuffer+"ch"
- );
- $currentInput.on('keyup input',function(){
- $currentInput.animate({
- 'width': $currentInput.val().length+settings.autoSizeBuffer+"ch"
- }, 200);
- });
- }
- });
- };
+ // Nicely size input
+ if ( settings.autoSize ) {
+ $currentInput.width(
+ $currentInput.val().length + settings.autoSizeBuffer + 'ch'
+ );
+ $currentInput.on( 'keyup input', function () {
+ $currentInput.animate(
+ {
+ width:
+ $currentInput.val().length +
+ settings.autoSizeBuffer +
+ 'ch',
+ },
+ 200
+ );
+ } );
+ }
+ } );
+ };
- function changeInterval(eventType, interval, callback) {
- if (eventType == "mousedown") {
- interval.timeout = setTimeout(function(){
- interval.actualInterval = setInterval(function(){
- callback();
- }, 100);
- }, 200);
- callback();
- } else {
- if (interval.timeout) {
- clearTimeout(interval.timeout);
- }
- if (interval.actualInterval) {
- clearInterval(interval.actualInterval);
- }
- }
- }
-}(jQuery));
+ function changeInterval( eventType, interval, callback ) {
+ if ( eventType == 'mousedown' ) {
+ interval.timeout = setTimeout( function () {
+ interval.actualInterval = setInterval( function () {
+ callback();
+ }, 100 );
+ }, 200 );
+ callback();
+ } else {
+ if ( interval.timeout ) {
+ clearTimeout( interval.timeout );
+ }
+ if ( interval.actualInterval ) {
+ clearInterval( interval.actualInterval );
+ }
+ }
+ }
+} )( jQuery );
diff --git a/js/ppom-simple-popup.js b/js/ppom-simple-popup.js
index 2cf07f6e..670c3e67 100644
--- a/js/ppom-simple-popup.js
+++ b/js/ppom-simple-popup.js
@@ -1,21 +1,30 @@
/*
- * jQuery Simple Popup Window Plugin 1.0
+ * PPOM frontend popup bridge.
+ *
+ * This wraps a lightweight popup pattern used by some frontend field flows and
+ * also bootstraps Tooltipster-based help tooltips when tooltip settings were
+ * localized for the current product form.
+ *
+ * @see ppom_setup_file_upload_input in js/file-upload.js
+ * @see imageTooltip in js/image-tooltip.js
*/
'use strict';
-(function($) {
-
- // Tooltip Init.
- var tooltipConfig = 'undefined' !== typeof ppom_tooltip_vars ? ppom_tooltip_vars : {};
- var tooltip_position = tooltipConfig.ppom_tooltip_position;
- var tooltip_trigger = tooltipConfig.ppom_tooltip_trigger;
- var tooltip_animation = tooltipConfig.ppom_tooltip_animation;
- var tooltip_maxwidth = tooltipConfig.ppom_tooltip_maxwidth;
- var tooltip_borderclr = tooltipConfig.ppom_tooltip_borderclr;
- var tooltip_bgclr = tooltipConfig.ppom_tooltip_bgclr;
- var tooltip_txtclr = tooltipConfig.ppom_tooltip_txtclr;
- var tooltip_interactive = (tooltipConfig.ppom_tooltip_interactive == 'yes') ? true : false;
+( function ( $ ) {
+ // Tooltipster settings are localized from PHP per product/form instance so
+ // the same script can respect store-specific trigger, width, and color rules.
+ const tooltipConfig =
+ 'undefined' !== typeof ppom_tooltip_vars ? ppom_tooltip_vars : {};
+ const tooltip_position = tooltipConfig.ppom_tooltip_position;
+ const tooltip_trigger = tooltipConfig.ppom_tooltip_trigger;
+ const tooltip_animation = tooltipConfig.ppom_tooltip_animation;
+ const tooltip_maxwidth = tooltipConfig.ppom_tooltip_maxwidth;
+ const tooltip_borderclr = tooltipConfig.ppom_tooltip_borderclr;
+ const tooltip_bgclr = tooltipConfig.ppom_tooltip_bgclr;
+ const tooltip_txtclr = tooltipConfig.ppom_tooltip_txtclr;
+ const tooltip_interactive =
+ tooltipConfig.ppom_tooltip_interactive == 'yes' ? true : false;
const tooltip_options = {
contentAsHTML: true,
@@ -27,11 +36,10 @@
maxWidth: tooltip_maxwidth,
tooltipBorderColor: tooltip_borderclr,
tooltipBGColor: tooltip_bgclr,
- tooltipContentColor: tooltip_txtclr
+ tooltipContentColor: tooltip_txtclr,
};
- if (tooltip_trigger != 'yes') {
-
+ if ( tooltip_trigger != 'yes' ) {
tooltip_options.triggerClose = {
mouseleave: true,
originClick: true,
@@ -42,9 +50,7 @@
mouseenter: true,
tap: true,
};
- }
- else {
-
+ } else {
tooltip_options.triggerClose = {
click: true,
tap: true,
@@ -55,113 +61,136 @@
tap: true,
};
}
- if ( $('[data-ppom-tooltip~=ppom_tooltip]').length > 0 ) {
- $('[data-ppom-tooltip~=ppom_tooltip]').ppom_tooltipster?.(tooltip_options);
+ // Tooltips are optional because some forms load this popup bridge only for
+ // modal behavior, while others use it to initialize tooltip-enabled inputs.
+ if ( $( '[data-ppom-tooltip~=ppom_tooltip]' ).length > 0 ) {
+ $( '[data-ppom-tooltip~=ppom_tooltip]' ).ppom_tooltipster?.(
+ tooltip_options
+ );
}
- // Plugin name and prefix
- var pluginName = 'megapopup';
- var prefix = 'ppom-popup';
+ // Plugin name and prefix
+ const pluginName = 'megapopup';
+ const prefix = 'ppom-popup';
+ // File-upload browse buttons need a refresh after hidden containers become
+ // visible inside a popup, otherwise Plupload can calculate stale positions.
function pluploadRefresh() {
- if( typeof uploaderInstances !== 'object' ) {
+ if ( typeof uploaderInstances !== 'object' ) {
return;
}
- const instances = Object.values(uploaderInstances);
+ const instances = Object.values( uploaderInstances );
- for( let i=0; i ').appendTo('body');
+ if ( modalBG.length == 0 ) {
+ modalBG = $(
+ ' '
+ ).appendTo( 'body' );
}
- // open popup
- modal.bind(prefix + ':open', function() {
-
- $('body').addClass(options.bodycontroller);
- modal.css({ 'display': 'block', });
+ // Opening only toggles classes/visibility; the popup contents are
+ // already rendered by PHP and may include live PPOM inputs/widgets.
+ modal.bind( prefix + ':open', function () {
+ $( 'body' ).addClass( options.bodycontroller );
+ modal.css( { display: 'block' } );
modalBG.fadeIn();
- modal.animate({
- "top": '0px',
- "opacity": 1
- }, 0);
-
- });
-
- // close popup
- modal.bind(prefix + ':close', function() {
-
- $('body').removeClass(options.bodycontroller);
+ modal.animate(
+ {
+ top: '0px',
+ opacity: 1,
+ },
+ 0
+ );
+ } );
+
+ // Closing keeps the modal in the DOM so reopening does not discard
+ // field state entered inside the popup-backed PPOM form controls.
+ modal.bind( prefix + ':close', function () {
+ $( 'body' ).removeClass( options.bodycontroller );
modalBG.fadeOut();
- modal.animate({
- "top": '0px',
- "opacity": 0
- }, 0, function() {
- modal.css({ 'display': 'none' });
- });
- });
+ modal.animate(
+ {
+ top: '0px',
+ opacity: 0,
+ },
+ 0,
+ function () {
+ modal.css( { display: 'none' } );
+ }
+ );
+ } );
//Open Modal Immediately
- modal.trigger(prefix + ':open');
+ modal.trigger( prefix + ':open' );
// close popup listner
- $('.' + options.popupcloseclass).bind('click.modalEvent', function(e) {
- modal.trigger(prefix + ':close');
- e.preventDefault();
- });
+ $( '.' + options.popupcloseclass ).bind(
+ 'click.modalEvent',
+ function ( e ) {
+ modal.trigger( prefix + ':close' );
+ e.preventDefault();
+ }
+ );
// Close popup on overlay click, but not when clicking inside the modal
- $('.ppom-enquiry-overlay, .ppom-popup-product-edit-overlay').bind('click.modalEvent', function(e) {
- if (!$(e.target).closest('.ppom-enquiry-modal, .ppom-popup-product-edit-modal').length) {
- modal.trigger(prefix + ':close');
+ $( '.ppom-enquiry-overlay, .ppom-popup-product-edit-overlay' ).bind(
+ 'click.modalEvent',
+ function ( e ) {
+ if (
+ ! $( e.target ).closest(
+ '.ppom-enquiry-modal, .ppom-popup-product-edit-modal'
+ ).length
+ ) {
+ modal.trigger( prefix + ':close' );
+ }
}
- });
+ );
// disable backgroundclickevent close
- if (options.backgroundclickevent) {
- modalBG.css({ "cursor": "pointer" })
- modalBG.bind('click.modalEvent', function() {
- modal.trigger(prefix + ':close')
- });
+ if ( options.backgroundclickevent ) {
+ modalBG.css( { cursor: 'pointer' } );
+ modalBG.bind( 'click.modalEvent', function () {
+ modal.trigger( prefix + ':close' );
+ } );
}
-
- });
- }
-
-})(jQuery);
+ } );
+ };
+} )( jQuery );
diff --git a/js/ppom-tooltip.js b/js/ppom-tooltip.js
index a93ba640..f71bfa0a 100644
--- a/js/ppom-tooltip.js
+++ b/js/ppom-tooltip.js
@@ -1,90 +1,97 @@
-"use strict";
-jQuery(function($){
-
- /*******************************************
- * PPOM Fields Desciption Tooltip JS Code *
- ********************************************/
+'use strict';
+/**
+ * Fallback tooltip renderer for PPOM helper icons.
+ *
+ * Some screens still use this lightweight tooltip instead of Tooltipster. It
+ * reads the native `title` attribute, temporarily moves that content into a
+ * positioned tooltip node, and restores the attribute on teardown.
+ */
+jQuery( function ( $ ) {
var target, tooltip, title, tip;
var targets = $( '[data-ppom-tooltip~=ppom_tooltip]' ),
- target = false,
- tooltip = false,
- title = false;
+ target = false,
+ tooltip = false,
+ title = false;
+
+ $( document ).on(
+ 'mouseenter',
+ '[data-ppom-tooltip~=ppom_tooltip]',
+ function () {
+ // for iphone double click issue
+ if ( navigator.userAgent.match( /(iPod|iPhone|iPad)/ ) ) {
+ $( this ).trigger( 'click' );
+ }
+
+ target = $( this );
+ tip = target.attr( 'title' );
+ tooltip = $( ' ' );
+
+ if ( ! tip || tip == '' ) {
+ return false;
+ }
+
+ target.removeAttr( 'title' );
+ tooltip.css( 'opacity', 0 ).html( tip ).appendTo( 'body' );
+
+ const init_tooltip = function () {
+ if ( $( window ).width() < tooltip.outerWidth() * 1.5 ) {
+ tooltip.css( 'max-width', $( window ).width() / 2 );
+ } else {
+ tooltip.css( 'max-width', 340 );
+ }
+
+ var pos_left =
+ target.offset().left +
+ target.outerWidth() / 2 -
+ tooltip.outerWidth() / 2,
+ pos_top = target.offset().top - tooltip.outerHeight() - 20;
+
+ if ( pos_left < 0 ) {
+ pos_left =
+ target.offset().left + target.outerWidth() / 2 - 20;
+ tooltip.addClass( 'left' );
+ } else {
+ tooltip.removeClass( 'left' );
+ }
+
+ if ( pos_left + tooltip.outerWidth() > $( window ).width() ) {
+ pos_left =
+ target.offset().left -
+ tooltip.outerWidth() +
+ target.outerWidth() / 2 +
+ 20;
+ tooltip.addClass( 'right' );
+ } else {
+ tooltip.removeClass( 'right' );
+ }
+
+ if ( pos_top < 0 ) {
+ var pos_top = target.offset().top + target.outerHeight();
+ tooltip.addClass( 'top' );
+ } else {
+ tooltip.removeClass( 'top' );
+ }
- $(document).on('mouseenter', '[data-ppom-tooltip~=ppom_tooltip]', function(){
+ tooltip
+ .css( { left: pos_left, top: pos_top } )
+ .animate( { top: '+=10', opacity: 1 }, 50 );
+ };
- // for iphone double click issue
- if(navigator.userAgent.match(/(iPod|iPhone|iPad)/)){
- $(this).trigger('click');
- }
+ init_tooltip();
+ $( window ).resize( init_tooltip );
+ const remove_tooltip = function () {
+ tooltip.animate( { top: '-=10', opacity: 0 }, 50, function () {
+ $( this ).remove();
+ } );
- target = $( this );
- tip = target.attr( 'title' );
- tooltip = $( ' ' );
-
- if( !tip || tip == '' )
- return false;
-
- target.removeAttr( 'title' );
- tooltip.css( 'opacity', 0 )
- .html( tip )
- .appendTo( 'body' );
-
- var init_tooltip = function()
- {
- if( $( window ).width() < tooltip.outerWidth() * 1.5 )
- tooltip.css( 'max-width', $( window ).width() / 2 );
- else
- tooltip.css( 'max-width', 340 );
-
- var pos_left = target.offset().left + ( target.outerWidth() / 2 ) - ( tooltip.outerWidth() / 2 ),
- pos_top = target.offset().top - tooltip.outerHeight() - 20;
-
- if( pos_left < 0 )
- {
- pos_left = target.offset().left + target.outerWidth() / 2 - 20;
- tooltip.addClass( 'left' );
- }
- else
- tooltip.removeClass( 'left' );
-
- if( pos_left + tooltip.outerWidth() > $( window ).width() )
- {
- pos_left = target.offset().left - tooltip.outerWidth() + target.outerWidth() / 2 + 20;
- tooltip.addClass( 'right' );
- }
- else
- tooltip.removeClass( 'right' );
-
- if( pos_top < 0 )
- {
- var pos_top = target.offset().top + target.outerHeight();
- tooltip.addClass( 'top' );
- }
- else
- tooltip.removeClass( 'top' );
-
- tooltip.css( { left: pos_left, top: pos_top } )
- .animate( { top: '+=10', opacity: 1 }, 50 );
- };
-
- init_tooltip();
- $( window ).resize( init_tooltip );
-
- var remove_tooltip = function()
- {
- tooltip.animate( { top: '-=10', opacity: 0 }, 50, function()
- {
- $( this ).remove();
- });
-
- target.attr( 'title', tip );
- };
-
- target.bind( 'mouseleave', remove_tooltip );
- tooltip.bind( 'click touchmove', remove_tooltip );
- $('body').bind( 'touchmove', remove_tooltip );
- });
+ target.attr( 'title', tip );
+ };
-});
\ No newline at end of file
+ target.bind( 'mouseleave', remove_tooltip );
+ tooltip.bind( 'click touchmove', remove_tooltip );
+ $( 'body' ).bind( 'touchmove', remove_tooltip );
+ }
+ );
+} );
diff --git a/js/ppom-validation.js b/js/ppom-validation.js
index 7b1ab208..126b8740 100644
--- a/js/ppom-validation.js
+++ b/js/ppom-validation.js
@@ -1,48 +1,52 @@
-"use strict"
-
-jQuery(function($){
-
-// console.log('loaded cart');
-
- var ppom_cart_validated = false;
-
- if($.blockUI !== undefined){
- $.blockUI.defaults.message = "";
- }
-
- $('form.cart').on('submit', function(e) {
-
- if( ppom_cart_validated ) return true;
-
- e.preventDefault();
-
- // Removing validation div
- $(".ppom-ajax-validation").remove();
- $('form.cart').block();
-
- var data = $(this).serialize();
- data = data+'&action=ppom_ajax_validation';
- data = data+'&ppom_nonce='+ppom_input_vars.ppom_validate_nonce;
-
- $.post(ppom_input_vars.ajaxurl, data, function( notices ) {
-
- $('form.cart').unblock();
- if( notices.status == 'error' ) {
-
- var show_notice = $(' ')
- .addClass('woocommerce-notices-wrapper ppom-ajax-validation')
- .css('clear','both')
- .css('margin-top', '5px')
- .html(notices.message)
- .appendTo('form.cart');
-
- } else {
-
- ppom_cart_validated = true;
- $('button[name="add-to-cart"]').trigger('click');
- }
-
- });
-
- });
-});
\ No newline at end of file
+'use strict';
+
+/**
+ * AJAX product-form validation before WooCommerce submits add-to-cart.
+ *
+ * The server still owns validation. This script performs a preflight request so
+ * PPOM can show validation notices inline without losing the current form state.
+ */
+jQuery( function ( $ ) {
+ // console.log('loaded cart');
+
+ let ppom_cart_validated = false;
+
+ if ( $.blockUI !== undefined ) {
+ $.blockUI.defaults.message = '';
+ }
+
+ $( 'form.cart' ).on( 'submit', function ( e ) {
+ if ( ppom_cart_validated ) {
+ return true;
+ }
+
+ // Stop the first native submit, ask PHP to validate the serialized PPOM
+ // payload, then re-trigger add-to-cart only after the response is clean.
+ e.preventDefault();
+
+ // Removing validation div
+ $( '.ppom-ajax-validation' ).remove();
+ $( 'form.cart' ).block();
+
+ let data = $( this ).serialize();
+ data = data + '&action=ppom_ajax_validation';
+ data = data + '&ppom_nonce=' + ppom_input_vars.ppom_validate_nonce;
+
+ $.post( ppom_input_vars.ajaxurl, data, function ( notices ) {
+ $( 'form.cart' ).unblock();
+ if ( notices.status == 'error' ) {
+ const show_notice = $( ' ' )
+ .addClass(
+ 'woocommerce-notices-wrapper ppom-ajax-validation'
+ )
+ .css( 'clear', 'both' )
+ .css( 'margin-top', '5px' )
+ .html( notices.message )
+ .appendTo( 'form.cart' );
+ } else {
+ ppom_cart_validated = true;
+ $( 'button[name="add-to-cart"]' ).trigger( 'click' );
+ }
+ } );
+ } );
+} );
diff --git a/js/ppom.inputs.js b/js/ppom.inputs.js
index 19d33869..6a364a9f 100644
--- a/js/ppom.inputs.js
+++ b/js/ppom.inputs.js
@@ -1,451 +1,590 @@
/**
- * PPOM input scripts
- **/
+ * Frontend field bootstrapper for PPOM inputs.
+ *
+ * PHP localizes each field definition into `ppom_input_vars.ppom_inputs`; this
+ * file hydrates those definitions into live widgets on the product page. It is
+ * the bridge between rendered markup and the other frontend subsystems such as
+ * pricing, conditional logic, and file uploads.
+ *
+ * @see ppom_update_option_prices in js/price/ppom-price.js
+ * @see ppom_update_option_prices in js/price/ppom-price-v2.js
+ * @see ppom_check_conditions in js/ppom-conditions-v2.js
+ * @see ppom_setup_file_upload_input in js/file-upload.js
+ */
-"use strict"
+/**
+ * Minimal shape of a localized PPOM field as consumed by the frontend scripts.
+ *
+ * PHP sends richer metadata, but these are the keys repeatedly used across the
+ * field bootstrapper, price engines, and condition/upload helpers.
+ *
+ * @typedef {{
+ * data_name: string,
+ * type: string,
+ * title?: string,
+ * field_type?: string,
+ * input_mask?: string,
+ * discount_type?: string,
+ * options?: Array | '\" on a separate line does not do anything\\.$#"
+ message: '#^Binary operation "\+\=" between 0 and float\|int\|string results in an error\.$#'
+ identifier: assignOp.invalid
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_calculate_totals_from_session\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:accumulate_legacy_option_price_row\(\) has parameter \$context with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_calculate_totals_from_session\\(\\) has parameter \\$cart with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:accumulate_legacy_option_price_row\(\) has parameter \$option with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_check_validation\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:accumulate_legacy_option_price_row\(\) has parameter \$state with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_check_validation\\(\\) has parameter \\$passed with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_cart_item_data\(\) has parameter \$cart with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_check_validation\\(\\) has parameter \\$post_data with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_cart_item_data\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_check_validation\\(\\) has parameter \\$product_id with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_fixed_fee\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_invoice_packing_slips_html\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_fixed_fee\(\) has parameter \$cart with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_invoice_packing_slips_html\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_item_meta\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_wc_order_again_compatibility\\(\\) has parameter \\$cart_item_data with no value type specified in iterable type array\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_item_meta\(\) has parameter \$cart_item with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_wc_order_again_compatibility\\(\\) with return type void returns array but should not return anything\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_item_meta\(\) has parameter \$item_meta with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_cart_item_data\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_to_cart_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_cart_item_data\\(\\) has parameter \\$cart with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_to_cart_quantity\(\) has parameter \$product_id with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_cart_item_data\\(\\) has parameter \\$product_id with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:add_to_cart_quantity\(\) has parameter \$quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_fixed_fee\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:apply_price_matrix_chunk_to_line_components\(\) has parameter \$matrix_found with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_fixed_fee\\(\\) has parameter \\$cart with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:calculate_totals_from_session\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_item_meta\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:calculate_totals_from_session\(\) has parameter \$cart with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_item_meta\\(\\) has parameter \\$cart_item with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:cart_update_validate\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_item_meta\\(\\) has parameter \\$item_meta with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:cart_update_validate\(\) has parameter \$cart_item_key with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_to_cart_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:cart_update_validate\(\) has parameter \$cart_validated with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_to_cart_quantity\\(\\) has parameter \\$product_id with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:cart_update_validate\(\) has parameter \$quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_add_to_cart_quantity\\(\\) has parameter \\$quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:cart_update_validate\(\) has parameter \$values with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_ajax_validate\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_cart_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_alter_price\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_cart_quantity\(\) has parameter \$cart_item_key with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_alter_price\\(\\) has parameter \\$price with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_cart_quantity\(\) has parameter \$quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_alter_price\\(\\) has parameter \\$product with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_cart_quantity_legacy\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_cart_update_validate\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_cart_quantity_legacy\(\) has parameter \$cart_item_key with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_cart_update_validate\\(\\) has parameter \\$cart_item_key with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_cart_quantity_legacy\(\) has parameter \$quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_cart_update_validate\\(\\) has parameter \\$cart_validated with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_checkout_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_cart_update_validate\\(\\) has parameter \\$quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_checkout_quantity\(\) has parameter \$cart_item with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_cart_update_validate\\(\\) has parameter \\$values with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_checkout_quantity\(\) has parameter \$cart_item_key with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_cart_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_checkout_quantity\(\) has parameter \$quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_cart_quantity\\(\\) has parameter \\$cart_item_key with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_email_item_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_cart_quantity\\(\\) has parameter \\$quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_email_item_quantity\(\) has parameter \$item with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_cart_quantity_legacy\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_email_item_quantity\(\) has parameter \$quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_cart_quantity_legacy\\(\\) has parameter \\$cart_item_key with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_oder_item_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_cart_quantity_legacy\\(\\) has parameter \\$quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_oder_item_quantity\(\) has parameter \$item with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_checkout_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_oder_item_quantity\(\) has parameter \$quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_checkout_quantity\\(\\) has parameter \\$cart_item with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_order_item_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_checkout_quantity\\(\\) has parameter \\$cart_item_key with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_order_item_quantity\(\) has parameter \$item with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_checkout_quantity\\(\\) has parameter \\$quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:control_order_item_quantity\(\) has parameter \$quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_email_item_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:item_subtotal\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_email_item_quantity\\(\\) has parameter \\$item with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:item_subtotal\(\) has parameter \$cart_item with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_email_item_quantity\\(\\) has parameter \\$quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:item_subtotal\(\) has parameter \$cart_item_key with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_oder_item_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:item_subtotal\(\) has parameter \$item_subtotal with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_oder_item_quantity\\(\\) has parameter \\$item with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:legacy_option_prices_aggregate_display_quantity\(\) has parameter \$option_prices with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_oder_item_quantity\\(\\) has parameter \\$quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:legacy_option_prices_matrix_context\(\) has parameter \$option_prices with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_order_item_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:mini_cart_fixed_fee\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_order_item_quantity\\(\\) has parameter \\$item with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:update_cart_fees\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_control_order_item_quantity\\(\\) has parameter \\$quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:update_cart_fees\(\) has parameter \$cart_items with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_hide_order_meta\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Cart\\CartHandler\:\:update_cart_fees\(\) has parameter \$values with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Cart/CartHandler.php
-
- message: "#^Function ppom_woocommerce_hide_order_meta\\(\\) has parameter \\$formatted_meta with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:alter_price\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_hide_order_meta\\(\\) has parameter \\$order_item with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:alter_price\(\) has parameter \$price with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_inputs_template_base\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:alter_price\(\) has parameter \$product with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_item_subtotal\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:apply_quantities_max_ceiling\(\) has parameter \$quantities_fields with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_item_subtotal\\(\\) has parameter \\$cart_item with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:apply_quantities_min_floor\(\) has parameter \$quantities_fields with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_item_subtotal\\(\\) has parameter \\$cart_item_key with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:product_default_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_item_subtotal\\(\\) has parameter \\$item_subtotal with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:product_default_quantity\(\) has parameter \$args with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_load_scripts\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:product_default_quantity\(\) has parameter \$product with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_mini_cart_fixed_fee\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_max_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_item_meta\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_max_quantity\(\) has parameter \$max_quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_item_meta\\(\\) has parameter \\$cart_item_key with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_max_quantity\(\) has parameter \$product with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_item_meta\\(\\) has parameter \\$item with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_min_quantity\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_item_meta\\(\\) has parameter \\$order with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_min_quantity\(\) has parameter \$min_quantity with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_item_meta\\(\\) has parameter \\$values with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_min_quantity\(\) has parameter \$product with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_item_meta_html\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_quantity_step\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_key\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_quantity_step\(\) has parameter \$product with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_key\\(\\) has parameter \\$display_key with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Catalog\\CatalogHandler\:\:set_quantity_step\(\) has parameter \$quantity_step with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_key\\(\\) has parameter \\$item with no type specified\\.$#"
+ message: '#^Offset 0 on non\-empty\-list\ in isset\(\) always exists and is not nullable\.$#'
+ identifier: isset.offset
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_key\\(\\) has parameter \\$meta with no type specified\\.$#"
+ message: '#^Parameter \#1 \$price of function wc_price expects float, string given\.$#'
+ identifier: argument.type
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Catalog/CatalogHandler.php
-
- message: "#^Function ppom_woocommerce_order_value\\(\\) has no return type specified\\.$#"
+ message: '#^Call to method get_product_id\(\) on an unknown class PPOM\\WooCommerce\\Order\\WC_Order_Item\.$#'
+ identifier: class.notFound
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_order_value\\(\\) has parameter \\$display_value with no type specified\\.$#"
+ message: '#^Call to method get_product_id\(\) on an unknown class PPOM\\WooCommerce\\Order\\WC_Order_Item_Product\.$#'
+ identifier: class.notFound
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_order_value\\(\\) has parameter \\$item with no type specified\\.$#"
+ message: '#^Call to method get_type\(\) on an unknown class PPOM\\WooCommerce\\Order\\WC_Order_Item\.$#'
+ identifier: class.notFound
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_order_value\\(\\) has parameter \\$meta with no type specified\\.$#"
- count: 1
- path: inc/woocommerce.php
+ message: '#^Call to method update_meta_data\(\) on an unknown class PPOM\\WooCommerce\\Order\\WC_Order_Item_Product\.$#'
+ identifier: class.notFound
+ count: 2
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_product_default_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Cannot access property \$key on object\|null\.$#'
+ identifier: property.nonObject
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_product_default_quantity\\(\\) has parameter \\$args with no type specified\\.$#"
- count: 1
- path: inc/woocommerce.php
+ message: '#^Cannot access property \$value on object\|null\.$#'
+ identifier: property.nonObject
+ count: 2
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_product_default_quantity\\(\\) has parameter \\$product with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:hide_order_meta\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_rename_files\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:hide_order_meta\(\) has parameter \$formatted_meta with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_rename_files\\(\\) has parameter \\$order with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:hide_order_meta\(\) has parameter \$order_item with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_rename_files\\(\\) has parameter \\$order_id with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:invoice_packing_slips_html\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_rename_files\\(\\) has parameter \\$posted_data with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:invoice_packing_slips_html\(\) has parameter \$args with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_max_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_item_meta\(\) has parameter \$values with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_max_quantity\\(\\) has parameter \\$max_quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_item_meta_html\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_max_quantity\\(\\) has parameter \\$product with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_key\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_min_quantity\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_key\(\) has parameter \$display_key with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_min_quantity\\(\\) has parameter \\$min_quantity with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_key\(\) has parameter \$item with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_min_quantity\\(\\) has parameter \\$product with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_key\(\) has parameter \$meta with no type specified\.$#'
+ identifier: missingType.parameter
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_quantity_step\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_meta_display_key_from_field_meta\(\) has parameter \$field_meta with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_quantity_step\\(\\) has parameter \\$product with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:posted_fields_without_cropper_payloads\(\) has parameter \$cropper_keys with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_set_quantity_step\\(\\) has parameter \\$quantity_step with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:posted_fields_without_cropper_payloads\(\) has parameter \$posted_fields with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_show_fields_on_product\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:posted_fields_without_cropper_payloads\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_show_fields_on_product\\(\\) has parameter \\$args with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:wc_order_again_compatibility\(\) has parameter \$cart_item_data with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_show_fields_on_product\\(\\) has parameter \\$product_id with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Order\\OrderHandler\:\:wc_order_again_compatibility\(\) with return type void returns array but should not return anything\.$#'
+ identifier: return.void
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_template_base_inputs_rendering\\(\\) has no return type specified\\.$#"
+ message: '#^Parameter \#2 \$field_meta of static method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_meta_display_key_from_field_meta\(\) expects array\|bool, array\|string given\.$#'
+ identifier: argument.type
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_template_base_inputs_rendering\\(\\) has parameter \\$args with no type specified\\.$#"
- count: 1
- path: inc/woocommerce.php
+ message: '#^Parameter \#3 \$item of static method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_value\(\) expects PPOM\\WooCommerce\\Order\\WC_Order_Item\|null, WC_Order_Item_Product given\.$#'
+ identifier: argument.type
+ count: 2
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_template_base_inputs_rendering\\(\\) has parameter \\$product_id with no type specified\\.$#"
+ message: '#^Parameter \$item of method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_item_meta\(\) has invalid type PPOM\\WooCommerce\\Order\\WC_Order_Item_Product\.$#'
+ identifier: class.notFound
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_update_cart_fees\\(\\) has no return type specified\\.$#"
+ message: '#^Parameter \$item of method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_value\(\) has invalid type PPOM\\WooCommerce\\Order\\WC_Order_Item\.$#'
+ identifier: class.notFound
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_update_cart_fees\\(\\) has parameter \\$cart_items with no type specified\\.$#"
+ message: '#^Parameter \$order of method PPOM\\WooCommerce\\Order\\OrderHandler\:\:order_item_meta\(\) has invalid type PPOM\\WooCommerce\\Order\\WC_Order\.$#'
+ identifier: class.notFound
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_update_cart_fees\\(\\) has parameter \\$values with no type specified\\.$#"
+ message: '#^Parameter \$order of method PPOM\\WooCommerce\\Order\\OrderHandler\:\:rename_files\(\) has invalid type PPOM\\WooCommerce\\Order\\WC_Order\.$#'
+ identifier: class.notFound
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Order/OrderHandler.php
-
- message: "#^Function ppom_woocommerce_validate_product\\(\\) has no return type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Product\\ProductHandler\:\:check_validation\(\) has parameter \$post_data with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Product/ProductHandler.php
-
- message: "#^Function ppom_woocommerce_validate_product\\(\\) has parameter \\$passed with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Product\\ProductHandler\:\:field_requires_add_to_cart_schema_checks\(\) has parameter \$field with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Product/ProductHandler.php
-
- message: "#^Function ppom_woocommerce_validate_product\\(\\) has parameter \\$product_id with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Product\\ProductHandler\:\:inputs_template_base\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Product/ProductHandler.php
-
- message: "#^Function ppom_woocommerce_validate_product\\(\\) has parameter \\$qty with no type specified\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Product\\ProductHandler\:\:load_scripts\(\) has no return type specified\.$#'
+ identifier: missingType.return
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Product/ProductHandler.php
-
- message: "#^Parameter \\#1 \\$price of function wc_price expects float, string given\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Product\\ProductHandler\:\:show_fields_on_product\(\) has parameter \$args with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Product/ProductHandler.php
-
- message: "#^Variable \\$price_after_precent might not be defined\\.$#"
+ message: '#^Method PPOM\\WooCommerce\\Product\\ProductHandler\:\:template_base_inputs_rendering\(\) has parameter \$args with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
count: 1
- path: inc/woocommerce.php
+ path: src/WooCommerce/Product/ProductHandler.php
-
- message: "#^Constant PPOM_PATH not found\\.$#"
+ message: '#^Parameter \#1 \$message of function wc_kses_notice expects string, string\|false given\.$#'
+ identifier: argument.type
count: 1
- path: templates/admin/changelog.php
+ path: src/WooCommerce/Product/ProductHandler.php
-
- message: "#^Access to an undefined property object\\:\\:\\$key\\.$#"
+ message: '#^Access to an undefined property object\:\:\$key\.$#'
+ identifier: property.notFound
count: 1
path: templates/admin/ppom-fields.php
-
- message: "#^Parameter \\#1 \\$text of function esc_attr expects string, int given\\.$#"
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, int given\.$#'
+ identifier: argument.type
count: 2
path: templates/admin/ppom-fields.php
-
- message: "#^Parameter \\#1 \\$text of function esc_attr expects string, int\\<1, max\\> given\\.$#"
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, int\<1, max\> given\.$#'
+ identifier: argument.type
count: 13
path: templates/admin/ppom-fields.php
-
- message: "#^Parameter \\#1 \\(void\\) of echo cannot be converted to string\\.$#"
- count: 1
- path: templates/admin/ppom-fields.php
-
- -
- message: "#^Result of function checked \\(void\\) is used\\.$#"
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, int\|string given\.$#'
+ identifier: argument.type
count: 1
path: templates/admin/ppom-fields.php
-
- message: "#^Variable \\$ppom_id might not be defined\\.$#"
+ message: '#^Variable \$ppom_id might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/component/form-data.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/component/form-data.php
-
- message: "#^Variable \\$product_id might not be defined\\.$#"
+ message: '#^Variable \$product_id might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/component/form-data.php
-
- message: "#^Variable \\$image_full might not be defined\\.$#"
+ message: '#^Variable \$image_full might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/component/image/image-modals.php
-
- message: "#^Variable \\$image_id might not be defined\\.$#"
+ message: '#^Variable \$image_id might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/component/image/image-modals.php
-
- message: "#^Variable \\$image_title might not be defined\\.$#"
+ message: '#^Variable \$image_title might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/component/image/image-modals.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/component/quantities/grid-layout.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 4
path: templates/frontend/component/quantities/grid-layout.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/component/quantities/grid-layout.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, int\|string given\.$#'
+ identifier: argument.type
+ count: 1
+ path: templates/frontend/component/quantities/horizontal-layout.php
+
+ -
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/component/quantities/horizontal-layout.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 4
path: templates/frontend/component/quantities/horizontal-layout.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/component/quantities/horizontal-layout.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/component/quantities/vertical-layout.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 4
path: templates/frontend/component/quantities/vertical-layout.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/component/quantities/vertical-layout.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, int\|string given\.$#'
+ identifier: argument.type
+ count: 2
+ path: templates/frontend/inputs/audio.php
+
+ -
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, string\|false given\.$#'
+ identifier: argument.type
+ count: 2
+ path: templates/frontend/inputs/audio.php
+
+ -
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/audio.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/checkbox.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 4
path: templates/frontend/inputs/checkbox.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/inputs/checkbox.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/color.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/color.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/color.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/cropper.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
- count: 3
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
+ count: 2
path: templates/frontend/inputs/cropper.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/cropper.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/date.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/date.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/date.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/daterange.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/daterange.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/daterange.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/divider.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/email.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/email.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/email.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/inputs/file.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/hidden.php
-
- message: "#^If condition is always true\\.$#"
+ message: '#^If condition is always true\.$#'
+ identifier: if.alwaysTrue
count: 1
path: templates/frontend/inputs/image.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, string\|false given\.$#'
+ identifier: argument.type
+ count: 3
+ path: templates/frontend/inputs/image.php
+
+ -
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 6
path: templates/frontend/inputs/image.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 3
path: templates/frontend/inputs/image.php
-
- message: "#^Parameter \\#1 \\$text of function esc_attr expects string, false given\\.$#"
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, false given\.$#'
+ identifier: argument.type
count: 1
path: templates/frontend/inputs/measure.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/measure.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/measure.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/measure.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/number.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/number.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/number.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/palettes.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 3
path: templates/frontend/inputs/palettes.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/palettes.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Parameter \#1 \$text of function esc_attr expects string, string\|false given\.$#'
+ identifier: argument.type
+ count: 1
+ path: templates/frontend/inputs/pricematrix.php
+
+ -
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 3
path: templates/frontend/inputs/pricematrix.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/pricematrix.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/quantities.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 3
path: templates/frontend/inputs/quantities.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/inputs/quantities.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 4
path: templates/frontend/inputs/radio.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/inputs/radio.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/section.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/select.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 3
path: templates/frontend/inputs/select.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/inputs/select.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/text.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/text.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/text.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Parameter \#1 \$str of function strip_tags expects string, array\\|string given\.$#'
+ identifier: argument.type
+ count: 1
+ path: templates/frontend/inputs/textarea.php
+
+ -
+ message: '#^Parameter \#1 \$text of function esc_html expects string, array\\|string given\.$#'
+ identifier: argument.type
+ count: 1
+ path: templates/frontend/inputs/textarea.php
+
+ -
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/frontend/inputs/textarea.php
-
- message: "#^Variable \\$wp_editor_setting might not be defined\\.$#"
+ message: '#^Variable \$wp_editor_setting might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/textarea.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/timezone.php
-
- message: "#^Variable \\$field_meta might not be defined\\.$#"
+ message: '#^Variable \$field_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/frontend/inputs/timezone.php
-
- message: "#^Variable \\$form_obj might not be defined\\.$#"
+ message: '#^Variable \$form_obj might not be defined\.$#'
+ identifier: variable.undefined
count: 12
path: templates/frontend/ppom-fields.php
-
- message: "#^Variable \\$args might not be defined\\.$#"
+ message: '#^Variable \$args might not be defined\.$#'
+ identifier: variable.undefined
count: 6
path: templates/input/quantities.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 3
path: templates/input/quantities.php
-
- message: "#^Variable \\$args might not be defined\\.$#"
+ message: '#^Variable \$args might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/render-fields.php
-
- message: "#^Variable \\$default_value might not be defined\\.$#"
+ message: '#^Variable \$default_value might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/render-fields.php
-
- message: "#^Variable \\$ppom_fields_meta might not be defined\\.$#"
+ message: '#^Variable \$ppom_fields_meta might not be defined\.$#'
+ identifier: variable.undefined
count: 2
path: templates/render-fields.php
-
- message: "#^Variable \\$ppom_id might not be defined\\.$#"
+ message: '#^Variable \$ppom_id might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/render-fields.php
-
- message: "#^Variable \\$ppom_settings might not be defined\\.$#"
+ message: '#^Variable \$ppom_settings might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/render-fields.php
-
- message: "#^Variable \\$product might not be defined\\.$#"
+ message: '#^Variable \$product might not be defined\.$#'
+ identifier: variable.undefined
count: 29
path: templates/render-fields.php
-
- message: "#^Variable \\$file_id might not be defined\\.$#"
+ message: '#^Variable \$file_id might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/v10/file-modals.php
-
- message: "#^Variable \\$image_full might not be defined\\.$#"
+ message: '#^Variable \$image_full might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/v10/file-modals.php
-
- message: "#^Variable \\$image_title might not be defined\\.$#"
+ message: '#^Variable \$image_title might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/v10/file-modals.php
-
- message: "#^Variable \\$image_full might not be defined\\.$#"
+ message: '#^Variable \$image_full might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/v10/image-modals.php
-
- message: "#^Variable \\$image_id might not be defined\\.$#"
+ message: '#^Variable \$image_id might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/v10/image-modals.php
-
- message: "#^Variable \\$image_title might not be defined\\.$#"
+ message: '#^Variable \$image_title might not be defined\.$#'
+ identifier: variable.undefined
count: 1
path: templates/v10/image-modals.php
diff --git a/phpstan-bootstrap-ppom.php b/phpstan-bootstrap-ppom.php
new file mode 100644
index 00000000..2fa1bd5f
--- /dev/null
+++ b/phpstan-bootstrap-ppom.php
@@ -0,0 +1,22 @@
+
- ./tests/
+ ./tests/unit/
-
\ No newline at end of file
+
diff --git a/readme.txt b/readme.txt
index 17b67e2c..50e7aa6e 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,7 +1,7 @@
=== PPOM - Product Addons & Custom Fields for WooCommerce ===
Contributors: themeisle
Tags: woocommerce product addons, woocommerce product options, woocommerce product fields, woocommerce product, product addons
-Requires at least: 3.5
+Requires at least: 6.2
Tested up to: 6.9
Stable tag: 33.0.18
License: GPLv2
diff --git a/src/Admin/Manager.php b/src/Admin/Manager.php
new file mode 100644
index 00000000..0cbd61fe
--- /dev/null
+++ b/src/Admin/Manager.php
@@ -0,0 +1,1097 @@
+has_multiple_meta() ) {
+ $total_items = count( $ppom->meta_id ); // Get the total number of items.
+ $current_item = 0; // Counter to track the current iteration.
+ $has_fields = false;
+ $settings_map = $ppom->get_settings_by_ids( (array) $ppom->meta_id );
+ foreach ( $ppom->meta_id as $meta_id ) {
+ ++$current_item; // Increment the counter.
+
+ $mid = absint( $meta_id );
+ $ppom_setting = ( $mid > 0 && isset( $settings_map[ $mid ] ) ) ? $settings_map[ $mid ] : null;
+ if ( $ppom_setting ) {
+ $meta_title = stripslashes( $ppom_setting->productmeta_name );
+ $url_edit = add_query_arg(
+ array(
+ 'productmeta_id' => $ppom_setting->productmeta_id,
+ 'do_meta' => 'edit',
+ ),
+ $ppom_settings_url
+ );
+ printf( '%2$s', esc_url( $url_edit ), $meta_title );
+ // Add a comma only if it's not the last item
+ if ( $current_item < $total_items ) {
+ echo ', ';
+ }
+ $has_fields = true;
+ }
+ }
+ if ( ! $has_fields ) {
+ printf( '%2$s', esc_url( $ppom_settings_url ), __( 'Add Fields', 'woocommerce-product-addon' ) );
+ }
+ } elseif ( $ppom->ppom_settings ) {
+ $url_edit = add_query_arg(
+ array(
+ 'productmeta_id' => $ppom->meta_id,
+ 'do_meta' => 'edit',
+ ),
+ $ppom_settings_url
+ );
+ printf( '%2$s', esc_url( $url_edit ), $ppom->meta_title );
+ } else {
+ printf( '%2$s', esc_url( $ppom_settings_url ), __( 'Add Fields', 'woocommerce-product-addon' ) );
+ }
+
+ break;
+
+ }
+ }
+
+ public static function product_meta_metabox() {
+
+ add_meta_box( 'ppom-select-meta', __( 'Select PPOM Meta', 'woocommerce-product-addon' ), 'ppom_meta_list', 'product', 'side', 'default' );
+ }
+
+ /**
+ * Renders the product edit metabox for selecting a PPOM field group.
+ *
+ * Reads the current product assignment from {@see PPOM_Meta} and lists every
+ * available group returned by {@see PPOM()} for attachment.
+ *
+ * @param WP_Post $post Product post being edited.
+ *
+ * @return void
+ *
+ * @see PPOM_Meta::__construct()
+ */
+ public static function meta_list( $post ) {
+
+ $ppom = new PPOM_Meta( $post->ID );
+ $all_meta = PPOM()->get_product_meta_list_for_ui();
+ $ppom_setting = admin_url( 'admin.php?page=ppom' );
+
+ $html = '';
+
+ $html .= '
';
+ $html .= '';
+
+ $html .= '
';
+ $html .= '';
+
+ ?>
+
+ ';
+ do_action( 'ppom_meta_box_after_list', $post );
+ echo '