1(function () { 2 3 4 // polyfill for matches and closest 5 if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector; 6 if (!Element.prototype.closest) Element.prototype.closest = function (selector) { 7 var el = this; 8 while (el) { 9 if (el.matches(selector)) { 10 return el; 11 } 12 el = el.parentElement; 13 } 14 }; 15 16 17 18 //execute init() on document ready 19 if (document.readyState === "complete" || (document.readyState !== "loading" && !document.documentElement.doScroll)) { 20 listen(); 21 } else { 22 document.addEventListener("DOMContentLoaded", listen); 23 } 24 25 function listen() { 26 if (typeof umbracoFormsCollection === "undefined") { 27 //this will occur if this js file is loaded before the inline scripts, in which case 28 //we'll listen for the inline scripts to execute a custom event. 29 document.addEventListener("umbracoFormLoaded", init); 30 } 31 else { 32 initCollection(umbracoFormsCollection); 33 } 34 } 35 36 function initCollection(formsCollection) { 37 configureUmbracoFormsValidation(); 38 39 for (var i = 0; i < formsCollection.length; i++) { 40 init({ form: formsCollection[i] }); 41 } 42 } 43 44 function init(e) { 45 46 var formItem = JSON.parse(decodeURI(e.form)); 47 48 var forms = document.querySelectorAll('.umbraco-forms-form'); 49 50 for( var i = 0; i < forms.length; i++) { 51 var form = forms[i]; 52 53 dependencyCheck(form); 54 55 var page = form.querySelector('.umbraco-forms-page'); 56 var conditions = new UmbracoFormsConditions(page, 57 formItem.fieldSetConditions, 58 formItem.fieldConditions, 59 formItem.recordValues); 60 conditions.watch(); 61 } 62 } 63 64 /** Configures the jquery validation for Umbraco forms */ 65 function configureUmbracoFormsValidation() { 66 67 if (window.aspnetValidation !== undefined) { 68 // Asp-net validation setup: 69 70 var validationService = new aspnetValidation.ValidationService(); 71 72 // TODO: equivilant to this: 73 /* 74 $.validator.setDefaults({ 75 ignore: ":hidden" 76 }); 77 */ 78 79 function required(value, element, params) { 80 // Handle single and multiple checkboxes: 81 if(element.type.toLowerCase() === "checkbox" || element.type.toLowerCase() === "radio") { 82 var allCheckboxesOfThisName = element.form.querySelectorAll("input[name='"+element.name+"']"); 83 for (var i=0; i<allCheckboxesOfThisName.length; i++) { 84 if (allCheckboxesOfThisName[i].checked === true) { 85 return true; 86 } 87 } 88 return false; 89 } 90 return Boolean(value); 91 } 92 validationService.addProvider("requiredcb", required); 93 validationService.addProvider("required", required);// this will go instead of the build-in required. 94 95 96 function umbracoforms_regex(value, element, params) { 97 if (!value || !params.pattern) { 98 return true; 99 } 100 101 var r = new RegExp(params.pattern); 102 return r.test(value); 103 } 104 validationService.addProvider("umbracoforms_regex", umbracoforms_regex); 105 106 function wrapProviderWithIgnorerBehaviour(provider) { 107 return function(value, element, params) { 108 109 // If field is hidden we ignorer the validation. 110 if (element.offsetParent === null) { 111 return true; 112 } 113 114 return provider(value, element, params); 115 } 116 } 117 118 // we can only incept with default validator if we do it after bootstrapping but before window load event triggers validationservice. 119 window.addEventListener('load', function() { 120 121 // Wrap all providers with ignorer hidden fields logic: 122 for (var key in validationService.providers) { 123 validationService.providers[key] = wrapProviderWithIgnorerBehaviour(validationService.providers[key]); 124 } 125 }); 126 127 // bootstrap validation service. 128 validationService.bootstrap(); 129
130 // Without jquery validation, the previous page submit button click isn't sent with it's name, 131 // so can't be used server-side to determine whether to go forward or back. 132 // Hence we'll use an alternate method, setting a hidden field that's also used in the check. 133 var handlePreviousClicked = function () { 134 this.form.elements["PreviousClicked"].value = "clicked"; 135 }; 136 var previousButtonElements = document.getElementsByClassName("prev cancel"); 137 for (var i = 0; i < previousButtonElements.length; i++) { 138 previousButtonElements[i].form.elements["PreviousClicked"].value = ""; 139 previousButtonElements[i].addEventListener('click', handlePreviousClicked, false); 140 } 141 142 } else if (typeof jQuery === "function" && $.validator) { 143 //Jquery validation setup 144 145 $.validator.setDefaults({ 146 ignore: ":hidden" 147 }); 148 149 $.validator.unobtrusive.adapters.addBool("requiredcb", "required"); 150 151 152 $.validator.addMethod("umbracoforms_regex", function (value, element) { 153 154 var regex = $(element).attr("data-regex"); 155 var val = $(element).val(); 156 if (val.length === 0) { 157 return true; 158 } 159 return val.match(regex); 160 }); 161 162 $.validator.unobtrusive.adapters.addBool("regex", "umbracoforms_regex"); 163 164 var submitInputs = document.querySelectorAll(".umbraco-forms-form input[type=submit]:not(.cancel)"); 165 for (var i = 0; i < submitInputs.length; i++) { 166 var input = submitInputs[i]; 167 input.addEventListener("click", function (evt) { 168 evt.preventDefault(); 169 var frm = $(this).closest("form"); 170 frm.validate(); 171 if (frm.valid()) { 172 frm.submit(); 173 this.setAttribute("disabled", "disabled"); 174 175 } 176 }.bind(input)); 177 } 178 } 179 180 } 181 182 /** 183 * method to determine if Umbraco Forms can run and has the required dependencies loaded 184 * @param {Form Element} formEl the element of the form 185 */ 186 function dependencyCheck(formEl) { 187 // Only perform check if the global 'Umbraco.Sys' is null/undefined. 188 // If present means we are in backoffice & that this is being rendered as a macro preview and We do not need to perform this check here. 189 // Similarly we need a check for if running in a rich text editor. 190 var isBackOffice = function () { 191 return typeof Umbraco !== "undefined" && typeof Umbraco.Sys !== "undefined"; 192 }; 193 var isBackOfficeRte = function () { 194 return document.body.id === "tinymce"; 195 }; 196 if (isBackOffice() || isBackOfficeRte()) { 197 return; 198 } 199 200 var errorElement = document.createElement("div"); 201 errorElement.className = "umbraco-forms missing-library"; 202 errorElement.style.color = "#fff"; 203 errorElement.style.backgroundColor = "#9d261d"; 204 errorElement.style.padding = "15px"; 205 errorElement.style.margin = "10px 0"; 206 var errorMessage = ""; 207 208 //Ensure umbracoForm is not null 209 if (formEl) { 210 211 //Check to see if the message for the form has been inserted already 212 var checkForExistinhgErr = formEl.getElementsByClassName('umbraco-forms missing-library'); 213 if (checkForExistinhgErr.length > 0) { 214 return; 215 } 216 217 var hasValidationFramework = false; 218 219 if (window.jQuery && $ && $.validator !== undefined) { 220 hasValidationFramework = true; 221 } else if (window.aspnetValidation !== undefined) { 222 hasValidationFramework = true; 223 } 224 225 if(hasValidationFramework === false) { 226 errorMessage = errorMessage + "Umbraco Forms requires a validation framework to run, please read documentation for posible options."; 227 } 228 229 if (errorMessage !== "") { 230 errorElement.innerHTML = errorMessage + '<br/> <a href="https://our.umbraco.org/documentation/products/umbracoforms/developer/Prepping-Frontend/" target="_blank" style="text-decoration:underline;
230 color:#fff;">See Umbraco Forms Documentation</a>'; 231 232 formEl.insertBefore(errorElement, formEl.childNodes[0]); 233 } 234 } 235 } 236 237 /** 238 * Class to handle Umbraco Forms conditional statements 239 * @param {any} form a reference to the form 240 * @param {any} fieldsetConditions a reference to the fieldset conditions 241 * @param {any} fieldConditions a reference to the field conditions 242 * @param {any} values the form values 243 * @return {Object} reference to the created class 244 */ 245 function UmbracoFormsConditions(form, fieldsetConditions, fieldConditions, values) { 246 247 //our conditions "class" - must always be newed to work as it uses a form instance to operate on 248 //load all the information from the dom and serverside info and then the class will take care of the rest 249 250 var self = {}; 251 self.form = form; 252 self.fieldsetConditions = fieldsetConditions; 253 self.fieldConditions = fieldConditions; 254 self.values = values; 255 self.dataTypes = {}; 256 257 //Iterates through all the form elements found on the page to update the registered value 258 function populateFieldValues(page, formValues, dataTypes) { 259 260 var selectFields = page.querySelectorAll("select"); 261 for(var i=0; i<selectFields.length; i++) { 262 var field = selectFields[i]; 263 formValues[field.getAttribute("id")] = field.value ? field.querySelector("option[value='" + field.value.replace(/'/g, "\\'") + "']").innerText : null; 264 dataTypes[field.getAttribute("id")] = "select"; 265 } 266 267 var textareaFields = page.querySelectorAll("textarea"); 268 for(var i=0; i<textareaFields.length; i++) { 269 var field = textareaFields[i]; 270 formValues[field.getAttribute("id")] = field.value; 271 dataTypes[field.getAttribute("id")] = "textarea"; 272 } 273 274 // clear out all saved checkbox values to we can safely append 275 var checkboxFields = page.querySelectorAll("input[type=checkbox]"); 276 for(var i=0; i<checkboxFields.length; i++) { 277 var field = checkboxFields[i]; 278 formValues[field.getAttribute("name")] = null; 279 dataTypes[field.getAttribute("id")] = "checkbox"; 280 } 281 282 //$page.find("input").each(function () { 283 var inputFields = page.querySelectorAll("input"); 284 for(var i=0; i<inputFields.length; i++) { 285 var field = inputFields[i]; 286 287 if (field.getAttribute('type') === "text" || field.getAttribute("type") === "hidden") { 288 formValues[field.getAttribute("id")] = field.value; 289 dataTypes[field.getAttribute("id")] = "text"; 290 } 291 292 if (field.getAttribute('type') === "radio") { 293 if (field.matches(':checked')) { 294 formValues[field.getAttribute("name")] = field.value; 295 dataTypes[field.getAttribute("id")] = "radio"; 296 } 297 } 298 299 if (field.getAttribute("type") === "checkbox") { 300 if (field.getAttribute("id") !== field.getAttribute("name")) { 301 if (field.matches(":checked")) { 302 if (formValues[field.getAttribute("name")] === null) { 303 formValues[field.getAttribute("name")] = field.value; 304 } 305 else { 306 formValues[field.getAttribute("name")] += ";;" + field.value; 307 } 308 } 309 } 310 else { 311 formValues[field.getAttribute("name")] = (field.matches(":checked") ? "true" : "false"); 312 } 313 } 314 } 315 } 316 317 /* Public api */ 318 319 self.operators = { 320 Is: function (value, expected, dataType) { 321 if ((value || "") === expected) { 322 return true; 323 } 324 if (value == null) { 325 return (expected == value); 326 } 327 328 if (dataType === "checkbox") { 329 if (expected.toUpperCase() === "TRUE" || expected.toUpperCase() === "ON") { 330 expected = "true" 331 } else if (expected.toUpperCase() === "FALSE" || expected.toUpperCase() === "OFF") { 332 expected = "false" 333 } 334 } 335 336 var values = value.split(';;'); 337 var matchingExpected = values.filter( 338 function (o) { 339 return o === expected; 340 }); 341 return matchingExpected.length > 0;
342 }, 343 IsNot: function (value, unexpected, dataType) { 344 if (value == null) { 345 return (unexpected != value); 346 } 347 var values = value.split(';;'); 348 var matchingUnexpected = values.filter( 349 function (o) { 350 return o === unexpected; 351 }); 352 353 if (dataType === "checkbox") { 354 if (unexpected.toUpperCase() === "TRUE" || unexpected.toUpperCase() === "ON") { 355 unexpected = "true" 356 } else if (unexpected.toUpperCase() === "FALSE" || unexpected.toUpperCase() === "OFF") { 357 unexpected = "false" 358 } 359 } 360 return (value || "") !== unexpected && matchingUnexpected.length === 0; 361 }, 362 GreaterThen: function (value, limit, dataType) { 363 return parseInt(value) > parseInt(limit); 364 }, 365 LessThen: function (value, limit, dataType) { 366 return parseInt(value) < parseInt(limit); 367 }, 368 StartsWith: function (value, criteria, dataType) { 369 return value && value.indexOf(criteria) === 0; 370 }, 371 EndsWith: function (value, criteria, dataType) { 372 return value && value.indexOf(criteria) === value.length - criteria.length; 373 }, 374 Contains: function (value, criteria, dataType) { 375 return value && value.indexOf(criteria) > -1; 376 } 377 }; 378 379 self.watch = function () { 380 // This is a special case for pikaday 381 // The only way around to pickup the value, for now, is to 382 // subscribe to blur events 383 var datepickerfields = self.form.querySelectorAll('.datepickerfield'); 384 for(var i = 0; i < datepickerfields.length; i++) { 385 var field = datepickerfields[i]; 386 field.addEventListener('blur', function () { 387 if(this.value===""){ 388 // Here comes the hack 389 // Force the hidden datepicker field the datepicker field 390 var id = this.getAttribute("id"); 391 var hiddenDatePickerField = id.substr(0, id.length-2); 392 self.values[hiddenDatePickerField]=""; 393 document.getElementById(hiddenDatePickerField).value="";// sadly we cant use querySelector with current mark-up (would need to prefix IDs) 394 } 395 396 populateFieldValues(self.form, self.values, self.dataTypes); 397 //process the conditions 398 self.run(); 399 }.bind(field)); 400 } 401 //subscribe to change events 402 var changeablefields = self.form.querySelectorAll("input, textarea, select"); 403 for(var i = 0; i < changeablefields.length; i++) { 404 var field = changeablefields[i]; 405 field.addEventListener("change", function () { 406 populateFieldValues(self.form, self.values, self.dataTypes); 407 //process the conditions 408 self.run(); 409 }.bind(field)); 410 } 411 412 //register all values from the current fields on the page 413 populateFieldValues(self.form, self.values, self.dataTypes); 414 415 //the initial run-through of all the conditions 416 self.run(); 417 }; 418 419 self.run = function () { 420 var fsId, 421 fieldId, 422 423 /* 424 fsConditions = params.fsConditions || {}, 425 fieldConditions = params.fieldConditions || {}, 426 values = params.values || {},*/ 427 428 cachedResults = {}; 429 430 function evaluateRuleInstance(rule) { 431 var value = self.values[rule.field], 432 dataType = self.dataTypes[rule.field], 433 func = self.operators[rule.operator], 434 result = value !== null && func(value, rule.value, dataType); 435 return result; 436 } 437 438 function evaluateRule(rule) { 439 var dependencyIsVisible = true; 440 441 if (self.fieldConditions[rule.field]) { 442 dependencyIsVisible = isVisible(rule.field, self.fieldConditions[rule.field]); 443 } 444 445 if (dependencyIsVisible) { 446 return evaluateRuleInstance(rule); 447 } 448 else { 449 return false; 450 } 451 } 452 453 function evaluateCondition(id, condition) { 454 // This was once pretty. Now it needs refactoring again. :) 455 456 var any = condition.logicType === "Any", 457 all = condition.logicType === "All", 458 fieldsetVisibilities = {}, 459 hasHiddenFieldset = false, 460 success = true, 461 rule, 462 i; 463 464 for (i = 0; i < condition.rules.length; i++) { 465 rule = condition.rules[i]; 466 467 if (id === rule.field || id === rule.fieldsetId) { 468 throw new Error("Field or fieldset " + id + " has a condition on itself."); 469 } 470 471 if (fieldsetVisibilities[rule.fieldsetId] !== undefined) { 472 continue; 473 } 474 475 if (self.fieldsetConditions[rule.fieldsetId]) { 476 477 fieldsetVisibilities[rule.fieldsetId] = 478 isVisible(rule.fieldsetId, self.fieldsetConditions[rule.fieldsetId]); 479 480 if (!fieldsetVisibilities[rule.fieldsetId]) { 481 hasHiddenFieldset = true; 482 } 483 } 484 else { 485 fieldsetVisibilities[rule.fieldsetId] = true; 486 } 487 } 488 489 if (all && hasHiddenFieldset) { 490 return false; 491 } 492 493 for (i = 0; i < condition.rules.length; i++) { 494 rule = condition.rules[i]; 495 496 if (fieldsetVisibilities[rule.fieldsetId]) { 497 success = evaluateRule(condition.rules[i]); 498 } 499 else { 500 success = false; 501 } 502 503 if (any && success) { 504 break; 505 } 506 if (all && !success) { 507 break; 508 } 509 } 510 return success; 511 } 512 513 function evaluateConditionVisibility(id, condition) { 514 var show = condition.actionType === "Show", 515 cachedResult = cachedResults[id]; 516 517 var success; 518 if (cachedResult === undefined) { 519 cachedResults[id] = show; // set default value to avoid circular issues 520 success = (cachedResults[id] = evaluateCondition(id, condition)); 521 } else { 522 success = cachedResult; 523 } 524 525 var visible = !(success ^ show); 526 return visible; 527 } 528 529 function isVisible(id, condition) { 530 if (condition) { 531 return evaluateConditionVisibility(id, condition); 532 } 533 return true; 534 } 535 536 function handleCondition(element, id, condition, type) { 537 var shouldShow = isVisible(id, condition); 538 if (element) { 539 if (shouldShow) { 540 element.style.display = ""; 541 } 542 else { 543 element.style.display = "none"; 544 } 545 } 546 } 547 548 for (fsId in self.fieldsetConditions) { 549 if (self.fieldsetConditions.hasOwnProperty(fsId)) { 550 handleCondition(document.getElementById(fsId), fsId, self.fieldsetConditions[fsId], "Fieldset");// sadly we cant use querySelector with current mark-up (would need to prefix IDs) 551 } 552 } 553 554 for (fieldId in self.fieldConditions) { 555 if (self.fieldConditions.hasOwnProperty(fieldId)) { 556 if (document.getElementById(fieldId)) { 557 handleCondition(document.getElementById(fieldId).closest(".umbraco-forms-field"),// sadly we cant use querySelector with current mark-up (would need to prefix IDs) 558 fieldId, 559 self.fieldConditions[fieldId], 560 "Field"); 561 } 562 } 563 } 564 }; 565 566 return self; 567 } 568})();
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.