1// to do late-binding of datepicker callbacks, just define an element here with a key equal to the id of the form input 2var dateCallbacks = {}; 3var ll_formValidatorsLoaded = false; 4 5$(document).ready(function(){ 6 // form validation and styling 7 $("div.formField").on ('focus', 'input,select,textarea', function () { 8 $("div.formField").removeClass("active"); 9 $(this).parents("div.formField").addClass("active"); 10 }).find ('div.error').each (function () { 11 $(this).parents("div.formField").addClass("error"); 12 }); 13 14 $("img.requiredField").each(function() { 15 $(this).parents("div.formField").find("input").not(".sms").addClass("required"); 16 $(this).parents("div.formField").find("select").addClass("required"); 17 $(this).parents("div.formField").find("textarea").addClass("required"); 18 }); 19 $("img.requiredIfVisible").each(function() { 20 $(this).parents("div.formField").find("input").not(".sms").addClass("requiredIfVisible"); 21 $(this).parents("div.formField").find("select").addClass("requiredIfVisible"); 22 $(this).parents("div.formField").find("textarea").addClass("requiredIfVisible"); 23 }); 24 $("input.causesValidation,img.causesValidation,a.causesValidation").on ('click', function () { 25 return ll_generic_form_validate ($(this).hasClass ("skipRequired")); 26 }); 27 28 // get focus on the login box 29 $("#loginBox input.username").each(function () { 30 this.focus (); 31 }); 32 33 // date pickers 34 var calendarSource = "/static/graphics/calendar.gif"; 35 var transparentSource = "/static/graphics/transparent.gif"; 36 if ($("img.calendarImageTemplate.hidden").size () > 0) 37 { 38 calendarSource = $("img.calendarImageTemplate.hidden").eq(0).attr("src"); 39 } 40 if ($("img.transparentImageTemplate.hidden").size () > 0) 41 { 42 transparentSource = $("img.transparentImageTemplate.hidden").eq(0).attr("src"); 43 } 44 var datePickerOptions = { 45 dateFormat:'m/d/y', 46 showOn:'both', 47 buttonImageOnly:true, 48 changeMonth: true, 49 changeYear: true, 50 onSelect:function(dateText) 51 { 52 datePicked(this,dateText); 53 } 54 }; 55 56 $("input.datePicker").each (function () { 57 if ($(this).hasClass("noIcon")) 58 { 59 60 datePickerOptions.buttonImage = transparentSource; 61 datePickerOptions.buttonText = ''; 62 } 63 else 64 { 65 datePickerOptions.buttonImage = calendarSource; 66 datePickerOptions.buttonText = "click to select a date"; 67 } 68 $(this).datepicker(datePickerOptions); 69 }); 70 71 // color pickers 72 $("div.colorPicker").each(function () { 73 var controlSelect = "#" + extractID ($(this).attr("id")); 74 $(this).farbtastic(controlSelect); 75 }); 76 77 // pmt form, select stored or new 78 $("#radio_storedPayment").on ('click', function () { 79 if ($(this).is(":checked")) 80 { 81 hide($("#newPaymentInfo")); 82 } 83 }); 84 $("#radio_newPayment").on ('click', function () { 85 if ($(this).is(":checked")) 86 { 87 show($("#newPaymentInfo")); 88 } 89 }); 90 91 // paypal submit button 92 $("#startExpressCheckoutButton").on ('click', function () { 93 var agreed = $("#chkAgreeToTerms").is(":checked"); 94 if (!agreed && $("#chkAgreeToTerms").length) 95 { 96 alert ('You must agree to the terms before you can continue.'); 97 return false; 98 } 99 $("#startExpressCheckoutAgree").val(agreed ? 1 : 0); 100 $("#startExpressCheckoutForm").submit (); 101 return false; 102 }); 103 104 // fancy pull-downs for team colors 105 $("select.teamColorSelector").not('.delay').each (function () { 106 $(this).selectmenu({ 107 style:'dropdown', 108 format: teamColorFormat 109 }); 110 }); 111 112 if ($("#stripePublishableKey").size() > 0) 113 { 114 initializeStripePayments (); 115 } 116 117 if ($("div#customPageForm").length > 0) 118 { 119 bindTextAreaLengthCheck(); 120 } 121 122 initializeButtonsWithSpinners (); 123 ll_formValidatorsLoaded = true; 124}); 125 126function bindTextAreaLengthCheck() 127{ 128 $("div#customPageForm form").submit(function(e){ 129 var textareaInputCheck = true; 130 var errorMsg = ''; 131 $("div#customPageForm form").find("textarea").each(function () { 132 var currentLen = $(this).val().length; 133 134 if (currentLen > 1900 && !$(this).attr('id').includes('captcha')) 135 { 136 var label = $(this).parents("div.formField").find("label").text().trim(); 137 errorMsg += '\n- ' + label + ' ' + currentLen + ' characters';
138 textareaInputCheck = false; 139 } 140 }); 141 142 if (textareaInputCheck) 143 return true; 144 else { 145 alert("Exceeded 1,900 character limit for following fields:\n" + errorMsg); 146 return false; 147 } 148 }); 149} 150 151/* 152* skipRequired is just a hack so we can have multiple forms on one page 153* and still validate the numeric fields without one form submission getting angry about 154* empty fields that actually belong to another form... (note that they might all be part 155* of the same <form>, but visually presented as separate views via JS... see PaymentActions) 156* The correct answer is 'Validation Groups' 157*/ 158function ll_generic_form_validate (skipRequired) 159{ 160 var emptyFields = emptyRequiredFields (); 161 var numberFields = badNumericFields (); 162 var message = ""; 163 164 if (!skipRequired) { 165 message += messageForRequiredFields(emptyFields, {}); 166 } 167 message += messageForNumericFields (numberFields); 168 169 if (message == "") 170 { 171 if ($("#formInputValidated").length > 0) 172 { 173 $("#formInputValidated").val(1); 174 } 175 return true; 176 } 177 else 178 { 179 alert (message); 180 return false; 181 } 182} 183 184function messageForRequiredFields (emptyFields, skipFields) 185{ 186 var message = ''; 187 if (emptyFields.length > 0) 188 { 189 for (var i = 0; i < emptyFields.length; i++) 190 { 191 if (!skipFields[emptyFields[i]]) { 192 message += " > " + emptyFields[i] + "\n"; 193 } 194 } 195 } 196 197 if (message != '') 198 { 199 message = "Please be sure to fill in the following required fields: \n" + message + "\n\n"; 200 } 201 return message; 202} 203 204function messageForNumericFields (numberFields) 205{ 206 var message = ''; 207 if (numberFields.length > 0) { 208 message += "The following fields require numeric values: \n"; 209 for (var i = 0; i < numberFields.length; i++) 210 { 211 message += " > " + numberFields[i] + "\n"; 212 } 213 message += "\n\n"; 214 } 215 return message; 216} 217 218// get an array of the required fields that have been left empty 219function emptyRequiredFields () 220{ 221 var badFields = []; 222 var badFieldCount = 0; 223 224 $("input.paymentTerms").each( 225 function () 226 { 227 if (!this.checked) 228 { 229 badFields[badFieldCount++] = "You must agree to the payment terms."; 230 } 231 } 232 ); 233 234 var requiredSelectors = ":input.required,:input:visible.requiredIfVisible"; 235 if ($("#teamColorIDContainer").is(":visible")) 236 { 237 requiredSelectors += ",#teamColorID.requiredIfVisible"; 238 } 239 if ($("#teamColorPreferenceContainer").is(":visible")) 240 { 241 requiredSelectors += ",#colorOneID.requiredIfVisible,#colorTwoID.requiredIfVisible,#colorThreeID.requiredIfVisible"; 242 } 243 244 $(requiredSelectors).each( 245 function(){ 246 if ((!$(this).hasClass("checkbox") && $(this).val() == "") || ($(this).hasClass("checkbox") && !this.checked)) 247 { 248 var pmtFields = {"Billing Address" : 1,"Credit Card Number" : 1,"Security Code" : 1}; 249 var fieldName = getFieldName(this); 250 if ($("#radio_storedPayment").is(":checked") && pmtFields[fieldName]) 251 { 252 return; 253 } 254 if (multiSelectSatisfied ($(this))) { 255 return; 256 } 257 if (indexOf (fieldName,badFields) == -1) 258 { 259 badFields[badFieldCount++] = fieldName; 260 } 261 } 262 } 263 ); 264 265 function multiSelectSatisfied ($input) { 266 var $container = $input.parents ('.formInputContainer'); 267 return ($container.hasClass ('multiSelect') && $container.find ('input:checked').length > 0); 268 } 269 270 return badFields; 271} 272 273function badNumericFields () 274{ 275 var badFields = []; 276 277 // fields that allow $ and . 278 validateFieldsByRegExp ("input.fee,input.amountInput,input.paymentRequest", 'money', badFields); 279 280 // pure numeric fields 281 validateFieldsByRegExp ("input.AreaCode,input.Prefix,input.LastFour,input.numericField,#input_CardNumber,#input_SecurityCode", 'number', badFields); 282 283 return badFields; 284} 285 286/* 287* make sure all fields matching the given selector have values that match rePattern 288* if they don't work, append their field name to the badFields array 289*/ 290function validateFieldsByRegExp (sSelector, patternName, badFields) 291{ 292 var badFieldCount = badFields.length; 293 $(sSelector).each ( 294 function () 295 { 296 if (!LLUtil.match ($(this).val (), patternName)) 297 { 298 var fieldName = getFieldName(this); 299 if (indexOf (fieldName,badFields) == -1) 300 { 301 badFields[badFieldCount++] = fieldName; 302 } 303 } 304 } 305 ); 306 return badFields; 307} 308 309// generic callback for datepickers. If it's a single date picker, value is the date. If a range, value is an array 310// with 'from' in position 0 and 'to' in position 1 311function datePicked(element,value) 312{ 313 var id = $(element).attr("id"); 314 callback = dateCallbacks[id]; 315 if (callback) 316 { 317 callback(value); 318 } 319} 320 321function getFieldName(objElement) 322{ 323 var labelText = $(objElement).parents("div.formField").find("label").html(); 324 if ($(objElement).attr("id") == 'couponCodeInput') 325 { 326 labelText = "Coupon Code"; 327 } 328 else if (labelText) 329 { 330 var nextTag = labelText.indexOf("<"); 331 if (nextTag != -1) 332 { 333 labelText = labelText.substr(0,nextTag); 334 } 335 } 336 else 337 { 338 var jqObj = $(objElement); 339 if (jqObj.hasClass("fee") || jqObj.hasClass("paymentRequest") || jqObj.hasClass ("amountInput")) 340 { 341 labelText = "All Dollar Amounts"; 342 } 343 } 344 return labelText.trim (); 345} 346 347/** 348* certain buttons should disappear and get replaced by loading graphics when they're clicked. 349*/ 350function initializeButtonsWithSpinners () 351{ 352 $('.buttonWithSpinner').each (function () { 353 var jqImage = $('<img class="submitButtonSpinner hidden" alt="Processing...please wait." src="/static/graphics/loader-image-30.gif" />').insertAfter ($(this)); 354 var jqForm = $(this).parents('form'); 355 if (jqForm.length > 0) { 356 jqForm.on ('submit', function () { 357 $(this).find ('.buttonWithSpinner').each (function () { 358 jqButton = $(this); 359 doShowSpinner (jqButton); 360 }).end ().find ('.hideOnSubmit').hide (); 361 }); 362 } else { 363 $(this).on ('click', function () { 364 doShowSpinner ($(this)); 365 }); 366 } 367 }); 368} 369function doShowSpinner (jqButton) 370{ 371 if (jqButton.is(':visible')) 372 { 373 hide (jqButton); 374 showLoadingGraphic(jqButton.next('img.submitButtonSpinner'), 250); 375 } 376} 377 378/** 379* set up the payment form to work with Stripe 380*/ 381function initializeStripePayments () 382{ 383 var jqKey = $("#stripePublishableKey"); 384 var jqForm = jqKey.parents ('form'); 385 jqForm.on ('submit', function () { 386 Stripe.setPublishableKey(jqKey.val ()); 387 jqCardNumberInput = $("#input_CardNumber"); 388 if (jqCardNumberInput.size () == 0 || !jqCardNumberInput.is (':visible')) 389 { 390 return true; // if the payment inputs aren't visible, we're not processing pmt so we let the form do what it will 391 } 392 var cardInfo = { 393 number: jqForm.find('#input_CardNumber').val (), 394 cvc: jqForm.find('#input_SecurityCode').val (), 395 exp_month: jqForm.find('select[name="ExpirationMonth"]').val (), 396 exp_year: jqForm.find('select[name="ExpirationYear"]').val (), 397 name: jqForm.find('input[name="FirstName"]').val () + ' ' + jqForm.find('input[name="LastName"]').val (), 398 address_line1: jqForm.find('input[name="AddressOne"]').val (), 399 address_city: jqForm.find('input[name="City"]').val (), 400 address_state: jqForm.find('input[name="State"]').val (), 401 address_zip: jqForm.find('input[name="PostalCode"]').val () 402 }; 403 404 Stripe.createToken(cardInfo, function (status, response) { 405 if (response.error) 406 { 407 $('<input name="StripeError" type="hidden" />').val (response.error.code).appendTo (jqForm); 408 } 409 else 410 {
411 $('<input name="StripeToken" type="hidden" />').val (response.id).appendTo (jqForm); 412 } 413 jqForm.get(0).submit (); 414 }); 415 return false; 416 }); 417} 418 419// function to format the team color pull-downs, adding the swatch 420var teamColorFormat = function (text) 421{ 422 var escaped = LLUtil.escapeTags (text); 423 return escaped.replace (/([^_]*)___([\da-fA-F]{6})___/g,'<div class="teamColorOption"><div class="teamColorSwatch" align="left" style="background-color:#$2;"> </div>$1</div>'); 424}; 425 426// prevent selecting the same color more than once 427if ($("#teamColorPreferenceContainer").length > 0 || $("#nonIndyTeamInfo").length > 0) 428 { 429 var removedOptions = new Array(); 430 $("[id^=color][id$=ID]").each(function(i, el) { 431 $(el).change(function() { 432 var newValue = this.value; 433 var selectedOption = $(this).children('option[selected]'); 434 var currentValuesArr = new Array(this.value); 435 // if we have a valid choice, not the "Choose One" option, remove it from the other selects and add it to the removedOptions holder 436 if(newValue != '') 437 { 438 $("[id^=color][id$=ID]").not(el).each(function() { 439 currentValuesArr.push(this.value); 440 $(this).children('option[value=' + newValue + ']').each(function() { $(this).remove(); }); 441 $(this).selectmenu('destroy').selectmenu({ style:'dropdown', format: teamColorFormat }); 442 }); 443 if(removedOptions.length == 0) 444 { 445 removedOptions.push(selectedOption); 446 } 447 else 448 { 449 var push = true; 450 for(i = 0; i < removedOptions.length; i++) 451 { 452 if(selectedOption == removedOptions[i]) 453 { 454 push = false; 455 } 456 } 457 if(push) removedOptions.push(selectedOption); 458 } 459 } 460 else 461 { 462 $("[id^=color][id$=ID]").not(el).each(function() { 463 currentValuesArr.push(this.value); 464 }); 465 }
466 // replace removed options as necessary 467 for(i = 0; i < removedOptions.length; i++) 468 { 469 // if any element of removedOptions is not one of the currently selected values then add it back to all selects and remove it from the array 470 if($.inArray(removedOptions[i].val(), currentValuesArr) == -1) 471 { 472 $("[id^=color][id$=ID]").each(function() { 473 if($.inArray(removedOptions[i], $(this).children('option')) == -1) 474 { 475 var currentValue = this.value; 476 $(this).append(removedOptions[i]); 477 // resort options 478 var options = $(this).children('option'); 479 options.sort(function(a,b) { 480 if(a.value == '' || a.text < b.text) return -1; 481 else if(a.text > b.text) return 1; 482 else return 0; 483 }); 484 $(this).empty().append(options).val(currentValue); 485 $(this).selectmenu('destroy').selectmenu({ style:'dropdown', format: teamColorFormat }); 486 } 487 }); 488 removedOptions.splice(i, 1); 489 } 490 } 491 }); 492 }); 493 }
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.