Search
Search
Search
Search
Information
Information
Light
Dark
Open actions menu
Basic upload method
Bypass upload method
Tips!
If you encounter an error (by firewall) while uploading using both methods,
try changing extension of the file before uploading it and rename it right after.
This uploader supports multiple file upload.
Submit
~
var
www
greenkey-forms-dev.wpress.dk
httpdocs
wp-content
plugins
gravityforms
js
File Content:
gravityforms.js
/* eslint-env jquery */ var gform = window.gform || {}; // "prop" method fix for previous versions of jQuery (1.5 and below) if( typeof jQuery.fn.prop === 'undefined' ) { jQuery.fn.prop = jQuery.fn.attr; } // Announcing validation errors after form render. jQuery( document ).on( 'gform_post_render', announceAJAXValidationErrors ); /** * Announce validation errors after form has been rendered. * * @since 2.5.1 */ function announceAJAXValidationErrors() { // Announce validation errors. if ( ! jQuery('.gform_validation_errors').length ) { return; } const focusableEl = document.querySelector( '[data-js="gform-focus-validation-error"]' ); if ( focusableEl ) { // elements with tabindex="-1" are not focusable by default, but can be focused programmatically focusableEl.setAttribute( 'tabindex', '-1' ); focusableEl.focus(); } setTimeout( function() { wp.a11y.speak( jQuery( '.gform_validation_errors > h2' ).text() ); }, 1000 ); } //Formatting free form currency fields to currency jQuery( document ).on( 'gform_post_render', gformBindFormatPricingFields ); function gformBindFormatPricingFields(){ // Namespace the event and remove before adding to prevent double binding. jQuery(".ginput_amount, .ginput_donation_amount").off('change.gform').on("change.gform", function(){ gformFormatPricingField(this); }); jQuery(".ginput_amount, .ginput_donation_amount").each(function(){ gformFormatPricingField(this); }); } //---------------------------------------- //------ INSTANCES ----------------------- //---------------------------------------- /** * Namespace to store our JavaScript class instances */ gform.instances = {}; //---------------------------------------- //------ CONSOLE FUNCTIONS --------------- //---------------------------------------- /** * Console namespace for our safe to use and extendable console functions. */ gform.console = { error: function( message ) { if( window.console ) { console.error( message ); } }, info: function( message ) { if( window.console ) { console.info( message ); } }, log: function( message ) { if( window.console ) { console.log( message ); } }, }; //---------------------------------------- //------ ADMIN UTIL FUNCTIONS ------------ //---------------------------------------- /** * Namespace for our admin utlity functions */ gform.adminUtils = { /** * Handle any unsaved changes to the current settings page. * * @since 2.4 * * @param {string} elemId The ID of the current element to check for changes. */ handleUnsavedChanges: function( elemId ) { var hasUnsavedChanges = null; jQuery( elemId ).find( 'input, select, textarea' ).on( 'change keyup', function() { if ( jQuery( this ).attr( 'onChange' ) === undefined && jQuery( this ).attr( 'onClick' ) === undefined ) { hasUnsavedChanges = true; } // Don't trigger unsaved changes on the enable api access button. if ( ( jQuery( this ).next().data("jsButton") || jQuery( this ).data("jsButton") ) === 'enable-api' ) { hasUnsavedChanges = null; } } ); // Standalone logic for the web api settings page. Trigger unsaved changes if the setting doesn't match the checkbox state. if ( this.getUrlParameter( 'subview' ) === 'gravityformswebapi' ) { if ( gf_webapi_vars.api_enabled !== gf_webapi_vars.enable_api_checkbox_checked ) { hasUnsavedChanges = true; } } jQuery( elemId ).on( 'submit', function() { hasUnsavedChanges = null; } ); window.onbeforeunload = function() { return hasUnsavedChanges; }; }, getUrlParameter: function( param ) { var url = window.location.search.substring( 1 ); var urlVariables = url.split( '&' ); for ( var i = 0; i < urlVariables.length; i++ ) { var parameterName = urlVariables[i].split( '=' ); if ( parameterName[0] == param ) { return parameterName[1]; } } }, handleIEDisplay: function() { var isIE = ! gform.tools.isIE(); var ieShow = gform.tools.getNodes( 'show-if-ie', true ); var ieHide = gform.tools.getNodes( 'hide-if-ie', true ); var otherShow = gform.tools.getNodes( 'show-if-not-ie', true ); var otherHide = gform.tools.getNodes( 'hide-if-not-ie', true ); if ( isIE ) { ieShow.forEach( function( el ) { el.classList.add( 'active' ); }); ieHide.forEach( function( el ) { el.classList.remove( 'active' ); }); } else { otherShow.forEach( function( el ) { el.classList.add( 'active' ); }); otherHide.forEach( function( el ) { el.classList.remove( 'active' ); }); } }, } window.HandleUnsavedChanges = gform.adminUtils.handleUnsavedChanges; //---------------------------------------- //------ TOOL FUNCTIONS ------------------ //---------------------------------------- /** * Tool namespace to house our common dom/function tools. */ gform.tools = { /** * Wrapper to add debouncing to any given callback. * * @since 2.5.2 * * @param {Function} fn The callback to execute. * @param {integer} debounceLength The amount of time for which to debounce (in milliseconds) * @param {bool} isImmediate Whether to fire this immediately, or at the tail end of the timeout. * * @returns {function} */ debounce: function( fn, debounceLength, isImmediate ) { // Initialize var to hold our window timeout var timeout; var lastArgs; var lastFn; return function() { // Initialize local versions of our context and arguments to pass to apply() var callbackContext = this; var args = arguments; // Create a deferred callback to fire if this shouldn't be immediate. var deferredCallback = function() { timeout = null; if ( ! isImmediate ) { fn.apply( callbackContext, args ); } }; // Begin processing the actual callback. var callNow = isImmediate && ! timeout; // Reset timeout if it is the same method with the same args. if ( args === lastArgs && ( ''+lastFn == ''+fn ) ) { clearTimeout( timeout ); } // Set the value of the last function call and arguments to help determine whether the next call is unique. var cachePreviousCall = function( fn, args ) { lastFn = fn; lastArgs = args; } timeout = setTimeout( deferredCallback, debounceLength ); cachePreviousCall( fn, args ); // Method should be executed on the trailing edge of the timeout. Bail for now. if ( ! callNow ) { return; } // Callback should be called immediately, and isn't currently debounced; execute it. fn.apply( callbackContext, args ); }; }, /** * @function gform.tools.defaultFor * @description Returns a default if first arg is undefined. Once we start migrating to es6 or use babel can * easily swap to default args * * @since 2.5 * * @param {*} arg * @param {*} val * @returns {*} */ defaultFor: function( arg, val ) { return typeof arg !== 'undefined' ? arg : val; }, /** * @function gform.tools.getFocusable * @description Get focusable elements inside a container and return as an array. * * @since 2.5 * * @param container the parent to search for focusable elements inside of * @returns {*[]} */ getFocusable: function( container ) { container = this.defaultFor( container, document ); var focusable = this.convertElements( container.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ) ); return focusable.filter( function( item ) { return this.visible( item ); }.bind( this ) ); }, /** * @function gform.tools.htmlToElement * * Allows you to convert an HTML string to a DOM Object. * * @param {string} html * * @returns {ChildNode} */ htmlToElement: function( html ) { var template = document.createElement( 'template' ); html = html.trim(); template.innerHTML = html; return template.content.firstChild; }, /** * @function gform.tools.elementToHTML * * Converts a DOM Element to an HTML string. * * @param {object} el * * @returns {string} */ elementToHTML: function( el ) { return el.outerHTML; }, /** * @function gform.tools.convertElements * @description Efficient function to convert a nodelist into a standard array. * Allows you to run Array.forEach in ie11/saf on result of querySelector functions. * Used by getNodes below. * * @since 2.5 * * @param {Element|NodeList} elements Elements to convert * * @returns {Array} Of converted elements */ convertElements: function( elements ) { var converted = []; var i = elements.length; for ( i; i--; converted.unshift( elements[ i ] ) ) ; return converted; }, /** * @function gform.tools.delegate * @description Simple jQuery on replacement. When migrating to ES6 bundle replace with npm delegate. * * @since 2.5 * * @param {String} selector * @param {String} event * @param {String} childSelector * @param {Function} handler */ delegate: function( selector, event, childSelector, handler ) { var is = function( el, selector ) { return ( el.matches || el.msMatchesSelector ).call( el, selector ); }; var elements = document.querySelectorAll( selector ); [].forEach.call( elements, function( el, i ) { el.addEventListener( event, function( e ) { if ( is( e.target, childSelector ) ) { handler( e ); } } ); } ); }, /** * @function gform.tools.getClosest * @description Get a parent node based on selector plus passed in child element. * * @since 2.5 * * @param {Element|EventTarget} el * @param {String} selector * * @returns {null|*} */ getClosest: function( el, selector ) { var matchesFn; var parent; [ 'matches', 'webkitMatchesSelector', 'mozMatchesSelector', 'msMatchesSelector', 'oMatchesSelector' ] .some( function( fn ) { if ( typeof document.body[ fn ] === 'function' ) { matchesFn = fn; return true; } return false; } ); while ( el ) { parent = el.parentElement; if ( parent && parent[ matchesFn ]( selector ) ) { return parent; } el = parent; } return null; }, /** * @function gform.tools.getNodes * @description Used for getting nodes. Please use the data-js attribute whenever possible. * * @since 2.5 * * @param {String} selector The selector string to search for. If arg 4 is false (default) then we search for [data-js="selector"] * @param {Boolean} [convert] Convert the NodeList to an array? Then we can Array.forEach directly. Uses convertElements from above. * @param {Element|EventTarget|Document} [node] Parent node to search from. Defaults to document. * @param {Boolean} [custom] Is this a custom selector were we don't want to use the data-js attribute? * * @returns {NodeList|Array} */ getNodes: function( selector, convert, node, custom ) { if ( ! selector ) { gform.console.error( 'Please pass a selector to gform.tools.getNodes' ); return []; } node = this.defaultFor( node, document ); var selectorString = custom ? selector : '[data-js="' + selector + '"]'; var nodes = node.querySelectorAll( selectorString ); if ( convert ) { nodes = this.convertElements( nodes ); } return nodes; }, /** * @function gform.tools.mergeObjects * @description ES5 Object.assign. Usage: gforms.tools.mergeObjects( obj1, obj2, obj3 ); * * @since 2.5 * * @returns {{}} */ mergeObjects: function() { var resObj = {}; for ( var i = 0; i < arguments.length; i += 1 ) { var obj = arguments[ i ] var keys = Object.keys( obj ); for ( var j = 0; j < keys.length; j += 1 ) { resObj[ keys[ j ] ] = obj[ keys[ j ] ]; } } return resObj; }, /** * @function gform.tools.setAttr * @description Sets attributes for a group of nodes based on a passed selector. * Can apply to document or subset, and has optional delay. * * @since 2.5 * * @param {String} selector A selector string, and valid js selector string for a dom element. * @param {String} attr The attribute name. * @param {String} value The attribute value. * @param {Element|EventTarget|Document} [container] Node to search from, default is document. * @param {Number} [delay] The delay to apply. */ setAttr: function( selector, attr, value, container, delay ) { if ( ! selector || ! attr || ! value ) { gform.console.error( 'Please pass a selector, attribute and value to gform.tools.setAttr' ); return []; } container = this.defaultFor( container, document ); delay = this.defaultFor( delay, 0 ); setTimeout( function() { gform.tools.getNodes( selector, true, container, true ) .forEach( function( node ) { node.setAttribute( attr, value ); } ); }, delay ); }, /** * @function gform.tools.isRtl * @description Determine if the page is in RTL. * * @since 2.5 * */ isRtl: function() { if ( jQuery( 'html' ).attr( 'dir' ) === 'rtl' ) { return true; } }, /** * @function gform.tools.isIE * @description Determine if the current client browser is IE. * * @return {bool} */ isIE: function() { return window.document.documentMode; }, /** * @function gform.tools.trigger * @description Trigger custom or native events on any element in a cross browser way, and pass along optional data. * * @since 2.5.1.1 * * @param {String} eventName The event name. * @param {Element|EventTarget|Document} el Default document. The element to trigger the event on. * @param {Boolean} native Default fasle. Is this a custom event or native? * @param {Object} data Custom data to send along, available in event.detail on listener. */ trigger: function( eventName, el, native, data ) { var event; eventName = this.defaultFor( eventName, '' ); el = this.defaultFor( el, document ); native = this.defaultFor( native, false ); data = this.defaultFor( data, {} ); if ( native ) { event = document.createEvent( 'HTMLEvents' ); event.initEvent( eventName, true, false ); } else { try { event = new CustomEvent( eventName, { detail: data } ); } catch ( e ) { event = document.createEvent( 'CustomEvent' ); event.initCustomEvent( eventName, true, true, data ); } } el.dispatchEvent( event ); }, /** * @function gform.tools.uniqueId * @description Generate a unique id * * @since 2.5.5.2 * * @param {String} prefix * @returns {string} */ uniqueId: function( prefix ) { prefix = this.defaultFor( prefix, 'id' ); return prefix + '-' + Math.random().toString( 36 ).substr( 2, 9 ); }, /** * @function gform.tools.visible * @description Determine if an element is visible in the dom. * * @since 2.5 * * @param elem The element to check * @returns {boolean} */ visible: function( elem ) { return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); }, stripSlashes: function( str ) { return (str + '').replace(/\\(.?)/g, function (s, n1) { switch (n1) { case '\\': return '\\'; case '0': return '\u0000'; case '': return ''; default: return n1; } }); }, /** * @function gform.tools.getCookie * @description Gets a specific cookie. * * @since 2.5.8 * * @param name The cookie to get * @returns {boolean|string} */ getCookie: function( name ) { var cookieArr = document.cookie.split( ";" ); for(var i = 0; i < cookieArr.length; i++) { var cookiePair = cookieArr[i].split( "=" ); if( name == cookiePair[0].trim() ) { return decodeURIComponent( cookiePair[1] ); } } return null; }, /** * @function gform.tools.setCookie * @description Creates and sets a cookie. * * @since 2.5.8 * * @param name The cookie name * @param value The cookie value * @param daysToExpire The number of days until cookie should expire. If not set, * will expire at the end of the user sessions. * @param updateExistingValue Whether or not to update the existing cookie value to include the new value. * Can be helpful for keeping cookie count lower for the browser. */ setCookie: function( name, value, daysToExpire, updateExistingValue ) { var expirationDate = ''; var cookieValue = value; if ( daysToExpire ) { var date = new Date(); date.setTime( date.getTime() + ( daysToExpire * 24 * 60 * 60 * 1000 ) ); expirationDate = ' expires=' + date.toUTCString(); } if ( updateExistingValue ) { var currentValue = gform.tools.getCookie( name ); cookieValue = currentValue !== '' && currentValue !== null ? currentValue + ',' + value : value; } // Set cookie document.cookie = encodeURIComponent( name ) + '=' + encodeURIComponent( cookieValue ) + ';' + expirationDate; }, /** * @function gform.tools.removeCookie * @description Removes a cookie. * * @since 2.5.8 * * @param name The cookie name to check */ removeCookie: function( name ) { gform.tools.setCookie( name, '', -1 ); } }; //------------------------------------------------ //---------- A11Y FUNCTIONS ---------------------- //------------------------------------------------ /** * A11y namespace to house our accessibility functions. */ gform.a11y = {}; //------------------------------------------------ //---------- OPTIONS ----------------------------- //------------------------------------------------ /** * Options namespace to house common plugin and custom options objects for reuse across our JavaScript. */ gform.options = { /** * Accordions in the editor sidebar use these options. Should be applied to any accordions that want to emulate * that look and feel, and patches an a11y issue with jq accordion and our custom usage. */ jqEditorAccordions: { header: 'button.panel-block-tabs__toggle', heightStyle: 'content', collapsible: true, animate: false, create: function( event ) { gform.tools.setAttr( '.ui-accordion-header', 'tabindex', '0', event.target, 100 ); }, activate: function( event ) { gform.tools.setAttr( '.ui-accordion-header', 'tabindex', '0', event.target, 100 ); }, beforeActivate: function( event ) { // handle advanced tab operations as needed before the tab is revealed in a fields settings if ( event.currentTarget.id === 'advanced_tab_toggle' ) { // handle address field if ( window.field && window.field.type && window.field.type === 'address' ) { // regen the Autocomplete UI on every tab open to handle changes to input visibility from interactions CreateAutocompleteUI( window.field ); } } } }, jqAddFieldAccordions: { heightStyle: 'content', collapsible: true, animate: false, create: function( event ) { gform.tools.setAttr( '.ui-accordion-header', 'tabindex', '0', event.target, 100 ); }, activate: function( event ) { gform.tools.setAttr( '.ui-accordion-header', 'tabindex', '0', event.target, 100 ); }, }, }; //------------------------------------------------ //---------- CURRENCY ---------------------------- //------------------------------------------------ function Currency(currency){ this.currency = currency; this.toNumber = function(text){ if(this.isNumeric(text)) { return parseFloat(text); } return gformCleanNumber(text, this.currency["symbol_right"], this.currency["symbol_left"], this.currency["decimal_separator"]); }; /** * Attempts to clean the specified number and formats it as currency. * * @since 2.1.1.16 Allow the overriding of numerical checks. * * @param number int Number to be formatted. It can be a clean number, or an already formatted number. * @param isNumeric bool Whether or not the number is guaranteed to be a clean, unformatted number. * When false the function will attempt to clean the number. Defaults to false. * * @return string A number formatted as currency. */ this.toMoney = function(number, isNumeric){ isNumeric = isNumeric || false; //isNumeric is an optional parameter. Defaults to false if( ! isNumeric ) { //Cleaning number, removing all formatting number = gformCleanNumber(number, this.currency["symbol_right"], this.currency["symbol_left"], this.currency["decimal_separator"]); } if(number === false) { return ""; } number = number + ""; negative = ""; if(number[0] == "-"){ number = parseFloat(number.substr(1)); negative = '-'; } money = this.numberFormat(number, this.currency["decimals"], this.currency["decimal_separator"], this.currency["thousand_separator"]); if ( money == '0.00' ){ negative = ''; } var symbol_left = this.currency["symbol_left"] ? this.currency["symbol_left"] + this.currency["symbol_padding"] : ""; var symbol_right = this.currency["symbol_right"] ? this.currency["symbol_padding"] + this.currency["symbol_right"] : ""; money = negative + this.htmlDecode(symbol_left) + money + this.htmlDecode(symbol_right); return money; }; /** * Formats a number given the specified parameters. * * @since Unknown * * @param number int Number to be formatted. Must be a clean, unformatted format. * @param decimals int Number of decimals that the output should contain. * @param dec_point string Character to use as the decimal separator. Defaults to ".". * @param thousands_sep string Character to use as the thousand separator. Defaults to ",". * @param padded bool Pads output with zeroes if the number is exact. For example, 1.200. * * @return string The formatted number. */ this.numberFormat = function(number, decimals, dec_point, thousands_sep, padded){ padded = typeof padded == 'undefined' ? true : padded; number = (number+'').replace(',', '').replace(' ', ''); var n = !isFinite(+number) ? 0 : +number, prec = !isFinite(+decimals) ? 0 : Math.abs(decimals), sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, dec = (typeof dec_point === 'undefined') ? '.' : dec_point, s = '', toFixedFix = function (n, prec) { var k = Math.pow(10, prec); return '' + Math.round(n * k) / k; }; if(decimals == '0') { n = n + 0.0000000001; // getting around floating point arithmetic issue when rounding. ( i.e. 4.005 is represented as 4.004999999999 and gets rounded to 4.00 instead of 4.01 ) s = ('' + Math.round(n)).split('.'); } else if(decimals == -1) { s = ('' + n).split('.'); } else { n = n + 0.0000000001; // getting around floating point arithmetic issue when rounding. ( i.e. 4.005 is represented as 4.004999999999 and gets rounded to 4.00 instead of 4.01 ) // Fix for IE parseFloat(0.55).toFixed(0) = 0; s = toFixedFix(n, prec).split('.'); } if (s[0].length > 3) { s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep); } if(padded) { if ((s[1] || '').length < prec) { s[1] = s[1] || ''; s[1] += new Array(prec - s[1].length + 1).join('0'); } } return s.join(dec); } this.isNumeric = function(number){ return gformIsNumber(number); }; this.htmlDecode = function(text) { var c,m,d = text; // look for numerical entities " var arr=d.match(/&#[0-9]{1,5};/g); // if no matches found in string then skip if(arr!=null){ for(var x=0;x<arr.length;x++){ m = arr[x]; c = m.substring(2,m.length-1); //get numeric part which is refernce to unicode character // if its a valid number we can decode if(c >= -32768 && c <= 65535){ // decode every single match within string d = d.replace(m, String.fromCharCode(c)); }else{ d = d.replace(m, ""); //invalid so replace with nada } } } return d; }; /** * Returns the currency code if it exists. * * @since 2.5.13 * * @return {string|false} */ this.getCode = function() { return 'code' in this.currency && this.currency.code !== '' ? this.currency.code : false; } } /** * Gets a formatted number and returns a clean "decimal dot" number. * * Note: Input must be formatted according to the specified parameters (symbol_right, symbol_left, decimal_separator). * @example input -> $1.20, output -> 1.2 * * @since 2.1.1.16 Modified to support additional param in Currency.toMoney. * * @param text string The currency-formatted number. * @param symbol_right string The symbol used on the right. * @param symbol_left string The symbol used on the left. * @param decimal_separator string The decimal separator being used. * * @return float The unformatted numerical value. */ function gformCleanNumber(text, symbol_right, symbol_left, decimal_separator){ var clean_number = '', float_number = '', digit = '', is_negative = false; //converting to a string if a number as passed text = text + " "; //Removing symbol in unicode format (i.e. ᅜ) text = text.replace(/&.*?;/g, ""); //Removing symbol from text text = text.replace(symbol_right, ""); text = text.replace(symbol_left, ""); //Removing all non-numeric characters for(var i=0; i<text.length; i++){ digit = text.substr(i,1); if( (parseInt(digit,10) >= 0 && parseInt(digit,10) <= 9) || digit == decimal_separator ) clean_number += digit; else if(digit == '-') is_negative = true; } //Removing thousand separators but keeping decimal point for(var i=0; i<clean_number.length; i++) { digit = clean_number.substr(i,1); if (digit >= '0' && digit <= '9') float_number += digit; else if(digit == decimal_separator){ float_number += "."; } } if(is_negative) float_number = "-" + float_number; return gformIsNumber(float_number) ? parseFloat(float_number) : false; } function gformGetDecimalSeparator(numberFormat){ var s; switch (numberFormat){ case 'currency' : var currency = new Currency(gf_global.gf_currency_config); s = currency.currency["decimal_separator"]; break; case 'decimal_comma' : s = ','; break; default : s = "." } return s; } function gformIsNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); } function gformIsNumeric(value, number_format){ switch(number_format){ case "decimal_dot" : var r = new RegExp("^(-?[0-9]{1,3}(?:,?[0-9]{3})*(?:\.[0-9]+)?)$"); return r.test(value); break; case "decimal_comma" : var r = new RegExp("^(-?[0-9]{1,3}(?:\.?[0-9]{3})*(?:,[0-9]+)?)$"); return r.test(value); break; } return false; } //------------------------------------------------ //---------- MULTI-PAGE -------------------------- //------------------------------------------------ function gformDeleteUploadedFile(formId, fieldId, deleteButton){ var parent = jQuery("#field_" + formId + "_" + fieldId); var fileIndex = jQuery(deleteButton).parent().index(); parent.find(".ginput_preview").eq(fileIndex).remove(); //displaying single file upload field parent.find('input[type="file"],.validation_message,#extensions_message_' + formId + '_' + fieldId).removeClass("gform_hidden"); //displaying post image label parent.find(".ginput_post_image_file").show(); //clearing post image meta fields parent.find("input[type=\"text\"]").val(''); //removing file from uploaded meta var filesJson = jQuery('#gform_uploaded_files_' + formId).val(); if(filesJson){ var files = jQuery.secureEvalJSON(filesJson); if(files) { var inputName = "input_" + fieldId; var $multfile = parent.find("#gform_multifile_upload_" + formId + "_" + fieldId ); if( $multfile.length > 0 ) { files[inputName].splice(fileIndex, 1); var settings = $multfile.data('settings'); var max = settings.gf_vars.max_files; jQuery("#" + settings.gf_vars.message_id).html(''); if(files[inputName].length < max) gfMultiFileUploader.toggleDisabled(settings, false); } else { files[inputName] = null; } jQuery('#gform_uploaded_files_' + formId).val(jQuery.toJSON(files)); } } } //------------------------------------------------ //---------- PRICE ------------------------------- //------------------------------------------------ var _gformPriceFields = new Array(); var _anyProductSelected; function gformIsHidden(element){ return element.parents('.gfield').not(".gfield_hidden_product").css("display") == "none"; } /** * Calculate total price when input is updated. * * @since 2.5.2 - This method is run through debounce() to avoid recursions. * */ var gformCalculateTotalPrice = gform.tools.debounce(function(formId){ if(!_gformPriceFields[formId]) { return; } var price = 0; _anyProductSelected = false; //Will be used by gformCalculateProductPrice(). for(var i=0; i<_gformPriceFields[formId].length; i++){ price += gformCalculateProductPrice(formId, _gformPriceFields[formId][i]); } //add shipping price if a product has been selected if(_anyProductSelected){ //shipping price var shipping = gformGetShippingPrice(formId) price += shipping; } //gform_product_total filter. Allows users to perform custom price calculation if(window["gform_product_total"]) price = window["gform_product_total"](formId, price); price = gform.applyFilters('gform_product_total', price, formId); gformUpdateTotalFieldPrice( formId, price ); }, 50, false ); /** * Updates the value of the total field with a new price if it has changed. * * @since 2.5.5 * * @param {string|number} formId The ID of the form with the total field. * @param {int} price The new price to apply. * * @return {void} */ function gformUpdateTotalFieldPrice( formId, price ) { var $totalElement = jQuery( '.ginput_total_' + formId ); if ( ! $totalElement.length > 0 ) { return; } /** * @function priceHasChanged * @description For legacy, compare numeric values, otherwise compare currency as that's what * the input stores as value. * * @param {Object} priceData * @returns {boolean} */ var priceHasChanged = function( priceData ) { return isLegacy ? priceData.current !== priceData.new : priceData.current !== priceData.newFormatted; } // Check whether this form is in legacy mode. var isLegacy = document.querySelector( '#gform_wrapper_' + formId + '.gform_legacy_markup_wrapper' ); // Input is hidden in legacy mode and comes after span that displays value, currently only the input is present and visible. var $totalInput = isLegacy ? $totalElement.next() : $totalElement; // Contains current value (numeric or currency formatted), new numeric value and newFormatted value var priceData = { current: String( $totalInput.val() ), new: String( price ), newFormatted: gformFormatMoney( String( price ), true ), } // New value is the same as the current value, bail before updating. if ( ! priceHasChanged( priceData ) ) { return; } // Legacy field if ( isLegacy ) { // Set input value to numeric value and trigger a change event for any js listeners in conditional logic // or third party integrations. $totalInput.val( priceData.new ).trigger( 'change' ); // Inject span with currency value for display. $totalElement.html( priceData.newFormatted ); return; } // First set the input to the numeric value and trigger the change event so that js listeners get the value in expected format. $totalInput.val( priceData.new ).trigger( 'change' ); // Then set the input to the currency value for display. If you have a script that wants to get the value // of this input without listening to the change event you will have to also handle removing the currency formatting // if expecting number in your code. $totalInput.val( priceData.newFormatted ); } function gformGetShippingPrice(formId){ var shippingField = jQuery(".gfield_shipping_" + formId + " input[readonly], .gfield_shipping_" + formId + " select, .gfield_shipping_" + formId + " input:checked"); var shipping = 0; if(shippingField.length == 1 && !gformIsHidden(shippingField)){ if(shippingField.attr("readonly")) shipping = shippingField.val(); else shipping = gformGetPrice(shippingField.val()); } return gformToNumber(shipping); } function gformGetFieldId(element){ var id = jQuery(element).attr("id"); var pieces = id.split("_"); if(pieces.length <=0) return 0; var fieldId = pieces[pieces.length-1]; return fieldId; } function gformCalculateProductPrice(form_id, productFieldId){ var suffix = '_' + form_id + '_' + productFieldId; //Drop down auto-calculating labels jQuery('.gfield_option' + suffix + ', .gfield_shipping_' + form_id).find('select').each(function(){ var dropdown_field = jQuery(this); var selected_price = gformGetPrice(dropdown_field.val()); var field_id = dropdown_field.attr('id').split('_')[2]; dropdown_field.children('option').each(function(){ var choice_element = jQuery(this); var label = gformGetOptionLabel(choice_element, choice_element.val(), selected_price, form_id, field_id); choice_element.html(label); }); }); //Checkboxes labels with prices jQuery('.gfield_option' + suffix).find('.gfield_checkbox').find('input:checkbox').each(function(){ var checkbox_item = jQuery(this); var id = checkbox_item.attr('id'); var field_id = id.split('_')[2]; var label_id = id.replace('choice_', '#label_'); var label_element = jQuery(label_id); var label = gformGetOptionLabel(label_element, checkbox_item.val(), 0, form_id, field_id); label_element.html(label); }); //Radio button auto-calculating lables jQuery('.gfield_option' + suffix + ', .gfield_shipping_' + form_id).find('.gfield_radio').each(function(){ var selected_price = 0; var radio_field = jQuery(this); var id = radio_field.attr('id'); var fieldId = id.split('_')[2]; var selected_value = radio_field.find('input:radio:checked').val(); if(selected_value) selected_price = gformGetPrice(selected_value); radio_field.find('input:radio').each(function(){ var radio_item = jQuery(this); var label_id = radio_item.attr('id').replace('choice_', '#label_'); var label_element = jQuery(label_id); if ( label_element ) { var label = gformGetOptionLabel(label_element, radio_item.val(), selected_price, form_id, fieldId); label_element.html(label); } }); }); var price = gformGetBasePrice(form_id, productFieldId); var quantity = gformGetProductQuantity( form_id, productFieldId ); //calculating options if quantity is more than 0 (a product was selected). if( quantity > 0 ) { jQuery('.gfield_option' + suffix).find('input:checked, select').each(function(){ if(!gformIsHidden(jQuery(this))) price += gformGetPrice(jQuery(this).val()); }); //setting global variable if quantity is more than 0 (a product was selected). Will be used when calculating total _anyProductSelected = true; } price = price * quantity; price = gformRoundPrice(price) ; return price; } function gformGetProductQuantity(formId, productFieldId) { //If product is not selected if (!gformIsProductSelected(formId, productFieldId)) { return 0; } var quantity, quantityInput = jQuery( '#ginput_quantity_' + formId + '_' + productFieldId ), numberFormat; // New input ID starts from 2.5, for the single product and calculation fields. if ( ! quantityInput.length ) { quantityInput = jQuery( '#input_' + formId + '_' + productFieldId + '_1' ); } if (gformIsHidden(quantityInput)) { return 0; } if (quantityInput.length > 0) { quantity = quantityInput.val(); } else { quantityInput = jQuery('.gfield_quantity_' + formId + '_' + productFieldId + ' :input'); quantity = 1; if (quantityInput.length > 0) { quantity = quantityInput.val(); var htmlId = quantityInput.attr('id'), fieldId = gf_get_input_id_by_html_id(htmlId); numberFormat = gf_get_field_number_format( fieldId, formId, 'value' ); } } if (!numberFormat) numberFormat = 'currency'; var decimalSeparator = gformGetDecimalSeparator(numberFormat); quantity = gformCleanNumber(quantity, '', '', decimalSeparator); if (!quantity) quantity = 0; return quantity; } function gformIsProductSelected( formId, productFieldId ) { var suffix = "_" + formId + "_" + productFieldId; var productField = jQuery("#ginput_base_price" + suffix + ", .gfield_donation" + suffix + " input[type=\"text\"], .gfield_product" + suffix + " .ginput_amount"); if( productField.val() && ! gformIsHidden(productField) ){ return true; } else { productField = jQuery(".gfield_product" + suffix + " select, .gfield_product" + suffix + " input:checked, .gfield_donation" + suffix + " select, .gfield_donation" + suffix + " input:checked"); if( productField.val() && ! gformIsHidden(productField) ){ return true; } } return false; } function gformGetBasePrice(formId, productFieldId){ var suffix = "_" + formId + "_" + productFieldId; var price = 0; var productField = jQuery("#ginput_base_price" + suffix+ ", .gfield_donation" + suffix + " input[type=\"text\"], .gfield_product" + suffix + " .ginput_amount"); if(productField.length > 0){ price = productField.val(); //If field is hidden by conditional logic, don't count it for the total if(gformIsHidden(productField)){ price = 0; } } else { productField = jQuery(".gfield_product" + suffix + " select, .gfield_product" + suffix + " input:checked, .gfield_donation" + suffix + " select, .gfield_donation" + suffix + " input:checked"); var val = productField.val(); if(val){ val = val.split("|"); price = val.length > 1 ? val[1] : 0; } //If field is hidden by conditional logic, don't count it for the total if(gformIsHidden(productField)) price = 0; } var c = new Currency(gf_global.gf_currency_config); price = c.toNumber(price); return price === false ? 0 : price; } function gformFormatMoney(text, isNumeric){ if(!gf_global.gf_currency_config) return text; var currency = new Currency(gf_global.gf_currency_config); return currency.toMoney(text, isNumeric); } function gformFormatPricingField(element){ if(gf_global.gf_currency_config){ var currency = new Currency(gf_global.gf_currency_config); var price = currency.toMoney(jQuery(element).val()); jQuery(element).val(price); } } function gformToNumber(text){ var currency = new Currency(gf_global.gf_currency_config); return currency.toNumber(text); } function gformGetPriceDifference(currentPrice, newPrice){ //getting price difference var diff = parseFloat(newPrice) - parseFloat(currentPrice); price = gformFormatMoney(diff, true); if(diff > 0) price = "+" + price; return price; } function gformGetOptionLabel(element, selected_value, current_price, form_id, field_id){ element = jQuery(element); var price = gformGetPrice(selected_value); var current_diff = element.attr('price'); var original_label = element.html().replace(/<span(.*)<\/span>/i, "").replace(current_diff, ""); var diff = gformGetPriceDifference(current_price, price); diff = gformToNumber(diff) == 0 ? "" : " " + diff; element.attr('price', diff); //don't add <span> for drop down items (not supported) var price_label = element[0].tagName.toLowerCase() == "option" ? diff : "<span class='ginput_price'>" + diff + "</span>"; var label = original_label + price_label; //calling hook to allow for custom option formatting if(window["gform_format_option_label"]) label = gform_format_option_label(label, original_label, price_label, current_price, price, form_id, field_id); return label; } function gformGetProductIds(parent_class, element){ var classes = jQuery(element).hasClass(parent_class) ? jQuery(element).attr("class").split(" ") : jQuery(element).parents("." + parent_class).attr("class").split(" "); for(var i=0; i<classes.length; i++){ if(classes[i].substr(0, parent_class.length) == parent_class && classes[i] != parent_class) return {formId: classes[i].split("_")[2], productFieldId: classes[i].split("_")[3]}; } return {formId:0, fieldId:0}; } function gformGetPrice(text){ var val = text.split("|"); var currency = new Currency(gf_global.gf_currency_config); if(val.length > 1 && currency.toNumber(val[1]) !== false) return currency.toNumber(val[1]); return 0; } function gformRoundPrice(price){ var currency = new Currency(gf_global.gf_currency_config); var roundedPrice = currency.numberFormat( price, currency.currency['decimals'], '.', '' ); return parseFloat( roundedPrice ); } function gformRegisterPriceField(item){ if(!_gformPriceFields[item.formId]) _gformPriceFields[item.formId] = new Array(); //ignore price fields that have already been registered for(var i=0; i<_gformPriceFields[item.formId].length; i++) if(_gformPriceFields[item.formId][i] == item.productFieldId) return; //registering new price field _gformPriceFields[item.formId].push(item.productFieldId); } function gformInitPriceFields(){ jQuery(".gfield_price").each(function(){ var productIds = gformGetProductIds("gfield_price", this); gformRegisterPriceField(productIds); jQuery( this ).on( 'input change', 'input[type="text"], input[type="number"], select', function() { var productIds = gformGetProductIds("gfield_price", this); if(productIds.formId == 0) productIds = gformGetProductIds("gfield_shipping", this); jQuery(document).trigger('gform_price_change', [productIds, this]); gformCalculateTotalPrice(productIds.formId); }); jQuery( this ).on( 'click', 'input[type="radio"], input[type="checkbox"]', function() { var productIds = gformGetProductIds("gfield_price", this); if(productIds.formId == 0) productIds = gformGetProductIds("gfield_shipping", this); jQuery(document).trigger('gform_price_change', [productIds, this]); gformCalculateTotalPrice(productIds.formId); }); }); for(formId in _gformPriceFields){ //needed when implementing for in loops if(!_gformPriceFields.hasOwnProperty(formId)) continue; gformCalculateTotalPrice(formId); } } //------------------------------------------- //---------- PASSWORD ----------------------- //------------------------------------------- function gformShowPasswordStrength(fieldId){ var password = document.getElementById( fieldId ).value, confirm = document.getElementById( fieldId + '_2' ) ? document.getElementById( fieldId + '_2' ).value : ''; var result = gformPasswordStrength( password, confirm ), text = window[ 'gf_text' ][ "password_" + result ], resultClass = result === 'unknown' ? 'blank' : result; jQuery("#" + fieldId + "_strength").val(result); jQuery("#" + fieldId + "_strength_indicator").removeClass("blank mismatch short good bad strong").addClass(resultClass).html(text); } // Password strength meter function gformPasswordStrength( password1, password2 ) { if ( password1.length <= 0 ) { return 'blank'; } var disallowedList = wp.passwordStrength.hasOwnProperty( 'userInputDisallowedList' ) ? wp.passwordStrength.userInputDisallowedList() : wp.passwordStrength.userInputBlacklist(), strength = wp.passwordStrength.meter( password1, disallowedList, password2 ); switch ( strength ) { case -1: return 'unknown'; case 2: return 'bad'; case 3: return 'good'; case 4: return 'strong'; case 5: return 'mismatch'; default: return 'short'; } } function gformToggleShowPassword( fieldId ) { var $password = jQuery( '#' + fieldId ), $button = $password.parent().find( 'button' ), $icon = $button.find( 'span' ), currentType = $password.attr( 'type' ); switch ( currentType ) { case 'password': $password.attr( 'type', 'text' ); $button.attr( 'aria-label', $button.attr( 'data-label-hide' ) ); $icon.removeClass( 'dashicons-hidden' ).addClass( 'dashicons-visibility' ); break; case 'text': $password.attr( 'type', 'password' ); $button.attr( 'aria-label', $button.attr( 'data-label-show' ) ); $icon.removeClass( 'dashicons-visibility' ).addClass( 'dashicons-hidden' ); break; } } //---------------------------- //------ CHECKBOX FIELD ------ //---------------------------- function gformToggleCheckboxes( toggleElement ) { var checked, $toggleElement = jQuery( toggleElement ), legacy = $toggleElement.is( 'input[type="checkbox"]' ), $toggle = legacy ? $toggleElement.parent() : $toggleElement.prev(), $toggleLabel = $toggle.find( 'label' ), $checkboxes = $toggle.parent().find( '.gchoice:not( .gchoice_select_all )' ), formId = gf_get_form_id_by_html_id( $toggle.parents( '.gfield' ).attr( 'id' ) ), calcObj = rgars( window, 'gf_global/gfcalc/' + formId ); // Determine checked state. if ( legacy ) { checked = toggleElement.checked; } else { // Get checked data. var checkedData = $toggleElement.data( 'checked' ); if ( typeof checkedData === 'boolean' ) { checked = !checkedData; } else { checked = !( parseInt( checkedData ) === 1 ) } } // Set checkboxes state. $checkboxes.each( function() { // Set checkbox checked state. jQuery( 'input[type="checkbox"]', this ).prop( 'checked', checked ).trigger( 'change' ); // Execute onclick event. if ( typeof jQuery( 'input[type="checkbox"]', this )[0].onclick === 'function' ) { jQuery( 'input[type="checkbox"]', this )[0].onclick(); } } ); // Change toggle label, checked state. if ( legacy ) { $toggleLabel.html( checked ? $toggleLabel.data( 'label-deselect' ) : $toggleLabel.data( 'label-select' ) ); } else { $toggleElement.html( checked ? $toggleElement.data( 'label-deselect' ) : $toggleElement.data( 'label-select' ) ); $toggleElement.data( 'checked', checked ); } // Announce change. wp.a11y.speak( checked ? gf_field_checkbox.strings.selected : gf_field_checkbox.strings.deselected ); if ( calcObj ) { calcObj.runCalcs( formId, calcObj.formulaFields ); } } //---------------------------- //------ RADIO FIELD ------ //---------------------------- function gformToggleRadioOther( radioElement ) { // Get Other input element. var $other = radioElement.parentElement.parentElement.parentElement.lastChild.querySelector( 'input[type="text"]' ); if ( $other ) { $other.disabled = radioElement.value !== 'gf_other_choice'; } } //---------------------------- //------ LIST FIELD ---------- //---------------------------- function gformAddListItem( addButton, max ) { var $addButton = jQuery( addButton ); if( $addButton.hasClass( 'gfield_icon_disabled' ) ) { return; } var $group = $addButton.parents( '.gfield_list_group' ), $clone = $group.clone(), $container = $group.parents( '.gfield_list_container' ), tabindex = $clone.find( ':input:last' ).attr( 'tabindex' ); // reset all inputs to empty state $clone .find( 'input, select, textarea' ).attr( 'tabindex', tabindex ) .not( ':checkbox, :radio' ).val( '' ); $clone.find( ':checkbox, :radio' ).prop( 'checked', false ); $clone = gform.applyFilters( 'gform_list_item_pre_add', $clone, $group ); $group.after( $clone ); gformToggleIcons( $container, max ); gformAdjustClasses( $container ); gformAdjustRowAttributes( $container ); gform.doAction( 'gform_list_post_item_add', $clone, $container ); wp.a11y.speak( window.gf_global.strings.newRowAdded ); } function gformDeleteListItem( deleteButton, max ) { var $deleteButton = jQuery( deleteButton ), $group = $deleteButton.parents( '.gfield_list_group' ), $container = $group.parents( '.gfield_list_container' ); $group.remove(); gformToggleIcons( $container, max ); gformAdjustClasses( $container ); gformAdjustRowAttributes( $container ); gform.doAction( 'gform_list_post_item_delete', $container ); wp.a11y.speak( window.gf_global.strings.rowRemoved ); } function gformAdjustClasses( $container ) { var $groups = $container.find( '.gfield_list_group' ); $groups.each( function( i ) { var $group = jQuery( this ), oddEvenClass = ( i + 1 ) % 2 == 0 ? 'gfield_list_row_even' : 'gfield_list_row_odd'; $group.removeClass( 'gfield_list_row_odd gfield_list_row_even' ).addClass( oddEvenClass ); } ); } function gformAdjustRowAttributes( $container ) { if( $container.parents( '.gform_wrapper' ).hasClass( 'gform_legacy_markup_wrapper' ) ) { return; } $container.find( '.gfield_list_group' ).each( function( i ) { var $input = jQuery( this ).find( 'input, select, textarea' ); $input.each( function( index, input ) { var $this = jQuery( input ); $this.attr( 'aria-label', $this.data( 'aria-label-template' ).gformFormat( i + 1 ) ); } ); var $remove = jQuery( this ).find( '.delete_list_item' ); $remove.attr( 'aria-label', $remove.data( 'aria-label-template' ).gformFormat( i + 1 ) ); } ); } function gformToggleIcons( $container, max ) { var groupCount = $container.find( '.gfield_list_group' ).length, $addButtons = $container.find( '.add_list_item' ), isLegacy = typeof gf_legacy !== 'undefined' && gf_legacy.is_legacy; $container.find( '.delete_list_item' ).css( 'visibility', groupCount == 1 ? 'hidden' : 'visible' ); if ( max > 0 && groupCount >= max ) { // store original title in the add button $addButtons.data( 'title', $container.find( '.add_list_item' ).attr( 'title' ) ); $addButtons.addClass( 'gfield_icon_disabled' ).attr( 'title', '' ); if ( ! isLegacy ) { $addButtons.prop( 'disabled', true ); } } else if( max > 0 ) { $addButtons.removeClass( 'gfield_icon_disabled' ); if ( ! isLegacy ) { $addButtons.prop( 'disabled', false ); } if( $addButtons.data( 'title' ) ) { $addButtons.attr( 'title', $addButtons.data( 'title' ) ); } } } //----------------------------------- //--------- REPEATER FIELD ---------- //----------------------------------- function gformAddRepeaterItem( addButton, max ) { var $addButton = jQuery( addButton ); if( $addButton.hasClass( 'gfield_icon_disabled' ) ) { return; } var $item = $addButton.closest( '.gfield_repeater_item' ), $clone = $item.clone(), $container = $item.closest( '.gfield_repeater_container' ), tabindex = $clone.find( ':input:last' ).attr( 'tabindex' ); // reset all inputs to empty state $clone .find( 'input[type!="hidden"], select, textarea' ).attr( 'tabindex', tabindex ) .not( ':checkbox, :radio' ).val( '' ); $clone.find( ':checkbox, :radio' ).prop( 'checked', false ); $clone.find('.validation_message').remove(); $clone = gform.applyFilters( 'gform_repeater_item_pre_add', $clone, $item ); $item.after( $clone ); var $cells = $clone.children('.gfield_repeater_cell'); $cells.each(function () { var $subContainer = jQuery(this).find('.gfield_repeater_container').first(); if ($subContainer.length > 0) { resetContainerItems = function ($c) { $c.children('.gfield_repeater_items').children('.gfield_repeater_item').each(function (i) { var $children = jQuery(this).children('.gfield_repeater_cell'); $children.each(function () { var $subSubContainer = jQuery(this).find('.gfield_repeater_container').first(); if ($subSubContainer.length > 0) { resetContainerItems($subSubContainer); } }) }) $c.children('.gfield_repeater_items').children('.gfield_repeater_item').not(':first').remove(); } resetContainerItems($subContainer); } }) gformResetRepeaterAttributes($container); if ( typeof gformInitDatepicker == 'function' ) { $container.find('.ui-datepicker-trigger').remove(); $container.find('.hasDatepicker').removeClass('hasDatepicker'); gformInitDatepicker(); } gformBindFormatPricingFields(); gformToggleRepeaterButtons( $container, max ); gform.doAction('gform_repeater_post_item_add', $clone, $container); } function gformDeleteRepeaterItem(deleteButton, max) { var $deleteButton = jQuery(deleteButton), $group = $deleteButton.closest('.gfield_repeater_item'), $container = $group.closest('.gfield_repeater_container'); $group.remove(); gformResetRepeaterAttributes($container); gformToggleRepeaterButtons($container, max); gform.doAction('gform_repeater_post_item_delete', $container); } function gformResetRepeaterAttributes($container, depth, row) { var cachedRadioSelection = null; if (typeof depth === 'undefined') { depth = 0; } if (typeof row === 'undefined') { row = 0; } $container.children('.gfield_repeater_items').children('.gfield_repeater_item').each(function () { var $children = jQuery(this).children('.gfield_repeater_cell'); $children.each(function () { var $cell = jQuery(this); var $subContainer = jQuery(this).find('.gfield_repeater_container').first(); if ($subContainer.length > 0) { var newDepth = depth + 1; gformResetRepeaterAttributes($subContainer, newDepth, row); return; } jQuery(this).find('input, select, textarea, :checkbox, :radio').each(function () { var $this = jQuery(this); var name = $this.attr('name'); if ( typeof name == 'undefined' ) { return; } var regEx = /^(input_[^\[]*)((\[[0-9]+\])+)/, parts = regEx.exec(name); if (!parts) { return; } var inputName = parts[1], arayParts = parts[2], regExIndex = /\[([0-9]+)\]/g, indexes = [], match = regExIndex.exec(arayParts); while (match != null) { indexes.push(match[1]); match = regExIndex.exec(arayParts); } var newNameIndex = parts[1]; indexes = indexes.reverse(); var newId = ''; for (var n = indexes.length - 1; n >= 0; n--) { if (n == depth) { newNameIndex += '[' + row + ']'; newId += '-' + row; } else { newNameIndex += '[' + indexes[n] + ']'; newId += '-' + indexes[n]; } } var currentId = $this.attr('id'); var $label = $cell.find("label[for='" + currentId + "']"); if ( currentId ) { var matches = currentId.match(/((choice|input)_[0-9|_]*)-/); if ( matches && matches[2] ) { newId = matches[1] + newId; $label.attr('for', newId); $this.attr('id', newId); } } var newName = name.replace(parts[0], newNameIndex), newNameIsChecked = jQuery('input[name="'+ newName +'"]').is(':checked'); if ( $this.is(':radio') && $this.is(':checked') && name !== newName && newNameIsChecked ) { if ( cachedRadioSelection !== null ) { cachedRadioSelection.prop('checked', true); } $this.prop('checked', false); cachedRadioSelection = $this; } $this.attr('name', newName); }); }); if (depth === 0) { row++; } }); if ( cachedRadioSelection !== null ) { cachedRadioSelection.prop('checked', true); cachedRadioSelection = null; } } function gformToggleRepeaterButtons($container) { var max = $container.closest('.gfield_repeater_wrapper').data('max_items'), groupCount = $container.children('.gfield_repeater_items').children('.gfield_repeater_item').length, $buttonsContainer = $container.children('.gfield_repeater_items').children('.gfield_repeater_item').children('.gfield_repeater_buttons'), $addButtons = $buttonsContainer.children('.add_repeater_item'); $buttonsContainer.children('.remove_repeater_item').css('visibility', groupCount == 1 ? 'hidden' : 'visible'); if (max > 0 && groupCount >= max) { // store original title in the add button $addButtons.data('title', $buttonsContainer.children('.add_repeater_item').attr('title')); $addButtons.addClass('gfield_icon_disabled').attr('title', ''); } else if (max > 0) { $addButtons.removeClass('gfield_icon_disabled'); if ($addButtons.data('title')) { $addButtons.attr('title', $addButtons.data('title')); } } $container .children('.gfield_repeater_items') .children('.gfield_repeater_item') .children( '.gfield_repeater_cell').each(function (i) { var $subContainer = jQuery(this).find('.gfield_repeater_container').first(); if ($subContainer.length > 0) { gformToggleRepeaterButtons($subContainer); } }); } //----------------------------------- //------ CREDIT CARD FIELD ---------- //----------------------------------- function gformMatchCard(id) { var cardType = gformFindCardType(jQuery('#' + id).val()); var cardContainer = jQuery('#' + id).parents('.gfield').find('.gform_card_icon_container'); if(!cardType) { jQuery(cardContainer).find('.gform_card_icon').removeClass('gform_card_icon_selected gform_card_icon_inactive'); } else { jQuery(cardContainer).find('.gform_card_icon').removeClass('gform_card_icon_selected').addClass('gform_card_icon_inactive'); jQuery(cardContainer).find('.gform_card_icon_' + cardType).removeClass('gform_card_icon_inactive').addClass('gform_card_icon_selected'); } } function gformFindCardType(value) { if(value.length < 4) return false; var rules = window['gf_cc_rules']; var validCardTypes = new Array(); for(type in rules) { //needed when implementing for in loops if(!rules.hasOwnProperty(type)) continue; for(i in rules[type]) { if(!rules[type].hasOwnProperty(i)) continue; if(rules[type][i].indexOf(value.substring(0, rules[type][i].length)) === 0) { validCardTypes[validCardTypes.length] = type; break; } } } return validCardTypes.length == 1 ? validCardTypes[0].toLowerCase() : false; } function gformToggleCreditCard(){ if(jQuery("#gform_payment_method_creditcard").is(":checked")) jQuery(".gform_card_fields_container").slideDown(); else jQuery(".gform_card_fields_container").slideUp(); } //---------------------------------------- //------ CHOSEN DROP DOWN FIELD ---------- //---------------------------------------- function gformInitChosenFields( fieldList, noResultsText ) { return jQuery( fieldList ).each( function(){ var element = jQuery( this ); var isConvoForm = typeof gfcf_theme_config !== 'undefined' ? ( gfcf_theme_config !== null && typeof gfcf_theme_config.data !== 'undefined' ? gfcf_theme_config.data.is_conversational_form : undefined ) : false; // RTL support if( jQuery( 'html' ).attr( 'dir' ) == 'rtl' ) { element.addClass( 'chosen-rtl chzn-rtl' ); } // only initialize once if( ( element.is( ':visible' ) || isConvoForm ) && element.siblings( '.chosen-container' ).length == 0 ) { var chosenOptions = { no_results_text: noResultsText }; if ( isConvoForm ) { chosenOptions.width = element.css( 'inline-size' ); } var options = gform.applyFilters( 'gform_chosen_options', chosenOptions, element ); element.chosen( options ); } }); } //---------------------------------------- //--- CURRENCY FORMAT NUMBER FIELD ------- //---------------------------------------- function gformInitCurrencyFormatFields(fieldList){ jQuery(fieldList).each(function(){ var $this = jQuery(this); $this.val( gformFormatMoney( jQuery(this).val() ) ); }).change( function( event ) { jQuery(this).val( gformFormatMoney( jQuery(this).val() ) ); }); } //---------------------------------------- //------ JS MERGE TAGS ------------------- //---------------------------------------- var GFMergeTag = function() { /** * Gets the merge tag value for the specified input Id * @param formId The current form Id * @param inputId The input Id to get the merge tag from. This could be a field id (i.e. 1) or a specific input Id for multi-input fields (i.e. 1.2) * @param modifier The merge tag modifier to be used. i.e. value, currency, price, etc... * @returns Returns a string containing the merge tag value for the specified input Id */ GFMergeTag.getMergeTagValue = function( formId, inputId, modifier ) { if ( modifier === undefined ) { modifier = ''; } modifier = modifier.replace(":", ""); var fieldId = parseInt(inputId,10); // Check address field's copy value checkbox and reset fieldID to source field if checked var isCopyPreviousAddressChecked = jQuery( '#input_' + formId + '_' + fieldId + '_copy_values_activated:checked' ).length > 0; if ( isCopyPreviousAddressChecked ) { var sourceFieldId = jQuery( '#input_' + formId + '_' + fieldId + '_copy_values_activated' ).data('source_field_id'); inputId = inputId == fieldId ? sourceFieldId : inputId.toString().replace( fieldId + '.', sourceFieldId + '.' ); fieldId = sourceFieldId; } var field = jQuery('#field_' + formId + '_' + fieldId); var inputSelector = fieldId == inputId ? 'input[name^="input_' + fieldId + '"]' : 'input[name="input_' + inputId + '"]'; var input = field.find( inputSelector + ', select[name^="input_' + inputId + '"], textarea[name="input_' + inputId + '"]'); // checking conditional logic var isVisible = window['gf_check_field_rule'] ? gf_check_field_rule( formId, fieldId, true, '' ) == 'show' : true, val; if ( ! isVisible ) { return ''; } // Filtering out the email field confirmation input to prevent the values from both inputs being returned. if ( field.find( '.ginput_container_email' ).hasClass( 'ginput_complex' ) ) { input = input.first(); } //If value has been filtered, use it. Otherwise use default logic var value = gform.applyFilters( 'gform_value_merge_tag_' + formId + '_' + fieldId, false, input, modifier ); if ( value !== false ){ return value; } value = ''; //Reset value to blank switch ( modifier ) { case 'label': // Remove screen reader text from product field label. var label = field.find('.gfield_label'); label.find( '.screen-reader-text' ).remove(); var labelText = label.text(); return labelText; break; case 'qty': if ( field.hasClass('gfield_price') ){ val = gformGetProductQuantity( formId, fieldId ); return val === false || val === '' ? 0 : val; } break; } // Filter out unselected checkboxes and radio buttons if ( input.prop('type') === 'checkbox' || input.prop('type') === 'radio' ) { input = input.filter(':checked'); } if ( input.length === 1 ) { if ( ( input.is('select') || input.prop('type') === 'radio' || input.prop('type') === 'checkbox' ) && modifier === '' ) { if ( input.is( 'select' ) ) { val = input.find( 'option:selected' ); } else if ( input.prop( 'type' ) === 'radio' && input.parent().hasClass( 'gchoice_button' ) ) { val = input.parent().siblings( '.gchoice_label' ).find( 'label' ).clone(); } else { val = input.next('label').clone(); } val.find('span').remove(); if ( val.length === 1 ) { val = val.text(); } else { var option = []; for(var i=0; i<val.length; i++) { option[i] = jQuery(val[i]).text(); } val = option; } } else if ( val === undefined ) { val = input.val(); } if ( jQuery.isArray( val ) ) { // multiple select value = val.join(', '); } else if ( typeof val === 'string' ) { value = GFMergeTag.formatValue( val, modifier ); } else { // empty multiple select returns null, set it to '' value = ''; } } else if ( input.length > 1 ) { val = []; for(var i=0; i<input.length; i++) { if( ( input.prop('type') === 'checkbox' ) && modifier === '' ) { var clone = jQuery(input[i]).next('label').clone(); clone.find('span').remove() val[i] = GFMergeTag.formatValue( clone.text(), modifier ); clone.remove(); } else { val[i] = GFMergeTag.formatValue( jQuery(input[i]).val(), modifier ); } } value = val.join(', '); } return value; } /** * Parses the specified text for merge tags, and replaces all of them with the appropriate merge tag values. Returns the resulting string * @param formId The current form Id * @param text The text containing merge tags * @returns Retuns the original "text" strings with all merge tags replaced with the appropriate merge tag values */ GFMergeTag.replaceMergeTags = function( formId, text ) { var mergeTags = GFMergeTag.parseMergeTags( text ); for(i in mergeTags) { if(! mergeTags.hasOwnProperty(i)) { continue; } var inputId = mergeTags[i][1]; var fieldId = parseInt(inputId,10); var modifier = mergeTags[i][3] == undefined ? '' : mergeTags[i][3].replace(":", ""); var value = GFMergeTag.getMergeTagValue( formId, inputId, modifier ); text = text.replace( mergeTags[i][0], value ); } return text; } GFMergeTag.formatValue = function( value, modifier ) { value = value.split( '|' ); var val = ''; if( value.length > 1 ) { val = modifier === 'price' || modifier === 'currency' ? gformToNumber( value[1] ) : value[0]; } else { val = value[0]; } switch ( modifier ) { case 'price': val = gformToNumber( val ); val = val === false ? '' : val; break; case 'currency': val = gformFormatMoney( val, false ); val = val === false ? '' : val; break; case 'numeric': val = gformToNumber( val ); return val === false ? 0 : val; break; default: val = val.trim(); break; } return val; } /** * Parses the merge tags in the specified text and returns an array of all the matched merge tags * * @param text The text with merge tags to be parsed * @param regEx The regular expression to be used to parse for merge tags. * * @returns Returns an array with all the merge tags that were matched in the original text */ GFMergeTag.parseMergeTags = function( text, regEx ) { if( typeof regEx === 'undefined' ) { regEx = /{[^{]*?:(\d+(\.\d+)?)(:(.*?))?}/i; } var matches = []; while( regEx.test( text ) ) { var i = matches.length; matches[i] = regEx.exec( text ); text = text.replace( '' + matches[i][0], '' ); } return matches; } } new GFMergeTag(); //---------------------------------------- //------ CALCULATION FUNCTIONS ----------- //---------------------------------------- var GFCalc = function(formId, formulaFields){ this.formId = formId; this.formulaFields = formulaFields; this.exprPatt = /^[0-9 -/*\(\)]+$/i; this.isCalculating = {}; this.init = function(formId, formulaFields) { var calc = this; // @since 2.5.10 - namespace event to avoid multiple bindings. jQuery(document) .off("gform_post_conditional_logic.gfCalc_{0}".gformFormat(formId)) .on("gform_post_conditional_logic.gfCalc_{0}".gformFormat(formId), function(){ calc.runCalcs( formId, formulaFields ); } ); for(var i=0; i<formulaFields.length; i++) { var formulaField = jQuery.extend({}, formulaFields[i]); this.runCalc(formulaField, formId); this.bindCalcEvents(formulaField, formId); } } this.runCalc = function(formulaField, formId) { var calcObj = this, field = jQuery('#field_' + formId + '_' + formulaField.field_id), formulaInput = field.hasClass( 'gfield_price' ) ? jQuery( '#ginput_base_price_' + formId + '_' + formulaField.field_id ) : jQuery( '#input_' + formId + '_' + formulaField.field_id ), previous_val = formulaInput.val(), formula = gform.applyFilters( 'gform_calculation_formula', formulaField.formula, formulaField, formId, calcObj ), expr = calcObj.replaceFieldTags( formId, formula, formulaField ).replace(/(\r\n|\n|\r)/gm,""), result = ''; if(calcObj.exprPatt.test(expr)) { try { //run calculation result = eval(expr); } catch( e ) { } } else { return; } // if result is positive infinity, negative infinity or a NaN, defaults to 0 if( ! isFinite( result ) ) result = 0; // allow users to modify result with their own function if( window["gform_calculation_result"] ) { result = window["gform_calculation_result"](result, formulaField, formId, calcObj); if( window.console ) console.log( '"gform_calculation_result" function is deprecated since version 1.8! Use "gform_calculation_result" JS hook instead.' ); } // allow users to modify result with their own function result = gform.applyFilters( 'gform_calculation_result', result, formulaField, formId, calcObj ); // allow result to be custom formatted var formattedResult = gform.applyFilters( 'gform_calculation_format_result', false, result, formulaField, formId, calcObj ); var numberFormat = gf_get_field_number_format(formulaField.field_id, formId); //formatting number if( formattedResult !== false) { result = formattedResult; } else if( field.hasClass( 'gfield_price' ) || numberFormat == "currency") { result = gformFormatMoney(result ? result : 0, true); } else { var decimalSeparator = "."; var thousandSeparator = ","; if(numberFormat == "decimal_comma"){ decimalSeparator = ","; thousandSeparator = "."; } result = gformFormatNumber(result, !gformIsNumber(formulaField.rounding) ? -1 : formulaField.rounding, decimalSeparator, thousandSeparator); } //If value doesn't change, abort. //This is needed to prevent an infinite loop condition with conditional logic if( result == previous_val ) return; // if this is a calculation product, handle differently if(field.hasClass('gfield_price')) { jQuery('#input_' + formId + '_' + formulaField.field_id).text(result); formulaInput.val(result).trigger('change'); // Announce the price change of the product only if there's no Total field. if ( jQuery( '.gfield_label_product' ).length && ! jQuery( '.ginput_total' ).length ) { result = jQuery( 'label[ for=input_' + formId + '_' + formulaField.field_id + '_1 ]' ).find( '.gfield_label_product' ).text() + ' ' + result; wp.a11y.speak( result ); } gformCalculateTotalPrice(formId); } else { formulaInput.val(result).trigger('change'); } }; this.runCalcs = function( formId, formulaFields ) { for(var i=0; i<formulaFields.length; i++) { var formulaField = jQuery.extend({}, formulaFields[i]); this.runCalc( formulaField, formId ); } } this.bindCalcEvents = function(formulaField, formId) { var calcObj = this; var formulaFieldId = formulaField.field_id; var matches = GFMergeTag.parseMergeTags( formulaField.formula ); calcObj.isCalculating[formulaFieldId] = false; for(var i in matches) { if(! matches.hasOwnProperty(i)) continue; var inputId = matches[i][1]; var fieldId = parseInt(inputId,10); var input = jQuery('#field_' + formId + '_' + fieldId).find('input[name="input_' + inputId + '"], select[name="input_' + inputId + '"]'); if(input.prop('type') == 'checkbox' || input.prop('type') == 'radio') { jQuery(input).click(function(){ calcObj.bindCalcEvent(inputId, formulaField, formId, 0); }); } else if(input.is('select') || input.prop('type') == 'hidden') { jQuery(input).change(function(){ calcObj.bindCalcEvent(inputId, formulaField, formId, 0); }); } else { jQuery(input).keydown(function(){ calcObj.bindCalcEvent(inputId, formulaField, formId); }).change(function(){ calcObj.bindCalcEvent(inputId, formulaField, formId, 0); }); } // allow users to add custom methods for triggering calculations gform.doAction( 'gform_post_calculation_events', matches[i], formulaField, formId, calcObj ); } } this.bindCalcEvent = function(inputId, formulaField, formId, delay) { var calcObj = this; var formulaFieldId = formulaField.field_id; delay = delay == undefined ? 345 : delay; if(calcObj.isCalculating[formulaFieldId][inputId]) clearTimeout(calcObj.isCalculating[formulaFieldId][inputId]); calcObj.isCalculating[formulaFieldId][inputId] = window.setTimeout(function() { calcObj.runCalc(formulaField, formId); }, delay); } this.replaceFieldTags = function( formId, expr, formulaField ) { var matches = GFMergeTag.parseMergeTags( expr ); for(i in matches) { if(! matches.hasOwnProperty(i)) continue; var inputId = matches[i][1]; var fieldId = parseInt(inputId,10); if ( fieldId == formulaField.field_id && fieldId == inputId ) { continue; } var modifier = 'value'; if( matches[i][3] ){ modifier = matches[i][3]; } else { var is_product_radio = jQuery('.gfield_price input[name=input_' + fieldId + ']').is('input[type=radio]'); var is_product_dropdown = jQuery('.gfield_price select[name=input_' + fieldId + ']').length > 0; var is_option_checkbox = jQuery('.gfield_price input[name="input_' + inputId + '"]').is('input[type=checkbox]'); if( is_product_dropdown || is_product_radio || is_option_checkbox ) { modifier = 'price'; } } var isVisible = window['gf_check_field_rule'] ? gf_check_field_rule( formId, fieldId, true, '' ) == 'show' : true; var value = isVisible ? GFMergeTag.getMergeTagValue( formId, inputId, modifier ) : 0; // allow users to modify value with their own function value = gform.applyFilters( 'gform_merge_tag_value_pre_calculation', value, matches[i], isVisible, formulaField, formId ); value = this.cleanNumber( value, formId, fieldId, formulaField ); expr = expr.replace( matches[i][0], value ); } return expr; } this.cleanNumber = function ( value, formId, fieldId, formulaField ) { var numberFormat = gf_get_field_number_format( fieldId, formId ); if( ! numberFormat ) { numberFormat = gf_get_field_number_format(formulaField.field_id, formId); } var decimalSeparator = gformGetDecimalSeparator(numberFormat); value = gformCleanNumber( value, '', '', decimalSeparator ); if( ! value ) value = 0; return value; } this.init(formId, formulaFields); } function gformFormatNumber(number, rounding, decimalSeparator, thousandSeparator){ if(typeof decimalSeparator == "undefined"){ if(window['gf_global']){ var currency = new Currency(gf_global.gf_currency_config); decimalSeparator = currency.currency["decimal_separator"]; } else{ decimalSeparator = "."; } } if(typeof thousandSeparator == "undefined"){ if(window['gf_global']){ var currency = new Currency(gf_global.gf_currency_config); thousandSeparator = currency.currency["thousand_separator"]; } else{ thousandSeparator = ","; } } var currency = new Currency(); return currency.numberFormat(number, rounding, decimalSeparator, thousandSeparator, false) } /** * @deprecated. Use GFMergeTags.parseMergeTag() instead */ function getMatchGroups(expr, patt) { var matches = new Array(); while(patt.test(expr)) { var i = matches.length; matches[i] = patt.exec(expr) expr = expr.replace('' + matches[i][0], ''); } return matches; } function gf_get_field_number_format(fieldId, formId, context) { var fieldNumberFormats = rgars(window, 'gf_global/number_formats/{0}/{1}'.gformFormat(formId, fieldId)), format = false; if (fieldNumberFormats === '') { return format; } if (typeof context == 'undefined') { format = fieldNumberFormats.price !== false ? fieldNumberFormats.price : fieldNumberFormats.value; } else { format = fieldNumberFormats[context]; } return format; } //---------------------------------------- //------ reCAPTCHA FUNCTIONS ------------- //---------------------------------------- gform.recaptcha = { /** * Callback function on the reCAPTCAH API script. * * @see GF_Field_CAPTCHA::get_field_input() in /includes/fields/class-gf-field-catpcha.php */ renderRecaptcha: function() { jQuery( '.ginput_recaptcha:not(.gform-initialized)' ).each( function() { var $elem = jQuery( this ), parameters = { 'sitekey': $elem.data( 'sitekey' ), 'theme': $elem.data( 'theme' ), 'tabindex': $elem.data( 'tabindex' ) }; if ( $elem.data( 'stoken' ) ) { parameters.stoken = $elem.data( 'stoken' ); } var callback = false; if ( $elem.data( 'size' ) == 'invisible' ) { callback = function( token ) { if ( token ) { $elem.closest('form').submit(); } } } /** * Allows a custom callback function to be executed when the user successfully submits the captcha. * * @since 2.4.x The callback will be a function if reCAPTCHA v2 Invisible is used. * @since 2.2.5.20 * * @param string|false|object The name of the callback function or the function object itself to be executed when the user successfully submits the captcha. * @param object $elem The jQuery object containing the div element with the ginput_recaptcha class for the current reCaptcha field. */ callback = gform.applyFilters( 'gform_recaptcha_callback', callback, $elem ); if ( callback ) { parameters.callback = callback; } $elem.data( 'widget-id', grecaptcha.render( this.id, parameters ) ); if ( parameters.tabindex ) { $elem.find( 'iframe' ).attr( 'tabindex', parameters.tabindex ); } $elem.addClass( 'gform-initialized' ); gform.doAction( 'gform_post_recaptcha_render', $elem ); } ); }, /** * Helper function to determine whether a recaptcha is pending. * * @since 2.4.23 * * @param {Object} form jQuery form object. * @returns {boolean} */ gformIsRecaptchaPending: function( form ) { var recaptcha = form.find( '.ginput_recaptcha' ), recaptchaResponse; if ( !recaptcha.length || recaptcha.data( 'size' ) !== 'invisible' ) { return false; } recaptchaResponse = recaptcha.find( '.g-recaptcha-response' ); return !( recaptchaResponse.length && recaptchaResponse.val() ); }, /** * @function gform.recaptcha.needsRender * @description Is there an non rendered Recaptcha field on the page? * * @since 2.5.6 */ needsRender: function() { return document.querySelectorAll( '.ginput_recaptcha:not(.gform-initialized)' )[ 0 ]; }, /** * @function gform.recaptcha.renderOnRecaptchaLoaded * @description Render recaptcha fields once the library is available, only if non rendered elements are present. * * @since 2.5.6 */ renderOnRecaptchaLoaded: function() { // if nothing to render, exit if ( ! gform.recaptcha.needsRender() ) { return; } var gfRecaptchaPoller = setInterval( function() { if ( ! window.grecaptcha || ! window.grecaptcha.render ) { return; } this.renderRecaptcha(); clearInterval( gfRecaptchaPoller ); }, 100 ); } }; gform.initializeOnLoaded( gform.recaptcha.renderOnRecaptchaLoaded ); jQuery( document ).on( 'gform_post_render', gform.recaptcha.renderOnRecaptchaLoaded ); window.renderRecaptcha = gform.recaptcha.renderRecaptcha; window.gformIsRecaptchaPending = gform.recaptcha.gformIsRecaptchaPending; //---------------------------------------- //----- SINGLE FILE UPLOAD FUNCTIONS ----- //---------------------------------------- function gformValidateFileSize( field, max_file_size ) { var validation_element; // Get validation message element. if ( jQuery( field ).closest( 'div' ).siblings( '.validation_message' ).length > 0 ) { validation_element = jQuery( field ).closest( 'div' ).siblings( '.validation_message' ); } else { validation_element = jQuery( field ).siblings( '.validation_message' ); } // If file API is not supported within browser, return. if ( ! window.FileReader || ! window.File || ! window.FileList || ! window.Blob ) { return; } // Get selected file. var file = field.files[0]; // If selected file is larger than maximum file size, set validation message and unset file selection. if ( file && file.size > max_file_size ) { // Set validation message. validation_element.text(file.name + " - " + gform_gravityforms.strings.file_exceeds_limit); // Announce error. wp.a11y.speak( file.name + " - " + gform_gravityforms.strings.file_exceeds_limit ); } else { // Reset validation message. validation_element.remove(); } } //---------------------------------------- //------ MULTIFILE UPLOAD FUNCTIONS ------ //---------------------------------------- (function (gfMultiFileUploader, $) { gfMultiFileUploader.uploaders = {}; var strings = typeof gform_gravityforms != 'undefined' ? gform_gravityforms.strings : {}; var imagesUrl = typeof gform_gravityforms != 'undefined' ? gform_gravityforms.vars.images_url : ""; $(document).on('gform_post_render', function(e, formID){ $("form#gform_" + formID + " .gform_fileupload_multifile").each(function(){ setup(this); }); var $form = $("form#gform_" + formID); if($form.length > 0){ $form.on( 'submit', function(){ var pendingUploads = false; $.each(gfMultiFileUploader.uploaders, function(i, uploader){ if(uploader.total.queued>0){ pendingUploads = true; return false; } }); if(pendingUploads){ alert(strings.currently_uploading); window["gf_submitting_" + formID] = false; $('#gform_ajax_spinner_' + formID).remove(); return false; } }); } }); $(document).on("gform_post_conditional_logic", function(e,formID, fields, isInit){ if(!isInit){ $.each(gfMultiFileUploader.uploaders, function(i, uploader){ uploader.refresh(); }); } }); $(document).ready(function () { if((typeof adminpage !== 'undefined' && adminpage === 'toplevel_page_gf_edit_forms')|| typeof plupload == 'undefined'){ $(".gform_button_select_files").prop("disabled", true); } else if (typeof adminpage !== 'undefined' && adminpage.indexOf('_page_gf_entries') > -1) { $(".gform_fileupload_multifile").each(function(){ setup(this); }); } }); gfMultiFileUploader.setup = function (uploadElement){ setup( uploadElement ); }; function setup(uploadElement){ var settings = $(uploadElement).data('settings'); var uploader = new plupload.Uploader(settings); formID = uploader.settings.multipart_params.form_id; gfMultiFileUploader.uploaders[settings.container] = uploader; var formID; var uniqueID; uploader.bind( 'Init', function( up, params ) { if ( ! up.features.dragdrop ) { $( ".gform_drop_instructions" ).hide(); } setFieldAccessibility( up.settings.container ); toggleLimitReached( up.settings ); } ); gfMultiFileUploader.toggleDisabled = function (settings, disabled){ var button = typeof settings.browse_button == "string" ? $("#" + settings.browse_button) : $(settings.browse_button); button.prop("disabled", disabled); }; /** * @function setFieldAccessibility * @description Patches accessibility issues with the plupload multi file container. * * @since 2.5.1 * * @param {Node} container The generated plupload container. */ function setFieldAccessibility( container ) { var input = container.querySelectorAll( 'input[type="file"]' )[ 0 ]; var button = container.querySelectorAll( '.gform_button_select_files' )[ 0 ]; var label = $( uploadElement ).closest( '.gfield' ).find( '.gfield_label' )[ 0 ]; if ( ! input || ! label || ! button ) { return; } label.setAttribute( 'for', input.id ); button.setAttribute( 'aria-label', button.innerText.toLowerCase() + ', ' + label.innerText.toLowerCase() ); input.setAttribute( 'tabindex', '-1' ); input.setAttribute( 'aria-hidden', 'true' ); } function addMessage( messagesID, message) { $( "#" + messagesID ).prepend( "<li class='gfield_description gfield_validation_message'>" + htmlEncode( message ) + "</li>" ); // Announce errors. setTimeout(function () { wp.a11y.speak( $( "#" + messagesID ).text() ); }, 1000 ); } function removeMessage(messagesID, message) { $("#" + messagesID + " li:contains('" + message + "')").remove(); } function toggleLimitReached(settings) { var limit = parseInt(settings.gf_vars.max_files, 10); if (limit > 0) { var totalCount = countFiles(settings.multipart_params.field_id), limitReached = totalCount >= limit; gfMultiFileUploader.toggleDisabled(settings, limitReached); if (!limitReached) { removeMessage(settings.gf_vars.message_id, strings.max_reached); } } } uploader.init(); uploader.bind('BeforeUpload', function(up, file){ up.settings.multipart_params.original_filename = file.name; }); uploader.bind('FilesAdded', function(up, files) { var max = parseInt(up.settings.gf_vars.max_files,10), fieldID = up.settings.multipart_params.field_id, totalCount = countFiles(fieldID), disallowed = up.settings.gf_vars.disallowed_extensions, extension; if( max > 0 && totalCount >= max){ $.each(files, function(i, file) { up.removeFile(file); return; }); return; } $.each(files, function(i, file) { extension = file.name.split('.').pop(); if($.inArray(extension, disallowed) > -1){ addMessage(up.settings.gf_vars.message_id, file.name + " - " + strings.illegal_extension); up.removeFile(file); return; } if ((file.status == plupload.FAILED) || (max > 0 && totalCount >= max)){ up.removeFile(file); return; } var size = typeof file.size !== 'undefined' ? plupload.formatSize(file.size) : strings.in_progress, removeFileJs = '$this=jQuery(this); var uploader = gfMultiFileUploader.uploaders.' + up.settings.container.id + ';uploader.stop();uploader.removeFile(uploader.getFile(\'' + file.id +'\'));$this.after(\'' + strings.cancelled + '\'); uploader.start();$this.remove();', statusMarkup = '<div id="{0}" class="ginput_preview"><span class="gfield_fileupload_filename">{1}</span><span class="gfield_fileupload_filesize">{2}</span><span class="gfield_fileupload_progress"><span class="gfield_fileupload_progressbar"><span class="gfield_fileupload_progressbar_progress"></span></span><span class="gfield_fileupload_percent"></span></span><a class="gfield_fileupload_cancel gform-theme-button gform-theme-button--simple" href="javascript:void(0)" title="{3}" onclick="{4}" onkeypress="{4}">{5}</a>'; /** * Filer the file upload markup as it is being uploaded. * * @param {string} statusMarkup Markup template used to render the status of the file being uploaded. * @param {plupload.File} file Instance of File being uploaded. See: https://www.plupload.com/docs/v2/File. * @param {int|string} size File size. * @param {object} strings Array of localized strings relating to the file upload UI. * @param {string} removeFileJs JS used to remove the file when the "Cancel" link is click/pressed. * @param {plupload.Uploader} up Instance of Uploader responsible for uploading current file. See: https://www.plupload.com/docs/v2/Uploader. */ statusMarkup = gform.applyFilters( 'gform_file_upload_status_markup', statusMarkup, file, size, strings, removeFileJs, up ) .gformFormat( file.id, htmlEncode( file.name ), size, strings.cancel_upload, removeFileJs, strings.cancel ); $( '#' + up.settings.filelist ).prepend( statusMarkup ); totalCount++; }); up.refresh(); // Reposition Flash var formElementID = "form#gform_" + formID; var uidElementID = "input:hidden[name='gform_unique_id']"; var uidSelector = formElementID + " " + uidElementID; var $uid = $(uidSelector); if($uid.length==0){ $uid = $(uidElementID); } uniqueID = $uid.val(); if('' === uniqueID){ uniqueID = generateUniqueID(); $uid.val(uniqueID); } if(max > 0 && totalCount >= max){ gfMultiFileUploader.toggleDisabled(up.settings, true); addMessage(up.settings.gf_vars.message_id, strings.max_reached) } up.settings.multipart_params.gform_unique_id = uniqueID; up.start(); }); uploader.bind('UploadProgress', function(up, file) { var html = file.percent + "%"; $('#' + file.id + ' span.gfield_fileupload_percent').html(html); $('#' + file.id + ' span.gfield_fileupload_progressbar_progress').css('width', file.percent + '%'); }); uploader.bind('Error', function(up, err) { if(err.code === plupload.FILE_EXTENSION_ERROR){ var extensions = typeof up.settings.filters.mime_types != 'undefined' ? up.settings.filters.mime_types[0].extensions /* plupoad 2 */ : up.settings.filters[0].extensions; addMessage(up.settings.gf_vars.message_id, err.file.name + " - " + strings.invalid_file_extension + " " + extensions); } else if (err.code === plupload.FILE_SIZE_ERROR) { addMessage(up.settings.gf_vars.message_id, err.file.name + " - " + strings.file_exceeds_limit); } else { var m = "Error: " + err.code + ", Message: " + err.message + (err.file ? ", File: " + err.file.name : ""); addMessage(up.settings.gf_vars.message_id, m); } $('#' + err.file.id ).html(''); up.refresh(); // Reposition Flash }); uploader.bind('ChunkUploaded', function(up, file, result) { var response = $.secureEvalJSON(result.response); if(response.status == "error"){ up.removeFile(file); addMessage(up.settings.gf_vars.message_id, file.name + " - " + response.error.message); $('#' + file.id ).html(''); } else { up.settings.multipart_params[file.target_name] = response.data; } }); uploader.bind('FileUploaded', function(up, file, result) { if (!up.getFile(file.id)) { // The file has been removed from the queue. return; } var response = $.secureEvalJSON(result.response); if (response.status == "error") { addMessage(up.settings.gf_vars.message_id, file.name + " - " + response.error.message); $('#' + file.id).html(''); toggleLimitReached(up.settings); return; } var uploadedName = rgars(response, 'data/uploaded_filename'); var html = '<span class="gfield_fileupload_filename">' + htmlEncode(uploadedName) + '</span><span class="gfield_fileupload_filesize">' + plupload.formatSize(file.size) + '</span>'; html += '<span class="gfield_fileupload_progress gfield_fileupload_progress_complete"><span class="gfield_fileupload_progressbar"><span class="gfield_fileupload_progressbar_progress"></span></span><span class="gfield_fileupload_percent">' + file.percent + '%</span></span>'; var formId = up.settings.multipart_params.form_id; var fieldId = up.settings.multipart_params.field_id; if (typeof gf_legacy !== 'undefined' && gf_legacy.is_legacy) { html = "<img " + "class='gform_delete' " + "src='" + imagesUrl + "/delete.png' " + "onclick='gformDeleteUploadedFile(" + formId + "," + fieldId + ", this);' " + "onkeypress='gformDeleteUploadedFile(" + formId + "," + fieldId + ", this);' " + "alt='" + strings.delete_file + "' " + "title='" + strings.delete_file + "' /> " + html; } else { html = html + "<button class='gform_delete_file gform-theme-button gform-theme-button--simple' onclick='gformDeleteUploadedFile(" + formId + "," + fieldId + ", this);'><span class='dashicons dashicons-trash' aria-hidden='true'></span><span class='screen-reader-text'>" + strings.delete_file + ': ' + htmlEncode(uploadedName) + "</span></button>"; } /** * Allows the markup for the file to be overridden. * * @since 1.9 * @since 2.4.23 Added the response param. * * @param {string} html The HTML for the file name and delete button. * @param {object} file The file upload properties. See: https://www.plupload.com/docs/v2/File. * @param {object} up The uploader properties. See: https://www.plupload.com/docs/v2/Uploader. * @param {object} strings Localized strings relating to file uploads. * @param {string} imagesURL The base URL to the Gravity Forms images directory. * @param {object} response The response from GFAsyncUpload. */ html = gform.applyFilters('gform_file_upload_markup', html, file, up, strings, imagesUrl, response); $('#' + file.id).html(html); $('#' + file.id + ' span.gfield_fileupload_progressbar_progress').css('width', file.percent + '%'); if (file.percent == 100) { if (response.status && response.status == 'ok') { addFile(fieldId, response.data); } else { addMessage(up.settings.gf_vars.message_id, strings.unknown_error + ': ' + file.name); } } }); uploader.bind('FilesRemoved', function (up, files) { toggleLimitReached(up.settings); }); function getAllFiles(){ var selector = '#gform_uploaded_files_' + formID, $uploadedFiles = $(selector), files; files = $uploadedFiles.val(); files = (typeof files === "undefined") || files === '' ? {} : $.parseJSON(files); return files; } function getFiles(fieldID){ var allFiles = getAllFiles(); var inputName = getInputName(fieldID); if(typeof allFiles[inputName] == 'undefined') allFiles[inputName] = []; return allFiles[inputName]; } function countFiles(fieldID){ var files = getFiles(fieldID); return files.length; } function addFile(fieldID, fileInfo){ var files = getFiles(fieldID); files.unshift(fileInfo); setUploadedFiles(fieldID, files); } function setUploadedFiles(fieldID, files){ var allFiles = getAllFiles(); var $uploadedFiles = $('#gform_uploaded_files_' + formID); var inputName = getInputName(fieldID); allFiles[inputName] = files; $uploadedFiles.val($.toJSON(allFiles)); } function getInputName(fieldID){ return "input_" + fieldID; } // fixes drag and drop in IE10 $("#" + settings.drop_element).on({ "dragenter": ignoreDrag, "dragover": ignoreDrag }); function ignoreDrag( e ) { e.preventDefault(); } } function generateUniqueID() { return 'xxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : r & 0x3 | 0x8; return v.toString(16); }); } function htmlEncode(value){ return $('<div/>').text(value).html(); } }(window.gfMultiFileUploader = window.gfMultiFileUploader || {}, jQuery)); //---------------------------------------- //------ GENERAL FUNCTIONS ------- //---------------------------------------- function gformInitSpinner(formId, spinnerUrl, isLegacy = true) { var spinnerCheck = gform.applyFilters('gform_spinner_url', spinnerUrl, formId); if ( spinnerCheck != spinnerUrl ) { isLegacy = true; } jQuery('#gform_' + formId).on( 'submit', function () { if ( isLegacy ) { gformAddSpinner(formId, spinnerUrl); return; } var $spinnerTarget = gform.applyFilters('gform_spinner_target_elem', jQuery('#gform_submit_button_' + formId + ', #gform_wrapper_' + formId + ' .gform_next_button, #gform_send_resume_link_button_' + formId), formId); gformInitializeSpinner(formId, $spinnerTarget); }); } /** * @description Initializes the theme-framework-based spinner after the provided target. * * @since 2.7 * * @param {int} formId The ID of the form within which to initialize the spinner. * @param {object} target The target element after which to inject the spinner. * @param {string} uniqId A unique ID to use for the spinner - used when removing the spinner. * * @return void */ function gformInitializeSpinner( formId, target, uniqId = 'gform-ajax-spinner' ) { if (jQuery('#gform_ajax_spinner_' + formId).length == 0) { var loaderHTML = '<span data-js-spinner-id="' + uniqId + '" id="gform_ajax_spinner_' + formId + '" class="gform-loader"></span>'; var $spinnerTarget = target instanceof jQuery ? target : jQuery( target ); $spinnerTarget.after( loaderHTML ); } } /** * @description Removes an existing theme-framework-based spinner. * * @since 2.7 * * @param {string} uniqId A unique ID to use for the spinner - used when removing the spinner. * * @return void */ function gformRemoveSpinner( uniqId = 'gform-ajax-spinner' ) { var spinner = document.querySelector( '[data-js-spinner-id="' + uniqId + '"]' ); if ( ! spinner ) { return; } spinner.remove(); } function gformAddSpinner(formId, spinnerUrl) { if (typeof spinnerUrl == 'undefined' || !spinnerUrl) { spinnerUrl = gform.applyFilters('gform_spinner_url', gf_global.spinnerUrl, formId); } if (jQuery('#gform_ajax_spinner_' + formId).length == 0) { /** * Filter the element after which the AJAX spinner will be inserted. * * @since 2.0 * * @param object $targetElem jQuery object containing all of the elements after which the AJAX spinner will be inserted. * @param int formId ID of the current form. */ var $spinnerTarget = gform.applyFilters('gform_spinner_target_elem', jQuery('#gform_submit_button_' + formId + ', #gform_wrapper_' + formId + ' .gform_next_button, #gform_send_resume_link_button_' + formId), formId); $spinnerTarget.after('<img id="gform_ajax_spinner_' + formId + '" class="gform_ajax_spinner" src="' + spinnerUrl + '" alt="" />'); } } //---------------------------------------- //------ TINYMCE FUNCTIONS --------------- //---------------------------------------- /** * @function gformReInitTinymceInstance * @description Reinitializes a tinymce instance bound to a gform field if found. * * @since 2.5 * * @param formId {int} Required. The form id. * @param fieldId {int} Required. The field id. */ function gformReInitTinymceInstance( formId, fieldId ) { // check for required arguments if ( ! formId || ! fieldId ) { gform.console.error( 'gformReInitTinymceInstance requires a form and field id.' ); return; } // make sure we have tinymce var tinymce = window.tinymce; if ( ! tinymce ) { gform.console.error( 'gformReInitTinymceInstance requires tinymce to be available.' ); return; } // get the editor instance by form and field id and bail if not found var editor = tinymce.get( 'input_' + formId + '_' + fieldId ); if ( ! editor ) { gform.console.error( 'gformReInitTinymceInstance did not find an instance for input_' + formId + '_' + fieldId + '.' ); return; } // get the settings, destroy the instance and reinitialize var settings = jQuery.extend( {}, editor.settings ); editor.remove(); tinymce.init( settings ); gform.console.log( 'gformReInitTinymceInstance reinitialized TinyMCE on input_' + formId + '_' + fieldId + '.' ); } //---------------------------------------- //------ EVENT FUNCTIONS ----------------- //---------------------------------------- var __gf_keyup_timeout; jQuery( document ).on( 'change keyup', '.gfield input, .gfield select, .gfield textarea', function( event ) { gf_raw_input_change( event, this ); } ); function gf_raw_input_change( event, elem ) { // clear regardless of event type for maximum efficiency ;) clearTimeout( __gf_keyup_timeout ); var $input = jQuery( elem ), htmlId = $input.attr( 'id' ), fieldId = gf_get_input_id_by_html_id( htmlId ), formId = gf_get_form_id_by_html_id( htmlId ), /** * Filter the field meta generated by a raw input change. * * @since 2.4.1 * * @param object fieldMeta An object containing the field ID and form ID of the triggering Gravity Forms field. * @param object $input The jQuery object for the triggering field element. * @param object event The raw JS event. */ fieldMeta = gform.applyFilters( 'gform_field_meta_raw_input_change', { fieldId: fieldId, formId: formId }, $input, event ); fieldId = fieldMeta.fieldId; formId = fieldMeta.formId; if( ! fieldId ) { return; } var isChangeElem = $input.is( ':checkbox' ) || $input.is( ':radio' ) || $input.is( 'select' ), isKeyupElem = ! isChangeElem || $input.is( 'textarea' ); if( event.type == 'keyup' && ! isKeyupElem ) { return; } else if( event.type == 'change' && ! isChangeElem && ! isKeyupElem ) { return; } if( event.type == 'keyup' ) { __gf_keyup_timeout = setTimeout( function() { gf_input_change( elem, formId, fieldId ); }, 300 ); } else { gf_input_change( elem, formId, fieldId ); } } /** * Get the input id from a form element's HTML id. * * @param {string} htmlId The HTML id of a form element. * * @returns {string} inputId The input id. */ function gf_get_input_id_by_html_id( htmlId ) { var ids = gf_get_ids_by_html_id( htmlId ), id = ids[ ids.length - 1 ]; if ( ids.length == 3 ) { ids.shift(); id = ids.join( '.' ); } return id; } /** * Get the form id from a form element's HTML id. * * @param {string} htmlId The HTML id of a form element. * * @returns {string} formId The form id. */ function gf_get_form_id_by_html_id( htmlId ) { var ids = gf_get_ids_by_html_id( htmlId ); return ids[0]; } /** * Get the form, field, and input id by a form elements HTML id. * * Note: Only multi-input fields will be return an input ID. * * @param {string} htmlId The HTML id of a form element. * * @returns {array} ids An array contain the form, field and input id. */ function gf_get_ids_by_html_id( htmlId ) { var ids = htmlId ? htmlId.split( '_' ) : []; for( var i = ids.length - 1; i >= 0; i-- ) { if ( ! gformIsNumber( ids[ i ] ) ) { ids.splice( i, 1 ); } } return ids; } function gf_input_change( elem, formId, fieldId ) { gform.doAction( 'gform_input_change', elem, formId, fieldId ); } function gformExtractFieldId( inputId ) { var fieldId = parseInt( inputId.toString().split( '.' )[0],10 ); return ! fieldId ? inputId : fieldId; } function gformExtractInputIndex( inputId ) { var inputIndex = parseInt( inputId.toString().split( '.' )[1],10 ); return ! inputIndex ? false : inputIndex; } jQuery( document ).on( 'submit.gravityforms', '.gform_wrapper form', function( event ) { var formWrapper = jQuery( this ).closest( '.gform_wrapper' ), formID = formWrapper.attr( 'id' ).split( '_' )[ 2 ], hasPages = formWrapper.find( '.gform_page' ).length > 0, sourcePage = parseInt( formWrapper.find( 'input[name^="gform_source_page_number_"]' ).val(), 10 ), targetPage = parseInt( formWrapper.find( 'input[name^="gform_target_page_number_"]' ).val(), 10 ), isSubmit = targetPage === 0, isNextSubmit = ! isSubmit && ( targetPage > sourcePage ), isSave = jQuery( '#gform_save_' + formID ).val() === '1', submitButton; // Get the next or submit button. if ( hasPages ) { // Get the visible page. var visiblePage = formWrapper.find( '.gform_page:visible' ), buttonType = isNextSubmit ? 'next' : 'submit'; submitButton = visiblePage.find( '.gform_page_footer [id^="gform_' + buttonType + '_button_"]' ); } else { submitButton = formWrapper.find( '#gform_submit_button_' + formID ); } if ( isSave ) { wp.a11y.speak( window.gf_global.strings.formSaved ); } var isButtonHidden = ! submitButton.is(':visible'), isButtonDisabled = submitButton.is( ':disabled' ), abortSubmission = ! isSave && ( isSubmit || isNextSubmit ) && ( isButtonHidden || isButtonDisabled ); // If we are not saving or returning to an earlier page and the next/submit button is hidden abort the submission. if ( abortSubmission ) { window[ 'gf_submitting_' + formID ] = false; formWrapper.find( '.gform_ajax_spinner' ).remove(); event.preventDefault(); } else if ( isSubmit || isSubmit ) { var $reCaptcha = formWrapper.find( '.ginput_recaptcha' ); if ( $reCaptcha.length !== 0 && $reCaptcha.data( 'size' ) === 'invisible' ) { // Check for the verified invisible captcha token first. var $reCaptchaResponse = formWrapper.find( 'input[name="g-recaptcha-response"]' ); if ( $reCaptchaResponse.length === 0 ) { $reCaptchaResponse = $reCaptcha.find( '.g-recaptcha-response' ); } var token = $reCaptchaResponse.val(); if ( ! token ) { // Execute the invisible captcha. grecaptcha.execute($reCaptcha.data('widget-id')); // Once the reCaptcha is triggered, set gf_submitting to false, so the form could be submitted if the // reCaptcha modal is closed (by clicking on the area out of the modal or the reCaptcha response expires) // do it after 4 seconds to reduce chance of multiple clicks when modal is not displayed setTimeout( function() { window['gf_submitting_' + formID] = false; }, 4000); event.preventDefault(); } } } }); //---------------------------------------- //------ HELPER FUNCTIONS ---------------- //---------------------------------------- if( ! window['rgars'] ) { function rgars( array, prop ) { var props = prop.split( '/' ), value = array; for( var i = 0; i < props.length; i++ ) { value = rgar( value, props[ i ] ); } return value; } } if( ! window['rgar'] ) { function rgar( array, prop ) { if ( typeof array[ prop ] != 'undefined' ) { return array[ prop ]; } return ''; } } if ( ! String.prototype.gformFormat ) { String.prototype.gformFormat = function() { var args = arguments; return this.replace( /{(\d+)}/g, function( match, number ) { return typeof args[ number ] != 'undefined' ? args[ number ] : match; } ); }; } // deprecated. remove in 2.8 String.prototype.format = function() { var args = arguments; console.warn( 'String.format will be replaced with String.gformFormat in Gravity Forms version 2.8.' ); return this.replace( /{(\d+)}/g, function( match, number ) { return typeof args[ number ] != 'undefined' ? args[ number ] : match; } ); }; /** * Toggle the dropdown submenus in the form editor menu bar. * * @since 2.5 */ jQuery( document ).ready( function() { jQuery( '#gform-form-toolbar__menu' ) .on( 'mouseenter', '> li',function() { jQuery( this ).find( '.gform-form-toolbar__submenu' ).toggleClass( 'open' ); jQuery( this ).find( '.has_submenu' ).toggleClass( 'submenu-open' ); } ); jQuery( '#gform-form-toolbar__menu' ) .on( 'mouseleave', '> li',function() { jQuery( '.gform-form-toolbar__submenu.open' ).removeClass( 'open' ); jQuery( '.has_submenu.submenu-open' ).removeClass( 'submenu-open' ); } ); jQuery( '#gform-form-toolbar__menu .has_submenu' ) .on( 'click', function( e ) { e.preventDefault(); } ); } ); /** * Add a containing class to fields with multiple inputs that we want to display inline. * * @since 2.5 */ jQuery( document ).ready( function() { var settingsFields = jQuery( '.gform-settings-field' ); settingsFields.each( function() { if ( jQuery( this ).find( '> .gform-settings-input__container' ).length > 1 ) { jQuery( this ).addClass( 'gform-settings-field--multiple-inputs' ); } } ); } ); jQuery( function() { gform.tools.trigger( 'gform_main_scripts_loaded' ); } );
Edit
Download
Unzip
Chmod
Delete