1dnnsfAngular15.module('blueimp.fileupload', []); 2dnnsfAngular15.module('afControls', ['ngSanitize', 'dnnsf', 'blueimp.fileupload']); 3var afApp = (function ($, angular) { 4 var formRoot; 5 const timeZoneOffset = new Date().getTimezoneOffset(); 6 // -------------------------------------------------------------------------------------------------------------------------------- 7 /// Start Action Form template specific code 8 9 angular.module('afControls').factory('afSettings', function () { 10 return { 11 settings: {} 12 } 13 }); 14 15 function ActionFormCtrl($scope, $http, $timeout, $sce, $cookieStore, $element, dataSources, dnnsf, afSettings, dnnsfHttp) { 16 var $ = dnnsfjQuery; 17 $scope.dnnsf = dnnsf; 18 $scope.$sce = $sce; 19 $scope.Math = Math; 20 $scope.testTags = []; 21 22 $scope.form = { 23 fields: [] 24 }; 25 // get the field based on the html element. 26 $scope.getField = function (htmlElement) { 27 var field = _.find(_.values($scope.form.fields), function (_field) { 28 return _field.id === htmlElement.id; 29 }); 30 if (!field) return null; 31 return field; 32 }; 33 34 $scope.controls = []; 35 $scope.registerControl = function (control) { 36 $scope.controls.push(control); 37 }; 38 39 $scope.parse = function (val) { 40 if (val === undefined || val === null) 41 return false; 42 43 if ($.isNumeric(val)) 44 return parseFloat(val); 45 46 // remove comma - this only works for locales that use comma for thousands 47 val = val.constructor === Array ? val.toString() : val.toString().replace(/,(\d{3})/g, '$1'); 48 if ($.isNumeric(val)) 49 return parseFloat(val); 50 51 if (val.toLowerCase() == "true") 52 return true; 53 54 if (val.toLowerCase() == "false") 55 return false; 56 57 return val; 58 }; 59 60 $scope.load = function (mid) { 61 formRoot = $('#dnn' + mid + 'root'); 62 var res = dnnsf.api.actionForm.getSettings(mid); 63 if (res.Data.error) { 64 alert('Error: ' + res.Data.error); 65 return; 66 } 67 68 // todo: handle errors 69 $scope.form = res.Data; 70 $scope.settings = afSettings.settings = res.Settings; 71 $scope.$$ = $; // just a hack to bypass angular 72 73 $scope.form.dirty = function () { 74 return formRoot.find('.ng-dirty').length > 0; 75 } 76 77 //closing the dropdown(multiple choice - dropdown with checkboxes) when clicking outside the container 78 $(document).mouseup(function (e) { 79 var container = $(".field-container.checkbox-list .panel.panel-default"); 80 if (!container.is(e.target) 81 && container.has(e.target).length === 0) { 82 $.each($scope.form.fields, function (i, field) { 83 if (field.show) { 84 setTimeout(function () { 85 field.show = false; 86 $scope.$apply(); 87 }, 0); 88 } 89 }); 90 } 91 }); 92 93 $.each(res.Settings.Fields, function (i, f) { 94 res.Settings.Fields[f.TitleCompacted] = f; 95 }); 96 97 var createOnChangeHandler = function (_scope, fieldId, onChangeScript) { 98 var refresh = function () { 99 // this is an Anti-Pattern https://github.com/angular/angular.js/wiki/Dev-Guide%3A-Anti-Patterns 100 // it is recommended to use $timeout instead https://stackoverflow.com/a/18996042/499220 101 console.warn('The refresh() method is obsolete. Please use the $timeout service that is now provided in the context of the script execution.'); 102 103 var scope = _scope; 104 while (scope) { 105 if (scope.$$phase) { 106 return; 107 } 108 } 109 110 _scope.$apply(); 111 }; 112 113 // Function constructor arguments information: Function(...args: string[], functionBody: string) 114 // a function with arguments named and ordered by the provided `args` parameters and `functionBody` as executable code will be generated. 115 // e.g. Function('form', 'scope', code) => function(form, scope) { /* code */ } 116 var onChangeFunc = new Function('form', 'item', 'scope', 'refresh', '$timeout', onChangeScript); 117 118 return function (form, item) { 119 try { 120 var field = $('#' + fieldId); 121 122 var allowSubmit = onChangeFunc.call(field, form, item, _scope, refresh, $timeout); 123 124 var fieldElement = field[0]; 125 if (fieldElement) { 126 fieldElement.preventSubmit = false;
127 if (allowSubmit === false) { 128 fieldElement.preventSubmit = true; 129 } 130 } 131 } catch (e) { 132 console.error('Error running Action Form on change script', e); 133 } 134 } 135 } 136 137 $.each($scope.form.fields, function (k, field) { 138 139 if (field.onChange) { 140 // This will be called from `data-ng-change` attributes that are generated by xsl (e.g. attr-common.xsl) 141 field.onChange = createOnChangeHandler($scope, field.id, field.onChange); 142 } 143 144 if (field.options) { 145 // this is a dropdown, initialize ddValue and tbValue 146 field.ddValue = field.tbValue = field.value; 147 148 // add a getValue function on dropdowns, which will be used by validators to get the proper value for validation 149 if (_.find(['closed-multiple-dropdown', 'closed-multiple-checkbox', 'dropdown-checkboxes'], 150 function (type) { return field.type == type; }) 151 ) { 152 field.getValue = function (value) { 153 return value.indexOf('/') === 0 ? value.substring(1) : value; 154 } 155 } 156 157 if (field.type == 'closed-multiple-checkbox' || field.type == 'dropdown-checkboxes') { 158 var selItems = field.value; 159 $.each(field.options, function (k, oItem) { 160 oItem.selected = $.inArray(oItem.value, selItems) != -1; 161 }); 162 } else { 163 for (var i = 0; i < field.options.length; i++) 164 if (field.options[i].value == field.value) { 165 field.selected = field.options[i]; break; 166 } 167 } 168 169 // if it has "other" option and value doesn't exist in dropdown, switch dd to other 170 var other = $.grep(field.options, function (oOpt, iOpt) { return oOpt.filter == 'other'; }); 171 other = other.length ? other[0] : null; 172 173 if (other && field.value && $.grep(field.options, function (oOpt, iOpt) { return oOpt.value == field.value; }).length == 0) { 174 field.ddValue = other.value; 175 field.otherValue = field.value; 176 field.selected = _.filter(field.options, function (o) { return o.filter == "other"; })[0]; 177 } 178 179 if (field.type == 'address-region') { 180 181 $scope.setRegionFieldValue = function (source) { // source: 'dropdown' | 'textbox' | 'other' 182 switch (source) { 183 case 'dropdown': { 184 if (field.ddValue !== 'Other') { 185 field.tbValue = field.otherValue = ''; 186 field.value = field.ddValue; 187 field.showOtherTextbox = false; 188 189 if (field.value && field.countryField) { 190 var selectedRegion = _.find($scope.countries[field.countryField].regions, function (obj) { 191 return obj.value == field.value; 192 }); 193 field.viewValue = selectedRegion ? selectedRegion.text : field.value; 194 } 195 } else { 196 field.value = ''; 197 field.showOtherTextbox = true; 198 field.otherValue = field.value; 199 } 200 break; 201 } 202 case 'textbox': { 203 field.ddValue = field.otherValue = ''; 204 field.value = field.tbValue; 205 break; 206 }
207 case 'other': { 208 field.ddValue = 'Other'; 209 field.value = field.otherValue; 210 field.showOtherTextbox = true; 211 break; 212 } 213 } 214 } 215 216 } 217 // if it's a checkbox list, this is also needed 218 if (field.type == 'closed-multiple-checkbox' || field.type == 'dropdown-checkboxes') { 219 $scope.concatValues(field); 220 // also, watch for changes 221 222 $scope.$watch('form.fields.' + field.name + '.value', function () { 223 var selItems = field.value; 224 $.each(field.options, function (k, oItem) { 225 oItem.selected = $.inArray(oItem.value, selItems) != -1; 226 }); 227 }, true); 228 229 field.checkAll = function () { 230 $.each(field.options, function (k, oItem) { 231 if (field.visible !== false) 232 oItem.selected = true; 233 }); 234 setTimeout(function () { 235 field.onChange && field.onChange($scope.form, field); 236 $("[name='" + field.id + "']").valid(); 237 }, 0) 238 }; 239 240 field.uncheckAll = function () { 241 $.each(field.options, function (k, oItem) { 242 if (field.visible !== false) 243 oItem.selected = false; 244 }); 245 setTimeout(function () { 246 field.onChange && field.onChange($scope.form, field); 247 $("[name='" + field.id + "']").valid(); 248 }, 0) 249 }; 250 } else { 251 $scope.$watch('form.fields.' + field.name + '.value', function (newValue, oldValue) { 252 // this allows setting the value of dropdown directly in the model 253 // but watch out if we have multiple entries with same value filtered away 254 newValue !== oldValue && setDropdownSelected(field, newValue); 255 }); 256 } 257 258 if (field.linkedTo) { 259 if (!_.find(field.options, function (option) { return field.tbValue.constructor === Array ? $.inArray(option.value, field.tbValue) != -1 : option.value == field.tbValue })) { 260 field.value = ''; 261 } 262 263 $.each($.map(field.linkedTo.split(','), function (x) { return x.trim() }), function (i, linkedField) { 264 $scope.$watch('form.fields.' + linkedField + '.value', function (newValue, oldValue) { 265 266 if (typeof newValue != 'undefined' && !angular.equals(newValue, oldValue)) { 267 console.log($scope.form.fields[linkedField]); 268 $scope.getFieldData(field); 269 } 270 271 }); 272 $scope.$watch('form.fields.' + linkedField + '.options', function (newValue, oldValue) { 273 274 if (typeof newValue != 'undefined' && $scope.form.fields[linkedField].linkedTo) { 275 $scope.getFieldData(field); 276 } 277 278 }, true); 279 }); 280 } 281 282 // also group options by filters for easy iteration 283 if (field.options.length) { 284 285 // this is used by the permission grid 286 field.optionsFilters = []; // the object below loses order, so we're going to use this one for iterations 287 $.each(field.options, function (ii, oo) { 288 if ($.inArray(oo.filter, field.optionsFilters) == -1) 289 field.optionsFilters.push(oo.filter); 290 }); 291 292 field.optionsNames = [];// the object below loses order, so we're going to use this one for iterations 293 field.optionsByName = {}; 294 $.each(field.options, function (ii, oo) { 295 if (!field.optionsByName[oo.text]) { 296 field.optionsNames.push(oo.text); 297 field.optionsByName[oo.text] = []; 298 } 299 field.optionsByName[oo.text].push(oo); 300 }); 301 } 302 303 } 304 305 if (field.type === 'open-dnn-editor') { 306 var dnnTextEditorIframe = $('#' + field.id); 307 if (dnnTextEditorIframe.length && dnnsf.canAccessIFrame(dnnTextEditorIframe[0]) && dnnTextEditorIframe[0].contentWindow.getContent) { 308 dnnTextEditorIframe.attr("data-content", field.value); 309 dnnTextEditorIframe[0].contentWindow.setContent(field.value); 310 } 311 } 312 }); 313 314 // done intializing fields, call on load handler if any 315 if ($scope.form.onLoad) { 316 eval('( function(form) { var $scope = scope = this; try { ' + $scope.form.onLoad + '; } catch (e) { console.log(\'Error running Action Form on load script\', e); } } )') 317 .call($scope, $scope.form); 318 } 319 320 if ($scope.form.SaveInCookie) { 321 var saveInCookiesTimer; 322 function saveInCookies() { 323 $timeout.cancel(saveInCookiesTimer); 324 saveInCookiesTimer = $timeout(function () { 325 326 var saveData = getFormData($element.closest('.form-root')); 327 dnnsfHttp($scope.settings.ModuleId, { 328 method: 'POST', 329 url: $scope.form.submitUrl + "&event=autosave&submission=" + ($scope.form.submissionKey || ''), 330 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 331 data: $.param(saveData) 332 }).success(function (data, status) { 333 $cookieStore.put($scope.form.SaveInCookie, data.submissionKey, { path: '/', expires: 365 }); 334 }); 335 336 }, 500); 337 } 338 339 $scope.$watch('form.fields', saveInCookies, true); 340 } 341 $scope.showLoader = false;
342 formRoot.find('.c-form.hidden').removeClass('hidden'); 343 344 dnnsf.events.emit('formLoaded'); 345 346 }; 347 348 function setDropdownSelected(field, val) { 349 for (var i = 0; i < field.options.length; i++) 350 if (field.options[i].value == val) { 351 field.selected = field.options[i]; 352 break; 353 } 354 } 355 356 $scope.refreshCaptcha = function (mid, fieldName) { 357 var field = $scope.form.fields[fieldName]; 358 359 var svcFramework = dnnsfjQuery.ServicesFramework(mid); 360 var apiUrl = svcFramework.getServiceRoot("DnnSharp/ActionForm"); 361 362 dnnsfHttp($scope.settings.ModuleId, { 363 method: 'GET', 364 url: apiUrl + 'RefreshField/Captcha?_portalId= ' + dnnsf.portalId + 365 '&tabId=' + dnnsf.tabId + 366 '&_alias=' + dnnsf.alias + 367 '&_mid=' + mid + 368 '&language=' + dnnsf.locale + 369 '&fieldId=' + field.fieldId + 370 '&fieldName=' + fieldName, 371 headers: { 'Content-Type': 'application/x-www-form-urlencoded' } 372 }).then(function (response) { 373 $timeout(function () { 374 $scope.form.fields[fieldName].value = response.data.CaptchaEncrypted; 375 $('#dnn' + mid + fieldName + 'captchaenc').val(response.data.CaptchaEncrypted); 376 $('#dnn' + mid + 'root').find("img[data-fieldid=" + field.fieldId + "]").attr('src', response.data.ImageUrl); 377 }); 378 }); 379 } 380 381 $scope.getFieldData = function (field) { 382 $timeout(function () { 383 // get list from server 384 var saveData = getFormData($element.closest('.form-root')); 385 field.$_loading = true; 386 387 dnnsfHttp($scope.settings.ModuleId, { 388 method: 'POST', 389 url: $scope.form.getItemsUrl + "&fieldId=" + $scope.form.fields[field.name].fieldId + '&fieldName=' + field.name, 390 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 391 data: $.param(saveData) 392 }).then(function (result) { 393 var data = result.data; 394 $('#' + field.id).closest('.field-container') 395 .removeClass('has-error') 396 .find('span.text-danger') 397 .html(''); 398 if (data.error) { 399 data.validationErrors = [{ 'fieldId': field.name, 'message': data.error }]; 400 delete data.error; 401 parseFormResponse2(formRoot, null, data); 402 field.$_loading = false; 403 field.options = []; 404 field.value = ''; 405 return; 406 } 407 field.options = data; 408 var found = false; 409 for (var i = 0; i < field.options.length; i++) 410 if (field.tbValue.constructor === Array ? $.inArray(field.options[i].value, field.tbValue) != -1 : field.options[i].value == field.tbValue) { 411 field.selected = field.options[i]; 412 if (field.tbValue.constructor === Array) { 413 $.each(field.tbValue, function (i, v) { 414 $.each(field.options, function (j, k) { 415 k.value == v && (field.options[j].selected = true); 416 found = true; 417 }); 418 }); 419 } else { 420 _.forEach(field.options, function (option, index) { 421 if (option.value === field.tbValue) { 422 option.selected = true; 423 found = true; 424 return false;
425 } 426 }); 427 } 428 field.value = field.tbValue; //need it to get 429 break; 430 } 431 if (!found) 432 field.value = ''; 433 $scope.concatValues(field); 434 field.$_loading = false; 435 }); 436 }); 437 }; 438 439 $scope.concatValues = function (ctl) { 440 if (!ctl.options) 441 return; 442 var vals = []; 443 var texts = []; 444 $.each(ctl.options, function (k, o) { 445 if (o.selected && o.visible !== false) { 446 vals.push(o.value); 447 texts.push(o.text); 448 } 449 }); 450 ctl.value = vals; 451 if (ctl.selectedItemsText) 452 texts.length == 0 453 ? 454 ctl.text = localization.dropdownNoSelection 455 : 456 ctl.text = localization.dropdownSelectedBefore + ' ' + texts.length + ' ' + localization.dropdownSelectedAfter; 457 else 458 texts.length == 0 ? ctl.text = localization.dropdownNoSelection : ctl.text = texts.join(','); 459 }; 460 461 // holds a list of region fields bound to each country 462 $scope.countries = {}; 463 464 $scope.wireRegion = function (regionField, countryField) { 465 if (!$scope.countries[countryField]) 466 $scope.countries[countryField] = { regionFields: [] }; 467 $scope.countries[countryField].regionFields.push(regionField); 468 469 $scope.loadRegions(regionField, countryField, function () { 470 // select current region by code or by name 471 var field = $scope.form.fields[regionField]; 472 var regions = $scope.countries[countryField].regions; 473 474 if (regions.length) { 475 476 for (var i = 0; i < regions.length; i++) 477 if (regions[i].value == field.value) { 478 $scope.form.fields[regionField].ddValue = regions[i].value; 479 return; 480 } 481 // check by text 482 for (var i = 0; i < regions.length; i++) 483 if (regions[i].text == field.value) { 484 $scope.form.fields[regionField].ddValue = regions[i].value; 485 return; 486 } 487 if (field.otherTextbox && $scope.initCountry) { 488 $scope.initCountry = false; 489 var isValueInOptions = $.grep(field.options, function (option, iOpt) { return option.value == field.value; }).length > 0; 490 if (!isValueInOptions && field.value) { 491 field.ddValue = 'Other'; 492 field.otherValue = field.value; 493 field.showOtherTextbox = true; 494 return; 495 } 496 } 497 $scope.setRegionFieldValue('dropdown'); 498 } 499 }); 500 }; 501 502 $scope.loadRegions = function (regionField, countryField, fnDone) { 503 dnnsf.log('loadRegions', countryField, $scope.countries[countryField], $scope.form.fields[countryField]); 504 505 $scope.$watch('form.fields.' + countryField, function () { 506 if (!$scope.countries[countryField] || !$scope.form.fields[countryField]) 507 return; 508 509 $scope.countries[countryField].loading = true; 510 511 var field = $scope.form.fields[regionField]; 512 var data = {}; 513 data[countryField] = $scope.form.fields[countryField].value; 514 515 dnnsfHttp($scope.settings.ModuleId, { 516 method: 'POST', 517 url: $scope.form.getItemsUrl + "&fieldId=" + field.fieldId + '&fieldName=' + field.name, 518 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 519 data: $.param(data) 520 }).then(function (result) { 521 $scope.countries[countryField].loading = false;
522 $scope.countries[countryField].regions = result.data; 523 if ($scope.countries[countryField].regions.length) { 524 // reset all textboxes values 525 $.each($scope.countries[countryField].regionFields, function (i, regionField) { 526 $scope.form.fields[regionField].countryField = countryField; 527 if ($scope.form.fields[regionField]) { 528 $scope.form.fields[regionField].ddValue = ""; 529 $scope.form.fields[regionField].tbValue = ""; 530 $scope.form.fields[regionField].otherValue = ""; 531 } 532 }); 533 } else { 534 // reset all dropdown values 535 $.each($scope.countries[countryField].regionFields, function (i, regionField) { 536 if ($scope.form.fields[regionField]) { 537 $scope.form.fields[regionField].ddValue = ""; 538 $scope.form.fields[regionField].tbValue = ""; 539 $scope.form.fields[regionField].otherValue = ""; 540 } 541 }); 542 } 543 544 fnDone && fnDone(); 545 }); 546 }, true); 547 548 }; 549 550 $scope.showSelected = function (node, selected, name) { 551 if (selected) { 552 $scope.form.fields[name].value = node.value; 553 $scope.form.fields[name].text = node.text; 554 } else { 555 $scope.form.fields[name].value = ""; 556 $scope.form.fields[name].text = ""; 557 } 558 } 559 560 $scope.closeDropdown = function (e, name) { 561 $scope.form.fields[name].showdrop = false; 562 $scope.$apply(); 563 } 564 565 $scope.uptStarRating = function (value, name) { 566 $scope.form.fields[name].value = value; 567 } 568 569 $scope.updateField = function (field, val) { //used for dropdown with autocomplete and sortable-input 570 $scope.form.fields[field].value = val; 571 if ($scope.form.fields[field].options && $scope.form.fields[field].options.length) { 572 setDropdownSelected($scope.form.fields[field], val); 573 } 574 $scope.form.fields[field].onChange && $scope.form.fields[field].onChange($scope.form, field); 575 } 576 } 577 ActionFormCtrl.$inject = ['$scope', '$http', '$timeout', '$sce', '$cookieStore', '$element', 'dataSources', 'dnnsf', 'afSettings', 'dnnsfHttp']; 578 579 var initForm = function (options, fnDone) { 580 var formRoot; 581 var svcFramework = dnnsfjQuery.ServicesFramework(options.moduleId); 582 options.apiUrl = svcFramework.getServiceRoot("DnnSharp/ActionForm"); 583 584 options.adminApiUrl = options.virtualDirectory + "/DesktopModules/DnnSharp/ActionForm/AdminApi.ashx"; 585 586 if (options) { 587 dnnsf.portalId = options.portalId; 588 } 589 //openMode: Always, Page, Popup, Inline, Manual 590 var $ = dnnsfjQuery; 591 var formSettings = dnnsf.api.actionForm.getSettings(options.moduleId); 592 var queryString = $.extend( 593 {}, 594 dnnsf.getUrlParts(location.search).query, 595 formSettings && !$.isEmptyObject(formSettings.passQs) && formSettings.passQs, 596 options.qs && !$.isEmptyObject(options.qs) && options.qs 597 ); 598 dnnsf.api.actionForm.patchSettings(options.moduleId, { options: options }); 599 $('#' + options.rootElementClientId).addClass(options.cssName); 600 $('#' + options.rootElementClientId).attr({ 'af-name': options.popupSettings.name, 'data-moduleid': options.moduleId }); //for openPopupByName() 601 if (options.openMode != "Always" && !options.manualMode) { 602 window['showFormPopup' + options.moduleId] = function () { 603 dnnsf.api.actionForm.openPopupById(options.moduleId); 604 }; 605 window['hideFormPopup' + options.moduleId] = function () { 606 $('#dnn' + options.moduleId + 'popup').modal('hide') 607 } 608 window['showFormInline' + options.moduleId] = function () { 609 dnnsf.api.actionForm.initForm(options.moduleId); 610 showFormInline(options.moduleId, options.rootElementClientId); 611 } 612 window['hideFormInline' + options.moduleId] = function () { 613 hideFormInline(options.moduleId, options.rootElementClientId); 614 } 615 if (!options.manualMode && options.openMode != "Always" && options.openMode != "Manual") 616 $('#' + options.rootElementClientId).html($('<div class="frontEndTemplate"></div>').html(options.frontEndTemplate)); 617 618 return; 619 } else { 620 setTimeout(function () { 621 options.showLoading && $('#' + options.rootElementClientId + ' > .common-loading-container').show(); 622 options.tabsProLoading && $('#' + options.rootElementClientId).closest('.tab-pane').hasClass('active') && dnnsf.events.broadcast('loadForm', { 'loading': true, moduleId: options.moduleId }); 623 dnnsf.initStickyLoading(options.rootElementClientId); 624 }, 0); 625 626 (!options.manualMode && options.openMode != "Always") && 627 $('#' + options.rootElementClientId).html($('<div class="frontEndTemplate"></div>').html(options.frontEndTemplate)); 628 } 629 // ^^backward compatibility for showFormPopup, hideFormPopup, showFormInline, hideFormInline functions^^ 630 // getSettings 631 $.extend(queryString, { 632 '_portalId': options.portalId, 633 'referrer': document.referrer, 634 'openMode': options.openMode === "Manual" ? options.manualMode : options.openMode, 635 '_tabId': options.tabId, 636 '_alias': options.alias, 637 '_mid': options.moduleId, 638 '_url': document.URL 639 }); 640 641 $.ajax({ 642 headers: { 643 "DNNSF-Time-Offset": timeZoneOffset, 644 "RequestVerificationToken": svcFramework.getAntiForgeryValue() 645 }, 646 url: options.apiUrl + '/settings/initializeForm?' + $.param(queryString) + (options.dnnPageQuery ? '&' + options.dnnPageQuery : ''), 647 type: "post", 648 data: options.requestForm, 649 async: true, 650 success: function (res) {
651 res.CssIncludes.forEach(function (cssPath) { 652 dnnsf.includeCss(cssPath); 653 }); 654 655 if (!res.Html) { 656 dnnsf.api.actionForm.hideFormLoading(options.moduleId); 657 } 658 $('#' + options.rootElementClientId).find('.af-alert').remove(); 659 options.hasErrors = false; 660 661 if (res.ResultType == "ActionResult") { 662 options.manualMode == "Popup" && (dnnsf.api.actionForm.isFormPopupOpen['formPopup' + options.moduleId] = false); // the popup will not be opened because of the error/message 663 parseFormResponse(res.ActionResult, { 664 error: function (err) { 665 options.hasErrors = true; 666 var pnlMessage = $('<div class="af-alert alert alert-danger"></div>').html(res.ActionResult.Error); 667 $('#' + options.rootElementClientId).html($('<div class="frontEndTemplate"></div>').append(pnlMessage)); 668 } 669 }); 670 } 671 res.Settings.apiUrl = options.apiUrl; 672 res.Settings.timeZoneOffset = timeZoneOffset; 673 dnnsf.api.actionForm.patchSettings(options.moduleId, res); 674 if (!$.isEmptyObject(res.ActionResult) && res.ActionResult.Content) { 675 formRoot = $(res.ActionResult.Content); 676 var pnlMessage = $('<div class="af-alert alert alert-info"></div>').append(formRoot); 677 $('#' + options.rootElementClientId).append(pnlMessage); 678 } else { 679 $('#' + options.rootElementClientId).find('#dnn' + options.moduleId + 'root').length ? 680 $('#dnn' + options.moduleId + 'root').replaceWith(res.Html) : 681 $('#' + options.rootElementClientId).append($.parseHTML(res.Html)); 682 dnnsf.loadJsFromHtml(res.Html); 683 formRoot = $('#dnn' + options.moduleId + 'root'); 684 } 685 686 if (res.JsIncludes.length) { 687 res.JsIncludes.forEach(function (jsPath, index, array) { 688 if (index !== array.length - 1) { 689 $.getScript(jsPath); 690 } else { 691 $.getScript(jsPath, function () { 692 initFormController(); 693 fnDone && fnDone(); 694 }); 695 } 696 }); 697 } else { 698 initFormController(); 699 fnDone && fnDone(); 700 } 701 702 }, 703 error: function (err) { 704 console.error(this.url, err) 705 options.hasErrors = true; 706 var pnlMessage = $('<div class="af-alert alert alert-info"></div>').html(err.responseText); 707 $('#' + options.rootElementClientId).html($('<div class="frontEndTemplate"></div>').append(pnlMessage)); 708 } 709 }); 710 711 function initFormController() { 712 713 if (options.hasErrors) return; 714 if (!formRoot.length || formRoot[0].initialized) 715 return; 716 717 formRoot[0].onFormSubmit = formRoot[0].onFormSubmit || []; 718 formRoot[0].initialized = true; 719 720 // init common features 721 dnnsf.init($, options); 722 dnnsf.localization = localization = dnnsf.api.actionForm.getSettings(options.moduleId).Localization; 723 // call localization inside fileupload-validate 724 dnnsf.useLocalization && dnnsf.useLocalization(); 725 726 var app = angular.module('ActionForm' + formRoot.attr('id'), ['ngAnimate', 'ngSanitize', 'dnnsf', 'afControls', 'ui.bootstrap', 'ui.bootstrap.datetimepicker', 'angucomplete', 'treeControl', 'ngstars', 'loadOnDemand']); 727 var dynamicDependencies = ['ui.bootstrap.contextMenu', 'cp.ngConfirm'] //will be added if the js is loaded from input config 728 $.each(dynamicDependencies, function (i, dep) { 729 try { 730 if (angular.module(dep)) 731 app.requires.push(dep); 732 } catch (e) { } 733 }); 734 app.run(["$http", function ($http) { 735 $http.defaults.headers.common["DNNSF-Time-Offset"] = timeZoneOffset; 736 }]); 737 app.controller('ActionFormCtrl', ActionFormCtrl); 738 739 app.directive('hasRepeaters', [function () { 740 return { 741 restrict: 'A', 742 priority: Number.MIN_SAFE_INTEGER, 743 scope: false, 744 link: function (scope) { 745 setTimeout(function () { 746 if (!scope.$root.repeaters) { 747 options.showLoading && $('#' + options.rootElementClientId + ' > .common-loading-container').hide(); 748 options.tabsProLoading && dnnsf.events.broadcast('loadForm', { 'loading': false, moduleId: scope.settings.ModuleId }); 749 } 750 }, 0) 751 } 752 } 753 }]); 754 755 app.directive('addCustomAttributes', [function () { 756 return { 757 restrict: 'A', 758 scope: false, 759 link: function (scope, element, attrs) { 760 if (scope.settings.Fields[attrs.afField]) { 761 var customAttributesList = scope.settings.Fields[attrs.afField].CustomAttributes; 762 customAttributesList.forEach(function (attribute) { 763 element.attr(attribute.name, attribute.value); 764 }); 765 } 766 } 767 } 768 }]); 769 770 app.directive('floatingLabel', ['$timeout', function ($timeout) { 771 return { 772 restrict: 'A', 773 // this directive works only with scope:false 774 scope: false, 775 link: function (scope, element) { 776 if (!scope.floatLabels) { 777 scope.floatLabels = {}; 778 } 779 780 if (!scope.floatLabels[element.attr('id')]) { 781 scope.floatLabels[element.attr('id')] = new FloatLabels(element.closest('.form-group')[0]); 782 } else { 783 scope.floatLabels[element.attr('id')].rebuild(); 784 } 785 } 786 } 787 }]); 788 789 app.directive('repeatDone', [function () { 790 return { 791 restrict: 'AE', 792 scope: false, 793 link: { 794 pre: function (scope, element, attrs) { 795 scope.$root.repeaters = true; 796 }, 797 post: function (scope, element, attrs) { 798 setTimeout(function () { 799 if (scope.$last) { // all are rendered 800 options.showLoading && $('#' + options.rootElementClientId + ' > .common-loading-container').hide(); 801 options.tabsProLoading && dnnsf.events.broadcast('loadForm', { 'loading': false, moduleId: scope.settings.ModuleId }); 802 } 803 }, 0); 804 } 805 } 806 } 807 }]); 808 809 app.directive('maxSelection', [function () { 810 return { 811 restrict: 'AE', 812 scope: { 813 value: '=', 814 maxSelection: '=', 815 ngModel: '=' 816 }, 817 link: { 818 post: function (scope, element, attrs) { 819 scope.$watch('value', function (newValue, oldValue) { 820 if ((newValue !== oldValue && newValue.length === scope.maxSelection) || scope.maxSelection <= 0) { 821 element.attr('disabled', !scope.ngModel); 822 } else { 823 element.attr('disabled', false); 824 } 825 }); 826 } 827 } 828 } 829 }]); 830 831 app.factory('dataSources', ['$http', 'dnnsf', function ($http, dnnsf) { 832 return { 833 get: function (settings, fnReady) { 834 var p = $.extend({}, settings, { Method: 'GetData', tabId: options.tabId, mid: options.moduleId, alias: options.alias }); 835 $http({ 836 method: 'GET', 837 url: options.adminApiUrl + '?' + $.param(p), 838 cache: true 839 }).success(function (data, status) { 840 fnReady && fnReady(data); 841 }); 842 } 843 }; 844 }]); 845 846 847 // the default cookieStore does not support path or expiration 848 app.provider('$cookieStore', [function () { 849 var self = this; 850 self.defaultOptions = {}; 851 852 self.setDefaultOptions = function (options) { 853 self.defaultOptions = options; 854 }; 855 856 self.$get = function () { 857 return { 858 get: function (name) { 859 var jsonCookie = $.cookie(name); 860 if (jsonCookie) { 861 return angular.fromJson(jsonCookie); 862 } 863 }, 864 put: function (name, value, options) { 865 options = $.extend({}, self.defaultOptions, options); 866 $.cookie(name, angular.toJson(value), options); 867 }, 868 remove: function (name, options) { 869 options = $.extend({}, self.defaultOptions, options); 870 $.removeCookie(name, options); 871 } 872 }; 873 }; 874 }]); 875 876 app.directive('dropdownWatch', ['$interval', function ($interval) { // resize "dropdown with checkboxes" based on content width and height 877 return { 878 restrict: 'A', 879 scope: { 880 dropdownName: '=', 881 disableCheckboxes: '@' 882 }, 883 controller: ['$scope', '$element', function ($scope, $element) { 884 $scope.dropdownName.show = false;
885 var dropdownPanel = $element.parent().parent().next().children('.panel'); 886 var disabled = $element[0].disabled; 887 888 if (disabled && $scope.disableCheckboxes === "True") { 889 $element.prop("disabled", false); 890 } 891 $scope.$watch("dropdownName.show", function (newVal, oldVal) { 892 if (!newVal) 893 return; 894 if (disabled && $scope.disableCheckboxes === "True") { 895 dropdownPanel.find('a') 896 .parent().addClass("dnnsf-disabled-checkboxes");// disable 'select all' and 'clear all' 897 898 dropdownPanel.find('.normalCheckBox').each(function () { 899 $(this).prop("disabled", true).addClass("disabled") 900 .parent().addClass("not-allowed"); 901 }); 902 } 903 904 var dropdownOpen = $interval(function () { 905 if (dropdownPanel.width() > 0) { 906 resizeDropdown(dropdownPanel, $scope.dropdownName); 907 $interval.cancel(dropdownOpen); 908 } 909 }, 50); //interval 910 }); 911 912 function resizeDropdown(dropdownPanel, dropdownName) { 913 if (dropdownPanel.attr('data-window-width') == $(window).width()) 914 return; 915 if (dropdownName.show) { 916 dropdownPanel.css('opacity', '0'); 917 if (dropdownPanel.attr('data-window-width')) { 918 dropdownPanel.width(dropdownPanel.width() - 15); // this eliminates endless adding of padding when you resize the window 919 dropdownPanel.attr('style', ''); 920 } 921 dropdownPanel.css('display', 'table'); 922 var inputWidth = dropdownPanel.parent().prev().children().first().width(); 923 var contentWidth = dropdownPanel.width() + 15; // magic number for padding 924 var rect = dropdownPanel[0].getBoundingClientRect(); 925 var windowWidth = $(window).width(); 926 927 dropdownPanel.attr('data-window-width', windowWidth); 928 dropdownPanel.css('display', 'inline-block'); // i took the width from display:table 929 // and make it inline-block with that width 930 if (inputWidth > contentWidth) { // if the input is bigger than the dropdown panel make the panel input-sized 931 dropdownPanel.width(inputWidth); 932 dropdownPanel.parent().width(inputWidth); 933 dropdownPanel.css('opacity', '1'); 934 } 935 else { 936 if (rect.right > windowWidth) { // if the dropdown is bigger than the window 937 contentWidth = contentWidth - (rect.right - windowWidth) - 15; // magic number for padding 938 dropdownPanel.css('overflow-x', 'scroll'); 939 } 940 dropdownPanel.width(contentWidth); 941 dropdownPanel.parent().width(contentWidth); 942 dropdownPanel.css('opacity', '1'); 943 } 944 }// show 945 }; //resize 946 }] //controller 947 }; //directive return 948 }]); //directive 949 950 app.directive('maxNumber', [function () { 951 return { 952 restrict: 'A', 953 require: 'ngModel', 954 link: function (scope, element, attrs, ngModel) { 955 if (typeof parseFloat(attrs.maxNumber) == 'number') { 956 scope.$watch(attrs.ngModel, function (newVal, oldVal) { 957 if (newVal > parseFloat(attrs.maxNumber)) { 958 ngModel.$setViewValue(parseFloat(attrs.maxNumber)); 959 ngModel.$render(); 960 $(element).closest('.form-group').addClass('has-error'); 961 setTimeout(function () { 962 $(element).closest('.form-group').removeClass('has-error'); 963 }, 1000); 964 } 965 }) 966 } 967 } 968 } 969 }]); 970 // wrapper for masked input 971 app.directive('inputMask', function () { 972 return { 973 restrict: 'A', 974 link: function (scope, el, attrs) { 975 delete $.jMaskGlobals.translation['#']; 976 var label = attrs.placeholder || ""; 977 var mask = attrs.inputMask.replace(/[0-9a-zA-Z]/g, '_'); 978 var maskOptionsObject = { 979 translation: { 980 '9': { 981 pattern: /\d/, optional: true 982 }, 983 'a': { 984 pattern: /[a-zA-Z]/ 985 }, 986 '*': { 987 pattern: /[0-9a-zA-Z]/ 988 } 989 }, 990 placeholder: label || mask 991 }; 992 993 var parsedMaskOptions; 994 995 if (attrs.maskOptions) { 996 try { 997 parsedMaskOptions = JSON.parse(attrs.maskOptions); 998 } catch (error) { 999 console.error("Couldn't parse JSON in " + attrs.afFieldTitle + " Mask Options") 1000 } 1001 } else { 1002 parsedMaskOptions = {} 1003 } 1004 1005 $(el).mask(attrs.inputMask, $.extend(maskOptionsObject, parsedMaskOptions)); 1006 1007 el.on('focus', function () { 1008 el.attr('placeholder', mask); 1009 }) 1010 el.on('keyup blur', function () { 1011 label && el.attr('placeholder', label); 1012 el.trigger('change'); 1013 }); 1014 if (scope.form.fields[attrs.afField].value) { 1015 setTimeout(function () { 1016 el.trigger('input'); 1017 }, 0); 1018 } 1019 } 1020 }; 1021 }); 1022 1023 // wrapper for masked input 1024 app.directive('onblur', function () { 1025 return { 1026 restrict: 'A', 1027 scope: { 1028 onblur: '&' 1029 }, 1030 link: function (scope, el, attrs) { 1031 $(el).parents('.element-area:first').click(function (e) { 1032 e.stopPropagation(); 1033 }); 1034 $(document).click(function () { 1035 scope.onblur(); 1036 scope.$apply(); 1037 }); 1038 } 1039 }; 1040 }); 1041 1042 // DOM wathcher 1043 app.directive('domWatch', function () { 1044 return { 1045 restrict: 'A', 1046 link: function (scope, el, attrs) { 1047 1048 $(el).on(attrs.domWatch, function () { 1049 scope.$eval(attrs.ngModel + "='" + el.val() + "'"); 1050 scope.$apply(); 1051 }); 1052 } 1053 }; 1054 }); 1055 1056 // this directive knows to bind a value to a html control, but only when this value exists 1057 app.directive('afBindvalue', ['$compile', '$timeout', '$parse', function ($compile, $timeout, $parse) { 1058 return { 1059 restrict: 'A', 1060 scope: false, 1061 require: 'ngModel', 1062 link: function (scope, element, attrs, ngModel) { 1063 var options = null; 1064 if (attrs.afBindfrom) 1065 options = scope.$eval(attrs.afBindfrom); 1066 1067 // if it's not an input element, define a new render function 1068 if (element.filter(':input').length == 0) { 1069 ngModel.$render = function () { 1070 if (ngModel.$viewValue === undefined || ngModel.$viewValue === null) 1071 return; 1072 if (!element.hasClass('model-only')) 1073 element.html(ngModel.$viewValue); 1074 }; 1075 } 1076 1077 scope.$watch(attrs.afBindvalue, function (value) { 1078 if (!scope.form.fields || !scope.form.fields[attrs.afField]) 1079 return; 1080 1081 var field = scope.form.fields[attrs.afField]; 1082 if (field.touched) 1083 return; 1084 1085 if (attrs.afBindfrom) { 1086 var options = scope.$eval(attrs.afBindfrom); 1087 if (!options) 1088 return; 1089 var optionsFound = $.grep(options, function (o) { return o.value === value }); 1090 if (optionsFound.length > 0) { 1091 ngModel.$setViewValue(optionsFound[
10910]); 1092 }; 1093 } 1094 else { 1095 ngModel.$setViewValue(value); 1096 } 1097 ngModel.$render(); 1098 }); 1099 } 1100 }; 1101 }]); 1102 1103 // initialize rich edits 1104 app.directive('afRichedit', ['$compile', '$timeout', '$parse', function ($compile, $timeout, $parse) { 1105 return { 1106 require: 'ngModel', 1107 link: function (scope, elm, attrs, ngModel) { 1108 var fnInitRichEdit = function () { 1109 1110 if (!$(elm).is(':visible')) { 1111 $timeout(fnInitRichEdit, 200); 1112 return; 1113 } 1114 1115 $(elm).wysiwyg({ 1116 autoGrow: false, 1117 maxHeight: 600, 1118 initialMinHeight: 50, 1119 initialContent: '', 1120 brIE: false, 1121 replaceDivWithP: true, 1122 events: { 1123 save: function () { 1124 try { 1125 ngModel.$setViewValue(this.getContent()); 1126 } catch (e) { 1127 } 1128 } 1129 } 1130 }); 1131 1132 // localize wysiwyg 1133 $('.wysiwyg [role="menuitem"]').each(function () { 1134 var l = localization['wysiwyg.' + $(this).attr('class')]; 1135 l && $(this).attr('title', l); 1136 }); 1137 1138 1139 ngModel.$render = function () { 1140 $(elm).wysiwyg('setContent', ngModel.$viewValue || ''); 1141 }; 1142 $(elm).width('100%'); 1143 }; 1144 1145 $timeout(fnInitRichEdit, 100); 1146 1147 } 1148 }; 1149 }]); 1150 1151 // dnnsfAngularLock is just a quick hack to skip angualr initialization in some situations 1152 !window.dnnsfAngularLock && angular.bootstrap(formRoot, ['ActionForm' + formRoot.attr('id')]); 1153 var $_scope = angular.element(formRoot).scope(); 1154 //formRoot[0].onFormSubmit = $_scope._registerToEvent['submit']; 1155 1156 // -------------------------------------------------------------------------------------------------------------------------------- 1157 // Start jquery plugin e 1158 $.fn.popover && formRoot.find('span.popupOnHover').popover({ trigger: 'hover' }); 1159 1160 function findErrorElement(sourceElement, errorElementClass) { 1161 var errorElement = sourceElement.find(errorElementClass); 1162 1163 if (!errorElement.length) 1164 errorElement = sourceElement.closest('.field-container').find(errorElementClass); 1165 1166 if (!errorElement.length) { 1167 errorElement = sourceElement.closest('.field-container').siblings(errorElementClass); 1168 } 1169 1170 return errorElement; 1171 } 1172 1173 // setup validation plugin 1174 var validationSettings = { 1175 errorElement: 'span', 1176 errorClass: 'text-danger', 1177 highlight: function (element, errorClass) { 1178 $(element).parents('.field-container:first').addClass('has-error'); 1179 }, 1180 unhighlight: function (element, errorClass) { 1181 if ($(element).hasClass('ignore')) { 1182 return; 1183 } 1184 $(element).removeAttr('aria-describedby'); 1185 1186 var fieldsGroup = element.attributes['class'] ? element.attributes['class'].value.match(/(group\d+-AtLeastOneIsFilled)/) : '', 1187 AtLeastOneIsFilled = fieldsGroup ? $(element).closest('.form-root').find('.form-group .' + fieldsGroup[0] + ':not(.required)') : ''; 1188 if (AtLeastOneIsFilled.length && !$(element).hasClass('required')) { 1189 $.each(AtLeastOneIsFilled, function (index, input) { 1190 if (input.type != 'checkbox' && input.type != 'radio') { 1191 $(input).parent().removeClass('has-error'); 1192 } else { 1193 $(input).closest('.form-group div.has-error').removeClass('has-error'); 1194 } 1195 }); 1196 $(element).next('.text-danger').hide(); 1197 } 1198 else { $(element).parents('.field-container:first').removeClass('has-error').find('.text-danger').hide(); } 1199 }, 1200 errorPlacement: function (error, element) { 1201 // accessibility 1202 var elementId = element.attr('id') || element.closest('.field-container').find('[data-ng-model]').attr('id')// file-upload; 1203 error.attr({ 'id': 'error-' + elementId, 'role': 'alert' }); 1204 element.attr('aria-describedby', 'error-' + elementId); 1205 if (element.hasClass('multiple-choice-checkbox')) { 1206 var parentElement = element.closest('.checkbox-list'); 1207 error.attr('id', 'error-' + parentElement.attr('id')); 1208 element.attr('aria-describedby', 'error-' + parentElement.attr('id')); 1209 } 1210 // end accessibility 1211 1212 var errPlace = findErrorElement(element, '.err-placeholder'); 1213 if (errPlace.length) {
1214 if (errPlace.find('span.text-danger').text() != error.text()) 1215 errPlace.append(error); 1216 } else { 1217 if (element.is(':checkbox') || element.is(':radio')) { 1218 element.parent().append(error); 1219 } else { 1220 element.next().is('.text-danger') ? element.next().replaceWith(error) : error.insertAfter(element.filter(function () { 1221 return !element.closest('.field-container').hasClass('ng-hide') 1222 })); 1223 } 1224 var tabParent = element.closest('.tab-pane'); 1225 tabParent.length && !tabParent.hasClass('active') && $('[href="#' + tabParent.attr('id') + '"]').addClass('has-error'); 1226 } 1227 }, 1228 success: function (element) { 1229 element.closest('.field-container').addClass("is-valid") 1230 }, 1231 onkeyup: function (element) { return true }, 1232 // numInput is for flatpickr 1233 ignore: '.ignore,:hidden,:disabled,.flatpickr-time .numInput', 1234 } 1235 1236 if (options.onFocusoutValidation) { 1237 _.assign(validationSettings, { 1238 onfocusout: function (element) { 1239 if ($(element).hasClass('ignore-focusout-validation')) 1240 return true; 1241 $(element).valid(); 1242 }, onkeyup: $.noop 1243 }); // for 'true' you need a function, 'false' works} 1244 } else { 1245 _.assign(validationSettings, { onkeyup: function (element) { $(element).valid(); } }); // for 'true' you need a function, 'false' works} 1246 } 1247 1248 formRoot.validate && formRoot.validate(validationSettings); 1249 1250 // validate required upload files 1251 $.validator && $.validator.addMethod("required-file", function (value, element) { 1252 return value !== "" || $(element).scope().queue.length || $(element).scope().loadedFiles.length; 1253 }); 1254 1255 // validate required upload files 1256 $.validator && $.validator.addMethod("required-fromclass", function (value, element) { 1257 return $(element).hasClass('afvalid'); 1258 }); 1259 1260 $.validator && $.validator.addMethod("required-cblist", function (value, element) { 1261 var group = $(element).attr('data-validation-group'); 1262 var valid = false; 1263 $('[data-validation-group="' + group + '"]').each(function () { 1264 if (this.checked) 1265 valid = true; 1266 }); 1267 return valid; 1268 }, localization.validation.required); 1269 1270 $.validator && $.validator.addMethod("required-ddwithcb", function (value, element) { 1271 var cboxes = $(element).closest('.field-container').find(':checkbox'); 1272 var valid = false; 1273 $.each(cboxes, function (i, v) { 1274 if ($(v).is(':checked')) 1275 valid = true; 1276 }); 1277 return valid; 1278 }, localization.validation.required); 1279 1280 $.validator && $.validator.addMethod("required-dnnsf", function (value, element) { 1281 1282 if (element.nodeName.toLowerCase() === "select") { 1283 // could be an array for select-multiple or a string, both are fine this way 1284 var val = $(element).val(); 1285 return val && val.length > 0; 1286 } 1287 if (this.checkable(element)) { 1288 return this.getLength(value, element) > 0; 1289 } 1290 1291 if ($_scope.form.RequiredFieldAllowsWhiteSpace) { 1292 return value.length > 0; 1293 } 1294 else { 1295 return $.trim(value).length > 0; 1296 } 1297 }, localization.validation.required); 1298 1299 // initialize password confirm 1300 formRoot.find('[data-password-confirm]').each(function () { 1301 $(this).rules("add", { 1302 equalTo: '#' + $(this).attr('data-password-confirm'), 1303 messages: { 1304 equalTo: localization['validation.passwordNoMatch'] 1305 } 1306 }); 1307 }); 1308 1309 formRoot.find('[data-textbox-confirm]').each(function () { 1310 var firstField = $(this).attr('af-field-title'); 1311 var secondField = $('#' + $(this).attr('data-textbox-confirm')).attr('af-field-title'); 1312 $(this).rules("add", { 1313 equalTo: '#' + $(this).attr('data-textbox-confirm'), 1314 messages: { 1315 equalTo: firstField + ' & ' + secondField + ' ' + localization['validation.fieldsDoNotMatch'] 1316 } 1317 }); 1318 }); 1319 1320 // fix width when printing 1321 if (formRoot.closest('#Table1').length) { 1322 formRoot.closest('#Table1').addClass('container') 1323 .parent().addClass(formRoot.attr('data-rootclass'));
1324 $('body').addClass('bstrap30 bstrap3-material'); 1325 } 1326 1327 //fix for when in pop-up 1328 if (formRoot.closest('.container').length == 0) { 1329 formRoot.closest('.phFormTemplate').addClass('container'); 1330 } 1331 1332 function parseVar(strVar) { 1333 if (!isNaN(parseInt(strVar))) 1334 return parseInt(strVar); 1335 1336 if (!isNaN(parseFloat(strVar))) 1337 return parseFloat(strVar); 1338 1339 if (strVar[0] == '[') { 1340 // looks like an array 1341 return eval(strVar.replace('\n', '')); 1342 } 1343 1344 if (strVar == "false") 1345 return false; 1346 1347 if (strVar == "true") 1348 return true; 1349 1350 // just return it as string 1351 return strVar; 1352 } 1353 1354 // load localized error messages 1355 for (var key in localization) { 1356 if (key.indexOf('validation.') == 0) { 1357 var relKey = key.substr('validation.'.length); 1358 if ($.validator) 1359 $.validator.messages[relKey] = localization[key].indexOf('{0}') == -1 ? localization[key] : $.validator.format(localization[key]); 1360 } else if (key.indexOf('jquery.datepicker.') == 0 && $.datepicker) { 1361 var relKey = key.substr('jquery.datepicker.'.length); 1362 var s = {}; 1363 s[relKey] = parseVar(localization[key]); 1364 $.datepicker.setDefaults(s); 1365 } 1366 } 1367 1368 //// load strings from the server 1369 //$.getJSON(formRoot.attr("") 1370 1371 // fix for datepicker, onchange does not trigger validation, so call onkeyup manually 1372 formRoot.find('.datepicker').change(function () { 1373 $(this).keyup(); 1374 }); 1375 1376 $('.modal').on('shown.bs.modal', function () { 1377 var x = 0; 1378 1379 var checkModals = setInterval(function () { 1380 $('.modal:visible').each(function () { 1381 var popup = $(this); 1382 popup.find('.modal-dialog:first').css('z-index', popup.find('.modal-backdrop:first').css('z-index') + 1); 1383 popup.after(popup.find('.modal-dialog:first').siblings('.modal-backdrop')); 1384 }); 1385 if (++x === 5) { 1386 window.clearInterval(checkModals); 1387 } 1388 }, 1000); 1389 }); 1390 1391 //// initialize input masks 1392 //formRoot.find('[data-mask]').each(function () { 1393 // $(this).mask($(this).attr('data-mask')); 1394 //}); 1395 1396 1397 // intialize date pickers 1398 formRoot.find(".datepicker").each(function () { 1399 var opts = { 1400 dateFormat: $(this).attr('data-dateformat'), 1401 changeMonth: $(this).attr('data-changemonth') == 'true', 1402 changeYear: $(this).attr('data-changeyear') == 'true', 1403 //Fix of IE 1404 fixFocusIE: false, 1405 onSelect: function (dateText, inst) { 1406 this.fixFocusIE = true; 1407 }, 1408 onClose: function (dateText, inst) { 1409 this.fixFocusIE = true; 1410 inst.input.trigger('change'); 1411 }, 1412 beforeShow: function (input, inst) { 1413 var result = true; 1414 this.fixFocusIE = false; 1415 return result; 1416 } 1417 }; 1418 1419 if ($(this).attr('data-yearrange')) 1420 opts["yearRange"] = $(this).attr('data-yearrange'); 1421 1422 1423 // merge other options 1424 if ($(this).attr('data-opts')) { 1425 opts = $.extend(opts, eval('(' + $(this).attr('data-opts') + ')')); 1426 } 1427 1428 $(this).datepicker(opts); 1429 var theme = $(this).attr('data-theme'); 1430 $('#ui-datepicker-div').each(function () { 1431 if ($(this).parent("." + theme).size() == 0) 1432 $(this).wrap('<div class="' + theme + '"></div>'); 1433 }); 1434 }); 1435 1436 // init file upload 1437 formRoot.find('.file-upload').each(function () { 1438 this["aform"] = formRoot; 1439 }); 1440 1441 if (window.aform_incFileUplad) { 1442 if (!$().fileupload) 1443 return; 1444 1445 formRoot.find('.file-upload').each(function () { 1446 1447 if (!this.aform) 1448 return; // not one of our fields 1449 1450 var $root = $(this).parents('.fileupload-root:first'); 1451 1452 // redefined formRoot in this context 1453 var formRoot = this.aform; 1454 var _this = $(this); 1455 $root.find('.files').empty().append($('<p/>').text(angular.element(formRoot).scope().form.fields[_this.attr('data-af-field')].value)); 1456 1457 // hack for DNN 7 to leave our upload field alone 1458 var btn = $root.find('.fileinput-button'); 1459 if (btn.find('.dnnInputFileWrapper').size() > 0) { 1460 btn.find('input').appendTo(btn); 1461 btn.find('.dnnInputFileWrapper').remove(); 1462 } else { 1463 if (btn.find('input')[0]) 1464 btn.find('input')[0].wrapper = 'hack'; 1465 } 1466 1467 }) 1468 1469 // once is enough 1470 window.aform_incFileUplad = false;
1471 } 1472 1473 //This is a fix for Xcilion skin for stoping changing the portal on EnterKey 1474 $(document).on('keydown', 'input:text.preventdefault', function (evt) { 1475 if (evt.keyCode == 13) { 1476 evt.preventDefault(); 1477 evt.stopImmediatePropagation(); 1478 } 1479 }); 1480 1481 $('body').on('keydown', 'input:text:not(.preventdefault),input:password:not(.preventdefault)', function (evt) { 1482 var btn = $(evt.currentTarget).closest('.form-root').find('.submit[data-default-button=on]:first'); 1483 if (evt.keyCode == 13 && btn.length) { 1484 btn.click(); 1485 evt.preventDefault(); 1486 } 1487 }); 1488 1489 // reset buttons, sometimes firefox leaves them disabled after refresh 1490 try { formRoot.find('.button').button('reset'); } catch (e) { } 1491 1492 formRoot.on('click', ".form-button", function () { 1493 submitForm(this); 1494 }); 1495 1496 function submitForm(el, fnDone, qs) { 1497 var fieldsToIgnoreSelectors = ':disabled, .ignore';// ignore dropdown with checkboxes 1498 $_scope.fieldsToIgnoreClass = ''; 1499 1500 if ($_scope.settings.SubmitHiddenFields.Value) { 1501 fieldsToIgnoreSelectors += ',.ignore-submit-hidden-fields, .richedit' 1502 } else { 1503 fieldsToIgnoreSelectors += ',:hidden'; 1504 $_scope.fieldsToIgnoreClass = '.ng-hide'; 1505 }; 1506 1507 var connectedForms = {}; 1508 var btnSettings = $_scope.settings.Fields[$(el).attr('data-name')]; 1509 if (!btnSettings) { //submit from tabsPro event 1510 btnSettings = { 1511 'isConnected': false 1512 } 1513 } 1514 else { 1515 var tokenizedConnectedForms = $_scope.form.fields[btnSettings.TitleCompacted].connectedForms; 1516 btnSettings.isConnected = tokenizedConnectedForms && tokenizedConnectedForms.length > 0; 1517 btnSettings.isConnected && (connectedForms = tokenizedConnectedForms); 1518 } 1519 var _this = el; 1520 // reset 1521 formRoot.find(".server-error").html("").hide(); 1522 var causesValidation = $(_this).attr('data-validation') == 'on'; 1523 var fieldsToValidate = formRoot.find('input,textarea,select,.checkbox-list').not(fieldsToIgnoreSelectors); 1524 $.each(connectedForms, function (i, v) { 1525 var formControls = {}; 1526 formControls.fields = angular.element('#dnn' + v.FormId + 'root').scope().controls; 1527 if (formControls.fields.length) 1528 $_scope.controls = _.uniqBy($_scope.controls.concat(formControls || []), 'field') 1529 // formControls.fields.length && $_scope.controls.push(formControls); 1530 var formEl = $('#dnn' + v.FormId + 'root'); 1531 var fields = formEl.find('.field-container').not($_scope.fieldsToIgnoreClass).find('input,textarea,select,.checkbox-list').not(fieldsToIgnoreSelectors); 1532 causesValidation && formEl.is(':visible') && fields.valid(); 1533 }); 1534 if (causesValidation) { 1535 fieldsToValidate.each(function (index, input) { 1536 if (!$(input).attr('keyup-listener')) { // we don't need duplicate listeners 1537 $(input).attr('keyup-listener', 'true') 1538 $(input).on("keyup change", function (event) { // add an event listener because the plugin doesn't work as it should 1539 $(event.target).valid(); 1540 }); 1541 } 1542 }); 1543 } 1544 if (causesValidation && fieldsToValidate.size() && !fieldsToValidate.valid()) { 1545 refreshCaptchaOnError($_scope); 1546 1547 formRoot.find('.has-error:first').find('input,textarea,select').focus(); 1548 $('.has-error').first()[0].scrollIntoView({ behavior: "smooth", block: "center" }); 1549 fnDone && fnDone({ value: false, refresh: angular.element(formRoot).scope().settings.TabsPro_RefreshTabStateOnLeave.Value }); 1550 return false;
1551 } 1552 1553 if (_this.preventSubmit) 1554 return; 1555 1556 // check if we need to start uploads 1557 //formRoot[0].toUpload = 0; 1558 formRoot[0].$btn = $(_this); 1559 if (qs && !$.isEmptyObject(qs)) { 1560 formRoot[0].qs = qs; 1561 } 1562 formRoot.bind('fileuploadsubmit', function (e, data) { 1563 // Sending the all form fields in fileUpload request 1564 var currentData = getFormData(formRoot); 1565 data.formData = currentData; 1566 }); 1567 1568 var $btn = $(_this); 1569 $($btn).data('connectedForms', connectedForms); 1570 try { $btn.hasClass('af-btn-loading') && $btn.button('loading'); } catch (e) { } 1571 1572 $btn.data().tabEvent && $btn.data('tabEvent', false); 1573 1574 var abortSubmit = false; 1575 var waitFor = 0; 1576 1577 $.each($_scope.controls, function (i, control) { 1578 if (control.fields && control.fields.length) { 1579 $.each(control.fields, function (i, v) { 1580 if (v.onSubmit) 1581 waitFor++; 1582 }) 1583 } 1584 else { 1585 if (control.onSubmit) 1586 waitFor++; 1587 } 1588 }); 1589 1590 if (formRoot[0].onFormSubmit.length) 1591 for (var i in formRoot[0].onFormSubmit) 1592 formRoot[0].onFormSubmit[i]($btn); 1593 if (!waitFor) { 1594 formRoot[0].submitData($btn, fnDone, btnSettings); 1595 } 1596 else if (waitFor && (!formRoot.find(".table-striped.files tr.file-table").length && !formRoot.find("[submit-data]").length && !btnSettings.isConnected)) { 1597 //sending with no file uploaded 1598 return formRoot[0].submitData($btn, fnDone, btnSettings); 1599 } else { 1600 var submitControl = function (control) { 1601 if (!control.onSubmit) 1602 return 1603 control.onSubmit(function () { 1604 if (abortSubmit) 1605 return; 1606 1607 waitFor--; 1608 if (waitFor == 0) 1609 formRoot[0].submitData($btn, fnDone, btnSettings); 1610 }, function (error) { 1611 if (abortSubmit) 1612 return; 1613 formRoot.find(".server-error").html(error).show(); 1614 afResetButton(formRoot, $btn); 1615 abortSubmit = true; 1616 }); 1617 } 1618 1619 $.each($_scope.controls, function (i, control) { 1620 if (control.fields && control.fields.length) { 1621 $.each(control.fields, function (i, v) { 1622 submitControl(v); 1623 }) 1624 } 1625 else 1626 submitControl(control); 1627 }); 1628 } 1629 } 1630 1631 formRoot[0].submitData = function ($btn, fnDone, btnSettings) { 1632 isConnected = btnSettings.isConnected; 1633 if (formRoot[0].qs && !$.isEmptyObject(formRoot[0].qs)) { 1634 var submitUrl = dnnsf.getUrlParts($btn.attr('data-submiturl')) 1635 submitUrl.query = $.extend(submitUrl.query, formRoot[0].qs); 1636 $btn.attr('data-submiturl', submitUrl.getUrl(submitUrl)); 1637 } 1638 var list = [], 1639 deleteUrl = ""; 1640 1641 if (formRoot[0].submitting) 1642 return; 1643 1644 //getting data for submit 1645 var data = getFormData(formRoot); 1646 if (isConnected) { 1647 data = { '$_thisForm': data }; 1648 $.each($($btn).data('connectedForms'), function (i, v) { 1649 var fields = {} 1650 fields[v.FormName] = getFormData($('#dnn' + v.FormId + 'root')); 1651 $.extend(data, fields) 1652 }); 1653 } 1654 1655 //delete files 1656 $.each($_scope.controls, function (i, control) { 1657 if (!control.deleteFiles) 1658 return; 1659 list = control.deleteFiles(); 1660 deleteUrl = control.deleteUrl(); 1661 1662 if (list) { 1663 $.each(list, function (i, filename) { 1664 $.ajax({ 1665 headers: { 1666 "DNNSF-Time-Offset": timeZoneOffset, 1667 "RequestVerificationToken": svcFramework.getAntiForgeryValue() 1668 }, 1669 url: deleteUrl + '&f=' + filename, 1670 type: "post", 1671 dataType: "json" 1672 }).done(function (data) { 1673 formRoot[0].submitting = false;
1674 parseFormResponse2(formRoot, $btn, data); 1675 }); 1676 }); 1677 } 1678 }); 1679 1680 var submitFormData = function (event, onDone) { 1681 formRoot[0].submitting = true; 1682 var setDisableState = function () { 1683 var btns = formRoot.find('.submit').not($btn); 1684 $.each(btns, function (i, formButton) { 1685 $(formButton).attr('disabled') && $(formButton).data('disabled', true); 1686 !$btn.data().tabEvent && $(formButton).attr('disabled', 'disabled'); 1687 }) 1688 } 1689 formRoot.find('.submit-progress').css('visibility', 'visible').stop(true, true).fadeIn(); 1690 var xhr = new XMLHttpRequest(); 1691 var executed = []; 1692 var executedOnDone = false; 1693 var qs = $.param({ 1694 "referrer": document.referrer, 1695 "_url": document.URL 1696 }); 1697 if (isConnected) { 1698 xhr.open("POST", options.apiUrl + "/MultiForm/Submit?" + $btn.attr("data-submitquery") + '&' + qs, true); 1699 xhr.setRequestHeader("Content-type", "application/json"); 1700 } else { 1701 xhr.open("POST", $btn.attr("data-submiturl") + '&' + qs, true); 1702 xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 1703 } 1704 xhr.setRequestHeader("DNNSF-Time-Offset", timeZoneOffset); 1705 xhr.setRequestHeader("RequestVerificationToken", svcFramework.getAntiForgeryValue()); 1706 1707 xhr.onprogress = function () { 1708 var response = xhr.response.split("\n"); 1709 if (response[response.length - 1] == "") 1710 response.pop(); 1711 1712 $.each(response, function (i, v) { 1713 if (executed.indexOf(i) != -1) 1714 return; 1715 1716 try { 1717 var responseObj = JSON.parse(v); 1718 } catch (e) { 1719 // json is incomplete. The next flush will get the rest of it. 1720 return; 1721 } 1722 1723 if (isConnected && responseObj.validationErrors) { 1724 responseObj.connectedForms = $($btn).data('connectedForms'); 1725 } 1726 1727 parseFormResponse2(formRoot, $btn, responseObj); 1728 if (onDone && responseObj.validationErrors) { 1729 onDone(responseObj); 1730 executedOnDone = true; 1731 } 1732 executed.push(i); 1733 }); 1734 }; 1735 xhr.onreadystatechange = function () { 1736 1737 formRoot[0].submitting = false; 1738 var response = xhr.response.split("\n"); 1739 1740 if (response[response.length - 1] == "") { 1741 response.pop(); //on firefox sometimes the response is an empty string 1742 } 1743 if (xhr.readyState == 4 && response.length) { 1744 try { 1745 var resJson = JSON.parse(response[0]); 1746 } 1747 catch (e) { 1748 parseFormResponse2(formRoot, $btn, { 'error': localization.actionErrorDefaultMessage, 'reset': true }); 1749 console.log(response); 1750 return; 1751 } 1752 if (onDone) { 1753 response = _.filter(response, function (value, key) { 1754 return executed.indexOf(key) == -1; 1755 }); 1756 !executedOnDone && onDone(response.length && JSON.parse(response[0])); 1757 } 1758 if (isConnected) 1759 $_scope.controls = []; 1760 1761 } 1762 }; 1763 1764 //xhr.addEventListener('loadend', function (event) { 1765 // afResetButton(formRoot, $btn); 1766 //}); 1767 1768 var ignoreEvent = false;
1769 if (event == 'TabsPro_OnTabLeave') { 1770 var ignoreTabLeave = $_scope.settings.TabsPro_IgnoreTabLeaveIfNoChanges && $_scope.settings.TabsPro_IgnoreTabLeaveIfNoChanges.Value; 1771 var formSettings = dnnsf.api.actionForm.getSettings($_scope.settings.ModuleId); 1772 var hasChanges = formSettings.submittedData && !_.isEqual(formSettings.submittedData, data); 1773 if (ignoreTabLeave && hasChanges == false) { 1774 onDone({}); 1775 formRoot[0].submitting = false; 1776 ignoreEvent = true; 1777 } 1778 dnnsf.api.actionForm.patchSettings($_scope.settings.ModuleId, { submittedData: data }); 1779 } 1780 if (ignoreEvent) 1781 return; 1782 setDisableState(); 1783 if (isConnected) 1784 xhr.send(JSON.stringify(data)); 1785 else 1786 xhr.send($.param(data)); 1787 } 1788 1789 var event = dnnsf.getUrlParts($btn.attr("data-submiturl")).query['event']; 1790 switch (event) { 1791 //case 'click': 1792 //break; 1793 //case 'TabsPro_OnTabEnter': 1794 //break; 1795 case 'TabsPro_OnTabLeave': 1796 if (!options.hasTabLeaveActions) 1797 return fnDone({ value: true, refresh: angular.element(formRoot).scope().settings.TabsPro_RefreshTabStateOnLeave.Value }); 1798 submitFormData(event, function (data) { 1799 if (data && data.validationErrors != undefined) { 1800 fnDone({ value: false, refresh: angular.element(formRoot).scope().settings.TabsPro_RefreshTabStateOnLeave.Value }); 1801 } else { 1802 if (!angular.element(formRoot).scope()) { 1803 var waitForInitialization = setInterval(function () { 1804 if (angular.element(formRoot.selector).scope()) { 1805 fnDone({ value: true, refresh: angular.element(formRoot.selector).scope().settings.TabsPro_RefreshTabStateOnLeave.Value }); 1806 clearInterval(waitForInitialization); 1807 } 1808 }, 5) 1809 } else 1810 fnDone({ value: true, refresh: angular.element(formRoot).scope().settings.TabsPro_RefreshTabStateOnLeave.Value }); 1811 1812 } 1813 }) 1814 break; 1815 default: 1816 submitFormData(event); 1817 } 1818 1819 }; 1820 1821 if (options.hasTabEnterActions || options.hasTabLeaveActions) { 1822 dnnsf.events.listen('OnTabsLeave', options.moduleId, function (data, fnDone) { 1823 1824 var isTarget = false;
1825 $.each(data.targetModuleList, function (index, moduleId) { 1826 if (options.moduleId == moduleId) 1827 isTarget = true; 1828 }) 1829 if (isTarget && options.hasTabLeaveActions) { 1830 var submitUrl = dnnsf.getUrlParts($_scope.form.submitUrl) 1831 submitUrl.query = $.extend(submitUrl.query, { 'event': 'TabsPro_OnTabLeave' }); 1832 var btn = document.createElement("button"); 1833 $(btn).attr('class', 'form-button'); 1834 $(btn).attr('data-submiturl', submitUrl.getUrl(submitUrl)); 1835 if ($_scope.settings.TabsPro_IgnoreValidationOnLeave.Value) { 1836 $(btn).attr('data-validation', 'off'); 1837 } else { 1838 $(btn).attr('data-validation', 'on'); 1839 } 1840 $(btn).data('tabEvent', true); 1841 submitForm(btn, fnDone); 1842 } else { 1843 fnDone(); 1844 } 1845 }); 1846 } 1847 1848 if (options.hasTabEnterActions) { 1849 dnnsf.events.listen('OnTabsEnter', options.moduleId, function (data, fnDone) { 1850 if (!options.hasTabEnterActions) { 1851 return fnDone && fnDone(); 1852 } 1853 var newQs = { 'event': 'TabsPro_OnTabEnter' }; 1854 if (data.qs && !$.isEmptyObject(data.qs)) { 1855 $.extend(newQs, data.qs); 1856 dnnsf.api.actionForm.patchSettings(options.moduleId, { passQs: data.qs }); 1857 } 1858 var isTarget = false;
1859 $.each(data.targetModuleList, function (index, moduleId) { 1860 if (options.moduleId == moduleId) 1861 isTarget = true; 1862 }) 1863 if (isTarget) { 1864 var submitUrl = dnnsf.getUrlParts($_scope.form.submitUrl) 1865 submitUrl.query = $.extend(submitUrl.query, newQs); 1866 var btn = document.createElement("button"); 1867 $(btn).attr('class', 'form-button'); 1868 $(btn).attr('data-submiturl', submitUrl.getUrl(submitUrl)); 1869 $(btn).attr('data-validation', 'off'); 1870 $(btn).data('tabEvent', true); 1871 submitForm(btn, fnDone, data.qs); 1872 } else { 1873 fnDone(); 1874 } 1875 }); 1876 } 1877 1878 dnnsf.events.listen('ActionFormPing', options.moduleId, function (data, fnDone) { 1879 $.each(data.targetModuleList, function (index, moduleId) { 1880 if (options.moduleId == moduleId) { 1881 fnDone(options.moduleId); 1882 } else { 1883 fnDone(); 1884 } 1885 }) 1886 }); 1887 }; 1888 } 1889 1890 function getFormData(formRoot) { 1891 var $_scope = angular.element(formRoot).scope(); 1892 var data = {}; 1893 formRoot.find(':input,[data-val],.value-node').not("button[class^='trumbowyg-'], textarea.g-recaptcha-response").each(function () { 1894 //formRoot.find('input').filter(':text,:password,:hidden').add(formRoot.find("select,textarea")).each(function () { 1895 if (!$(this).attr("id") || $(this).closest($_scope.fieldsToIgnoreClass).length) 1896 return; 1897 // initialize to empty 1898 var name = $(this).attr("field-id") || $(this).attr("id").replace(/dnn\d+/, ""); 1899 if (!data[name]) 1900 data[name] = ''; 1901 1902 // for radios, only set if selected 1903 if ($(this).attr('type') == 'radio') { 1904 if (this.checked) 1905 data[name] = $(this).val(); 1906 } else if ($(this).attr('data-val') || $(this).attr('data-val') === '') { 1907 data[name] = $(this).attr('data-val'); 1908 } 1909 else if ($(this).hasClass('value-node')) { 1910 data[name] = $(this).html(); 1911 } else { 1912 data[name] = $(this).val(); 1913 } 1914 1915 }); 1916 1917 var checkboxes = _.map(formRoot.find('.field-container').not($_scope.fieldsToIgnoreClass).find('[type="checkbox"][id]:not([id=""])'), 1918 function (checkbox) { 1919 return [$(checkbox).attr('id').replace(/dnn\d+/, ''), $(checkbox)] 1920 }); 1921 1922 var part_checkboxes = _.partition(checkboxes, 1923 function (checkbox) { 1924 return checkbox[0].indexOf('-') == -1 1925 }); 1926 1927 _.each(part_checkboxes[0], 1928 function (checkbox) { 1929 data[checkbox[0]] = checkbox[1].is(':checked') ? 'True' : 'False' 1930 }); 1931 1932 var checkbox_lists = _.groupBy(part_checkboxes[1], 1933 function (checkbox) { 1934 return checkbox[0].substr(0, checkbox[0].indexOf('-')) 1935 }); 1936 1937 _.each(checkbox_lists, 1938 function (list, name) { 1939 data[name] = JSON.stringify( 1940 _.map( 1941 _.filter(list, 1942 function (checkbox) { 1943 return checkbox[1].is(':checked') 1944 }), 1945 function (checkbox) { 1946 return checkbox[1].val() 1947 })) 1948 }); 1949 1950 formRoot.find(".itemwithqty input:visible").each(function () { 1951 data[$(this).attr("id").replace(/dnn\d+/, "")] = $('#' + $(this).attr("id") + 'Qty').val() + ' ' + $(this).val(); 1952 }); 1953 1954 formRoot.find('iframe.dnnsf-text-editor').each(function () { 1955 if (dnnsf.canAccessIFrame(this) && this.contentWindow.getContent) { 1956 data[$(this).attr("name").replace(/dnn\d+/, "")] = this.contentWindow.getContent(); 1957 } 1958 }); 1959 1960 // finally, call controls 1961 $.each($_scope.controls, function (i, control) { 1962 if (!control.getValue) 1963 return; 1964 data[control.field.TitleCompacted] = control.getValue(); 1965 }); 1966 1967 _.forEach($_scope.form.fields, function (afField) { 1968 if (afField.type === "recaptcha") { 1969 data[afField.name] = afField.value; 1970 return false;
1971 } 1972 }); 1973 1974 return data; 1975 } 1976 1977 function resetRecaptchaValidation(widgetId, formRoot) { 1978 1979 var recaptchaElement = $("div[vc-recaptcha]", formRoot); 1980 1981 if (!recaptchaElement.length) { 1982 return; 1983 } 1984 1985 if (typeof grecaptcha === 'undefined') { 1986 console.error('grecaptcha is undefined'); 1987 return; 1988 } 1989 1990 grecaptcha.reset(widgetId); 1991 var recaptchaParent = recaptchaElement.closest('.field-container'); 1992 var recaptchaError = recaptchaParent.find('.text-danger'); 1993 1994 setTimeout(function () { 1995 recaptchaParent.removeAttr('aria-describedby'); 1996 recaptchaError.remove(); 1997 }, 3000); 1998 } 1999 2000 function afResetButton(formRoot, $btn) { 2001 if (!$btn) { 2002 return; 2003 } 2004 setTimeout(function () { 2005 try { $btn.button('reset'); } catch (e) { } 2006 var btns = formRoot.find('.submit').not($btn); 2007 $.each(btns, function (i, formButton) { 2008 if ($(formButton).data('disabled')) { 2009 $(formButton).data('disabled', false) 2010 } else { 2011 $(formButton).removeAttr('disabled'); 2012 } 2013 }) 2014 formRoot && formRoot.find('.submit-progress').stop(true, true).fadeOut(function () { 2015 $(this).css('visibility', 'hidden'); 2016 }); 2017 }, 500); 2018 } 2019 2020 function refreshCaptchaOnError(scope) { 2021 formRoot.find('.imgcode').each(function (index, captchaImg) { 2022 scope.refreshCaptcha(scope.settings.ModuleId, $(captchaImg).attr('data-af-field')); 2023 }); 2024 } 2025 2026 function parseFormResponse2(formRoot, $btn, data) { 2027 2028 var $ = dnnsfjQuery; 2029 parseFormResponse(data, { 2030 executeJsFunction: function (fnName) { 2031 window.parent[fnName](window.frameElement); 2032 }, 2033 executeJsCode: function (jsCode) { 2034 if (!form) 2035 var form = formRoot.scope() && formRoot.scope().form; 2036 2037 var toEval = new Function('form', jsCode); 2038 2039 // try/catch is already implemented in the code being executed that we have in Common in the actions 2040 toEval.call(this, form); 2041 }, 2042 error: function (err, reset) { 2043 formRoot.find(".server-error").append($.parseHTML(err)).show(); 2044 $('.g-recaptchadnnsf', formRoot).each(function (index, recaptchaElement) { 2045 grecaptcha.reset($(this).attr('data-widgetid')); 2046 if ($(recaptchaElement).attr('data-size') === 'invisible') { 2047 grecaptcha.execute($(this).attr('data-widgetid')); 2048 } 2049 }); 2050 }, 2051 validationErrors: function (_data) { 2052 var displayErrors = function (errors, mid) { 2053 refreshCaptchaOnError(formRoot.scope()); 2054 $.each(errors, function (i, err) { 2055 var fieldId = mid + err.fieldId; 2056 var field = $('#dnn' + fieldId); 2057 var parent = field.closest('.field-container'); 2058 2059 if (!parent.length) 2060 parent = $('[name="dnn' + fieldId + '"]').closest('.field-container'); 2061 2062 // tabspro specific 2063 var tabParent = parent.closest('.tab-pane'); 2064 tabParent.length && !tabParent.hasClass('active') && $('[href="#' + tabParent.attr('id') + '"]').addClass('has-error'); 2065 // end tabspro specific 2066 2067 field.attr('aria-describedby', 'error-' + field.attr('id')); 2068 2069 parent.addClass('has-error'); 2070 $('#dnn' + fieldId) 2071 parent.find('.text-danger').length ? 2072 parent.find('.text-danger').html(err.message).show() : 2073 parent.append('<span id="' + 'error-' + field.attr('id') + '" class="text-danger">' + err.message + '</span>'); 2074 2075 var errorElements = $('.has-error'); 2076 if (errorElements.length) { 2077 // focus and scroll to the element 2078 errorElements.first()[0].scrollIntoView({ behavior: "smooth", block: "center" }); 2079 } else { 2080 console.error("Action Form: Validation error on a hidden field.") 2081 } 2082 }); 2083 2084 } 2085 var mid = $(formRoot).parent().attr('data-moduleid'); 2086 if (_data.validationErrors.constructor === Array) { 2087 //is current form 2088 displayErrors(_data.validationErrors, mid); 2089 } else { 2090 //conected forms 2091 $.each(_data.validationErrors, function (i, v) { 2092 if (v.length) { 2093 var form; 2094 2095 if (i != '$_thisForm') { 2096 form = _data.connectedForms.find(function (j, k) { 2097 return j.FormName.toLowerCase() == i.toLowerCase(); 2098 }) 2099 } 2100 displayErrors(v, form ? form.FormId : mid); 2101 } 2102 }); 2103 } 2104 2105 resetRecaptchaValidation(formRoot.scope().widgetId, formRoot); 2106 2107 return; 2108 }, 2109 redirect: function (url, isPushState) { 2110 if (data.forceDownload) { 2111 var urlPath = dnnsf.getUrlParts(url).pathname; 2112 var fileNameAndExtension = urlPath.substring(urlPath.lastIndexOf("/") + 1); 2113 if (!window.ActiveXObject) { 2114 var save = document.createElement('a'); 2115 save.href = url; 2116 save.target = '_blank'; 2117 save.download = fileNameAndExtension || 'unknown'; 2118 2119 var evt = new MouseEvent('click', { 2120 'view': window, 2121 'bubbles': true, 2122 'cancelable': false 2123 }); 2124 save.dispatchEvent(evt); 2125 2126 (window.URL || window.webkitURL).revokeObjectURL(save.href); 2127 afResetButton(formRoot, $btn); 2128 2129 } 2130 // for IE < 11 2131 else if (!!window.ActiveXObject && document.execCommand) { 2132 var _window = window.open(url, '_blank'); 2133 _window.document.close(); 2134 _window.document.execCommand('SaveAs', true, fileNameAndExtension || url) 2135 _window.close(); 2136 afResetButton(formRoot, $btn); 2137 } 2138 return; 2139 } 2140 2141 if (!data.popup && !data.newTab) { 2142 2143 if (isPushState) { 2144 //$('.angrid').scope().$location.url(url); 2145 setTimeout(function () { 2146 window.history.pushState({}, '', url + location.hash);//added location.hash for tabsPro 2147 afResetButton(formRoot, $btn); 2148 }, 500) 2149 } else { 2150 window.location = url; 2151 if (url.indexOf("mailto:") != -1 || url.indexOf("tel:") != -1 || url.indexOf("ftp:") != -1) { 2152 afResetButton(formRoot, $btn); 2153 } 2154 } 2155 return; 2156 } 2157 2158 // handle new tab 2159 if (data.newTab) { 2160 if (!!window.ApplePaySession) { // it's Safari 10+ 2161 window.location = url; 2162 } else { 2163 window.open(url, '_blank'); 2164 afResetButton(formRoot, $btn); 2165 } 2166 return; 2167 } 2168 2169 // open popup 2170 //set modal and append to body 2171 var popup = $('<div class="af-modal modal fade">' + 2172 '<div class="modal-dialog modal-lg">' + 2173 '<div class="modal-content">' + 2174 '<div class="modal-header">' + 2175 '<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>' + 2176 '<h4 class="modal-title">' + data.popupTitle + '</h4>' + 2177 '</div>' + 2178 '<div class="modal-body">' + 2179 '<iframe width="100%" src="' + url + '" frameborder="0" scrolling="yes"></iframe>' + 2180 '</div>' + 2181 '</div>' + 2182 '</div>' + 2183 '</div>').appendTo('body'); 2184 2185 //console.log(popup.modal); // removed unnecesary logs 2186 popup.modal({ 2187 backdrop: true 2188 }); 2189 2190 popup.on('hidden.bs.modal', function () { 2191 //stop resizing 2192 window.clearInterval(resizeInterval); 2193 //remove from DOM 2194 popup.remove(); 2195 //remove backdrop 2196 $('.modal-backdrop').remove(); 2197 2198 }); 2199 2200 afResetButton(formRoot, $btn); 2201 2202 //resize iframe so it has no scroll bar 2203 var __prevHeight = 0; 2204 var resizeInterval = setInterval(function () { 2205 var iframe = $('.af-modal:visible').find('iframe'); 2206 try { 2207 var bodyHeight = iframe[0].contentWindow.document.body.scrollHeight;// the corect way to get iframe height 2208 //if (bodyHeight < __prevHeight) { 2209 // bodyHeight = bodyHeight - 50;// Decreseaing in height is slow because something somewhere is intercating with them. Here is a little boost ... 2210 //} 2211 if (bodyHeight != __prevHeight) { 2212 __prevHeight = bodyHeight; 2213 iframe.height(Math.max(200, bodyHeight));// minimum 200px 2214 } 2215 } catch (e) { 2216 iframe.height(window.innerHeight - 240);// better save the sorry 2217 } 2218 }, 1000); 2219 }, 2220 2221 message: function (msg, type) { 2222 if (!type || type == 'success') { 2223 formRoot.find(".c-form").slideUp(); 2224 formRoot.find(".submit-confirm2").hide(); 2225 $('html, body').animate({ 2226 scrollTop: formRoot.offset().top - 200 2227 }, 500); 2228 } 2229 formRoot.find(".submit-confirm").html(msg).show(); 2230 var scope = formRoot.find(".submit-confirm").scope(); 2231 2232 angular.element(formRoot).injector().invoke(function ($compile) {
2233 $compile(formRoot.find(".submit-confirm").contents())(scope); 2234 }); 2235 2236 }, 2237 appendHtml: function (appendHtml, appendTo, reset) { 2238 $(appendTo).append(appendHtml); 2239 reset && afResetButton(formRoot, $btn); 2240 }, 2241 data: function (data) { 2242 var moduleId = data.baseId.replace('dnn', ''); 2243 dnnsf.api.actionForm.patchSettings(moduleId, { Data: data }); 2244 angular.element(formRoot).scope().load(moduleId); 2245 angular.element(formRoot).scope().$apply(); 2246 angular.element(formRoot).scope().$broadcast('updateFormData'); 2247 }, 2248 noOp: function () { 2249 } 2250 }, $btn, formRoot); 2251 } 2252 2253 function parseFormResponse(data, handlers, afButton, afFormRoot) { 2254 2255 var $ = dnnsfjQuery; 2256 2257 // initialize with default handlers, unless provieded by caller 2258 handlers = $.extend({ 2259 keepOnPage: function (url) { 2260 window.location.reload(true); 2261 }, 2262 redirect: function (url) { 2263 window.location = url; 2264 }, 2265 appendHtml: function (appendHtml, appendTo) { 2266 $(appendTo).append(appendHtml); 2267 }, 2268 error: function (err) { }, 2269 message: function (msg, type) { }, 2270 data: function (msg, type) { }, 2271 executeJsCode: function (jsCode) { 2272 try { 2273 eval(jsCode) 2274 } catch (e) { 2275 console.error(e); 2276 } 2277 } 2278 }, handlers); 2279 2280 // parse response and call handlers 2281 if (data.functionName) { 2282 handlers.executeJsFunction && handlers.executeJsFunction(data.functionName); 2283 } if (data.JsCode) { 2284 handlers.executeJsCode && handlers.executeJsCode(data.JsCode); 2285 } else if (data.Error || data.error) { 2286 handlers.error && handlers.error(data.Error || data.error, data.reset); 2287 } else if (data.validationErrors) { 2288 handlers.validationErrors && handlers.validationErrors(data); 2289 } else if (data.Content) { 2290 handlers.message && handlers.message(data.Content, data.Type); 2291 } else if (data.KeepOnPage) { 2292 handlers.KeepOnPage && handlers.KeepOnPage(data.Url); 2293 } else if (data.Url) { 2294 handlers.redirect && handlers.redirect(data.Url, data.PushState); 2295 return; // do not reset form button by default 2296 } else if (data.appendHtml) { 2297 handlers.appendHtml && handlers.appendHtml(data.appendHtml, data.appendTo, data.reset); 2298 return; 2299 } else if (data.data) { 2300 handlers.data && handlers.data(data.data); 2301 } else if (data.noOp) { 2302 handlers.noOp && handlers.noOp(); 2303 } 2304 2305 afResetButton(afFormRoot || formRoot, afButton); 2306 } 2307 2308 return { 2309 initForm: initForm, 2310 getFormData: getFormData, 2311 afResetButton: afResetButton, 2312 parseFormResponse: parseFormResponse 2313 } 2314})(dnnsfjQuery, window.dnnsfAngular15); 2315 2316var initForm = afApp.initForm, 2317 getFormData = afApp.getFormData, 2318 afResetButton = afApp.afResetButton, 2319 parseFormResponse = afApp.parseFormResponse; 2320 2321function browseGrid(settings) { 2322 $('body').append('<div class="loader-wrapper" id="modalLoader"><div class="loader"></div></div>'); 2323 $.get(window.dnnsf.commonUrl + '/static/dnnsf/tpl/gridModal.html', function (data) { 2324 var iframeData = data.replace('gridUrl', settings.url); 2325 iframeData = iframeData.replace('popupHeight', $(window).height() - 150 + 'px'); 2326 $('body').append(iframeData); 2327 $('#gridFrame').load(function () { 2328 $('#gridFrame').contents().find('body').css({ 'overflow': 'auto' }); 2329 $('#gridFrame').contents().find('body table').css({ 'width': '90%', 'margin': '0 auto' }); 2330 setTimeout(function () { 2331 dnnsfjQuery('#gridModal').modal('show'); 2332 $('#modalLoader').remove(); 2333 }, 500); 2334 dnnsfjQuery('#gridModal').on('shown.bs.modal', function (e) { 2335 getData(); 2336 }); 2337 }); 2338 dnnsfjQuery('#gridModal').on('hidden.bs.modal', function (e) { 2339 $('#gridModal').remove(); 2340 }); 2341 }); 2342 function getData() { 2343 var iframe = window.frames['gridFrame'].document; 2344 $('body', iframe).on('click', '.grid-item', function () { 2345 if (!$(this).has('span.grid-field-value').size()) 2346 return; 2347 var gridScope = window.frames['gridFrame'].angular.element($(this).closest('.item-value')).scope(); 2348 $.each(settings.mappings, function (e, f) { 2349 var elem = $('* [data-ng-model^="form.fields.' + e + '"]'); 2350 var parent = angular.element(elem).closest('.form-root').scope(); 2351 parent.form.fields[e].value = gridScope.item[f]; 2352 parent.form.fields[e].onChange && parent.form.fields[e].onChange(parent.form); 2353 parent.$apply(); 2354 }); 2355 dnnsfjQuery('#gridModal').modal('hide'); 2356 }) 2357 .on('mouseenter', '.grid-item', function () { 2358 $(this).closest('.item-value').addClass('hover-item'); 2359 }) 2360 .on('mouseleave', '.grid-item', function () { 2361 $(this).closest('.item-value').removeClass('hover-item'); 2362 }); 2363 }; 2364}
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.