PageSourceSearch

https://muaythaiontario.org/wp-content/plugins/gravityforms/js/gravityforms.js?ver=2.4.16.10

js muaythaiontario.org collected 2026-10-04 05:23:13 UTC 78,931 bytes, 2,439 lines download raw bytes

1
2// "prop" method fix for previous versions of jQuery (1.5 and below)
3if( typeof jQuery.fn.prop === 'undefined' ) {
4    jQuery.fn.prop = jQuery.fn.attr;
5}
6
7jQuery(document).ready(function(){
8    //Formatting free form currency fields to currency
9    jQuery(document).bind('gform_post_render', gformBindFormatPricingFields);
10});
11
12function gformBindFormatPricingFields(){
13	// Namespace the event and remove before adding to prevent double binding.
14    jQuery(".ginput_amount, .ginput_donation_amount").off('change.gform').on("change.gform", function(){
15        gformFormatPricingField(this);
16    });
17
18    jQuery(".ginput_amount, .ginput_donation_amount").each(function(){
19        gformFormatPricingField(this);
20    });
21}
22
23//------------------------------------------------
24//---------- CURRENCY ----------------------------
25//------------------------------------------------
26function Currency(currency){
27    this.currency = currency;
28
29    this.toNumber = function(text){
30
31        if(this.isNumeric(text)) {
32			return parseFloat(text);
33		}
34
35        return gformCleanNumber(text, this.currency["symbol_right"], this.currency["symbol_left"], this.currency["decimal_separator"]);
36    };
37
38	/**
39	 * Attempts to clean the specified number and formats it as currency.
40	 *
41	 * @since 2.1.1.16 Allow the overriding of numerical checks.
42	 *
43	 * @param number    int  Number to be formatted. It can be a clean number, or an already formatted number.
44	 * @param isNumeric bool Whether or not the number is guaranteed to be a clean, unformatted number.
45	 *                       When false the function will attempt to clean the number. Defaults to false.
46	 *
47	 * @return string A number formatted as currency.
48	 */
49	this.toMoney = function(number, isNumeric){
50
51		isNumeric = isNumeric || false; //isNumeric is an optional parameter. Defaults to false
52
53		if( ! isNumeric ) {
54			//Cleaning number, removing all formatting
55			number = gformCleanNumber(number, this.currency["symbol_right"], this.currency["symbol_left"], this.currency["decimal_separator"]);
56		}
57
58		if(number === false) {
59			return "";
60		}
61
62        number = number + "";
63        negative = "";
64        if(number[0] == "-"){
65
66            number = parseFloat(number.substr(1));
67			negative = '-';
68		}
69
70        money = this.numberFormat(number, this.currency["decimals"], this.currency["decimal_separator"], this.currency["thousand_separator"]);
71
72		if ( money == '0.00' ){
73			negative = '';
74		}
75
76        var symbol_left = this.currency["symbol_left"] ? this.currency["symbol_left"] + this.currency["symbol_padding"] : "";
77        var symbol_right = this.currency["symbol_right"] ? this.currency["symbol_padding"] + this.currency["symbol_right"] : "";
78
79		money =  negative + this.htmlDecode(symbol_left) + money + this.htmlDecode(symbol_right);
80
81		return money;
82    };
83
84
85	/**
86	 * Formats a number given the specified parameters.
87	 *
88	 * @since Unknown
89	 *
90	 * @param number        int    Number to be formatted. Must be a clean, unformatted  format.
91	 * @param decimals      int    Number of decimals that the output should contain.
92	 * @param dec_point     string Character to use as the decimal separator. Defaults to ".".
93	 * @param thousands_sep string Character to use as the thousand separator. Defaults to ",".
94	 * @param padded        bool   Pads output with zeroes if the number is exact. For example, 1.200.
95	 *
96	 * @return string The formatted number.
97	 */
98    this.numberFormat = function(number, decimals, dec_point, thousands_sep, padded){
99
100    	var padded = typeof padded == 'undefined';
101        number = (number+'').replace(',', '').replace(' ', '');
102        var n = !isFinite(+number) ? 0 : +number,
103        prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
104        sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
105        s = '',
106
107        toFixedFix = function (n, prec) {
108            var k = Math.pow(10, prec);
109            return '' + Math.round(n * k) / k;
110        };
111
112        if(decimals == '0') {
113
114            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 )
115
116            s = ('' + Math.round(n)).split('.');
117        } else
118        if(decimals == -1) {
119            s = ('' + n).split('.');
120        } else {
121
122            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 )
123
124            // Fix for IE parseFloat(0.55).toFixed(0) = 0;
125            s = toFixedFix(n, prec).split('.');
126        }
127
128        if (s[0].length > 3) {
129            s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
130        }
131
132        if(padded) {
133            if ((s[1] || '').length < prec) {
134                s[1] = s[1] || '';
135                s[1] += new Array(prec - s[1].length + 1).join('0');
136            }
137        }
138
139        return s.join(dec);
140    }
141
142    this.isNumeric = function(number){
143        return gformIsNumber(number);
144    };
145
146    this.htmlDecode = function(text) {
147        var c,m,d = text;
148
149        // look for numerical entities &#34;
150        var arr=d.match(/&#[0-9]{1,5};/g);
151
152        // if no matches found in string then skip
153        if(arr!=null){
154            for(var x=0;x<arr.length;x++){
155                m = arr[x];
156                c = m.substring(2,m.length-1); //get numeric part which is refernce to unicode character
157                // if its a valid number we can decode
158                if(c >= -32768 && c <= 65535){
159                    // decode every single match within string
160                    d = d.replace(m, String.fromCharCode(c));
161                }else{
162                    d = d.replace(m, ""); //invalid so replace with nada
163                }
164            }
165        }
166        return d;
167    };
168}
169
170/**
171 * Gets a formatted number and returns a clean "decimal dot" number.
172 *
173 * Note: Input must be formatted according to the specified parameters (symbol_right, symbol_left, decimal_separator).
174 * @example input -> $1.20, output -> 1.2
175 *
176 * @since 2.1.1.16 Modified to support additional param in Currency.toMoney.
177 *
178 * @param text              string The currency-formatted number.
179 * @param symbol_right      string The symbol used on the right.
180 * @param symbol_left       string The symbol used on the left.
181 * @param decimal_separator string The decimal separator being used.
182 *
183 * @return float The unformatted numerical value.
184 */
185function gformCleanNumber(text, symbol_right, symbol_left, decimal_separator){
186    var clean_number = '',
187        float_number = '',
188        digit = '',
189        is_negative = false;
190
191    //converting to a string if a number as passed
192    text = text + " ";
193
194    //Removing symbol in unicode format (i.e. &#4444;)
195    text = text.replace(/&.*?;/g, "");
196
197    //Removing symbol from text
198    text = text.replace(symbol_right, "");
199    text = text.replace(symbol_left, "");
200
201    //Removing all non-numeric characters
202    for(var i=0; i<text.length; i++){
203        digit = text.substr(i,1);
204        if( (parseInt(digit,10) >= 0 && parseInt(digit,10) <= 9) || digit == decimal_separator )
205            clean_number += digit;
206        else if(digit == '-')
207            is_negative = true;
208    }
209
210    //Removing thousand separators but keeping decimal point
211    for(var i=0; i<clean_number.length; i++) {
212        digit = clean_number.substr(i,1);
213        if (digit >= '0' && digit <= '9')
214            float_number += digit;
215        else if(digit == decimal_separator){
216            float_number += ".";
217        }
218    }
219
220    if(is_negative)
221        float_number = "-" + float_number;
222
223    return gformIsNumber(float_number) ? parseFloat(float_number) : false;
224}
225
226function gformGetDecimalSeparator(numberFormat){
227    var s;
228    switch (numberFormat){
229        case 'currency' :
230            var currency = new Currency(gf_global.gf_currency_config);
231            s = currency.currency["decimal_separator"];
232            break;
233        case 'decimal_comma' :
234            s = ',';
235            break;
236        default :
237            s = "."
238     }
239    return s;
240}
241
242function gformIsNumber(n) {
243  return !isNaN(parseFloat(n)) && isFinite(n);
244}
245
246function gformIsNumeric(value, number_format){
247
248    switch(number_format){
249        case "decimal_dot" :
250            var r = new RegExp("^(-?[0-9]{1,3}(?:,?[0-9]{3})*(?:\.[0-9]+)?)$");
251            return r.test(value);
252        break;
253
254        case "decimal_comma" :
255            var r = new RegExp("^(-?[0-9]{1,3}(?:\.?[0-9]{3})*(?:,[0-9]+)?)$");
256            return r.test(value);
257        break;
258    }
259    return false;
260}
261
262//------------------------------------------------
263//---------- MULTI-PAGE --------------------------
264//------------------------------------------------
265function gformDeleteUploadedFile(formId, fieldId, deleteButton){
266    var parent = jQuery("#field_" + formId + "_" + fieldId);
267
268    var fileIndex = jQuery(deleteButton).parent().index();
269
270    parent.find(".ginput_preview").eq(fileIndex).remove();
271
272    //displaying single file upload field
273    parent.find('input[type="file"],.validation_message,#extensions_message_' + formId + '_' + fieldId).removeClass("gform_hidden");
274
275    //displaying post image label
276    parent.find(".ginput_post_image_file").show();
277
278    //clearing post image meta fields
279    parent.find("input[type=\"text\"]").val('');
280
281    //removing file from uploaded meta
282    var filesJson = jQuery('#gform_uploaded_files_' + formId).val();
283
284    if(filesJson){
285        var files = jQuery.secureEvalJSON(filesJson);
286        if(files) {
287            var inputName = "input_" + fieldId;
288            var $multfile = parent.find("#gform_multifile_upload_" + formId + "_" + fieldId );
289            if( $multfile.length > 0 ) {
290                files[inputName].splice(fileIndex, 1);
291                var settings = $multfile.data('settings');
292                var max = settings.gf_vars.max_files;
293                jQuery("#" + settings.gf_vars.message_id).html('');
294                if(files[inputName].length < max)
295                    gfMultiFileUploader.toggleDisabled(settings, false);
296
297            } else {
298                files[inputName] = null;
299            }
300
301            jQuery('#gform_uploaded_files_' + formId).val(jQuery.toJSON(files));
302        }
303    }
304}
305
306
307//------------------------------------------------
308//---------- PRICE -------------------------------
309//------------------------------------------------
310var _gformPriceFields = new Array();
311var _anyProductSelected;
312
313function gformIsHidden(element){
314    return element.parents('.gfield').not(".gfield_hidden_product").css("display") == "none";
315}
316
317function gformCalculateTotalPrice(formId){
318
319    if(!_gformPriceFields[formId])
320        return;
321
322    var price = 0;
323
324    _anyProductSelected = false; //Will be used by gformCalculateProductPrice().
325    for(var i=0; i<_gformPriceFields[formId].length; i++){
326        price += gformCalculateProductPrice(formId, _gformPriceFields[formId][i]);
327    }
328
329    //add shipping price if a product has been selected
330    if(_anyProductSelected){
331        //shipping price
332        var shipping = gformGetShippingPrice(formId)
333        price += shipping;
334    }
335
336    //gform_product_total filter. Allows uers to perform custom price calculation
337    if(window["gform_product_total"])
338        price = window["gform_product_total"](formId, price);
339
340
341    price = gform.applyFilters('gform_product_total', price, formId);
342
343    //updating total
344    var totalElement = jQuery(".ginput_total_" + formId);
345    if( totalElement.length > 0 ) {
346
347        var currentTotal = totalElement.next().val(),
348            formattedTotal = gformFormatMoney(price, true);
349
350        if (currentTotal != price) {
351            totalElement.next().val(price).change();
352        }
353
354        if (formattedTotal != totalElement.first().text()) {
355            totalElement.html(formattedTotal);
356        }
357
358    }
359}
360
361function gformGetShippingPrice(formId){
362    var shippingField = jQuery(".gfield_shipping_" + formId + " input[type=\"hidden\"], .gfield_shipping_" + formId + " select, .gfield_shipping_" + formId + " input:checked");
363    var shipping = 0;
364    if(shippingField.length == 1 && !gformIsHidden(shippingField)){
365        if(shippingField.attr("type") && shippingField.attr("type").toLowerCase() == "hidden")
366            shipping = shippingField.val();
367        else
368            shipping = gformGetPrice(shippingField.val());
369    }
370
371    return gformToNumber(shipping);
372}
373
374function gformGetFieldId(element){
375    var id = jQuery(element).attr("id");
376    var pieces = id.split("_");
377    if(pieces.length <=0)
378        return 0;
379
380    var fieldId = pieces[pieces.length-1];
381    return fieldId;
382
383}
384
385function gformCalculateProductPrice(form_id, productFieldId){
386
387    var suffix = '_' + form_id + '_' + productFieldId;
388
389
390    //Drop down auto-calculating labels
391    jQuery('.gfield_option' + suffix + ', .gfield_shipping_' + form_id).find('select').each(function(){
392
393        var dropdown_field = jQuery(this);
394        var selected_price = gformGetPrice(dropdown_field.val());
395        var field_id = dropdown_field.attr('id').split('_')[2];
396        dropdown_field.children('option').each(function(){
397            var choice_element = jQuery(this);
398            var label = gformGetOptionLabel(choice_element, choice_element.val(), selected_price, form_id, field_id);
399            choice_element.html(label);
400        });
401    });
402
403
404    //Checkboxes labels with prices
405    jQuery('.gfield_option' + suffix).find('.gfield_checkbox').find('input:checkbox').each(function(){
406        var checkbox_item = jQuery(this);
407        var id = checkbox_item.attr('id');
408        var field_id = id.split('_')[2];
409        var label_id = id.replace('choice_', '#label_');
410        var label_element = jQuery(label_id);
411        var label = gformGetOptionLabel(label_element, checkbox_item.val(), 0, form_id, field_id);
412        label_element.html(label);
413    });
414
415
416    //Radio button auto-calculating lables
417    jQuery('.gfield_option' + suffix + ', .gfield_shipping_' + form_id).find('.gfield_radio').each(function(){
418        var selected_price = 0;
419        var radio_field = jQuery(this);
420        var id = radio_field.attr('id');
421        var fieldId = id.split('_')[2];
422        var selected_value = radio_field.find('input:radio:checked').val();
423
424        if(selected_value)
425            selected_price = gformGetPrice(selected_value);
426
427        radio_field.find('input:radio').each(function(){
428            var radio_item = jQuery(this);
429            var label_id = radio_item.attr('id').replace('choice_', '#label_');
430            var label_element = jQuery(label_id);
431            if ( label_element ) {
432                var label = gformGetOptionLabel(label_element, radio_item.val(), selected_price, form_id, fieldId);
433                label_element.html(label);
434            }
435        });
436    });
437
438	var price = gformGetBasePrice(form_id, productFieldId);
439	var quantity = gformGetProductQuantity( form_id, productFieldId );
440
441	//calculating options if quantity is more than 0 (a product was selected).
442	if( quantity > 0 ) {
443
444		jQuery('.gfield_option' + suffix).find('input:checked, select').each(function(){
445			if(!gformIsHidden(jQuery(this)))
446				price += gformGetPrice(jQuery(this).val());
447		});
448
449		//setting global variable if quantity is more than 0 (a product was selected). Will be used when calculating total
450		_anyProductSelected = true;
451	}
452
453    price = price * quantity;
454
455	price = gformRoundPrice(price) ;
456
457
458    return price;
459}
460
461
462function gformGetProductQuantity(formId, productFieldId) {
463
464    //If product is not selected
465    if (!gformIsProductSelected(formId, productFieldId)) {
466        return 0;
467    }
468
469    var quantity,
470        quantityInput = jQuery('#ginput_quantity_' + formId + '_' + productFieldId),
471        numberFormat;
472
473    if (gformIsHidden(quantityInput)) {
474        return 0;
475    }
476
477    if (quantityInput.length > 0) {
478
479        quantity = quantityInput.val();
480
481    } else {
482
483        quantityInput = jQuery('.gfield_quantity_' + formId + '_' + productFieldId + ' :input');
484        quantity = 1;
485
486        if (quantityInput.length > 0) {
487            quantity = quantityInput.val();
488
489            var htmlId = quantityInput.attr('id'),
490                fieldId = gf_get_input_id_by_html_id(htmlId);
491
492            numberFormat = gf_get_field_number_format( fieldId, formId, 'value' );
493        }
494
495    }
496
497    if (!numberFormat)
498        numberFormat = 'currency';
499
500    var decimalSeparator = gformGetDecimalSeparator(numberFormat);
501
502    quantity = gformCleanNumber(quantity, '', '', decimalSeparator);
503    if (!quantity)
504        quantity = 0;
505
506    return quantity;
507}
508
509
510function gformIsProductSelected( formId, productFieldId ) {
511
512	var suffix = "_" + formId + "_" + productFieldId;
513
514	var productField = jQuery("#ginput_base_price" + suffix + ", .gfield_donation" + suffix + " input[type=\"text\"], .gfield_product" + suffix + " .ginput_amount");
515	if( productField.val() && ! gformIsHidden(productField) ){
516		return true;
517	}
518	else
519	{
520		productField = jQuery(".gfield_product" + suffix + " select, .gfield_product" + suffix + " input:checked, .gfield_donation" + suffix + " select, .gfield_donation" + suffix + " input:checked");
521		if( productField.val() && ! gformIsHidden(productField) ){
522			return true;
523		}
524	}
525
526	return false;
527}
528
529function gformGetBasePrice(formId, productFieldId){
530
531    var suffix = "_" + formId + "_" + productFieldId;
532    var price = 0;
533    var productField = jQuery("#ginput_base_price" + suffix+ ", .gfield_donation" + suffix + " input[type=\"text\"], .gfield_product" + suffix + " .ginput_amount");
534    if(productField.length > 0){
535        price = productField.val();
536
537        //If field is hidden by conditional logic, don't count it for the total
538        if(gformIsHidden(productField)){
539            price = 0;
540        }
541    }
542    else
543    {
544        productField = jQuery(".gfield_product" + suffix + " select, .gfield_product" + suffix + " input:checked, .gfield_donation" + suffix + " select, .gfield_donation" + suffix + " input:checked");
545        var val = productField.val();
546        if(val){
547            val = val.split("|");
548            price = val.length > 1 ? val[1] : 0;
549        }
550
551        //If field is hidden by conditional logic, don't count it for the total
552        if(gformIsHidden(productField))
553            price = 0;
554
555    }
556
557    var c = new Currency(gf_global.gf_currency_config);
558    price = c.toNumber(price);
559    return price === false ? 0 : price;
560}
561
562function gformFormatMoney(text, isNumeric){
563    if(!gf_global.gf_currency_config)
564        return text;
565
566    var currency = new Currency(gf_global.gf_currency_config);
567    return currency.toMoney(text, isNumeric);
568}
569
570function gformFormatPricingField(element){
571    if(gf_global.gf_currency_config){
572        var currency = new Currency(gf_global.gf_currency_config);
573        var price = currency.toMoney(jQuery(element).val());
574        jQuery(element).val(price);
575    }
576}
577
578function gformToNumber(text){
579    var currency = new Currency(gf_global.gf_currency_config);
580    return currency.toNumber(text);
581}
582
583function gformGetPriceDifference(currentPrice, newPrice){
584
585    //getting price difference
586    var diff = parseFloat(newPrice) - parseFloat(currentPrice);
587    price = gformFormatMoney(diff, true);
588    if(diff > 0)
589        price = "+" + price;
590
591    return price;
592}
593
594function gformGetOptionLabel(element, selected_value, current_price, form_id, field_id){
595    element = jQuery(element);
596    var price = gformGetPrice(selected_value);
597    var current_diff = element.attr('price');
598    var original_label = element.html().replace(/<span(.*)<\/span>/i, "").replace(current_diff, "");
599
600    var diff = gformGetPriceDifference(current_price, price);
601    diff = gformToNumber(diff) == 0 ? "" : " " + diff;
602    element.attr('price', diff);
603
604    //don't add <span> for drop down items (not supported)
605    var price_label = element[0].tagName.toLowerCase() == "option" ? " " + diff : "<span class='ginput_price'>" + diff + "</span>";
606    var label = original_label + price_label;
607
608    //calling hook to allow for custom option formatting
609    if(window["gform_format_option_label"])
610        label = gform_format_option_label(label, original_label, price_label, current_price, price, form_id, field_id);
611
612    return label;
613}
614
615function gformGetProductIds(parent_class, element){
616    var classes = jQuery(element).hasClass(parent_class) ? jQuery(element).attr("class").split(" ") : jQuery(element).parents("." + parent_class).attr("class").split(" ");
617    for(var i=0; i<classes.length; i++){
618        if(classes[i].substr(0, parent_class.length) == parent_class && classes[i] != parent_class)
619            return {formId: classes[i].split("_")[2], productFieldId: classes[i].split("_")[3]};
620    }
621    return {formId:0, fieldId:0};
622}
623
624function gformGetPrice(text){
625    var val = text.split("|");
626    var currency = new Currency(gf_global.gf_currency_config);
627
628    if(val.length > 1 && currency.toNumber(val[1]) !== false)
629         return currency.toNumber(val[1]);
630
631    return 0;
632}
633
634function gformRoundPrice(price){
635
636	var currency = new Currency(gf_global.gf_currency_config);
637    var roundedPrice = currency.numberFormat( price, currency.currency['decimals'], '.', '' );
638
639    return parseFloat( roundedPrice );
640}
641
642function gformRegisterPriceField(item){
643
644    if(!_gformPriceFields[item.formId])
645        _gformPriceFields[item.formId] = new Array();
646
647    //ignore price fields that have already been registered
648    for(var i=0; i<_gformPriceFields[item.formId].length; i++)
649        if(_gformPriceFields[item.formId][i] == item.productFieldId)
650            return;
651
652    //registering new price field
653    _gformPriceFields[item.formId].push(item.productFieldId);
654}
655
656function gformInitPriceFields(){
657
658    jQuery(".gfield_price").each(function(){
659
660        var productIds = gformGetProductIds("gfield_price", this);
661        gformRegisterPriceField(productIds);
662
663       jQuery( this ).on( 'change', 'input[type="text"], input[type="number"], select', function() {
664
665           var productIds = gformGetProductIds("gfield_price", this);
666           if(productIds.formId == 0)
667                productIds = gformGetProductIds("gfield_shipping", this);
668
669           jQuery(document).trigger('gform_price_change', [productIds, this]);
670           gformCalculateTotalPrice(productIds.formId);
671       });
672
673       jQuery( this ).on( 'click', 'input[type="radio"], input[type="checkbox"]', function() {
674
675           var productIds = gformGetProductIds("gfield_price", this);
676           if(productIds.formId == 0)
677                productIds = gformGetProductIds("gfield_shipping", this);
678
679           jQuery(document).trigger('gform_price_change', [productIds, this]);
680           gformCalculateTotalPrice(productIds.formId);
681       });
682
683    });
684
685    for(formId in _gformPriceFields){
686
687        //needed when implementing for in loops
688        if(!_gformPriceFields.hasOwnProperty(formId))
689            continue;
690
691        gformCalculateTotalPrice(formId);
692    }
693
694}
695
696
697//-------------------------------------------
698//---------- PASSWORD -----------------------
699//-------------------------------------------
700function gformShowPasswordStrength(fieldId){
701    var password = document.getElementById( fieldId ).value,
702        confirm = document.getElementById( fieldId + '_2' ) ? document.getElementById( fieldId + '_2' ).value : '';
703
704    var result = gformPasswordStrength( password, confirm ),
705        text = window[ 'gf_text' ][ "password_" + result ],
706        resultClass = result === 'unknown' ? 'blank' : result;
707
708    jQuery("#" + fieldId + "_strength").val(result);
709    jQuery("#" + fieldId + "_strength_indicator").removeClass("blank mismatch short good bad strong").addClass(resultClass).html(text);
710}
711
712// Password strength meter
713function gformPasswordStrength( password1, password2 ) {
714
715    if ( password1.length <= 0 ) {
716        return 'blank';
717    }
718
719    var strength = wp.passwordStrength.meter( password1, wp.passwordStrength.userInputBlacklist(), password2 );
720
721    switch ( strength ) {
722
723        case -1:
724            return 'unknown';
725
726        case 2:
727            return 'bad';
728
729        case 3:
730            return 'good';
731
732        case 4:
733            return 'strong';
734
735        case 5:
736            return 'mismatch';
737
738        default:
739            return 'short';
740
741    }
742
743}
744
745function gformToggleShowPassword( fieldId ) {
746    var $password = jQuery( '#' + fieldId ),
747        $button = $password.parent().find( 'button' ),
748        $icon = $button.find( 'span' ),
749        currentType = $password.attr( 'type' );
750
751    switch ( currentType ) {
752        case 'password':
753            $password.attr( 'type', 'text' );
754            $button.attr( 'label', $button.attr( 'data-label-hide' ) );
755            $icon.removeClass( 'dashicons-hidden' ).addClass( 'dashicons-visibility' );
756            break;
757        case 'text':
758            $password.attr( 'type', 'password' );
759            $button.attr( 'label', $button.attr( 'data-label-show' ) );
760            $icon.removeClass( 'dashicons-visibility' ).addClass( 'dashicons-hidden' );
761            break;
762    }
763}
764
765//----------------------------
766//------ CHECKBOX FIELD ------
767//----------------------------
768
769function gformToggleCheckboxes( toggleCheckbox ) {
770
771	var $toggle      = jQuery( toggleCheckbox ).parent(),
772	    $toggleLabel = $toggle.find( 'label' ),
773	    $checkboxes  = $toggle.parent().find( 'li:not( .gchoice_select_all )' ),
774	    formId       = gf_get_form_id_by_html_id( $toggle.parents( '.gfield' ).attr( 'id' ) ),
775	    calcObj      = rgars( window, 'gf_global/gfcalc/' + formId );
776
777	// Set checkboxes state.
778	$checkboxes.each( function() {
779
780		// Set checkbox checked state.
781		jQuery( 'input[type="checkbox"]', this ).prop( 'checked', toggleCheckbox.checked ).trigger( 'change' );
782
783		// Execute onclick event.
784		if ( typeof jQuery( 'input[type="checkbox"]', this )[0].onclick === 'function' ) {
785			jQuery( 'input[type="checkbox"]', this )[0].onclick();
786		}
787
788	} );
789
790	// Change toggle label.
791	if ( toggleCheckbox.checked ) {
792		$toggleLabel.html( $toggleLabel.data( 'label-deselect' ) );
793	} else {
794		$toggleLabel.html( $toggleLabel.data( 'label-select' ) );
795	}
796
797	if ( calcObj ) {
798		calcObj.runCalcs( formId, calcObj.formulaFields );
799	}
800
801}
802
803
804//----------------------------
805//------ LIST FIELD ----------
806//----------------------------
807
808function gformAddListItem( addButton, max ) {
809
810    var $addButton = jQuery( addButton );
811
812    if( $addButton.hasClass( 'gfield_icon_disabled' ) ) {
813        return;
814    }
815
816    var $group     = $addButton.parents( '.gfield_list_group' ),
817        $clone     = $group.clone(),
818        $container = $group.parents( '.gfield_list_container' ),
819        tabindex   = $clone.find( ':input:last' ).attr( 'tabindex' );
820
821    // reset all inputs to empty state
822    $clone
823        .find( 'input, select, textarea' ).attr( 'tabindex', tabindex )
824        .not( ':checkbox, :radio' ).val( '' );
825    $clone.find( ':checkbox, :radio' ).prop( 'checked', false );
826
827    $clone = gform.applyFilters( 'gform_list_item_pre_add', $clone, $group );
828
829    $group.after( $clone );
830
831    gformToggleIcons( $container, max );
832    gformAdjustClasses( $container );
833
834    gform.doAction( 'gform_list_post_item_add', $clone, $container );
835
836}
837
838function gformDeleteListItem( deleteButton, max ) {
839
840    var $deleteButton = jQuery( deleteButton ),
841        $group        = $deleteButton.parents( '.gfield_list_group' ),
842        $container    = $group.parents( '.gfield_list_container' );
843
844    $group.remove();
845
846    gformToggleIcons( $container, max );
847    gformAdjustClasses( $container );
848
849    gform.doAction( 'gform_list_post_item_delete', $container );
850
851}
852
853function gformAdjustClasses( $container ) {
854
855    var $groups = $container.find( '.gfield_list_group' );
856
857    $groups.each( function( i ) {
858
859        var $group       = jQuery( this ),
860            oddEvenClass = ( i + 1 ) % 2 == 0 ? 'gfield_list_row_even' : 'gfield_list_row_odd';
861
862        $group.removeClass( 'gfield_list_row_odd gfield_list_row_even' ).addClass( oddEvenClass );
863
864    } );
865
866}
867
868function gformToggleIcons( $container, max ) {
869
870    var groupCount  = $container.find( '.gfield_list_group' ).length,
871        $addButtons = $container.find( '.add_list_item' );
872
873    $container.find( '.delete_list_item' ).css( 'visibility', groupCount == 1 ? 'hidden' : 'visible' );
874
875    if ( max > 0 && groupCount >= max ) {
876
877        // store original title in the add button
878        $addButtons.data( 'title', $container.find( '.add_list_item' ).attr( 'title' ) );
879        $addButtons.addClass( 'gfield_icon_disabled' ).attr( 'title', '' );
880
881    } else if( max > 0 ) {
882
883        $addButtons.removeClass( 'gfield_icon_disabled' );
884
885        if( $addButtons.data( 'title' ) )   {
886            $addButtons.attr( 'title', $addButtons.data( 'title' ) );
887        }
888
889    }
890}
891
892//-----------------------------------
893//--------- REPEATER FIELD ----------
894//-----------------------------------
895
896function gformAddRepeaterItem( addButton, max ) {
897
898	var $addButton = jQuery( addButton );
899
900	if( $addButton.hasClass( 'gfield_icon_disabled' ) ) {
901		return;
902	}
903
904	var $item     = $addButton.closest( '.gfield_repeater_item' ),
905		$clone     = $item.clone(),
906		$container = $item.closest( '.gfield_repeater_container' ),
907		tabindex   = $clone.find( ':input:last' ).attr( 'tabindex' );
908
909	// reset all inputs to empty state
910	$clone
911		.find( 'input[type!="hidden"], select, textarea' ).attr( 'tabindex', tabindex )
912		.not( ':checkbox, :radio' ).val( '' );
913	$clone.find( ':checkbox, :radio' ).prop( 'checked', false );
914	$clone.find('.validation_message').remove();
915
916	$clone = gform.applyFilters( 'gform_repeater_item_pre_add', $clone, $item );
917
918	$item.after( $clone );
919
920	var $cells = $clone.children('.gfield_repeater_cell');
921	$cells.each(function () {
922		var $subContainer = jQuery(this).find('.gfield_repeater_container').first();
923		if ($subContainer.length > 0) {
924			resetContainerItems = function ($c) {
925				$c.children('.gfield_repeater_items').children('.gfield_repeater_item').each(function (i) {
926					var $children = jQuery(this).children('.gfield_repeater_cell');
927					$children.each(function () {
928						var $subSubContainer = jQuery(this).find('.gfield_repeater_container').first();
929						if ($subSubContainer.length > 0) {
930							resetContainerItems($subSubContainer);
931						}
932					})
933				})
934				$c.children('.gfield_repeater_items').children('.gfield_repeater_item').not(':first').remove();
935			}
936			resetContainerItems($subContainer);
937		}
938	})
939
940	gformResetRepeaterAttributes($container);
941
942	if ( typeof gformInitDatepicker == 'function' ) {
943		$container.find('.ui-datepicker-trigger').remove();
944		$container.find('.hasDatepicker').removeClass('hasDatepicker');
945		gformInitDatepicker();
946	}
947
948	gformBindFormatPricingFields();
949
950	gformToggleRepeaterButtons( $container, max );
951
952	gform.doAction('gform_repeater_post_item_add', $clone, $container);
953
954}
955
956function gformDeleteRepeaterItem(deleteButton, max) {
957
958	var $deleteButton = jQuery(deleteButton),
959		$group = $deleteButton.closest('.gfield_repeater_item'),
960		$container = $group.closest('.gfield_repeater_container');
961
962	$group.remove();
963
964	gformResetRepeaterAttributes($container);
965	gformToggleRepeaterButtons($container, max);
966
967	gform.doAction('gform_repeater_post_item_delete', $container);
968
969}
970
971function gformResetRepeaterAttributes($container, depth, row) {
972
973	var cachedRadioSelection = null;
974
975	if (typeof depth === 'undefined') {
976		depth = 0;
977	}
978
979	if (typeof row === 'undefined') {
980		row = 0;
981	}
982
983	$container.children('.gfield_repeater_items').children('.gfield_repeater_item').each(function () {
984		var $children = jQuery(this).children('.gfield_repeater_cell');
985		$children.each(function () {
986			var $cell = jQuery(this);
987			var $subContainer = jQuery(this).find('.gfield_repeater_container').first();
988
989			if ($subContainer.length > 0) {
990				var newDepth = depth + 1;
991				gformResetRepeaterAttributes($subContainer, newDepth, row);
992				return;
993			}
994
995			jQuery(this).find('input, select, textarea, :checkbox, :radio').each(function () {
996				var $this = jQuery(this);
997				var name = $this.attr('name');
998
999				if ( typeof name == 'undefined' ) {
1000					return;
1001				}
1002
1003				var regEx = /^(input_[^\[]*)((\[[0-9]+\])+)/,
1004					parts = regEx.exec(name);
1005
1006				if (!parts) {
1007					return;
1008				}
1009				var inputName = parts[1],
1010					arayParts = parts[2],
1011					regExIndex = /\[([0-9]+)\]/g,
1012					indexes = [],
1013					match = regExIndex.exec(arayParts);
1014
1015				while (match != null) {
1016					indexes.push(match[1]);
1017					match = regExIndex.exec(arayParts);
1018				}
1019				var newNameIndex = parts[1];
1020				indexes = indexes.reverse();
1021				var newId = '';
1022				for (var n = indexes.length - 1; n >= 0; n--) {
1023					if (n == depth) {
1024						newNameIndex += '[' + row + ']';
1025						newId += '-' + row;
1026					} else {
1027						newNameIndex += '[' + indexes[n] + ']';
1028						newId += '-' + indexes[n];
1029					}
1030				}
1031
1032				var currentId = $this.attr('id');
1033				var $label = $cell.find("label[for='" + currentId + "']");
1034
1035				if ( currentId ) {
1036					var matches = currentId.match(/((choice|input)_[0-9|_]*)-/);
1037					if ( matches && matches[2] ) {
1038						newId = matches[1] + newId;
1039						$label.attr('for', newId);
1040						$this.attr('id', newId);
1041					}
1042				}
1043				var newName = name.replace(parts[0], newNameIndex),
1044					newNameIsChecked = jQuery('input[name="'+ newName +'"]').is(':checked');
1045
1046				if ( $this.is(':radio') && $this.is(':checked') && name !== newName && newNameIsChecked ) {
1047					if ( cachedRadioSelection !== null ) {
1048						cachedRadioSelection.prop('checked', true);
1049					}
1050
1051					$this.prop('checked', false);
1052					cachedRadioSelection = $this;
1053				}
1054
1055				$this.attr('name', newName);
1056			});
1057		});
1058		if (depth === 0) {
1059			row++;
1060		}
1061	});
1062
1063	if ( cachedRadioSelection !== null ) {
1064		cachedRadioSelection.prop('checked', true);
1065		cachedRadioSelection = null;
1066	}
1067
1068}
1069
1070function gformToggleRepeaterButtons($container) {
1071
1072	var max = $container.closest('.gfield_repeater_wrapper').data('max_items'),
1073		groupCount = $container.children('.gfield_repeater_items').children('.gfield_repeater_item').length,
1074		$buttonsContainer = $container.children('.gfield_repeater_items').children('.gfield_repeater_item').children('.gfield_repeater_buttons'),
1075		$addButtons = $buttonsContainer.children('.add_repeater_item');
1076
1077	$buttonsContainer.children('.remove_repeater_item').css('visibility', groupCount == 1 ? 'hidden' : 'visible');
1078
1079	if (max > 0 && groupCount >= max) {
1080
1081		// store original title in the add button
1082		$addButtons.data('title', $buttonsContainer.children('.add_repeater_item').attr('title'));
1083		$addButtons.addClass('gfield_icon_disabled').attr('title', '');
1084
1085	} else if (max > 0) {
1086
1087		$addButtons.removeClass('gfield_icon_disabled');
1088
1089		if ($addButtons.data('title')) {
1090			$addButtons.attr('title', $addButtons.data('title'));
1091		}
1092	}
1093
1094	$container
1095		.children('.gfield_repeater_items')
1096		.children('.gfield_repeater_item')
1097		.children( '.gfield_repeater_cell').each(function (i) {
1098			var $subContainer = jQuery(this).find('.gfield_repeater_container').first();
1099			if ($subContainer.length > 0) {
1100				gformToggleRepeaterButtons($subContainer);
1101			}
1102		});
1103}
1104
1105
1106//-----------------------------------
1107//------ CREDIT CARD FIELD ----------
1108//-----------------------------------
1109function gformMatchCard(id) {
1110
1111    var cardType = gformFindCardType(jQuery('#' + id).val());
1112    var cardContainer = jQuery('#' + id).parents('.gfield').find('.gform_card_icon_container');
1113
1114    if(!cardType) {
1115
1116        jQuery(cardContainer).find('.gform_card_icon').removeClass('gform_card_icon_selected gform_card_icon_inactive');
1117
1118    } else {
1119
1120        jQuery(cardContainer).find('.gform_card_icon').removeClass('gform_card_icon_selected').addClass('gform_card_icon_inactive');
1121        jQuery(cardContainer).find('.gform_card_icon_' + cardType).removeClass('gform_card_icon_inactive').addClass('gform_card_icon_selected');
1122    }
1123}
1124
1125function gformFindCardType(value) {
1126
1127    if(value.length < 4)
1128        return false;
1129
1130    var rules = window['gf_cc_rules'];
1131    var validCardTypes = new Array();
1132
1133    for(type in rules) {
1134
1135        //needed when implementing for in loops
1136        if(!rules.hasOwnProperty(type))
1137            continue;
1138
1139
1140        for(i in rules[type]) {
1141
1142            if(!rules[type].hasOwnProperty(i))
1143                continue;
1144
1145            if(rules[type][i].indexOf(value.substring(0, rules[type][i].length)) === 0) {
1146                validCardTypes[validCardTypes.length] = type;
1147                break;
1148            }
1149
1150        }
1151    }
1152
1153    return validCardTypes.length == 1 ? validCardTypes[0].toLowerCase() : false;
1154}
1155
1156function gformToggleCreditCard(){
1157    if(jQuery("#gform_payment_method_creditcard").is(":checked"))
1158        jQuery(".gform_card_fields_container").slideDown();
1159    else
1160        jQuery(".gform_card_fields_container").slideUp();
1161}
1162
1163
1164//----------------------------------------
1165//------ CHOSEN DROP DOWN FIELD ----------
1166//----------------------------------------
1167
1168function gformInitChosenFields(fieldList, noResultsText){
1169    return jQuery(fieldList).each(function(){
1170
1171        var element = jQuery( this );
1172
1173        // RTL support
1174        if( jQuery( 'html' ).attr( 'dir' ) == 'rtl' ) {
1175            element.addClass( 'chosen-rtl chzn-rtl' );
1176        }
1177
1178        // only initialize once
1179        if( element.is(":visible") && element.siblings(".chosen-container").length == 0 ){
1180            var options = gform.applyFilters( 'gform_chosen_options', { no_results_text: noResultsText }, element );
1181            element.chosen( options );
1182        }
1183
1184    });
1185}
1186
1187//----------------------------------------
1188//--- CURRENCY FORMAT NUMBER FIELD -------
1189//----------------------------------------
1190
1191function gformInitCurrencyFormatFields(fieldList){
1192    jQuery(fieldList).each(function(){
1193        var $this = jQuery(this);
1194        $this.val( gformFormatMoney( jQuery(this).val() ) );
1195    }).change( function( event ) {
1196            jQuery(this).val( gformFormatMoney( jQuery(this).val() ) );
1197        });
1198}
1199
1200
1201
1202//----------------------------------------
1203//------ JS MERGE TAGS -------------------
1204//----------------------------------------
1205
1206var GFMergeTag = function() {
1207
1208	/**
1209     * Gets the merge tag value for the specified input Id
1210	 * @param formId    The current form Id
1211	 * @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)
1212	 * @param modifier  The merge tag modifier to be used. i.e. value, currency, price, etc...
1213	 * @returns         Returns a string containg the merge tag value for the spcified input Id
1214	 */
1215	GFMergeTag.getMergeTagValue = function( formId, inputId, modifier ) {
1216
1217		if ( modifier === undefined ) {
1218			modifier = '';
1219		}
1220		modifier = modifier.replace(":", "");
1221
1222		var fieldId = parseInt(inputId,10);
1223		var field = jQuery('#field_' + formId + '_' + fieldId);
1224
1225		var inputSelector = fieldId == inputId ? 'input[name^="input_' + fieldId + '"]' : 'input[name="input_' + inputId + '"]';
1226		var input = field.find( inputSelector + ', select[name^="input_' + inputId + '"], textarea[name="input_' + inputId + '"]');
1227
1228		// checking conditional logic
1229		var isVisible = window['gf_check_field_rule'] ? gf_check_field_rule( formId, fieldId, true, '' ) == 'show' : true,
1230			val;
1231
1232		if ( ! isVisible ) {
1233			return '';
1234		}
1235
1236		//If value has been filtered, use it. Otherwise use default logic
1237		var value = gform.applyFilters( 'gform_value_merge_tag_' + formId + '_' + fieldId, false, input, modifier );
1238		if ( value !== false ){
1239			return value;
1240		}
1241
1242		value = ''; //Reset value to blank
1243
1244		switch ( modifier ) {
1245			case 'label':
1246				var label = field.find('.gfield_label').text();
1247				return label;
1248			    break;
1249			case 'qty':
1250				if ( field.hasClass('gfield_price') ){
1251					val = gformGetProductQuantity( formId, fieldId );
1252					return val === false || val === '' ? 0 : val;
1253				}
1254				break;
1255
1256		}
1257
1258		// Filter out unselected checkboxes and radio buttons
1259		if ( input.prop('type') === 'checkbox' || input.prop('type') === 'radio' ) {
1260			input = input.filter(':checked');
1261		}
1262
1263		if ( input.length === 1 ) {
1264			if ( ( input.is('select') || input.prop('type') === 'radio' || input.prop('type') === 'checkbox' ) && modifier === '' ) {
1265				val = ( input.is('select') ) ? input.find('option:selected') : input.next('label').clone();
1266                val.find('span').remove();
1267
1268				if ( val.length === 1 ) {
1269					val = val.text();
1270				} else {
1271					var option = [];
1272					for(var i=0; i<val.length; i++) {
1273						option[i] = jQuery(val[i]).text();
1274					}
1275
1276					val = option;
1277				}
1278			} else if ( val === undefined ) {
1279				val = input.val();
1280			}
1281
1282			if ( jQuery.isArray( val ) ) {
1283				// multiple select
1284				value = val.join(', ');
1285			} else if ( typeof val === 'string' ) {
1286
1287			    value = GFMergeTag.formatValue( val, modifier );
1288
1289			} else {
1290				// empty multiple select returns null, set it to ''
1291				value = '';
1292            }
1293		} else if ( input.length > 1 ) {
1294			val = [];
1295			for(var i=0; i<input.length; i++) {
1296				if( ( input.prop('type') === 'checkbox' ) && modifier === '' ) {
1297
1298				    var clone = jQuery(input[i]).next('label').clone();
1299					clone.find('span').remove()
1300					val[i] = GFMergeTag.formatValue( clone.text(), modifier );
1301
1302					clone.remove();
1303
1304				} else {
1305					val[i] = GFMergeTag.formatValue( jQuery(input[i]).val(), modifier );
1306				}
1307			}
1308
1309			value = val.join(', ');
1310		}
1311
1312		return value;
1313	}
1314
1315	/**
1316     * Parses the specified text for merge tags, and replaces all of them with the appropriate merge tag values. Returns the resulting string
1317	 * @param formId    The current form Id
1318	 * @param text      The text containing merge tags
1319	 * @returns         Retuns the original "text" strings with all merge tags replaced with the appropriate merge tag values
1320	 */
1321	GFMergeTag.replaceMergeTags = function( formId, text ) {
1322
1323		var mergeTags = GFMergeTag.parseMergeTags( text );
1324
1325		for(i in mergeTags) {
1326
1327			if(! mergeTags.hasOwnProperty(i)) {
1328				continue;
1329			}
1330
1331			var inputId = mergeTags[i][1];
1332			var fieldId = parseInt(inputId,10);
1333			var modifier = mergeTags[i][3] == undefined ? '' : mergeTags[i][3].replace(":", "");
1334
1335			var value = GFMergeTag.getMergeTagValue( formId, inputId, modifier );
1336
1337			text = text.replace( mergeTags[i][0], value );
1338		}
1339
1340		return text;
1341	}
1342
1343	GFMergeTag.formatValue = function( value, modifier ) {
1344
1345		value = value.split( '|' );
1346		var val = '';
1347		if( value.length > 1 ) {
1348			val = modifier === 'price' || modifier === 'currency' ? gformToNumber( value[1] ) : value[0];
1349		} else {
1350			val = value[0];
1351		}
1352
1353		switch ( modifier ) {
1354
1355			case 'price':
1356				val = gformToNumber( val );
1357				val = val === false ? '' : val;
1358				break;
1359
1360			case 'currency':
1361				val = gformFormatMoney( val, false );
1362				val = val === false ? '' : val;
1363				break;
1364
1365			case 'numeric':
1366				val = gformToNumber( val );
1367				return val === false ? 0 : val;
1368				break;
1369		}
1370
1371		return val;
1372	}
1373
1374	/**
1375     * Parses the merge tags in the specified text and returns an array of all the matched merge tags
1376	 *
1377	 * @param text  The text with merge tags to be parsed
1378	 * @param regEx The regular expression to be used to parse for merge tags.
1379	 *
1380	 * @returns Returns an array with all the merge tags that were matched in the original text
1381	 */
1382	GFMergeTag.parseMergeTags = function( text, regEx ) {
1383
1384		if( typeof regEx === 'undefined' ) {
1385			regEx = /{[^{]*?:(\d+(\.\d+)?)(:(.*?))?}/i;
1386		}
1387
1388		var matches = [];
1389
1390		while( regEx.test( text ) ) {
1391			var i = matches.length;
1392			matches[i] = regEx.exec( text );
1393			text = text.replace( '' + matches[i][0], '' );
1394		}
1395
1396		return matches;
1397	}
1398}
1399
1400new GFMergeTag();
1401
1402
1403//----------------------------------------
1404//------ CALCULATION FUNCTIONS -----------
1405//----------------------------------------
1406
1407var GFCalc = function(formId, formulaFields){
1408
1409	this.formId = formId;
1410	this.formulaFields = formulaFields;
1411
1412    this.exprPatt = /^[0-9 -/*\(\)]+$/i;
1413    this.isCalculating = {};
1414
1415    this.init = function(formId, formulaFields) {
1416
1417        var calc = this;
1418        jQuery(document).bind("gform_post_conditional_logic", function(){
1419            calc.runCalcs( formId, formulaFields );
1420        } );
1421
1422        for(var i=0; i<formulaFields.length; i++) {
1423            var formulaField = jQuery.extend({}, formulaFields[i]);
1424            this.runCalc(formulaField, formId);
1425            this.bindCalcEvents(formulaField, formId);
1426        }
1427
1428    }
1429
1430    this.runCalc = function(formulaField, formId) {
1431
1432        var calcObj      = this,
1433            field        = jQuery('#field_' + formId + '_' + formulaField.field_id),
1434            formulaInput = field.hasClass( 'gfield_price' ) ? jQuery( '#ginput_base_price_' + formId + '_' + formulaField.field_id ) : jQuery( '#input_' + formId + '_' + formulaField.field_id ),
1435            previous_val = formulaInput.val(),
1436            formula      = gform.applyFilters( 'gform_calculation_formula', formulaField.formula, formulaField, formId, calcObj ),
1437            expr         = calcObj.replaceFieldTags( formId, formula, formulaField ).replace(/(\r\n|\n|\r)/gm,""),
1438            result       = '';
1439
1440        if(calcObj.exprPatt.test(expr)) {
1441            try {
1442
1443                //run calculation
1444                result = eval(expr);
1445
1446            } catch( e ) { }
1447        }
1448
1449        // if result is positive infinity, negative infinity or a NaN, defaults to 0
1450        if( ! isFinite( result ) )
1451            result = 0;
1452
1453        // allow users to modify result with their own function
1454        if( window["gform_calculation_result"] ) {
1455            result = window["gform_calculation_result"](result, formulaField, formId, calcObj);
1456            if( window.console )
1457                console.log( '"gform_calculation_result" function is deprecated since version 1.8! Use "gform_calculation_result" JS hook instead.' );
1458        }
1459
1460        // allow users to modify result with their own function
1461        result = gform.applyFilters( 'gform_calculation_result', result, formulaField, formId, calcObj );
1462
1463        // allow result to be custom formatted
1464        var formattedResult = gform.applyFilters( 'gform_calculation_format_result', false, result, formulaField, formId, calcObj );
1465
1466        var numberFormat = gf_get_field_number_format(formulaField.field_id, formId);
1467
1468        //formatting number
1469        if( formattedResult !== false) {
1470            result = formattedResult;
1471        }
1472        else if( field.hasClass( 'gfield_price' ) || numberFormat == "currency") {
1473
1474            result = gformFormatMoney(result ? result : 0, true);
1475        }
1476        else {
1477
1478            var decimalSeparator = ".";
1479            var thousandSeparator = ",";
1480
1481            if(numberFormat == "decimal_comma"){
1482                decimalSeparator = ",";
1483                thousandSeparator = ".";
1484            }
1485
1486            result = gformFormatNumber(result, !gformIsNumber(formulaField.rounding) ? -1 : formulaField.rounding, decimalSeparator, thousandSeparator);
1487        }
1488
1489        //If value doesn't change, abort.
1490        //This is needed to prevent an infinite loop condition with conditional logic
1491        if( result == previous_val )
1492            return;
1493
1494        // if this is a calculation product, handle differently
1495        if(field.hasClass('gfield_price')) {
1496            jQuery('#input_' + formId + '_' + formulaField.field_id).text(result);
1497            formulaInput.val(result).trigger('change');
1498            gformCalculateTotalPrice(formId);
1499        } else {
1500            formulaInput.val(result).trigger('change');
1501        }
1502
1503    };
1504
1505    this.runCalcs = function( formId, formulaFields ) {
1506	    for(var i=0; i<formulaFields.length; i++) {
1507		    var formulaField = jQuery.extend({}, formulaFields[i]);
1508		    this.runCalc( formulaField, formId );
1509	    }
1510    }
1511
1512    this.bindCalcEvents = function(formulaField, formId) {
1513
1514        var calcObj = this;
1515        var formulaFieldId = formulaField.field_id;
1516        var matches = GFMergeTag.parseMergeTags( formulaField.formula );
1517
1518        calcObj.isCalculating[formulaFieldId] = false;
1519
1520        for(var i in matches) {
1521
1522            if(! matches.hasOwnProperty(i))
1523                continue;
1524
1525            var inputId = matches[i][1];
1526            var fieldId = parseInt(inputId,10);
1527            var input = jQuery('#field_' + formId + '_' + fieldId).find('input[name="input_' + inputId + '"], select[name="input_' + inputId + '"]');
1528
1529            if(input.prop('type') == 'checkbox' || input.prop('type') == 'radio') {
1530                jQuery(input).click(function(){
1531                    calcObj.bindCalcEvent(inputId, formulaField, formId, 0);
1532                });
1533            } else
1534            if(input.is('select') || input.prop('type') == 'hidden') {
1535                jQuery(input).change(function(){
1536                    calcObj.bindCalcEvent(inputId, formulaField, formId, 0);
1537                });
1538            } else {
1539                jQuery(input).keydown(function(){
1540                    calcObj.bindCalcEvent(inputId, formulaField, formId);
1541                }).change(function(){
1542                    calcObj.bindCalcEvent(inputId, formulaField, formId, 0);
1543                });
1544            }
1545
1546            // allow users to add custom methods for triggering calculations
1547            gform.doAction( 'gform_post_calculation_events', matches[i], formulaField, formId, calcObj );
1548
1549        }
1550
1551    }
1552
1553    this.bindCalcEvent = function(inputId, formulaField, formId, delay) {
1554
1555        var calcObj = this;
1556        var formulaFieldId = formulaField.field_id;
1557
1558        delay = delay == undefined ? 345 : delay;
1559
1560        if(calcObj.isCalculating[formulaFieldId][inputId])
1561            clearTimeout(calcObj.isCalculating[formulaFieldId][inputId]);
1562
1563        calcObj.isCalculating[formulaFieldId][inputId] = window.setTimeout(function() {
1564            calcObj.runCalc(formulaField, formId);
1565        }, delay);
1566
1567    }
1568
1569    this.replaceFieldTags = function( formId, expr, formulaField ) {
1570
1571        var matches = GFMergeTag.parseMergeTags( expr );
1572
1573        for(i in matches) {
1574
1575            if(! matches.hasOwnProperty(i))
1576                continue;
1577
1578            var inputId = matches[i][1];
1579            var fieldId = parseInt(inputId,10);
1580
1581            var modifier = 'value';
1582			if( matches[i][3] ){
1583				modifier = matches[i][3];
1584			}
1585			else {
1586				var is_product_radio =  jQuery('.gfield_price input[name=input_' + fieldId + ']').is('input[type=radio]');
1587                var is_product_dropdown = jQuery('.gfield_price select[name=input_' + fieldId + ']').length > 0;
1588                var is_option_checkbox = jQuery('.gfield_price input[name="input_' + inputId + '"]').is('input[type=checkbox]');
1589
1590                if( is_product_dropdown || is_product_radio || is_option_checkbox ) {
1591					modifier = 'price';
1592				}
1593			}
1594
1595			var isVisible = window['gf_check_field_rule'] ? gf_check_field_rule( formId, fieldId, true, '' ) == 'show' : true;
1596
1597			var value = isVisible ? GFMergeTag.getMergeTagValue( formId, inputId, modifier ) : 0;
1598
1599            // allow users to modify value with their own function
1600            value = gform.applyFilters( 'gform_merge_tag_value_pre_calculation', value, matches[i], isVisible, formulaField, formId );
1601
1602            value = this.cleanNumber( value, formId, fieldId, formulaField );
1603
1604            expr = expr.replace( matches[i][0], value );
1605        }
1606
1607        return expr;
1608    }
1609
1610	this.cleanNumber = function ( value, formId, fieldId, formulaField ) {
1611
1612		var numberFormat = gf_get_field_number_format( fieldId, formId );
1613
1614		if( ! numberFormat ) {
1615			numberFormat = gf_get_field_number_format(formulaField.field_id, formId);
1616		}
1617
1618		var decimalSeparator = gformGetDecimalSeparator(numberFormat);
1619
1620		value = gformCleanNumber( value, '', '', decimalSeparator );
1621		if( ! value )
1622			value = 0;
1623
1624		return value;
1625	}
1626
1627    this.init(formId, formulaFields);
1628
1629
1630}
1631
1632function gformFormatNumber(number, rounding, decimalSeparator, thousandSeparator){
1633
1634    if(typeof decimalSeparator == "undefined"){
1635        if(window['gf_global']){
1636            var currency = new Currency(gf_global.gf_currency_config);
1637            decimalSeparator = currency.currency["decimal_separator"];
1638        }
1639        else{
1640            decimalSeparator = ".";
1641        }
1642    }
1643
1644    if(typeof thousandSeparator == "undefined"){
1645        if(window['gf_global']){
1646            var currency = new Currency(gf_global.gf_currency_config);
1647            thousandSeparator = currency.currency["thousand_separator"];
1648        }
1649        else{
1650            thousandSeparator = ",";
1651        }
1652    }
1653
1654    var currency = new Currency();
1655    return currency.numberFormat(number, rounding, decimalSeparator, thousandSeparator, false)
1656}
1657
1658function gformToNumber(text) {
1659    var currency = new Currency(gf_global.gf_currency_config);
1660    return currency.toNumber(text);
1661}
1662
1663/**
1664 * @deprecated. Use GFMergeTags.parseMergeTag() instead
1665 */
1666function getMatchGroups(expr, patt) {
1667
1668    var matches = new Array();
1669
1670    while(patt.test(expr)) {
1671
1672        var i = matches.length;
1673        matches[i] = patt.exec(expr)
1674        expr = expr.replace('' + matches[i][0], '');
1675
1676    }
1677
1678    return matches;
1679}
1680
1681function gf_get_field_number_format(fieldId, formId, context) {
1682
1683    var fieldNumberFormats = rgars(window, 'gf_global/number_formats/{0}/{1}'.format(formId, fieldId)),
1684        format = false;
1685
1686    if (fieldNumberFormats === '') {
1687        return format;
1688    }
1689
1690    if (typeof context == 'undefined') {
1691        format = fieldNumberFormats.price !== false ? fieldNumberFormats.price : fieldNumberFormats.value;
1692    } else {
1693        format = fieldNumberFormats[context];
1694    }
1695
1696    return format;
1697}
1698
1699
1700//----------------------------------------
1701//------ JAVASCRIPT HOOK FUNCTIONS -------
1702//----------------------------------------
1703
1704var gform = {
1705	hooks: { action: {}, filter: {} },
1706	addAction: function( action, callable, priority, tag ) {
1707		gform.addHook( 'action', action, callable, priority, tag );
1708	},
1709	addFilter: function( action, callable, priority, tag ) {
1710		gform.addHook( 'filter', action, callable, priority, tag );
1711	},
1712	doAction: function( action ) {
1713		gform.doHook( 'action', action, arguments );
1714	},
1715	applyFilters: function( action ) {
1716		return gform.doHook( 'filter', action, arguments );
1717	},
1718	removeAction: function( action, tag ) {
1719		gform.removeHook( 'action', action, tag );
1720	},
1721	removeFilter: function( action, priority, tag ) {
1722		gform.removeHook( 'filter', action, priority, tag );
1723	},
1724	addHook: function( hookType, action, callable, priority, tag ) {
1725		if ( undefined == gform.hooks[hookType][action] ) {
1726			gform.hooks[hookType][action] = [];
1727		}
1728		var hooks = gform.hooks[hookType][action];
1729		if ( undefined == tag ) {
1730			tag = action + '_' + hooks.length;
1731		}
1732        if( priority == undefined ){
1733            priority = 10;
1734        }
1735
1736        gform.hooks[hookType][action].push( { tag:tag, callable:callable, priority:priority } );
1737	},
1738	doHook: function( hookType, action, args ) {
1739
1740        // splice args from object into array and remove first index which is the hook name
1741        args = Array.prototype.slice.call(args, 1);
1742
1743		if ( undefined != gform.hooks[hookType][action] ) {
1744			var hooks = gform.hooks[hookType][action], hook;
1745			//sort by priority
1746			hooks.sort(function(a,b){return a["priority"]-b["priority"]});
1747			for( var i=0; i<hooks.length; i++) {
1748                hook = hooks[i].callable;
1749                if(typeof hook != 'function')
1750                    hook = window[hook];
1751				if ( 'action' == hookType ) {
1752                    hook.apply(null, args);
1753				} else {
1754                    args[0] = hook.apply(null, args);
1755				}
1756			}
1757		}
1758		if ( 'filter'==hookType ) {
1759			return args[0];
1760		}
1761	},
1762	removeHook: function( hookType, action, priority, tag ) {
1763		if ( undefined != gform.hooks[hookType][action] ) {
1764			var hooks = gform.hooks[hookType][action];
1765			for( var i=hooks.length-1; i>=0; i--) {
1766				if ((undefined==tag||tag==hooks[i].tag) && (undefined==priority||priority==hooks[i].priority)){
1767					hooks.splice(i,1);
1768				}
1769			}
1770		}
1771	}
1772};
1773
1774
1775
1776//----------------------------------------
1777//------ reCAPTCHA FUNCTIONS -------------
1778//----------------------------------------
1779
1780/**
1781 * Callback function on the reCAPTCAH API script.
1782 *
1783 * @see GF_Field_CAPTCHA::get_field_input() in /includes/fields/class-gf-field-catpcha.php
1784 */
1785function renderRecaptcha() {
1786
1787    jQuery( '.ginput_recaptcha' ).each( function() {
1788
1789        var $elem      = jQuery( this ),
1790            parameters = {
1791                'sitekey':  $elem.data( 'sitekey' ),
1792                'theme':    $elem.data( 'theme' ),
1793	            'tabindex': $elem.data( 'tabindex' )
1794            };
1795
1796        if ( ! $elem.is( ':empty' ) ) {
1797            return;
1798        }
1799
1800        if ( $elem.data( 'stoken' ) ) {
1801            parameters.stoken = $elem.data( 'stoken' );
1802        }
1803
1804        var callback = false;
1805
1806        if ( $elem.data( 'size' ) == 'invisible' ) {
1807            callback = function( token ) {
1808                if ( token ) {
1809                    $elem.closest('form').submit();
1810                }
1811            }
1812        }
1813
1814        /**
1815         * Allows a custom callback function to be executed when the user successfully submits the captcha.
1816         *
1817         * @since 2.4.x     The callback will be a function if reCAPTCHA v2 Invisible is used.
1818         * @since 2.2.5.20
1819         *
1820         * @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.
1821         * @param object       $elem    The jQuery object containing the div element with the ginput_recaptcha class for the current reCaptcha field.
1822         */
1823	    callback = gform.applyFilters( 'gform_recaptcha_callback', callback, $elem );
1824	    if ( callback ) {
1825		    parameters.callback = callback;
1826	    }
1827
1828        $elem.data( 'widget-id', grecaptcha.render( this.id, parameters ) );
1829
1830	    if ( parameters.tabindex ) {
1831		    $elem.find( 'iframe' ).attr( 'tabindex', parameters.tabindex );
1832	    }
1833
1834        gform.doAction( 'gform_post_recaptcha_render', $elem );
1835
1836
1837    } );
1838
1839}
1840
1841//----------------------------------------
1842//----- SINGLE FILE UPLOAD FUNCTIONS -----
1843//----------------------------------------
1844
1845function gformValidateFileSize( field, max_file_size ) {
1846	var validation_element;
1847
1848	// Get validation message element.
1849	if ( jQuery( field ).closest( 'div' ).siblings( '.validation_message' ).length > 0 ) {
1850		validation_element = jQuery( field ).closest( 'div' ).siblings( '.validation_message' );
1851	} else {
1852		validation_element = jQuery( field ).siblings( '.validation_message' );
1853	}
1854
1855
1856	// If file API is not supported within browser, return.
1857	if ( ! window.FileReader || ! window.File || ! window.FileList || ! window.Blob ) {
1858		return;
1859	}
1860
1861	// Get selected file.
1862	var file = field.files[0];
1863
1864	// If selected file is larger than maximum file size, set validation message and unset file selection.
1865	if ( file && file.size > max_file_size ) {
1866
1867		// Set validation message.
1868		validation_element.text(file.name + " - " + gform_gravityforms.strings.file_exceeds_limit);
1869
1870    } else {
1871
1872		// Reset validation message.
1873		validation_element.text( '' );
1874
1875	}
1876
1877}
1878
1879//----------------------------------------
1880//------ MULTIFILE UPLOAD FUNCTIONS ------
1881//----------------------------------------
1882
1883(function (gfMultiFileUploader, $) {
1884    gfMultiFileUploader.uploaders = {};
1885    var strings = typeof gform_gravityforms != 'undefined' ? gform_gravityforms.strings : {};
1886    var imagesUrl = typeof gform_gravityforms != 'undefined' ? gform_gravityforms.vars.images_url : "";
1887
1888
1889	$(document).bind('gform_post_render', function(e, formID){
1890
1891		$("form#gform_" + formID + " .gform_fileupload_multifile").each(function(){
1892			setup(this);
1893		});
1894		var $form = $("form#gform_" + formID);
1895		if($form.length > 0){
1896			$form.submit(function(){
1897				var pendingUploads = false;
1898				$.each(gfMultiFileUploader.uploaders, function(i, uploader){
1899					if(uploader.total.queued>0){
1900						pendingUploads = true;
1901						return false;
1902					}
1903				});
1904				if(pendingUploads){
1905					alert(strings.currently_uploading);
1906					window["gf_submitting_" + formID] = false;
1907					$('#gform_ajax_spinner_' + formID).remove();
1908					return false;
1909				}
1910			});
1911		}
1912
1913	});
1914
1915	$(document).bind("gform_post_conditional_logic", function(e,formID, fields, isInit){
1916		if(!isInit){
1917			$.each(gfMultiFileUploader.uploaders, function(i, uploader){
1918				uploader.refresh();
1919			});
1920		}
1921	});
1922
1923    $(document).ready(function () {
1924        if((typeof adminpage !== 'undefined' && adminpage === 'toplevel_page_gf_edit_forms')|| typeof plupload == 'undefined'){
1925            $(".gform_button_select_files").prop("disabled", true);
1926        } else if (typeof adminpage !== 'undefined' && adminpage.indexOf('_page_gf_entries') > -1) {
1927            $(".gform_fileupload_multifile").each(function(){
1928                setup(this);
1929            });
1930        }
1931    });
1932
1933    gfMultiFileUploader.setup = function (uploadElement){
1934        setup( uploadElement );
1935    };
1936
1937    function setup(uploadElement){
1938        var settings = $(uploadElement).data('settings');
1939
1940        var uploader = new plupload.Uploader(settings);
1941        formID = uploader.settings.multipart_params.form_id;
1942        gfMultiFileUploader.uploaders[settings.container] = uploader;
1943        var formID;
1944        var uniqueID;
1945
1946        uploader.bind('Init', function(up, params) {
1947            if(!up.features.dragdrop)
1948                $(".gform_drop_instructions").hide();
1949            var fieldID = up.settings.multipart_params.field_id;
1950            var maxFiles = parseInt(up.settings.gf_vars.max_files,10);
1951            var initFileCount = countFiles(fieldID);
1952            if(maxFiles > 0 && initFileCount >= maxFiles){
1953                gfMultiFileUploader.toggleDisabled(up.settings, true);
1954            }
1955
1956        });
1957
1958        gfMultiFileUploader.toggleDisabled = function (settings, disabled){
1959
1960            var button = typeof settings.browse_button == "string" ? $("#" + settings.browse_button) : $(settings.browse_button);
1961            button.prop("disabled", disabled);
1962        };
1963
1964        function addMessage(messagesID, message){
1965            $("#" + messagesID).prepend("<li>" + htmlEncode(message) + "</li>");
1966        }
1967
1968        uploader.init();
1969
1970		uploader.bind('BeforeUpload', function(up, file){
1971			up.settings.multipart_params.original_filename = file.name;
1972		});
1973
1974        uploader.bind('FilesAdded', function(up, files) {
1975            var max = parseInt(up.settings.gf_vars.max_files,10),
1976                fieldID = up.settings.multipart_params.field_id,
1977                totalCount = countFiles(fieldID),
1978                disallowed = up.settings.gf_vars.disallowed_extensions,
1979                extension;
1980
1981            if( max > 0 && totalCount >= max){
1982                $.each(files, function(i, file) {
1983                    up.removeFile(file);
1984                    return;
1985                });
1986                return;
1987            }
1988            $.each(files, function(i, file) {
1989
1990                extension = file.name.split('.').pop();
1991
1992                if($.inArray(extension, disallowed) > -1){
1993                    addMessage(up.settings.gf_vars.message_id, file.name + " - " + strings.illegal_extension);
1994                    up.removeFile(file);
1995                    return;
1996                }
1997
1998                if ((file.status == plupload.FAILED) || (max > 0 && totalCount >= max)){
1999                    up.removeFile(file);
2000                    return;
2001                }
2002
2003                var size         = typeof file.size !== 'undefined' ? plupload.formatSize(file.size) : strings.in_progress,
2004                    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();',
2005                    statusMarkup = '<div id="{0}" class="ginput_preview">{1} ({2}) <b></b> <a href="javascript:void(0)" title="{3}" onclick="{4}" onkeypress="{4}">{5}</a></div>';
2006
2007                /**
2008                 *  Filer the file upload markup as it is being uploaded.
2009                 *
2010                 *  @param {string}            statusMarkup Markup template used to render the status of the file being uploaded.
2011                 *  @param {plupload.File}     file         Instance of File being uploaded. See: https://www.plupload.com/docs/v2/File.
2012                 *  @param {int|string}        size         File size.
2013                 *  @param {object}            strings      Array of localized strings relating to the file upload UI.
2014                 *  @param {string}            removeFileJs JS used to remove the file when the "Cancel" link is click/pressed.
2015                 *  @param {plupload.Uploader} up           Instance of Uploader responsible for uploading current file. See: https://www.plupload.com/docs/v2/Uploader.
2016                 */
2017                statusMarkup = gform.applyFilters( 'gform_file_upload_status_markup', statusMarkup, file, size, strings, removeFileJs, up )
2018                    .format( file.id, htmlEncode( file.name ), size, strings.cancel_upload, removeFileJs, strings.cancel );
2019
2020                $( '#' + up.settings.filelist ).prepend( statusMarkup );
2021
2022                totalCount++;
2023
2024            });
2025
2026            up.refresh(); // Reposition Flash
2027
2028            var formElementID = "form#gform_" + formID;
2029            var uidElementID = "input:hidden[name='gform_unique_id']";
2030            var uidSelector = formElementID + " " + uidElementID;
2031            var $uid = $(uidSelector);
2032            if($uid.length==0){
2033                $uid = $(uidElementID);
2034            }
2035
2036            uniqueID = $uid.val();
2037            if('' === uniqueID){
2038                uniqueID = generateUniqueID();
2039                $uid.val(uniqueID);
2040            }
2041
2042
2043            if(max > 0 && totalCount >= max){
2044                gfMultiFileUploader.toggleDisabled(up.settings, true);
2045                addMessage(up.settings.gf_vars.message_id, strings.max_reached)
2046            }
2047
2048
2049            up.settings.multipart_params.gform_unique_id = uniqueID;
2050            up.start();
2051
2052        });
2053
2054        uploader.bind('UploadProgress', function(up, file) {
2055            var html = file.percent + "%";
2056            $('#' + file.id + " b").html(html);
2057        });
2058
2059        uploader.bind('Error', function(up, err) {
2060            if(err.code === plupload.FILE_EXTENSION_ERROR){
2061                var extensions = typeof up.settings.filters.mime_types != 'undefined' ? up.settings.filters.mime_types[0].extensions /* plupoad 2 */ : up.settings.filters[0].extensions;
2062                addMessage(up.settings.gf_vars.message_id, err.file.name + " - " + strings.invalid_file_extension + " " + extensions);
2063            } else if (err.code === plupload.FILE_SIZE_ERROR) {
2064                addMessage(up.settings.gf_vars.message_id, err.file.name + " - " + strings.file_exceeds_limit);
2065            } else {
2066                var m = "Error: " + err.code +
2067                    ", Message: " + err.message +
2068                    (err.file ? ", File: " + err.file.name : "");
2069
2070                addMessage(up.settings.gf_vars.message_id, m);
2071            }
2072            $('#' + err.file.id ).html('');
2073
2074            up.refresh(); // Reposition Flash
2075        });
2076
2077		uploader.bind('ChunkUploaded', function(up, file, result) {
2078			var response = $.secureEvalJSON(result.response);
2079			if(response.status == "error"){
2080				up.removeFile(file);
2081				addMessage(up.settings.gf_vars.message_id, file.name + " - " + response.error.message);
2082				$('#' + file.id ).html('');
2083			}
2084		});
2085
2086        uploader.bind('FileUploaded', function(up, file, result) {
2087			if( ! up.getFile(file.id) ) {
2088				// The file has been removed from the queue.
2089				return;
2090			}
2091            var response = $.secureEvalJSON(result.response);
2092            if(response.status == "error"){
2093                addMessage(up.settings.gf_vars.message_id, file.name + " - " + response.error.message);
2094                $('#' + file.id ).html('');
2095                return;
2096            }
2097
2098            var html = '<strong>' + htmlEncode(file.name) + '</strong>';
2099            var formId = up.settings.multipart_params.form_id;
2100            var fieldId = up.settings.multipart_params.field_id;
2101            html = "<img "
2102                + "class='gform_delete' "
2103                + "src='" + imagesUrl + "/delete.png' "
2104                + "onclick='gformDeleteUploadedFile(" + formId + "," + fieldId + ", this);' "
2105                + "onkeypress='gformDeleteUploadedFile(" + formId + "," + fieldId + ", this);' "
2106                + "alt='"+ strings.delete_file + "' "
2107                + "title='" + strings.delete_file
2108                + "' /> "
2109                + html;
2110
2111            html = gform.applyFilters( 'gform_file_upload_markup', html, file, up, strings, imagesUrl );
2112
2113            $( '#' + file.id ).html( html );
2114
2115            if(file.percent == 100){
2116                if(response.status && response.status == 'ok'){
2117                    addFile(fieldId, response.data);
2118                }  else {
2119                    addMessage(up.settings.gf_vars.message_id, strings.unknown_error + ': ' + file.name);
2120                }
2121            }
2122
2123
2124
2125        });
2126
2127		function getAllFiles(){
2128			var selector = '#gform_uploaded_files_' + formID,
2129				$uploadedFiles = $(selector), files;
2130
2131			files = $uploadedFiles.val();
2132			files = (typeof files === "undefined") || files === '' ? {} : $.parseJSON(files);
2133
2134			return files;
2135		}
2136
2137        function getFiles(fieldID){
2138            var allFiles = getAllFiles();
2139            var inputName = getInputName(fieldID);
2140
2141            if(typeof allFiles[inputName] == 'undefined')
2142                allFiles[inputName] = [];
2143            return allFiles[inputName];
2144        }
2145
2146        function countFiles(fieldID){
2147            var files = getFiles(fieldID);
2148            return files.length;
2149        }
2150
2151        function addFile(fieldID, fileInfo){
2152
2153            var files = getFiles(fieldID);
2154
2155            files.unshift(fileInfo);
2156            setUploadedFiles(fieldID, files);
2157        }
2158
2159        function setUploadedFiles(fieldID, files){
2160            var allFiles = getAllFiles();
2161            var $uploadedFiles = $('#gform_uploaded_files_' + formID);
2162            var inputName = getInputName(fieldID);
2163            allFiles[inputName] = files;
2164            $uploadedFiles.val($.toJSON(allFiles));
2165        }
2166
2167        function getInputName(fieldID){
2168            return "input_" + fieldID;
2169        }
2170
2171        // fixes drag and drop in IE10
2172        $("#" + settings.drop_element).on({
2173            "dragenter": ignoreDrag,
2174            "dragover": ignoreDrag
2175        });
2176
2177        function ignoreDrag( e ) {
2178            e.preventDefault();
2179        }
2180    }
2181
2182
2183    function generateUniqueID() {
2184        return 'xxxxxxxx'.replace(/[xy]/g, function (c) {
2185            var r = Math.random() * 16 | 0, v = c == 'x' ? r : r & 0x3 | 0x8;
2186            return v.toString(16);
2187        });
2188    }
2189
2190	function htmlEncode(value){
2191		return $('<div/>').text(value).html();
2192	}
2193
2194}(window.gfMultiFileUploader = window.gfMultiFileUploader || {}, jQuery));
2195
2196
2197//----------------------------------------
2198//------ GENERAL FUNCTIONS -------
2199//----------------------------------------
2200
2201function gformInitSpinner(formId, spinnerUrl) {
2202
2203	jQuery('#gform_' + formId).submit(function () {
2204		gformAddSpinner(formId, spinnerUrl);
2205	});
2206
2207}
2208
2209function gformAddSpinner(formId, spinnerUrl) {
2210
2211	if (typeof spinnerUrl == 'undefined' || !spinnerUrl) {
2212		spinnerUrl = gform.applyFilters('gform_spinner_url', gf_global.spinnerUrl, formId);
2213	}
2214
2215	if (jQuery('#gform_ajax_spinner_' + formId).length == 0) {
2216		/**
2217		 * Filter the element after which the AJAX spinner will be inserted.
2218		 *
2219		 * @since 2.0
2220		 *
2221		 * @param object $targetElem jQuery object containing all of the elements after which the AJAX spinner will be inserted.
2222		 * @param int    formId      ID of the current form.
2223		 */
2224		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);
2225		$spinnerTarget.after('<img id="gform_ajax_spinner_' + formId + '"  class="gform_ajax_spinner" src="' + spinnerUrl + '" alt="" />');
2226	}
2227
2228}
2229
2230//----------------------------------------
2231//------ EVENT FUNCTIONS -----------------
2232//----------------------------------------
2233
2234var __gf_keyup_timeout;
2235
2236jQuery( document ).on( 'change keyup', '.gfield input, .gfield select, .gfield textarea', function( event ) {
2237    gf_raw_input_change( event, this );
2238} );
2239
2240function gf_raw_input_change( event, elem ) {
2241
2242    // clear regardless of event type for maximum efficiency ;)
2243    clearTimeout( __gf_keyup_timeout );
2244
2245    var $input    = jQuery( elem ),
2246        htmlId    = $input.attr( 'id' ),
2247        fieldId   = gf_get_input_id_by_html_id( htmlId ),
2248        formId    = gf_get_form_id_by_html_id( htmlId ),
2249	    /**
2250	     * Filter the field meta generated by a raw input change.
2251	     *
2252	     * @since 2.4.1
2253	     *
2254	     * @param object fieldMeta An object containing the field ID and form ID of the triggering Gravity Forms field.
2255	     * @param object $input    The jQuery object for the triggering field element.
2256	     * @param object event     The raw JS event.
2257	     */
2258        fieldMeta = gform.applyFilters( 'gform_field_meta_raw_input_change', { fieldId: fieldId, formId: formId }, $input, event );
2259
2260    fieldId = fieldMeta.fieldId;
2261    formId = fieldMeta.formId;
2262
2263    if( ! fieldId ) {
2264        return;
2265    }
2266
2267    var isChangeElem = $input.is( ':checkbox' ) || $input.is( ':radio' ) || $input.is( 'select' ),
2268        isKeyupElem  = ! isChangeElem || $input.is( 'textarea' );
2269
2270    if( event.type == 'keyup' && ! isKeyupElem ) {
2271        return;
2272    } else if( event.type == 'change' && ! isChangeElem && ! isKeyupElem ) {
2273        return;
2274    }
2275
2276    if( event.type == 'keyup' ) {
2277        __gf_keyup_timeout = setTimeout( function() {
2278            gf_input_change( elem, formId, fieldId );
2279        }, 300 );
2280    } else {
2281        gf_input_change( elem, formId, fieldId );
2282    }
2283
2284}
2285
2286/**
2287 * Get the input id from a form element's HTML id.
2288 *
2289 * @param {string} htmlId The HTML id of a form element.
2290 *
2291 * @returns {string} inputId The input id.
2292 */
2293function gf_get_input_id_by_html_id( htmlId ) {
2294
2295    var ids = gf_get_ids_by_html_id( htmlId ),
2296        id  = ids[ ids.length - 1 ];
2297
2298    if ( ids.length == 3 ) {
2299        ids.shift();
2300        id = ids.join( '.' );
2301    }
2302
2303    return id;
2304}
2305
2306/**
2307 * Get the form id from a form element's HTML id.
2308 *
2309 * @param {string} htmlId The HTML id of a form element.
2310 *
2311 * @returns {string} formId The form id.
2312 */
2313function gf_get_form_id_by_html_id( htmlId ) {
2314    var ids = gf_get_ids_by_html_id( htmlId );
2315    return ids[0];
2316}
2317
2318/**
2319 * Get the form, field, and input id by a form elements HTML id.
2320 *
2321 * Note: Only multi-input fields will be return an input ID.
2322 *
2323 * @param {string} htmlId The HTML id of a form element.
2324 *
2325 * @returns {array} ids An array contain the form, field and input id.
2326 */
2327function gf_get_ids_by_html_id( htmlId ) {
2328    var ids = htmlId ? htmlId.split( '_' ) : [];
2329    for( var i = ids.length - 1; i >= 0; i-- ) {
2330        if ( ! gformIsNumber( ids[ i ] ) ) {
2331            ids.splice( i, 1 );
2332        }
2333    }
2334    return ids;
2335}
2336
2337function gf_input_change( elem, formId, fieldId ) {
2338    gform.doAction( 'gform_input_change', elem, formId, fieldId );
2339}
2340
2341function gformExtractFieldId( inputId ) {
2342    var fieldId = parseInt( inputId.toString().split( '.' )[0],10 );
2343    return ! fieldId ? inputId : fieldId;
2344}
2345
2346function gformExtractInputIndex( inputId ) {
2347    var inputIndex = parseInt( inputId.toString().split( '.' )[1],10 );
2348    return ! inputIndex ? false : inputIndex;
2349}
2350
2351jQuery( document ).on( 'submit.gravityforms', '.gform_wrapper form', function( event ) {
2352
2353	var formWrapper = jQuery( this ).closest( '.gform_wrapper' ),
2354		formID = formWrapper.attr( 'id' ).split( '_' )[ 2 ],
2355		hasPages = formWrapper.find( '.gform_page' ).length > 0,
2356		sourcePage = parseInt( formWrapper.find( 'input[name^="gform_source_page_number_"]' ).val(), 10 ),
2357		targetPage = parseInt( formWrapper.find( 'input[name^="gform_target_page_number_"]' ).val(), 10 ),
2358		isSubmit = targetPage === 0,
2359		isNextSubmit = ! isSubmit && ( targetPage > sourcePage ),
2360		isSave = jQuery( '#gform_save_' + formID ).val() === '1',
2361		submitButton;
2362
2363	// Get the next or submit button.
2364	if ( hasPages ) {
2365		// Get the visible page.
2366		var visiblePage = formWrapper.find( '.gform_page:visible' ),
2367			buttonType = isNextSubmit ? 'next' : 'submit';
2368
2369		submitButton = visiblePage.find( '.gform_page_footer [id^="gform_' + buttonType + '_button_"]' );
2370	} else {
2371		submitButton = formWrapper.find( '#gform_submit_button_' + formID );
2372	}
2373
2374	var isButtonHidden = ! submitButton.is(':visible'),
2375		abortSubmission = ! isSave && ( isSubmit || isNextSubmit ) && isButtonHidden;
2376
2377	// If we are not saving or returning to an earlier page and the next/submit button is hidden abort the submission.
2378	if ( abortSubmission ) {
2379		window[ 'gf_submitting_' + formID ] = false;
2380		formWrapper.find( '.gform_ajax_spinner' ).remove();
2381		event.preventDefault();
2382	} else if ( isSubmit || isSubmit ) {
2383        var $reCaptcha = formWrapper.find( '.ginput_recaptcha' );
2384
2385        if ( $reCaptcha.length !== 0 && $reCaptcha.data( 'size' ) === 'invisible' ) {
2386            // Check for the verified invisible captcha token first.
2387            var $reCaptchaResponse = formWrapper.find( 'input[name="g-recaptcha-response"]' );
2388            if ( $reCaptchaResponse.length === 0 ) {
2389                $reCaptchaResponse = $reCaptcha.find( '.g-recaptcha-response' );
2390            }
2391            var token = $reCaptchaResponse.val();
2392            if ( ! token ) {
2393                // Execute the invisible captcha.
2394                grecaptcha.execute($reCaptcha.data('widget-id'));
2395                // Once the reCaptcha is triggered, set gf_submitting to true, so the form could be submitted if the
2396                // reCaptcha modal is closed (by clicking on the area out of the modal or the reCaptcha response expires)
2397                window['gf_submitting_' + formID] = false;
2398                event.preventDefault();
2399            }
2400        }
2401    }
2402
2403} );
2404
2405
2406
2407//----------------------------------------
2408//------ HELPER FUNCTIONS ----------------
2409//----------------------------------------
2410
2411if( ! window['rgars'] ) {
2412    function rgars( array, prop ) {
2413
2414        var props = prop.split( '/' ),
2415            value = array;
2416
2417        for( var i = 0; i < props.length; i++ ) {
2418            value = rgar( value, props[ i ] );
2419        }
2420
2421        return value;
2422    }
2423}
2424
2425if( ! window['rgar'] ) {
2426    function rgar( array, prop ) {
2427        if ( typeof array[ prop ] != 'undefined' ) {
2428            return array[ prop ];
2429        }
2430        return '';
2431    }
2432}
2433
2434String.prototype.format = function () {
2435    var args = arguments;
2436    return this.replace(/{(\d+)}/g, function (match, number) {
2437        return typeof args[number] != 'undefined' ? args[number] : match;
2438    });
2439};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.