1(function(angular, $, _) { 2 angular.module('ng') 3 .config(['$locationProvider', function($locationProvider) { 4 $locationProvider.hashPrefix(''); 5 }]); 6 7 8angular.module('af', CRM.angRequires('af')); 9 10 angular.module('af').directive('afButton', function() { 11 return { 12 restrict: 'C', 13 bindToController: { 14 type: '@' 15 }, 16 require: { 17 afForm: '?^^afForm', 18 }, 19 controller: function($scope, $element) { 20 const ctrl = this; 21 22 this.$onInit = () => { 23 const type = $element.attr('type'); 24 if (type === 'reset') { 25 $element.on('click', (e) => { 26 e.preventDefault(); 27 $scope.$apply(() => { 28 if (this.afForm) { 29 this.afForm.resetForm(); 30 } 31 else { 32 $scope.$parent.$broadcast('afFormReset'); 33 } 34 }); 35 }); 36 } 37 }; 38 } 39 }; 40 }); 41 42 angular.module('af').component('afCharacterCount', { 43 bindings: { 44 maxlength: '<', 45 getter: '<', 46 }, 47 templateUrl: '~/af/afCharacterCount.html', 48 controller: function($scope, $element) { 49 this.$onInit = () => { 50 }; 51 52 this.getLength = function() { 53 return (this.getter() || '').length; 54 }; 55
56 this.getStatusClass = function() { 57 let fraction = this.getLength() / (this.maxlength || 1); 58 if (fraction > 0.9) { 59 return 'text-danger'; 60 } 61 if (fraction > 0.7) { 62 return 'text-warning'; 63 } 64 return 'text-success'; 65 }; 66 67 } 68 }); 69 70const modelProps = { 71 type: '@', 72 data: '=', 73 actions: '=', 74 modelName: '@name', 75 label: '@' 76 }; 77 angular.module('af').component('afEntity', { 78 require: {afForm: '^afForm'}, 79 bindings: modelProps, 80 controller: function() { 81 82 this.$onInit = () => { 83 const entity = Object.keys(modelProps).reduce((obj, key) => { 84 if (this[key] !== undefined) { 85 obj[key] = this[key]; 86 } 87 return obj; 88 }, {}); 89 entity.actions = entity.actions || {update: true, create: true}; 90 entity.id = null; 91 this.afForm.registerEntity(entity); 92 }; 93 } 94 95 }); 96 97let afFieldId = 0; 98 angular.module('af').component('afField', { 99 require: { 100 afForm: '?^^afForm', 101 afFieldset: '?^^afFieldset', 102 afJoin: '?^^afJoin', 103 afRepeatItem: '?^^afRepeatItem' 104 }, 105 templateUrl: '~/af/afField.html', 106 bindings: { 107 fieldName: '@name', 108 defn: '=' 109 }, 110 controller: function($scope, $element, crmApi4, $timeout) { 111 const ts = $scope.ts = CRM.ts('org.civicrm.afform'); 112 const ctrl = this; 113 let namePrefix = ''; 114 let fieldOptions = null; 115 let isExtra = false; 116 this.inputAttrs = []; 117 118 this.$onInit = () => { 119 if (!this.fieldName) { 120 isExtra = true; 121 $scope.dataProvider = this.afForm; 122 this.fieldName = this.defn.name; 123 } else { 124 const closestController = $($element).closest('[af-fieldset],[af-join],[af-repeat-item]'); 125 $scope.dataProvider = closestController.is('[af-repeat-item]') ? this.afRepeatItem : this.afJoin || this.afFieldset; 126 } 127 $scope.fieldId = _.kebabCase(this.fieldName) + '-' + afFieldId++; 128 129 $element.addClass('af-field-type-' + _.kebabCase(this.defn.input_type)); 130 131 if (this.defn.input_attrs && this.defn.input_attrs.multiple) { 132 $element.addClass('af-field-type-multiple'); 133 } 134 135 this.fkEntity = this.defn.fk_entity || null; 136 137 if (this.defn.name !== this.fieldName) { 138 if (!this.defn.name) { 139 console.error('Missing field definition for: ' + this.fieldName); 140 return; 141 } 142 namePrefix = this.fieldName.substr(0, this.fieldName.length - this.defn.name.length); 143 } 144 145 if (this.defn.search_operator) { 146 this.search_operator = this.defn.search_operator; 147 } 148 149 fieldOptions = this.defn.options || null; 150 if (this.defn.data_type === 'Boolean') { 151 if (Array.isArray(fieldOptions)) { 152 fieldOptions.forEach((option) => option.id = !!option.id); 153 } else { 154 fieldOptions = [{id: true, label: ts('Yes')}, {id: false, label: ts('No')}]; 155 } 156 } 157 if (this.fieldName === 'is_primary' && 'repeatIndex' in $scope.dataProvider) { 158 fieldOptions = [{id: true, label: ''}]; 159 $scope.$watch('dataProvider.afRepeat.getEntityController().getData()', function (items, prev) { 160 const index = $scope.dataProvider.repeatIndex; 161 if (items && !index && !items.some((item) => item.is_primary)) { 162 $scope.dataProvider.getFieldData().is_primary = true; 163 } 164 if (items && prev && 165 items.length === prev.length && 166 items[index].is_primary && 167 prev[index].is_primary && 168 items.filter((item) => item.is_primary).length > 1 169 ) { 170 $scope.dataProvider.getFieldData().is_primary = false; 171 } 172 }, true); 173 } 174 const tokens = this.afForm?.identifyTokens(this.defn.afform_default); 175 if (tokens && tokens.length) { 176 const calculateValueWatcher = $scope.$watchCollection(() => Object.values(this.afForm.getTokenValues(tokens)), () => { 177 if ($element[0].querySelector('.ng-touched')) { 178 calculateValueWatcher(); 179 return; 180 } 181 const calculatedValue = this.afForm.replaceTokens(this.defn.afform_default); 182 setValue(calculatedValue); 183 }); 184 } 185 if (this.defn.input_type === 'ChainSelect' && this.defn.input_attrs.control_field) { 186 const controlField = namePrefix + this.defn.input_attrs.control_field; 187 $scope.$watch('dataProvider.getFieldData()["' + controlField + '"]', function(val) { 188 if (val && (typeof val === 'number' || val.length)) { 189 $('input[crm-ui-select]', $element).addClass('loading').prop('disabled', true); 190 const params = { 191 name: ctrl.afFieldset.getFormName(), 192 modelName: ctrl.afFieldset.getName(), 193 fieldName: ctrl.fieldName, 194 joinEntity: ctrl.afJoin ? ctrl.afJoin.entity : null, 195 values: $scope.dataProvider.getFieldData() 196 }; 197 crmApi4('Afform', 'getOptions', params) 198 .then((data) => { 199 $('input[crm-ui-select]', $element).removeClass('loading').prop('disabled', !data.length); 200 fieldOptions = data; 201 }); 202 } else { 203 fieldOptions = null; 204 } 205 }, true); 206 } 207 if (this.defn.input_type === 'ChainSelect' || (Array.isArray(fieldOptions) && fieldOptions.some((o) => o && o.if && o.if.length))) { 208 $scope.$watchCollection(() => $scope.getOptions().map((o) => o.id), () => this.validateValue()); 209 } 210 if (this.defn.input_type === 'EntityRef' && this.defn.dfk_entities && this.defn.input_attrs.control_field) { 211 const controlField = namePrefix + this.defn.input_attrs.control_field; 212 $scope.$watch('dataProvider.getFieldData()["' + controlField + '"]', (val) => { 213 if (val && val.length) { 214 if (Array.isArray(val)) { 215 this.fkEntity = this.defn.dfk_entities[val[0]]; 216 } else { 217 this.fkEntity = this.defn.dfk_entities[val]; 218 } 219 } else { 220 this.fkEntity = null; 221 } 222 }); 223 } 224 $timeout(() => { 225 initializeValue(true);
226 $scope.$watch('$parent.routeParams', setValueFromRouteParams); 227 }); 228 function setValueFromRouteParams(routeParams) { 229 if (!routeParams) { 230 return; 231 } 232 const entityName = ctrl.afFieldset?.getName(); 233 const joinEntity = ctrl.afJoin?.entity; 234 let uniquePrefix = ''; 235 if (!isExtra && entityName) { 236 const index = ctrl.getEntityIndex(); 237 uniquePrefix = entityName + (index ? index + 1 : '') + (joinEntity ? '.' + joinEntity : '') + '.'; 238 } 239 if ((uniquePrefix + ctrl.fieldName) in routeParams) { 240 setValue(routeParams[uniquePrefix + ctrl.fieldName]); 241 } 242 else if (ctrl.fieldName in routeParams) { 243 setValue(routeParams[ctrl.fieldName]); 244 } 245 else if (!isExtra && routeParams._s) { 246 setValue(ctrl.afFieldset.getSearchParamSetFieldValue(ctrl.fieldName)); 247 } 248 } 249 250 function initializeValue(firstLoad) { 251 if (firstLoad && ctrl.afFieldset?.getStoredValue(ctrl.fieldName) !== undefined) { 252 setValue(ctrl.afFieldset.getStoredValue(ctrl.fieldName)); 253 } 254 else if ('afform_default' in ctrl.defn) { 255 if (ctrl.afForm?.identifyTokens(ctrl.defn.afform_default)) { 256 setValue(ctrl.afForm.replaceTokens(ctrl.defn.afform_default)); 257 } 258 else { 259 setValue(ctrl.defn.afform_default); 260 } 261 } 262 263 if (ctrl.defn.search_range) { 264 const initialVal = $scope.dataProvider.getFieldData()[ctrl.fieldName]; 265 if (!Array.isArray($scope.dataProvider.getFieldData()[ctrl.fieldName]) && 266 (ctrl.defn.input_type !== 'Select' || !ctrl.defn.is_date || initialVal === '{}') 267 ) { 268 $scope.dataProvider.getFieldData()[ctrl.fieldName] = {}; 269 } 270 if (ctrl.defn.is_date) { 271 ctrl.inputAttrs.push(ctrl.defn.input_attrs || {}); 272 for (let i = 1; i <= 2; ++i) { 273 const attrs = _.cloneDeep(ctrl.defn.input_attrs || {}); 274 attrs.placeholder = attrs['placeholder' + i]; 275 attrs.timePlaceholder = attrs['timePlaceholder' + i]; 276 ctrl.inputAttrs.push(attrs); 277 } 278 } 279 } 280 } 281 $scope.$on('afFormReset', function() { 282 delete $scope.dataProvider.getFieldData()[ctrl.fieldName]; 283 initializeValue(false); 284 }); 285 }; 286 $scope.$on('afIfDestroy', function() { 287 if (ctrl.defn.input_type !== 'DisplayOnly') { 288 delete $scope.dataProvider.getFieldData()[ctrl.fieldName]; 289 } 290 }); 291 function correctValueType(value, dataType) { 292 if (value === null) { 293 return value; 294 } 295 if (Array.isArray(value)) { 296 return value.map((val) => correctValueType(val, dataType)); 297 } else if (dataType === 'Integer' || dataType === 'Float') { 298 return Number(value); 299 } else if (dataType === 'Boolean') { 300 return (value == 1); 301 } 302 return value; 303 } 304 305 this.isMultiple = function() { 306 return ( 307 (['Select', 'EntityRef', 'ChainSelect'].includes(ctrl.defn.input_type) && ctrl.defn.input_attrs.multiple) || 308 ((ctrl.defn.input_type === 'CheckBox' || ctrl.defn.input_type === 'Toggle') && ctrl.defn.data_type !== 'Boolean') || 309 ((ctrl.defn.input_type === 'Hidden' || ctrl.defn.input_type === 'DisplayOnly') && (ctrl.defn.serialize || ctrl.defn.data_type === 'Array')) 310 ); 311 }; 312 function setValue(value) { 313 if (typeof value === 'string' && ctrl.isMultiple()) { 314 value = value.split(','); 315 } 316 if (typeof value === 'object' && value !== null && !Array.isArray(value) && ctrl.search_operator) { 317 if (ctrl.defn.expose_operator) { 318 ctrl.search_operator = Object.keys(value)[0]; 319 } 320 value = value[ctrl.search_operator] ? value[ctrl.search_operator] : null; 321 } 322 if (ctrl.defn.input_type === 'EntityRef' && ['Contact', 'Individual'].includes(ctrl.fkEntity) && value === 'user_contact_id') { 323 value = CRM.config.cid; 324 } 325 if (ctrl.defn.input_type !== 'DisplayOnly') { 326 value = correctValueType(value, ctrl.defn.data_type); 327 } 328 329 if (ctrl.defn.input_type === 'Date' && typeof value === 'string' && value.startsWith('now')) { 330 value = getRelativeDate(value, ctrl.defn.input_attrs.time); 331 } 332 if (ctrl.defn.input_type === 'Number' && ctrl.defn.search_range) { 333 if (!_.isPlainObject(value)) { 334 value = { 335 '>=': +(('' + value).split('-')[0] || 0), 336 '<=': +(('' + value).split('-')[1] || 0), 337 }; 338 } 339 } else if (ctrl.defn.input_type === 'Number') { 340 value = Number(value); 341 } 342 else if (ctrl.defn.search_range && !_.isPlainObject(value) && 343 !(ctrl.defn.options && ctrl.defn.options.some((option) => option.id === value)) 344 ) { 345 value = { 346 '>=': ('' + value).split('-')[0], 347 '<=': ('' + value).split('-')[1] || '', 348 }; 349 } 350 $scope.getSetValue(value); 351 } 352 ctrl.getEntityIndex = function() { 353 if ('repeatIndex' in $scope.dataProvider && $scope.dataProvider.afRepeat.getRepeatType() === 'join') { 354 return $scope.dataProvider.outerRepeatItem ? $scope.dataProvider.outerRepeatItem.repeatIndex : 0; 355 } else { 356 return ctrl.afRepeatItem ? ctrl.afRepeatItem.repeatIndex : 0; 357 } 358 }; 359 360 ctrl.isReadonly = function() { 361 if (!isExtra && ctrl.defn.input_attrs && ctrl.defn.input_attrs.autofill && !ctrl.afJoin) { 362 return ctrl.afFieldset.getEntity()?.actions?.[ctrl.defn.input_attrs.autofill] === false; 363 } 364 return ctrl.defn.input_type === 'DisplayOnly'; 365 }; 366 367 ctrl.isDisabled = function() { 368 if (ctrl.isReadonly()) { 369 return true; 370 } 371 return ctrl.defn.input_type === 'EntityRef' && !ctrl.fkEntity; 372 }; 373 374 ctrl.getDisplayValue = function(value) { 375 if (value === undefined || value === null || value === '' || (Array.isArray(value) && !value.length)) { 376 return ''; 377 } 378 if (Array.isArray(fieldOptions)) { 379 let keys = Array.isArray(value) ? value : [value]; 380 let options = fieldOptions.filter((option) => keys.includes(option.id)); 381 return options.map((option) => option.label).join(', '); 382 } 383 if (ctrl.defn.data_type === 'Date' || ctrl.defn.data_type === 'Timestamp') { 384 try { 385 return CRM.utils.formatDate(value, null, ctrl.defn.data_type === 'Timestamp'); 386 } catch (e) { 387 return ''; 388 } 389 } 390 if (ctrl.fkEntity) { 391 const ids = Array.isArray(value) ? value : [value]; 392 if (!ctrl._entityLabels) { 393 ctrl._entityLabels = {}; 394 } 395 if (!(ids.join() in ctrl._entityLabels)) { 396 ctrl._entityLabels[ids.join()] = null; 397 const params = ctrl.getAutocompleteParams(); 398 params.ids = ids; 399 crmApi4(ctrl.fkEntity, 'autocomplete', params) 400 .then((result) => { 401 ctrl._entityLabels[ids.join()] = result.map((item) => item.label).join(', '); 402 }); 403 } 404 return ctrl._entityLabels[ids.join()] || ts('Loading...'); 405 } 406 return value; 407 }; 408 ctrl.onSelectEntity = function() { 409 if (ctrl.afForm && ctrl.defn.input_attrs && ctrl.defn.input_attrs.autofill) { 410 const val = $scope.getSetSelect(); 411 const entity = ctrl.afFieldset.modelName; 412 const entityIndex = ctrl.getEntityIndex(); 413 const joinEntity = ctrl.afJoin ? ctrl.afJoin.entity : null; 414 const joinIndex = ctrl.afJoin && $scope.dataProvider.repeatIndex || 0; 415 ctrl.afFieldset.afFormCtrl.loadData(entity, entityIndex, val, ctrl.defn.name, joinEntity, joinIndex); 416 } 417 }; 418 ctrl.getFileUploadParams = function() { 419 return { 420 modelName: ctrl.afFieldset.getName(), 421 fieldName: ctrl.fieldName, 422 joinEntity: ctrl.afJoin ? ctrl.afJoin.entity : null, 423 entityIndex: ctrl.getEntityIndex(), 424 joinIndex: ctrl.afJoin && $scope.dataProvider.repeatIndex || null 425 }; 426 }; 427 428 ctrl.getAutocompleteParams = function() { 429 let fieldName = isExtra ? 'extra' : ctrl.afFieldset.getName(); 430 if (!isExtra && ctrl.afJoin) { 431 fieldName += '+' + ctrl.afJoin.entity; 432 } 433 fieldName += ':' + ctrl.fieldName; 434 return { 435 formName: 'afform:' + ctrl.afFieldset.getFormName(), 436 fieldName: fieldName, 437 values: $scope.dataProvider.getFieldData() 438 }; 439 }; 440 441 $scope.getOptions = () => { 442 if (!Array.isArray(fieldOptions)) { 443 return []; 444 } 445 return fieldOptions.filter((opt) => { 446 if (!opt.if || !opt.if.length) return true; 447 try { 448 return this.afForm.checkConditions(opt.if); 449 } catch (e) { 450 console.warn('[afField] `if` evaluation failed for option', opt.id, e); 451 return true; 452 } 453 }); 454 }; 455 456 this.validateValue = () => {
457 const current = $scope.dataProvider.getFieldData()[this.fieldName]; 458 const options = $scope.getOptions().map((o) => o.id); 459 460 if (Array.isArray(current)) { 461 const valid = current.filter((v) => options.includes(v)); 462 if (valid.length < current.length) { 463 $scope.dataProvider.getFieldData()[this.fieldName] = valid; 464 $('input[crm-ui-select]', $element).val(valid).change(); 465 } 466 } else { 467 if (!options.includes(current)) { 468 $('input[crm-ui-select]', $element).val('').change(); 469 delete $scope.dataProvider.getFieldData()[this.fieldName]; 470 } 471 } 472 }; 473 474 $scope.select2Options = function() { 475 return { 476 results: _.transform($scope.getOptions(), function(result, opt) { 477 result.push({id: opt.id, text: opt.label}); 478 }, []) 479 }; 480 }; 481 482 this.onChangeOperator = function() { 483 $scope.dataProvider.getFieldData()[ctrl.fieldName] = {}; 484 }; 485 $scope.getSetValue = function(val) { 486 const currentVal = $scope.dataProvider.getFieldData()[ctrl.fieldName]; 487 if (arguments.length) { 488 if (ctrl.search_operator) { 489 if (typeof currentVal !== 'object') { 490 $scope.dataProvider.getFieldData()[ctrl.fieldName] = {}; 491 } 492 return ($scope.dataProvider.getFieldData()[ctrl.fieldName][ctrl.search_operator] = val); 493 } 494 return ($scope.dataProvider.getFieldData()[ctrl.fieldName] = val); 495 } 496 if (ctrl.search_operator) { 497 return (currentVal || {})[ctrl.search_operator]; 498 } 499 return currentVal; 500 }; 501 $scope.getSetSelect = function(val) { 502 const currentVal = $scope.dataProvider.getFieldData()[ctrl.fieldName]; 503 if (arguments.length) { 504 if (ctrl.defn.is_date) { 505 if (val === '{}') { 506 val = !_.isPlainObject(currentVal) ? {} : currentVal; 507 } 508 } 509 else if (ctrl.defn.search_range) { 510 return ($scope.dataProvider.getFieldData()[ctrl.fieldName]['>='] = val); 511 } 512 else if (ctrl.search_operator) { 513 if (typeof currentVal !== 'object') { 514 $scope.dataProvider.getFieldData()[ctrl.fieldName] = {}; 515 } 516 return ($scope.dataProvider.getFieldData()[ctrl.fieldName][ctrl.search_operator] = val); 517 } 518 if (ctrl.defn.data_type === 'Boolean') { 519 return ($scope.dataProvider.getFieldData()[ctrl.fieldName] = (val === 'true')); 520 } 521 if (ctrl.defn.data_type === 'Integer' || ctrl.defn.data_type === 'Float') { 522 if (typeof val === 'string') { 523 return ($scope.dataProvider.getFieldData()[ctrl.fieldName] = val.length ? Number(val) : null); 524 } else if (Array.isArray(val)) { 525 return ($scope.dataProvider.getFieldData()[ctrl.fieldName] = val.map(Number)); 526 } 527 } 528 return ($scope.dataProvider.getFieldData()[ctrl.fieldName] = val); 529 } 530 if (ctrl.defn.is_date) { 531 return _.isPlainObject(currentVal) ? '{}' : currentVal; 532 } 533 else if (ctrl.defn.search_range) { 534 return currentVal['>=']; 535 } 536 else if (ctrl.search_operator) { 537 return (currentVal || {})[ctrl.search_operator]; 538 } 539 else if (!ctrl.isMultiple() && (typeof currentVal === 'boolean' || typeof currentVal === 'number')) { 540 return JSON.stringify(currentVal); 541 } 542 return currentVal; 543 }; 544 545 function getRelativeDate(dateString, includeTime) { 546 const parts = dateString.split(' '); 547 const baseDate = new Date(); 548 let unit = parts[2] || 'day'; 549 let offset = parseInt(parts[1] || '0', 10); 550 551 switch (unit) { 552 case 'week': 553 offset *= 7; 554 break; 555 556 case 'year': 557 offset *= 365; 558 } 559 let newDate = new Date(baseDate.getTime() + offset * 24 * 60 * 60 * 1000); 560 let localYear = newDate.getFullYear(); 561 let localMonth = String(newDate.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed 562 let localDay = String(newDate.getDate()).padStart(2, '0'); 563 let defaultDate = `${localYear}-${localMonth}-${localDay}
563`; // Format YYYY-MM-DD 564 565 if (includeTime) { 566 defaultDate += ' ' + newDate.toTimeString().slice(0,8); 567 } 568 return defaultDate; 569 } 570 571 } 572 }); 573 574angular.module('af').directive('afFieldset', function() { 575 return { 576 restrict: 'A', 577 require: ['afFieldset', '?^^afForm', '?afRepeat'], 578 bindToController: { 579 modelName: '@afFieldset', 580 storeValues: '<' 581 }, 582 link: function($scope, $el, $attr, ctrls) { 583 const self = ctrls[0]; 584 self.afFormCtrl = ctrls[1]; 585 self.afRepeatCtrl = ctrls[2]; 586 }, 587 controller: function($scope, $element, crmApi4) { 588 const ctrl = this; 589 const localData = []; 590 const joinOffsets = {}; 591 const ts = $scope.ts = CRM.ts('org.civicrm.afform'); 592 593 this.getData = function() { 594 return ctrl.afFormCtrl ? ctrl.afFormCtrl.getData(ctrl.modelName) : localData; 595 }; 596 this.getName = function() { 597 return this.modelName || 598 $element.find('[search-name][display-name]').attr('display-name'); 599 }; 600 this.getEntity = function() { 601 return this.afFormCtrl ? this.afFormCtrl.getEntity(this.modelName) : null; 602 }; 603 this.getEntityType = function() { 604 return this.afFormCtrl ? this.afFormCtrl.getEntity(this.modelName)?.type : null; 605 }; 606 this.getFieldData = function() { 607 const data = ctrl.getData(); 608 if (!data.length && !ctrl.afRepeatCtrl) { 609 data.push({fields: {}}); 610 } 611 return data[0]?.fields; 612 }; 613 this.addRepeatItem = () => { 614 this.getData().push({fields: {}}); 615 }; 616 617 this.getFormName = function() { 618 return ctrl.afFormCtrl ? ctrl.afFormCtrl.getFormMeta().name : $scope.meta.name; 619 }; 620 621 this.getFieldMeta = () => { 622 const meta = {}; 623 const fieldElements = $element[0].querySelectorAll('af-field'); 624 fieldElements.forEach((field) => { 625 const name = field.getAttribute('name'); 626 const defn = $scope.$eval(field.getAttribute('defn')); 627 meta[name] = defn; 628 }); 629 return meta; 630 }; 631 632 this.getJoinOffset = function(joinEntity) { 633 joinOffsets[joinEntity] = joinEntity in joinOffsets ? joinOffsets[joinEntity] + 1 : 0; 634 return joinOffsets[joinEntity]; 635 }; 636 function getCacheKey() { 637 return 'afform:' + ctrl.getFormName() + ctrl.getName(); 638 }
639 this.getStoredValue = function(fieldName) { 640 if (!this.storeValues) { 641 return; 642 } 643 if (!this.reloadedStoredValues) { 644 this.reloadedStoredValues = CRM.cache.get(getCacheKey(), {}); 645 } 646 return this.reloadedStoredValues[fieldName]; 647 }; 648 649 this.$onInit = function() { 650 $scope.$watch(ctrl.getFieldData, (newVal, oldVal) => { 651 $element[0].dispatchEvent(new Event('crmFormChangeFilters')); 652 if (this.storeValues) { 653 if (typeof newVal === 'object' && typeof oldVal === 'object' && Object.keys(newVal).length) { 654 CRM.cache.set(getCacheKey(), newVal); 655 } 656 } 657 }, true); 658 659 $scope.$watch(this.getSearchParamSetId, () => this.fetchSearchParamSetValues()); 660 }; 661 662 /** 663 * Get fieldset values to use for afform filters 664 * 665 * Filter out empty values (null, '', [], {}) at top level and one level 666 * below for object values (fields with ranges/operators) 667 * 668 * @returns Object 669 */ 670 this.getFilterValues = () => { 671 const isEmpty = (v) => { 672 if (v === null || v === '') { 673 return true; 674 } 675 if (Array.isArray(v) && v.length === 0) { 676 return true; 677 } 678 if (typeof v === 'object' && Object.keys(v).length === 0) { 679 return true; 680 } 681 }; 682 const data = _.cloneDeep(this.getFieldData()); 683 684 Object.keys(data).forEach((key) => { 685 if (typeof data[key] === 'object' && data[key] !== null) { 686 Object.keys(data[key]).forEach((subKey) => { 687 if (isEmpty(data[key][subKey])) { 688 delete data[key][subKey]; 689 } 690 }); 691 } 692 if (isEmpty(data[key])) { 693 delete data[key]; 694 } 695 }); 696 697 return data; 698 }; 699 700 /** 701 * Get value for a given field based on currently applied SearchParamSet 702 * Used by afField controller on load 703 */
704 this.getSearchParamSetFieldValue = (fieldName) => { 705 if (this.selectedSearchParamSet && this.selectedSearchParamSet.filters && this.selectedSearchParamSet.filters.hasOwnProperty(fieldName)) { 706 return this.selectedSearchParamSet.filters[fieldName]; 707 } 708 return null; 709 }; 710 711 /** 712 * Fetch values for a SearchParamSet from the server 713 */ 714 this.fetchSearchParamSetValues = () => { 715 const searchParamSetId = this.getSearchParamSetId(); 716 if (!searchParamSetId) { 717 return; 718 } 719 crmApi4('SearchParamSet', 'get', { 720 where: [ 721 ['id', '=', searchParamSetId], 722 ['afform_name', '=', this.getFormName()] 723 ], 724 select: ['filters', 'columns'], 725 }) 726 .then((result) => { 727 if (result && result[0]) { 728 this.selectedSearchParamSet = result[0]; 729 } 730 else { 731 this.selectedSearchParamSet = {}; 732 } 733 $scope.$broadcast('afFormReset'); 734 }); 735 }; 736 737 /** 738 * Get search param set ID from URL hash param 739 */
740 this.getSearchParamSetId = () => ($scope && $scope.routeParams) ? $scope.routeParams._s : null; 741 } 742 }; 743 }); 744 745 angular.module('af').component('afForm', { 746 bindings: { 747 ctrl: '@' 748 }, 749 require: { 750 ngForm: 'form' 751 }, 752 controller: function($scope, $element, $timeout, crmApi4, crmStatus, $window, $location, $parse, FileUploader) { 753 const 754 ctrl = this, 755 ts = CRM.ts('org.civicrm.afform'); 756 757 const saveDraftButtons = []; 758 const schema = {}; 759 const data = { 760 extra: {fields: {}}, 761 }; 762 763 let 764 args, 765 submissionResponse, 766 autoSave = () => {}, 767 draftStatus = 'pristine', 768 cancelDraftWatcher, 769 uploadingDraftFiles = false; 770 771 this.$onInit = () => { 772 $scope.$parent[this.ctrl] = this; 773 774 $timeout(() => { 775 $element[0].querySelectorAll('.af-markup:not(af-markup)').forEach((el) => { 776 const renderer = document.createElement('af-markup'); 777 renderer.markup = el.innerHTML; 778 el.replaceChildren(renderer); 779 }); 780 781 ctrl.loadData() 782 .then(setupDraftWatcher); 783 784 ctrl.showSubmitButton = displaySubmitButton(args); 785 }); 786 }; 787 788 this.registerEntity = function registerEntity(entity) { 789 schema[entity.modelName] = entity; 790 data[entity.modelName] = []; 791 }; 792 this.getEntity = function getEntity(name) { 793 return schema[name]; 794 }; 795 this.getData = function getData(name) { 796 return data[name]; 797 };
798 this.getSchema = function getSchema(name) { 799 return schema[name]; 800 }; 801 this.getFormMeta = function getFormMeta() { 802 return $scope.$parent.meta; 803 }; 804 this.resetForm = function() { 805 this.ngForm.$setPristine(); 806 $scope.$parent.$broadcast('afFormReset'); 807 this.loadData(); 808 }; 809 this.loadData = function(selectedEntity, selectedIndex, selectedId, selectedField, joinEntity, joinIndex) { 810 let toLoad = true; 811 const params = {name: ctrl.getFormMeta().name, args: {}}; 812 if (selectedEntity) { 813 toLoad = !!selectedId; 814 params.args[selectedEntity] = {}; 815 params.args[selectedEntity][selectedIndex] = {}; 816 if (joinEntity) { 817 params.fillMode = 'join'; 818 params.args[selectedEntity][selectedIndex].joins = {}; 819 params.args[selectedEntity][selectedIndex].joins[joinEntity] = {}; 820 params.args[selectedEntity][selectedIndex].joins[joinEntity][joinIndex] = {}; 821 params.args[selectedEntity][selectedIndex].joins[joinEntity][joinIndex][selectedField] = selectedId; 822 } else { 823 params.fillMode = 'entity'; 824 params.args[selectedEntity][selectedIndex][selectedField] = selectedId; 825 } 826 } 827 else { 828 params.fillMode = 'form'; 829 args = Object.assign({}, $scope.$parent.routeParams || {}, $scope.$parent.options || {}); 830 Object.keys(schema).forEach((entityName) => { 831 if (args[entityName] && typeof args[entityName] === 'string') { 832 args[entityName] = args[entityName].split(','); 833 } 834 }); 835 params.args = args; 836 ctrl.showSubmitButton = displaySubmitButton(args); 837 } 838 if (toLoad) { 839 if (params.fillMode === 'form') { 840 $element.block(); 841 } 842 return crmApi4('Afform', 'prefill', params) 843 .then((result) => { 844 result.forEach((item) => { 845 _.each(item.values, (values, index) => { 846 data[item.name][index] = data[item.name][index] || {}; 847 data[item.name][index].joins = data[item.name][index].joins || {}; 848 angular.merge(data[item.name][index], values, {fields: _.cloneDeep(schema[item.name]?.data || {})}); 849 }); 850 }); 851 $element.unblock(); 852 }, (error) => { 853 disableForm(error.error_message, ts('Sorry'), 'error'); 854 $element.unblock(); 855 }); 856 } 857 else if (joinEntity) { 858 data[selectedEntity][selectedIndex].joins[joinEntity][joinIndex] = {}; 859 } 860 else if (selectedEntity) { 861 Object.keys(data[selectedEntity][selectedIndex].fields).forEach((key) => delete data[selectedEntity][selectedIndex].fields[key]); 862 angular.merge(data[selectedEntity][selectedIndex].fields, _.cloneDeep(schema[selectedEntity].data || {})); 863 data[selectedEntity][selectedIndex].joins = {}; 864 } 865 }; 866 867 function displaySubmitButton(args) { 868 if (args.sid && args.sid.length > 0) { 869 return false; 870 } 871 return true; 872 } 873 const token = new URLSearchParams(window.location.search).get('_aff'); 874 const headers = {'X-Requested-With': 'XMLHttpRequest'}; 875 if (token) { 876 headers['X-Civi-Auth-Afform'] = token; 877 } 878 this.fileUploader = new FileUploader({ 879 url: CRM.url('civicrm/ajax/api4/Afform/submitFile'), 880 headers: headers, 881 onAfterAddingFile: (item) => { 882 setDraftStatus('unsaved'); 883 }, 884 onSuccessItem: onFileUploadSuccess, 885 onCompleteAll: onFileUploadsComplete, 886 onBeforeUploadItem: (item) => { 887 item._status = CRM.status({ 888 start: ts('Uploading %1', {1: item.file.name}), 889 error: ts('Upload failed'), 890 success: ts('Upload complete'), 891 }); 892 }, 893 onCompleteItem: (item, response, status, headers) => { 894 if (status === 200) { 895 item._status.resolve(); 896 } else { 897 item._status.reject(); 898 } 899 } 900 }); 901 902 function onFileUploadSuccess(item, response, status, headers) { 903 if (response.values && response.values[0] && response.values[0].id) { 904 const dataProvider = item.crmDataProvider; 905 dataProvider.getFieldData()[item.crmFieldName] = response.values[0]; 906 } 907 } 908 909 function onFileUploadsComplete() { 910 if (uploadingDraftFiles) { 911 uploadingDraftFiles = false;
912 setDraftStatus('saved'); 913 if (draftStatus === 'unsaved') { 914 autoSave(); 915 } 916 } else { 917 postProcess(); 918 } 919 } 920 function setupDraftWatcher() { 921 const buttons = getDraftButtons(); 922 const autoSaveEnabled = ctrl.getFormMeta().autosave_draft; 923 924 if ((!autoSaveEnabled && !buttons.length) || !ctrl.showSubmitButton || (!CRM.config.cid && !token)) { 925 return; 926 } 927 $.each(buttons, function(index, button) { 928 saveDraftButtons[index] = { 929 text: $(button).text(), 930 icon: $(button).attr('crm-icon'), 931 }; 932 }); 933 if (autoSaveEnabled) { 934 autoSave = _.debounce(ctrl.submitDraft, 10000); 935 } 936 937 cancelDraftWatcher = $scope.$watch(() => data, function (newVal, oldVal) { 938 if (oldVal) { 939 if (draftStatus === 'pristine') { 940 setDraftStatus('saved'); 941 } else { 942 setDraftStatus('unsaved'); 943 autoSave(newVal); 944 } 945 } 946 }, 947 true 948 ); 949 } 950 this.checkConditions = function(conditions, op) { 951 op = op || 'AND'; 952 let ret = op === 'AND', 953 flip = !ret; 954 _.each(conditions, function(clause) { 955 if (Array.isArray(clause[1])) { 956 if (ctrl.checkConditions(clause[1], clause[0]) === flip) { 957 ret = flip; 958 } 959 } else { 960 if (typeof clause[0] === 'string' && clause[0].charAt(0) !== '"') { 961 clause[0] = clause[0].replace(/\[([^'"])/g, "['$1").replace(/([^'"])]/g, "$1']"); 962 } 963 let parser1 = $parse(clause[0]); 964 let parser2 = $parse(clause[2]); 965 let result = compareConditions(parser1(data), clause[1], parser2(data)); 966 if (result === flip) { 967 ret = flip; 968 } 969 } 970 }); 971 return op === 'NOT' ? !ret : ret; 972 }; 973 974 function compareConditions(val1, op, val2) { 975 const yes = (op !== '!=' && !op.includes('NOT ')); 976 977 switch (op) { 978 case '=': 979 case '!=': 980 case '==': 981 if (typeof val1 === 'string') { 982 val1 = val1.toLowerCase(); 983 } 984 if (typeof val2 === 'string') { 985 val2 = val2.toLowerCase(); 986 } 987 return angular.equals(val1, val2) === yes; 988 989 case '>': 990 return val1 > val2; 991 992 case '<': 993 return val1 < val2; 994 995 case '>=': 996 return val1 >= val2; 997 998 case '<=': 999 return val1 <= val2; 1000 1001 case 'IS EMPTY': 1002 return !val1; 1003 1004 case 'IS NOT EMPTY': 1005 return !!val1; 1006 1007 case 'CONTAINS': 1008 case 'NOT CONTAINS': 1009 if (Array.isArray(val1)) { 1010 return val1.includes(val2) === yes; 1011 } else if (typeof val1 === 'string' && typeof val2 === 'string') { 1012 return val1.toLowerCase().includes(val2.toLowerCase()) === yes; 1013 } 1014 return angular.equals(val1, val2) === yes; 1015 1016 case 'IN': 1017 case 'NOT IN': 1018 if (Array.isArray(val2)) { 1019 return val2.includes(val1) === yes; 1020 } 1021 return angular.equals(val1, val2) === yes; 1022 1023 case 'LIKE': 1024 case 'NOT LIKE': 1025 if (typeof val1 === 'string' && typeof val2 === 'string') { 1026 return likeCompare(val1, val2) === yes; 1027 } 1028 return angular.equals(val1, val2) === yes; 1029 } 1030 } 1031 1032 function likeCompare(str, pattern) { 1033 const regexPattern = pattern 1034 .replace(/([.+?^=!:${}()|\[\]\/\\])/g, "\\$1") 1035 .replace(/%/g, '.*') // Convert % to .* 1036 .replace(/_/g, '.'); // Convert _ to . 1037 const regex = new RegExp(`^${regexPattern}$`, 'i'); 1038 return regex.test(str); 1039 } 1040 function postProcess() { 1041 const metaData = ctrl.getFormMeta(), 1042 dialog = $element.closest('.ui-dialog-content'); 1043 ctrl.ngForm.$setPristine(); 1044 1045 $element.trigger('crmFormSuccess', { 1046 afform: metaData, 1047 data: data, 1048 submissionResponse: submissionResponse, 1049 }); 1050 1051 if (submissionResponse[0].redirect) { 1052 let url = submissionResponse[0].redirect; 1053 if (url.indexOf('civicrm/') === 0) { 1054 url = CRM.url(url); 1055 } else if (url.indexOf('/') === 0) { 1056 let port = $location.port(); 1057 port = port ? `:${port}` : ''; 1058 url = `${$location.protocol()}://${$location.host()}${port}${url}`; 1059 } 1060 $window.location.href = url; 1061 return; 1062 } 1063 1064 if (submissionResponse[0].message) { 1065 $element.hide(); 1066 const $confirmation = $('<div class="afform-confirmation" />'); 1067 $confirmation.html(submissionResponse[0].message); 1068 $confirmation.insertAfter($element); 1069 } 1070 else if (dialog.length) { 1071 dialog.dialog('close'); 1072 } 1073 else { 1074 $element.unblock(); 1075 } 1076 } 1077 1078 function validateFileFields() { 1079 let valid = true; 1080 $("af-form[ng-form=" + ctrl.getFormMeta().name + "] input[type='file']").each((index, fld) => { 1081 if ($(fld).attr('required') && $(fld).get(0).files.length == 0) { 1082 valid = false;
1083 } 1084 }); 1085 return valid; 1086 } 1087 1088 function disableForm(errorMsg) { 1089 $('af-form[ng-form="' + ctrl.getFormMeta().name + '"]') 1090 .addClass('disabled') 1091 .find('button[ng-click="afform.submit()"]').prop('disabled', true); 1092 CRM.alert(errorMsg, ts('Sorry'), 'error'); 1093 } 1094 1095 const handleError = (error) => { 1096 if (error && error.error_code !== '1') { 1097 CRM.alert(error.error_message, ts('Please resolve these issues'), 'warning'); 1098 } 1099 else { 1100 const message = error?.error_message ? error.error_message : ts('Unknown error'); 1101 CRM.alert(message, ts('There is a problem'), 'error'); 1102 } 1103 }; 1104 1105 this.validate = () => { 1106 if (!ctrl.ngForm.$valid || !validateFileFields()) { 1107 return new Promise((resolve) => resolve({ 1108 is_error: true, 1109 message: ts('Please fill all required fields.'), 1110 })); 1111 } 1112 return crmApi4('Afform', 'validate', { 1113 name: this.getFormMeta().name, 1114 args: args, 1115 values: data, 1116 }); 1117 }; 1118 1119 this.submit = function () { 1120 if (!ctrl.ngForm.$valid || !validateFileFields()) { 1121 const firstInvalidInput = $element[0].closest('af-form').querySelector('.ng-invalid'); 1122 const isRequired = firstInvalidInput.classList.contains('ng-invalid-required'); 1123 const message = isRequired ? ts('Please fill all required fields.') : ts('Please check all answers are valid.'); 1124 1125 if (document.getElementById('crm-notification-container')) { 1126 CRM.alert(message, ts('Form Error')); 1127 } 1128 else { 1129 if (firstInvalidInput.classList.contains('select2-container')) { 1130 firstInvalidInput.scrollIntoView(); 1131 firstInvalidInput.focus(); 1132 CRM.alert(message, ts('Form Error')); 1133 } 1134 } 1135 return; 1136 } 1137 $element.block(); 1138 if (cancelDraftWatcher) { 1139 cancelDraftWatcher(); 1140 } 1141 1142 const submitApi = crmApi4('Afform', 'submit', { 1143 name: ctrl.getFormMeta().name, 1144 args: args, 1145 values: data, 1146 }).then((response) => { 1147 submissionResponse = response; 1148 if (ctrl.fileUploader.getNotUploadedItems().length) { 1149 _.each(ctrl.fileUploader.getNotUploadedItems(), function(file) { 1150 file.formData.push({ 1151 params: JSON.stringify(_.extend({ 1152 token: response[0].token, 1153 name: ctrl.getFormMeta().name 1154 }, file.crmApiParams())) 1155 }); 1156 }); 1157 ctrl.fileUploader.uploadAll(); 1158 } 1159 else { 1160 postProcess(); 1161 } 1162 }) 1163 .catch((error) => { 1164 $element.unblock(); 1165 1166 handleError(error); 1167 1168 $element.trigger('crmFormError', { 1169 afform: ctrl.getFormMeta(), 1170 data: data, 1171 submissionResponse: submissionResponse, 1172 error: error 1173 }); 1174 }); 1175 crmStatus({ 1176 error: ts('Not Saved'), 1177 }, submitApi); 1178 }; 1179 1180 this.submitDraft = function() { 1181 if (uploadingDraftFiles) { 1182 return; 1183 } 1184 setDraftStatus('saving'); 1185 const submitDraft = crmApi4('Afform', 'submitDraft', { 1186 name: ctrl.getFormMeta().name, 1187 args: args, 1188 values: data, 1189 }).then((response) => { 1190 if (ctrl.fileUploader.getNotUploadedItems().length) { 1191 uploadingDraftFiles = true;
1192 ctrl.fileUploader.getNotUploadedItems().forEach((file) => { 1193 file.formData.push({ 1194 params: JSON.stringify(_.extend({ 1195 name: ctrl.getFormMeta().name 1196 }, file.crmApiParams())) 1197 }); 1198 }); 1199 ctrl.fileUploader.uploadAll(); 1200 } else { 1201 setDraftStatus('saved'); 1202 } 1203 }) 1204 .catch((error) => { 1205 setDraftStatus('unsaved'); 1206 handleError(error); 1207 }); 1208 crmStatus({ 1209 start: ts('Saving Draft'), 1210 success: ts('Draft Saved'), 1211 error: ts('Not Saved'), 1212 }, submitDraft); 1213 }; 1214 1215 function getDraftButtons() { 1216 return $element.find('button[ng-click="afform.submitDraft()"]'); 1217 } 1218 1219 function setDraftStatus(newStatus) { 1220 if (draftStatus === newStatus) { 1221 return; 1222 } 1223 if (draftStatus === 'unsaved' && newStatus === 'saved') { 1224 return; 1225 } 1226 if (newStatus === 'unsaved' && !uploadingDraftFiles) { 1227 restoreDraftButtons(); 1228 } 1229 else if (!uploadingDraftFiles) { 1230 const newText = newStatus === 'saving' ? ts('Saving Draft') : ts('Draft Saved'); 1231 const newIcon = newStatus === 'saving' ? 'fa-spinner fa-spin' : 'fa-check'; 1232 disableDraftButtons(newText, newIcon); 1233 } 1234 draftStatus = newStatus; 1235 } 1236 1237 function disableDraftButtons(text, icon) { 1238 const buttons = getDraftButtons(); 1239 $.each(buttons, function(index, button) { 1240 $(button).text(text).attr('disabled', true); 1241 $(button).prepend('<i class="crm-i ' + icon + '" role="img" aria-hidden="true"></i> '); 1242 }); 1243 } 1244 1245 function restoreDraftButtons() { 1246 const buttons = getDraftButtons(); 1247 $.each(buttons, function(index, button) { 1248 const initialState = saveDraftButtons[index] || saveDraftButtons[0]; 1249 $(button).text(initialState.text).attr('disabled', false); 1250 if (initialState.icon) { 1251 $(button).prepend('<i class="crm-i ' + saveDraftButtons[index].icon + '" role="img" aria-hidden="true"></i> '); 1252 } 1253 }); 1254 } 1255 this.getFieldData = () => { 1256 return data.extra.fields; 1257 }; 1258 this.identifyTokens = (message) => { 1259 if (typeof message !== 'string') { 1260 return null; 1261 } 1262 const tokens = new Set(message.match(/\[[a-zA-Z0-9_]+\.[0-9]+\.[^\]]+\]/g)); 1263 1264 return tokens.size ? tokens : null; 1265 }; 1266 1267 this.getTokenValues = (tokens) => { 1268 const values = {}; 1269 1270 tokens.forEach((token) => { 1271 const parts = token.slice(1, -1).split('.'); 1272 const entity = parts[0]; 1273 const index = parts[1]; 1274 const fieldName = parts.slice(2).join('.'); 1275 if (!data || !data[entity] || !data[entity][index] || !data[entity][index].fields || (data[entity][index].fields[fieldName] === undefined) ) { 1276 values[token] = ''; 1277 } 1278 else { 1279 values[token] = data[entity][index].fields[fieldName]; 1280 } 1281 }); 1282 1283 return values; 1284 }; 1285 1286 this.replaceTokens = (message) => { 1287 const tokens = this.identifyTokens(message); 1288 const tokenValues = this.getTokenValues(tokens); 1289 tokens.forEach((token) => message = message.replaceAll(token, tokenValues[token])); 1290 return message; 1291 }; 1292 } 1293 }); 1294 1295angular.module('af').directive('afIf', function($compile, $animate, $parse) { 1296 return { 1297 multiElement: true, 1298 transclude: 'element', 1299 priority: 601, 1300 terminal: true, 1301 restrict: 'A', 1302 require: ['^^afForm'], 1303 $$tlb: true, 1304 link: function($scope, $element, $attr, ctrl, $transclude) { 1305 let block, childScope, previousElements; 1306 1307 function watcher() { 1308 const conditions = $parse($attr.afIf)(); 1309 return ctrl[0].checkConditions(conditions); 1310 } 1311 1312 $scope.$watch(watcher, function(value) { 1313 if (value) { 1314 if (!childScope) { 1315 $transclude(function(clone, newScope) { 1316 childScope = newScope; 1317 clone[clone.length++] = $compile.$$createComment('end afIf', $attr.afIf); 1318 block = { 1319 clone: clone 1320 }; 1321 $animate.enter(clone, $element.parent(), $element); 1322 }); 1323 } 1324 } else { 1325 if (previousElements) { 1326 previousElements.remove(); 1327 previousElements = null; 1328 } 1329 if (childScope) { 1330 childScope.$broadcast('afIfDestroy'); 1331 childScope.$destroy(); 1332 childScope = null; 1333 } 1334 if (block) { 1335 previousElements = getBlockNodes(block.clone); 1336 $animate.leave(previousElements).done(function(response) { 1337 if (response !== false) previousElements = null; 1338 }); 1339 block = null; 1340 } 1341 } 1342 }); 1343 } 1344 }; 1345 }); 1346 1347 /** 1348 * Return the DOM siblings between the first and last node in the given array. 1349 * @param {Array} array like object 1350 * @returns {Array} the inputted object or a jqLite collection containing the nodes 1351 */
1352 function getBlockNodes(nodes) { 1353 var node = nodes[0]; 1354 var endNode = nodes[nodes.length - 1]; 1355 var blockNodes; 1356 1357 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) { 1358 if (blockNodes || nodes[i] !== node) { 1359 if (!blockNodes) { 1360 blockNodes = $(slice.call(nodes, 0, i)); 1361 } 1362 blockNodes.push(node); 1363 } 1364 } 1365 1366 return blockNodes || nodes; 1367 } 1368 1369 1370angular.module('af') 1371 .directive('afJoin', function() { 1372 return { 1373 restrict: 'A', 1374 require: ['afJoin', '^^afFieldset', '?^^afRepeatItem'], 1375 bindToController: { 1376 entity: '@afJoin', 1377 }, 1378 link: function($scope, $el, $attr, ctrls) { 1379 const self = ctrls[0]; 1380 self.afFieldset = ctrls[1]; 1381 self.repeatItem = ctrls[2]; 1382 self.offset = self.afFieldset.getJoinOffset($attr.afJoin); 1383 }, 1384 controller: function($scope) { 1385 const self = this; 1386 this.getEntityType = function() { 1387 return this.entity; 1388 }; 1389 this.getData = function() { 1390 let data, fieldsetData; 1391 if (self.repeatItem) { 1392 data = self.repeatItem.item; 1393 } else { 1394 fieldsetData = self.afFieldset.getData(); 1395 if (!fieldsetData.length) { 1396 fieldsetData.push({fields: {}, joins: {}}); 1397 } 1398 data = fieldsetData[0]; 1399 } 1400 if (!data.joins) { 1401 data.joins = {}; 1402 } 1403 if (!data.joins[self.entity]) { 1404 data.joins[self.entity] = []; 1405 } 1406 return data.joins[self.entity]; 1407 }; 1408 this.addRepeatItem = () => { 1409 this.getData().push({}); 1410 }; 1411 1412 this.getFieldData = function() { 1413 const data = this.getData(); 1414 if (!data[this.offset] || (Array.isArray(data[this.offset]) && !data[this.offset].length)) { 1415 data[this.offset] = {}; 1416 } 1417 return data[this.offset]; 1418 }; 1419 } 1420 }; 1421 }); 1422})(angular, CRM.$, CRM._); 1423 1424(function (CRM, angular) { 1425 1426 1427 class AfMarkup extends HTMLElement { 1428 1429 /* jshint ignore:start */ 1430 static observedAttributes = ['markup']; 1431 /* jshint ignore:end */ 1432 1433 connectedCallback() { 1434 this.afForm = this.closest('af-form'); 1435 setTimeout(() => this.render()); 1436 } 1437 1438 attributeChangedCallback() { 1439 this.render(); 1440 } 1441 1442 render() { 1443 let markup = this.markup; 1444 if (this.afForm) { 1445 markup = this.replaceTokensWithElements(markup); 1446 } 1447 this.innerHTML = markup; 1448 } 1449 1450 get markup() { 1451 return this.getAttribute('markup'); 1452 } 1453 1454 set markup(content) { 1455 this.setAttribute('markup', content); 1456 } 1457 1458 replaceTokensWithElements(markup) { 1459 const tokens = new Set(markup.match(/\[[a-zA-Z0-9_]+\.[0-9]+\.[^\]]+\]/g)); 1460 tokens?.forEach((token) => markup = markup.replaceAll(token, `<af-token expression="${token}"></af-token>`)); 1461 return markup; 1462 } 1463 } 1464 1465 customElements.define('af-markup', AfMarkup); 1466 1467})(CRM, angular); 1468 1469(function(angular, $, _) { 1470 angular.module('af') 1471 .directive('afRepeat', function() { 1472 return { 1473 restrict: 'A', 1474 require: ['?afFieldset', '?afJoin'], 1475 transclude: true, 1476 scope: { 1477 min: '=', 1478 max: '=', 1479 afRepeatDefault: '=', 1480 addLabel: '@afRepeat', 1481 addIcon: '@', 1482 copyLabel: '@afCopy', 1483 copyIcon: '@' 1484 }, 1485 templateUrl: '~/af/afRepeat.html', 1486 link: function($scope, $el, $attr, ctrls) { 1487 $scope.afFieldset = ctrls[0]; 1488 $scope.afJoin = ctrls[1]; 1489 $scope.element = $el; 1490 }, 1491 controller: function($scope) { 1492 1493 this.$onInit = () => { 1494 $scope.$evalAsync(() => { 1495 const data = getEntityController().getData(); 1496 let defaultCount = $scope.afRepeatDefault; 1497 if (defaultCount === undefined || defaultCount === '' || isNaN(defaultCount)) { 1498 defaultCount = 1; 1499 } 1500 if ($scope.min !== undefined && $scope.min !== '' && defaultCount < $scope.min) { 1501 defaultCount = $scope.min; 1502 } 1503 while (data.length < defaultCount) { 1504 getEntityController().addRepeatItem(); 1505 } 1506 }); 1507 }; 1508 1509 this.getItems = $scope.getItems = () => { 1510 return getEntityController().getData(); 1511 }; 1512 1513 function getRepeatType() { 1514 return $scope.afJoin ? 'join' : 'fieldset'; 1515 } 1516 this.getRepeatType = getRepeatType; 1517 1518 function getEntityController() { 1519 return $scope.afJoin || $scope.afFieldset; 1520 } 1521 this.getEntityController = getEntityController; 1522 1523 $scope.addItem = function() { 1524 getEntityController().addRepeatItem(); 1525 }; 1526 1527 $scope.copyItem = function() { 1528 const data = $scope.getItems(); 1529 const last = data[data.length - 1]; 1530 data.push(getRepeatType() === 'join' ? angular.copy(last) : {fields: angular.copy(last.fields)}); 1531 }; 1532 1533 $scope.removeItem = function(index) { 1534 $scope.getItems().splice(index, 1); 1535 }; 1536 1537 $scope.canAdd = function() { 1538 return !$scope.max || $scope.getItems().length < $scope.max; 1539 }; 1540 1541 $scope.canRemove = function() { 1542 return !$scope.min || $scope.getItems().length > $scope.min; 1543 }; 1544 } 1545 }; 1546 }) 1547 .directive('afRepeatItem', function() { 1548 return { 1549 restrict: 'A', 1550 require: { 1551 afRepeat: '^^', 1552 outerRepeatItem: '?^^afRepeatItem' 1553 }, 1554 bindToController: { 1555 item: '=afRepeatItem', 1556 repeatIndex: '=' 1557 }, 1558 controller: function() { 1559 this.getFieldData = function() { 1560 return this.afRepeat.getRepeatType() === 'join' ? this.item : this.item.fields; 1561 }; 1562 1563 this.getEntityType = function() { 1564 return this.afRepeat.getEntityController().getEntityType(); 1565 }; 1566 } 1567 }; 1568 }); 1569 1570 angular.module('af').component('afSearchParamSets', { 1571 require: { 1572 afFieldset: '^^' 1573 }, 1574 templateUrl: '~/af/afSearchParamSets.html', 1575 controller: function($scope, $element, crmApi4, $window, $location) { 1576 const ts = $scope.ts = CRM.ts('org.civicrm.afform'); 1577 this.savedSets = []; 1578 1579 this.$onInit = () => { 1580 this.formName = this.afFieldset.getFormName(); 1581 this.saveDialog.reset(); 1582 1583 this.fetchSearchParamSets(); 1584 }; 1585 1586 this.fetchSearchParamSets = () => crmApi4('SearchParamSet', 'get', { 1587 select: ['label', 'filters', 'columns', 'created_by.display_name', 'created_date'], 1588 where: [['afform_name', '=', this.formName]], 1589 orderBy: {'label': 'ASC'} 1590 }) 1591 .then((results) => this.savedSets = results.map((paramSet) => { 1592 const created = new Date(paramSet.created_date); 1593 const createdDate = created.toLocaleDateString(); 1594 const isToday = createdDate === (new Date()).toLocaleDateString(); 1595 paramSet.created_date_or_time = isToday ? ts('%1 today', {1: created.toLocaleTimeString()}) : createdDate; 1596 return paramSet; 1597 })) 1598 .catch(() => this.savedSets = [{id: '', label: ts('Error fetching Saved Searches')}]); 1599 1600 this.applySearchParamSet = (id) => $window.location.hash = `#?_s=${id ? id : 0}`; 1601
1602 this.getSetSearchParamSet = (value) => { 1603 if (value !== undefined) { 1604 this.applySearchParamSet(value); 1605 return ''; 1606 } 1607 const selected = this.getSelectedParamSet(); 1608 return (selected && selected.id) ? `${selected.id}` : ''; 1609 }; 1610 1611 this.getSelectedParamSet = () => { 1612 const hashParams = new URLSearchParams($window.location.hash.slice(1)); 1613 const urlValue = parseInt(hashParams.get('_s')); 1614 return this.savedSets.find((searchParamSet) => searchParamSet.id === urlValue); 1615 }; 1616 1617 this.getCurrentParams = () => { 1618 if (!this.afFieldset) { 1619 return {}; 1620 } 1621 const params = {}; 1622 1623 params.filters = this.afFieldset.getFilterValues(); 1624 1625 params.columns = {}; 1626 $element.closest('[af-fieldset]').find('crm-search-display-table').each((i, $table) => { 1627 const tableCtrl = angular.element($table).controller('crmSearchDisplayTable'); 1628 if (!tableCtrl) { 1629 return; 1630 } 1631 const columns = tableCtrl.getToggledColumns(); 1632 if (!Object.keys(columns).length) { 1633 return; 1634 } 1635 params.columns[tableCtrl.getSearchDisplayKey()] = columns; 1636 }); 1637 return params; 1638 }; 1639 1640 this.renderSearchParamSetDescription = (paramSet) => ts('Created %1 by %2', { 1641 1: paramSet.created_date_or_time, 1642 2: (paramSet['created_by.display_name'] ? paramSet['created_by.display_name'] : 'UNKNOWN') 1643 }); 1644 1645 this.renderSearchParamSetDetails = (paramSet) => { 1646 const rendered = {}; 1647 1648 if (paramSet.filters) { 1649 const fieldMeta = this.afFieldset.getFieldMeta(); 1650 1651 Object.keys(paramSet.filters).forEach((key) => { 1652 const defn = fieldMeta[key]; 1653 const rawValue = paramSet.filters[key]; 1654 1655 const formatValue = (v) => { 1656 if (v && v.map) { 1657 return v.map(formatValue).join(' OR '); 1658 } 1659 if (defn.options && defn.options.length) { 1660 const selected = defn.options.find((o) => o.id == v); 1661 if (selected) { 1662 return selected.label; 1663 } 1664 } 1665 switch (typeof v) { 1666 case 'number': 1667 case 'string': 1668 return v; 1669 } 1670 return Object.entries(v) 1671 .filter((e) => e[1]) 1672 .map((e) => `${e[0]} ${e[1]}`) 1673 .join(' AND '); 1674 }; 1675 rendered[defn.label] = formatValue(rawValue); 1676 }); 1677 } 1678 1679 if (paramSet.columns) { 1680 const displayKeys = Object.keys(paramSet.columns); 1681 if (displayKeys.length > 1) 1682 displayKeys.forEach((displayKey) => { 1683 const label = ts('%1 columns', {1: displayKey}); 1684 const columns = Object.values(paramSet.columns[displayKey]).join(', '); 1685 rendered[label] = columns; 1686 }); 1687 else if (displayKeys.length === 1) { 1688 const displayKey = displayKeys[0]; 1689 rendered[ts('Columns')] = Object.values(paramSet.columns[displayKey]).join(', '); 1690 } 1691 } 1692 1693 return rendered; 1694 }; 1695 1696 this.saveDialog = { 1697 open: () => $element[0].querySelector('dialog.af-search-param-set-new').showModal(), 1698 close: () => $element[0].querySelector('dialog.af-search-param-set-new').close(), 1699 reset: () => { 1700 this.saveDialog.label = ''; 1701 this.saveDialog.inProgress = false; 1702 }, 1703 canOpen: () => { 1704 const params = this.getCurrentParams(); 1705 if (Object.keys(params.filters).length) { 1706 return true; 1707 } 1708 if (Object.keys(params.columns).length) { 1709 return true; 1710 } 1711 return false; 1712 }, 1713 canSave: () => !this.saveDialog.inProgress && this.saveDialog.label && Object.keys(this.getCurrentParams()).length, 1714 save: () => { 1715 if (!this.saveDialog.canSave()) { 1716 return; 1717 } 1718 1719 this.saveDialog.inProgress = true; 1720
1721 const current = this.getCurrentParams(); 1722 1723 const values = { 1724 afform_name: this.formName, 1725 filters: current.filters, 1726 columns: current.columns, 1727 label: this.saveDialog.label, 1728 created_by: CRM.config.cid, 1729 }; 1730 1731 let newId = null; 1732 1733 return crmApi4('SearchParamSet', 'create', { 1734 values: values 1735 }) 1736 .then((result) => newId = (result && result[0]) ? result[0].id : null) 1737 .then(() => this.fetchSearchParamSets()) 1738 .then(() => this.applySearchParamSet(newId)) 1739 .then(() => this.saveDialog.close()) 1740 .catch((error) => { 1741 const errorMessage = (error && error.error_message) ? error.error_message : ts('Unknown error'); 1742 CRM.alert(ts('Error saving filters: %1', {1: errorMessage})); 1743 }) 1744 .finally(() => this.saveDialog.reset()); 1745 } 1746 }; 1747 1748 this.manageDialog = { 1749 open: () => $element[0].querySelector('dialog.af-search-param-sets-manage').showModal(), 1750 close: () => $element[0].querySelector('dialog.af-search-param-sets-manage').close(), 1751 deleteItem: (searchParamSet) => { 1752 crmApi4('SearchParamSet', 'delete', { 1753 where: [['id', '=', searchParamSet.id]] 1754 }) 1755 .then(() => this.fetchSearchParamSets()); 1756 }, 1757 }; 1758 1759 this.updateDialog = { 1760 id: null, 1761 label: '', 1762 open: () => { 1763 const selected = this.getSelectedParamSet(); 1764 this.updateDialog.id = selected.id; 1765 this.updateDialog.oldLabel = selected.label; 1766 this.updateDialog.newLabel = selected.label; 1767 this.updateDialog.valueComparison = this.updateDialog.getValueComparison(); 1768 $element[0].querySelector('dialog.af-search-param-set-update').showModal(); 1769 }, 1770 close: () => $element[0].querySelector('dialog.af-search-param-set-update').close(), 1771 inProgress: false, 1772 canOpen: () => { 1773 const selected = this.getSelectedParamSet(); 1774 if (!selected) { 1775 return false; 1776 } 1777 const newParams = this.getCurrentParams(); 1778 if (JSON.stringify(selected.filters) !== JSON.stringify(newParams.filters)) { 1779 return true; 1780 } 1781 if (JSON.stringify(selected.columns) !== JSON.stringify(newParams.columns)) { 1782 return true; 1783 } 1784 return false; 1785 }, 1786 canUpdate: () => this.updateDialog.id && this.updateDialog.newLabel, 1787 update: () => { 1788 if (!this.updateDialog.canUpdate()) { 1789 return; 1790 } 1791 1792 this.updateDialog.inProgress = true; 1793 1794 const newParams = this.getCurrentParams(); 1795 1796 crmApi4('SearchParamSet', 'update', { 1797 where: [['id', '=', this.updateDialog.id]], 1798 values: { 1799 label: this.updateDialog.newLabel, 1800 filters: newParams.filters, 1801 columns: newParams.columns 1802 } 1803 }) 1804 .then(() => this.fetchSearchParamSets()) 1805 .then(() => this.updateDialog.inProgress = false) 1806 .then(() => this.updateDialog.close()); 1807 }, 1808 getValueComparison: () => { 1809 const current = this.getSelectedParamSet(); 1810 const oldValues = this.renderSearchParamSetDetails(current); 1811 const newValues = this.renderSearchParamSetDetails(this.getCurrentParams()); 1812 1813 const oldKeys = Object.keys(oldValues); 1814 const newKeys = Object.keys(newValues); 1815 1816 const comparison = {}; 1817 oldKeys.filter((key) => !newKeys.includes(key)).forEach((key) => comparison[key] = [oldValues[key], ts('[none]')]); 1818 oldKeys.filter((key) => newKeys.includes(key)).forEach((key) => { 1819 if (newValues[key] === oldValues[key]) { 1820 comparison[key] = [oldValues[key]]; 1821 return; 1822 } 1823 comparison[key] = [oldValues[key], newValues[key]]; 1824 }); 1825 newKeys.filter((key) => !oldKeys.includes(key)).forEach((key) => comparison[key] = [ts('[none]'), newValues[key]]); 1826 1827 return comparison; 1828 } 1829 }; 1830 } 1831 }); 1832 1833 1834 let tabNumber = 0; 1835 1836 angular.module('af').component('afTabset', { 1837 templateUrl: '~/af/afTabset.html', 1838 transclude: true, 1839 require: { 1840 afFormCtrl: '?^^afForm', 1841 }, 1842 bindings: { 1843 urlArg: '@', 1844 selectedTab: '=?', 1845 rememberSelection: '<', 1846 }, 1847 controller: function($scope, $element, $timeout) { 1848 this.tabs = []; 1849 1850 this.$onInit = function() { 1851 $element.addClass('crm-tabset'); 1852 1853 if (this.urlArg) { 1854 $scope.$bindToRoute({ 1855 expr: '$ctrl.selectedTab', 1856 param: this.urlArg, 1857 format: 'raw' 1858 }); 1859 } 1860 1861 $timeout(() => { 1862 if (!this.selectedTab && this.rememberSelection) { 1863 this.selectedTab = CRM.cache.get(this.getCacheKey()); 1864 } 1865 if (!this.selectedTab && this.tabs.length) { 1866 this.selectedTab = this.tabs[0].name; 1867 } 1868 }); 1869 1870 if (this.rememberSelection) { 1871 $scope.$watch('$ctrl.selectedTab', (newTab) => { 1872 if (newTab) { 1873 CRM.cache.set(this.getCacheKey(), newTab); 1874 } 1875 }); 1876 } 1877 }; 1878 1879 this.addTab = (tab) => { 1880 this.tabs.push(tab); 1881 }; 1882 1883 this.selectTab = (tabName) => { 1884 this.selectedTab = tabName; 1885 }; 1886 1887 this.getFormName = () => this.afFormCtrl?.getFormMeta().name ?? $scope.$parent.meta.name; 1888 1889 this.getCacheKey = () => this.getFormName() + 'SelectedTab'; 1890 } 1891 }); 1892 1893 angular.module('af').directive('afTab', function() { 1894 return { 1895 restrict: 'E', 1896 require: '^afTabset', 1897 scope: { 1898 title: '@', 1899 icon: '@', 1900 count: '@', 1901 name: '@', 1902 }, 1903 transclude: true, 1904 template: '<div ng-transclude role="tabpanel" ng-show="name === afTabsetCtrl.selectedTab" class="ng-hide"></div>', 1905 link: function (scope, element, attrs, afTabsetCtrl) { 1906 scope.name = scope.name || 'tab' + tabNumber++; 1907 scope.afTabsetCtrl = afTabsetCtrl; 1908 afTabsetCtrl.addTab(scope); 1909 } 1910 }; 1911 }); 1912 1913 angular.module('af').directive('afTitle', function() { 1914 return { 1915 restrict: 'A', 1916 bindToController: { 1917 title: '@afTitle' 1918 }, 1919 controller: function($scope, $element) { 1920 1921 $scope.$watch(() => this.title, (text) => { 1922 let tag = 'h4'; 1923 if ($element.is('fieldset')) { 1924 tag = 'legend'; 1925 } 1926 if ($element.is('details')) { 1927 tag = 'summary'; 1928 } 1929 let $title = $element.children(tag + '.af-title'); 1930 if (!$title.length) { 1931 $title = $(`<${tag} class="af-title" />`).prependTo($element); 1932 } 1933 $title.text(text); 1934 }); 1935 } 1936 }; 1937 }); 1938})(angular, CRM.$, CRM._); 1939 1940(function (CRM, angular) { 1941 1942 1943 class AfToken extends HTMLElement { 1944 1945 connectedCallback() { 1946 this.afForm = this.closest('af-form'); 1947 if (!this.afForm) { 1948 throw new Error('af-token should be placed within an af-form'); 1949 } 1950 1951 this.render(); 1952 this.registerListener(); 1953 } 1954 1955 disconnectedCallback() { 1956 this.removeListener(); 1957 } 1958 1959 render() { 1960 this.innerText = this.evaluate(this.expression); 1961 } 1962 1963 registerListener() { 1964 this.afForm?.addEventListener('change', () => this.render()); 1965 } 1966 1967 removeListener() { 1968 this.afForm?.removeEventListener('change', () => this.render()); 1969 } 1970 1971 get expression() { 1972 return this.getAttribute('expression'); 1973 } 1974 1975 get afFormCtrl() { 1976 return angular.element(this.afForm).controller('afForm'); 1977 } 1978 1979 evaluate(expression) { 1980 return this.afFormCtrl ? this.afFormCtrl.replaceTokens(expression) : ''; 1981 } 1982 1983 } 1984 1985 customElements.define('af-token', AfToken); 1986 1987})(CRM, angular); 1988(function(angular, $, _) { 1989 "use strict"; 1990 angular.module('af').component('afLocationInput', { 1991 require: { 1992 ngModel: 'ngModel', 1993 }, 1994 templateUrl: '~/af/fields/afLocationInput.html', 1995 controller: function($scope, $element) { 1996 const ts = $scope.ts = CRM.ts('org.civicrm.afform'); 1997 1998 this.values = { 1999 distance: null, 2000 distance_unit: null, 2001 address: '', 2002 }; 2003 2004 this.$onInit = () => { 2005 $scope.$watchCollection('$ctrl.values', (values) => { 2006 if (values.address && values.distance_unit && values.distance !== null) { 2007 this.ngModel.$setViewValue(values); 2008 } else { 2009 this.ngModel.$setViewValue(null); 2010 } 2011 }); 2012 this.ngModel.$render = () => { 2013 this.values = this.ngModel.$viewValue || this.values; 2014 }; 2015 2016 }; 2017 2018 } 2019 }); 2020 2021angular.module('afCore', CRM.angRequires('afCore')); 2022 angular.module('afCore').service('afCoreDirective', function($location, crmApi4, crmStatus, crmUiAlert) { 2023 return function(camelName, meta, d) { 2024 d.restrict = 'E'; 2025 d.scope = {}; 2026 d.scope.options = '='; 2027 d.link = { 2028 pre: function($scope, $el, $attr) { 2029 $scope.ts = CRM.ts(camelName); 2030 $scope.meta = meta; 2031 $scope.crmApi4 = crmApi4; 2032 $scope.crmStatus = crmStatus; 2033 $scope.crmUiAlert = crmUiAlert; 2034 $scope.crmUrl = CRM.url; 2035 2036 $el.addClass('afform-directive'); 2037 const dialog = $el.closest('.ui-dialog-content'); 2038 if (!dialog.length) {
2039 $scope.routeParams = $location.search(); 2040 $scope.$watch(() => $location.search(), params => $scope.routeParams = params); 2041 } else { 2042 $scope.routeParams = {}; 2043 if (typeof dialog.data('urlHash') === 'string' && dialog.data('urlHash').includes('?')) { 2044 const searchParams = new URLSearchParams(dialog.data('urlHash').split('?')[1]); 2045 searchParams.forEach((value, key) => $scope.routeParams[key] = value); 2046 } 2047 } 2048 2049 $scope.$parent.afformTitle = meta.title; 2050 $scope.addTitle = function(addition) { 2051 $scope.$parent.afformTitle = addition + ' ' + meta.title; 2052 }; 2053 2054 $scope.checkLinkPerm = function(permissionName, createdId) { 2055 if (permissionName.startsWith('manage own') && CRM.config.cid !== createdId) { 2056 permissionName = permissionName.replace('manage own', 'administer'); 2057 } 2058 return CRM.checkPerm(permissionName); 2059 }; 2060 2061 } 2062 }; 2063 return d; 2064 }; 2065 }); 2066 2067angular.module('afCore').directive('afApi3Ctrl', function() { 2068 return { 2069 restrict: 'EA', 2070 scope: { 2071 afApi3Ctrl: '=', 2072 afApi3: '@', 2073 afApi3Refresh: '@', 2074 onRefresh: '@' 2075 }, 2076 controllerAs: 'afApi3Ctrl', 2077 controller: function($scope, $parse, crmThrottle, crmApi) { 2078 var ctrl = this; 2079 var parts = $parse($scope.afApi3)($scope.$parent); 2080 ctrl.entity = parts[0]; 2081 ctrl.action = parts[1]; 2082 ctrl.params = parts[2]; 2083 ctrl.result = {}; 2084 ctrl.loading = ctrl.firstLoad = true; 2085 2086 ctrl.refresh = function refresh() { 2087 ctrl.loading = true; 2088 crmThrottle(function () { 2089 return crmApi(ctrl.entity, ctrl.action, ctrl.params) 2090 .then(function (response) { 2091 ctrl.result = response; 2092 ctrl.loading = ctrl.firstLoad = false; 2093 if ($scope.onRefresh) { 2094 $scope.$parent.$eval($scope.onRefresh, ctrl); 2095 } 2096 }); 2097 }); 2098 }; 2099 2100 $scope.afApi3Ctrl = this; 2101 2102 var mode = $scope.afApi3Refresh ? $scope.afApi3Refresh : 'auto'; 2103 switch (mode) { 2104 case 'auto': $scope.$watchCollection('afApi3Ctrl.params', ctrl.refresh); break; 2105 case 'init': ctrl.refresh(); break; 2106 case 'manual': break; 2107 default: throw 'Unrecognized refresh mode: '+ mode; 2108 } 2109 } 2110 }; 2111 }); 2112 2113 2114angular.module('afCore').directive('afApi4Action', function($parse, crmStatus, crmApi4) { 2115 return { 2116 restrict: 'A', 2117 scope: { 2118 afApi4Action: '@', 2119 afApi4StartMsg: '=', 2120 afApi4ErrorMsg: '=', 2121 afApi4SuccessMsg: '=', 2122 afApi4Success: '@', 2123 onError: '@' 2124 }, 2125 link: function($scope, $el, $attr) { 2126 const ts = CRM.ts(null); 2127 2128 function running(x) {$el.toggleClass('af-api4-action-running', x).toggleClass('af-api4-action-idle', !x);} 2129 2130 running(false); 2131 2132 $el.click(function(){ 2133 const parts = $parse($scope.afApi4Action)($scope.$parent); 2134 const msgs = { 2135 start: $scope.afApi4StartMsg || ts('Submitting...'), 2136 success: $scope.afApi4SuccessMsg, 2137 error: $scope.afApi4ErrorMsg 2138 }; 2139 running(true); 2140 crmStatus(msgs, crmApi4(parts[0], parts[1], parts[2])) 2141 .finally(function(){running(false);}) 2142 .then(function(response){$scope.$parent.$eval($scope.afApi4Success, {response: response});}) 2143 .catch(function(error){$scope.$parent.$eval($scope.onError, {error: error});}); 2144 }); 2145 } 2146 }; 2147 }); 2148 2149 2150angular.module('afCore').directive('afApi4Ctrl', function() { 2151 return { 2152 restrict: 'EA', 2153 scope: { 2154 afApi4Ctrl: '=', 2155 afApi4: '@', 2156 afApi4Refresh: '@', 2157 onRefresh: '@' 2158 }, 2159 controllerAs: 'afApi4Ctrl', 2160 controller: function($scope, $parse, crmThrottle, crmApi4) { 2161 var ctrl = this; 2162 var parts = $parse($scope.afApi4)($scope.$parent); 2163 ctrl.entity = parts[0]; 2164 ctrl.action = parts[1]; 2165 ctrl.params = parts[2]; 2166 ctrl.index = parts[3]; 2167 ctrl.result = {}; 2168 ctrl.loading = ctrl.firstLoad = true; 2169 2170 ctrl.refresh = function refresh() { 2171 ctrl.loading = true; 2172 crmThrottle(function () { 2173 return crmApi4(ctrl.entity, ctrl.action, ctrl.params, ctrl.index) 2174 .then(function (response) { 2175 ctrl.result = response; 2176 ctrl.loading = ctrl.firstLoad = false;
2177 if ($scope.onRefresh) { 2178 $scope.$parent.$eval($scope.onRefresh, ctrl); 2179 } 2180 }); 2181 }); 2182 }; 2183 2184 $scope.afApi4Ctrl = this; 2185 2186 var mode = $scope.afApi4Refresh ? $scope.afApi4Refresh : 'auto'; 2187 switch (mode) { 2188 case 'auto': 2189 $scope.$watchCollection('afApi4Ctrl.params', ctrl.refresh, true); 2190 $scope.$watch('afApi4Ctrl.index', ctrl.refresh, true); 2191 $scope.$watch('afApi4Ctrl.entity', ctrl.refresh, true); 2192 $scope.$watch('afApi4Ctrl.action', ctrl.refresh, true); 2193 break; 2194 case 'init': ctrl.refresh(); break; 2195 case 'manual': break; 2196 default: throw 'Unrecognized refresh mode: '+ mode; 2197 } 2198 } 2199 }; 2200 }); 2201 2202 2203angular.module('afsearchUpcomingPlaygroups', CRM.angRequires('afsearchUpcomingPlaygroups')); 2204 angular.module('afsearchUpcomingPlaygroups').directive('afsearchUpcomingPlaygroups', function(afCoreDirective) { 2205 return afCoreDirective("afsearchUpcomingPlaygroups", {"title":"upcoming playgroups","redirect":null,"autosave_draft":false,"confirmation_type":"redirect_to_url","confirmation_message":null,"name":"afsearchUpcomingPlaygroups"}, { 2206 templateUrl: "~/afsearchUpcomingPlaygroups/afsearchUpcomingPlaygroups.aff.html" 2207 }); 2208 }); 2209})(angular, CRM.$, CRM._); 2210 2211
vendor: 23,361 bytes, lines 2212-2217
2212/* 2213 angular-file-upload v2.6.1 2214 https://github.com/nervgh/angular-file-upload 2215*/ 2216 2217!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports["angular-file-upload"]=t():e["angular-file-upload"]=t()}(this,function(){return function(e){function t(n){if(o[n])return o[n].exports;var r=o[n]={exports:{},id:n,loaded:!1};return e[n].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var o={};return t.m=e,t.c=o,t.p="",t(0)}([function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}var r=o(1),i=n(r),s=o(2),a=n(s),u=o(3),l=n(u),p=o(4),c=n(p),f=o(5),d=n(f),h=o(6),y=n(h),m=o(7),v=n(m),_=o(8),g=n(_),b=o(9),F=n(b),O=o(10),C=n(O),T=o(11),I=n(T),w=o(12),A=n(w),U=o(13),x=n(U);angular.module(i.default.name,[]).value("fileUploaderOptions",a.default).factory("FileUploader",l.default).factory("FileLikeObject",c.default).factory("FileItem",d.default).factory("FileDirective",y.default).factory("FileSelect",v.default).factory("FileDrop",F.default).factory("FileOver",C.default).factory("Pipeline",g.default).directive("nvFileSelect",I.default).directive("nvFileDrop",A.default).directive("nvFileOver",x.default).run(["FileUploader","FileLikeObject","FileItem","FileDirective","FileSelect","FileDrop","FileOver","Pipeline",function(e,t,o,n,r,i,s,a){e.FileLikeObject=t,e.FileItem=o,e.FileDirective=n,e.FileSelect=r,e.FileDrop=i,e.FileOver=s,e.Pipeline=a}])},function(e,t){e.exports={name:"angularFileUpload"}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={url:"/",alias:"file",headers:{},queue:[],progress:0,autoUpload:!1,removeAfterUpload:!1,method:"POST",filters:[],formData:[],queueLimit:Number.MAX_VALUE,withCredentials:!1,disableMultipart:!1}},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t,o,n,i,a,u,g){var b=n.File,F=n.FormData,O=function(){function n(t){r(this,n);var o=p(e);c(this,o,t,{isUploading:!1,_nextIndex:0,_directives:{select:[],drop:[],over:[]}}),this.filters.unshift({name:"queueLimit",fn:this._queueLimitFilter}),this.filters.unshift({name:"folder",fn:this._folderFilter})}return n.prototype.addToQueue=function(e,t,o){var n=this,r=this.isArrayLikeObject(e)?Array.prototype.slice.call(e):[e],i=this._getFilters(o),l=this.queue.length,p=[],c=function e(){var o=r.shift();if(v(o))return f();var l=n.isFile(o)?o:new a(o),c=n._convertFiltersToPipes(i),d=new g(c),h=function(t){var o=t.pipe.originalFilter,r=s(t.args,2),i=r[0],a=r[1];n._onWhenAddingFileFailed(i,o,a),e()},y=function(t,o){var r=new u(n,t,o);p.push(r),n.queue.push(r),n._onAfterAddingFile(r),e()};d.onThrown=h,d.onSuccessful=y,d.exec(l,t)},f=function(){n.queue.length!==l&&(n._onAfterAddingAll(p),n.progress=n._getTotalProgress()),n._render(),n.autoUpload&&n.uploadAll()};c()},n.prototype.removeFromQueue=function(e){var t=this.getIndexOfItem(e),o=this.queue[t];o.isUploading&&o.cancel(),this.queue.splice(t,1),o._destroy(),this.progress=this._getTotalProgress()},n.prototype.clearQueue=function(){for(;this.queue.length;)this.queue[0].remove();this.progress=0},n.prototype.uploadItem=function(e){var t=this.getIndexOfItem(e),o=this.queue[t],n=this.isHTML5?"_xhrTransport":"_iframeTransport";o._prepareToUploading(),this.isUploading||(this._onBeforeUploadItem(o),o.isCancel||(o.isUploading=!0,this.isUploading=!0,this[n](o),this._render()))},n.prototype.cancelItem=function(e){var t=this,o=this.getIndexOfItem(e),n=this.queue[o],r=this.isHTML5?"_xhr":"_form";if(n)if(n.isCancel=!0,n.isUploading)n[r].abort();else{var s=[void 0,0,{}],a=function(){t._onCancelItem.apply(t,[n].concat(s)),t._onCompleteItem.apply(t,[n].concat(s))};i(a)}},n.prototype.uploadAll=function(){var e=this.getNotUploadedItems().filter(function(e){return!e.isUploading});e.length&&(f(e,function(e){return e._prepareToUploading()}),e[0].upload())},n.prototype.cancelAll=function(){var e=this.getNotUploadedItems();f(e,function(e){return e.cancel()})},n.prototype.isFile=function(e){return this.constructor.isFile(e)},n.prototype.isFileLikeObject=function(e){return this.constructor.isFileLikeObject(e)},n.prototype.isArrayLikeObject=function(e){return this.constructor.isArrayLikeObject(e)},n.prototype.getIndexOfItem=function(e){return h(e)?e:this.queue.indexOf(e)},n.prototype.getNotUploadedItems=function(){return this.queue.filter(function(e){return!e.isUploaded})},n.prototype.getReadyItems=function(){return this.queue.filter(function(e){return e.isReady&&!e.isUploading}).sort(function(e,t){return e.index-t.index})},n.prototype.destroy=function(){var e=this;f(this._directives,function(t){f(e._directives[t],function(e){e.destroy()})})},n.prototype.onAfterAddingAll=function(e){},n.prototype.onAfterAddingFile=function(e){},n.prototype.onWhenAddingFileFailed=function(e,t,o){},n.prototype.onBeforeUploadItem=function(e){},n.prototype.onProgressItem=function(e,t){},n.prototype.onProgressAll=function(e){},n.prototype.onSuccessItem=function(e,t,o,n){},n.prototype.onErrorItem=function(e,t,o,n){},n.prototype.onCancelItem=function(e,t,o,n){},n.prototype.onCompleteItem=function(e,t,o,n){},n.prototype.onTimeoutItem=function(e){},n.prototype.onCompleteAll=function(){},n.prototype._getTotalProgress=function(e){if(this.removeAfterUpload)return e||0;var t=this.getNotUploadedItems().length,o=t?this.queue.length-t:this.queue.length,n=100/this.queue.length,r=(e||0)*n/100;return Math.round(o*n+r)},n.prototype._getFilters=function(e){if(!e)return this.filters;if(m(e))return e;var t=e.match(/[^\s,]+/g);return this.filters.filter(function(e){return t.indexOf(e.name)!==-1})},n.prototype._convertFiltersToPipes=function(e){var t=this;return e.map(function(e){var o=l(t,e.fn);return o.isAsync=3===e.fn.length,o.originalFilter=e,o})},n.prototype._render=function(){t.$$phase||t.$apply()},n.prototype._folderFilter=function(e){return!(!e.size&&!e.type)},n.prototype._queueLimitFilter=function(){return this.queue.length<this.queueLimit},n.prototype._isSuccessCode=function(e){return e>=200&&e<300||304===e},n.prototype._transformResponse=function(e,t){var n=this._headersGetter(t);return f(o.defaults.transformResponse,function(t){e=t(e,n)}),e},n.prototype._parseHeaders=function(e){var t,o,n,r={};return e?(f(e.split("\n"),function(e){n=e.indexOf(":"),t=e.slice(0,n).trim().toLowerCase(),o=e.slice(n+1).trim(),t&&(r[t]=r[t]?r[t]+", "+o:o)}),r):r},n.prototype._headersGetter=function(e){return function(t){return t?e[t.toLowerCase()]||null:e}},n.prototype._xhrTransport=function(e){var t,o=this,n=e._xhr=new XMLHttpRequest;if(e.disableMultipart?t=e._file:(t=new F,f(e.formData,function(e){f(e,function(e,o){t.append(o,e)})}),t.append(e.alias,e._file,e.file.name)),"number"!=typeof e._file.size)throw new TypeError("The file specified is no longer valid");n.upload.onprogress=function(t){var n=Math.round(t.lengthComputable?100*t.loaded/t.total:0);o._onProgressItem(e,n)},n.onload=function(){var t=o._parseHeaders(n.getAllResponseHeaders()),r=o._transformResponse(n.response,t),i=o._isSuccessCode(n.status)?"Success":"Error",s="_on"+i+"Item";o[s](e,r,n.status,t),o._onCompleteItem(e,r,n.status,t)},n.onerror=function(){var t=o._parseHeaders(n.getAllResponseHeaders()),r=o._transformResponse(n.response,t);o._onErrorItem(e,r,n.status,t),o._onCompleteItem(e,r,n.status,t)},n.onabort=function(){var t=o._parseHeaders(n.getAllResponseHeaders()),r=o._transformResponse(n.response,t);o._onCancelItem(e,r,n.status,t),o._onCompleteItem(e,r,n.status,t)},n.ontimeout=function(t){var r=o._parseHeaders(n.getAllResponseHeaders()),i="Request Timeout.";o._onTimeoutItem(e),o._onCompleteItem(e,i,408,r)},n.open(e.method,e.url,!0),n.timeout=e.timeout||0,n.withCredentials=e.withCredentials,f(e.headers,function(e,t){n.setRequestHeader(t,e)}),n.send(t)},n.prototype._iframeTransport=function(e){var t=this,o=_('<form style="display: none;" />'),n=_('<iframe name="iframeTransport'+Date.now()+'">'),r=e._input,i=0,s=null,a=!1;e._form&&e._form.replaceWith(r),e._form=o,r.prop("name",e.alias),f(e.formData,function(e){f(e,function(e,t){var n=_('<input type="hidden" name="'+t+'" />');n.val(e),o.append(n)})}),o.prop({action:e.url,method:"POST",target:n.prop("name"),enctype:"multipart/form-data",encoding:"multipart/form-data"}),n.bind("load",function(){var o="",r=200;try{o=n[0].contentDocument.body.innerHTML}catch(e){r=500}if(s&&clearTimeout(s),s=null,a)return!1;var i={response:o,status:r,dummy:!0},u={},l=t._transformResponse(i.response,u);t._onSuccessItem(e,l,i.status,u),t._onCompleteItem(e,l,i.status,u)}),o.abort=function(){var i,s={status:0,dummy:!0},a={};n.unbind("load").prop("src","javascript:false;"),o.replaceWith(r),t._onCancelItem(e,i,s.status,a),t._onCompleteItem(e,i,s.status,a)},r.after(o),o.append(r).append(n),i=e.timeout||0,s=null,i&&(s=setTimeout(function(){a=!0,e.isCancel=!0,e.isUploading&&(n.unbind("load").prop("src","javascript:false;"),o.replaceWith(r));var i={},s="Request Timeout.";t._onTimeoutItem(e),t._onCompleteItem(e,s,408,i)},i)),o[0].submit()},n.prototype._onWhenAddingFileFailed=function(e,t,o){this.onWhenAddingFileFailed(e,t,o)},n.prototype._onAfterAddingFile=function(e){this.onAfterAddingFile(e)},n.prototype._onAfterAddingAll=function(e){this.onAfterAddingAll(e)},n.prototype._onBeforeUploadItem=function(e){e._onBeforeUpload(),this.onBeforeUploadItem(e)},n.prototype._onProgressItem=function(e,t){var o=this._getTotalProgress(t);this.progress=o,e._onProgress(t),this.onProgressItem(e,t),this.onProgressAll(o),this._render()},n.prototype._onSuccessItem=function(e,t,o,n){e._onSuccess(t,o,n),this.onSuccessItem(e,t,o,n)},n.prototype._onErrorItem=function(e,t,o,n){e._onError(t,o,n),this.onErrorItem(e,t,o,n)},n.prototype._onCancelItem=function(e,t,o,n){e._onCancel(t,o,n),this.onCancelItem(e,t,o,n)},n.prototype._onCompleteItem=function(e,t,o,n){e._onComplete(t,o,n),this.onCompleteItem(e,t,o,n);var r=this.getReadyItems()[0];return this.isUploading=!1,y(r)?void r.upload():(this.onCompleteAll(),this.progress=this._getTotalProgress(),void this._render())},n.prototype._onTimeoutItem=function(e){e._onTimeout(),this.onTimeoutItem(e)},n.isFile=function(e){return b&&e instanceof b},n.isFileLikeObject=function(e){return e instanceof a},n.isArrayLikeObject=function(e){return d(e)&&"length"in e},n.inherit=function(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.super_=t},n}();return O.prototype.isHTML5=!(!b||!F),O.isHTML5=O.prototype.isHTML5,O}Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t){var o=[],n=!0,r=!1,i=void 0;try{for(var s,a=e[Symbol.iterator]();!(n=(s=a.next()).done)&&(o.push(s.value),!t||o.length!==t);n=!0);}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}return o}return function(t,o){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,o);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();t.default=i;var a=o(1),u=(n(a),angular),l=u.bind,p=u.copy,c=u.extend,f=u.forEach,d=u.isObject,h=u.isNumber,y=u.isDefined,m=u.isArray,v=u.isUndefined,_=u.element;i.$inject=["fileUploaderOptions","$rootScope","$http","$window","$timeout","FileLikeObject","FileItem","Pipeline"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(){return function(){function e(t){r(this,e);var o=l(t),n=o?t.value:t,i=p(n)?"FakePath":"Object",s="_createFrom"+i;this[s](n,t)}return e.prototype._createFromFakePath=function(e,t){this.lastModifiedDate=null,this.size=null,this.type="like/"+e.slice(e.lastIndexOf(".")+1).toLowerCase(),this.name=e.slice(e.lastIndexOf("/")+e.lastIndexOf("\\")+2),this.input=t},e.prototype._createFromObject=function(e){this.lastModifiedDate=u(e.lastModifiedDate),this.size=e.size,this.type=e.type,this.name=e.name,this.input=e.input},e}()}Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var s=o(1),a=(n(s),angular),u=a.copy,l=a.isElement,p=a.isString},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){return function(){function o(e,n,i){r(this,o);var s=!!n.input,a=s?p(n.input):null,c=s?null:n;l(this,{url:e.url,alias:e.alias,headers:u(e.headers),formData:u(e.formData),removeAfterUpload:e.removeAfterUpload,withCredentials:e.withCredentials,disableMultipart:e.disableMultipart,method:e.method,timeout:e.timeout},i,{uploader:e,file:new t(n),isReady:!1,isUploading:!1,isUploaded:!1,isSuccess:!1,isCancel:!1,isError:!1,progress:0,index:null,_file:c,_input:a}),a&&this._replaceNode(a)}return o.prototype.upload=function(){try{this.uploader.uploadItem(this)}catch(t){var e=t.name+":"+t.message;this.uploader._onCompleteItem(this,e,t.code,[]),this.uploader._onErrorItem(this,e,t.code,[])}},o.prototype.cancel=function(){this.uploader.cancelItem(this)},o.prototype.remove=function(){this.uploader.removeFromQueue(this)},o.prototype.onBeforeUpload=function(){},o.prototype.onProgress=function(e){},o.prototype.onSuccess=function(e,t,o){},o.prototype.onError=function(e,t,o){},o.prototype.onCancel=function(e,t,o){},o.prototype.onComplete=function(e,t,o){},o.prototype.onTimeout=function(){},o.prototype._onBeforeUpload=function(){this.isReady=!0,this.isUploading=!1,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!1,this.isError=!1,this.progress=0,this.onBeforeUpload()},o.prototype._onProgress=function(e){this.progress=e,this.onProgress(e)},o.prototype._onSuccess=function(e,t,o){this.isReady=!1,this.isUploading=!1,this.isUploaded=!0,this.isSuccess=!0,this.isCancel=!1,this.isError=!1,this.progress=100,this.index=null,this.onSuccess(e,t,o)},o.prototype._onError=function(e,t,o){this.isReady=!1,this.isUploading=!1,this.isUploaded=!0,this.isSuccess=!1,this.isCancel=!1,this.isError=!0,this.progress=0,this.index=null,this.onError(e,t,o)},o.prototype._onCancel=function(e,t,o){this.isReady=!1,this.isUploading=!1,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!0,this.isError=!1,this.progress=0,this.index=null,this.onCancel(e,t,o)},o.prototype._onComplete=function(e,t,o){this.onComplete(e,t,o),this.removeAfterUpload&&this.remove()},o.prototype._onTimeout=function(){this.isReady=!1,this.isUploading=!1,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!1,this.isError=!0,this.progress=0,this.index=null,this.onTimeout()},o.prototype._destroy=function(){this._input&&this._input.remove(),this._form&&this._form.remove(),delete this._form,delete this._input},o.prototype._prepareToUploading=function(){this.index=this.index||++this.uploader._nextIndex,this.isReady=!0},o.prototype._replaceNode=function(t){var o=e(t.clone())(t.scope());o.prop("value",null),t.css("display","none"),t.after(o)},o}()}Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var s=o(1),a=(n(s),angular),u=a.copy,l=a.extend,p=a.element;a.isElement;i.$inject=["$compile","FileLikeObject"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(){var e=function(){function e(t){r(this,e),u(this,t),this.uploader._directives[this.prop].push(this),this._saveLinks(),this.bind()}return e.prototype.bind=function(){for(var e in this.events){var t=this.events[e];this.element.bind(e,this[t])}},e.prototype.unbind=function(){for(var e in this.events)this.element.unbind(e,this.events[e])},e.prototype.destroy=function(){var e=this.uploader._directives[this.prop].indexOf(this);this.uploader._directives[this.prop].splice(e,1),this.unbind()},e.prototype._saveLinks=function(){for(var e in this.events){var t=this.events[e];this[t]=this[t].bind(this)}},e}();return e.prototype.events={},e}Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var s=o(1),a=(n(s),angular),u=a.extend},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e,t){return function(t){function o(e){r(this,o);var n=p(e,{events:{$destroy:"destroy",change:"onChange"},prop:"select"}),s=i(this,t.call(this,n));return s.uploader.isHTML5||s.element.removeAttr("multiple"),s.element.prop("value",null),s}return s(o,t),o.prototype.getOptions=function(){},o.prototype.getFilters=function(){},o.prototype.isEmptyAfterSelection=function(){return!!this.element.attr("multiple")},o.prototype.onChange=function(){var t=this.uploader.isHTML5?this.element[0].files:this.element[0],o=this.getOptions(),n=this.getFilters();this.uploader.isHTML5||this.destroy(),this.uploader.addToQueue(t,o,n),this.isEmptyAfterSelection()&&(this.element.prop("value",null),this.element.replaceWith(e(this.element.clone())(this.scope)))},o}(t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var u=o(1),l=(n(u),angular),p=l.extend;a.$inject=["$compile","FileDirective"]},function(e,t){"use strict";function o(e){if(Array.isArray(e)){for(var t=0,o=Array(e.length);t<e.length;t++)o[t]=e[t];return o}return Array.from(e)}function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e){return function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];n(this,t),this.pipes=e}return t.prototype.next=function(t){var n=this.pipes.shift();if(a(n))return void this.onSuccessful.apply(this,o(t));var r=new Error("The filter has not passed");if(r.pipe=n,r.args=t,n.isAsync){var i=e.defer(),u=s(this,this.next,t),l=s(this,this.onThrown,r);i.promise.then(u,l),n.apply(void 0,o(t).concat([i]))}else{var p=Boolean(n.apply(void 0,o(t)));p?this.next(t):this.onThrown(r)}},t.prototype.exec=function(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];this.next(t)},t.prototype.onThrown=function(e){},t.prototype.onSuccessful=function(){},t}()}Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=angular,s=i.bind,a=i.isUndefined;r.$inject=["$q"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e){return function(e){function t(o){r(this,t);var n=p(o,{events:{$destroy:"destroy",drop:"onDrop",dragover:"onDragOver",dragleave:"onDragLeave"},prop:"drop"});return i(this,e.call(this,n))}return s(t,e),t.prototype.getOptions=function(){},t.prototype.getFilters=function(){},t.prototype.onDrop=function(e){var t=this._getTransfer(e);if(t){var o=this.getOptions(),n=this.getFilters();this._preventAndStop(e),c(this.uploader._directives.over,this._removeOverClass,this),this.uploader.addToQueue(t.files,o,n)}},t.prototype.onDragOver=function(e){var t=this._getTransfer(e);this._haveFiles(t.types)&&(t.dropEffect="copy",this._preventAndStop(e),c(this.uploader._directives.over,this._addOverClass,this))},t.prototype.onDragLeave=function(e){e.currentTarget!==this.element[0]&&(this._preventAndStop(e),c(this.uploader._directives.over,this._removeOverClass,this))},t.prototype._getTransfer=function(e){return e.dataTransfer?e.dataTransfer:e.originalEvent.dataTransfer},t.prototype._preventAndStop=function(e){e.preventDefault(),e.stopPropagation()},t.prototype._haveFiles=function(e){return!!e&&(e.indexOf?e.indexOf("Files")!==-1:!!e.contains&&e.contains("Files"))},t.prototype._addOverClass=function(e){e.addOverClass()},t.prototype._removeOverClass=function(e){e.removeOverClass()},t}(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var u=o(1),l=(n(u),angular),p=l.extend,c=l.forEach;a.$inject=["FileDirective"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e){return function(e){function t(o){r(this,t);var n=p(o,{events:{$destroy:"destroy"},prop:"over",overClass:"nv-file-over"});return i(this,e.call(this,n))}return s(t,e),t.prototype.addOverClass=function(){this.element.addClass(this.getOverClass())},t.prototype.removeOverClass=function(){this.element.removeClass(this.getOverClass())},t.prototype.getOverClass=function(){return this.overClass},t}(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var u=o(1),l=(n(u),angular),p=l.extend;a.$inject=["FileDirective"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t,o){return{link:function(n,r,i){var s=n.$eval(i.uploader);if(!(s instanceof t))throw new TypeError('"Uploader" must be an instance of FileUploader');var a=new o({uploader:s,element:r,scope:n});a.getOptions=e(i.options).bind(a,n),a.getFilters=function(){return i.filters}}}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=o(1);n(i);r.$inject=["$parse","FileUploader","FileSelect"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t,o){return{link:function(n,r,i){var s=n.$eval(i.uploader);if(!(s instanceof t))throw new TypeError('"Uploader" must be an instance of FileUploader');if(s.isHTML5){var a=new o({uploader:s,element:r});a.getOptions=e(i.options).bind(a,n),a.getFilters=function(){return i.filters}}}}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=o(1);n(i);r.$inject=["$parse","FileUploader","FileDrop"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){return{link:function(o,n,r){var i=o.$eval(r.uploader);if(!(i instanceof e))throw new TypeError('"Uploader" must be an instance of FileUploader');var s=new t({uploader:i,element:n});s.getOverClass=function(){return r.overClass||s.overClass}}}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=o(1);n(i);r.$inject=["FileUploader","FileOver"]}])});
2218 2219(function(angular, $, _) { 2220 angular.module('api4', CRM.angRequires('api4')); 2221 2222angular.module('api4').factory('crmApi4', function($q) { 2223 var crmApi4 = function(entity, action, params, index) { 2224 var deferred = $q.defer(); 2225 var p; 2226 var backend = crmApi4.backend || CRM.api4; 2227 if (_.isObject(entity)) { 2228 /*jshint -W061 */ 2229 p = backend(eval('('+angular.toJson(entity)+')'), action); 2230 } else { 2231 /*jshint -W061 */ 2232 p = backend(entity, action, eval('('+angular.toJson(params)+')'), index); 2233 } 2234 p.then( 2235 function(result) { 2236 deferred.resolve(result); 2237 }, 2238 function(error) { 2239 deferred.reject(error); 2240 } 2241 ); 2242 return deferred.promise; 2243 }; 2244 crmApi4.backend = null; 2245 crmApi4.val = function(value) { 2246 var d = $.Deferred(); 2247 d.resolve(value); 2248 return d.promise(); 2249 }; 2250 return crmApi4; 2251 }); 2252 2253})(angular, CRM.$, CRM._); 2254 2255/** 2256 * Checklist-model 2257 * AngularJS directive for list of checkboxes 2258 * https://github.com/vitalets/checklist-model 2259 * License: MIT http://opensource.org/licenses/MIT 2260 */ 2261 2262 /* commonjs package manager support (eg componentjs) */ 2263 if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){ 2264 module.exports = 'checklist-model'; 2265 } 2266 2267angular.module('checklist-model', []) 2268.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) { 2269 function contains(arr, item, comparator) { 2270 if (angular.isArray(arr)) { 2271 for (var i = arr.length; i--;) { 2272 if (comparator(arr[i], item)) { 2273 return true; 2274 } 2275 } 2276 } 2277 return false; 2278 } 2279 function add(arr, item, comparator) { 2280 arr = angular.isArray(arr) ? arr : []; 2281 if(!contains(arr, item, comparator)) { 2282 arr.push(item); 2283 } 2284 return arr; 2285 } 2286 function remove(arr, item, comparator) { 2287 if (angular.isArray(arr)) { 2288 for (var i = arr.length; i--;) { 2289 if (comparator(arr[i], item)) { 2290 arr.splice(i, 1); 2291 break; 2292 } 2293 } 2294 } 2295 return arr; 2296 } 2297 function postLinkFn(scope, elem, attrs) { 2298 var checklistModel = attrs.checklistModel; 2299 attrs.$set("checklistModel", null); 2300 $compile(elem)(scope); 2301 attrs.$set("checklistModel", checklistModel); 2302 var checklistModelGetter = $parse(checklistModel); 2303 var checklistChange = $parse(attrs.checklistChange); 2304 var checklistBeforeChange = $parse(attrs.checklistBeforeChange); 2305 var ngModelGetter = $parse(attrs.ngModel); 2306 2307 2308 2309 var comparator = function (a, b) { 2310 if(!isNaN(a) && !isNaN(b)) { 2311 return String(a) === String(b); 2312 } else { 2313 return angular.equals(a,b); 2314 } 2315 }; 2316 2317 if (attrs.hasOwnProperty('checklistComparator')){ 2318 if (attrs.checklistComparator[0] == '.') { 2319 var comparatorExpression = attrs.checklistComparator.substring(1); 2320 comparator = function (a, b) { 2321 return a[comparatorExpression] === b[comparatorExpression]; 2322 }; 2323 2324 } else { 2325 comparator = $parse(attrs.checklistComparator)(scope.$parent); 2326 } 2327 } 2328 var unbindModel = scope.$watch(attrs.ngModel, function(newValue, oldValue) { 2329 if (newValue === oldValue) { 2330 return; 2331 } 2332 2333 if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) { 2334 ngModelGetter.assign(scope, contains(checklistModelGetter(scope.$parent), getChecklistValue(), comparator)); 2335 return; 2336 } 2337 2338 setValueInChecklistModel(getChecklistValue(), newValue); 2339 2340 if (checklistChange) { 2341 checklistChange(scope); 2342 } 2343 }); 2344 var unbindCheckListValue = scope.$watch(getChecklistValue, function(newValue, oldValue) { 2345 if( newValue != oldValue && angular.isDefined(oldValue) && scope[attrs.ngModel] === true ) { 2346 var current = checklistModelGetter(scope.$parent); 2347 checklistModelGetter.assign(scope.$parent, remove(current, oldValue, comparator)); 2348 checklistModelGetter.assign(scope.$parent, add(current, newValue, comparator)); 2349 } 2350 }, true); 2351 2352 var unbindDestroy = scope.$on('$destroy', destroy); 2353 2354 function destroy() { 2355 unbindModel(); 2356 unbindCheckListValue(); 2357 unbindDestroy(); 2358 } 2359 2360 function getChecklistValue() { 2361 return attrs.checklistValue ? $parse(attrs.checklistValue)(scope.$parent) : attrs.value; 2362 } 2363 2364 function setValueInChecklistModel(value, checked) { 2365 var current = checklistModelGetter(scope.$parent); 2366 if (angular.isFunction(checklistModelGetter.assign)) { 2367 if (checked === true) { 2368 checklistModelGetter.assign(scope.$parent, add(current, value, comparator)); 2369 } else { 2370 checklistModelGetter.assign(scope.$parent, remove(current, value, comparator)); 2371 } 2372 } 2373 2374 } 2375 function setChecked(newArr, oldArr) {
2376 if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) { 2377 setValueInChecklistModel(getChecklistValue(), ngModelGetter(scope)); 2378 return; 2379 } 2380 ngModelGetter.assign(scope, contains(newArr, getChecklistValue(), comparator)); 2381 } 2382 if (angular.isFunction(scope.$parent.$watchCollection)) { 2383 scope.$parent.$watchCollection(checklistModel, setChecked); 2384 } else { 2385 scope.$parent.$watch(checklistModel, setChecked, true); 2386 } 2387 } 2388 2389 return { 2390 restrict: 'A', 2391 priority: 1000, 2392 terminal: true, 2393 scope: true, 2394 compile: function(tElement, tAttrs) { 2395 2396 if (!tAttrs.checklistValue && !tAttrs.value) { 2397 throw 'You should provide `value` or `checklist-value`.'; 2398 } 2399 if (!tAttrs.ngModel) { 2400 tAttrs.$set("ngModel", "checked"); 2401 } 2402 2403 return postLinkFn; 2404 } 2405 }; 2406}]); 2407 2408(function(angular, $, _) { 2409 "use strict"; 2410 angular.module('civiCaseTasks', CRM.angRequires('civiCaseTasks')); 2411 2412 2413 angular.module('civiCaseTasks').controller('civiCaseTaskCaseRole', function($scope, crmApi4, searchTaskBaseTrait) { 2414 const ts = $scope.ts = CRM.ts('civi_case'); 2415 const ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait); 2416 const values = this.task.values && !Array.isArray(this.task.values) ? this.task.values : {}; 2417 $scope.values = values; 2418 2419 this.existingCount = null; 2420 this.autocompleteParams = {}; 2421 this.relationshipInfo = this.task.relationshipTypes.reduce((acc, item) => { 2422 acc[item.id] = item; 2423 return acc; 2424 }, {}); 2425 if (values.relationship_type) { 2426 setCountAndFilters(); 2427 } 2428 2429 this.onChangeRole = function() { 2430 values.addOrReplace = null; 2431 setCountAndFilters(); 2432 }; 2433 2434 function setCountAndFilters() { 2435 ctrl.existingCount = null; 2436 crmApi4('Relationship', 'get', { 2437 select: ['row_count'], 2438 where: [ 2439 ['case_id', 'IN', ctrl.ids], 2440 ['is_current', '=', true], 2441 ['relationship_type_id', '=', values.relationship_type.split('_')[0]], 2442 ], 2443 groupBy: ['case_id'] 2444 }).then(function(result) { 2445 ctrl.existingCount = result.count; 2446 }); 2447 ctrl.autocompleteParams = { 2448 filters: {contact_sub_type: ctrl.relationshipInfo[values.relationship_type].contact_sub_type} 2449 }; 2450 } 2451 2452 this.submit = function() { 2453 const relationshipType = values.relationship_type.split('_')[0]; 2454 const a = values.relationship_type.split('_')[1]; 2455 const b = values.relationship_type.split('_')[2]; 2456 if (values.addOrReplace === 'replace') { 2457 crmApi4('Relationship', 'update', { 2458 values: {end_date: 'now', is_active: false}, 2459 where: [ 2460 ['case_id', 'IN', ctrl.ids], 2461 ['is_current', '=', true], 2462 ['relationship_type_id', '=', relationshipType], 2463 ['contact_id_' + b, '!=', values.contact_id], 2464 ] 2465 }); 2466 } 2467 const apiValues = { 2468 case_id: '$case_id', 2469 relationship_type_id: relationshipType, 2470 start_date: 'now', 2471 }; 2472 apiValues['contact_id_' + a] = '$contact_id'; 2473 apiValues['contact_id_' + b] = values.contact_id; 2474 const apiParams = { 2475 chain: {relationship: ['Relationship', 'create', {values: apiValues}]} 2476 }; 2477 ctrl.start(apiParams); 2478 }; 2479 2480 this.onSuccess = function(result) { 2481 let added = 0; 2482 let duplicate = 0; 2483 result.forEach(function(item) { 2484 item.relationship.forEach(function(relationship) { 2485 if (relationship.duplicate_id) { 2486 duplicate++; 2487 } else { 2488 added++; 2489 } 2490 }); 2491 }); 2492 let msg = _.escape(ts('1 case role added.', {plural: '%count case roles added.', count: added})); 2493 if (duplicate) { 2494 msg += '<br>' + _.escape(ts('1 case role already occupied by the selected contact.', {plural: '%count case roles already occupied by the selected contact.', count: duplicate})); 2495 } 2496 CRM.alert(msg, ts('Saved'), 'success'); 2497 this.close(result); 2498 }; 2499 2500 }); 2501 2502 2503 angular.module('civiCaseTasks').controller('civiCaseTaskOpenCase', function($scope, crmApi4, searchTaskBaseTrait, searchTaskFieldsTrait) { 2504 const ts = $scope.ts = CRM.ts('civi_case'); 2505 const ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait, searchTaskFieldsTrait); 2506 2507 this.skipDuplicates = true; 2508 this.multipleClients = false;
2509 2510 this.loadFieldsAndValues(this.task, 'Case', 2511 {action: 'create', where: [['name', 'NOT IN', ['contact_id', 'is_deleted']]]}, 2512 {existingCaseContacts: ['CaseContact', 'get', { 2513 where: [ 2514 ['contact_id', 'IN', ctrl.ids], 2515 ['case_id.status_id:name', '!=', 'Closed'] 2516 ], 2517 }, ['contact_id']]} 2518 ).then((results) => { 2519 this.existingCaseContacts = results.existingCaseContacts; 2520 this.addField('subject'); 2521 }); 2522 2523 this.submit = function() { 2524 const apiParams = {}; 2525 if (ctrl.skipDuplicates) { 2526 ctrl.ids = ctrl.ids.filter(id => !ctrl.existingCaseContacts.includes(id)); 2527 } 2528 if (!ctrl.ids.length) { 2529 CRM.alert(_.escape(ts('No cases to open.')), _.escape(ts('All contacts skipped'))); 2530 ctrl.cancel(); 2531 } 2532 if (ctrl.multipleClients) { 2533 apiParams.values = _.zipObject(ctrl.values); 2534 apiParams.values.contact_id = ctrl.ids; 2535 } 2536 else { 2537 apiParams.records = [_.zipObject(ctrl.values)]; 2538 } 2539 ctrl.start(apiParams); 2540 }; 2541 2542 this.onSuccess = function(result) { 2543 let msg = _.escape(ts('Successfully opened 1 case.', {plural: 'Successfully opened %count cases.', count: result.length})); 2544 if (result.length === 1) { 2545 const viewLink = this.getUrl('view', result[0]); 2546 if (viewLink) { 2547 msg += '<br><a href="' + viewLink + '" target="_blank"><i class="crm-i fa-external-link" role="img" aria-hidden="true"></i> ' + _.escape(ts('View case')) + '</a>'; 2548 } 2549 } 2550 CRM.alert(msg, ts('Saved'), 'success'); 2551 this.close(result); 2552 }; 2553 2554 }); 2555 2556 2557 angular.module('crmDialog', CRM.angRequires('crmDialog')); 2558 angular.module('crmDialog').directive('crmDialogPopup', function(dialogService) { 2559 return { 2560 restrict: 'A', 2561 bindToController: { 2562 popupTpl: '@', 2563 popupName: '@crmDialogPopup', 2564 popupData: '<' 2565 }, 2566 controller: function($scope, $element) { 2567 var ctrl = this; 2568 $element.on('click', function() { 2569 var options = CRM.utils.adjustDialogDefaults({ 2570 autoOpen: false, 2571 title: _.trim($element.attr('title') || $element.text()) 2572 }); 2573 dialogService.open(ctrl.popupName, ctrl.popupTpl, ctrl.popupData || {}, options) 2574 .then(function(success) { 2575 $element.trigger('crmPopupFormSuccess'); 2576 }); 2577 }); 2578 } 2579 }; 2580 }); 2581 angular.module('crmDialog').directive('crmDialog', function(dialogService) { 2582 return { 2583 restrict: 'A', 2584 controllerAs: '$dialog', 2585 controller: function($scope, $parse, $timeout) { 2586 var $dialog = this; 2587 $dialog.buttons = []; 2588 2589 $dialog.close = function(result) { 2590 dialogService.close($dialog.name, result); 2591 }; 2592 2593 $dialog.cancel = function() { 2594 dialogService.cancel($dialog.name); 2595 }; 2596 2597 $dialog.loadButtons = function() { 2598 var buttons = []; 2599 angular.forEach($dialog.buttons, function (crmDialogButton) { 2600 var button = _.pick(crmDialogButton, ['icons', 'text', 'disabled']); 2601 button.click = function() { 2602 $scope.$apply(crmDialogButton.onClick); 2603 }; 2604 buttons.push(button); 2605 }); 2606 dialogService.setButtons($dialog.name, buttons); 2607 }; 2608 2609 $timeout(function() { 2610 $('.ui-dialog:last input:not([disabled]):not([type="submit"]):first').focus(); 2611 }); 2612 2613 }, 2614 link: function(scope, element, attrs, controller) { 2615 controller.name = attrs.crmDialog; 2616 scope[attrs.crmDialog] = controller; 2617 } 2618 }; 2619 }); 2620 angular.module('crmDialog').component('crmDialogButton', { 2621 bindings: { 2622 disabled: '<', 2623 icons: '<', 2624 text: '<', 2625 onClick: '&' 2626 }, 2627 require: { 2628 crmDialog: '?^^crmDialog' 2629 }, 2630 controller: function($scope) { 2631 var $ctrl = this; 2632 $ctrl.$onInit = function() { 2633 $ctrl.crmDialog.buttons.push(this); 2634 $scope.$watch('$ctrl.disabled', $ctrl.crmDialog.loadButtons); 2635 $scope.$watch('$ctrl.text', $ctrl.crmDialog.loadButtons); 2636 $scope.$watch('$ctrl.icons', $ctrl.crmDialog.loadButtons); 2637 }; 2638 } 2639 }); 2640 2641
2642angular.module('crmResource', []); 2643 2644 angular.module('crmResource').factory('crmResource', function($q, $http) { 2645 var deferreds = {}; // null|object; deferreds[url][idx] = Deferred; 2646 2647 var notify = function notify() { 2648 var oldDfrds = deferreds; 2649 deferreds = null; 2650 2651 angular.forEach(oldDfrds, function(dfrs, url) { 2652 if (CRM.angular.templates[url]) { 2653 angular.forEach(dfrs, function(dfr) { 2654 dfr.resolve({ 2655 status: 200, 2656 headers: function(name) { 2657 var headers = {'Content-type': 'text/html'}; 2658 return name ? headers[name] : headers; 2659 }, 2660 data: CRM.angular.templates[url] 2661 }); 2662 }); 2663 } 2664 else { 2665 angular.forEach(dfrs, function(dfr) { 2666 dfr.reject({status: 500}); // FIXME 2667 }); 2668 } 2669 }); 2670 }; 2671 2672 var moduleUrl = CRM.angular.bundleUrl; 2673 $http.get(moduleUrl) 2674 .then(function httpSuccess(response) { 2675 CRM.angular.templates = CRM.angular.templates || {}; 2676 angular.forEach(response.data, function (module) { 2677 if (module.partials) { 2678 angular.extend(CRM.angular.templates, module.partials); 2679 } 2680 if (module.strings) { 2681 CRM.addStrings(module.domain, module.strings); 2682 } 2683 }); 2684 notify(); 2685 }, function httpError() { 2686 notify(); 2687 }); 2688 2689 return { 2690 getUrl: function getUrl(url) { 2691 if (CRM.angular.templates && CRM.angular.templates[url]) { 2692 return CRM.angular.templates[url]; 2693 } 2694 else { 2695 var deferred = $q.defer(); 2696 if (!deferreds[url]) { 2697 deferreds[url] = []; 2698 } 2699 deferreds[url].push(deferred); 2700 return deferred.promise; 2701 } 2702 } 2703 }; 2704 }); 2705 2706 angular.module('crmResource').config(function($provide) { 2707 $provide.decorator('$templateCache', function($delegate, $http, $q, crmResource) { 2708 var origGet = $delegate.get; 2709 var urlPat = /^~\//; 2710 $delegate.get = function(url) { 2711 if (urlPat.test(url)) { 2712 return crmResource.getUrl(url); 2713 } 2714 else { 2715 return origGet.call(this, url); 2716 } 2717 }; 2718 return $delegate; 2719 }); 2720 }); 2721 2722 2723 angular.module('crmSearchDisplay', CRM.angRequires('crmSearchDisplay')); 2724 2725 2726 angular.module('crmSearchDisplay').component('crmSearchDisplay', { 2727 bindings: { 2728 type: '@', 2729 apiEntity: '@', 2730 search: '<', 2731 display: '<', 2732 settings: '<', 2733 filters: '<', 2734 }, 2735 template: () => { 2736 let html = ''; 2737 const displayTypes = CRM.crmSearchDisplay.viewableDisplayTypes; 2738 Object.entries(displayTypes).forEach(([type, directive]) => { 2739 html += `<${directive} ng-if="$ctrl.type === '${type}'" api-entity="{{:: $ctrl.apiEntity }}" search="$ctrl.search" display="$ctrl.display" settings="$ctrl.settings" filters="$ctrl.filters"></${directive}>`; 2740 }); 2741 return html; 2742 }, 2743 controller: function($scope, crmApi4) { 2744 2745 this.$onInit = () => { 2746 if (!this.settings || !this.apiEntity || !this.type) { 2747 const apiCalls = { 2748 search: ['SavedSearch', 'get', { 2749 select: ['id', 'name', 'api_entity'], 2750 where: [['name', '=', this.search]], 2751 }, 0], 2752 }; 2753 if (this.display) { 2754 apiCalls.display = ['SearchDisplay', 'get', { 2755 select: ['type', 'settings'], 2756 where: [['name', '=', this.display], ['saved_search_id.name', '=', this.search]], 2757 }, 0]; 2758 } else { 2759 apiCalls.display = ['SearchDisplay', 'getDefault', { 2760 select: ['type', 'settings'], 2761 savedSearch: this.search, 2762 }, 0]; 2763 } 2764 crmApi4(apiCalls).then((result) => { 2765 this.apiEntity = result.search.api_entity; 2766 this.settings = result.display.settings; 2767 this.type = result.display.type; 2768 }); 2769 } 2770 }; 2771 2772 } 2773 2774 }); 2775 2776 2777 2778 angular.module('crmSearchDisplay').component('crmSearchDisplayEditable', { 2779 bindings: { 2780 row: '<?', 2781 display: '<', 2782 colKey: '<', 2783 colData: '<?', 2784 isFullRowMode: '<', 2785 }, 2786 templateUrl: '~/crmSearchDisplay/crmSearchDisplayEditable.html', 2787 controller: function($scope, $element, crmApi4, crmStatus) { 2788 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'); 2789 const ctrl = this; 2790 let initialValue; 2791 let editableInfo; 2792 let valuePath; 2793 2794 this.$onInit = function() { 2795 editableInfo = this.display.results.editable[this.colKey];
2796 valuePath = this.colKey.split(':')[0]; 2797 this.display.editValues = this.display.editValues || {}; 2798 if (this.row) { 2799 initialValue = JSON.parse(JSON.stringify(this.row.data[valuePath])); 2800 this.display.editValues[this.colKey] = JSON.parse(JSON.stringify(this.row.data[valuePath])); 2801 } 2802 2803 this.field = { 2804 data_type: editableInfo.data_type, 2805 input_type: editableInfo.input_type, 2806 entity: editableInfo.entity, 2807 name: editableInfo.value_key, 2808 options: editableInfo.options, 2809 fk_entity: editableInfo.fk_entity, 2810 serialize: editableInfo.serialize, 2811 nullable: editableInfo.nullable && ctrl.row && ctrl.row.data[editableInfo.id_path], 2812 }; 2813 2814 if (this.field.options === true) { 2815 loadOptions(); 2816 } 2817 2818 $(document).off('.crmSearchDisplayEditable'); 2819 $(document).on('keydown.crmSearchDisplayEditable', (e) => { 2820 if (e.key === 'Escape') { 2821 $scope.$apply(() => ctrl.cancel()); 2822 } 2823 else if (e.key === 'Enter' && e.target.type !== 'textarea') { 2824 $scope.$apply(() => ctrl.save()); 2825 } 2826 }); 2827 }; 2828 2829 this.$onDestroy = function() { 2830 $(document).off('.crmSearchDisplayEditable'); 2831 }; 2832 2833 this.save = function() { 2834 const value = ctrl.display.editValues[ctrl.colKey]; 2835 if (value !== initialValue || ctrl.isFullRowMode) { 2836 ctrl.display.saveEditing(ctrl.row, ctrl.colKey); 2837 } 2838 else { 2839 ctrl.display.cancelEditing(ctrl.row); 2840 } 2841 }; 2842 2843 this.cancel = function() { 2844 ctrl.display.cancelEditing(ctrl.row); 2845 }; 2846 function loadOptions() { 2847 crmApi4(editableInfo.entity, 'getFields', { 2848 action: 'update', 2849 select: ['options'], 2850 values: ctrl.row && ctrl.row.data, 2851 loadOptions: ['id', 'name', 'label', 'description', 'color', 'icon'], 2852 where: [['name', '=', ctrl.field.name]] 2853 }, 0).then(function(fieldInfo) { 2854 ctrl.field.options = fieldInfo.options; 2855 }); 2856 } 2857 } 2858 }); 2859 2860 2861 angular.module('crmSearchDisplay').factory('searchDisplayBaseTrait', function($timeout, $interval, $sce, crmApi4, crmStatus) { 2862 return { 2863 page: 1, 2864 rowCount: null, 2865 onInitialize: [], 2866 onChangeFilters: [], 2867 onPreRun: [], 2868 onPostRun: [], 2869 _runCount: 0, 2870 isArray: Array.isArray, 2871 initializeDisplay: function($scope, $element) { 2872 const ctrl = this; 2873 this.$element = $element; 2874 this.limit = this.settings.limit; 2875 this.sort = Array.isArray(this.settings.sort) ? _.cloneDeep(this.settings.sort) : []; 2876 this.uniqueId = Math.floor(Math.random() * 10e10); 2877 this.placeholders = []; 2878 const placeholderCount = 'placeholder' in this.settings ? this.settings.placeholder : 5; 2879 for (let p=0; p < placeholderCount; ++p) { 2880 this.placeholders.push({}); 2881 } 2882 const isSubsearch = $element.closest('.crm-search-col-type-subsearch').length > 0; 2883 const setColumnDefaults = (col) => { 2884 col.enabled = true; 2885 col.fetched = true; 2886 }; 2887 $scope.crmUrl = CRM.url; 2888 if (this.settings.columnMode === 'auto' && (!this.settings.columns || !this.settings.columns.length)) { 2889 this.columns = []; 2890 crmApi4('SearchDisplay', 'getDefault', { 2891 savedSearch: this.search, 2892 select: ['settings'], 2893 }) 2894 .then((result) => this.columns = result[0].settings.columns) 2895 .then(() => this.columns.forEach(setColumnDefaults)) 2896 .catch((error) => CRM.alert(ts('Error loading search columns'))); 2897 } 2898 else { 2899 this.columns = _.cloneDeep(this.settings.columns); 2900 this.columns.forEach(setColumnDefaults); 2901 } 2902 2903 ctrl.onInitialize.forEach(callback => callback.call(ctrl, $scope, $element)); 2904 this.getResultsPronto = _.debounce(ctrl.runSearch, 300, {leading: true, trailing: false}); 2905 this.getResultsSoon = _.debounce(function() { 2906 $scope.$apply(function() { 2907 ctrl.runSearch(); 2908 }); 2909 }, 600); 2910 let contactTab = $element.closest('.crm-contact-page .ui-tabs-panel').attr('id'); 2911 if (contactTab && !$element.is($('#' + contactTab + ' [search][display]').first())) { 2912 contactTab = null; 2913 } 2914 let hasCounter = !isSubsearch && (contactTab || ctrl.hasOwnProperty('totalCount')); 2915 if (hasCounter) { 2916 $scope.$watch('$ctrl.rowCount', function(rowCount) { 2917 if (typeof rowCount === 'number' && angular.equals({}, ctrl.getAfformFilters())) { 2918 setTotalCount(rowCount); 2919 } 2920 }); 2921 } 2922 2923 function setTotalCount(rowCount) { 2924 if (ctrl.hasOwnProperty('totalCount')) { 2925 ctrl.totalCount = rowCount; 2926 } 2927 if (contactTab) { 2928 CRM.tabHeader.updateCount(contactTab.replace('contact-', '#tab_'), rowCount); 2929 } 2930 } 2931 if (!isSubsearch) { 2932 $element.closest('form').on('crmPopupFormSuccess crmFormSuccess', () => { 2933 ctrl.rowCount = null; 2934 ctrl.getResultsPronto(); 2935 }); 2936 } 2937 function onChangeFilters() { 2938 ctrl.page = 1; 2939 ctrl.rowCount = null;
2940 ctrl.onChangeFilters.forEach(callback => callback.call(ctrl)); 2941 if (!ctrl.settings.button) { 2942 ctrl.getResultsSoon(); 2943 } 2944 } 2945 2946 function onChangePageSize() { 2947 ctrl.page = 1; 2948 if (ctrl.results) { 2949 ctrl.getResultsSoon(); 2950 } 2951 } 2952 if (this.settings.toolbar) { 2953 this.onPostRun.push(function (apiResults) { 2954 if (apiResults.run.toolbar) { 2955 ctrl.toolbar = apiResults.run.toolbar; 2956 ctrl.toolbar.forEach((link) => { 2957 if (link.autoOpen && ctrl._runCount === 1 && !ctrl.results.length) { 2958 CRM.loadForm(link.url) 2959 .on('crmFormSuccess', (e, data) => { 2960 ctrl.rowCount = null; 2961 ctrl.getResultsPronto(); 2962 }); 2963 } 2964 }); 2965 } 2966 }); 2967 } 2968 2969 if (this.afFieldset) { 2970 this.onPostRun.push(function (apiResults) { 2971 if (apiResults.run.labels && apiResults.run.labels.length && $scope.$parent.addTitle) { 2972 $scope.$parent.addTitle(apiResults.run.labels.join(' ')); 2973 } 2974 }); 2975 } 2976 this.onPostRun.push(() => $element[0].dispatchEvent(new Event('load'))); 2977 function setUpWatches() { 2978 if (ctrl.afFieldset && !isSubsearch) { 2979 $scope.$watch(ctrl.afFieldset.getFilterValues, onChangeFilters, true); 2980 } 2981 if (ctrl.settings.pager && ctrl.settings.pager.expose_limit) { 2982 $scope.$watch('$ctrl.limit', onChangePageSize); 2983 } 2984 $scope.$watch('$ctrl.filters', onChangeFilters, true); 2985 } 2986 $element.css('display', 'block'); 2987 if ($element.is(':visible')) { 2988 setUpWatches(); 2989 } 2990 else { 2991 let checkVisibility = $interval(() => { 2992 if ($element.is(':visible')) { 2993 $interval.cancel(checkVisibility); 2994 setUpWatches(); 2995 } 2996 }, 250); 2997 } 2998 $timeout(function() { 2999 if (hasCounter && (!(ctrl.loading || ctrl.results) || !angular.equals({}, ctrl.getAfformFilters()))) { 3000 const params = ctrl.getApiParams('row_count'); 3001 params.filters = ctrl.filters; 3002 crmApi4('SearchDisplay', 'run', params).then(function(result) { 3003 setTotalCount(result.count); 3004 }); 3005 } 3006 }, 900); 3007 }, 3008 3009 hasExtraFirstColumn: function() { 3010 return this.settings.actions || this.settings.draggable || this.settings.collapsible || this.settings.editableRow || (this.settings.tally && this.settings.tally.label); 3011 }, 3012 3013 getFilters: function() { 3014 return _.assign({}, this.getAfformFilters(), this.filters); 3015 }, 3016 3017 getAfformFilters: function() { 3018 return this.afFieldset ? this.afFieldset.getFilterValues() : {}; 3019 }, 3020 getRawHtml(html) { 3021 return $sce.trustAsHtml(html); 3022 }, 3023 getApiParams: function(mode) { 3024 const apiParams = { 3025 return: arguments.length ? mode : 'page:' + this.page, 3026 savedSearch: this.search, 3027 display: this.display, 3028 sort: this.sort, 3029 limit: this.limit, 3030 seed: this.uniqueId, 3031 filters: this.getFilters(), 3032 afform: this.afFieldset ? this.afFieldset.getFormName() : null 3033 }; 3034 const toggleColumns = this.columns.reduce((indices, col, index) => { 3035 if (col.enabled) { 3036 indices.push(index); 3037 } 3038 return indices; 3039 }, []); 3040 if (toggleColumns.length < this.columns.length) { 3041 apiParams.toggleColumns = toggleColumns; 3042 } 3043 return apiParams; 3044 }, 3045 3046 onClickSearchButton: function() { 3047 this.rowCount = null; 3048 this.page = 1; 3049 this.getResultsPronto(); 3050 }, 3051 runSearch: function(apiCalls, statusParams, editedRow) { 3052 const ctrl = this; 3053 const requestId = ++this._runCount; 3054 const apiParams = this.getApiParams(); 3055 if (!statusParams) { 3056 this.loading = true; 3057 } 3058 apiCalls = apiCalls || {}; 3059 apiCalls.run = ['SearchDisplay', 'run', apiParams];
3060 ctrl.onPreRun.forEach(callback => callback.call(ctrl, apiCalls)); 3061 const apiRequest = crmApi4(apiCalls); 3062 apiRequest.then(function(apiResults) { 3063 if (requestId < ctrl._runCount) { 3064 return; // Another request started after this one 3065 } 3066 ctrl.results = apiResults.run; 3067 ctrl.loading = false; 3068 if (!ctrl.rowCount || editedRow) { 3069 if (!ctrl.limit || (ctrl.results.length < ctrl.limit && ctrl.page === 1)) { 3070 ctrl.rowCount = ctrl.results.length; 3071 } else if (ctrl.settings.pager || ctrl.settings.headerCount) { 3072 const params = ctrl.getApiParams('row_count'); 3073 crmApi4('SearchDisplay', apiCalls.run[1], params).then(function(result) { 3074 if (requestId < ctrl._runCount) { 3075 return; // Another request started after this one 3076 } 3077 3078 ctrl.rowCount = result.count; 3079 }); 3080 } 3081 } 3082 ctrl.onPostRun.forEach(callback => callback.call(ctrl, apiResults, 'success', editedRow)); 3083 }, function(error) { 3084 if (requestId < ctrl._runCount) { 3085 return; // Another request started after this one 3086 } 3087 ctrl.results = []; 3088 ctrl.loading = false; 3089 ctrl.onPostRun.forEach(callback => callback.call(ctrl, error, 'error', editedRow)); 3090 }); 3091 if (statusParams) { 3092 crmStatus(statusParams, apiRequest); 3093 } 3094 return apiRequest; 3095 }, 3096 3097 getFieldClass: function(colIndex, colData) { 3098 return (colData.cssClass || '') + ' crm-search-col-type-' + this.columns[colIndex].type + (this.columns[colIndex].break ? '' : ' crm-inline-block'); 3099 }, 3100 3101 getFieldTemplate: function(colIndex, colData) { 3102 let colType = this.columns[colIndex].type; 3103 if (colType === 'include') { 3104 if (/^~\/.+/.test(this.columns[colIndex].path) === false) { 3105 throw 'Invalid path for include column: "' + this.columns[colIndex].path + '"'; 3106 } 3107 return this.columns[colIndex].path; 3108 } 3109 if (colType === 'field') { 3110 if (colData.edit) { 3111 colType = 'editable'; 3112 } else if (colData.links) { 3113 colType = 'link'; 3114 } 3115 } 3116 return '~/crmSearchDisplay/colType/' + colType + '.html'; 3117 }, 3118 3119 getSearchDisplayKey: function() { 3120 return this.display ? `${this.search}.${this.display}` : this.search; 3121 } 3122 3123 }; 3124 }); 3125 3126 3127 angular.module('crmSearchDisplay').factory('searchDisplayEditableTrait', function(crmApi4, crmStatus, $timeout) { 3128 return { 3129 3130 editing: false, 3131 3132 onPostRun: [function(apiResults) { 3133 this.cancelEditing(); 3134 }], 3135 3136 startEditing: function(row, colIndex) { 3137 if (this.editing === false && row.columns[colIndex].edit) { 3138 this.editValues = {}; 3139 this.editing = row.key; 3140 row.columns.forEach((col, index) => { 3141 col.editing = (index === colIndex || (this.settings.editableRow && this.settings.editableRow.full)); 3142 col.focused = index === colIndex; 3143 }); 3144 } 3145 }, 3146 3147 startCreating: function() { 3148 this.editing = -1; 3149 this.editValues = {}; 3150 }, 3151 3152 cancelEditing: function(row) { 3153 this.editing = false; 3154 this.editValues = {}; 3155 if (row && row.columns) { 3156 row.columns.forEach((col) => col.editing = false); 3157 } 3158 }, 3159 3160 saveEditing: function(row, colKey) { 3161 const ctrl = this; 3162 const apiParams = this.getApiParams(null); 3163 if (row) { 3164 if (colKey) { 3165 const colIndex = this.settings.columns.findIndex(column => column.key === colKey); 3166 row.columns[colIndex].loading = true; 3167 } else { 3168 row.columns.forEach((col) => col.loading = true); 3169 } 3170 apiParams.rowKey = row.key; 3171 } 3172 apiParams.values = this.editValues; 3173 this.cancelEditing(); 3174 3175 crmStatus({}, crmApi4('SearchDisplay', 'inlineEdit', apiParams)) 3176 .then(function(result) { 3177 ctrl.refreshAfterEditing(result, row ? row.key : null); 3178 }); 3179 }, 3180 3181 refreshAfterEditing: function(result, rowKey) { 3182 if (!rowKey && result.length) { 3183 this.results.push(result[0]); 3184 this.rowCount++; 3185 return; 3186 } 3187 const rowIndex = this.results.findIndex(result =>
3187 result.key === rowKey); 3188 if (result.length && rowIndex >= 0) { 3189 const row = this.results[rowIndex]; 3190 _.defaults(result[0].data, row.data); 3191 delete (row.cssClass); 3192 _.defaults(result[0], row); 3193 this.results.splice(rowIndex, 1); 3194 $timeout(() => this.results.splice(rowIndex, 0, result[0])); 3195 } 3196 else { 3197 this.rowCount = null; 3198 this.getResultsPronto(); 3199 this.$element.trigger('crmPopupFormSuccess'); 3200 } 3201 } 3202 3203 }; 3204 }); 3205 3206 3207 angular.module('crmSearchDisplay').factory('searchDisplaySortableTrait', function() { 3208 const ts = CRM.ts('org.civicrm.search_kit'); 3209 return { 3210 3211 isSortable: function(col) { 3212 return !this.settings.draggable && ['field', 'html'].includes(col.type) && col.sortable !== false; 3213 }, 3214 3215 getSort: function(col) { 3216 const dir = this.sort.reduce((dir, item) => item[0] === col.key ? item[1] : dir, null); 3217 if (dir) { 3218 return 'fa-sort-' + dir.toLowerCase(); 3219 } 3220 return 'fa-sort disabled'; 3221 }, 3222 3223 setSort: function(col, $event) { 3224 if (!this.isSortable(col)) { 3225 return; 3226 } 3227 const dir = this.getSort(col) === 'fa-sort-asc' ? 'DESC' : 'ASC'; 3228 if (!$event.shiftKey || !this.sort) { 3229 this.sort = []; 3230 } 3231 const index = this.sort.findIndex(item => item[0] === col.key); 3232 if (index > -1) { 3233 this.sort[index][1] = dir; 3234 } else { 3235 this.sort.push([col.key, dir]); 3236 } 3237 if (this.results || !this.settings.button) { 3238 this.getResultsPronto(); 3239 } 3240 } 3241 3242 }; 3243 }); 3244 3245 3246 angular.module('crmSearchDisplayBatch', CRM.angRequires('crmSearchDisplayBatch')); 3247 3248 3249 let displayInstance = 0; 3250 3251 angular.module('crmSearchDisplayBatch').component('crmSearchDisplayBatch', { 3252 bindings: { 3253 apiEntity: '@', 3254 search: '<', 3255 display: '<', 3256 apiParams: '<', 3257 settings: '<', 3258 filters: '<', 3259 totalCount: '=?' 3260 }, 3261 require: { 3262 afFieldset: '?^^afFieldset', 3263 formCtrl: '?^form', 3264 }, 3265 templateUrl: '~/crmSearchDisplayBatch/crmSearchDisplayBatch.html', 3266 controller: function($scope, $element, $location, $interval, $q, crmApi4, searchDisplayBaseTrait, searchDisplayEditableTrait) { 3267 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'); 3268 const ctrl = angular.extend(this, _.cloneDeep(searchDisplayBaseTrait), _.cloneDeep(searchDisplayEditableTrait)); 3269 3270 let autoSaveTimer; 3271 let errorNotification; 3272 this.loading = true; 3273 this.unsaved = []; 3274 this.formName = 'searchDisplayBatch' + displayInstance++; 3275 3276 this.$onInit = function() { 3277 this.limit = this.settings.limit || 0; 3278 if (errorNotification && errorNotification.close) { 3279 errorNotification.close(); 3280 } 3281 this.isPreviewMode = typeof this.search !== 'string'; 3282 this.userJobId = this.isPreviewMode ? null : $location.search().batch; 3283 this.columns = _.cloneDeep(this.settings.columns); 3284 if (this.userJobId) { 3285 this.runSearch(); 3286 autoSaveTimer = $interval(function() { 3287 ctrl.saveRows(); 3288 }, 10000); 3289 } 3290 else { 3291 this.newBatch = { 3292 rowCount: 1, 3293 targets: {} 3294 }; 3295 this.reportLinks = [ 3296 { 3297 title: ts('View My Import Batches'), 3298 href: CRM.url('civicrm/imports/my-listing#/?job_type=search_batch_import'), 3299 icon: 'fa-user-tag', 3300 }, 3301 ]; 3302 if (CRM.checkPerm('administer queues')) { 3303 this.reportLinks.push({ 3304 title: ts('View All Import Batches'), 3305 href: CRM.url('civicrm/imports/all-imports#/?job_type=search_batch_import'), 3306 icon: 'fa-list-alt', 3307 }); 3308 } 3309 } 3310 if (this.isPreviewMode) { 3311 this.results = [{data: {}}]; 3312 this.loading = false; 3313 } 3314 }; 3315 3316 this.$onDestroy = function() { 3317 if (typeof autoSaveTimer !== 'undefined') { 3318 $interval.cancel(autoSaveTimer); 3319 } 3320 }; 3321 this.getResultsPronto = function() { 3322 if (this.limit) { 3323 const start = (this.page - 1) * this.limit; 3324 this.resultsPage = this.results.slice(start, start + this.limit); 3325 } else { 3326 this.resultsPage = this.results; 3327 } 3328 }; 3329 3330 $scope.$watch('$ctrl.results.length', function() { 3331 if (ctrl.results) { 3332 ctrl.rowCount = ctrl.results.length; 3333 if (ctrl.limit && ctrl.page > 1 && Math.ceil(ctrl.results.length / ctrl.limit) < ctrl.page) { 3334 ctrl.page--; 3335 } 3336 ctrl.getResultsPronto(); 3337 } 3338 }); 3339 3340 $scope.$watch('$ctrl.limit', function() { 3341 if (ctrl.results) { 3342 ctrl.page = 1; 3343 ctrl.getResultsPronto(); 3344 } 3345 }); 3346 3347 this.createNewBatch = function() { 3348 this.creatingBatch = true; 3349 crmApi4('SearchDisplay', 'createBatch', { 3350 savedSearch: this.search, 3351 display: this.display, 3352 rowCount: this.newBatch.rowCount, 3353 targets: this.newBatch.targets, 3354 label: this.newBatch.label, 3355 }, 0).then(function(userJob) { 3356 $location.search('batch', userJob.id); 3357 ctrl.$onInit(); 3358 }); 3359 }; 3360 3361 this.addRows = function(rowCount) { 3362 this.addingRows = true; 3363 const newRows = []; 3364 for (let i = 0; i < rowCount; i++) { 3365 newRows.push({}); 3366 } 3367 crmApi4(getApiName(), 'save', {records: newRows, reload: ['*']}).then(function(savedRows) { 3368 savedRows.forEach(function(row) { 3369 delete row._entity_id; 3370 delete row._status; 3371 delete row._status_message; 3372 ctrl.results.push({data: row}); 3373 }); 3374 ctrl.addingRows = false;
3375 }); 3376 }; 3377 3378 this.deleteRow = function(index) { 3379 const rowNum = (this.page - 1) * this.limit + index; 3380 const id = this.results[rowNum].data._id; 3381 const unsavedIndex = this.unsaved.findIndex(item => item._id === id); 3382 if (unsavedIndex !== -1) { 3383 this.unsaved.splice(unsavedIndex, 1); 3384 } 3385 crmApi4(getApiName(), 'delete', {where: [['_id', '=', id]]}); 3386 this.results.splice(rowNum, 1); 3387 }; 3388 3389 this.onChangeData = function(row) { 3390 if (!this.unsaved.some(item => item._id === row.data._id)) { 3391 this.unsaved.push(row.data); 3392 } 3393 this.settings.columns.forEach(function(col) { 3394 const editable = ctrl.results.editable[col.key]; 3395 if (editable && editable.input_attrs && editable.input_attrs.formula) { 3396 let formula = editable.input_attrs.formula; 3397 const prefix = editable.explicit_join ? (editable.explicit_join + '.') : ''; 3398 formula = formula.replace(/\[/g, '(data["' + prefix); 3399 formula = formula.replace(/]/g, '"] || 0)'); 3400 row.data[col.spec.name] = $scope.$eval(formula, {data: row.data}); 3401 } 3402 }); 3403 }; 3404 3405 this.saveRows = function() { 3406 if (this.saving) { 3407 return this.saving; 3408 } 3409 if (!this.unsaved.length) { 3410 return $q.resolve(); 3411 } 3412 const records = this.unsaved; 3413 this.unsaved = []; 3414 this.saving = crmApi4(getApiName(), 'save', { 3415 records: records, 3416 }).then(function(savedRows) { 3417 ctrl.saving = false; 3418 }, function(error) { 3419 ctrl.saving = false; 3420 }); 3421 return this.saving; 3422 }; 3423 3424 this.getFieldName = function(index, key) { 3425 const rowIndex = ((this.page - 1) * this.limit) + index; 3426 return 'batch-row-' + rowIndex + '-' + _.snakeCase(key); 3427 }; 3428 3429 this.isValid = function() { 3430 return ctrl.formCtrl.$valid; 3431 }; 3432 3433 this.doImport = function() { 3434 if (errorNotification && errorNotification.close) { 3435 errorNotification.close(); 3436 } 3437 if (!this.isValid()) { 3438 this.showValidationErrors(); 3439 return; 3440 } 3441 const tallyMismatches = getTallyMismatches(); 3442 if (tallyMismatches.length) { 3443 let markup = ''; 3444 tallyMismatches.forEach((item, index, array) => { 3445 markup += '<p><i class="crm-i fa-warning" role="img" aria-hidden="true"></i> ' + _.escape(item) + '</p>'; 3446 }); 3447 CRM.confirm({ 3448 title: ts('Totals Mismatch'), 3449 message: markup + '<p>' + _.escape(ts('Run import anyway?')) + '</p>', 3450 options: { 3451 no: ts('Cancel'), 3452 yes: ts('Run Import'), 3453 }, 3454 }).on('crmConfirm:yes', runImport); 3455 } else { 3456 runImport(); 3457 } 3458 }; 3459 3460 function runImport() { 3461 $element.block(); 3462 ctrl.saveRows().then(function() { 3463 crmApi4('SearchDisplay', 'importBatch', { 3464 savedSearch: ctrl.search, 3465 display: ctrl.display, 3466 userJobId: ctrl.userJobId, 3467 }).then(function(result) { 3468 window.location.href = result[0].url; 3469 }); 3470 }); 3471 } 3472 3473 this.showValidationErrors = () => { 3474 const formCtrl = this.formCtrl[this.formName]; 3475 let invalidRows = {}; 3476 let messages = []; 3477 Object.keys(formCtrl).forEach((key) => { 3478 if (key.startsWith('batch-row-') && formCtrl[key].$invalid) { 3479 const rowNum = 1 + parseInt(key.split('-')[2], 10); 3480 const fieldKey = key.split('-')[3]; 3481 const col = this.settings.columns.find(col => _.snakeCase(col.key) === fieldKey); 3482 invalidRows[rowNum] = invalidRows[rowNum] || []; 3483 invalidRows[rowNum].push(col.label); 3484 } 3485 }); 3486 const invalidRowNumbers = Object.keys(invalidRows); 3487 invalidRowNumbers.sort((a, b) => a - b); 3488 let more = 0; 3489 if (invalidRowNumbers.length > 3) { 3490 more = invalidRowNumbers.length - 3; 3491 invalidRowNumbers.length = 3; 3492 } 3493
3494 invalidRowNumbers.forEach((rowNum) => { 3495 messages.push(_.escape(ts('Row %1: %2', {1: rowNum, 2: invalidRows[rowNum].join(', ')}))); 3496 }); 3497 if (more) { 3498 messages.push(_.escape(ts('And %1 more', {1: more}))); 3499 } 3500 3501 errorNotification = CRM.alert( 3502 '<ul><li>' + messages.join('</li><li>') + '</li></ul>', 3503 _.escape(ts('Please complete the following:')), 3504 'error' 3505 ); 3506 }; 3507 3508 this.copyCol = function(index) { 3509 const fieldName = this.settings.columns[index].spec.name; 3510 const value = this.results[0].data[fieldName]; 3511 const updateValue = {}; 3512 updateValue[fieldName] = value; 3513 this.results.forEach((row) => row.data[fieldName] = value); 3514 crmApi4(getApiName(), 'update', { 3515 where: [['_id', '>', this.results[0].data._id]], 3516 values: updateValue, 3517 }); 3518 }; 3519 3520 this.getTally = function(col) { 3521 if (this.isPreviewMode) { 3522 return 0; 3523 } 3524 const fn = col.tally.fn; 3525 let tally = 0; 3526 this.results.forEach(function(row) { 3527 if (row.data[col.spec.name]) { 3528 if (fn === 'COUNT') { 3529 tally++; 3530 } else { 3531 const colVal = Number(row.data[col.spec.name]); 3532 if (!isNaN(colVal)) { 3533 tally += colVal; 3534 } 3535 } 3536 } 3537 }); 3538 if (fn === 'AVG' && this.results.length) { 3539 return tally / this.results.length; 3540 } 3541 return tally; 3542 }; 3543 3544 this.getTallyClass = function(col) { 3545 if (this.isPreviewMode) { 3546 return ''; 3547 } 3548 const tallyTarget = this.getTallyTarget(col); 3549 if (tallyTarget) { 3550 return this.getTally(col) == tallyTarget ? 'text-success' : 'text-danger'; 3551 } 3552 return ''; 3553 }; 3554 3555 this.getTallyTarget = function(col) { 3556 if (col.tally && col.tally.fn === 'SUM' && ctrl.results && ctrl.results.editable[col.key]) { 3557 return ctrl.results.editable[col.key].target; 3558 } 3559 }; 3560 3561 function getTallyMismatches() { 3562 const tallyMismatches = []; 3563 ctrl.settings.columns.forEach(function(col) { 3564 const tallyTarget = ctrl.getTallyTarget(col); 3565 if (tallyTarget) { 3566 const tally = ctrl.getTally(col); 3567 if (tally != tallyTarget) { 3568 tallyMismatches.push(ts('%1 value %2 does not match expected %3', {1: col.label, 2: tally, 3: tallyTarget})); 3569 } 3570 } 3571 }); 3572 return tallyMismatches; 3573 } 3574 3575 function getApiName() { 3576 return 'Import_' + ctrl.userJobId; 3577 } 3578 const _getApiParams = this.getApiParams; 3579 this.getApiParams = function(mode) { 3580 const apiParams = _getApiParams.call(this, mode); 3581 apiParams.userJobId = this.userJobId; 3582 return apiParams; 3583 }; 3584 this.onPreRun.push(function(apiCalls) { 3585 apiCalls.run[1] = 'runBatch'; 3586 }); 3587 3588 } 3589 }); 3590 3591 3592 angular.module('crmSearchDisplayGrid', CRM.angRequires('crmSearchDisplayGrid')); 3593 3594 3595 3596 angular.module('crmSearchDisplayGrid').component('crmSearchDisplayGrid', { 3597 bindings: { 3598 apiEntity: '@', 3599 search: '<', 3600 display: '<', 3601 apiParams: '<', 3602 settings: '<', 3603 filters: '<', 3604 totalCount: '=?' 3605 }, 3606 require: { 3607 afFieldset: '?^^afFieldset' 3608 }, 3609 templateUrl: '~/crmSearchDisplayGrid/crmSearchDisplayGrid.html', 3610 controller: function($scope, $element, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplayEditableTrait) { 3611 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 3612 ctrl = angular.extend(this, _.cloneDeep(searchDisplayBaseTrait), _.cloneDeep(searchDisplayTasksTrait), _.cloneDeep(searchDisplayEditableTrait)); 3613 3614 this.$onInit = function() { 3615 this.initializeDisplay($scope, $element); 3616 }; 3617 3618 } 3619 }); 3620 3621 3622 angular.module('crmSearchDisplayList', CRM.angRequires('crmSearchDisplayList')); 3623 3624 3625 3626 angular.module('crmSearchDisplayList').component('crmSearchDisplayList', { 3627 bindings: { 3628 apiEntity: '@', 3629 search: '<', 3630 display: '<', 3631 apiParams: '<', 3632 settings: '<', 3633 filters: '<', 3634 totalCount: '=?' 3635 }, 3636 require: { 3637 afFieldset: '?^^afFieldset' 3638 }, 3639 templateUrl: '~/crmSearchDisplayList/crmSearchDisplayList.html', 3640 controller: function($scope, $element, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplayEditableTrait) { 3641 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 3642 ctrl = angular.extend(this, _.cloneDeep(searchDisplayBaseTrait), _.cloneDeep(searchDisplayTasksTrait), _.cloneDeep(searchDisplayEditableTrait)); 3643 3644 this.$onInit = function() { 3645 this.initializeDisplay($scope, $element); 3646 }; 3647 3648 } 3649 }); 3650 3651
3652 angular.module('crmSearchDisplayTable', CRM.angRequires('crmSearchDisplayTable')); 3653 3654 3655 3656 angular.module('crmSearchDisplayTable').component('crmSearchDisplayTable', { 3657 bindings: { 3658 apiEntity: '@', 3659 search: '<', 3660 display: '<', 3661 settings: '<', 3662 filters: '<', 3663 totalCount: '=?' 3664 }, 3665 require: { 3666 afFieldset: '?^^afFieldset' 3667 }, 3668 templateUrl: '~/crmSearchDisplayTable/crmSearchDisplayTable.html', 3669 controller: function($scope, $element, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplaySortableTrait, searchDisplayEditableTrait, crmApi4, crmStatus) { 3670 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 3671 ctrl = angular.extend(this, _.cloneDeep(searchDisplayBaseTrait), _.cloneDeep(searchDisplayTasksTrait), _.cloneDeep(searchDisplaySortableTrait), _.cloneDeep(searchDisplayEditableTrait)); 3672 3673 this.$onInit = function() { 3674 let tallyParams; 3675 3676 ctrl.onPreRun.push(this.trackFetchedColumns); 3677 if (ctrl.settings.tally) { 3678 ctrl.onPreRun.push(function (apiCalls) { 3679 ctrl.tally = null; 3680 tallyParams = _.cloneDeep(apiCalls.run[2]); 3681 }); 3682 3683 ctrl.onPostRun.push(function (apiResults, status) { 3684 ctrl.tally = null; 3685 if (status === 'success' && tallyParams) { 3686 tallyParams.return = 'tally'; 3687 crmApi4('SearchDisplay', 'run', tallyParams).then(function (result) { 3688 ctrl.tally = result[0]; 3689 }); 3690 } 3691 }); 3692 } 3693 3694 this.initializeDisplay($scope, $element); 3695 3696 if (ctrl.settings.draggable) { 3697 ctrl.draggableOptions = { 3698 containment: $element.children('div').first(), 3699 handle: '.crm-draggable', 3700 forcePlaceholderSize: true, 3701 helper: function(e, ui) { 3702 ui.children().each(function() { 3703 $(this).width($(this).width()); 3704 }); 3705 return ui; 3706 }, 3707 stop: function(e, ui) { 3708 const movedItem = ui.item.sortable.model, 3709 oldPosition = ui.item.sortable.index, 3710 newPosition = ctrl.results.indexOf(movedItem), 3711 displacement = newPosition < oldPosition ? -1 : 1, 3712 displacedItem = ctrl.results[newPosition - displacement]; 3713 if (newPosition > -1 && oldPosition !== newPosition) { 3714 updateDraggableWeights(movedItem.key, displacedItem.data); 3715 } 3716 } 3717 }; 3718 } 3719 if (this.afFieldset) { 3720 $scope.$on('afFormReset', () => this.onAfformReset()); 3721 } 3722 }; 3723 3724 this.onAfformReset = () => { 3725 const savedSearchParamSet = this.afFieldset.selectedSearchParamSet; 3726 if (!savedSearchParamSet) { 3727 this.columns.forEach((col) => col.enabled = true); 3728 return; 3729 } 3730 const columns = savedSearchParamSet.columns ? savedSearchParamSet.columns[this.getSearchDisplayKey()] : null; 3731 if (!columns || !Object.keys(columns).length) { 3732 this.columns.forEach((col) => col.enabled = true); 3733 return; 3734 } 3735 this.columns.forEach((col) => col.enabled = false); 3736 Object.keys(columns).forEach((keyOrLabel) => { 3737 const findByKey = this.columns.findIndex((col) => col.key === keyOrLabel); 3738 if (findByKey > -1) { 3739 this.columns[findByKey].enabled = true; 3740 return; 3741 } 3742 const findByLabel = this.columns.findIndex((col) => this.getColumnToggleLabel(col) === keyOrLabel); 3743 if (findByLabel > -1) { 3744 this.columns[findByLabel].enabled = true; 3745 return; 3746 } 3747 }); 3748 this.toggleColumns(); 3749 }; 3750 3751 function updateDraggableWeights(key, data) { 3752 const weightField = ctrl.settings.draggable; 3753 const newWeight = data[weightField]; 3754 const apiParams = ctrl.getApiParams('draggableWeight'); 3755 apiParams.rowKey = key; 3756 apiParams.values = {}; 3757 apiParams.values[weightField] = newWeight; 3758 crmStatus({}, crmApi4('SearchDisplay', 'inlineEdit', apiParams)) 3759 .then(function(newWeights) { 3760 const weightColumn = ctrl.settings.columns.findIndex(col => col.key === weightField);
3761 ctrl.results.forEach(function(row) { 3762 if (row.key in newWeights) { 3763 row.data[weightField] = newWeights[row.key]; 3764 if (weightColumn >= 0) { 3765 row.columns[weightColumn].val = newWeights[row.key]; 3766 row.columns[weightColumn] = JSON.parse(angular.toJson(row.columns[weightColumn])); 3767 } 3768 } 3769 }); 3770 }); 3771 } 3772 this.getHeaderClass = function (column) { 3773 let headerClasses = []; 3774 if (ctrl.isSortable(column)) { 3775 headerClasses.push('crm-sortable-col'); 3776 } 3777 if (column.alignment) { 3778 headerClasses.push(column.alignment); 3779 } 3780 if (Array.isArray(column.cssRules)) { 3781 column.cssRules.forEach(cssRule => { 3782 if (cssRule.length === 1) { 3783 headerClasses.push(cssRule[0]); 3784 } 3785 }); 3786 } 3787 return headerClasses.join(' '); 3788 }; 3789 3790 this.getRowClass = function (row) { 3791 let cssClass = ''; 3792 if (ctrl.settings.hierarchical) { 3793 cssClass += ' crm-hierarchical-row crm-hierarchical-depth-' + row.data._depth; 3794 if (row.data._depth) { 3795 cssClass += ' crm-hierarchical-child'; 3796 } 3797 if (row.data._descendents) { 3798 cssClass += ' crm-hierarchical-parent'; 3799 } 3800 } 3801 return cssClass; 3802 }; 3803 3804 this.getColumnToggleLabel = (col) => { 3805 if (col.label) { 3806 return col.label; 3807 } 3808 if (col.key) { 3809 return `[${col.key}]`; 3810 } 3811 if (col.type === 'menu') { 3812 return ts('Menu'); 3813 } 3814 if (col.type === 'buttons') { 3815 return ts('Buttons'); 3816 } 3817 if (col.type === 'links') { 3818 return ts('Links'); 3819 } 3820 return ts('Column %1', {1: col.index}); 3821 }; 3822 3823 /** 3824 * Update the settings for which columns to include. 3825 * 3826 * If including columns that we haven't fetched, trigger a refetch 3827 */ 3828 this.toggleColumns = () => { 3829 if (this.columns.find((col) => col.enabled && !col.fetched)) { 3830 this.getResultsPronto(); 3831 } 3832 }; 3833 3834 /** 3835 * Get a list of currently selected columns, for saving in SearchParamSet 3836 * Unfortunately not all columns have keys, so we sometimes save the label, 3837 * which may not be unique :( 3838 * 3839 * @returns string[] 3840 */ 3841 this.getToggledColumns = () => { 3842 const toggledColumns = this.columns.filter((col) => col.enabled); 3843 if (!toggledColumns.length || toggledColumns.length === this.columns.length) { 3844 return []; 3845 } 3846 const keyLabelMap = {}; 3847 toggledColumns.forEach((col) => { 3848 const label = this.getColumnToggleLabel(col); 3849 const key = col.key ? col.key : label; 3850 keyLabelMap[key] = label; 3851 }); 3852 return keyLabelMap; 3853 }; 3854 3855 this.resetColumnToggles = () => { 3856 this.columns.forEach((col, index) => { 3857 this.columns[index].enabled = true; 3858 }); 3859 this.toggleColumns(); 3860 }; 3861 3862 this.clearColumnToggles = () => { 3863 this.columns.forEach((col, index) => { 3864 this.columns[index].enabled = false; 3865 }); 3866 this.toggleColumns(); 3867 }; 3868 3869 /** 3870 * Keep track of which columns we have fetched each 3871 * time we fetch results. This allows us to only 3872 * refetch if we need columns we don't have (we can 3873 * hide columns without refetching) 3874 */ 3875 this.trackFetchedColumns = () => { 3876 this.columns.forEach((col) => { 3877 col.fetched = col.enabled; 3878 }); 3879 }; 3880 3881 this.onToggleDisclosure = (col, event) => { 3882 col.hasBeenOpened = true; 3883 const detailsElement = event.target.closest('details.crm-search-display-subsearch-dropdown'); 3884 if (detailsElement && !detailsElement.open) { 3885 detailsElement.closest('.crm-search-display-table') 3886 .querySelectorAll('details.crm-search-display-subsearch-dropdown[open]') 3887 .forEach((details) => details.open = false); 3888 } 3889 }; 3890 3891 } 3892 }); 3893 3894 3895
3896 angular.module('crmSearchDisplayTable').component('crmSearchDisplayToggleCollapse', { 3897 bindings: { 3898 rows: '<', 3899 rowIndex: '<', 3900 defaultCollapsed: '<', 3901 }, 3902 templateUrl: '~/crmSearchDisplayTable/crmSearchDisplayToggleCollapse.html', 3903 controller: function($scope, $element) { 3904 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 3905 ctrl = this; 3906 3907 this.$onInit = function() { 3908 const row = this.getRow(); 3909 if (!('collapsed' in row)) { 3910 row.collapsed = row.data._descendents && this.defaultCollapsed === 'closed'; 3911 } 3912 if (row.collapsed && row.data._descendents) { 3913 row.collapsed = false; 3914 this.toggleCollapsed(); 3915 } else { 3916 row.collapsed = false; 3917 } 3918 if (!row.data._descendents) { 3919 $element.css('visibility', 'hidden'); 3920 } 3921 }; 3922 3923 this.getRow = function () { 3924 return this.rows[this.rowIndex]; 3925 }; 3926 3927 this.isCollapsed = function() { 3928 return this.getRow().collapsed && this.getRow().data._descendents; 3929 }; 3930 3931 this.countDescendents = function () { 3932 return this.getRow().data._descendents; 3933 }; 3934 3935 this.toggleCollapsed = function() { 3936 const row = this.getRow(); 3937 row.collapsed = !row.collapsed; 3938 3939 let descendentsEnd = this.rowIndex + row.data._descendents; 3940 for (let i = this.rowIndex + 1; i <= descendentsEnd; i++) { 3941 let hide = row.collapsed; 3942 if (!ctrl.rows[i]) { 3943 return; 3944 } 3945 ctrl.rows[i].hidden = hide; 3946 if (!hide && ctrl.rows[i].collapsed) { 3947 i += ctrl.rows[i].data._descendents; 3948 } 3949 } 3950 }; 3951 3952 } 3953 }); 3954 3955 3956 angular.module('crmSearchDisplayTree', CRM.angRequires('crmSearchDisplayTree')); 3957 3958 3959 3960 angular.module('crmSearchDisplayTree').component('crmSearchDisplayTree', { 3961 bindings: { 3962 apiEntity: '@', 3963 search: '<', 3964 display: '<', 3965 apiParams: '<', 3966 settings: '<', 3967 filters: '<', 3968 totalCount: '=?' 3969 }, 3970 require: { 3971 afFieldset: '?^^afFieldset' 3972 }, 3973 templateUrl: '~/crmSearchDisplayTree/crmSearchDisplayTree.html', 3974 controller: function($scope, $element, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplayEditableTrait) { 3975 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'); 3976 const ctrl = angular.extend(this, _.cloneDeep(searchDisplayBaseTrait), _.cloneDeep(searchDisplayTasksTrait), _.cloneDeep(searchDisplayEditableTrait)); 3977 3978 this.tree = []; 3979 3980 this.$onInit = function() { 3981 this.onPostRun.push(function(apiResults, status) { 3982 if (status === 'success') { 3983 ctrl.tree = sortTree(apiResults.run); 3984 } 3985 }); 3986 3987 this.initializeDisplay($scope, $element); 3988 }; 3989 function sortTree(results) { 3990 const parentField = ctrl.settings.parent_field; 3991 if (!parentField) { 3992 return results; 3993 } 3994 const indexedResults = results.reduce((acc, item) => { 3995 item.children = []; 3996 acc[item.key] = item; 3997 return acc; 3998 }, {}); 3999 results.forEach(item => { 4000 const parentKey = item.data[parentField]; 4001 if (parentKey && indexedResults[parentKey]) { 4002 indexedResults[parentKey].children.push(item); 4003 indexedResults[parentKey].collapsed = ctrl.settings.collapsible && ctrl.settings.collapsible === 'closed'; 4004 } 4005 }); 4006 return results.filter(item => !item.data[parentField]); 4007 } 4008 4009 } 4010 }); 4011 4012 4013 4014 angular.module('crmSearchDisplayTree').component('crmSearchDisplayTreeBranch', { 4015 bindings: { 4016 items: '<', 4017 parentKey: '<', 4018 }, 4019 require: { 4020 displayCtrl: '^crmSearchDisplayTree', 4021 }, 4022 templateUrl: '~/crmSearchDisplayTree/crmSearchDisplayTreeBranch.html', 4023 controllerAs: '$branch', 4024 controller: function($scope, $element, crmApi4, crmStatus) { 4025 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'); 4026 const ctrl = this; 4027 4028 this.$onInit = function() { 4029 $scope.$ctrl = this.displayCtrl; 4030 4031 ctrl.draggableOptions = { 4032 handle: '.crm-draggable', 4033 connectWith: 'crm-search-display-tree [ui-sortable]', 4034 placeholder: 'crm-search-display-tree-placeholder', 4035 tolerance: 'pointer', // seems to work better for dragging large items into small gaps 4036 update: function(e, ui) { 4037 const movedItem = ui.item.sortable.model; 4038 let newPosition = ui.item.sortable.dropindex; 4039 let displacedItem; 4040 if (ui.item.sortable.source[0] === ui.item.sortable.droptarget[0]) { 4041 displacedItem = ctrl.items[newPosition]; 4042 updateDraggableWeights(movedItem, displacedItem); 4043 } 4044 else if (ui.sender) { 4045 displacedItem = ctrl.items[newPosition + 1]; 4046 updateDraggableWeights(movedItem, displacedItem); 4047 } 4048 } 4049 }; 4050 }; 4051 4052 function updateDraggableWeights(movedItem, displacedItem) { 4053 const weightField = ctrl.displayCtrl.settings.draggable; 4054 const parentField = ctrl.displayCtrl.settings.parent_field; 4055 const apiParams = ctrl.displayCtrl.getApiParams('draggableWeight'); 4056 apiParams.rowKey = movedItem.key; 4057 apiParams.values = {}; 4058 if (displacedItem) { 4059 apiParams.values[weightField] = displacedItem.data[weightField]; 4060 } 4061 else if (ctrl.items.length > 1) { 4062 apiParams.values[weightField] = ctrl.items[ctrl.items.length - 1].data[weightField] + 1; 4063 } 4064 else { 4065 apiParams.values[weightField] = 1; 4066 } 4067 apiParams.values[parentField] = ctrl.parentKey; 4068 crmStatus({}, crmApi4('SearchDisplay', 'inlineEdit', apiParams)) 4069 .then(function(newWeights) { 4070 const weightColumn = ctrl.displayCtrl.settings.columns.findIndex(col => col.key === weightField);
4071 ctrl.displayCtrl.results.forEach(function(row) { 4072 if (row.key in newWeights) { 4073 row.data[weightField] = newWeights[row.key]; 4074 if (weightColumn >= 0) { 4075 row.columns[weightColumn].val = newWeights[row.key]; 4076 row.columns[weightColumn] = JSON.parse(angular.toJson(row.columns[weightColumn])); 4077 } 4078 } 4079 }); 4080 }); 4081 } 4082 4083 } 4084 }); 4085 4086 4087 angular.module('crmSearchTasks', CRM.angRequires('crmSearchTasks')); 4088 4089 4090 4091 angular.module('crmSearchTasks').component('crmSearchBatchRunner', { 4092 bindings: { 4093 entity: '<', 4094 action: '@', 4095 ids: '<', 4096 idField: '@', 4097 params: '<', 4098 displayCtrl: '<', 4099 isLink: '<', 4100 success: '&', 4101 error: '&' 4102 }, 4103 templateUrl: '~/crmSearchTasks/crmSearchBatchRunner.html', 4104 controller: function($scope, $timeout, $interval, crmApi4) { 4105 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'); 4106 const ctrl = this; 4107 4108 let currentBatch = 0; 4109 let totalBatches; 4110 let processedCount = 0; 4111 let countMatched = 0; 4112 let incrementer; 4113 let batchResult; 4114 4115 this.progress = 0; 4116 const BATCH_SIZE = 500; 4117 const EST_BATCH_TIME = 5; 4118 4119 this.$onInit = function() { 4120 if (ctrl.action === 'create' && !ctrl.idField) { 4121 ctrl.ids = [0]; 4122 } 4123 totalBatches = Math.ceil(ctrl.ids.length / BATCH_SIZE); 4124 runBatch(); 4125 }; 4126 4127 this.$onDestroy = function() { 4128 stopIncrementer(); 4129 }; 4130 4131 function runBatch() { 4132 let entityName = ctrl.entity; 4133 let actionName = ctrl.action; 4134 ctrl.first = currentBatch * BATCH_SIZE; 4135 ctrl.last = (currentBatch + 1) * BATCH_SIZE; 4136 if (ctrl.last > ctrl.ids.length) { 4137 ctrl.last = ctrl.ids.length; 4138 } 4139 const params = _.cloneDeep(ctrl.params); 4140 if (ctrl.action === 'save' || (ctrl.action === 'create' && ctrl.idField)) { 4141 actionName = 'save'; 4142 let originalRecords = params.records || [{}]; 4143 if ('values' in params) { 4144 originalRecords = [params.values]; 4145 delete params.values; 4146 } 4147 params.records = _.transform(ctrl.ids.slice(ctrl.first, ctrl.last), function(records, id) { 4148 _.each(_.cloneDeep(originalRecords), function(record) { 4149 record[ctrl.idField || 'id'] = id; 4150 records.push(record); 4151 }); 4152 }); 4153 } else if (ctrl.isLink && ctrl.action === 'update' && ctrl.ids.length === 1 && ctrl.displayCtrl) { 4154 entityName = 'SearchDisplay'; 4155 actionName = 'inlineEdit'; 4156 angular.extend(params, ctrl.displayCtrl.getApiParams(null)); 4157 delete params.where; 4158 params.rowKey = ctrl.ids[0]; 4159 } else if (ctrl.action !== 'create') { 4160 params.where = params.where || []; 4161 params.where.push([ctrl.idField || 'id', 'IN', ctrl.ids.slice(ctrl.first, ctrl.last)]); 4162 } 4163 crmApi4(entityName, actionName, params).then( 4164 function(result) { 4165 stopIncrementer(); 4166 ctrl.progress = Math.floor(100 * ++currentBatch / totalBatches); 4167 processedCount += result.countFetched; 4168 countMatched += ('countMatched' in result ? result.countMatched : result.count); 4169 if (batchResult) { 4170 batchResult.push(...result); 4171 } else { 4172 batchResult = result; 4173 } 4174 if (ctrl.last >= ctrl.ids.length) { 4175 $timeout(function() { 4176 batchResult.batchCount = processedCount; 4177 batchResult.countMatched = countMatched; 4178 ctrl.success({result: batchResult}); 4179 }, 500); 4180 } else { 4181 runBatch(); 4182 } 4183 }, function(error) { 4184 ctrl.error({error: error}); 4185 }); 4186 incrementer = $interval(function(i) { 4187 const est = Math.floor(100 * (currentBatch + (i / EST_BATCH_TIME)) / totalBatches); 4188 ctrl.progress = est > 100 ? 100 : est; 4189 }, 1000, EST_BATCH_TIME); 4190 } 4191 4192 function stopIncrementer() { 4193 if (angular.isDefined(incrementer)) { 4194 $interval.cancel(incrementer); 4195 incrementer = undefined; 4196 } 4197 } 4198 4199 } 4200 }); 4201 4202 4203
4204 angular.module('crmSearchTasks') 4205 .directive('crmMultiSelectDate', function () { 4206 return { 4207 restrict: 'A', 4208 require: 'ngModel', 4209 link: function (scope, element, attrs, ngModel) { 4210 4211 let defaultDate = null; 4212 4213 function getDisplayDate(date) { 4214 return $.datepicker.formatDate(CRM.config.dateInputFormat, $.datepicker.parseDate('yy-mm-dd', date)); 4215 } 4216 4217 ngModel.$render = function () { 4218 element.val(Array.isArray(ngModel.$viewValue) ? ngModel.$viewValue.join(',') : ngModel.$viewValue).change(); 4219 }; 4220 4221 element 4222 .crmSelect2({ 4223 multiple: true, 4224 data: [], 4225 initSelection: function(element, callback) { 4226 const values = []; 4227 $.each($(element).val().split(','), function(k, v) { 4228 values.push({ 4229 text: getDisplayDate(v), 4230 id: v 4231 }); 4232 }); 4233 callback(values); 4234 } 4235 }) 4236 .on('select2-opening', function(e) { 4237 const $el = $(this), 4238 $input = $('.select2-search-field input', $el.select2('container')); 4239 e.preventDefault(); 4240 if (!$input.data('datepicker')) { 4241 $input 4242 .datepicker({ 4243 beforeShow: function() { 4244 const existingSelections = _.pluck($el.select2('data') || [], 'id'); 4245 return { 4246 changeMonth: true, 4247 changeYear: true, 4248 defaultDate: defaultDate, 4249 beforeShowDay: function(date) { 4250 const dateStr = $.datepicker.formatDate('yy-mm-dd', date); 4251 if (_.includes(existingSelections, dateStr)) { 4252 return [false, '', '']; 4253 } 4254 return [true, '', '']; 4255 } 4256 }; 4257 } 4258 }) 4259 .datepicker('show') 4260 .on('change.crmDate', function() { 4261 if ($(this).val()) { 4262 const data = $el.select2('data') || []; 4263 defaultDate = $(this).datepicker('getDate'); 4264 data.push({ 4265 text: $.datepicker.formatDate(CRM.config.dateInputFormat, defaultDate), 4266 id: $.datepicker.formatDate('yy-mm-dd', defaultDate) 4267 }); 4268 $el.select2('data', data, true); 4269 } 4270 }) 4271 .on('keyup', function() { 4272 $(this).val('').datepicker('show'); 4273 }); 4274 } 4275 }) 4276 .on('select2-removed', function() { 4277 $('input.hasDatepicker', $(this).select2('container')) 4278 .datepicker('hide'); 4279 }) 4280 .on('change', function() { 4281 ngModel.$setViewValue(element.val().split(',')); 4282 }); 4283 } 4284 }; 4285 }); 4286 4287 let searchInputInstance = 0; 4288 4289 angular.module('crmSearchTasks').component('crmSearchInput', { 4290 bindings: { 4291 field: '<', 4292 op: '<', 4293 format: '<', 4294 optionKey: '<', 4295 showLabel: '<', 4296 name: '@', 4297 }, 4298 require: {ngModel: 'ngModel'}, 4299 templateUrl: '~/crmSearchTasks/crmSearchInput/crmSearchInput.html', 4300 controller: function($scope) { 4301 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 4302 ctrl = this; 4303 4304 this.domId = 'search-input-' + searchInputInstance++; 4305 4306 this.$onInit = function() { 4307 4308 $scope.$watch('$ctrl.value', function() { 4309 ctrl.ngModel.$setViewValue(ctrl.value); 4310 }); 4311 ctrl.ngModel.$parsers.push(function(viewValue) { 4312 return ctrl.format === 'json' && typeof viewValue === 'string' && viewValue.length ? JSON.stringify(viewValue) : viewValue; 4313 }); 4314 ctrl.ngModel.$formatters.push(function(value) { 4315 return ctrl.format === 'json' && typeof value === 'string' && value.length ? JSON.parse(value) : value; 4316 }); 4317 4318 this.ngModel.$render = function() { 4319 ctrl.value = formatDataType(ctrl.ngModel.$viewValue); 4320 }; 4321 4322 function formatDataType(val) { 4323 if (val === null || val === undefined) { 4324 return val; 4325 } 4326 if (ctrl.optionKey && ctrl.optionKey !== 'id') { 4327 return val; 4328 } 4329 if (ctrl.op && ctrl.op.includes('REGEXP')) { 4330 return val; 4331 } 4332 if (Array.isArray(val)) { 4333 const formatted = angular.copy(val); 4334 formatted.forEach((v, i) => formatted[i] = formatDataType(v)); 4335 return formatted; 4336 } 4337 if (['Integer', 'Float'].includes(ctrl.field ? ctrl.field.data_type : null) && ctrl.field.category !== 'partial_date') { 4338 let newVal = Number(val); 4339 if ((ctrl.field.name === 'id' || ctrl.field.fk_entity) && ('' + newVal) !== val) { 4340 return val; 4341 } 4342 return newVal; 4343 } 4344 return val; 4345 } 4346 4347 }; 4348 } 4349 }); 4350 4351})(angular, CRM.$, CRM._); 4352 4353(function(angular, $) { 4354 "use strict"; 4355
4356 angular.module('crmSearchTasks').directive('crmSearchInputFocus', function($timeout) { 4357 function waitForElement(selector, context = document) { 4358 return new Promise((resolve) => { 4359 const observer = new MutationObserver((mutations, observer) => { 4360 const match = $(selector, context); 4361 if (match.length) { 4362 observer.disconnect(); 4363 resolve(match); 4364 } 4365 }); 4366 4367 observer.observe(context, { 4368 childList: true, 4369 subtree: true, 4370 }); 4371 }); 4372 } 4373 4374 return { 4375 link: function(scope, element, attrs) { 4376 function getFocusableInput() { 4377 const types = new Map ([ 4378 ['select2', 'input[crm-ui-select], input[crm-autocomplete]'], 4379 ['datepicker', 'input[crm-ui-datepicker]'], 4380 ['boolean', 'input[type="radio"], input[type="checkbox"]'], 4381 ['default', ':focusable'] 4382 ]); 4383 for (const [type, selector] of types) { 4384 const jQueryObject = $(selector, element); 4385 if (jQueryObject.length > 0 || type === 'default') { 4386 return [jQueryObject, type]; 4387 } 4388 } 4389 } 4390 4391 function focusOn() { 4392 let [jQueryObject, type] = getFocusableInput(); 4393 4394 if (type === 'default') { 4395 jQueryObject.first().trigger('focus'); 4396 } 4397 4398 else if (type === 'boolean') { 4399 let checkedItem = $(':checked', element); 4400 if (checkedItem.length) { 4401 jQueryObject = checkedItem; 4402 } 4403 jQueryObject.first().trigger('focus'); 4404 } 4405 4406 else if (type === 'select2') { 4407 waitForElement('.select2-choice', element[0]).then((elements) => { 4408 $timeout(() => { 4409 const container = elements.first().closest('.select2-container'); 4410 container.select2('open'); 4411 $(element[0]).one('initSelectionComplete', () => { 4412 container.select2('open'); 4413 }); 4414 }); 4415 }); 4416 } 4417 4418 else if (type === 'datepicker') { 4419 waitForElement('.hasDatepicker', element[0]).then((elements) => { 4420 elements.first().datepicker('show'); 4421 }); 4422 } 4423 } 4424 4425 scope.$watch(attrs.crmSearchInputFocus, function(flag) { 4426 if(flag === true) { 4427 $timeout(focusOn); 4428 } 4429 }); 4430 } 4431 }; 4432 }); 4433 4434})(angular, CRM.$); 4435 4436(function(angular, $, _) { 4437 "use strict"; 4438 4439 angular.module('crmSearchTasks').component('crmSearchInputVal', { 4440 bindings: { 4441 op: '<', 4442 labelId: '@', 4443 }, 4444 require: { 4445 ngModel: 'ngModel', 4446 input: '^crmSearchInput' 4447 }, 4448 template: '<div class="form-group" ng-include="$ctrl.getTemplate()"></div>', 4449 controller: function($scope, formatForSelect2, crmApi4) { 4450 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 4451 ctrl = this; 4452 4453 this.$onInit = function() { 4454 const field = getField(); 4455 let rendered = false; 4456 this.dateRanges = CRM.crmSearchTasks.dateRanges; 4457 4458 this.ngModel.$render = function() { 4459 ctrl.value = ctrl.ngModel.$viewValue; 4460 if (!rendered && isDateField(field)) { 4461 setDateType(); 4462 } 4463 rendered = true; 4464 }; 4465 4466 $scope.$watch('$ctrl.value', function() { 4467 ctrl.ngModel.$setViewValue(ctrl.value); 4468 }); 4469 4470 function setDateType() { 4471 if (_.findWhere(ctrl.dateRanges, {id: ctrl.value})) { 4472 ctrl.dateType = 'range'; 4473 } else if (ctrl.value === 'now') { 4474 ctrl.dateType = 'now'; 4475 } else if (_.includes(ctrl.value, 'now -')) { 4476 ctrl.dateType = 'now -'; 4477 } else if (_.includes(ctrl.value, 'now +')) { 4478 ctrl.dateType = 'now +'; 4479 } else { 4480 ctrl.dateType = 'fixed'; 4481 } 4482 } 4483 }; 4484 4485 this.getFkEntity = function() { 4486 const field = getField(); 4487 return field.fk_entity || field.entity || null; 4488 }; 4489 4490 const autocompleteStaticOptions = { 4491 Contact: ['user_contact_id'], 4492 Individual: ['user_contact_id'], 4493 '': [] 4494 }; 4495 4496 this.getAutocompleteStaticOptions = function() { 4497 if (ctrl.op) { 4498 return autocompleteStaticOptions[ctrl.getFkEntity() || ''] || autocompleteStaticOptions['']; 4499 } 4500 return autocompleteStaticOptions['']; 4501 }; 4502 4503 this.isMulti = function() { 4504 if (ctrl.op) { 4505 return ctrl.op === 'IN' || ctrl.op === 'NOT IN' || ctrl.op === 'CONTAINS' || ctrl.op === 'NOT CONTAINS' || ctrl.op === 'CONTAINS ONE OF' || ctrl.op === 'NOT CONTAINS ONE OF'; 4506 }
4507 return ctrl.input.field && (ctrl.input.field.serialize || ctrl.input.field.data_type === 'Array') ? true : null; 4508 }; 4509 4510 this.changeDateType = function() { 4511 switch (ctrl.dateType) { 4512 case 'fixed': 4513 ctrl.value = ''; 4514 break; 4515 4516 case 'range': 4517 ctrl.value = ctrl.dateRanges[0].id; 4518 break; 4519 4520 case 'now': 4521 ctrl.value = 'now'; 4522 break; 4523 4524 default: 4525 ctrl.value = ctrl.dateType + ' 1 day'; 4526 } 4527 }; 4528 4529 this.dateUnits = function(setUnit) { 4530 const vals = ctrl.value.split(' '); 4531 if (arguments.length) { 4532 vals[3] = setUnit; 4533 ctrl.value = vals.join(' '); 4534 } else { 4535 return vals[3]; 4536 } 4537 }; 4538 4539 this.dateNumber = function(setNumber) { 4540 const vals = ctrl.value.split(' '); 4541 if (arguments.length) { 4542 vals[2] = setNumber; 4543 ctrl.value = vals.join(' '); 4544 } else { 4545 return parseInt(vals[2], 10); 4546 } 4547 }; 4548 4549 this.lookupAddress = function() { 4550 ctrl.value.geo_code_1 = null; 4551 ctrl.value.geo_code_2 = null; 4552 if (ctrl.value.address) { 4553 crmApi4('Address', 'getCoordinates', { 4554 address: ctrl.value.address 4555 }).then(function(coordinates) { 4556 if (coordinates[0]) { 4557 ctrl.value.geo_code_1 = coordinates[0].geo_code_1; 4558 ctrl.value.geo_code_2 = coordinates[0].geo_code_2; 4559 } 4560 }); 4561 } 4562 }; 4563 4564 this.getTemplate = function() { 4565 const field = getField(); 4566 4567 if (['LIKE', 'NOT LIKE', 'REGEXP', 'NOT REGEXP', 'REGEXP BINARY', 'NOT REGEXP BINARY'].includes(ctrl.op)) { 4568 return '~/crmSearchTasks/crmSearchInput/text.html'; 4569 } 4570 4571 if (field.input_type === 'Location') { 4572 ctrl.value = ctrl.value || {distance_unit: CRM.crmSearchAdmin.defaultDistanceUnit}; 4573 return '~/crmSearchTasks/crmSearchInput/location.html'; 4574 } 4575 4576 if (isDateField(field)) { 4577 return '~/crmSearchTasks/crmSearchInput/date.html'; 4578 } 4579 4580 if (field.data_type === 'Boolean') { 4581 return '~/crmSearchTasks/crmSearchInput/boolean.html'; 4582 } 4583 4584 if (!['>', '<', '>=', '<='].includes(ctrl.op)) { 4585 if (field.options && (!field.suffixes || field.suffixes.includes('name'))) { 4586 return '~/crmSearchTasks/crmSearchInput/select.html'; 4587 } 4588 if (field.fk_entity || field.name === 'id') { 4589 return '~/crmSearchTasks/crmSearchInput/entityRef.html'; 4590 } 4591 } 4592 4593 if (field.data_type === 'Integer') { 4594 return '~/crmSearchTasks/crmSearchInput/integer.html'; 4595 } 4596 4597 if (field.data_type === 'Float') { 4598 return '~/crmSearchTasks/crmSearchInput/float.html'; 4599 } 4600 4601 if (field.input_type === 'Email') { 4602 return '~/crmSearchTasks/crmSearchInput/email.html'; 4603 } 4604 4605 if (field.input_type === 'TextArea') { 4606 return '~/crmSearchTasks/crmSearchInput/textArea.html'; 4607 } 4608 4609 return '~/crmSearchTasks/crmSearchInput/text.html'; 4610 }; 4611 4612 this.getFieldOptions = function() { 4613 const field = getField(); 4614 return {results: formatForSelect2(field.options || [], ctrl.input.optionKey || 'id', 'label', ['description', 'color', 'icon'])}; 4615 }; 4616 4617 function getField() { 4618 return ctrl.input.field || {}; 4619 } 4620 4621 function isDateField(field) { 4622 return field.category === 'partial_date' || field.data_type === 'Date' || field.data_type === 'Timestamp'; 4623 } 4624 4625 } 4626 }); 4627 4628 4629 angular.module('crmSearchTasks').controller('crmSearchTaskApiBatch', function($scope, searchTaskBaseTrait) { 4630 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 4631 ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait); 4632 4633 this.apiBatch = $scope.apiBatch = this.task.apiBatch; 4634 this.entityTitle = this.getEntityTitle(); 4635 this.apiBatch.entity = this.apiBatch.entity || this.entity; 4636 this.apiBatch.idField = this.apiBatch.idField || this.entity
4636Info.primary_key[0]; 4637 if (!ctrl.apiBatch.confirmMsg && !ctrl.apiBatch.fields) { 4638 ctrl.start(ctrl.apiBatch.params); 4639 } 4640 4641 if (ctrl.apiBatch.fields) { 4642 ctrl.apiBatch.params = ctrl.apiBatch.params || {}; 4643 ctrl.apiBatch.params.values = ctrl.apiBatch.params.values || {}; 4644 ctrl.apiBatch.fields.forEach((field) => { 4645 let value = ''; 4646 if ('default_value' in field) { 4647 value = field.default_value; 4648 } else if (field.serialize || field.data_type === 'Array') { 4649 value = []; 4650 } else if (field.data_type === 'Boolean') { 4651 value = true; 4652 } else if (field.options && field.options.length) { 4653 value = field.options[0].id; 4654 } 4655 ctrl.apiBatch.params.values[field.name] = value; 4656 }); 4657 } 4658 4659 this.onSuccess = function(result) { 4660 const entityTitle = this.getEntityTitle(result.batchCount); 4661 if (result.action === 'inlineEdit') { 4662 CRM.status(ts('Saved')); 4663 } else { 4664 if (ctrl.apiBatch.successMsg) { 4665 CRM.alert(ts(ctrl.apiBatch.successMsg, { 1: result.batchCount, 2: entityTitle }), ts('%1 Complete', { 1: ctrl.task.title }), 'success'); 4666 } 4667 } 4668 this.close(result); 4669 }; 4670 4671 this.onError = function(error) { 4672 CRM.alert(ts(ctrl.apiBatch.errorMsg || error.error_message || '', {1: ctrl.ids.length, 2: ctrl.entityTitle}), ts('Error'), 'error'); 4673 this.cancel(); 4674 }; 4675 4676 }); 4677 4678 4679 angular.module('crmSearchTasks').controller('crmSearchTaskDownload', function($scope, $http, searchTaskBaseTrait, $timeout, $interval) { 4680 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 4681 ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait); 4682 4683 this.entityTitle = this.getEntityTitle(); 4684 this.format = 'csv'; 4685 this.progress = null; 4686 4687 this.download = function() { 4688 ctrl.progress = 0; 4689 $('div.ui-dialog').last().find('.ui-dialog-titlebar .ui-button').hide(); 4690 let incrementer = $interval(function() { 4691 if (ctrl.progress < 90) { 4692 ctrl.progress += 10; 4693 } 4694 }, 1000); 4695 const apiParams = ctrl.taskManager.getApiParams(); 4696 delete apiParams.return; 4697 delete apiParams.limit; 4698 apiParams.filters.id = ctrl.ids || null; 4699 apiParams.format = ctrl.format; 4700 const httpConfig = { 4701 responseType: 'arraybuffer', 4702 headers: {'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded'} 4703 }; 4704 $http.post(CRM.url('civicrm/ajax/api4/SearchDisplay/download'), $.param({ 4705 params: JSON.stringify(apiParams) 4706 }), httpConfig) 4707 .then(function(response) { 4708 $interval.cancel(incrementer); 4709 ctrl.progress = 100; 4710 const blob = new Blob([response.data], { 4711 type: response.headers('Content-Type') 4712 }), 4713 a = document.createElement("a"), 4714 url = a.href = window.URL.createObjectURL(blob), 4715 fileName = getFileNameFromHeader(response.headers('Content-Disposition')); 4716 a.download = fileName; 4717 a.click(); 4718 window.URL.revokeObjectURL(url); 4719 $timeout(function() { 4720 CRM.alert(ts('%1 has been downloaded to your computer.', {1: fileName}), ts('Download Complete'), 'success'); 4721 ctrl.cancel(); 4722 }, 1000); 4723 }); 4724 }; 4725 function getFileNameFromHeader(contentDisposition) { 4726 const utf8FilenameRegex = /filename\*=utf-8''([\w%\-\.]+)(?:; ?|$)/i, 4727 asciiFilenameRegex = /filename=(["']?)(.*?[^\\])\1(?:; ?|$)/; 4728 4729 if (contentDisposition && contentDisposition.length) { 4730 if (utf8FilenameRegex.test(contentDisposition)) { 4731 return decodeURIComponent(utf8FilenameRegex.exec(contentDisposition)[1]); 4732 } else { 4733 const matches = asciiFilenameRegex.exec(contentDisposition); 4734 if (matches != null && matches[2]) { 4735 return matches[2]; 4736 } 4737 } 4738 } 4739 return ctrl.entityTitle + '.' + ctrl.format; 4740 } 4741 4742 }); 4743 4744 4745 angular.module('crmSearchTasks').controller('crmSearchTaskMailing', function($scope, crmApi4, searchTaskBaseTrait, formatForSelect2) { 4746 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 4747 ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait); 4748 let mailingId; 4749 4750 this.mailing = { 4751 name: '', 4752 template_type: null, 4753 }; 4754 this.entityTitle = this.getEntityTitle(); 4755 crmApi4({
4756 templateTypes: ['Mailing', 'getFields', { 4757 loadOptions: ['id', 'label', 'description'], 4758 where: [['name', '=', 'template_type']] 4759 }, ['options']], 4760 recipientCount: ['Contact', 'get', { 4761 select: ['row_count'], 4762 join: [['Email AS email', 'INNER', ['id', '=', 'email.contact_id']]], 4763 where: [['id', 'IN', ctrl.ids], ['do_not_email', '!=', true], ['is_opt_out', '!=', true], ['email.on_hold', '=', 0]], 4764 groupBy: ['id'] 4765 }] 4766 }).then(function(results) { 4767 ctrl.templateTypes = formatForSelect2(results.templateTypes[0], 'id', 'label', ['description']); 4768 ctrl.mailing.template_type = ctrl.templateTypes[0].id; 4769 ctrl.recipientCount = results.recipientCount.count; 4770 }); 4771 4772 this.submit = function() { 4773 ctrl.start({ 4774 values: { 4775 title: 'Hidden Group ' + Date.now(), 4776 is_hidden: true, 4777 'group_type:name': ['Mailing List'], 4778 }, 4779 chain: { 4780 mailing: ['Mailing', 'create', { 4781 values: ctrl.mailing, 4782 }, 0], 4783 mailingGroup: ['MailingGroup', 'create', { 4784 values: { 4785 group_type: 'Include', 4786 'entity_table:name': 'Group', 4787 entity_id: '$id', 4788 mailing_id: '$mailing.id' 4789 }, 4790 }, 0] 4791 } 4792 }); 4793 }; 4794 this.afterGroupCreate = function(result) { 4795 mailingId = result[0].mailing.id; 4796 ctrl.addContacts = { 4797 defaults: {group_id: result[0].id} 4798 }; 4799 }; 4800 4801 this.onSuccess = function(result) { 4802 window.location = CRM.url('civicrm/a#/mailing/' + mailingId); 4803 }; 4804 4805 this.onError = function(error) { 4806 CRM.alert(ts('An error occurred while attempting to create mailing.'), ts('Error'), 'error'); 4807 this.cancel(); 4808 }; 4809 4810 }); 4811 4812 4813 angular.module('crmSearchTasks').controller('crmSearchTaskRelationship', function($scope, crmApi4, searchTaskBaseTrait) { 4814 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'); 4815 const ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait); 4816 const values = this.task.values && !Array.isArray(this.task.values) ? this.task.values : {}; 4817 $scope.values = values; 4818 4819 this.autocompleteParams = {}; 4820 this.relationshipInfo = this.task.relationshipTypes.reduce((acc, item) => { 4821 acc[item.id] = item; 4822 return acc; 4823 }, {}); 4824 if (values.relationship_type) { 4825 this.onChangeRelationship(); 4826 } 4827 4828 this.onChangeRelationship = function() { 4829 ctrl.relationshipFields = null; 4830 ctrl.autocompleteParams = { 4831 filters: {contact_sub_type: ctrl.relationshipInfo[values.relationship_type].contact_sub_type} 4832 }; 4833 crmApi4('Relationship', 'getFields', { 4834 action: 'create', 4835 values: {relationship_type_id: values.relationship_type.split('_')[0]}, 4836 loadOptions: ['id', 'name', 'label', 'description', 'color', 'icon'], 4837 select: ['name', 'label', 'description', 'input_type', 'data_type', 'serialize', 'options', 'fk_entity', 'nullable', 'required'], 4838 where: [ 4839 ['readonly', '=', false], 4840 ['OR', [ 4841 ['type', '=', 'Custom'], 4842 ['name', 'IN', ['description', 'start_date', 'end_date']]] 4843 ] 4844 ] 4845 }).then(function(result) { 4846 ctrl.relationshipFields = result; 4847 }); 4848 }; 4849 4850 this.submit = function() { 4851 const apiValues = _.cloneDeep(values); 4852 const relationshipType = values.relationship_type.split('_')[0]; 4853 const a = values.relationship_type.split('_')[1]; 4854 const b = values.relationship_type.split('_')[2]; 4855 ctrl.idField = 'contact_id_' + a; 4856 delete apiValues.relationship_type; 4857 delete apiValues.contact_id; 4858 delete apiValues.disableRelationshipSelect; 4859 apiValues.relationship_type_id = relationshipType; 4860 apiValues['contact_id_' + b] = values.contact_id; 4861 ctrl.start({defaults: apiValues}); 4862 }; 4863 4864 this.onSuccess = function(result) { 4865 let added = 0; 4866 let duplicate = 0; 4867 result.forEach(function(relationship) { 4868 if (relationship.duplicate_id) { 4869 duplicate++; 4870 } else { 4871 added++; 4872 } 4873 }); 4874 let msg = _.escape(ts('1 relationship added.', {plural: '%count relationships added.', count: added})); 4875 if (duplicate) { 4876 msg += '<br>' + _.escape(ts('1 relationship already exists.', {plural: '%count relationships already exist.', count: duplicate})); 4877 } 4878 CRM.alert(msg, ts('Saved'), 'success'); 4879 this.close(result); 4880 }; 4881 4882 }); 4883 4884
4885 angular.module('crmSearchTasks').controller('crmSearchTaskTag', function($scope, crmApi4, searchTaskBaseTrait) { 4886 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 4887 ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait); 4888 4889 this.entityTitle = this.getEntityTitle(); 4890 this.action = 'save'; 4891 this.selection = []; 4892 this.selectedTags = []; 4893 this.selectedTagsetTags = {}; 4894 4895 crmApi4({ 4896 tags: ['Tag', 'get', { 4897 select: ['id', 'label', 'color', 'description', 'is_selectable', 'parent_id'], 4898 where: [ 4899 ['is_tagset', '=', false], 4900 ['used_for:name', 'CONTAINS', this.entity], 4901 ['OR', [['parent_id', 'IS NULL'], ['parent_id.is_tagset', '=', false]]] 4902 ], 4903 orderBy: {label: 'ASC'} 4904 }], 4905 tagsets: ['Tag', 'get', { 4906 select: ['id', 'name', 'label'], 4907 where: [['is_tagset', '=', true], ['used_for:name', 'CONTAINS', this.entity]] 4908 }], 4909 }).then(function(result) { 4910 ctrl.tagsets = result.tagsets; 4911 ctrl.tags = sortTagsForSelect2(result.tags); 4912 }); 4913 function sortTagsForSelect2(rawTags) { 4914 const sorted = rawTags.reduce((accum, tag) => { 4915 accum[tag.id] = { 4916 id: tag.id, 4917 text: tag.label, 4918 description: tag.description, 4919 color: tag.color, 4920 disabled: !tag.is_selectable, 4921 parent_id: tag.parent_id 4922 }; 4923 return accum; 4924 }, {}); 4925 Object.values(sorted).forEach(tag => { 4926 if (tag.parent_id && sorted[tag.parent_id]) { 4927 sorted[tag.parent_id].children = sorted[tag.parent_id].children || []; 4928 sorted[tag.parent_id].children.push(tag); 4929 } 4930 }); 4931 return Object.values(sorted).filter(tag => tag.parent_id === null); 4932 } 4933 4934 this.saveTags = function() { 4935 const params = {}; 4936 if (ctrl.action === 'save') { 4937 params.defaults = { 4938 'entity_table:name': ctrl.entity 4939 }; 4940 params.records = ctrl.selection.map(tagId => { 4941 return {tag_id: tagId}; 4942 }); 4943 params.match = ['entity_id', 'tag_id', 'entity_table']; 4944 } 4945 else { 4946 params.where = [ 4947 ['entity_table:name', '=', ctrl.entity], 4948 ['tag_id', 'IN', ctrl.selection] 4949 ]; 4950 } 4951 ctrl.start(params); 4952 }; 4953 4954 this.onSelectTags = function() { 4955 ctrl.selection = [...ctrl.selectedTags]; 4956 Object.values(ctrl.selectedTagsetTags).forEach(set => { 4957 ctrl.selection = [...ctrl.selection, ...set]; 4958 }); 4959 }; 4960 4961 this.onSuccess = function(result) { 4962 let msg; 4963 if (ctrl.action === 'delete') { 4964 msg = ts('1 tag removed.', {plural: '%count tags removed.', count: result.batchCount}); 4965 CRM.alert(msg, ts('Saved'), 'success'); 4966 } else { 4967 const added = result.batchCount - result.countMatched; 4968 msg = ts('1 tag added', {plural: '%count tags added.', count: added}); 4969 msg += '<br/>'; 4970 if (result.countMatched > 0) { 4971 msg += ts('1 tag already exists and was not added.', {plural: '%count tags already exist and were not added.', count: result.countMatched}); 4972 } 4973 CRM.alert(msg, ts('Saved'), 'success'); 4974 } 4975 this.close(); 4976 }; 4977 4978 this.onError = function(error) { 4979 CRM.alert(ts('An error occurred while updating tags.'), ts('Error'), 'error'); 4980 this.cancel(); 4981 }; 4982 4983 }); 4984 4985 4986 angular.module('crmSearchTasks').controller('crmSearchTaskUpdate', function ($scope, $timeout, crmApi4, searchTaskBaseTrait, searchTaskFieldsTrait) { 4987 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'); 4988 const ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait, searchTaskFieldsTrait); 4989 4990 this.entityTitle = this.getEntityTitle(); 4991 4992 this.loadFieldsAndValues(this.task, this.entity); 4993 4994 this.save = function() { 4995 ctrl.start({ 4996 values: _.zipObject(ctrl.values) 4997 }); 4998 }; 4999 5000 this.onSuccess = function() { 5001 CRM.alert(ts('Successfully updated %1 %2.', {1: ctrl.ids.length, 2: ctrl.entityTitle}), ts('Saved'), 'success'); 5002 this.close(); 5003 }; 5004 5005 this.onError = function(error) {
5006 CRM.alert(ts('An error occurred while attempting to update %1 %2.', {1: ctrl.ids.length, 2: ctrl.entityTitle}), ts('Error'), 'error'); 5007 this.cancel(); 5008 }; 5009 5010 }); 5011 5012 5013 angular.module('crmSearchTasks').component('crmSearchTasksMenu', { 5014 bindings: { 5015 taskManager: '<', 5016 displayMode: '<', 5017 ids: '<' 5018 }, 5019 template: '<div class="btn-group" ng-include="\'~/crmSearchTasks/crmSearchTasks-\'+$ctrl.displayMode+\'.html\'"></div>', 5020 controller: function($scope) { 5021 const ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), 5022 ctrl = this; 5023 5024 this.$onInit = function() { 5025 if (ctrl.displayMode === 'buttons') { 5026 ctrl.taskManager.getMetadata(); 5027 } 5028 let unwatchIDs = $scope.$watch('$ctrl.ids.length', function (idsLength) { 5029 if (idsLength) { 5030 unwatchIDs(); 5031 ctrl.taskManager.getMetadata(); 5032 } 5033 }); 5034 }; 5035 5036 this.isActionAllowed = function(action) { 5037 return $scope.$eval('' + ctrl.ids.length + action.number); 5038 }; 5039 5040 this.getActionTitle = function(action) { 5041 if (ctrl.isActionAllowed(action)) { 5042 return ctrl.ids.length ? 5043 ts('Perform action on %1 %2', {1: ctrl.ids.length, 2: ctrl.taskManager.entityInfo[ctrl.ids.length === 1 ? 'title' : 'title_plural']}) : 5044 ts('Perform action on all %1', {1: ctrl.taskManager.entityInfo.title_plural}); 5045 } 5046 return ts('Selected number must be %1', {1: action.number.replace('===', '')}); 5047 }; 5048 5049 this.doAction = function(action) { 5050 if (!ctrl.isActionAllowed(action)) { 5051 return; 5052 } 5053 ctrl.taskManager.doTask(action, ctrl.ids); 5054 }; 5055 } 5056 }); 5057 5058 5059 angular.module('crmSearchTasks').factory('searchDisplayTasksTrait', function($rootScope, $window, crmApi4, dialogService) { 5060 const ts = CRM.ts('org.civicrm.search_kit'); 5061 function TaskManager(displayCtrl, $element) { 5062 const mngr = this; 5063 let fetchedMetadata; 5064 this.tasks = null; 5065 this.entityInfo = null; 5066 5067 this.getMetadata = function() { 5068 if (!fetchedMetadata) { 5069 fetchedMetadata = crmApi4('SearchDisplay', 'getSearchTasks', { 5070 savedSearch: displayCtrl.search, 5071 display: displayCtrl.display, 5072 }).then(function(result) { 5073 mngr.entityInfo = result.editable.entityInfo; 5074 mngr.tasks = result; 5075 }, function(failure) { 5076 mngr.tasks = []; 5077 mngr.entityInfo = []; 5078 }); 5079 } 5080 return fetchedMetadata; 5081 }; 5082 5083 this.getApiParams = function() { 5084 return displayCtrl.getApiParams(); 5085 }; 5086 this.getRowCount = function() { 5087 return displayCtrl.rowCount; 5088 }; 5089 this.isDisplayReady = function() { 5090 return !displayCtrl.loading && displayCtrl.results && displayCtrl.results.length; 5091 }; 5092 this.getTaskInfo = function(taskName) { 5093 return _.findWhere(mngr.tasks, {name: taskName}); 5094 }; 5095 5096 this.doTask = function(task, ids, isLink) { 5097 const data = { 5098 ids: ids, 5099 entity: mngr.entityInfo.name, 5100 search: displayCtrl.search, 5101 display: displayCtrl.display, 5102 displayCtrl: displayCtrl, 5103 taskManager: mngr, 5104 entityInfo: mngr.entityInfo, 5105 isLink: isLink, 5106 task: _.cloneDeep(task), 5107 }; 5108 if (task.crmPopup) { 5109 const mode = ('mode' in task.crmPopup) ? task.crmPopup.mode : 'back'; 5110 const path = $rootScope.$eval(task.crmPopup.path, data), 5111 query = task.crmPopup.query && $rootScope.$eval(task.crmPopup.query, data); 5112 CRM.loadForm(CRM.url(path, query, mode), {post: task.crmPopup.data && $rootScope.$eval(task.crmPopup.data, data)}) 5113 .on('crmFormSuccess', (e) => { 5114 e.stopPropagation(); 5115 mngr.refreshAfterTask(); 5116 }); 5117 } 5118 else if (task.redirect) { 5119 const mode = ('mode' in task.redirect) ? task.redirect.mode : 'back'; 5120 const redirectPath = $rootScope.$eval(task.redirect.path, data), 5121 redirectQuery = task.redirect.query && $rootScope.$eval(task.redirect.query, data) && $rootScope.$eval(task.redirect.data, data); 5122 $window.open(CRM.url(redirectPath, redirectQuery, mode), '_blank'); 5123 } 5124 else { 5125 const options = CRM.utils.adjustDialogDefaults({ 5126 autoOpen: false, 5127 dialogClass: 'crm-search-task-dialog', 5128 title: task.title 5129 }); 5130 dialogService.open('crmSearchTask', (task.uiDialog && task.uiDialog.templateUrl) || '~/crmSearchTasks/crmSearchTaskApiBatch.html', data, options) 5131 .then((result) => mngr.refreshAfterTask(result, ids), _.noop); 5132 } 5133 }; 5134 5135 this.refreshAfterTask = function(result, ids) { 5136 displayCtrl.selectedRows = []; 5137 displayCtrl.allRowsSelected = false;
5138 if (result && ids && result.action === 'inlineEdit' && ids.length === 1) { 5139 displayCtrl.refreshAfterEditing(result, ids[0]); 5140 } 5141 else { 5142 displayCtrl.rowCount = null; 5143 displayCtrl.getResultsPronto(); 5144 $element.trigger('crmPopupFormSuccess'); 5145 } 5146 }; 5147 } 5148 return { 5149 selectAllPages: function() { 5150 const ctrl = this; 5151 ctrl.loadingAllRows = ctrl.allRowsSelected = true; 5152 const params = ctrl.getApiParams('id'); 5153 crmApi4('SearchDisplay', 'run', params).then(function(ids) { 5154 ctrl.loadingAllRows = false; 5155 ctrl.selectedRows = _.uniq(_.toArray(ids)); 5156 }); 5157 }, 5158 selectPage: function() { 5159 this.allRowsSelected = (this.rowCount <= this.results.length); 5160 this.selectedRows = _.uniq(_.pluck(this.results, 'key')); 5161 }, 5162 selectNone: function() { 5163 this.allRowsSelected = false; 5164 this.selectedRows = []; 5165 }, 5166 toggleAllRows: function() { 5167 if (this.selectedRows && this.selectedRows.length) { 5168 this.selectNone(); 5169 } 5170 else if (this.page === 1 && this.rowCount === this.results.length) { 5171 this.selectPage(); 5172 } 5173 else { 5174 this.selectAllPages(); 5175 } 5176 }, 5177 toggleRow: function(row, event) { 5178 this.selectedRows = this.selectedRows || []; 5179 const ctrl = this, 5180 index = ctrl.selectedRows.indexOf(row.key); 5181 function checkRange(allRows, checkboxPosition, dir) { 5182 for (let row = checkboxPosition; row >= 0 && row <= allRows.length; row += dir) { 5183 if (ctrl.selectedRows.indexOf(allRows[row]) > -1) { 5184 return row; 5185 } 5186 } 5187 } 5188 function selectRange(allRows, start, end) { 5189 for (let row = start; row <= end; ++row) { 5190 ctrl.selectedRows.push(allRows[row]); 5191 } 5192 } 5193 5194 if (index < 0) { 5195 if (event.shiftKey && ctrl.selectedRows.length) { 5196 const allRows = _.pluck(ctrl.results, 'key'), 5197 checkboxPosition = allRows.indexOf(row.key); 5198 5199 const nearestBefore = checkRange(allRows, checkboxPosition, -1), 5200 nearestAfter = checkRange(allRows, checkboxPosition, 1); 5201 if (nearestAfter !== undefined && (nearestBefore === undefined || nearestAfter === allRows.indexOf(_.last(ctrl.selectedRows)))) { 5202 selectRange(allRows, checkboxPosition + 1, nearestAfter - 1); 5203 } else if (nearestBefore !== undefined && (nearestAfter === undefined || nearestBefore === allRows.indexOf(_.last(ctrl.selectedRows)))) { 5204 selectRange(allRows, nearestBefore + 1, checkboxPosition -1); 5205 } 5206 } 5207 ctrl.selectedRows = _.uniq(ctrl.selectedRows.concat([row.key])); 5208 ctrl.allRowsSelected = (ctrl.rowCount === ctrl.selectedRows.length); 5209 } else { 5210 ctrl.allRowsSelected = false; 5211 ctrl.selectedRows.splice(index, 1); 5212 } 5213 }, 5214 isRowSelected: function(row) { 5215 return this.allRowsSelected || _.includes(this.selectedRows, row.key); 5216 }, 5217 5218 isPageSelected: function() { 5219 return (this.allRowsSelected && this.rowCount === this.results.length) || 5220 (!this.allRowsSelected && this.selectedRows && this.selectedRows.length === this.results.length); 5221 }, 5222 onClickLink: function(link, id, event) { 5223 if (link.task) { 5224 const mngr = this.taskManager; 5225 event.preventDefault(); 5226 mngr.getMetadata().then(function() { 5227 mngr.doTask(_.extend({title: link.title}, mngr.getTaskInfo(link.task)), [id], true); 5228 }); 5229 } 5230 }, 5231 onInitialize: [function($scope, $element) { 5232 if (!this.taskManager) { 5233 this.taskManager = new TaskManager(this, $element); 5234 } 5235 }], 5236 onChangeFilters: [function() { 5237 this.selectedRows = []; 5238 this.allRowsSelected = false; 5239 }], 5240 onPostRun: [function(apiResults, status, editedRow) { 5241 if (editedRow && status === 'success' && this.selectedRows) { 5242 const index = this.selectedRows.indexOf(editedRow.key); 5243 if (index > -1 && !_.findWhere(apiResults.run, {key: editedRow.key})) { 5244 this.selectedRows.splice(index, 1); 5245 } 5246 } 5247 else if (status === 'success' && !editedRow && apiResults.run && apiResults.run[0]) { 5248 const mngr = this.taskManager; 5249 if (this.settings.actions_display_mode === 'buttons') { 5250 mngr.getMetadata(); 5251 return; 5252 } 5253 _.each(apiResults.run[0].columns, function(column) { 5254 if ((column.link && column.link.task) || _.find(column.links || [], 'task')) { 5255 mngr.getMetadata(); 5256 } 5257 }); 5258 } 5259 }] 5260 5261 }; 5262 }); 5263 5264
5265 angular.module('crmSearchTasks').factory('searchTaskBaseTrait', function(dialogService) { 5266 const ts = CRM.ts('org.civicrm.search_kit'); 5267 return { 5268 5269 getEntityTitle: function(count) { 5270 if (typeof count !== 'number') { 5271 count = this.ids.length; 5272 } 5273 return count === 1 ? this.entityInfo.title : this.entityInfo.title_plural; 5274 }, 5275 5276 start: function(runParams) { 5277 $('.ui-dialog-titlebar button').hide(); 5278 this.run = runParams || {}; 5279 }, 5280 5281 cancel: function() { 5282 dialogService.cancel('crmSearchTask'); 5283 }, 5284 5285 close: function(result) { 5286 dialogService.close('crmSearchTask', result); 5287 } 5288 5289 }; 5290 }); 5291 5292 5293 5294 let paths = {}; 5295 angular.module('crmSearchTasks').factory('searchTaskFieldsTrait', function(crmApi4, $timeout) { 5296 const ctrl = { 5297 5298 values: [], 5299 fields: [], 5300 5301 loadFieldsAndValues: function(task, entityName, getFieldsParams, apiCalls) { 5302 this.fields.length = 0; 5303 this.values.length = 0; 5304 if (task.values && !Array.isArray(task.values)) { 5305 Object.keys(task.values).forEach(key => this.values.push([key, task.values[key]])); 5306 } 5307 5308 apiCalls = apiCalls || {}; 5309 getFieldsParams = _.merge({ 5310 action: 'update', 5311 select: ['name', 'label', 'description', 'input_type', 'data_type', 'serialize', 'options', 'fk_entity', 'nullable', 'required', 'default_value'], 5312 loadOptions: ['id', 'name', 'label', 'description', 'color', 'icon'], 5313 where: [], 5314 }, 5315 getFieldsParams || {} 5316 ); 5317 getFieldsParams.where.push(['deprecated', '=', false], ['readonly', '=', false]); 5318 apiCalls.getFields = [entityName, 'getFields', getFieldsParams]; 5319 apiCalls.entityInfo = ['Entity', 'get', { 5320 where: [['name', '=', entityName]], 5321 select: ['paths'] 5322 }, 0]; 5323 5324 const apiResult = crmApi4(apiCalls); 5325 apiResult.then((results) => { 5326 ctrl.fields.push(... results.getFields); 5327 paths = results.entityInfo.paths; 5328 if (getFieldsParams.action === 'create') { 5329 results.getFields.forEach(field => { 5330 if (field.required && !field.default_value) { 5331 this.addField(field.name); 5332 } 5333 }); 5334 } 5335 }); 5336 return apiResult; 5337 }, 5338 5339 getField: function(fieldName) { 5340 return ctrl.fields.find(field => field.name === fieldName); 5341 }, 5342 5343 addField: function(selection) { 5344 if (selection && !fieldInUse(selection)) { 5345 const field = this.getField(selection); 5346 let value = ''; 5347 if (field.serialize || field.data_type === 'Array') { 5348 value = []; 5349 } else if (field.data_type === 'Boolean') { 5350 value = true; 5351 } else if (field.default_value) { 5352 value = field.default_value; 5353 } else if (field.options && field.options.length) { 5354 value = field.options[0].id; 5355 } 5356 this.values.push([selection, value]); 5357 } 5358 }, 5359 5360 updateField: function(index) { 5361 $timeout(() => { 5362 if (!this.values[index][0]) { 5363 this.values.splice(index, 1); 5364 } 5365 }); 5366 }, 5367 5368 availableFields: function() { 5369 const results = ctrl.fields.map(item => { 5370 const formatted = { 5371 id: item.name, 5372 text: item.label, 5373 description: item.description 5374 }; 5375 if (fieldInUse(item.name)) { 5376 formatted.disabled = true; 5377 } 5378 return formatted; 5379 }); 5380 return {results: results}; 5381 }, 5382 5383 getUrl(action, values) { 5384 const path = paths[action]; 5385 if (!path) { 5386 return null; 5387 } 5388 return CRM.url(path.replace(/\[(.*?)]/g, (match, key) => values[key])); 5389 } 5390 5391 }; 5392 5393 function fieldInUse(fieldName) { 5394 return ctrl.values.map(value => value[0]).includes(fieldName); 5395 } 5396 5397 return ctrl; 5398 }); 5399 5400 5401let uidCount = 0, 5402 pageTitleHTML = 'CiviCRM', 5403 documentTitle = 'CiviCRM'; 5404
5405 angular.module('crmUi', CRM.angRequires('crmUi')) 5406 .directive('crmUiAccordion', function() { 5407 return { 5408 scope: { 5409 crmUiAccordion: '=' 5410 }, 5411 template: '<details class="crm-accordion-bold"><summary>{{crmUiAccordion.title}} <a crm-ui-help="help" ng-if="help"></a></summary><div class="crm-accordion-body" ng-transclude></div></details>', 5412 transclude: true, 5413 link: function (scope, element, attrs) { 5414 scope.help = null; 5415 let openSet = false; 5416 scope.$watch('crmUiAccordion', function(crmUiAccordion, oldVal) { 5417 if (crmUiAccordion) { 5418 if (!openSet) { 5419 $(element).children('details').prop('open', !crmUiAccordion.collapsed); 5420 openSet = true; 5421 } 5422 if (crmUiAccordion.help) { 5423 scope.help = crmUiAccordion.help.clone({}, { 5424 title: crmUiAccordion.title 5425 }); 5426 } 5427 } 5428 }); 5429 } 5430 }; 5431 }) 5432 .service('crmUiAlert', function($compile, $rootScope, $templateRequest, $q) { 5433 let count = 0; 5434 return function crmUiAlert(params) { 5435 const id = 'crmUiAlert_' + (++count); 5436 let tpl = null; 5437 if (params.templateUrl) { 5438 tpl = $templateRequest(params.templateUrl); 5439 } 5440 else if (params.template) { 5441 tpl = params.template; 5442 } 5443 if (tpl) { 5444 params.text = '<div id="' + id + '"></div>'; // temporary stub 5445 } 5446 const result = CRM.alert(params.text, params.title, params.type, params.options); 5447 if (tpl) { 5448 $q.when(tpl, function(html) { 5449 const scope = params.scope || $rootScope.$new(); 5450 const linker = $compile(html); 5451 $('#' + id).append($(linker(scope))); 5452 }); 5453 } 5454 return result; 5455 }; 5456 }) 5457 .directive('crmUiDatepicker', function ($timeout) { 5458 return { 5459 restrict: 'AE', 5460 require: 'ngModel', 5461 scope: { 5462 crmUiDatepicker: '=' 5463 }, 5464 link: function (scope, element, attrs, ngModel) { 5465 ngModel.$render = function () { 5466 const viewVal = ngModel.$viewValue || ''; 5467 if (element.val() != viewVal) { 5468 element.val(viewVal).change(); 5469 } 5470 }; 5471 let settings = angular.copy(scope.crmUiDatepicker || {}); 5472 settings.start_date_years = settings.start_date_years || 100; 5473 settings.end_date_years = settings.end_date_years || 100; 5474 $timeout(function() { 5475 element 5476 .crmDatepicker(settings) 5477 .on('change', function () { 5478 $timeout(function() { 5479 let requiredLength = 19; 5480 if (settings.time === false) { 5481 requiredLength = 10; 5482 } 5483 if (settings.date === false) { 5484 requiredLength = 8; 5485 } 5486 else if (typeof settings.date === 'string') { 5487 const lowerFormat = settings.date.toLowerCase(); 5488 if (lowerFormat.indexOf('y') < 0 || lowerFormat.indexOf('m') < 0 || lowerFormat.indexOf('d') < 0) { 5489 requiredLength = element.val().length; 5490 } 5491 } 5492 ngModel.$setValidity('incompleteDateTime', !(element.val().length && element.val().length !== requiredLength)); 5493 }); 5494 }); 5495 }); 5496 } 5497 }; 5498 }) 5499 .directive('crmUiDebug', function ($location) { 5500 return { 5501 restrict: 'AE', 5502 scope: { 5503 crmUiDebug: '@' 5504 }, 5505 template: function() { 5506 const args = $location.search(); 5507 if (args && args.angularDebug) { 5508 const jsonTpl = (CRM.angular.modules.indexOf('jsonFormatter') < 0) ? '<pre>{{data|json}}</pre>' : '<json-formatter json="data" open="1"></json-formatter>'; 5509 return '<div crm-ui-accordion=\'{title: ts("Debug (%1)", {1: crmUiDebug}), collapsed: true}\'>' + jsonTpl + '</div>'; 5510 } 5511 return ''; 5512 }, 5513 link: function(scope, element, attrs) { 5514 const args = $location.search(); 5515 if (args && args.angularDebug) { 5516 scope.ts = CRM.ts(null); 5517 scope.$parent.$watch(attrs.crmUiDebug, function(data) { 5518 scope.data = data; 5519 }); 5520 } 5521 } 5522 }; 5523 }) 5524 .directive('crmUiField', function() { 5525 const templateUrls = { 5526 default: '~/crmUi/field.html', 5527 checkbox: '~/crmUi/field-cb.html' 5528 }; 5529 5530 return { 5531 require: '^crmUiIdScope', 5532 restrict: 'EA', 5533 scope: { 5534 crmUiField: '=' 5535 },
5536 templateUrl: function(tElement, tAttrs){ 5537 const layout = tAttrs.crmLayout ? tAttrs.crmLayout : 'default'; 5538 return templateUrls[layout]; 5539 }, 5540 transclude: true, 5541 link: function (scope, element, attrs, crmUiIdCtrl) { 5542 $(element).addClass('crm-section'); 5543 scope.help = null; 5544 scope.$watch('crmUiField', function(crmUiField) { 5545 if (crmUiField && crmUiField.help) { 5546 scope.help = crmUiField.help.clone({}, { 5547 title: crmUiField.title 5548 }); 5549 } 5550 }); 5551 } 5552 }; 5553 }) 5554 .directive('crmUiId', function () { 5555 return { 5556 require: '^crmUiIdScope', 5557 restrict: 'EA', 5558 link: { 5559 pre: function (scope, element, attrs, crmUiIdCtrl) { 5560 const id = crmUiIdCtrl.get(attrs.crmUiId); 5561 element.attr('id', id); 5562 } 5563 } 5564 }; 5565 }) 5566 .service('crmUiHelp', function(){ 5567 function FieldHelp(options) { 5568 this.options = options; 5569 } 5570 angular.extend(FieldHelp.prototype, { 5571 get: function(n) { 5572 return this.options[n]; 5573 }, 5574 open: function open() { 5575 CRM.help(this.options.title, {id: this.options.id, file: this.options.file}); 5576 }, 5577 clone: function clone(options, defaults) { 5578 return new FieldHelp(angular.extend({}, defaults, this.options, options)); 5579 } 5580 }); 5581 return function(defaults){ 5582 return function(options) { 5583 if (typeof options === 'string') { 5584 options = {id: options}; 5585 } 5586 return new FieldHelp(angular.extend({}, defaults, options)); 5587 }; 5588 }; 5589 }) 5590 .directive('crmUiHelp', function() { 5591 return { 5592 restrict: 'EA', 5593 link: function(scope, element, attrs) { 5594 setTimeout(function() { 5595 const crmUiHelp = scope.$eval(attrs.crmUiHelp); 5596 let title = crmUiHelp && crmUiHelp.get('title') ? ts('%1 Help', {1: crmUiHelp.get('title')}) : ts('Help'); 5597 element.attr('title', title); 5598 }, 50); 5599 5600 element 5601 .addClass('helpicon') 5602 .attr('href', '#') 5603 .on('click', function(e) { 5604 e.preventDefault(); 5605 scope.$eval(attrs.crmUiHelp).open(); 5606 }); 5607 } 5608 }; 5609 }) 5610 .directive('crmUiFor', function ($parse, $timeout) { 5611 return { 5612 require: '^crmUiIdScope', 5613 restrict: 'EA', 5614 template: '<span ng-class="cssClasses"><span ng-transclude/><span crm-ui-visible="crmIsRequired" class="crm-marker" title="This field is required.">*</span></span>', 5615 transclude: true, 5616 link: function (scope, element, attrs, crmUiIdCtrl) { 5617 scope.crmIsRequired = false; 5618 scope.cssClasses = {}; 5619 5620 if (!attrs.crmUiFor) return; 5621 5622 const id = crmUiIdCtrl.get(attrs.crmUiFor); 5623 element.attr('for', id); 5624 let ngModel = null; 5625 5626 const updateCss = function () { 5627 scope.cssClasses['crm-error'] = !ngModel.$valid && !ngModel.$pristine; 5628 }; 5629 const init = function (retries, retryDelay) { 5630 const input = $('#' + id); 5631 if (input.length === 0 && !attrs.crmUiForceRequired) { 5632 if (retries) { 5633 $timeout(function(){ 5634 init(retries-1, retryDelay); 5635 }, retryDelay); 5636 } 5637 return; 5638 } 5639 5640 if (attrs.crmUiForceRequired) { 5641 scope.crmIsRequired = true; 5642 return; 5643 } 5644 5645 let tgtScope = scope;//.$parent; 5646 if (attrs.crmDepth) { 5647 for (let i = attrs.crmDepth; i > 0; i--) { 5648 tgtScope = tgtScope.$parent; 5649 } 5650 } 5651 5652 if (input.attr('ng-required')) { 5653 scope.crmIsRequired = scope.$parent.$eval(input.attr('ng-required')); 5654 scope.$parent.$watch(input.attr('ng-required'), function (isRequired) { 5655 scope.crmIsRequired = isRequired; 5656 }); 5657 } 5658 else { 5659 scope.crmIsRequired = input.prop('required'); 5660 } 5661 5662 ngModel = $parse(attrs.crmUiFor)(tgtScope); 5663 if (ngModel) { 5664 ngModel.$viewChangeListeners.push(updateCss); 5665 } 5666 }; 5667 5668 $timeout(function(){ 5669 init(3, 100); 5670 }); 5671 } 5672 }; 5673 }) 5674 .directive('crmUiIdScope', function () { 5675 return { 5676 restrict: 'EA', 5677 scope: {}, 5678 controllerAs: 'crmUiIdCtrl', 5679 controller: function($scope) { 5680 const ids = {}; 5681 this.get = function(name) { 5682 if (!ids[name]) { 5683 ids[name] = "crmUiId_" + (++uidCount); 5684 } 5685 return ids[name]; 5686 }; 5687 }, 5688 link: function (scope, element, attrs) {} 5689 }; 5690 }) 5691 .directive('crmUiIframe', function ($parse) { 5692 return { 5693 scope: { 5694 crmUiIframeSrc: '@', // expression which evaluates to a URL 5695 crmUiIframe: '@' // expression which evaluates to HTML content 5696 }, 5697 link: function (scope, elm, attrs) { 5698 const iframe = $(elm)[0]; 5699 iframe.setAttribute('width', '100%'); 5700 iframe.setAttribute('height', '250px'); 5701 iframe.setAttribute('frameborder', '0'); 5702 5703 const refresh = function () { 5704 if (attrs.crmUiIframeSrc) { 5705 iframe.setAttribute('src', scope.$parent.$eval(attrs.crmUiIframeSrc)); 5706 } 5707 else { 5708 let iframeHtml = scope.$parent.$eval(attrs.crmUiIframe); 5709 5710 let doc = iframe.document; 5711 if (iframe.contentDocument) { 5712 doc = iframe.contentDocument; 5713 } 5714 else if (iframe.contentWindow) { 5715 doc = iframe.contentWindow.document; 5716 } 5717 5718 doc.open(); 5719 doc.writeln(iframeHtml); 5720 doc.close(); 5721 } 5722 }; 5723 $(elm).parent().on('dialogresize dialogopen', function(e, ui) { 5724 $(this).css({padding: '0', margin: '0', overflow: 'hidden'}); 5725 iframe.setAttribute('height', '' + $(this).innerHeight() + 'px'); 5726 }); 5727 5728 $(elm).parent().on('dialogresize', function(e, ui) { 5729 iframe.setAttribute('class', 'resized'); 5730 }); 5731 5732 scope.$parent.$watch(attrs.crmUiIframe, refresh); 5733 } 5734 }; 5735 }) 5736 .directive('crmUiInsertRx', function() { 5737 return { 5738 link: function(scope, element, attrs) { 5739 scope.$on(attrs.crmUiInsertRx, function(e, tokenName) { 5740 CRM.wysiwyg.insert(element, tokenName); 5741 $(element).select2('close').select2('val', ''); 5742 CRM.wysiwyg.focus(element); 5743 }); 5744 } 5745 }; 5746 }) 5747 .directive('crmUiRichtext', function ($timeout) { 5748 return { 5749 require: '?ngModel', 5750 link: function (scope, elm, attr, ngModel) { 5751 $timeout(function() { 5752 const editor = CRM.wysiwyg.create(elm); 5753 5754 if (!ngModel) { 5755 return; 5756 } 5757 5758 if (attr.ngBlur) { 5759 $(elm).on('blur', function() { 5760 $timeout(function() { 5761 scope.$eval(attr.ngBlur); 5762 }); 5763 }); 5764 } 5765 5766 ngModel.$render = function(value) { 5767 editor.done(function() { 5768 CRM.wysiwyg.setVal(elm, ngModel.$viewValue || ''); 5769 }); 5770 }; 5771 }); 5772 } 5773 }; 5774 }) 5775 .directive('crmUiLock', function ($parse, $rootScope) { 5776 const defaultVal = function (defaultValue) { 5777 const f = function (scope) { 5778 return defaultValue; 5779 }; 5780 f.assign = function (scope, value) { 5781 }; 5782 return f; 5783 }; 5784 const parse = function (expr, defaultValue) { 5785 return expr ? $parse(expr) : defaultVal(defaultValue); 5786 }; 5787 5788 return { 5789 template: '', 5790 link: function (scope, element, attrs) { 5791 const binding = parse(attrs.binding, true); 5792 const titleLocked = parse(attrs.titleLocked, ts('Locked')); 5793 const titleUnlocked = parse(attrs.titleUnlocked, ts('Unlocked')); 5794 5795 $(element).addClass('crm-i lock-button'); 5796 const refresh = function () { 5797 const locked = binding(scope); 5798 if (locked) { 5799 $(element) 5800 .removeClass('fa-unlock') 5801 .addClass('fa-lock') 5802 .prop('title', titleLocked(scope)) 5803 ; 5804 } 5805 else { 5806 $(element) 5807 .removeClass('fa-lock') 5808 .addClass('fa-unlock') 5809 .prop('title', titleUnlocked(scope)) 5810 ; 5811 } 5812 }; 5813 5814 $(element).click(function () { 5815 binding.assign(scope, !binding(scope)); 5816 $rootScope.$digest(); 5817 }); 5818 5819 scope.$watch(attrs.binding, refresh); 5820 scope.$watch(attrs.titleLocked, refresh); 5821 scope.$watch(attrs.titleUnlocked, refresh); 5822 5823 refresh(); 5824 } 5825 }; 5826 }) 5827 .service('CrmUiOrderCtrl', function(){ 5828 function CrmUiOrderCtrl(defaults){ 5829 this.values = defaults; 5830 } 5831 angular.extend(CrmUiOrderCtrl.prototype, { 5832 get: function get() { 5833 return this.values; 5834 }, 5835 getDir: function getDir(name) { 5836 if (this.values.indexOf(name) >= 0 || this.values.indexOf('+' + name) >= 0) { 5837 return '+'; 5838 } 5839 if (this.values.indexOf('-' + name) >= 0) { 5840 return '-'; 5841 } 5842 return ''; 5843 }, 5844 remove: function remove(name) { 5845 const idx = this.values.indexOf(name); 5846 if (idx >= 0) { 5847 this.values.splice(idx, 1); 5848 return true; 5849 } 5850 else { 5851 return false;
5852 } 5853 }, 5854 setDir: function setDir(name, dir) { 5855 return this.toggle(name, dir); 5856 }, 5857 toggle: function toggle(name, next) { 5858 if (!next && next !== '') { 5859 next = '+'; 5860 if (this.remove(name) || this.remove('+' + name)) { 5861 next = '-'; 5862 } 5863 if (this.remove('-' + name)) { 5864 next = ''; 5865 } 5866 } 5867 5868 if (next == '+') { 5869 this.values.unshift('+' + name); 5870 } 5871 else if (next == '-') { 5872 this.values.unshift('-' + name); 5873 } 5874 } 5875 }); 5876 return CrmUiOrderCtrl; 5877 }) 5878 .directive('crmUiOrder', function(CrmUiOrderCtrl) { 5879 return { 5880 link: function(scope, element, attrs){ 5881 const options = angular.extend({var: 'crmUiOrderBy'}, scope.$eval(attrs.crmUiOrder)); 5882 scope[options.var] = new CrmUiOrderCtrl(options.defaults); 5883 } 5884 }; 5885 }) 5886 .directive('crmUiOrderBy', function() { 5887 return { 5888 link: function(scope, element, attrs) { 5889 function updateClass(crmUiOrderCtrl, name) { 5890 const dir = crmUiOrderCtrl.getDir(name); 5891 element 5892 .toggleClass('sorting_asc', dir === '+') 5893 .toggleClass('sorting_desc', dir === '-') 5894 .toggleClass('sorting', dir === ''); 5895 } 5896 5897 element.on('click', function(e){ 5898 const tgt = scope.$eval(attrs.crmUiOrderBy); 5899 tgt[0].toggle(tgt[1]); 5900 updateClass(tgt[0], tgt[1]); 5901 e.preventDefault(); 5902 scope.$digest(); 5903 }); 5904 5905 const tgt = scope.$eval(attrs.crmUiOrderBy); 5906 updateClass(tgt[0], tgt[1]); 5907 } 5908 }; 5909 }) 5910 .directive('crmUiSelect', function ($parse, $timeout) { 5911 return { 5912 require: '?ngModel', 5913 priority: 1, 5914 scope: { 5915 crmUiSelect: '=' 5916 }, 5917 link: function (scope, element, attrs, ngModel) { 5918 5919 if (ngModel && !attrs.ngOptions) { 5920 ngModel.$render = function () { 5921 $timeout(function () { 5922 let newVal = _.cloneDeep(ngModel.$modelValue); 5923 if (typeof newVal === 'string' && element.select2('container').hasClass('select2-container-multi')) { 5924 newVal = newVal.length ? newVal.split(scope.crmUiSelect.separator || ',') : []; 5925 } 5926 element.select2('val', newVal); 5927 }); 5928 }; 5929 } 5930 function refreshModel() { 5931 const oldValue = ngModel.$viewValue; 5932 let newValue = element.select2('val'); 5933 if (Array.isArray(newValue) && attrs.ngList) { 5934 newValue = newValue.join(attrs.ngList); 5935 } 5936 if (oldValue != newValue) { 5937 scope.$parent.$apply(function () { 5938 ngModel.$setViewValue(newValue); 5939 }); 5940 } 5941 } 5942 5943 function init() { 5944 element.crmSelect2(scope.crmUiSelect || {}); 5945 if (ngModel) { 5946 element.on('change', refreshModel); 5947 } 5948 } 5949 if (attrs.ngOptions) { 5950 $timeout(function() { 5951 element.crmSelect2(scope.crmUiSelect || {}); 5952 ngModel.$render = function () { 5953 element.val(ngModel.$viewValue || '').change(); 5954 }; 5955 }); 5956 } else { 5957 $timeout(init); 5958 } 5959 } 5960 }; 5961 }) 5962 .directive('onCrmUiSelect', function () { 5963 return { 5964 priority: 10, 5965 link: function (scope, element, attrs) { 5966 element.on('select2-selecting', function(e) { 5967 e.preventDefault(); 5968 element.select2('close').select2('val', ''); 5969 scope.$apply(function() { 5970 scope.$eval(attrs.onCrmUiSelect, {selection: e.val}); 5971 }); 5972 }); 5973 } 5974 }; 5975 }) 5976 .directive('crmEntityref', function ($parse, $timeout) { 5977 return { 5978 require: '?ngModel', 5979 scope: { 5980 crmEntityref: '=' 5981 }, 5982 link: function (scope, element, attrs, ngModel) { 5983 5984 ngModel.$render = function () { 5985 $timeout(function () { 5986 let newVal = _.cloneDeep(ngModel.$modelValue); 5987 if (typeof newVal === 'string' && element.select2('container').hasClass('select2-container-multi')) { 5988 newVal = newVal.length ? newVal.split(',') : []; 5989 } 5990 element.select2('val', newVal); 5991 }); 5992 }; 5993 function refreshModel() { 5994 const oldValue = ngModel.$viewValue; 5995 let newValue = element.select2('val'); 5996 if (oldValue != newValue) { 5997 scope.$parent.$apply(function () { 5998 ngModel.$setViewValue(newValue); 5999 }); 6000 } 6001 } 6002
6003 function init() { 6004 element.crmEntityRef(scope.crmEntityref || {}); 6005 element.on('change', refreshModel); 6006 $timeout(ngModel.$render); 6007 } 6008 6009 init(); 6010 } 6011 }; 6012 }) 6013 .directive('crmAutocomplete', function () { 6014 return { 6015 require: { 6016 crmAutocomplete: 'crmAutocomplete', 6017 ngModel: '?ngModel' 6018 }, 6019 priority: 100, 6020 bindToController: { 6021 entity: '<crmAutocomplete', 6022 crmAutocompleteParams: '<', 6023 multi: '<', 6024 autoOpen: '<', 6025 quickAdd: '<', 6026 quickEdit: '<', 6027 staticOptions: '<' 6028 }, 6029 link: function(scope, element, attr, ctrl) { 6030 const parseList = function(viewValue) { 6031 if (typeof viewValue === 'undefined') { 6032 return; 6033 } 6034 6035 if (!ctrl.crmAutocomplete.multi) { 6036 return viewValue; 6037 } 6038 6039 const list = []; 6040 6041 if (viewValue) { 6042 _.each(viewValue.split(','), function(value) { 6043 if (value) { 6044 list.push(_.trim(value)); 6045 } 6046 }); 6047 } 6048 6049 return list; 6050 }; 6051 6052 if (ctrl.ngModel) { 6053 ctrl.ngModel.$render = function() { 6054 const newValue = (ctrl.ngModel.$viewValue === null || ctrl.ngModel.$viewValue === undefined || ctrl.ngModel.$viewValue === false) ? '' : ctrl.ngModel.$viewValue.toString(); 6055 if (newValue !== element.val().toString()) { 6056 element.val(newValue).change(); 6057 } 6058 }; 6059 ctrl.ngModel.$parsers.push(parseList); 6060 ctrl.ngModel.$formatters.push(function(value) { 6061 return Array.isArray(value) ? value.join(',') : value; 6062 }); 6063 ctrl.ngModel.$isEmpty = function(value) { 6064 return !value || !value.length; 6065 }; 6066 } 6067 }, 6068 controller: function($element, $timeout) { 6069 const ctrl = this; 6070 this.$onChanges = function() { 6071 $timeout(function() { 6072 const autoOpen = ctrl.autoOpen && 6073 !(ctrl.staticOptions && ctrl.staticOptions.length) && 6074 !(ctrl.quickAdd === true || (ctrl.quickAdd && ctrl.quickAdd.length)); 6075 $element.crmAutocomplete(ctrl.entity, ctrl.crmAutocompleteParams || {}, { 6076 multiple: ctrl.multi, 6077 minimumInputLength: autoOpen ? 0 : 1, 6078 static: ctrl.staticOptions || [], 6079 quickAdd: ctrl.quickAdd, 6080 quickEdit: ctrl.quickEdit, 6081 }); 6082 }); 6083 }; 6084 } 6085 }; 6086 }) 6087 .directive('crmMultipleEmail', function ($parse, $timeout) { 6088 return { 6089 require: 'ngModel', 6090 link: function(scope, element, attrs, ctrl) { 6091 ctrl.$parsers.unshift(function(viewValue) { 6092 if (_.isEmpty(viewValue)) { 6093 ctrl.$setValidity('crmMultipleEmail', true); 6094 return viewValue; 6095 } 6096 const emails = viewValue.split(','); 6097 const emailRegex = /\S+@\S+\.\S+/; 6098 6099 const validityArr = emails.map(function(str){ 6100 return emailRegex.test(str.trim()); 6101 }); 6102 6103 if ($.inArray(false, validityArr) > -1) { 6104 ctrl.$setValidity('crmMultipleEmail', false); 6105 } else { 6106 ctrl.$setValidity('crmMultipleEmail', true); 6107 } 6108 return viewValue; 6109 }); 6110 } 6111 }; 6112 }) 6113 .directive('crmUiTab', function($parse) { 6114 return { 6115 require: '^crmUiTabSet', 6116 restrict: 'EA', 6117 scope: { 6118 crmTitle: '@', 6119 crmIcon: '@', 6120 count: '@', 6121 id: '@' 6122 }, 6123 template: '<div ng-transclude></div>', 6124 transclude: true, 6125 link: function (scope, element, attrs, crmUiTabSetCtrl) { 6126 crmUiTabSetCtrl.add(scope); 6127 } 6128 }; 6129 }) 6130 .directive('crmUiTabSet', function() { 6131 return { 6132 restrict: 'EA', 6133 scope: { 6134 crmUiTabSet: '@', 6135 tabSetOptions: '<' 6136 }, 6137 templateUrl: '~/crmUi/tabset.html', 6138 transclude: true, 6139 controllerAs: 'crmUiTabSetCtrl', 6140 controller: function($scope, $element, $timeout) { 6141 let init; 6142 $scope.tabs = []; 6143 this.add = function(tab) { 6144 if (!tab.id) throw "Tab is missing 'id'"; 6145 $scope.tabs.push(tab); 6146 if (init) { 6147 $timeout.cancel(init); 6148 } 6149 init = $timeout(function() { 6150 $element.find('.crm-tabset').tabs($scope.tabSetOptions); 6151 }); 6152 }; 6153 } 6154 }; 6155 }) 6156 .directive('crmUiValidate', function() { 6157 return { 6158 restrict: 'EA', 6159 require: 'ngModel', 6160 link: function(scope, element, attrs, ngModel) { 6161 const validationKey = attrs.crmUiValidateName ? attrs.crmUiValidateName : 'crmUiValidate'; 6162 scope.$watch(attrs.crmUiValidate, function(newValue){ 6163 ngModel.$setValidity(validationKey, !!newValue); 6164 }); 6165 } 6166 }; 6167 }) 6168 .directive('crmUiVisible', function($parse) { 6169 return { 6170 restrict: 'EA', 6171 scope: { 6172 crmUiVisible: '@' 6173 }, 6174 link: function (scope, element, attrs) { 6175 const model = $parse(attrs.crmUiVisible); 6176 function updatecChildren() { 6177 element.css('visibility', model(scope.$parent) ? 'inherit' : 'hidden'); 6178 } 6179 updatecChildren(); 6180 scope.$parent.$watch(attrs.crmUiVisible, updatecChildren); 6181 } 6182 }; 6183 }) 6184 .directive('crmUiWizard', function() { 6185 return { 6186 restrict: 'EA', 6187 scope: { 6188 crmUiWizard: '@', 6189 crmUiWizardNavClass: '@' // string, A list of classes that will be added to the nav items 6190 }, 6191 templateUrl: '~/crmUi/wizard.html', 6192 transclude: true, 6193 controllerAs: 'crmUiWizardCtrl', 6194 controller: function($scope, $parse) { 6195 const steps = $scope.steps = []; // array<$scope> 6196 let maxVisited = 0; 6197 let selectedIndex = null; 6198 6199 const findIndex = function() { 6200 let found = null; 6201 angular.forEach(steps, function(step, stepKey) { 6202 if (step.selected) found = stepKey; 6203 }); 6204 return found; 6205 }; 6206 this.$index = function() { return selectedIndex; }; 6207 this.$first = function() { return this.$index() === 0; }; 6208 this.$last = function() { return this.$index() === steps.length -1; }; 6209 this.$maxVisit = function() { return maxVisited; }; 6210 this.$validStep = function() { 6211 return steps[selectedIndex] && steps[selectedIndex].isStepValid(); 6212 }; 6213 this.iconFor = function(index) { 6214 if (index < this.$index()) return 'crm-i fa-check'; 6215 if (index === this.$index()) return 'crm-i fa-angle-double-right'; 6216 return ''; 6217 }; 6218 this.isSelectable = function(step) { 6219 if (step.selected) return false;
6220 return this.$validStep(); 6221 }; 6222 6223 /*** @param Object step the $scope of the step */ 6224 this.select = function(step) { 6225 angular.forEach(steps, function(otherStep, otherKey) { 6226 otherStep.selected = (otherStep === step); 6227 if (otherStep === step && maxVisited < otherKey) maxVisited = otherKey; 6228 }); 6229 selectedIndex = findIndex(); 6230 }; 6231 /*** @param Object step the $scope of the step */ 6232 this.add = function(step) { 6233 if (steps.length === 0) { 6234 step.selected = true; 6235 selectedIndex = 0; 6236 } 6237 steps.push(step); 6238 steps.sort(function(a,b){ 6239 return a.crmUiWizardStep - b.crmUiWizardStep; 6240 }); 6241 selectedIndex = findIndex(); 6242 }; 6243 this.remove = function(step) { 6244 let key = null; 6245 angular.forEach(steps, function(otherStep, otherKey) { 6246 if (otherStep === step) key = otherKey; 6247 }); 6248 if (key !== null) { 6249 steps.splice(key, 1); 6250 } 6251 }; 6252 this.goto = function(index) { 6253 if (index < 0) index = 0; 6254 if (index >= steps.length) index = steps.length-1; 6255 this.select(steps[index]); 6256 }; 6257 this.previous = function() { this.goto(this.$index()-1); }; 6258 this.next = function() { this.goto(this.$index()+1); }; 6259 if ($scope.crmUiWizard) { 6260 $parse($scope.crmUiWizard).assign($scope.$parent, this); 6261 } 6262 }, 6263 link: function (scope, element, attrs) { 6264 scope.ts = CRM.ts(null); 6265 6266 element.find('.crm-wizard-buttons button[ng-click^=crmUiWizardCtrl]').click(function () { 6267 const topOfWizard = element.offset().top; 6268 const heightOfMenu = $('#civicrm-menu').height() || 0; 6269 6270 $('html') 6271 .stop() 6272 .animate({scrollTop: topOfWizard - heightOfMenu}, 1000); 6273 }); 6274 } 6275 }; 6276 }) 6277 .directive('crmUiWizardButtons', function() { 6278 return { 6279 require: '^crmUiWizard', 6280 restrict: 'EA', 6281 scope: {}, 6282 template: '<span ng-transclude></span>', 6283 transclude: true, 6284 link: function (scope, element, attrs, crmUiWizardCtrl) { 6285 const realButtonsEl = $(element).closest('.crm-wizard').find('.crm-wizard-buttons'); 6286 $(element).appendTo(realButtonsEl); 6287 } 6288 }; 6289 }) 6290 .directive('crmIcon', function() { 6291 return { 6292 restrict: 'EA', 6293 link: function (scope, element, attrs) { 6294 if (element.is('[crm-ui-tab]')) { 6295 return; 6296 } 6297 if (attrs.crmIcon) { 6298 if (attrs.crmIcon.substring(0,3) == 'fa-') { 6299 $(element).prepend('<i class="crm-i ' + attrs.crmIcon + '" role="img" aria-hidden="true"></i> '); 6300 } 6301 else { 6302 $(element).prepend('<span class="icon ui-icon-' + attrs.crmIcon + '"></span> '); 6303 } 6304 } 6305 if ($(element).is('button:not(.btn)')) { 6306 $(element).addClass('crm-button'); 6307 } 6308 } 6309 }; 6310 }) 6311 .directive('crmUiWizardStep', function() { 6312 let nextWeight = 1; 6313 return { 6314 require: ['^crmUiWizard', 'form'], 6315 restrict: 'EA', 6316 scope: { 6317 crmTitle: '@', // expression, evaluates to a printable string 6318 crmUiWizardStep: '@', // int, a weight which determines the ordering of the steps 6319 crmUiWizardStepClass: '@' // string, A list of classes that will be added to the template 6320 }, 6321 template: '<div class="crm-wizard-step {{crmUiWizardStepClass}}" ng-show="selected" ng-transclude/></div>', 6322 transclude: true, 6323 link: function (scope, element, attrs, ctrls) { 6324 const crmUiWizardCtrl = ctrls[0]; 6325 const form = ctrls[1]; 6326 if (scope.crmUiWizardStep) { 6327 scope.crmUiWizardStep = parseInt(scope.crmUiWizardStep); 6328 } else { 6329 scope.crmUiWizardStep = nextWeight++; 6330 } 6331 scope.isStepValid = function() { 6332 return form.$valid; 6333 }; 6334 crmUiWizardCtrl.add(scope); 6335 scope.$on('$destroy', function(){ 6336 crmUiWizardCtrl.remove(scope); 6337 }); 6338 } 6339 }; 6340 }) 6341 .directive('crmConfirm', function ($compile, $rootScope, $templateRequest, $q) { 6342 const defaultFuncs = { 6343 'disable': function (options) { 6344 return {
6345 message: ts('Are you sure you want to disable this?'), 6346 options: {no: ts('Cancel'), yes: ts('Disable')}, 6347 width: 300, 6348 title: ts('Disable %1?', { 6349 1: options.obj.title || options.obj.label || options.obj.name || ts('the record') 6350 }) 6351 }; 6352 }, 6353 'revert': function (options) { 6354 return { 6355 message: ts('Are you sure you want to revert this?'), 6356 options: {no: ts('Cancel'), yes: ts('Revert')}, 6357 width: 300, 6358 title: ts('Revert %1?', { 6359 1: options.obj.title || options.obj.label || options.obj.name || ts('the record') 6360 }) 6361 }; 6362 }, 6363 'delete': function (options) { 6364 return { 6365 message: ts('Are you sure you want to delete this?'), 6366 options: {no: ts('Cancel'), yes: ts('Delete')}, 6367 width: 300, 6368 title: ts('Delete %1?', { 6369 1: options.obj.title || options.obj.label || options.obj.name || ts('the record') 6370 }) 6371 }; 6372 } 6373 }; 6374 let confirmCount = 0; 6375 return { 6376 link: function (scope, element, attrs) { 6377 $(element).click(function () { 6378 const options = scope.$eval(attrs.crmConfirm); 6379 if (attrs.title && !options.title) { 6380 options.title = attrs.title; 6381 } 6382 const defaults = (options.type) ? defaultFuncs[options.type](options) : {}; 6383 6384 let tpl = null; 6385 let stubId = null; 6386 if (!options.message) { 6387 if (options.templateUrl) { 6388 tpl = $templateRequest(options.templateUrl); 6389 } 6390 else if (options.template) { 6391 tpl = options.template; 6392 } 6393 if (tpl) { 6394 stubId = 'crmUiConfirm_' + (++confirmCount); 6395 options.message = '<div id="' + stubId + '"></div>'; 6396 } 6397 } 6398 6399 if (options.confirmed) { 6400 scope.$apply(attrs.onYes); 6401 return; 6402 } 6403 6404 CRM.confirm(_.extend(defaults, options)) 6405 .on('crmConfirm:yes', function() { scope.$apply(attrs.onYes); }) 6406 .on('crmConfirm:no', function() { scope.$apply(attrs.onNo); }); 6407 6408 if (tpl && stubId) { 6409 $q.when(tpl, function(html) { 6410 const scope = options.scope || $rootScope.$new(); 6411 if (options.export) { 6412 angular.extend(scope, options.export); 6413 } 6414 const linker = $compile(html); 6415 $('#' + stubId).append($(linker(scope))); 6416 }); 6417 } 6418 }); 6419 } 6420 }; 6421 }) 6422 .directive('crmPageTitle', function($timeout) { 6423 return { 6424 scope: { 6425 crmDocumentTitle: '=' 6426 }, 6427 link: function(scope, $el, attrs) { 6428 pageTitleHTML = attrs.initialPageTitle || 'CiviCRM'; 6429 documentTitle = attrs.initialDocumentTitle || 'CiviCRM'; 6430 6431 function update() { 6432 $timeout(function() { 6433 const newPageTitleHTML = $el.html().trim(), 6434 newDocumentTitle = scope.crmDocumentTitle || $el.text(), 6435 dialog = $el.closest('.ui-dialog-content'); 6436 if (dialog.length) { 6437 dialog.dialog('option', 'title', newDocumentTitle); 6438 $el.hide(); 6439 } else { 6440 document.title = $('title').text().replace(documentTitle, newDocumentTitle); 6441 [].forEach.call(document.querySelectorAll('h1:not(.crm-container h1), .crm-page-title-wrapper>h1'), h1 => { 6442 if (h1.classList.contains('crm-page-title') || h1.innerHTML.trim() === pageTitleHTML) { 6443 h1.classList.add('crm-page-title'); 6444 h1.innerHTML = newPageTitleHTML; 6445 $el.hide(); 6446 } 6447 }); 6448 pageTitleHTML = newPageTitleHTML; 6449 documentTitle = newDocumentTitle; 6450 } 6451 }); 6452 } 6453 6454 scope.$watch(function() {return scope.crmDocumentTitle + $el.html();}, update); 6455 } 6456 }; 6457 }) 6458 .directive("crmUiEditable", function() { 6459 return { 6460 restrict: "A", 6461 require: "ngModel", 6462 scope: { 6463 defaultValue: '=' 6464 }, 6465 link: function(scope, element, attrs, ngModel) { 6466 function read() { 6467 let htmlVal = element.text(); 6468 if (!htmlVal) { 6469 htmlVal = scope.defaultValue || ''; 6470 element.text(htmlVal); 6471 } 6472 ngModel.$setViewValue(htmlVal); 6473 } 6474 6475 ngModel.$render = function() { 6476 element.text(ngModel.$viewValue || scope.defaultValue || ''); 6477 }; 6478 element.on('keydown', function(e) { 6479 if (e.which === 13) { 6480 e.preventDefault(); 6481 element.blur(); 6482 } 6483 if (e.which === 27) { 6484 element.text(ngModel.$viewValue || scope.defaultValue || ''); 6485 element.blur(); 6486 } 6487 }); 6488 6489 element.on("blur change", function() { 6490 scope.$apply(read); 6491 }); 6492 6493 element.attr('contenteditable', 'true'); 6494 } 6495 }; 6496 }) 6497 .directive('crmUiIconPicker', function($timeout) { 6498 return { 6499 restrict: 'A', 6500 require: '?ngModel', // Soft require ngModel 6501 controller: function($element, $scope, $attrs) { 6502 CRM.loadScript(CRM.config.resourceBase + 'js/jquery/jquery.crmIconPicker.js').then(function() { 6503 $timeout(function() { 6504 $element.crmIconPicker(); 6505 if ($attrs.ngModel) { 6506 $scope.$watch($attrs.ngModel, function(newValue) { 6507 if (newValue !== undefined) { 6508 $element.val(newValue).trigger('change'); 6509 } 6510 }); 6511 } 6512 }); 6513 }); 6514 } 6515 }; 6516 }) 6517 .factory('formatForSelect2', function() { 6518 return function(input, key, label, extra) { 6519 return input.reduce((result, item) => { 6520 const formatted = {id: item[key], text: item[label]}; 6521 6522 if (extra) { 6523 extra.forEach(prop => formatted[prop] = item[prop]); 6524 } 6525 6526 result.push(formatted); 6527 return result; 6528 }, []); 6529 }; 6530 }) 6531 6532 .run(function($rootScope, $location) { 6533 $rootScope.goto = function(path) { 6534 $location.path(path); 6535 }; 6536 }); 6537 6538
6539angular.module('crmUtil', CRM.angRequires('crmUtil')); 6540 angular.module('crmUtil').factory('crmApi', function($q) { 6541 var crmApi = function(entity, action, params, message) { 6542 var deferred = $q.defer(); 6543 var p; 6544 var backend = crmApi.backend || CRM.api3; 6545 if (params && params.body_html) { 6546 params.body_html = params.body_html.replace(/([\u2028]|[\u2029])/g, '\n'); 6547 } 6548 if (_.isObject(entity)) { 6549 /*jshint -W061 */ 6550 p = backend(eval('('+angular.toJson(entity)+')'), action); 6551 } else { 6552 /*jshint -W061 */ 6553 p = backend(entity, action, eval('('+angular.toJson(params)+')'), message); 6554 } 6555 p.then( 6556 function(result) { 6557 if (result.is_error) { 6558 deferred.reject(result); 6559 } else { 6560 deferred.resolve(result); 6561 } 6562 }, 6563 function(error) { 6564 deferred.reject(error); 6565 } 6566 ); 6567 return deferred.promise; 6568 }; 6569 crmApi.backend = null; 6570 crmApi.val = function(value) { 6571 var d = $.Deferred(); 6572 d.resolve(value); 6573 return d.promise(); 6574 }; 6575 return crmApi; 6576 }); 6577 angular.module('crmUtil').factory('crmMetadata', function($q, crmApi) { 6578 function convertOptionsToMap(options) { 6579 var result = {}; 6580 angular.forEach(options, function(o) { 6581 result[o.key] = o.value; 6582 }); 6583 return result; 6584 } 6585 6586 var cache = {}; // cache[entityName+'::'+action][fieldName].title 6587 var deferreds = {}; // deferreds[cacheKey].push($q.defer()) 6588 var crmMetadata = { 6589 getField: function getField(entity, field) { 6590 return $q.when(crmMetadata.getFields(entity)).then(function(fields){ 6591 return fields[field]; 6592 }); 6593 }, 6594 getFields: function getFields(entity) { 6595 var action = '', cacheKey; 6596 if (_.isArray(entity)) { 6597 action = entity[1]; 6598 entity = entity[0]; 6599 cacheKey = entity + '::' + action; 6600 } else { 6601 cacheKey = entity; 6602 } 6603 6604 if (_.isObject(cache[cacheKey])) { 6605 return cache[cacheKey]; 6606 } 6607 6608 var needFetch = _.isEmpty(deferreds[cacheKey]); 6609 deferreds[cacheKey] = deferreds[cacheKey] || []; 6610 var deferred = $q.defer(); 6611 deferreds[cacheKey].push(deferred); 6612 6613 if (needFetch) { 6614 crmApi(entity, 'getfields', {action: action, sequential: 1, options: {get_options: 'all'}}) 6615 .then( 6616 function(fields) { 6617 cache[cacheKey] = _.indexBy(fields.values, 'name'); 6618 angular.forEach(cache[cacheKey],function (field){ 6619 if (field.options) { 6620 field.optionsMap = convertOptionsToMap(field.options); 6621 } 6622 }); 6623 angular.forEach(deferreds[cacheKey], function(dfr) { 6624 dfr.resolve(cache[cacheKey]); 6625 }); 6626 delete deferreds[cacheKey]; 6627 }, 6628 function() { 6629 cache[cacheKey] = {}; // cache nack 6630 angular.forEach(deferreds[cacheKey], function(dfr) { 6631 dfr.reject(); 6632 }); 6633 delete deferreds[cacheKey]; 6634 } 6635 ); 6636 } 6637 6638 return deferred.promise; 6639 } 6640 }; 6641 6642 return crmMetadata; 6643 }); 6644 angular.module('crmUtil').factory('crmBlocker', function() { 6645 return function() { 6646 var blocks = 0; 6647 var result = function(promise) { 6648 blocks++; 6649 return promise.finally(function() { 6650 blocks--; 6651 }); 6652 }; 6653 result.check = function() { 6654 return blocks > 0; 6655 }; 6656 return result; 6657 }; 6658 }); 6659 6660 angular.module('crmUtil').factory('crmLegacy', function() { 6661 return CRM; 6662 }); 6663 angular.module('crmUtil').factory('crmLog', function(){ 6664 var level = 0; 6665 var write = console.log; 6666 function indent() { 6667 var s = '>'; 6668 for (var i = 0; i < level; i++) s = s + ' '; 6669 return s; 6670 } 6671 var crmLog = { 6672 log: function(msg, vars) { 6673 write(indent() + msg, vars); 6674 }, 6675 wrap: function(label, f) { 6676 return function(){ 6677 level++; 6678 crmLog.log(label + ": start", arguments); 6679 var r; 6680 try { 6681 r = f.apply(this, arguments); 6682 } finally { 6683 crmLog.log(label + ": end"); 6684 level--; 6685 } 6686 return r; 6687 }; 6688 } 6689 }; 6690 return crmLog; 6691 }); 6692
6693 angular.module('crmUtil').factory('crmNavigator', ['$window', function($window) { 6694 return { 6695 redirect: function(path) { 6696 $window.location.href = path; 6697 } 6698 }; 6699 }]); 6700 angular.module('crmUtil').factory('crmQueue', function($q) { 6701 return function crmQueue(worker) { 6702 var queue = []; 6703 function next() { 6704 var task = queue[0]; 6705 worker.apply(null, task.a).then( 6706 function onOk(data) { 6707 queue.shift(); 6708 task.dfr.resolve(data); 6709 if (queue.length > 0) next(); 6710 }, 6711 function onErr(err) { 6712 queue.shift(); 6713 task.dfr.reject(err); 6714 if (queue.length > 0) next(); 6715 } 6716 ); 6717 } 6718 function enqueue() { 6719 var dfr = $q.defer(); 6720 queue.push({a: arguments, dfr: dfr}); 6721 if (queue.length === 1) { 6722 next(); 6723 } 6724 return dfr.promise; 6725 } 6726 return enqueue; 6727 }; 6728 }); 6729 angular.module('crmUtil').factory('crmStatus', function($q){ 6730 return function(options, aPromise){ 6731 if (aPromise) { 6732 return CRM.toAPromise($q, CRM.status(options, CRM.toJqPromise(aPromise))); 6733 } else { 6734 return CRM.toAPromise($q, CRM.status(options)); 6735 } 6736 }; 6737 }); 6738 angular.module('crmUtil').factory('crmWatcher', function(){ 6739 return function() { 6740 var unwatches = {}, watchFactories = {}, suspends = {}; 6741 this.setup = function(name, newWatchFactory) { 6742 watchFactories[name] = newWatchFactory; 6743 unwatches[name] = watchFactories[name](); 6744 suspends[name] = 0; 6745 return this; 6746 }; 6747 this.suspend = function(name, f) { 6748 suspends[name]++; 6749 this.teardown(name); 6750 var r; 6751 try { 6752 r = f.apply(this, []); 6753 } finally { 6754 if (suspends[name] === 1) { 6755 unwatches[name] = watchFactories[name](); 6756 if (!angular.isArray(unwatches[name])) { 6757 unwatches[name] = [unwatches[name]]; 6758 } 6759 } 6760 suspends[name]--; 6761 } 6762 return r; 6763 }; 6764 6765 this.teardown = function(name) { 6766 if (!unwatches[name]) return; 6767 _.each(unwatches[name], function(unwatch){ 6768 unwatch(); 6769 }); 6770 delete unwatches[name]; 6771 }; 6772 6773 return this; 6774 }; 6775 }); 6776 angular.module('crmUtil').factory('crmThrottle', function($q) { 6777 var pending = [], 6778 executing = []; 6779 return function(func) { 6780 var deferred = $q.defer(); 6781 6782 function checkResult(result, success) { 6783 _.pull(executing, func); 6784 if (_.includes(pending, func)) { 6785 runNext(); 6786 } else if (success) { 6787 deferred.resolve(result); 6788 } else { 6789 deferred.reject(result); 6790 } 6791 } 6792 6793 function runNext() { 6794 executing.push(func); 6795 _.pull(pending, func); 6796 func().then(function(result) { 6797 checkResult(result, true); 6798 }, function(result) { 6799 checkResult(result, false); 6800 }); 6801 } 6802 6803 if (!_.includes(executing, func)) { 6804 runNext(); 6805 } else if (!_.includes(pending, func)) { 6806 pending.push(func); 6807 } 6808 return deferred.promise; 6809 }; 6810 }); 6811 6812 angular.module('crmUtil').factory('crmLoadScript', function($q) { 6813 return function(url) { 6814 var deferred = $q.defer(); 6815 6816 CRM.loadScript(url).done(function() { 6817 deferred.resolve(true); 6818 }); 6819 6820 return deferred.promise; 6821 }; 6822 }); 6823 angular.module('crmUtil').filter('encodeURIComponent', function() { 6824 return function(input) { 6825 return input ? encodeURIComponent(input) : ''; 6826 }; 6827 }); 6828 6829 6830 6831 /** 6832 * Allows using ng-model with a custom element like civi-rich-text-input 6833 */ 6834 angular.module("crmUtil").directive("crmCustomElementModel", () => ({ 6835 restrict: "A", 6836 require: 'ngModel', 6837 link: function ($scope, $element, attributes, ngModelController) { 6838 const element = $element[0]; 6839 ngModelController.$render = () => element.value = ngModelController.$viewValue; 6840 element.addEventListener('change', () => ngModelController.$setViewValue(element.value)); 6841 } 6842 })); 6843 6844})(angular, CRM.$, CRM._); 6845 6846(function($, angular){ 6847angular.module('dialogService', []).service('dialogService', 6848 ['$rootScope', '$q', '$compile', '$templateCache', '$http', 6849 function($rootScope, $q, $compile, $templateCache, $http) { 6850 6851 var _this = this; 6852 _this.dialogs = {}; 6853 6854 this.open = function(id, template, model, options) { 6855 if (!angular.isDefined(id)) { 6856 throw "dialogService requires id in call to open"; 6857 } 6858 6859 if (!angular.isDefined(template)) { 6860 throw "dialogService requires template in call to open"; 6861 } 6862 if (!angular.isDefined(model)) { 6863 model = null; 6864 } 6865 var dialogOptions = {}; 6866 if (angular.isDefined(options)) { 6867 angular.extend(dialogOptions, options); 6868 } 6869 var dialog = { scope: null, ref: null, deferred: $q.defer() }; 6870 loadTemplate(template).then( 6871 function(dialogTemplate) { 6872 dialog.scope = $rootScope.$new(); 6873 dialog.scope.model = model; 6874 var dialogLinker = $compile(dialogTemplate); 6875 dialog.ref = $(dialogLinker(dialog.scope)); 6876 var customCloseFn = dialogOptions.close; 6877 dialogOptions.close = function(event, ui) { 6878 if (customCloseFn) { 6879 customCloseFn(event, ui); 6880 } 6881 cleanup(id); 6882 }; 6883 dialog.ref.dialog(dialogOptions); 6884 dialog.ref.dialog("open"); 6885 _this.dialogs[id] = dialog; 6886 6887 }, function(error) { 6888 throw error; 6889 } 6890 ); 6891 return dialog.deferred.promise; 6892 }; 6893 6894 this.close = function(id, result) { 6895 var dialog = getExistingDialog(id); 6896 dialog.deferred.resolve(result); 6897 dialog.ref.dialog("close"); 6898 }; 6899 6900 this.cancel = function(id) { 6901 var dialog = getExistingDialog(id); 6902 dialog.deferred.reject(); 6903 dialog.ref.dialog("close"); 6904 }; 6905 6906 this.setButtons = function(id, buttons) { 6907 var dialog = getExistingDialog(id); 6908 dialog.ref.dialog("option", 'buttons', buttons); 6909 }; 6910 6911 function cleanup (id) { 6912 var dialog = getExistingDialog(id); 6913 dialog.deferred.reject(); 6914 dialog.scope.$destroy(); 6915 dialog.ref.remove(); 6916 delete _this.dialogs[id]; 6917 }; 6918 6919 function getExistingDialog(id) { 6920 var dialog = _this.dialogs[id]; 6921 if (!angular.isDefined(dialog)) { 6922 throw "DialogService does not have a reference to dialog id " + id; 6923 } 6924 return dialog; 6925 }; 6926 function loadTemplate(template) { 6927 6928 var deferred = $q.defer(); 6929 var html = $templateCache.get(template); 6930 6931 if (angular.isDefined(html)) { 6932 html = html.trim(); 6933 deferred.resolve(html); 6934 } else { 6935 return $http.get(template, { cache : $templateCache }).then( 6936 function(response) { 6937 var html = response.data; 6938 if(!html || !html.length) { 6939 return $q.reject("Template " + template + " was not found"); 6940 } 6941 html = html.trim(); 6942 $templateCache.put(template, html); 6943 return html; 6944 }, function() { 6945 return $q.reject("Template " + template + " was not found"); 6946 } 6947 ); 6948 } 6949 return deferred.promise; 6950 } 6951 }
vendor: 4,665 bytes, lines 6952-6967
6952]); 6953})(jQuery, angular); 6954 6955/* 6956 AngularJS v1.8.0 6957 (c) 2010-2020 Google, Inc. http://angularjs.org 6958 License: MIT 6959*/ 6960(function(s,c){'use strict';function P(c){var h=[];C(h,E).chars(c);return h.join("")}var D=c.$$minErr("$sanitize"),F,h,G,H,I,q,E,J,K,C;c.module("ngSanitize",[]).provider("$sanitize",function(){function f(a,e){return B(a.split(","),e)}function B(a,e){var d={},b;for(b=0;b<a.length;b++)d[e?q(a[b]):a[b]]=!0;return d}function t(a,e){e&&e.length&&h(a,B(e))}function Q(a){for(var e={},d=0,b=a.length;d<b;d++){var k=a[d];e[k.name]=k.value}return e}function L(a){return a.replace(/&/g,"&").replace(z,function(a){var d= 6961a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(d-55296)+(a-56320)+65536)+";"}).replace(u,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"<").replace(/>/g,">")}function A(a){for(;a;){if(a.nodeType===s.Node.ELEMENT_NODE)for(var e=a.attributes,d=0,b=e.length;d<b;d++){var k=e[d],g=k.name.toLowerCase();if("xmlns:ns1"===g||0===g.lastIndexOf("ns1:",0))a.removeAttributeNode(k),d--,b--}(e=a.firstChild)&&A(e);a=v("nextSibling",a)}}function v(a,e){var d=e[a];if(d&&J.call(e,d))throw D("elclob", 6962e.outerHTML||e.outerText);return d}var y=!1,g=!1;this.$get=["$$sanitizeUri",function(a){y=!0;g&&h(m,l);return function(e){var d=[];K(e,C(d,function(b,d){return!/^unsafe:/.test(a(b,d))}));return d.join("")}}];this.enableSvg=function(a){return I(a)?(g=a,this):g};this.addValidElements=function(a){y||(H(a)&&(a={htmlElements:a}),t(l,a.svgElements),t(r,a.htmlVoidElements),t(m,a.htmlVoidElements),t(m,a.htmlElements));return this};this.addValidAttrs=function(a){y||h(M,B(a,!0));return this};F=c.bind;h=c.extend; 6963G=c.forEach;H=c.isArray;I=c.isDefined;q=c.$$lowercase;E=c.noop;K=function(a,e){null===a||void 0===a?a="":"string"!==typeof a&&(a=""+a);var d=N(a);if(!d)return"";var b=5;do{if(0===b)throw D("uinput");b--;a=d.innerHTML;d=N(a)}while(a!==d.innerHTML);for(b=d.firstChild;b;){switch(b.nodeType){case 1:e.start(b.nodeName.toLowerCase(),Q(b.attributes));break;case 3:e.chars(b.textContent)}var k;if(!(k=b.firstChild)&&(1===b.nodeType&&e.end(b.nodeName.toLowerCase()),k=v("nextSibling",b),!k))for(;null==k;){b= 6964v("parentNode",b);if(b===d)break;k=v("nextSibling",b);1===b.nodeType&&e.end(b.nodeName.toLowerCase())}b=k}for(;b=d.firstChild;)d.removeChild(b)};C=function(a,e){var d=!1,b=F(a,a.push);return{start:function(a,g){a=q(a);!d&&w[a]&&(d=a);d||!0!==m[a]||(b("<"),b(a),G(g,function(d,g){var c=q(g),f="img"===a&&"src"===c||"background"===c;!0!==M[c]||!0===O[c]&&!e(d,f)||(b(" "),b(g),b('="'),b(L(d)),b('"'))}),b(">"))},end:function(a){a=q(a);d||!0!==m[a]||!0===r[a]||(b("</"),b(a),b(">"));a==d&&(d=!1)},chars:function(a){d|| 6965b(L(a))}}};J=s.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)};var z=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,u=/([^#-~ |!])/g,r=f("area,br,col,hr,img,wbr"),x=f("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),p=f("rp,rt"),n=h({},p,x),x=h({},x,f("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")),p=h({},p,f("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")), 6966l=f("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan"),w=f("script,style"),m=h({},r,x,p,n),O=f("background,cite,href,longdesc,src,xlink:href,xml:base"),n=f("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width"), 6967p=f("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,str
6967ikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan", 6968!0),M=h({},O,p,n),N=function(a,e){function d(b){b="<remove></remove>"+b;try{var d=(new a.DOMParser).parseFromString(b,"text/html").body;d.firstChild.remove();return d}catch(e){}}function b(a){c.innerHTML=a;e.documentMode&&A(c);return c}var g;if(e&&e.implementation)g=e.implementation.createHTMLDocument("inert");else throw D("noinert");var c=(g.documentElement||g.getDocumentElement()).querySelector("body");c.innerHTML='<svg><g onload="this.parentNode.remove()"></g></svg>';return c.querySelector("svg")? 6969(c.innerHTML='<svg><p><style><img src="</style><img src=x onerror=alert(1)//">',c.querySelector("svg img")?d:b):function(b){b="<remove></remove>"+b;try{b=encodeURI(b)}catch(d){return}var e=new a.XMLHttpRequest;e.responseType="document";e.open("GET","data:text/html;charset=utf-8,"+b,!1);e.send(null);b=e.response.body;b.firstChild.remove();return b}}(s,s.document)}).info({angularVersion:"1.8.0"});c.module("ngSanitize").filter("linky",["$sanitize",function(f){var h=/((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, 6970t=/^mailto:/i,q=c.$$minErr("linky"),s=c.isDefined,A=c.isFunction,v=c.isObject,y=c.isString;return function(c,z,u){function r(c){c&&l.push(P(c))}function x(c,g){var f,a=p(c);l.push("<a ");for(f in a)l.push(f+'="'+a[f]+'" ');!s(z)||"target"in a||l.push('target="',z,'" ');l.push('href="',c.replace(/"/g,"""),'">');r(g);l.push("</a>")}if(null==c||""===c)return c;if(!y(c))throw q("notstring",c);for(var p=A(u)?u:v(u)?function(){return u}:function(){return{}},n=c,l=[],w,m;c=n.match(h);)w=c[0],c[2]|| 6971c[4]||(w=(c[3]?"http://":"mailto:")+w),m=c.index,r(n.substr(0,m)),x(w,c[0].replace(t,"")),n=n.substring(m+c[0].length);r(n);return f(l.join(""))}}])})(window,window.angular); 6972 6973/* 6974 * angular-ui-bootstrap 6975 * http://angular-ui.github.io/bootstrap/ 6976 6977 * Version: 2.5.0 - 2017-01-28 6978 * License: MIT 6979 */angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.collapse","ui.bootstrap.tabindex","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.multiMap","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.collapse",[]).directive("uibCollapse",["$animate","$q","$parse","$injector",function(a,b,c,d){var e=d.has("$animateCss")?d.get("$animateCss"):null;return{link:function(d,f,g){function h(){r=!!("horizontal"in g),r?(s={width:""},t={width:"0"}):(s={height:""},t={height:"0"}),d.$eval(g.uibCollapse)||f.addClass("in").addClass("collapse").attr("aria-expanded",!0).attr("aria-hidden",!1).css(s)}function i(a){return r?{width:a.scrollWidth+"px"}:{height:a.scrollHeight+"px"}}function j(){f.hasClass("collapse")&&f.hasClass("in")||b.resolve(n(d)).then(function(){f.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),e?e(f,{addClass:"in",easing:"ease",css:{overflow:"hidden"},to:i(f[0])}).start()["finally"](k):a.addClass(f,"in",{css:{overflow:"hidden"},to:i(f[0])}).then(k)},angular.noop)}function k(){f.removeClass("collapsing").addClass("collapse").css(s),o(d)}function l(){return f.hasClass("collapse")||f.hasClass("in")?void b.resolve(p(d)).then(function(){f.css(i(f[0])).removeClass("collapse").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),e?e(f,{removeClass:"in",to:t}).start()["finally"](m):a.removeClass(f,"in",{to:t}).then(m)},angular.noop):m()}function m(){f.css(t),f.removeClass("collapsing").addClass("collapse"),q(d)}var n=c(g.expanding),o=c(g.expanded),p=c(g.collapsing),q=c(g.collapsed),r=!1,s={},t={};
vendor: 4,630 bytes, line 6979
6979h(),d.$watch(g.uibCollapse,function(a){a?l():j()})}}}]),angular.module("ui.bootstrap.tabindex",[]).directive("uibTabindexToggle",function(){return{restrict:"A",link:function(a,b,c){c.$observe("disabled",function(a){c.$set("tabindex",a?-1:null)})}}}),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse","ui.bootstrap.tabindex"]).constant("uibAccordionConfig",{closeOthers:!0}).controller("UibAccordionController",["$scope","$attrs","uibAccordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(c){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(b,1)}}]).directive("uibAccordion",function(){return{controller:"UibAccordionController",controllerAs:"accordion",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion.html"}}}).directive("uibAccordionGroup",function(){return{require:"^uibAccordion",transclude:!0,restrict:"A",templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion-group.html"},scope:{heading:"@",panelClass:"@?",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(a,b,c,d){b.addClass("panel"),d.addGroup(a),a.openClass=c.openClass||"panel-open",a.panelClass=c.panelClass||"panel-default",a.$watch("isOpen",function(c){b.toggleClass(a.openClass,!!c),c&&d.closeOthers(a)}),a.toggleOpen=function(b){a.isDisabled||b&&32!==b.which||(a.isOpen=!a.isOpen)};var e="accordiongroup-"+a.$id+"-"+Math.floor(1e4*Math.random());a.headingId=e+"-tab",a.panelId=e+"-panel"}}}).directive("uibAccordionHeading",function(){return{transclude:!0,template:"",replace:!0,require:"^uibAccordionGroup",link:function(a,b,c,d,e){d.setHeading(e(a,angular.noop))}}}).directive("uibAccordionTransclude",function(){function a(){return"uib-accordion-header,data-uib-accordion-header,x-uib-accordion-header,uib\\:accordion-header,[uib-accordion-header],[data-uib-accordion-header],[x-uib-accordion-header]"}return{require:"^uibAccordionGroup",link:function(b,c,d,e){b.$watch(function(){return e[d.uibAccordionTransclude]},function(b){if(b){var d=angular.element(c[0].querySelector(a()));d.html(""),d.append(b)}})}}}),angular.module("ui.bootstrap.alert",[]).controller("UibAlertController",["$scope","$element","$attrs","$interpolate","$timeout",function(a,b,c,d,e){a.closeable=!!c.close,b.addClass("alert"),c.$set("role","alert"),a.closeable&&b.addClass("alert-dismissible");var f=angular.isDefined(c.dismissOnTimeout)?d(c.dismissOnTimeout)(a.$parent):null;f&&e(function(){a.close()},parseInt(f,10))}]).directive("uibAlert",function(){return{controller:"UibAlertController",controllerAs:"alert",restrict:"A",templateUrl:function(a,b){return b.templateUrl||"uib/template/alert/alert.html"},transclude:!0,scope:{close:"&"}}}),angular.module("ui.bootstrap.buttons",[]).constant("uibButtonConfig",{activeClass:"active",toggleEvent:"click"}).controller("UibButtonsController",["uibButtonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("uibBtnRadio",["$parse",function(a){return{require:["uibBtnRadio","ngModel"],controller:"UibButtonsController",controllerAs:"buttons",link:function(b,c,d,e){var f=e[0],g=e[1],h=a(d.uibUncheckable);c.find("input").css({display:"none"}),g.$render=function(){c.toggleClass(f.activeClass,angular.equals(g.$modelValue,b.$eval(d.uibBtnRadio)))},c.on(f.toggleEvent,function(){if(!d.disabled){var a=c.hasClass(f.activeClass);a&&!angular.isDefined(d.uncheckable)||b.$apply(function(){g.$setViewValue(a?null:b.$eval(d.uibBtnRadio)),g.$render()})}}),d.uibUncheckable&&b.$watch(h,function(a){d.$set("uncheckable",a?"":void 0)})}}}]).directive("uibBtnCheckbox",function(){return{require:["uibBtnCheckbox","ngModel"],controller:"UibButtonsController",controllerAs:"button",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){return angular.isDefined(b)?a.$eval(b):c}var h=d[0],i=d[1];b.find("input").css({display:"none"}),i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.on(h.toggleEvent,function(){c.disabled||a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.carousel",[]).controller("UibCarouselController",["$scope","$element","$interval","$t
6979imeout","$animate",function(a,b,c,d,e){function f(a){for(var b=0;b<p.length;b++)p[b].slide.active=b===a}function g(c,d,g){if(!s){if(angular.extend(c,{direction:g}),angular.extend(p[r].slide||{},{direction:g}),e.enabled(b)&&!a.$currentTransition&&p[d].element&&o.slides.length>1){p[d].element.data(q,c.direction);var h=o.getCurrentIndex();angular.isNumber(h)&&p[h].element&&p[h].element.data(q,c.direction),a.$currentTransition=!0,e.on("addClass",p[d].element,function(b,c){"close"===c&&(a.$currentTransition=null,e.off("addClass",b))})}a.active=c.index,r=c.index,f(d),k()}}function h(a){for(var b=0;b<p.length;b++)if(p[b].slide===a)return b}function i(){m&&(c.cancel(m),m=null)}function j(b){b.length||(a.$currentTransition=null)}function k(){i();var b=+a.interval;!isNaN(b)&&b>0&&(m=c(l,b))}function l(){var b=+a.interval;n&&!isNaN(b)&&b>0&&p.length?a.next():a.pause()}var m,n,o=this,p=o.slides=a.slides=[],q="uib-slideDirection",r=a.active,s=!1;b.addClass("carousel"),o.addSlide=function(b,c){p.push({slide:b,element:c}),p.sort(function(a,b){return+a.slide.index-+b.slide.index}),(b.index===a.active||1===p.length&&!angular.isNumber(a.active))&&(a.$currentTransition&&(a.$currentTransition=null),r=b.index,a.active=b.index,f(r),o.select(p[h(b)]),1===p.length&&a.play())},o.getCurrentIndex=function(){for(var a=0;a<p.length;a++)if(p[a].slide.index===r)return a},o.next=a.next=function(){var b=(o.getCurrentIndex()+1)%p.length;return 0===b&&a.noWrap()?void a.pause():o.select(p[b],"next")},o.prev=a.prev=function(){var b=o.getCurrentIndex()-1<0?p.length-1:o.getCurrentIndex()-1;return a.noWrap()&&b===p.length-1?void a.pause():o.select(p[b],"prev")},o.removeSlide=function(b){var c=h(b);p.splice(c,1),p.length>0&&r===c?c>=p.length?(r=p.length-1,a.active=r,f(r),o.select(p[p.length-1])):(r=c,a.active=r,f(r),o.select(p[c])):r>c&&(r--,a.active=r),0===p.length&&(r=null,a.active=null)},o.select=a.select=function(b,c){var d=h(b.slide);void 0===c&&(c=d>o.getCurrentIndex()?"next":"prev"),b.slide.index===r||a.$currentTransition||g(b.slide,d,c)},a.indexOfSlide=function(a){return+a.slide.index},a.isActive=function(b){return a.active===b.slide.index},a.isPrevDisabled=function(){return 0===a.active&&a.noWrap()},a.isNextDisabled=function(){return a.active===p.length-1&&a.noWrap()},a.pause=function(){a.noPause||(n=!1,i())},a.play=function(){n||(n=!0,k())},b.on("mouseenter",a.pause),b.on("mouseleave",a.play),a.$on("$destroy",function(){s=!0,i()}),a.$watch("noTransition",function(a){e.enabled(b,!a)}),a.$watch("interval",k),a.$watchCollection("slides",j),a.$watch("active",function(a){if(angular.isNumber(a)&&r!==a){for(var b=0;b<p.length;b++)if(p[b].slide.index===a){a=b;break}var c=p[a];c&&(f(a),o.select(p[a]),r=a)}})}]).directive("uibCarousel",function(){return{transclude:!0,controller:"UibCarouselController",controllerAs:"carousel",restrict:"A",templateUrl:function(a,b){return b.templateUrl||"uib/template/carousel/carousel.html"},scope:{active:"=",interval:"=",noTransition:"=",noPause:"=",noWrap:"&"}}}).directive("uibSlide",["$animate",function(a){return{require:"^uibCarousel",restrict:"A",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/carousel/slide.html"},scope:{actual:"=?",index:"=?"},link:function(b,c,d,e){c.addClass("item"),e.addSlide(b,c),b.$on("$destroy",function(){e.removeSlide(b)}),b.$watch("active",function(b){a[b?"addClass":"removeClass"](c,"active")})}}}]).animation(".item",["$animateCss",function(a){function b(a,b,c){a.removeClass(b),c&&c()}var c="uib-slideDirection";return{beforeAddClass:function(d,e,f){if("active"===e){var g=!1,h=d.data(c),i="next"===h?"left":"right",j=b.bind(this,d,i+" "+h,f);return d.addClass(h),a(d,{addClass:i}).start().done(j),function(){g=!0}}f()},beforeRemoveClass:function(d,e,f){if("active"===e){var g=!1,h=d.data(c),i="next"===h?"left":"right",j=b.bind(this,d,i,f);return a(d,{addClass:i}).start().done(j),function(){g=!0}}f()}}}]),angular.module("ui.bootstrap.dateparser",[]).service("uibDateParser",["$log","$locale","dateFilter","orderByFilter","filterFilter",function(a,b,c,d,e){function f(a){return e(s,{key:a},!0)[0]}
vendor: 28,178 bytes, lines 6979-6980
6979function g(a){var b=[],c=a.split(""),e=a.indexOf("'");if(e>-1){var f=!1;a=a.split("");for(var g=e;g<a.length;g++)f?("'"===a[g]&&(g+1<a.length&&"'"===a[g+1]?(a[g+1]="$",c[g+1]=""):(c[g]="",f=!1)),a[g]="$"):"'"===a[g]&&(a[g]="$",c[g]="",f=!0);a=a.join("")}return angular.forEach(s,function(d){var e=a.indexOf(d.key);if(e>-1){a=a.split(""),c[e]="("+d.regex+")",a[e]="$";for(var f=e+1,g=e+d.key.length;g>f;f++)c[f]="",a[f]="$";a=a.join(""),b.push({index:e,key:d.key,apply:d.apply,matcher:d.regex})}}),{regex:new RegExp("^"+c.join("")+"$"),map:d(b,"index")}}function h(a){for(var b,c,d=[],e=0;e<a.length;)if(angular.isNumber(c)){if("'"===a.charAt(e))(e+1>=a.length||"'"!==a.charAt(e+1))&&(d.push(i(a,c,e)),c=null);else if(e===a.length)for(;c<a.length;)b=j(a,c),d.push(b),c=b.endIdx;e++}else"'"!==a.charAt(e)?(b=j(a,e),d.push(b.parser),e=b.endIdx):(c=e,e++);return d}function i(a,b,c){return function(){return a.substr(b+1,c-b-1)}}function j(a,b){for(var c=a.substr(b),d=0;d<s.length;d++)if(new RegExp("^"+s[d].key).test(c)){var e=s[d];return{endIdx:b+e.key.length,parser:e.formatter}}return{endIdx:b+1,parser:function(){return c.charAt(0)}}}function k(a,b,c){return 1>c?!1:1===b&&c>28?29===c&&(a%4===0&&a%100!==0||a%400===0):3===b||5===b||8===b||10===b?31>c:!0}function l(a){return parseInt(a,10)}function m(a,b){return a&&b?q(a,b):a}function n(a,b){return a&&b?q(a,b,!0):a}function o(a,b){a=a.replace(/:/g,"");var c=Date.parse("Jan 01, 1970 00:00:00 "+a)/6e4;return isNaN(c)?b:c}function p(a,b){return a=new Date(a.getTime()),a.setMinutes(a.getMinutes()+b),a}function q(a,b,c){c=c?-1:1;var d=a.getTimezoneOffset(),e=o(b,d);return p(a,c*(e-d))}var r,s,t=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;this.init=function(){r=b.id,this.parsers={},this.formatters={},s=[{key:"yyyy",regex:"\\d{4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yyyy")}},{key:"yy",regex:"\\d{2}",apply:function(a){a=+a,this.year=69>a?a+2e3:a+1900},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yy")}},{key:"y",regex:"\\d{1,4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"y")}},{key:"M!",regex:"0?[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){var b=a.getMonth();return/^[0-9]$/.test(b)?c(a,"MM"):c(a,"M")}},{key:"MMMM",regex:b.DATETIME_FORMATS.MONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.MONTH.indexOf(a)},formatter:function(a){return c(a,"MMMM")}},{key:"MMM",regex:b.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.SHORTMONTH.indexOf(a)},formatter:function(a){return c(a,"MMM")}},{key:"MM",regex:"0[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"MM")}},{key:"M",regex:"[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"M")}},{key:"d!",regex:"[0-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){var b=a.getDate();return/^[1-9]$/.test(b)?c(a,"dd"):c(a,"d")}},{key:"dd",regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"dd")}},{key:"d",regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"d")}},{key:"EEEE",regex:b.DATETIME_FORMATS.DAY.join("|"),formatter:function(a){return c(a,"EEEE")}},{key:"EEE",regex:b.DATETIME_FORMATS.SHORTDAY.join("|"),formatter:function(a){return c(a,"EEE")}},{key:"HH",regex:"(?:0|1)[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"HH")}},{key:"hh",regex:"0[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"hh")}},{key:"H",regex:"1?[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"H")}},{key:"h",regex:"[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"h")}},{key:"mm",regex:"[0-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"mm")}},{key:"m",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"m")}},{key:"sss",regex:"[0-9][0-9][0-9]",apply:function(a){this.milliseconds=+a},formatter:function(a){return c(a,"sss")}},{key:"ss",regex:"[0-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"ss")}},{key:"s",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"s")}},{key:"a",regex:b.DATETIME_FORMATS.AMPMS.join("|"),apply:function(a){12===this.hours&&(this.hours=0),"PM"===a&&(this.hours+=12)},formatter:function(a){return c(a,"a")}},{key:"Z",regex:"[+-]\\d{4}",apply:function(a){var b=a.match(/([+-])(\d{2})(\d{2})/),c=b[1],d=b[2],e=b[3];this.hours+=l(c+d),this.minutes+=l(c+e)},formatter:function(a){return c(a,"Z")}},{key:"ww",regex:"[0-4][0-9]|5[0-3]",formatter:function(a){return c(a,"ww")}},{key:"w",regex:"[0-9]|[1-4][0-9]|5[0-3]",formatter:function(a){return c(a,"w")}},{key:"GGGG",regex:b.DATETIME_FORMATS.ERANAMES.join("|").replace(/\s/g,"\\s"),formatter:function(a){return c(a,"GGGG")}},{key:"GGG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GGG")}},{key:"GG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GG")}},{key:"G",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"G")}}],angular.version.major>=1&&angular.version.minor>4&&s.push({key:"LLLL",regex:b.DATETIME_FORMATS.STANDALONEMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.STANDALONEMONTH.indexOf(a)},formatter:function(a){return c(a,"LLLL")}})},this.init(),this.getParser=function(a){var b=f(a);return b&&b.apply||null},this.overrideParser=function(a,b){var c=f(a);c&&angular.isFunction(b)&&(this.parsers={},c.apply=b)}.bind(this),this.filter=function(a,c){if(!angular.isDate(a)||isNaN(a)||!c)return"";c=b.DATETIME_FORMATS[c]||c,b.id!==r&&this.init(),this.formatters[c]||(this.formatters[c]=h(c));var d=this.formatters[c];return d.reduce(function(b,c){return b+c(a)},"")},this.parse=function(c,d,e){if(!angular.isString(c)||!d)return c;d=b.DATETIME_FORMATS[d]||d,d=d.replace(t,"\\$&"),b.id!==r&&this.init(),this.parsers[d]||(this.parsers[d]=g(d,"apply"));var f=this.parsers[d],h=f.regex,i=f.map,j=c.match(h),l=!1;if(j&&j.length){var m,n;angular.isDate(e)&&!isNaN(e.getTime())?m={year:e.getFullYear(),month:e.getMonth(),date:e.getDate(),hours:e.getHours(),minutes:e.getMinutes(),seconds:e.getSeconds(),milliseconds:e.getMilliseconds()}:(e&&a.warn("dateparser:","baseDate is not a valid date"),m={year:1900,month:0,date:1,hours:0,minutes:0,seconds:0,milliseconds:0});for(var o=1,p=j.length;p>o;o++){var q=i[o-1];"Z"===q.matcher&&(l=!0),q.apply&&q.apply.call(m,j[o])}var s=l?Date.prototype.setUTCFullYear:Date.prototype.setFullYear,u=l?Date.prototype.setUTCHours:Date.prototype.setHours;return k(m.year,m.month,m.date)&&(!angular.isDate(e)||isNaN(e.getTime())||l?(n=new Date(0),s.call(n,m.year,m.month,m.date),u.call(n,m.hours||0,m.minutes||0,m.seconds||0,m.milliseconds||0)):(n=new Date(e),s.call(n,m.year,m.month,m.date),u.call(n,m.hours,m.minutes,m.seconds,m.milliseconds))),n}},this.toTimezone=m,this.fromTimezone=n,this.timezoneToOffset=o,this.addDateMinutes=p,this.convertTimezoneToLocal=q}]),angular.module("ui.bootstrap.isClass",[]).directive("uibIsClass",["$animate",function(a){var b=/^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/,c=/^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;return{restrict:"A",compile:function(d,e){function f(a,b,c){i.push(a),j.push({scope:a,element:b}),o.forEach(function(b,c){g(b,a)}),a.$on("$destroy",h)}function g(b,d){var e=b.match(c),f=d.$eval(e[1]),g=e[2],h=k[b];if(!h){var i=function(b){var c=null;j.some(function(a){var d=a.scope.$eval(m);return d===b?(c=a,!0):void 0}),h.lastActivated!==c&&(h.lastActivated&&a.removeClass(h.lastActivated.element,f),c&&a.addClass(c.element,f),h.lastActivated=c)};k[b]=h={lastActivated:null,scope:d,watchFn:i,compareWithExp:g,watcher:d.$watch(g,i)}}h.watchFn(d.$eval(g))}function h(a){var b=a.targetScope,c=i.indexOf(b);if(i.splice(c,1),j.splice(c,1),i.length){var d=i[0];angular.forEach(k,function(a){a.scope===b&&(a.watcher=d.$watch(a.compareWithExp,a.watchFn),a.scope=d)})}else k={}}var i=[],j=[],k={},l=e.uibIsClass.match(b),m=l[2],n=l[1],o=n.split(",");return f}}}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.isClass"]).value("$datepickerSuppressError",!1).value("$datepickerLiteralWarning",!0).constant("uibDatepickerConfig",{datepickerMode:"day",formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",maxDate:null,maxMode:"year",minDate:null,minMode:"day",monthColumns:3,ngModelOptions:{},shortcutPropagation:!1,showWeeks:!0,yearColumns:5,yearRows:4}).controller("UibDatepickerController",["$scope","$element","$attrs","$parse","$interpolate","$locale","$log","dateFilter","uibDatepickerConfig","$datepickerLiteralWarning","$datepickerSuppressError","uibDateParser",function(a,b,c,d,e,f,g,h,i,j,k,l){function m(b){a.datepickerMode=b,a.datepickerOptions.datepickerMode=b}function n(b){var c;if(angular.version.minor<6)c=b.$options||a.datepickerOptions.ngModelOptions||i.ngModelOptions||{},c.getOption=function(a){return c[a]};else{var d=b.$options.getOption("timezone")||(a.datepickerOptions.ngModelOptions?a.datepickerOptions.ngModelOptions.timezone:null)||(i.ngModelOptions?i.ngModelOptions.timezone:null);c=b.$options.createChild(i.ngModelOptions).createChild(a.datepickerOptions.ngModelOptions).createChild(b.$options).createChild({timezone:d})}return c}var o=this,p={$setViewValue:angular.noop},q={},r=[];b.addClass("uib-datepicker"),c.$set("role","application"),a.datepickerOptions||(a.datepickerOptions={}),this.modes=["day","month","year"],["customClass","dateDisabled","datepickerMode","formatDay","formatDayHeader","formatDayTitle","formatMonth","formatMonthTitle","formatYear","maxDate","maxMode","minDate","minMode","monthColumns","showWeeks","shortcutPropagation","startingDay","yearColumns","yearRows"].forEach(function(b){switch(b){case"customClass":case"dateDisabled":a[b]=a.datepickerOptions[b]||angular.noop;break;case"datepickerMode":a.datepickerMode=angular.isDefined(a.datepickerOptions.datepickerMode)?a.datepickerOptions.datepickerMode:i.datepickerMode;break;case"formatDay":case"formatDayHeader":case"formatDayTitle":case"formatMonth":case"formatMonthTitle":case"formatYear":o[b]=angular.isDefined(a.datepickerOptions[b])?e(a.datepickerOptions[b])(a.$parent):i[b];break;case"monthColumns":case"showWeeks":case"shortcutPropagation":case"yearColumns":case"yearRows":o[b]=angular.isDefined(a.datepickerOptions[b])?a.datepickerOptions[b]:i[b];break;case"startingDay":angular.isDefined(a.datepickerOptions.startingDay)?o.startingDay=a.datepickerOptions.startingDay:angular.isNumber(i.startingDay)?o.startingDay=i.startingDay:o.startingDay=(f.DATETIME_FORMATS.FIRSTDAYOFWEEK+8)%7;break;case"maxDate":case"minDate":a.$watch("datepickerOptions."+b,function(a){a?angular.isDate(a)?o[b]=l.fromTimezone(new Date(a),q.getOption("timezone")):(j&&g.warn("Literal date support has been deprecated, please switch to date object usage"),o[b]=new Date(h(a,"medium"))):o[b]=i[b]?l.fromTimezone(new Date(i[b]),q.getOption("timezone")):null,o.refreshView()});break;case"maxMode":case"minMode":a.datepickerOptions[b]?a.$watch(function(){return a.datepickerOptions[b]},function(c){o[b]=a[b]=angular.isDefined(c)?c:a.datepickerOptions[b],("minMode"===b&&o.modes.indexOf(a.datepickerOptions.datepickerMode)<o.modes.indexOf(o[b])||"maxMode"===b&&o.modes.indexOf(a.datepickerOptions.datepickerMode)>o.modes.indexOf(o[b]))&&(a.datepickerMode=o[b],a.datepickerOptions.datepickerMode=o[b])}):o[b]=a[b]=i[b]||null}}),a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),a.disabled=angular.isDefined(c.disabled)||!1,angular.isDefined(c.ngDisabled)&&r.push(a.$parent.$watch(c.ngDisabled,function(b){a.disabled=b,o.refreshView()})),a.isActive=function(b){return 0===o.compare(b.date,o.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(b){p=b,q=n(p),a.datepickerOptions.initDate?(o.activeDate=l.fromTimezone(a.datepickerOptions.initDate,q.getOption("timezone"))||new Date,a.$watch("datepickerOptions.initDate",function(a){a&&(p.$isEmpty(p.$modelValue)||p.$invalid)&&(o.activeDate=l.fromTimezone(a,q.getOption("timezone")),o.refreshView())})):o.activeDate=new Date;var c=p.$modelValue?new Date(p.$modelValue):new Date;this.activeDate=isNaN(c)?l.fromTimezone(new Date,q.getOption("timezone")):l.fromTimezone(c,q.getOption("timezone")),p.$render=function(){o.render()}},this.render=function(){if(p.$viewValue){var a=new Date(p.$viewValue),b=!isNaN(a);b?this.activeDate=l.fromTimezone(a,q.getOption("timezone")):k||g.error('Datepicker directive: "ng-model" value must be a Date object')}this.refreshView()},this.refreshView=function(){if(this.element){a.selectedDt=null,this._refreshView(),a.activeDt&&(a.activeDateId=a.activeDt.uid);var b=p.$viewValue?new Date(p.$viewValue):null;b=l.fromTimezone(b,q.getOption("timezone")),p.$setValidity("dateDisabled",!b||this.element&&!this.isDisabled(b))}},this.createDateObject=function(b,c){var d=p.$viewValue?new Date(p.$viewValue):null;d=l.fromTimezone(d,q.getOption("timezone"));var e=new Date;e=l.fromTimezone(e,q.getOption("timezone"));var f=this.compare(b,e),g={date:b,label:l.filter(b,c),selected:d&&0===this.compare(b,d),disabled:this.isDisabled(b),past:0>f,current:0===f,future:f>0,customClass:this.customClass(b)||null};return d&&0===this.compare(b,d)&&(a.selectedDt=g),o.activeDate&&0===this.compare(g.date,o.activeDate)&&(a.activeDt=g),g},this.isDisabled=function(b){return a.disabled||this.minDate&&this.compare(b,this.minDate)<0||this.maxDate&&this.compare(b,this.maxDate)>0||a.dateDisabled&&a.dateDisabled({date:b,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},a.select=function(b){if(a.datepickerMode===o.minMode){var c=p.$viewValue?l.fromTimezone(new Date(p.$viewValue),q.getOption("timezone")):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),c=l.toTimezone(c,q.getOption("timezone")),p.$setViewValue(c),p.$render()}else o.activeDate=b,m(o.modes[o.modes.indexOf(a.datepickerMode)-1]),a.$emit("uib:datepicker.mode");a.$broadcast("uib:datepicker.focus")},a.move=function(a){var b=o.activeDate.getFullYear()+a*(o.step.years||0),c=o.activeDate.getMonth()+a*(o.step.months||0);o.activeDate.setFullYear(b,c,1),o.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===o.maxMode&&1===b||a.datepickerMode===o.minMode&&-1===b||(m(o.modes[o.modes.indexOf(a.datepickerMode)+b]),a.$emit("uib:datepicker.mode"))},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var s=function(){o.element[0].focus()};a.$on("uib:datepicker.focus",s),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey&&!a.disabled)if(b.preventDefault(),o.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(o.isDisabled(o.activeDate))return;a.select(o.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(o.handleKeyDown(c,b),o.refreshView()):a.toggleMode("up"===c?1:-1)},b.on("keydown",function(b){a.$apply(function(){a.keydown(b)})}),a.$on("$destroy",function(){for(;r.length;)r.shift()()})}]).controller("UibDaypickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a,b){return 1!==b||a%4!==0||a%100===0&&a%400!==0?f[b]:29}function e(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}var f=[31,28,31,30,31,30,31,31,30,31,30,31];this.step={months:1},this.element=b,this.init=function(b){angular.extend(b,this),a.showWeeks=b.showWeeks,b.refreshView()},this.getDates=function(a,b){for(var c,d=new Array(b),e=new Date(a),f=0;b>f;)c=new Date(e),d[f++]=c,e.setDate(e.getDate()+1);return d},this._refreshView=function(){var b=this.activeDate.getFullYear(),d=this.activeDate.getMonth(),f=new Date(this.activeDate);f.setFullYear(b,d,1);var g=this.startingDay-f.getDay(),h=g>0?7-g:-g,i=new Date(f);h>0&&i.setDate(-h+1);for(var j=this.getDates(i,42),k=0;42>k;k++)j[k]=angular.extend(this.createDateObject(j[k],this.formatDay),{secondary:j[k].getMonth()!==d,uid:a.uniqueId+"-"+k});a.labels=new Array(7);for(var l=0;7>l;l++)a.labels[l]={abbr:c(j[l].date,this.formatDayHeader),full:c(j[l].date,"EEEE")};if(a.title=c(this.activeDate,this.formatDayTitle),a.rows=this.split(j,7),a.showWeeks){a.weekNumbers=[];for(var m=(11-this.startingDay)%7,n=a.rows.length,o=0;n>o;o++)a.weekNumbers.push(e(a.rows[o][m].date))}},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth(),a.getDate()),d=new Date(b.getFullYear(),b.getMonth(),b.getDate());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getDate();if("left"===a)c-=1;else if("up"===a)c-=7;else if("right"===a)c+=1;else if("down"===a)c+=7;else if("pageup"===a||"pagedown"===a){var e=this.activeDate.getMonth()+("pageup"===a?-1:1);this.activeDate.setMonth(e,1),c=Math.min(d(this.activeDate.getFullYear(),this.activeDate.getMonth()),c)}else"home"===a?c=1:"end"===a&&(c=d(this.activeDate.getFullYear(),this.activeDate.getMonth()));this.activeDate.setDate(c)}}]).controller("UibMonthpickerController",["$scope","$element","dateFilter",function(a,b,c){this.step={years:1},this.element=b,this.init=function(a){angular.extend(a,this),a.refreshView()},this._refreshView=function(){for(var b,d=new Array(12),e=this.activeDate.getFullYear(),f=0;12>f;f++)b=new Date(this.activeDate),b.setFullYear(e,f,1),d[f]=angular.extend(this.createDateObject(b,this.formatMonth),{uid:a.uniqueId+"-"+f});a.title=c(this.activeDate,this.formatMonthTitle),a.rows=this.split(d,this.monthColumns),a.yearHeaderColspan=this.monthColumns>3?this.monthColumns-2:1},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth()),d=new Date(b.getFullYear(),b.getMonth());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getMonth();if("left"===a)c-=1;else if("up"===a)c-=this.monthColumns;else if("right"===a)c+=1;else if("down"===a)c+=this.monthColumns;else if("pageup"===a||"pagedown"===a){var d=this.activeDate.getFullYear()+("pageup"===a?-1:1);this.activeDate.setFullYear(d)}else"home"===a?c=0:"end"===a&&(c=11);this.activeDate.setMonth(c)}}]).controller("UibYearpickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a){return parseInt((a-1)/f,10)*f+1}var e,f;this.element=b,this.yearpickerInit=function(){e=this.yearColumns,f=this.yearRows*e,this.step={years:f}},this._refreshView=function(){for(var b,c=new Array(f),g=0,h=d(this.activeDate.getFullYear());f>g;g++)b=new Date(this.activeDate),b.setFullYear(h+g,0,1),c[g]=angular.extend(this.createDateObject(b,this.formatYear),{uid:a.uniqueId+"-"+g});a.title=[c[0].label,c[f-1].label].join(" - "),a.rows=this.split(c,e),a.columns=e},this.compare=function(a,b){return a.getFullYear()-b.getFullYear()},this.handleKeyDown=function(a,b){var c=this.activeDate.getFullYear();"left"===a?c-=1:"up"===a?c-=e:"right"===a?c+=1:"down"===a?c+=e:"pageup"===a||"pagedown"===a?c+=("pageup"===a?-1:1)*f:"home"===a?c=d(this.activeDate.getFullYear()):"end"===a&&(c=d(this.activeDate.getFullYear())+f-1),this.activeDate.setFullYear(c)}}]).directive("uibDatepicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/datepicker.html"},scope:{datepickerOptions:"=?"},require:["uibDatepicker","^ngModel"],restrict:"A",controller:"UibDatepickerController",controllerAs:"datepicker",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}).directive("uibDaypicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/day.html"}, 6980require:["^uibDatepicker","uibDaypicker"],restrict:"A",controller:"UibDaypickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibMonthpicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/month.html"},require:["^uibDatepicker","uibMonthpicker"],restrict:"A",controller:"UibMonthpickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibYearpicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/year.html"},require:["^uibDatepicker","uibYearpicker"],restrict:"A",controller:"UibYearpickerController",link:function(a,b,c,d){var e=d[0];angular.extend(e,d[1]),e.yearpickerInit(),e.refreshView()}}}),angular.module("ui.bootstrap.position",[]).factory("$uibPosition",["$document","$window",function(a,b){var c,d,e={normal:/(auto|scroll)/,hidden:/(auto|scroll|hidden)/},f={auto:/\s?auto?\s?/i,primary:/^(top|bottom|left|right)$/,secondary:/^(top|bottom|left|right|center)$/,vertical:/^(top|bottom)$/},g=/(HTML|BODY)/;return{getRawNode:function(a){return a.nodeName?a:a[0]||a},parseStyle:function(a){return a=parseFloat(a),isFinite(a)?a:0},offsetParent:function(c){function d(a){return"static"===(b.getComputedStyle(a).position||"static")}c=this.getRawNode(c);for(var e=c.offsetParent||a[0].documentElement;e&&e!==a[0].documentElement&&d(e);)e=e.offsetParent;return e||a[0].documentElement},scrollbarWidth:function(e){if(e){if(angular.isUndefined(d)){var f=a.find("body");f.addClass("uib-position-body-scrollbar-measure"),d=b.innerWidth-f[0].clientWidth,d=isFinite(d)?d:0,f.removeClass("uib-position-body-scrollbar-measure")}return d}if(angular.isUndefined(c)){var g=angular.element('<div class="uib-position-scrollbar-measure"></div>');a.find("body").append(g),c=g[0].offsetWidth-g[0].clientWidth,c=isFinite(c)?c:0,g.remove()}return c},scrollbarPadding:function(a){a=this.getRawNode(a);var c=b.getComputedStyle(a),d=this.parseStyle(c.paddingRight),e=this.parseStyle(c.paddingBottom),f=this.scrollParent(a,!1,!0),h=this.scrollbarWidth(g.test(f.tagName));return{scrollbarWidth:h,widthOverflow:f.scrollWidth>f.clientWidth,right:d+h,originalRight:d,heightOverflow:f.scrollHeight>f.clientHeight,bottom:e+h,originalBottom:e}},isScrollable:function(a,c){a=this.getRawNode(a);var d=c?e.hidden:e.normal,f=b.getComputedStyle(a);return d.test(f.overflow+f.overflowY+f.overflowX)},scrollParent:function(c,d,f){c=this.getRawNode(c);var g=d?e.hidden:e.normal,h=a[0].documentElement,i=b.getComputedStyle(c);if(f&&g.test(i.overflow+i.overflowY+i.overflowX))return c;var j="absolute"===i.position,k=c.parentElement||h;if(k===h||"fixed"===i.position)return h;for(;k.parentElement&&k!==h;){var l=b.getComputedStyle(k);if(j&&"static"!==l.position&&(j=!1),!j&&g.test(l.overflow+l.overflowY+l.overflowX))break;k=k.parentElement}return k},position:function(c,d){c=this.getRawNode(c);var e=this.offset(c);if(d){var f=b.getComputedStyle(c);e.top-=this.parseStyle(f.marginTop),e.left-=this.parseStyle(f.marginLeft)}var g=this.offsetParent(c),h={top:0,left:0};return g!==a[0].documentElement&&(h=this.offset(g),h.top+=g.clientTop-g.scrollTop,h.left+=g.clientLeft-g.scrollLeft),{width:Math.round(angular.isNumber(e.width)?e.width:c.offsetWidth),height:Math.round(angular.isNumber(e.height)?e.height:c.offsetHeight),top:Math.round(e.top-h.top),left:Math.round(e.left-h.left)}},offset:function(c){c=this.getRawNode(c);var d=c.getBoundingClientRect();return{width:Math.round(angular.isNumber(d.width)?d.width:c.offsetWidth),height:Math.round(angular.isNumber(d.height)?d.height:c.offsetHeight),top:Math.round(d.top+(b.pageYOffset||a[0].documentElement.scrollTop)),left:Math.round(d.left+(b.pageXOffset||a[0].documentElement.scrollLeft))}},viewportOffset:function(c,d,e){c=this.getRawNode(c),e=e!==!1;var f=c.getBoundingClientRect(),g={top:0,left:0,bottom:0,right:0},h=d?a[0].documentElement:this.scrollParent(c),i=h.getBoundingClientRect();if(g.top=i.top+h.clientTop,g.left=i.left+h.clientLeft,h===a[0].documentElement&&(g.top+=b.pageYOffset,g.left+=b.pageXOffset),g.bottom=g.top+h.clientHeight,g.right=g.left+h.clientWidth,e){var j=b.getComputedStyle(h);g.top+=this.parseStyle(j.paddingTop),g.bottom-=this.parseStyle(j.paddingBottom),g.left+=this.parseStyle(j.paddingLeft),g.right-=this.parseStyle(j.paddingRight)}return{top:Math.round(f.top-g.top),bottom:Math.round(g.bottom-f.bottom),left:Math.round(f.left-g.left),right:Math.round(g.right-f.right)}},parsePlacement:function(a){var b=f.auto.test(a);return b&&(a=a.replace(f.auto,"")),a=a.split("-"),a[0]=a[0]||"top",f.primary.test(a[0])||(a[0]="top"),a[1]=a[1]||"center",f.secondary.test(a[1])||(a[1]="center"),b?a[2]=!0:a[2]=!1,a},positionElements:function(a,c,d,e){a=this.getRawNode(a),c=this.getRawNode(c);var g=angular.isDefined(c.offsetWidth)?c.offsetWidth:c.prop("offsetWidth"),h=angular.isDefined(c.offsetHeight)?c.offsetHeight:c.prop("offsetHeight");d=this.parsePlacement(d);var i=e?this.offset(a):this.position(a),j={top:0,left:0,placement:""};if(d[2]){var k=this.viewportOffset(a,e),l=b.getComputedStyle(c),m={width:g+Math.round(Math.abs(this.parseStyle(l.marginLeft)+this.parseStyle(l.marginRight))),height:h+Math.round(Math.abs(this.parseStyle(l.marginTop)+this.parseStyle(l.marginBottom)))};if(d[0]="top"===d[0]&&m.height>k.top&&m.height<=k.bottom?"bottom":"bottom"===d[0]&&m.height>k.bottom&&m.height<=k.top?"top":"left"===d[0]&&m.width>k.left&&m.width<=k.right?"right":"right"===d[0]&&m.width>k.right&&m.width<=k.left?"left":d[0],d[1]="top"===d[1]&&m.height-i.height>k.bottom&&m.height-i.height<=k.top?"bottom":"bottom"===d[1]&&m.height-i.height>k.top&&m.height-i.height<=k.bottom?"top":"left"===d[1]&&m.width-i.width>k.right&&m.width-i.width<=k.left?"right":"right"===d[1]&&m.width-i.width>k.left&&m.width-i.width<=k.right?"left":d[1],"center"===d[1])if(f.vertical.test(d[0])){var n=i.width/2-g/2;k.left+n<0&&m.width-i.width<=k.right?d[1]="left":k.right+n<0&&m.width-i.width<=k.left&&(d[1]="right")}else{var o=i.height/2-m.height/2;k.top+o<0&&m.height-i.height<=k.bottom?d[1]="top":k.bottom+o<0&&m.height-i.height<=k.top&&(d[1]="bottom")}}switch(d[0]){case"top":j.top=i.top-h;break;case"bottom":j.top=i.top+i.height;break;case"left":j.left=i.left-g;break;case"right":j.left=i.left+i.width}switch(d[1]){case"top":j.top=i.top;break;case"bottom":j.top=i.top+i.height-h;break;case"left":j.left=i.left;break;case"right":j.left=i.left+i.width-g;break;case"center":f.vertical.test(d[0])?j.left=i.left+i.width/2-g/2:j.top=i.top+i.height/2-h/2}return j.top=Math.round(j.top),j.left=Math.round(j.left),j.placement="center"===d[1]?d[0]:d[0]+"-"+d[1],j},adjustTop:function(a,b,c,d){return-1!==a.indexOf("top")&&c!==d?{top:b.top-d+"px"}:void 0},positionArrow:function(a,c){a=this.getRawNode(a);var d=a.querySelector(".tooltip-inner, .popover-inner");if(d){var e=angular.element(d).hasClass("tooltip-inner"),g=e?a.querySelector(".tooltip-arrow"):a.querySelector(".arrow");if(g){var h={top:"",bottom:"",left:"",right:""};if(c=this.parsePlacement(c),"center"===c[1])return void angular.element(g).css(h);var i="border-"+c[0]+"-width",j=b.getComputedStyle(g)[i],k="border-";k+=f.vertical.test(c[0])?c[0]+"-"+c[1]:c[1]+"-"+c[0],k+="-radius";var l=b.getComputedStyle(e?d:a)[k];switch(c[0]){case"top":h.bottom=e?"0":"-"+j;break;case"bottom":h.top=e?"0":"-"+j;break;case"left":h.right=e?"0":"-"+j;break;case"right":h.left=e?"0":"-"+j}h[c[1]]=l,angular.element(g).css(h)}}}}}]),angular.module("ui.bootstrap.datepickerPopup",["ui.bootstrap.datepicker","ui.bootstrap.position"]).value("$datepickerPopupLiteralWarning",!0).constant("uibDatepickerPopupConfig",{altInputFormats:[],appendToBody:!1,clearText:"Clear",closeOnDateSelection:!0,closeText:"Done",currentText:"Today",datepickerPopup:"yyyy-MM-dd",datepickerPopupTemplateUrl:"uib/template/datepickerPopup/popup.html",datepickerTemplateUrl:"uib/template/datepicker/datepicker.html",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},onOpenFocus:!0,showButtonBar:!0,placement:"auto bottom-left"}).controller("UibDatepickerPopupController",["$scope","$element","$attrs","$compile","$log","$parse","$
6980window","$document","$rootScope","$uibPosition","dateFilter","uibDateParser","uibDatepickerPopupConfig","$timeout","uibDatepickerConfig","$datepickerPopupLiteralWarning",function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){function q(b){var c=l.parse(b,x,a.date);if(isNaN(c))for(var d=0;d<J.length;d++)if(c=l.parse(b,J[d],a.date),!isNaN(c))return c;return c}function r(a){if(angular.isNumber(a)&&(a=new Date(a)),!a)return null;if(angular.isDate(a)&&!isNaN(a))return a;if(angular.isString(a)){var b=q(a);if(!isNaN(b))return l.toTimezone(b,H.getOption("timezone"))}return H.getOption("allowInvalid")?a:void 0}function s(a,b){var d=a||b;return c.ngRequired||d?(angular.isNumber(d)&&(d=new Date(d)),d?angular.isDate(d)&&!isNaN(d)?!0:angular.isString(d)?!isNaN(q(d)):!1:!0):!0}function t(c){if(a.isOpen||!a.disabled){var d=I[0],e=b[0].contains(c.target),f=void 0!==d.contains&&d.contains(c.target);!a.isOpen||e||f||a.$apply(function(){a.isOpen=!1})}}function u(c){27===c.which&&a.isOpen?(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!1}),b[0].focus()):40!==c.which||a.isOpen||(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!0}))}function v(){if(a.isOpen){var d=angular.element(I[0].querySelector(".uib-datepicker-popup")),e=c.popupPlacement?c.popupPlacement:m.placement,f=j.positionElements(b,d,e,z);d.css({top:f.top+"px",left:f.left+"px"}),d.hasClass("uib-position-measure")&&d.removeClass("uib-position-measure")}}function w(a){var b;return angular.version.minor<6?(b=angular.isObject(a.$options)?a.$options:{timezone:null},b.getOption=function(a){return b[a]}):b=a.$options,b}var x,y,z,A,B,C,D,E,F,G,H,I,J,K=!1,L=[];this.init=function(e){if(G=e,H=w(G),y=angular.isDefined(c.closeOnDateSelection)?a.$parent.$eval(c.closeOnDateSelection):m.closeOnDateSelection,z=angular.isDefined(c.datepickerAppendToBody)?a.$parent.$eval(c.datepickerAppendToBody):m.appendToBody,A=angular.isDefined(c.onOpenFocus)?a.$parent.$eval(c.onOpenFocus):m.onOpenFocus,B=angular.isDefined(c.datepickerPopupTemplateUrl)?c.datepickerPopupTemplateUrl:m.datepickerPopupTemplateUrl,C=angular.isDefined(c.datepickerTemplateUrl)?c.datepickerTemplateUrl:m.datepickerTemplateUrl,J=angular.isDefined(c.altInputFormats)?a.$parent.$eval(c.altInputFormats):m.altInputFormats,a.showButtonBar=angular.isDefined(c.showButtonBar)?a.$parent.$eval(c.showButtonBar):m.showButtonBar,m.html5Types[c.type]?(x=m.html5Types[c.type],K=!0):(x=c.uibDatepickerPopup||m.datepickerPopup,c.$observe("uibDatepickerPopup",function(a,b){var c=a||m.datepickerPopup;if(c!==x&&(x=c,G.$modelValue=null,!x))throw new Error("uibDatepickerPopup must have a date format specified.")})),!x)throw new Error("uibDatepickerPopup must have a date format specified.");if(K&&c.uibDatepickerPopup)throw new Error("HTML5 date input types do not support custom formats.");D=angular.element("<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>"),D.attr({"ng-model":"date","ng-change":"dateSelection(date)","template-url":B}),E=angular.element(D.children()[0]),E.attr("template-url",C),a.datepickerOptions||(a.datepickerOptions={}),K&&"month"===c.type&&(a.datepickerOptions.datepickerMode="month",a.datepickerOptions.minMode="month"),E.attr("datepicker-options","datepickerOptions"),K?G.$formatters.push(function(b){return a.date=l.fromTimezone(b,H.getOption("timezone")),b}):(G.$$parserName="date",G.$validators.date=s,G.$parsers.unshift(r),G.$formatters.push(function(b){return G.$isEmpty(b)?(a.date=b,b):(angular.isNumber(b)&&(b=new Date(b)),a.date=l.fromTimezone(b,H.getOption("timezone")),l.filter(a.date,x))})),G.$viewChangeListeners.push(function(){a.date=q(G.$viewValue)}),b.on("keydown",u),I=d(D)(a),D.remove(),z?h.find("body").append(I):b.after(I),a.$on("$destroy",function(){for(a.isOpen===!0&&(i.$$phase||a.$apply(function(){a.isOpen=!1})),I.remove(),b.off("keydown",u),h.off("click",t),F&&F.off("scroll",v),angular.element(g).off("resize",v);L.length;)L.shift()()})},a.getText=function(b){return a[b+"Text"]||m[b+"Text"]},a.isDisabled=function(b){"today"===b&&(b=l.fromTimezone(new Date,H.getOption("timezone")));var c={};return angular.forEach(["minDate","maxDate"],function(b){a.datepickerOptions[b]?angular.isDate(a.datepickerOptions[b])?c[b]=new Date(a.datepickerOptions[b]):(p&&e.warn("Literal date support has been deprecated, please switch to date object usage"),c[b]=new Date(k(a.datepickerOptions[b],"medium"))):c[b]=null}),a.datepickerOptions&&c.minDate&&a.compare(b,c.minDate)<0||c.maxDate&&a.compare(b,c.maxDate)>0},a.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},a.dateSelection=function(c){a.date=c;var d=a.date?l.filter(a.date,x):null;b.val(d),G.$setViewValue(d),y&&(a.isOpen=!1,b[0].focus())},a.keydown=function(c){27===c.which&&(c.stopPropagation(),a.isOpen=!1,b[0].focus())},a.select=function(b,c){if(c.stopPropagation(),"today"===b){var d=new Date;angular.isDate(a.date)?(b=new Date(a.date),b.setFullYear(d.getFullYear(),d.getMonth(),d.getDate())):(b=l.fromTimezone(d,H.getOption("timezone")),b.setHours(0,0,0,0))}a.dateSelection(b)},a.close=function(c){c.stopPropagation(),a.isOpen=!1,b[0].focus()},a.disabled=angular.isDefined(c.disabled)||!1,c.ngDisabled&&L.push(a.$parent.$watch(f(c.ngDisabled),function(b){a.disabled=b})),a.$watch("isOpen",function(d){d?a.disabled?a.isOpen=!1:n(function(){v(),A&&a.$broadcast("uib:datepicker.focus"),h.on("click",t);var d=c.popupPlacement?c.popupPlacement:m.placement;z||j.parsePlacement(d)[2]?(F=F||angular.element(j.scrollParent(b)),F&&F.on("scroll",v)):F=null,angular.element(g).on("resize",v)},0,!1):(h.off("click",t),F&&F.off("scroll",v),angular.element(g).off("resize",v))}),a.$on("uib:datepicker.mode",function(){n(v,0,!1)})}]).directive("uibDatepickerPopup",function(){return{require:["ngModel","uibDatepickerPopup"],controller:"UibDatepickerPopupController",scope:{datepickerOptions:"=?",isOpen:"=?",currentText:"@",clearText:"@",closeText:"@"},link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibDatepickerPopupWrap",function(){return{restrict:"A",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepickerPopup/popup.html"}}}),angular.module("ui.bootstrap.debounce",[]).factory("$$debounce",["$timeout",function(a){return function(b,c){var d;return function(){var e=this,f=Array.prototype.slice.call(arguments);d&&a.cancel(d),d=a(function(){b.apply(e,f)},c)}}}]),angular.module("ui.bootstrap.multiMap",[]).factory("$$multiMap",function(){return{createNew:function(){var a={};return{entries:function(){return Object.keys(a).map(function(b){return{key:b,value:a[b]}})},get:function(b){return a[b]},hasKey:function(b){return!!a[b]},keys:function(){return Object.keys(a)},put:function(b,c){a[b]||(a[b]=[]),a[b].push(c)},remove:function(b,c){var d=a[b];if(d){var e=d.indexOf(c);-1!==e&&d.splice(e,1),d.length||delete a[b]}}}}}}),angular.module("ui.bootstrap.dropdown",["ui.bootstrap.multiMap","ui.bootstrap.position"]).constant("uibDropdownConfig",{appendToOpenClass:"uib-dropdown-open",openClass:"open"}).service("uibDropdownService",["$document","$rootScope","$$multiMap",function(a,b,c){var d=null,e=c.createNew();this.isOnlyOpen=function(a,b){var c=e.get(b);if(c){var d=c.reduce(function(b,c){return c.scope===a?c:b},{});if(d)return 1===c.length}return!1},this.open=function(b,c,g){if(d||a.on("click",f),d&&d!==b&&(d.isOpen=!1),d=b,g){var h=e.get(g);if(h){var i=h.map(function(a){return a.scope});-1===i.indexOf(b)&&e.put(g,{scope:b})}else e.put(g,{scope:b})}},this.close=function(b,c,g){if(d===b&&(a.off("click",f),a.off("keydown",this.keybindFilter),d=null),g){var h=e.get(g);if(h){var i=h.reduce(function(a,c){return c.scope===b?c:a},{});i&&e.remove(g,i)}}};
vendor: 9,649 bytes, line 6980
6980var f=function(a){if(d&&d.isOpen&&!(a&&"disabled"===d.getAutoClose()||a&&3===a.which)){var c=d.getToggleElement();if(!(a&&c&&c[0].contains(a.target))){var e=d.getDropdownElement();a&&"outsideClick"===d.getAutoClose()&&e&&e[0].contains(a.target)||(d.focusToggleElement(),d.isOpen=!1,b.$$phase||d.$apply())}}};this.keybindFilter=function(a){if(d){var b=d.getDropdownElement(),c=d.getToggleElement(),e=b&&b[0].contains(a.target),g=c&&c[0].contains(a.target);27===a.which?(a.stopPropagation(),d.focusToggleElement(),f()):d.isKeynavEnabled()&&-1!==[38,40].indexOf(a.which)&&d.isOpen&&(e||g)&&(a.preventDefault(),a.stopPropagation(),d.focusDropdownEntry(a.which))}}}]).controller("UibDropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest",function(a,b,c,d,e,f,g,h,i,j,k){function l(){b.append(o.dropdownMenu)}var m,n,o=this,p=a.$new(),q=e.appendToOpenClass,r=e.openClass,s=angular.noop,t=c.onToggle?d(c.onToggle):angular.noop,u=!1,v=i.find("body");b.addClass("dropdown"),this.init=function(){c.isOpen&&(n=d(c.isOpen),s=n.assign,a.$watch(n,function(a){p.isOpen=!!a})),u=angular.isDefined(c.keyboardNav)},this.toggle=function(a){return p.isOpen=arguments.length?!!a:!p.isOpen,angular.isFunction(s)&&s(p,p.isOpen),p.isOpen},this.isOpen=function(){return p.isOpen},p.getToggleElement=function(){return o.toggleElement},p.getAutoClose=function(){return c.autoClose||"always"},p.getElement=function(){return b},p.isKeynavEnabled=function(){return u},p.focusDropdownEntry=function(a){var c=o.dropdownMenu?angular.element(o.dropdownMenu).find("a"):b.find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(o.selectedOption)?o.selectedOption=o.selectedOption===c.length-1?o.selectedOption:o.selectedOption+1:o.selectedOption=0;break;case 38:angular.isNumber(o.selectedOption)?o.selectedOption=0===o.selectedOption?0:o.selectedOption-1:o.selectedOption=c.length-1}c[o.selectedOption].focus()},p.getDropdownElement=function(){return o.dropdownMenu},p.focusToggleElement=function(){o.toggleElement&&o.toggleElement[0].focus()},p.$watch("isOpen",function(e,n){var u=null,w=!1;if(angular.isDefined(c.dropdownAppendTo)){var x=d(c.dropdownAppendTo)(p);x&&(u=angular.element(x))}if(angular.isDefined(c.dropdownAppendToBody)){var y=d(c.dropdownAppendToBody)(p);y!==!1&&(w=!0)}if(w&&!u&&(u=v),u&&o.dropdownMenu&&(e?(u.append(o.dropdownMenu),b.on("$destroy",l)):(b.off("$destroy",l),l())),u&&o.dropdownMenu){var z,A,B,C=h.positionElements(b,o.dropdownMenu,"bottom-left",!0),D=0;if(z={top:C.top+"px",display:e?"block":"none"},A=o.dropdownMenu.hasClass("dropdown-menu-right"),A?(z.left="auto",B=h.scrollbarPadding(u),B.heightOverflow&&B.scrollbarWidth&&(D=B.scrollbarWidth),z.right=window.innerWidth-D-(C.left+b.prop("offsetWidth"))+"px"):(z.left=C.left+"px",z.right="auto"),!w){var E=h.offset(u);z.top=C.top-E.top+"px",A?z.right=window.innerWidth-(C.left-E.left+b.prop("offsetWidth"))+"px":z.left=C.left-E.left+"px"}o.dropdownMenu.css(z)}var F=u?u:b,G=u?q:r,H=F.hasClass(G),I=f.isOnlyOpen(a,u);if(H===!e){var J;J=u?I?"removeClass":"addClass":e?"addClass":"removeClass",g[J](F,G).then(function(){angular.isDefined(e)&&e!==n&&t(a,{open:!!e})})}if(e)o.dropdownMenuTemplateUrl?k(o.dropdownMenuTemplateUrl).then(function(a){m=p.$new(),j(a.trim())(m,function(a){var b=a;o.dropdownMenu.replaceWith(b),o.dropdownMenu=b,i.on("keydown",f.keybindFilter)})}):i.on("keydown",f.keybindFilter),p.focusToggleElement(),f.open(p,b,u);else{if(f.close(p,b,u),o.dropdownMenuTemplateUrl){m&&m.$destroy();var K=angular.element('<ul class="dropdown-menu"></ul>');o.dropdownMenu.replaceWith(K),o.dropdownMenu=K}o.selectedOption=null}angular.isFunction(s)&&s(a,e)})}]).directive("uibDropdown",function(){return{controller:"UibDropdownController",link:function(a,b,c,d){d.init()}}}).directive("uibDropdownMenu",function(){return{restrict:"A",require:"?^uibDropdown",link:function(a,b,c,d){if(d&&!angular.isDefined(c.dropdownNested)){b.addClass("dropdown-menu");var e=c.templateUrl;e&&(d.dropdownMenuTemplateUrl=e),d.dropdownMenu||(d.dropdownMenu=b)}}}}).directive("uibDropdownToggle",function(){return{require:"?^uibDropdown",link:function(a,b,c,d){if(d){b.addClass("dropdown-toggle"),d.toggleElement=b;var e=function(e){e.preventDefault(),b.hasClass("disabled")||c.disabled||a.$apply(function(){d.toggle()})};b.on("click",e),b.attr({"aria-haspopup":!0,"aria-expanded":!1}),a.$watch(d.isOpen,function(a){b.attr("aria-expanded",!!a)}),a.$on("$destroy",function(){b.off("click",e)})}}}}),angular.module("ui.bootstrap.stackedMap",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c<a.length;c++)if(b===a[c].key)return a[c]},keys:function(){for(var b=[],c=0;c<a.length;c++)b.push(a[c].key);return b},top:function(){return a[a.length-1]},remove:function(b){for(var c=-1,d=0;d<a.length;d++)if(b===a[d].key){c=d;break}return a.splice(c,1)[0]},removeTop:function(){return a.pop()},length:function(){return a.length}}}}}),angular.module("ui.bootstrap.modal",["ui.bootstrap.multiMap","ui.bootstrap.stackedMap","ui.bootstrap.position"]).provider("$uibResolve",function(){var a=this;this.resolver=null,this.setResolver=function(a){this.resolver=a},this.$get=["$injector","$q",function(b,c){var d=a.resolver?b.get(a.resolver):null;return{resolve:function(a,e,f,g){if(d)return d.resolve(a,e,f,g);var h=[];return angular.forEach(a,function(a){angular.isFunction(a)||angular.isArray(a)?h.push(c.resolve(b.invoke(a))):angular.isString(a)?h.push(c.resolve(b.get(a))):h.push(c.resolve(a))}),c.all(h).then(function(b){var c={},d=0;return angular.forEach(a,function(a,e){c[e]=b[d++]}),c})}}}]}).directive("uibModalBackdrop",["$animate","$injector","$uibModalStack",function(a,b,c){function d(b,d,e){e.modalInClass&&(a.addClass(d,e.modalInClass),b.$on(c.NOW_CLOSING_EVENT,function(c,f){var g=f();b.modalOptions.animation?a.removeClass(d,e.modalInClass).then(g):g()}))}return{restrict:"A",compile:function(a,b){return a.addClass(b.backdropClass),d}}}]).directive("uibModalWindow",["$uibModalStack","$q","$animateCss","$document",function(a,b,c,d){return{scope:{index:"@"},restrict:"A",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/modal/window.html"},link:function(e,f,g){f.addClass(g.windowTopClass||""),e.size=g.size,e.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!==c.value.backdrop&&b.target===b.currentTarget&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))},f.on("click",e.close),e.$isRendered=!0;var h=b.defer();e.$$postDigest(function(){h.resolve()}),h.promise.then(function(){var h=null;g.modalInClass&&(h=c(f,{addClass:g.modalInClass}).start(),e.$on(a.NOW_CLOSING_EVENT,function(a,b){var d=b();c(f,{removeClass:g.modalInClass}).start().then(d)})),b.when(h).then(function(){var b=a.getTop();if(b&&a.modalRendered(b.key),!d[0].activeElement||!f[0].contains(d[0].activeElement)){var c=f[0].querySelector("[autofocus]");c?c.focus():f[0].focus()}})})}}}]).directive("uibModalAnimationClass",function(){return{compile:function(a,b){b.modalAnimation&&a.addClass(b.uibModalAnimationClass)}}}).directive("uibModalTransclude",["$animate",function(a){return{link:function(b,c,d,e,f){f(b.$parent,function(b){c.empty(),a.enter(b,c)})}}}]).factory("$uibModalStack",["$animate","$animateCss","$document","$compile","$rootScope","$q","$$multiMap","$$stackedMap","$uibPosition",function(a,b,c,d,e,f,g,h,i){function j(a){var b="-";return a.replace(E,function(a,c){return(c?b:"")+a.toLowerCase()})}function k(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)}function l(){for(var a=-1,b=x.keys(),c=0;c<b.length;c++)x.get(b[c]).value.backdrop&&(a=c);return a>-1&&A>a&&(a=A),a}function m(a,b){var c=x.get(a).value,d=c.appendTo;x.remove(a),B=x.top(),B&&(A=parseInt(B.value.modalDomEl.attr("index"),10)),p(c.modalDomEl,c.modalScope,function(){var b=c.openedClass||w;y.remove(b,a);var e=y.hasKey(b);d.toggleClass(b,e),!e&&v&&v.heightOverflow&&v.scrollbarWidth&&(v.originalRight?d.css({paddingRight:v.originalRight+"px"}):d.css({paddingRight:""}),v=null),n(!0)},c.closedDeferred),o(),b&&b.focus?b.focus():d.focus&&d.focus()}function n(a){var b;x.length()>0&&(b=x.top().value,b.modalDomEl.toggleClass(b.windowTopClass||"",a))}function o(){if(t&&-1===l()){var a=u;p(t,u,function(){a=null}),t=void 0,u=void 0}}function p(b,c,d,e){function g(){g.done||(g.done=!0,a.leave(b).then(function(){d&&d(),b.remove(),e&&e.resolve()}),c.$destroy())}var h,i=null,j=function(){return h||(h=f.defer(),i=h.promise),function(){h.resolve()}};return c.$broadcast(z.NOW_CLOSING_EVENT,j),f.when(i).then(g)}function q(a){if(a.isDefaultPrevented())return a;var b=x.top();if(b)switch(a.which){case 27:b.value.keyboard&&(a.preventDefault(),e.$apply(function(){z.dismiss(b.key,"escape key press")}));break;case 9:var c=z.loadFocusElementList(b),d=!1;a.shiftKey?(z.isFocusInFirstItem(a,c)||z.isModalFocused(a,b))&&(d=z.focusLastFocusableElement(c)):z.isFocusInLastItem(a,c)&&(d=z.focusFirstFocusableElement(c)),d&&(a.preventDefault(),a.stopPropagation())}}function r(a,b,c){return!a.value.modalScope.$broadcast("modal.closing",b,c).defaultPrevented}function s(){Array.prototype.forEach.call(document.querySelectorAll("["+C+"]"),function(a){var b=parseInt(a.getAttribute(C),10),c=b-1;a.setAttribute(C,c),c||(a.removeAttribute(C),a.removeAttribute("aria-hidden"))})}var t,u,v,w="modal-open",x=h.createNew(),y=g.createNew(),z={NOW_CLOSING_EVENT:"modal.stack.now-closing"},A=0,B=null,C="data-bootstrap-modal-aria-hidden-count",D="a[href], area[href], input
6980:not([disabled]):not([tabindex='-1']), button:not([disabled]):not([tabindex='-1']),select:not([disabled]):not([tabindex='-1']), textarea:not([disabled]):not([tabindex='-1']), iframe, object, embed, *[tabindex]:not([tabindex='-1']), *[contenteditable=true]",E=/[A-Z]/g;return e.$watch(l,function(a){u&&(u.index=a)}),c.on("keydown",q),e.$on("$destroy",function(){c.off("keydown",q)}),z.open=function(b,f){function g(a){function b(a){var b=a.parent()?a.parent().children():[];return Array.prototype.filter.call(b,function(b){return b!==a[0]})}if(a&&"BODY"!==a[0].tagName)return b(a).forEach(function(a){var b="true"===a.getAttribute("aria-hidden"),c=parseInt(a.getAttribute(C),10);c||(c=b?1:0),a.setAttribute(C,c+1),a.setAttribute("aria-hidden","true")}),g(a.parent())}var h=c[0].activeElement,k=f.openedClass||w;n(!1),B=x.top(),x.add(b,{deferred:f.deferred,renderDeferred:f.renderDeferred,closedDeferred:f.closedDeferred,modalScope:f.scope,backdrop:f.backdrop,keyboard:f.keyboard,openedClass:f.openedClass,windowTopClass:f.windowTopClass,animation:f.animation,appendTo:f.appendTo}),y.put(k,b);var m=f.appendTo,o=l();o>=0&&!t&&(u=e.$new(!0),u.modalOptions=f,u.index=o,t=angular.element('<div uib-modal-backdrop="modal-backdrop"></div>'),t.attr({"class":"modal-backdrop","ng-style":"{'z-index': 1040 + (index && 1 || 0) + index*10}","uib-modal-animation-class":"fade","modal-in-class":"in"}),f.backdropClass&&t.addClass(f.backdropClass),f.animation&&t.attr("modal-animation","true"),d(t)(u),a.enter(t,m),i.isScrollable(m)&&(v=i.scrollbarPadding(m),v.heightOverflow&&v.scrollbarWidth&&m.css({paddingRight:v.right+"px"})));var p;f.component?(p=document.createElement(j(f.component.name)),p=angular.element(p),p.attr({resolve:"$resolve","modal-instance":"$uibModalInstance",close:"$close($value)",dismiss:"$dismiss($value)"})):p=f.content,A=B?parseInt(B.value.modalDomEl.attr("index"),10)+1:0;var q=angular.element('<div uib-modal-window="modal-window"></div>');q.attr({"class":"modal","template-url":f.windowTemplateUrl,"window-top-class":f.windowTopClass,role:"dialog","aria-labelledby":f.ariaLabelledBy,"aria-describedby":f.ariaDescribedBy,size:f.size,index:A,animate:"animate","ng-style":"{'z-index': 1050 + $$topModalIndex*10, display: 'block'}",tabindex:-1,"uib-modal-animation-class":"fade","modal-in-class":"in"}).append(p),f.windowClass&&q.addClass(f.windowClass),f.animation&&q.attr("modal-animation","true"),m.addClass(k),f.scope&&(f.scope.$$topModalIndex=A),a.enter(d(q)(f.scope),m),x.top().value.modalDomEl=q,x.top().value.modalOpener=h,g(q)},z.close=function(a,b){var c=x.get(a);return s(),c&&r(c,b,!0)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.resolve(b),m(a,c.value.modalOpener),!0):!c},z.dismiss=function(a,b){var c=x.get(a);return s(),c&&r(c,b,!1)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.reject(b),m(a,c.value.modalOpener),!0):!c},z.dismissAll=function(a){for(var b=this.getTop();b&&this.dismiss(b.key,a);)b=this.getTop()},z.getTop=function(){return x.top()},z.modalRendered=function(a){var b=x.get(a);b&&b.value.renderDeferred.resolve()},z.focusFirstFocusableElement=function(a){return a.length>0?(a[0].focus(),!0):!1},z.focusLastFocusableElement=function(a){return a.length>0?(a[a.length-1].focus(),!0):!1},z.isModalFocused=function(a,b){if(a&&b){var c=b.value.modalDomEl;if(c&&c.length)return(a.target||a.srcElement)===c[0]}return!1},z.isFocusInFirstItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[0]:!1},z.isFocusInLastItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[b.length-1]:!1},z.loadFocusElementList=function(a){if(a){var b=a.value.modalDomEl;if(b&&b.length){var c=b[0].querySelectorAll(D);return c?Array.prototype.filter.call(c,function(a){return k(a)}):c}}},z}]).provider("$uibModal",function(){var a={options:{animation:!0,backdrop:!0,keyboard:!0},$get:["$rootScope","$q","$document","$templateRequest","$controller","$uibResolve","$uibModalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?c.when(a.template):e(angular.isFunction(a.templateUrl)?a.templateUrl():a.templateUrl)}var j={},k=null;return j.getPromiseChain=function(){return k},j.open=function(e){function j(){return q}var l=c.defer(),m=c.defer(),n=c.defer(),o=c.defer(),p={result:l.promise,opened:m.promise,closed:n.promise,rendered:o.promise,close:function(a){return h.close(p,a)}
vendor: 9,408 bytes, lines 6980-6981
6980,dismiss:function(a){return h.dismiss(p,a)}};if(e=angular.extend({},a.options,e),e.resolve=e.resolve||{},e.appendTo=e.appendTo||d.find("body").eq(0),!e.appendTo.length)throw new Error("appendTo element not found. Make sure that the element passed is in DOM.");if(!e.component&&!e.template&&!e.templateUrl)throw new Error("One of component or template or templateUrl options is required.");var q;q=e.component?c.when(g.resolve(e.resolve,{},null,null)):c.all([i(e),g.resolve(e.resolve,{},null,null)]);var r;return r=k=c.all([k]).then(j,j).then(function(a){function c(b,c,d,e){b.$scope=g,b.$scope.$resolve={},d?b.$scope.$uibModalInstance=p:b.$uibModalInstance=p;var f=c?a[1]:a;angular.forEach(f,function(a,c){e&&(b[c]=a),b.$scope.$resolve[c]=a})}var d=e.scope||b,g=d.$new();g.$close=p.close,g.$dismiss=p.dismiss,g.$on("$destroy",function(){g.$$uibDestructionScheduled||g.$dismiss("$uibUnscheduledDestruction")});var i,j,k={scope:g,deferred:l,renderDeferred:o,closedDeferred:n,animation:e.animation,backdrop:e.backdrop,keyboard:e.keyboard,backdropClass:e.backdropClass,windowTopClass:e.windowTopClass,windowClass:e.windowClass,windowTemplateUrl:e.windowTemplateUrl,ariaLabelledBy:e.ariaLabelledBy,ariaDescribedBy:e.ariaDescribedBy,size:e.size,openedClass:e.openedClass,appendTo:e.appendTo},q={},r={};e.component?(c(q,!1,!0,!1),q.name=e.component,k.component=q):e.controller&&(c(r,!0,!1,!0),j=f(e.controller,r,!0,e.controllerAs),e.controllerAs&&e.bindToController&&(i=j.instance,i.$close=g.$close,i.$dismiss=g.$dismiss,angular.extend(i,{$resolve:r.$scope.$resolve},d)),i=j(),angular.isFunction(i.$onInit)&&i.$onInit()),e.component||(k.content=a[0]),h.open(p,k),m.resolve(!0)},function(a){m.reject(a),l.reject(a)})["finally"](function(){k===r&&(k=null)}),p},j}]};return a}),angular.module("ui.bootstrap.paging",[]).factory("uibPaging",["$parse",function(a){return{create:function(b,c,d){b.setNumPages=d.numPages?a(d.numPages).assign:angular.noop,b.ngModelCtrl={$setViewValue:angular.noop},b._watchers=[],b.init=function(a,e){b.ngModelCtrl=a,b.config=e,a.$render=function(){b.render()},d.itemsPerPage?b._watchers.push(c.$parent.$watch(d.itemsPerPage,function(a){ 6981b.itemsPerPage=parseInt(a,10),c.totalPages=b.calculateTotalPages(),b.updatePage()})):b.itemsPerPage=e.itemsPerPage,c.$watch("totalItems",function(a,d){(angular.isDefined(a)||a!==d)&&(c.totalPages=b.calculateTotalPages(),b.updatePage())})},b.calculateTotalPages=function(){var a=b.itemsPerPage<1?1:Math.ceil(c.totalItems/b.itemsPerPage);return Math.max(a||0,1)},b.render=function(){c.page=parseInt(b.ngModelCtrl.$viewValue,10)||1},c.selectPage=function(a,d){d&&d.preventDefault();var e=!c.ngDisabled||!d;e&&c.page!==a&&a>0&&a<=c.totalPages&&(d&&d.target&&d.target.blur(),b.ngModelCtrl.$setViewValue(a),b.ngModelCtrl.$render())},c.getText=function(a){return c[a+"Text"]||b.config[a+"Text"]},c.noPrevious=function(){return 1===c.page},c.noNext=function(){return c.page===c.totalPages},b.updatePage=function(){b.setNumPages(c.$parent,c.totalPages),c.page>c.totalPages?c.selectPage(c.totalPages):b.ngModelCtrl.$render()},c.$on("$destroy",function(){for(;b._watchers.length;)b._watchers.shift()()})}}}]),angular.module("ui.bootstrap.pager",["ui.bootstrap.paging","ui.bootstrap.tabindex"]).controller("UibPagerController",["$scope","$attrs","uibPaging","uibPagerConfig",function(a,b,c,d){a.align=angular.isDefined(b.align)?a.$parent.$eval(b.align):d.align,c.create(this,a,b)}]).constant("uibPagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("uibPager",["uibPagerConfig",function(a){return{scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["uibPager","?ngModel"],restrict:"A",controller:"UibPagerController",controllerAs:"pager",templateUrl:function(a,b){return b.templateUrl||"uib/template/pager/pager.html"},link:function(b,c,d,e){c.addClass("pager");var f=e[0],g=e[1];g&&f.init(g,a)}}}]),angular.module("ui.bootstrap.pagination",["ui.bootstrap.paging","ui.bootstrap.tabindex"]).controller("UibPaginationController",["$scope","$attrs","$parse","uibPaging","uibPaginationConfig",function(a,b,c,d,e){function f(a,b,c){return{number:a,text:b,active:c}}function g(a,b){var c=[],d=1,e=b,g=angular.isDefined(i)&&b>i;g&&(j?(d=Math.max(a-Math.floor(i/2),1),e=d+i-1,e>b&&(e=b,d=e-i+1)):(d=(Math.ceil(a/i)-1)*i+1,e=Math.min(d+i-1,b)));for(var h=d;e>=h;h++){var n=f(h,m(h),h===a);c.push(n)}if(g&&i>0&&(!j||k||l)){if(d>1){if(!l||d>3){var o=f(d-1,"...",!1);c.unshift(o)}if(l){if(3===d){var p=f(2,"2",!1);c.unshift(p)}var q=f(1,"1",!1);c.unshift(q)}}if(b>e){if(!l||b-2>e){var r=f(e+1,"...",!1);c.push(r)}if(l){if(e===b-2){var s=f(b-1,b-1,!1);c.push(s)}var t=f(b,b,!1);c.push(t)}}}return c}var h=this,i=angular.isDefined(b.maxSize)?a.$parent.$eval(b.maxSize):e.maxSize,j=angular.isDefined(b.rotate)?a.$parent.$eval(b.rotate):e.rotate,k=angular.isDefined(b.forceEllipses)?a.$parent.$eval(b.forceEllipses):e.forceEllipses,l=angular.isDefined(b.boundaryLinkNumbers)?a.$parent.$eval(b.boundaryLinkNumbers):e.boundaryLinkNumbers,m=angular.isDefined(b.pageLabel)?function(c){return a.$parent.$eval(b.pageLabel,{$page:c})}:angular.identity;a.boundaryLinks=angular.isDefined(b.boundaryLinks)?a.$parent.$eval(b.boundaryLinks):e.boundaryLinks,a.directionLinks=angular.isDefined(b.directionLinks)?a.$parent.$eval(b.directionLinks):e.directionLinks,b.$set("role","menu"),d.create(this,a,b),b.maxSize&&h._watchers.push(a.$parent.$watch(c(b.maxSize),function(a){i=parseInt(a,10),h.render()}));var n=this.render;this.render=function(){n(),a.page>0&&a.page<=a.totalPages&&(a.pages=g(a.page,a.totalPages))}}]).constant("uibPaginationConfig",{itemsPerPage:10,boundaryLinks:!1,boundaryLinkNumbers:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0,forceEllipses:!1}).directive("uibPagination",["$parse","uibPaginationConfig",function(a,b){return{scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["uibPagination","?ngModel"],restrict:"A",controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"uib/template/pagination/pagination.html"},link:function(a,c,d,e){c.addClass("pagination");var f=e[0],g=e[1];g&&f.init(g,b)}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.stackedMap"]).provider("$uibTooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",placementClassPrefix:"",animation:!0,popupDelay:0,popupCloseDelay:0,useContentExp:!1},c={mouseenter:"mouseleave",click:"click",outsideClick:"outsideClick",focus:"blur",none:""},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$document","$uibPosition","$interpolate","$rootScope","$parse","$$stackedMap",function(e,f,g,h,i,j,k,l,m){function n(a){if(27===a.which){var b=o.top();b&&(b.value.close(),b=null)}}var o=m.createNew();return h.on("keyup",n),k.$on("$destroy",function(){h.off("keyup",n)}),function(e,k,m,n){function p(a){var b=(a||n.trigger||m).split(" "),d=b.map(function(a){return c[a]||a});return{show:b,hide:d}}n=angular.extend({},b,d,n);var q=a(e),r=j.startSymbol(),s=j.endSymbol(),t="<div "+q+'-popup uib-title="'+r+"title"+s+'" '+(n.useContentExp?'content-exp="contentExp()" ':'content="'+r+"content"+s+'" ')+'origin-scope="origScope" class="uib-position-measure '+k+'" tooltip-animation-class="fade"uib-tooltip-classes ng-class="{ in: isOpen }" ></div>';return{compile:function(a,b){var c=f(t);return function(a,b,d,f){function j(){P.isOpen?q():m()}function m(){O&&!a.$eval(d[k+"Enable"])||(u(),x(),P.popupDelay?H||(H=g(r,P.popupDelay,!1)):r())}function q(){s(),P.popupCloseDelay?I||(I=g(t,P.popupCloseDelay,!1)):t()}function r(){return s(),u(),P.content?(v(),void P.$evalAsync(function(){P.isOpen=!0,y(!0),U()})):angular.noop}function s(){H&&(g.cancel(H),H=null),J&&(g.cancel(J),J=null)}function t(){P&&P.$evalAsync(function(){P&&(P.isOpen=!1,y(!1),P.animation?G||(G=g(w,150,!1)):w())})}function u(){I&&(g.cancel(I),I=null),G&&(g.cancel(G),G=null)}function v(){E||(F=P.$new(),E=c(F,function(a){M?h.find("body").append(a):b.after(a)}),o.add(P,{close:t}),z())}function w(){s(),u(),A(),E&&(E.remove(),E=null,K&&g.cancel(K)),o.remove(P),F&&(F.$destroy(),F=null)}function x(){P.title=d[k+"Title"],S?P.content=S(a):P.content=d[e],P.popupClass=d[k+"Class"],P.placement=angular.isDefined(d[k+"Placement"])?d[k+"Placement"]:n.placement;var b=i.parsePlacement(P.placement);L=b[1]?b[0]+"-"+b[1]:b[0];var c=parseInt(d[k+"PopupDelay"],10),f=parseInt(d[k+"PopupCloseDelay"],10);P.popupDelay=isNaN(c)?n.popupDelay:c,P.popupCloseDelay=isNaN(f)?n.popupCloseDelay:f}function y(b){R&&angular.isFunction(R.assign)&&R.assign(a,b)}function z(){T.length=0,S?(T.push(a.$watch(S,function(a){P.content=a,!a&&P.isOpen&&t()})),T.push(F.$watch(function(){Q||(Q=!0,F.$$postDigest(function(){Q=!1,P&&P.isOpen&&U()}))}))):T.push(d.$observe(e,function(a){P.content=a,!a&&P.isOpen?t():U()})),T.push(d.$observe(k+"Title",function(a){P.title=a,P.isOpen&&U()})),T.push(d.$observe(k+"Placement",function(a){P.placement=a?a:n.placement,P.isOpen&&U()}))}function A(){T.length&&(angular.forEach(T,function(a){a()}),T.length=0)}function B(a){P&&P.isOpen&&E&&(b[0].contains(a.target)||E[0].contains(a.target)||q())}
vendor: 5,491 bytes, line 6981
6981function C(a){27===a.which&&q()}function D(){var c=[],e=[],f=a.$eval(d[k+"Trigger"]);V(),angular.isObject(f)?(Object.keys(f).forEach(function(a){c.push(a),e.push(f[a])}),N={show:c,hide:e}):N=p(f),"none"!==N.show&&N.show.forEach(function(a,c){"outsideClick"===a?(b.on("click",j),h.on("click",B)):a===N.hide[c]?b.on(a,j):a&&(b.on(a,m),b.on(N.hide[c],q)),b.on("keypress",C)})}var E,F,G,H,I,J,K,L,M=angular.isDefined(n.appendToBody)?n.appendToBody:!1,N=p(void 0),O=angular.isDefined(d[k+"Enable"]),P=a.$new(!0),Q=!1,R=angular.isDefined(d[k+"IsOpen"])?l(d[k+"IsOpen"]):!1,S=n.useContentExp?l(d[e]):!1,T=[],U=function(){E&&E.html()&&(J||(J=g(function(){var a=i.positionElements(b,E,P.placement,M),c=angular.isDefined(E.offsetHeight)?E.offsetHeight:E.prop("offsetHeight"),d=M?i.offset(b):i.position(b);E.css({top:a.top+"px",left:a.left+"px"});var e=a.placement.split("-");E.hasClass(e[0])||(E.removeClass(L.split("-")[0]),E.addClass(e[0])),E.hasClass(n.placementClassPrefix+a.placement)||(E.removeClass(n.placementClassPrefix+L),E.addClass(n.placementClassPrefix+a.placement)),K=g(function(){var a=angular.isDefined(E.offsetHeight)?E.offsetHeight:E.prop("offsetHeight"),b=i.adjustTop(e,d,c,a);b&&E.css(b),K=null},0,!1),E.hasClass("uib-position-measure")?(i.positionArrow(E,a.placement),E.removeClass("uib-position-measure")):L!==a.placement&&i.positionArrow(E,a.placement),L=a.placement,J=null},0,!1)))};P.origScope=a,P.isOpen=!1,P.contentExp=function(){return P.content},d.$observe("disabled",function(a){a&&s(),a&&P.isOpen&&t()}),R&&a.$watch(R,function(a){P&&!a===P.isOpen&&j()});var V=function(){N.show.forEach(function(a){"outsideClick"===a?b.off("click",j):(b.off(a,m),b.off(a,j)),b.off("keypress",C)}),N.hide.forEach(function(a){"outsideClick"===a?h.off("click",B):b.off(a,q)})};D();var W=a.$eval(d[k+"Animation"]);P.animation=angular.isDefined(W)?!!W:n.animation;var X,Y=k+"AppendToBody";X=Y in d&&void 0===d[Y]?!0:a.$eval(d[Y]),M=angular.isDefined(X)?X:M,a.$on("$destroy",function(){V(),w(),P=null})}}}}}]}).directive("uibTooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest",function(a,b,c,d){return{link:function(e,f,g){var h,i,j,k=e.$eval(g.tooltipTemplateTranscludeScope),l=0,m=function(){i&&(i.remove(),i=null),h&&(h.$destroy(),h=null),j&&(a.leave(j).then(function(){i=null}),i=j,j=null)};e.$watch(b.parseAsResourceUrl(g.uibTooltipTemplateTransclude),function(b){var g=++l;b?(d(b,!0).then(function(d){if(g===l){var e=k.$new(),i=d,n=c(i)(e,function(b){m(),a.enter(b,f)});h=e,j=n,h.$emit("$includeContentLoaded",b)}},function(){g===l&&(m(),e.$emit("$includeContentError",b))}),e.$emit("$includeContentRequested",b)):m()}),e.$on("$destroy",m)}}}]).directive("uibTooltipClasses",["$uibPosition",function(a){return{restrict:"A",link:function(b,c,d){if(b.placement){var e=a.parsePlacement(b.placement);c.addClass(e[0])}b.popupClass&&c.addClass(b.popupClass),b.animation&&c.addClass(d.tooltipAnimationClass)}}}]).directive("uibTooltipPopup",function(){return{restrict:"A",scope:{content:"@"},templateUrl:"uib/template/tooltip/tooltip-popup.html"}}).directive("uibTooltip",["$uibTooltip",function(a){return a("uibTooltip","tooltip","mouseenter")}]).directive("uibTooltipTemplatePopup",function(){return{restrict:"A",scope:{contentExp:"&",originScope:"&"},templateUrl:"uib/template/tooltip/tooltip-template-popup.html"}}).directive("uibTooltipTemplate",["$uibTooltip",function(a){return a("uibTooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("uibTooltipHtmlPopup",function(){return{restrict:"A",scope:{contentExp:"&"},templateUrl:"uib/template/tooltip/tooltip-html-popup.html"}}).directive("uibTooltipHtml",["$uibTooltip",function(a){return a("uibTooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("uibPopoverTemplatePopup",function(){return{restrict:"A",scope:{uibTitle:"@",contentExp:"&",originScope:"&"},templateUrl:"uib/template/popover/popover-template.html"}}).directive("uibPopoverTemplate",["$uibTooltip",function(a){return a("uibPopoverTemplate","popover","click",{useContentExp:!0})}]).directive("uibPopoverHtmlPopup",function(){return{restrict:"A",scope:{contentExp:"&",uibTitle:"@"},templateUrl:"uib/template/popover/popover-html.html"}}).directive("uibPopoverHtml",["$uibTooltip",function(a){return a("uibPopoverHtml","popover","click",{useContentExp:!0})}]).directive("uibPopoverPopup",function(){return{restrict:"A",scope:{uibTitle:"@",content:"@"},templateUrl:"uib/template/popover/popover.html"}}).directive("uibPopover",["$uibTooltip",function(a){return a("uibPopover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("uibProgressConfig",{animate:!0,max:100}).controller("UibProgressController",["$scope","$attrs","uibProgressConfig",function(a,b,c){function d(){return angular.isDefined(a.maxParam)?a.maxParam:c.max}var e=this,f=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=d(),this.addBar=function(a,b,c){f||b.css({transition:"none"}),this.bars.push(a),a.max=d(),a.title=c&&angular.isDefined(c.title)?c.title:"progressbar",a.$watch("value",function(b){a.recalculatePercentage()}),a.recalculatePercentage=function(){var b=e.bars.reduce(function(a,b){return b.percent=+(100*b.value/b.max).toFixed(2),a+b.percent},0);b>100&&(a.percent-=b-100)},a.$on("$destroy",function(){b=null,e.removeBar(a)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1),this.bars.forEac
vendor: 17,149 bytes, line 6981
6981h(function(a){a.recalculatePercentage()})},a.$watch("maxParam",function(a){e.bars.forEach(function(a){a.max=d(),a.recalculatePercentage()})})}]).directive("uibProgress",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",require:"uibProgress",scope:{maxParam:"=?max"},templateUrl:"uib/template/progressbar/progress.html"}}).directive("uibBar",function(){return{replace:!0,transclude:!0,require:"^uibProgress",scope:{value:"=",type:"@"},templateUrl:"uib/template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b,c)}}}).directive("uibProgressbar",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",scope:{value:"=",maxParam:"=?max",type:"@"},templateUrl:"uib/template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]),{title:c.title})}}}),angular.module("ui.bootstrap.rating",[]).constant("uibRatingConfig",{max:5,stateOn:null,stateOff:null,enableReset:!0,titles:["one","two","three","four","five"]}).controller("UibRatingController",["$scope","$attrs","uibRatingConfig",function(a,b,c){var d={$setViewValue:angular.noop},e=this;this.init=function(e){d=e,d.$render=this.render,d.$formatters.push(function(a){return angular.isNumber(a)&&a<<0!==a&&(a=Math.round(a)),a}),this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):c.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):c.stateOff,this.enableReset=angular.isDefined(b.enableReset)?a.$parent.$eval(b.enableReset):c.enableReset;var f=angular.isDefined(b.titles)?a.$parent.$eval(b.titles):c.titles;this.titles=angular.isArray(f)&&f.length>0?f:c.titles;var g=angular.isDefined(b.ratingStates)?a.$parent.$eval(b.ratingStates):new Array(angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max);a.range=this.buildTemplateObjects(g)},this.buildTemplateObjects=function(a){for(var b=0,c=a.length;c>b;b++)a[b]=angular.extend({index:b},{stateOn:this.stateOn,stateOff:this.stateOff,title:this.getTitle(b)},a[b]);return a},this.getTitle=function(a){return a>=this.titles.length?a+1:this.titles[a]},a.rate=function(b){if(!a.readonly&&b>=0&&b<=a.range.length){var c=e.enableReset&&d.$viewValue===b?0:b;d.$setViewValue(c),d.$render()}},a.enter=function(b){a.readonly||(a.value=b),a.onHover({value:b})},a.reset=function(){a.value=d.$viewValue,a.onLeave()},a.onKeydown=function(b){/(37|38|39|40)/.test(b.which)&&(b.preventDefault(),b.stopPropagation(),a.rate(a.value+(38===b.which||39===b.which?1:-1)))},this.render=function(){a.value=d.$viewValue,a.title=e.getTitle(a.value-1)}}]).directive("uibRating",function(){return{require:["uibRating","ngModel"],restrict:"A",scope:{readonly:"=?readOnly",onHover:"&",onLeave:"&"},controller:"UibRatingController",templateUrl:"uib/template/rating/rating.html",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}),angular.module("ui.bootstrap.tabs",[]).controller("UibTabsetController",["$scope",function(a){function b(a){for(var b=0;b<d.tabs.length;b++)if(d.tabs[b].index===a)return b}var c,d=this;d.tabs=[],d.select=function(a,f){if(!e){var g=b(c),h=d.tabs[g];if(h){if(h.tab.onDeselect({$event:f,$selectedIndex:a}),f&&f.isDefaultPrevented())return;h.tab.active=!1}var i=d.tabs[a];i?(i.tab.onSelect({$event:f}),i.tab.active=!0,d.active=i.index,c=i.index):!i&&angular.isDefined(c)&&(d.active=null,c=null)}},d.addTab=function(a){if(d.tabs.push({tab:a,index:a.index}),d.tabs.sort(function(a,b){return a.index>b.index?1:a.index<b.index?-1:0}),a.index===d.active||!angular.isDefined(d.active)&&1===d.tabs.length){var c=b(a.index);d.select(c)}},d.removeTab=function(a){for(var b,c=0;c<d.tabs.length;c++)if(d.tabs[c].tab===a){b=c;break}if(d.tabs[b].index===d.active){var e=b===d.tabs.length-1?b-1:b+1%d.tabs.length;d.select(e)}d.tabs.splice(b,1)},a.$watch("tabset.active",function(a){angular.isDefined(a)&&a!==c&&d.select(b(a))});var e;a.$on("$destroy",function(){e=!0})}]).directive("uibTabset",function(){return{transclude:!0,replace:!0,scope:{},bindToController:{active:"=?",type:"@"},controller:"UibTabsetController",controllerAs:"tabset",templateUrl:function(a,b){return b.templateUrl||"uib/template/tabs/tabset.html"},link:function(a,b,c){a.vertical=angular.isDefined(c.vertical)?a.$parent.$eval(c.vertical):!1,a.justified=angular.isDefined(c.justified)?a.$parent.$eval(c.justified):!1}}}).directive("uibTab",["$parse",function(a){return{require:"^uibTabset",replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/tabs/tab.html"},transclude:!0,scope:{heading:"@",index:"=?",classes:"@?",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},controllerAs:"tab",link:function(b,c,d,e,f){b.disabled=!1,d.disable&&b.$parent.$watch(a(d.disable),function(a){b.disabled=!!a}),angular.isUndefined(d.index)&&(e.tabs&&e.tabs.length?b.index=Math.max.apply(null,e.tabs.map(function(a){return a.index}))+1:b.index=0),angular.isUndefined(d.classes)&&(b.classes=""),b.select=function(a){if(!b.disabled){for(var c,d=0;d<e.tabs.length;d++)if(e.tabs[d].tab===b){c=d;break}e.select(c,a)}},e.addTab(b),b.$on("$destroy",function(){e.removeTab(b)}),b.$transcludeFn=f}}}]).directive("uibTabHeadingTransclude",function(){return{restrict:"A",require:"^uibTab",link:function(a,b){a.$watch("headingElement",function(a){a&&(b.html(""),b.append(a))})}}}).directive("uibTabContentTransclude",function(){function a(a){return a.tagName&&(a.hasAttribute("uib-tab-heading")||a.hasAttribute("data-uib-tab-heading")||a.hasAttribute("x-uib-tab-heading")||"uib-tab-heading"===a.tagName.toLowerCase()||"data-uib-tab-heading"===a.tagName.toLowerCase()||"x-uib-tab-heading"===a.tagName.toLowerCase()||"uib:tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:"^uibTabset",link:function(b,c,d){var e=b.$eval(d.uibTabContentTransclude).tab;e.$transcludeFn(e.$parent,function(b){angular.forEach(b,function(b){a(b)?e.headingElement=b:c.append(b)})})}}}),angular.module("ui.bootstrap.timepicker",[]).constant("uibTimepickerConfig",{hourStep:1,minuteStep:1,secondStep:1,showMeridian:!0,showSeconds:!1,meridians:null,readonlyInput:!1,mousewheel:!0,arrowkeys:!0,showSpinners:!0,templateUrl:"uib/template/timepicker/timepicker.html"}).controller("UibTimepickerController",["$scope","$element","$attrs","$parse","$log","$locale","uibTimepickerConfig",function(a,b,c,d,e,f,g){function h(){var b=+a.hours,c=a.showMeridian?b>0&&13>b:b>=0&&24>b;return c&&""!==a.hours?(a.showMeridian&&(12===b&&(b=0),a.meridian===y[1]&&(b+=12)),b):void 0}function i(){var b=+a.minutes,c=b>=0&&60>b;return c&&""!==a.minutes?b:void 0}function j(){var b=+a.seconds;return b>=0&&60>b?b:void 0}function k(a,b){return null===a?"":angular.isDefined(a)&&a.toString().length<2&&!b?"0"+a:a.toString()}function l(a){m(),x.$setViewValue(new Date(v)),n(a)}function m(){s&&s.$setValidity("hours",!0),t&&t.$setValidity("minutes",!0),u&&u.$setValidity("seconds",!0),x.$setValidity("time",!0),a.invalidHours=!1,a.invalidMinutes=!1,a.invalidSeconds=!1}function n(b){if(x.$modelValue){var c=v.getHours(),d=v.getMinutes(),e=v.getSeconds();a.showMeridian&&(c=0===c||12===c?12:c%12),a.hours="h"===b?c:k(c,!z),"m"!==b&&(a.minutes=k(d)),a.meridian=v.getHours()<12?y[0]:y[1],"s"!==b&&(a.seconds=k(e)),a.meridian=v.getHours()<12?y[0]:y[1]}else a.hours=null,a.minutes=null,a.seconds=null,a.meridian=y[0]}function o(a){v=q(v,a),l()}function p(a,b){return q(a,60*b)}function q(a,b){var c=new Date(a.getTime()+1e3*b),d=new Date(a);return d.setHours(c.getHours(),c.getMinutes(),c.getSeconds()),d}function r(){return(null===a.hours||""===a.hours)&&(null===a.minutes||""===a.minutes)&&(!a.showSeconds||a.showSeconds&&(null===a.seconds||""===a.seconds))}var s,t,u,v=new Date,w=[],x={$setViewValue:angular.noop},y=angular.isDefined(c.meridians)?a.$parent.$eval(c.meridians):g.meridians||f.DATETIME_FORMATS.AMPMS,z=angular.isDefined(c.padHours)?a.$parent.$eval(c.padHours):!0;a.tabindex=angular.isDefined(c.tabindex)?c.tabindex:0,b.removeAttr("tabindex"),this.init=function(b,d){x=b,x.$render=this.render,x.$formatters.unshift(function(a){return a?new Date(a):null});var e=d.eq(0),f=d.eq(1),h=d.eq(2);s=e.controller("ngModel"),t=f.controller("ngModel"),u=h.controller("ngModel");var i=angular.isDefined(c.mousewheel)?a.$parent.$eval(c.mousewheel):g.mousewheel;i&&this.setupMousewheelEvents(e,f,h);var j=angular.isDefined(c.arrowkeys)?a.$parent.$eval(c.arrowkeys):g.arrowkeys;j&&this.setupArrowkeyEvents(e,f,h),a.readonlyInput=angular.isDefined(c.readonlyInput)?a.$parent.$eval(c.readonlyInput):g.readonlyInput,this.setupInputEvents(e,f,h)};var A=g.hourStep;c.hourStep&&w.push(a.$parent.$watch(d(c.hourStep),function(a){A=+a}));var B=g.minuteStep;c.minuteStep&&w.push(a.$parent.$watch(d(c.minuteStep),function(a){B=+a}));var C;w.push(a.$parent.$watch(d(c.min),function(a){var b=new Date(a);C=isNaN(b)?void 0:b}));var D;w.push(a.$parent.$watch(d(c.max),function(a){var b=new Date(a);D=isNaN(b)?void 0:b}));var E=!1;c.ngDisabled&&w.push(a.$parent.$watch(d(c.ngDisabled),function(a){E=a})),a.noIncrementHours=function(){var a=p(v,60*A);return E||a>D||v>a&&C>a},a.noDecrementHours=function(){var a=p(v,60*-A);return E||C>a||a>v&&a>D},a.noIncrementMinutes=function(){var a=p(v,B);return E||a>D||v>a&&C>a},a.noDecrementMinutes=function(){var a=p(v,-B);return E||C>a||a>v&&a>D},a.noIncrementSeconds=function(){var a=q(v,F);return E||a>D||v>a&&C>a},a.noDecrementSeconds=function(){var a=q(v,-F);return E||C>a||a>v&&a>D},a.noToggleMeridian=function(){return v.getHours()<12?E||p(v,720)>D:E||p(v,-720)<C};var F=g.secondStep;c.secondStep&&w.push(a.$parent.$watch(d(c.secondStep),function(a){F=+a})),a.showSeconds=g.showSeconds,c.showSeconds&&w.push(a.$parent.$watch(d(c.showSeconds),function(b){a.showSeconds=!!b})),a.showMeridian=g.showMeridian,c.showMeridian&&w.push(a.$parent.$watch(d(c.showMeridian),function(b){if(a.showMeridian=!!b,x.$error.time){var c=h(),d=i();angular.isDefined(c)&&angular.isDefined(d)&&(v.setHours(c),l())}else n()})),this.setupMousewheelEvents=function(b,c,d){var e=function(a){a.originalEvent&&(a=a.originalEvent);var b=a.wheelDelta?a.wheelDelta:-a.deltaY;return a.detail||b>0};b.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementHours():a.decrementHours()),b.preventDefault()}),c.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementMinutes():a.decrementMinutes()),b.preventDefault()}),d.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementSeconds():a.decrementSeconds()),b.preventDefault()})},this.setupArrowkeyEvents=function(b,c,d){b.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementHours(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementHours(),a.$apply()))}),c.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementMinutes(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementMinutes(),a.$apply()))}),d.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementSeconds(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementSeconds(),a.$apply()))})},this.setupInputEvents=function(b,c,d){if(a.readonlyInput)return a.updateHours=angular.noop,a.updateMinutes=angular.noop,void(a.updateSeconds=angular.noop);var e=function(b,c,d){x.$setViewValue(null),x.$setValidity("time",!1),angular.isDefined(b)&&(a.invalidHours=b,s&&s.$setValidity("hours",!1)),angular.isDefined(c)&&(a.invalidMinutes=c,t&&t.$setValidity("minutes",!1)),angular.isDefined(d)&&(a.invalidSeconds=d,u&&u.$setValidity("seconds",!1))};a.updateHours=function(){var a=h(),b=i();x.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(v.setHours(a),v.setMinutes(b),C>v||v>D?e(!0):l("h")):e(!0)},b.on("blur",function(b){x.$setTouched(),r()?m():null===a.hours||""===a.hours?e(!0):!a.invalidHours&&a.hours<10&&a.$apply(function(){a.hours=k(a.hours,!z)})}),a.updateMinutes=function(){var a=i(),b=h();x.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(v.setHours(b),v.setMinutes(a),C>v||v>D?e(void 0,!0):l("m")):e(void 0,!0)},c.on("blur",function(b){x.$setTouched(),r()?m():null===a.minutes?e(void 0,!0):!a.invalidMinutes&&a.minutes<10&&a.$apply(function(){a.minutes=k(a.minutes)})}),a.updateSeconds=function(){var a=j();x.$setDirty(),angular.isDefined(a)?(v.setSeconds(a),l("s")):e(void 0,void 0,!0)},d.on("blur",function(b){r()?m():!a.invalidSeconds&&a.seconds<10&&a.$apply(function(){a.seconds=k(a.seconds)})})},this.render=function(){var b=x.$viewValue;isNaN(b)?(x.$setValidity("time",!1),e.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(b&&(v=b),C>v||v>D?(x.$setValidity("time",!1),a.invalidHours=!0,a.invalidMinutes=!0):m(),n())},a.showSpinners=angular.isDefined(c.showSpinners)?a.$parent.$eval(c.showSpinners):g.showSpinners,a.incrementHours=function(){a.noIncrementHours()||o(60*A*60)},a.decrementHours=function(){a.noDecrementHours()||o(60*-A*60)},a.incrementMinutes=function(){a.noIncrementMinutes()||o(60*B)},a.decrementMinutes=function(){a.noDecrementMinutes()||o(60*-B)},a.incrementSeconds=function(){a.noIncrementSeconds()||o(F)},a.decrementSeconds=function(){a.noDecrementSeconds()||o(-F)},a.toggleMeridian=function(){var b=i(),c=h();a.noToggleMeridian()||(angular.isDefined(b)&&angular.isDefined(c)?o(720*(v.getHours()<12?60:-60)):a.meridian=a.meridian===y[0]?y[1]:y[0])},a.blur=function(){x.$setTouched()},a.$on("$destroy",function(){for(;w.length;)w.shift()()})}]).directive("uibTimepicker",["uibTimepickerConfig",function(a){return{require:["uibTimepicker","?^ngModel"],restrict:"A",controller:"UibTimepickerController",controllerAs:"timepicker",scope:{},templateUrl:function(b,c){return c.templateUrl||a.templateUrl},link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f,b.find("input"))}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.debounce","ui.bootstrap.position"]).factory("uibTypeaheadParser",["$parse",function(a){var b=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+c+'".');return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).controller("UibTypeaheadController",["$scope","$element","$attrs","$compile","$parse","$q","$timeout","$document","$window","$rootScope","$$debounce","$uibPosition","uibTypeaheadParser",function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(){P.moveInProgress||(P.moveInProgress=!0,P.$digest()),$()}function o(){P.position=F?l.offset(b):l.position(b),P.position.top+=b.prop("offsetHeight")}function p(a){var b;return angular.version.minor<6?(b=a.$options||{},b.getOption=function(a){return b[a]}):b=a.$options,b}var q,r,s=[9,13,27,38,40],t=200,u=a.$eval(c.typeaheadMinLength);u||0===u||(u=1),a.$watch(c.typeaheadMinLength,function(a){u=a||0===a?a:1});var v=a.$eval(c.typeaheadWaitMs)||0,w=a.$eval(c.typeaheadEditable)!==!1;a.$watch(c.typeaheadEditable,function(a){w=a!==!1});var x,y,z=e(c.typeaheadLoading).assign||angular.noop,A=c.typeaheadShouldSelect?e(c.typeaheadShouldSelect):function(a,b){var c=b.$event;return 13===c.which||9===c.which},B=e(c.typeaheadOnSelect),C=angular.isDefined(c.typeaheadSelectOnBlur)?a.$eval(c.typeaheadSelectOnBlur):!1,D=e(c.typeaheadNoResults).assign||angular.noop,E=c.typeaheadInputFormatter?e(c.typeaheadInputFormatter):void 0,F=c.typeaheadAppendToBody?a.$eval(c.typeaheadAppendToBody):!1,G=c.typeaheadAppendTo?a.$eval(c.typeaheadAppendTo):null,H=a.$eval(c.typeaheadFocusFirst)!==!1,I=c.typeaheadSelectOnExact?a.$eval(c.typeaheadSelectOnExact):!1,J=e(c.typeaheadIsOpen).assign||angular.noop,K=a.$eval(c.typeaheadShowHint)||!1,L=e(c.ngModel),M=e(c.ngModel+"($$$p)"),N=function(b,c){return angular.isFunction(L(a))&&r.getOption("getterSetter")?M(b,{$$$p:c}):L.assign(b,c)},O=m.parse(c.uibTypeahead),P=a.$new(),Q=a.$on("$destroy",function(){P.$destroy()});P.$on("$destroy",Q);var R="typeahead-"+P.$id+"-"+Math.floor(1e4*Math.random());b.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":R});var S,T;K&&(S=angular.element("<div></div>"),S.css("position","relative"),b.after(S),T=b.clone(),T.attr("placeholder",""),T.attr("tabindex","-1"),T.val(""),T.css({position:"absolute",top:"0px",left:"0px","border-color":"transparent","box-shadow":"none",opacity:1,background:"none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)",color:"#999"}),b.css({position:"relative","vertical-align":"top","background-color":"transparent"}),T.attr("id")&&T.removeAttr("id"),S.append(T),T.after(b));var U=angular.element("<div uib-typeahead-popup></div>");U.attr({id:R,matches:"matches",active:"activeIdx",select:"select(activeIdx, evt)","move-in-progress":"moveInProgress",query:"query",position:"position","assign-is-open":"assignIsOpen(isOpen)",debounce:"debounceUpdate"}),angular.isDefined(c.typeaheadTemplateUrl)&&U.attr("template-url",c.typeaheadTemplateUrl),angular.isDefined(c.typeaheadPopupTemplateUrl)&&U.attr("popup-template-url",c.typeaheadPopupTemplateUrl);var V=function(){K&&T.val("")},W=function(){P.matches=[],P.activeIdx=-1,b.attr("aria-expanded",!1),V()},X=function(a){return R+"-option-"+a};
vendor: 6,584 bytes, lines 6981-6982
6981P.$watch("activeIdx",function(a){0>a?b.removeAttr("aria-activedescendant"):b.attr("aria-activedescendant",X(a))});var Y=function(a,b){return P.matches.length>b&&a?a.toUpperCase()===P.matches[b].label.toUpperCase():!1},Z=function(c,d){var e={$viewValue:c};z(a,!0),D(a,!1),f.when(O.source(a,e)).then(function(f){var g=c===q.$viewValue;if(g&&x)if(f&&f.length>0){P.activeIdx=H?0:-1,D(a,!1),P.matches.length=0;for(var h=0;h<f.length;h++)e[O.itemName]=f[h],P.matches.push({id:X(h),label:O.viewMapper(P,e),model:f[h]});if(P.query=c,o(),b.attr("aria-expanded",!0),I&&1===P.matches.length&&Y(c,0)&&(angular.isNumber(P.debounceUpdate)||angular.isObject(P.debounceUpdate)?k(function(){P.select(0,d)},angular.isNumber(P.debounceUpdate)?P.debounceUpdate:P.debounceUpdate["default"]):P.select(0,d)),K){var i=P.matches[0].label;angular.isString(c)&&c.length>0&&i.slice(0,c.length).toUpperCase()===c.toUpperCase()?T.val(c+i.slice(c.length)):T.val("")}}else W(),D(a,!0);g&&z(a,!1)},function(){W(),z(a,!1),D(a,!0)})};F&&(angular.element(i).on("resize",n),h.find("body").on("scroll",n));var $=k(function(){P.matches.length&&o(),P.moveInProgress=!1},t);P.moveInProgress=!1,P.query=void 0;var _,aa=function(a){_=g(function(){Z(a)},v)},ba=function(){_&&g.cancel(_)};W(),P.assignIsOpen=function(b){J(a,b)},P.select=function(d,e){var f,h,i={};y=!0,i[O.itemName]=h=P.matches[d].model,f=O.modelMapper(a,i),N(a,f),q.$setValidity("editable",!0),q.$setValidity("parse",!0),B(a,{$item:h,$model:f,$label:O.viewMapper(a,i),$event:e}),W(),P.$eval(c.typeaheadFocusOnSelect)!==!1&&g(function(){b[0].focus()},0,!1)},b.on("keydown",function(b){if(0!==P.matches.length&&-1!==s.indexOf(b.which)){var c=A(a,{$event:b});if(-1===P.activeIdx&&c||9===b.which&&b.shiftKey)return W(),void P.$digest();b.preventDefault();var d;switch(b.which){case 27:b.stopPropagation(),W(),a.$digest();break;case 38:P.activeIdx=(P.activeIdx>0?P.activeIdx:P.matches.length)-1,P.$digest(),d=U[0].querySelectorAll(".uib-typeahead-match")[P.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;case 40:P.activeIdx=(P.activeIdx+1)%P.matches.length,P.$digest(),d=U[0].querySelectorAll(".uib-typeahead-match")[P.activeIdx], 6982d.parentNode.scrollTop=d.offsetTop;break;default:c&&P.$apply(function(){angular.isNumber(P.debounceUpdate)||angular.isObject(P.debounceUpdate)?k(function(){P.select(P.activeIdx,b)},angular.isNumber(P.debounceUpdate)?P.debounceUpdate:P.debounceUpdate["default"]):P.select(P.activeIdx,b)})}}}),b.on("focus",function(a){x=!0,0!==u||q.$viewValue||g(function(){Z(q.$viewValue,a)},0)}),b.on("blur",function(a){C&&P.matches.length&&-1!==P.activeIdx&&!y&&(y=!0,P.$apply(function(){angular.isObject(P.debounceUpdate)&&angular.isNumber(P.debounceUpdate.blur)?k(function(){P.select(P.activeIdx,a)},P.debounceUpdate.blur):P.select(P.activeIdx,a)})),!w&&q.$error.editable&&(q.$setViewValue(),P.$apply(function(){q.$setValidity("editable",!0),q.$setValidity("parse",!0)}),b.val("")),x=!1,y=!1});var ca=function(c){b[0]!==c.target&&3!==c.which&&0!==P.matches.length&&(W(),j.$$phase||a.$digest())};h.on("click",ca),a.$on("$destroy",function(){h.off("click",ca),(F||G)&&da.remove(),F&&(angular.element(i).off("resize",n),h.find("body").off("scroll",n)),U.remove(),K&&S.remove()});var da=d(U)(P);F?h.find("body").append(da):G?angular.element(G).eq(0).append(da):b.after(da),this.init=function(b){q=b,r=p(q),P.debounceUpdate=e(r.getOption("debounce"))(a),q.$parsers.unshift(function(b){return x=!0,0===u||b&&b.length>=u?v>0?(ba(),aa(b)):Z(b):(z(a,!1),ba(),W()),w?b:b?void q.$setValidity("editable",!1):(q.$setValidity("editable",!0),null)}),q.$formatters.push(function(b){var c,d,e={};return w||q.$setValidity("editable",!0),E?(e.$model=b,E(a,e)):(e[O.itemName]=b,c=O.viewMapper(a,e),e[O.itemName]=void 0,d=O.viewMapper(a,e),c!==d?c:b)})}}]).directive("uibTypeahead",function(){return{controller:"UibTypeaheadController",require:["ngModel","uibTypeahead"],link:function(a,b,c,d){d[1].init(d[0])}}}).directive("uibTypeaheadPopup",["$$debounce",function(a){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&",assignIsOpen:"&",debounce:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"uib/template/typeahead/typeahead-popup.html"},link:function(b,c,d){b.templateUrl=d.templateUrl,b.isOpen=function(){var a=b.matches.length>0;return b.assignIsOpen({isOpen:a}),a},b.isActive=function(a){return b.active===a},b.selectActive=function(a){b.active=a},b.selectMatch=function(c,d){var e=b.debounce();angular.isNumber(e)||angular.isObject(e)?a(function(){b.select({activeIdx:c,evt:d})},angular.isNumber(e)?e:e["default"]):b.select({activeIdx:c,evt:d})}}}}]).directive("uibTypeaheadMatch",["$templateRequest","$compile","$parse",function(a,b,c){return{scope:{index:"=",match:"=",query:"="},link:function(d,e,f){var g=c(f.templateUrl)(d.$parent)||"uib/template/typeahead/typeahead-match.html";a(g).then(function(a){var c=angular.element(a.trim());e.replaceWith(c),b(c)(d)})}}}]).filter("uibTypeaheadHighlight",["$sce","$injector","$log",function(a,b,c){function d(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function e(a){return/<.*>/g.test(a)}var f;return f=b.has("$sanitize"),function(b,g){return!f&&e(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=g?(""+b).replace(new RegExp(d(g),"gi"),"<strong>$&</strong>"):b,f||(b=a.trustAsHtml(b)),b}}]),angular.module("uib/template/accordion/accordion-group.html",[]).run(["$templateCache",function(a){a.put("uib/template/accordion/accordion-group.html",'<div role="tab" id="{{::headingId}}" aria-selected="{{isOpen}}" class="panel-heading" ng-keypress="toggleOpen($event)">\n <h4 class="panel-title">\n <a role="button" data-toggle="collapse" href aria-expanded="{{isOpen}}" aria-controls="{{::panelId}}" tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading" ng-disabled="isDisabled" uib-tabindex-toggle><span uib-accordion-header ng-class="{\'text-muted\': isDisabled}">{{heading}}</span></a>\n </h4>\n</div>\n<div id="{{::panelId}}" aria-labelledby="{{::headingId}}" aria-hidden="{{!isOpen}}" role="tabpanel" class="panel-collapse collapse" uib-collapse="!isOpen">\n <div class="panel-body" ng-transclude></div>\n</div>\n')}]),angular.module("uib/template/accordion/accordion.html",[]).run(["$templateCache",function(a){a.put("uib/template/accordion/accordion.html",'<div role="tablist" class="panel-group" ng-transclude></div>')}]),angular.module("uib/template/alert/alert.html",[]).run(["$templateCache",function(a){a.put("uib/template/alert/alert.html",'<button ng-show="closeable" t
6982ype="button" class="close" ng-click="close({$event: $event})">\n <span aria-hidden="true">×</span>\n <span class="sr-only">Close</span>\n</button>\n<div ng-transclude></div>\n')}]),angular.module("uib/template/carousel/carousel.html",[]).run(["$templateCache",function(a){a.put("uib/template/carousel/carousel.html",'<div class="carousel-inner" ng-transclude></div>\n<a role="button" href class="left carousel-control" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">\n <span aria-hidden="true" class="glyphicon glyphicon-chevron-left"></span>\n <span class="sr-only">previous</span>\n</a>\n<a role="button" href class="right carousel-control" ng-click="next()" ng-class="{ disabled: isNextDisabled() }" ng-show="slides.length > 1">\n <span aria-hidden="true" class="glyphicon glyphicon-chevron-right"></span>\n <span class="sr-only">next</span>\n</a>\n<ol class="carousel-indicators" ng-show="slides.length > 1">\n <li ng-repeat="slide in slides | orderBy:indexOfSlide track by $index" ng-class="{ active: isActive(slide) }" ng-click="select(slide)">\n <span class="sr-only">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if="isActive(slide)">, currently active</span></span>\n </li>\n</ol>\n')}]),angular.module("uib/template/carousel/slide.html",[]).run(["$templateCache",function(a){a.put("uib/template/carousel/slide.html",'<div class="text-center" ng-transclude></div>\n')}]),angular.module("uib/template/datepicker/datepicker.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/datepicker.html",'<div ng-switch="datepickerMode">\n <div uib-daypicker ng-switch-when="day" tabindex="0" class="uib-daypicker"></div>\n <div uib-monthpicker ng-switch-when="month" tabindex="0" class="uib-monthpicker"></div>\n <div uib-yearpicker ng-switch-when="year" tabindex="0" class="uib-yearpicker"></div>\n</div>\n')}]),angular.module("uib/template/datepicker/day.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/day.html",'<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>\n <th colspan="{{::5 + showWeeks}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></button></th>\n </tr>\n <tr>\n <th ng-if="showWeeks" class="text-center"></th>\n <th ng-repeat="label in ::labels track by $index" class="text-center"><small aria-label="{{::label.full}}">{{::label.abbr}}</small></th>\n </tr>\n </thead>\n <tbody>\n <tr class="uib-weeks" ng-repeat="row in rows track by $index" role="row">\n <td ng-if="showWeeks" class="text-center h6"><em>{{ weekNumbers[$index] }}</em></td>\n <td ng-repeat="dt in row" class="uib-day text-center" role="gridcell"\n id="{{::dt.uid}}"\n ng-class="::dt.customClass">\n <button type="button" class="btn btn-default btn-sm"\n uib-is-class="\n \'btn-info\' for selectedDt,\n \'active\' for activeDt\n on dt"\n ng-click="select(dt.date)"\n ng-disabled="::dt.disabled"\n tabindex="-1"><span ng-class="::{\'text-muted\': dt.secondary, \'text-info\': dt.current}">{{::dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("uib/template/datepicker/month.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/month.html",'<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>\n <th colspan="{{::yearHeaderColspan}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === max
6982Mode" tabindex="-1"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></i></button></th>\n </tr>\n </thead>\n <tbody>\n <tr class="uib-months" ng-repeat="row in rows track by $index" role="row">\n <td ng-repeat="dt in row" class="uib-month text-center" role="gridcell"\n id="{{::dt.uid}}"\n ng-class="::dt.customClass">\n <button type="button" class="btn btn-default"\n uib-is-class="\n \'btn-info\' for selectedDt,\n \'active\' for activeDt\n on dt"\n ng-click="select(dt.date)"\n ng-disabled="::dt.disabled"\n tabindex="-1"><span ng-class="::{\'text-info\': dt.current}">{{::dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("uib/template/datepicker/year.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/year.html",'<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>\n <th colspan="{{::columns - 2}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></button></th>\n </tr>\n </thead>\n <tbody>\n <tr class="uib-years" ng-repeat="row in rows track by $index" role="row">\n <td ng-repeat="dt in row" class="uib-year text-center" role="gridcell"\n id="{{::dt.uid}}"\n ng-class="::dt.customClass">\n <button type="button" class="btn btn-default"\n uib-is-class="\n \'btn-info\' for selectedDt,\n \'active\' for activeDt\n on dt"\n ng-click="select(dt.date)"\n ng-disabled="::dt.disabled"\n tabindex="-1"><span ng-class="::{\'text-info\': dt.current}">{{::dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("uib/template/datepickerPopup/popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepickerPopup/popup.html",'<ul role="presentation" class="uib-datepicker-popup dropdown-menu uib-position-measure" dropdown-nested ng-if="isOpen" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">\n <li ng-transclude></li>\n <li ng-if="showButtonBar" class="uib-button-bar">\n <span class="btn-group pull-left">\n <button type="button" class="btn btn-sm btn-info uib-datepicker-current" ng-click="select(\'today\', $event)" ng-disabled="isDisabled(\'today\')">{{ getText(\'current\') }}</button>\n <button type="button" class="btn btn-sm btn-danger uib-clear" ng-click="select(null, $event)">{{ getText(\'clear\') }}</button>\n </span>\n <button type="button" class="btn btn-sm btn-success pull-right uib-close" ng-click="close($event)">{{ getText(\'close\') }}</button>\n </li>\n</ul>\n')}]),angular.module("uib/template/modal/window.html",[]).run(["$templateCache",function(a){a.put("uib/template/modal/window.html","<div class=\"modal-dialog {{size ? 'modal-' + size : ''}}\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n")}]),angular.module("uib/template/pager/pager.html",[]).run(["$templateCache",function(a){a.put("uib/template/pager/pager.html",'<li ng-class="{disabled: noPrevious()||ngDisabled, previous: align}"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText(\'previous\')}}</a></li>\n<li ng-class="{disabled: noNext()||ngDisabled, next: align}"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText(\'next\')}}</a></li>\n')}]),angular.module("uib/template/pagination/pagination.html",[]).run(["$templateCache",function(a){a.put("uib/template/pagination/pagination.html",'<li role="menuitem" ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText(\'first\')}}</a></li>\n<li role="menuitem" ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText(\'previous\')}}</a></li>\n<li role="menuitem" ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)" ng-disabled="ngDisabled&&!page.active" uib-tabindex-toggle>{{page.text}}</a></li>\n<li role="menuitem" ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText(\'next\')}}</a></li>\n<li role="menuitem" ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText(\'last\')}}</a></li>\n')}]),angular.module("uib/template/tooltip/tooltip-html-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-html-popup.html",'<div class="tooltip-arrow"></div>\n<div class="tooltip-inner" ng-bind-html="contentExp()"></div>\n')}]),angular.module("uib/template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-popup.html",'<div class="tooltip-arrow"></div>\n<div class="tooltip-inner" ng-bind="content"></div>\n')}]),angular.module("uib/template/tooltip/tooltip-template-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-template-popup.html",'<div class="tooltip-arrow"></div>\n<div class="tooltip-inner"\n uib-tooltip-template-transclude="contentExp()"\n tooltip-template-transclude-scope="originScope()"></div>\n')}]),angular.module("uib/template/popover/popover-html.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover-html.html",'<div class="arrow"></div>\n\n<div class="popover-inner">\n <h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>\n <div class="popover-content" ng-bind-html="contentExp()"></div>\n</div>\n')}]),angular.module("uib/template/popover/popover-template.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover-template.html",'<div class="arrow"></div>\n\n<div class="popover-inner">\n <h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>\n <div class="popover-content"\n uib-tooltip-template-transclude="contentExp()"\n tooltip-template-transclude-scope="originScope()"></div>\n</div>\n')}]),angular.module("uib/template/popover/popover.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover.html",'<div class="arrow"></div>\n\n<div class="popover-inner">\n <h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>\n <div class="popover-content" ng-bind="content"></div>\n</div>\n')}]),angular.module("uib/template/progressbar/bar.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/bar.html",'<div class="progress-bar" ng-class="type && \'progress-bar-\' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: (percent < 100 ? percent : 100) + \'%\'}" aria-valuetext="{{percent | number:0}}%" aria-labelledby="{{::title}}" ng-transclude></div>\n')}]),angular.module("uib/template/progressbar/progress.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/progress.html",'<div class="progress" ng-transclude aria-labelledby="{{::title}}"></div>')}]),angular.module("uib/template/progressbar/progressbar.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/progressbar.html",'<div class="progress">\n <div class="progress-bar" ng-class="type && \'progress-bar-\' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: (percent < 100 ? percent : 100) + \'%\'}" aria-valuetext="{{percent | number:0}}%" aria-labelledby="{{::title}}" ng-transclude></div>\n</div>\n')}]),angular.module("uib/template/rating/rating.html",[]).run(["$templateCache",function(a){a.put("uib/template/rating/rating.html",'<span ng-mouseleave="reset()" ng-keydown="onKeydown($event)" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="{{range.length}}" aria-valuenow="{{value}}" aria-valuetext="{{title}}">\n <span ng-repeat-start="r in range track by $index" class="sr-only">({{ $index < value ? \'*\' : \' \' }})</span>\n <i ng-repeat-end ng-mouseenter="enter($index + 1)" ng-click="rate($index + 1)" class="glyphicon" ng-class="$index < value && (r.stateOn || \'glyphicon-star\') || (r.stateOff || \'glyphicon-star-empty\')" ng-attr-title="{{r.title}}"></i>\n</span>\n')}]),angular.module("uib/template/tabs/tab.html",[]).run(["$templateCache",function(a){a.put("uib/template/tabs/tab.html",'<li ng-class="[{active: active, disabled: disabled}, classes]" class="uib-tab nav-item">\n <a href ng-click="select($event)" class="nav-link" uib-tab-heading-transclude>{{heading}}</a>\n</li>\n')}]),angular.module("uib/template/tabs/tabset.html",[]).run(["$templateCache",function(a){a.put("uib/template/tabs/tabset.html",'<div>\n <ul class="nav nav-{{tabset.type || \'tabs\'}}" ng-class="{\'nav-stacked\': vertical, \'nav-justified\': justified}" ng-transclude></ul>\n <div class="tab-content">\n <div class="tab-pane"\n ng-repeat="tab in tabset.tabs"\n ng-class="{active: tabset.active === tab.index}"\n uib-tab-content-transclude="tab">\n </div>\n </div>\n</div>\n')}]),angular.module("uib/template/timepicker/timepicker.html",[]).run(["$templateCache",function(a){a.put("uib/template/timepicker/timepicker.html",'<table class="uib-timepicker">\n <tbody>\n <tr class="text-center" ng-show="::showSpinners">\n <td class="uib-increment hours"><a ng-click="incrementHours()" ng-class="{disabled: noIncrementHours()}" class="btn btn-link" ng-disabled="noIncrementHours()" tabindex="-1"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n <td> </td>\n <td class="uib-increment minutes"><a ng-click="incrementMinutes()" ng-class="{disabled: noIncrementMinutes()}" class="btn btn-link" ng-disabled="noIncrementMinutes()" tabindex="-1"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n <td ng-show="showSeconds"> </td>\n <td ng-show="showSeconds" class="uib-increment seconds"><a ng-click="incrementSeconds()" ng-class="{disabled: noIncrementSeconds()}" class="btn btn-link" ng-disabled="noIncrementSeconds()" tabindex="-1"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n <td ng-show="showMeridian"></td>\n </tr>\n <tr>\n <td class="form-group uib-time hours" ng-class="{\'has-error\': invalidHours}">\n <input type="text" placeholder="HH" ng-model="hours" ng-change="updateHours()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2" tabindex="{{::tabindex}}" ng-disabled="noIncrementHours()" ng-blur="blur()">\n </td>\n <td class="uib-separator">:</td>\n <td class="form-group uib-time minutes" ng-class="{\'has-error\': invalidMinutes}">\n <input type="text" placeholder="MM" ng-model="minutes" ng-change="updateMinutes()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2" tabindex="{{::tabindex}}" ng-disabled="noIncrementMinutes()" ng-blur="blur()">\n </td>\n <td ng-show="showSeconds" class="uib-separator">:</td>\n <td class="form-group uib-time seconds" ng-class="{\'has-error\': invalidSeconds}" ng-show="showSeconds">\n <input type="text" placeholder="SS" ng-model="seconds" ng-change="updateSeconds()" class="form-control text-center" ng-readonly="readonlyInput" maxlength="2" tabindex="{{::tabindex}}" ng-disabled="noIncrementSeconds()" ng-blur="blur()">\n </td>\n <td ng-show="showMeridian" class="uib-time am-pm"><button type="button" ng-class="{disabled: noToggleMeridian()}" class="btn btn-default text-center" ng-click="toggleMeridian()" ng-disabled="noToggleMeridian()" tabindex="{{::tabindex}}">{{meridian}}</button></td>\n </tr>\n <tr class="text-center" ng-show="::showSpinners">\n <td class="uib-decrement hours"><a ng-click="decrementHours()" ng-class="{disabled: noDecrementHours()}" class="btn btn-link" ng-disabled="noDecrementHours()" tabindex="-1"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n <td> </td>\n <td class="uib-decrement minutes"><a ng-click="decrementMinutes()" ng-class="{disabled: noDecrementMinutes()}" class="btn btn-link" ng-disabled="noDecrementMinutes()" tabindex="-1"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n <td ng-show="showSeconds"> </td>\n <td ng-show="showSeconds" class="uib-decrement seconds"><a ng-click="decrementSeconds()" ng-class="{disabled: noDecrementSeconds()}" class="btn btn-link" ng-disabled="noDecrementSeconds()" tabindex="-1"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n <td ng-show="showMeridian"></td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("uib/template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(a){a.put("uib/template/typeahead/typeahead-match.html",'<a href\n tabindex="-1"\n ng-bind-html="match.label | uibTypeaheadHighlight:query"\n ng-attr-title="{{match.label}}"></a>\n')}]),angular.module("uib/template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/typeahead/typeahead-popup.html",'<ul class="dropdown-menu" ng-show="isOpen() && !moveInProgress" ng-style="{top: position().top+\'px\', left: position().left+\'px\'}" role="listbox" aria-hidden="{{!isOpen()}}">\n <li class="uib-typeahead-match" ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index, $event)" role="option" id="{{::match.id}}">\n <div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>\n </li>\n</ul>\n')}]),angular.module("ui.bootstrap.carousel").run(function(){!angular.$$csp().noInlineStyle&&!
6982angular.$$uibCarouselCss&&angular.element(document).find("head").prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>'),angular.$$uibCarouselCss=!0}),angular.module("ui.bootstrap.datepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerCss&&angular.element(document).find("head").prepend('<style type="text/css">.uib-datepicker .uib-title{width:100%;}.uib-day button,.uib-month button,.uib-year button{min-width:100%;}.uib-left,.uib-right{width:100%}</style>'),angular.$$uibDatepickerCss=!0}),angular.module("ui.bootstrap.position").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibPositionCss&&angular.element(document).find("head").prepend('<style type="text/css">.uib-position-measure{display:block !important;visibility:hidden !important;position:absolute !important;top:-9999px !important;left:-9999px !important;}.uib-position-scrollbar-measure{position:absolute !important;top:-9999px !important;width:50px !important;height:50px !important;overflow:scroll !important;}.uib-position-body-scrollbar-measure{overflow:scroll !important;}</style>'),angular.$$uibPositionCss=!0}),angular.module("ui.bootstrap.datepickerPopup").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerpopupCss&&angular.element(document).find("head").prepend('<style type="text/css">.uib-datepicker-popup.dropdown-menu{display:block;float:none;margin:0;}.uib-button-bar{padding:10px 9px 2px;}</style>'),angular.$$uibDatepickerpopupCss=!0}),angular.module("ui.bootstrap.tooltip").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTooltipCss&&angular.element(document).find("head").prepend('<style type="text/css">[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow,[uib-popover-popup].popover.top-left > .arrow,[uib-popover-popup].popover.top-right > .arrow,[uib-popover-popup].popover.bottom-left > .arrow,[uib-popover-popup].popover.bottom-right > .arrow,[uib-popover-popup].popover.left-top > .arrow,[uib-popover-popup].popover.left-bottom > .arrow,[uib-popover-popup].popover.right-top > .arrow,[uib-popover-popup].popover.right-bottom > .arrow,[uib-popover-html-popup].popover.top-left > .arrow,[uib-popover-html-popup].popover.top-right > .arrow,[uib-popover-html-popup].popover.bottom-left > .arrow,[uib-popover-html-popup].popover.bottom-right > .arrow,[uib-popover-html-popup].popover.left-top > .arrow,[uib-popover-html-popup].popover.left-bottom > .arrow,[uib-popover-html-popup].popover.right-top > .arrow,[uib-popover-html-popup].popover.right-bottom > .arrow,[uib-popover-template-popup].popover.top-left > .arrow,[uib-popover-template-popup].popover.top-right > .arrow,[uib-popover-template-popup].popover.bottom-left > .arrow,[uib-popover-template-popup].popover.bottom-right > .arrow,[uib-popover-template-popup].popover.left-top > .arrow,[uib-popover-template-popup].popover.left-bottom > .arrow,[uib-popover-template-popup].popover.right-top > .arrow,[uib-popover-template-popup].popover.right-bottom > .arrow{top:auto;bottom:auto;left:auto;right:auto;margin:0;}[uib-popover-popup].popover,[uib-popover-html-popup].popover,[uib-popover-template-popup].popover{display:block !important;}</style>'),angular.$$uibTooltipCss=!0}),angular.module("ui.bootstrap.timepicker").run(function(){!angular.$$csp().noInlineStyle&&!
6982angular.$$uibTimepickerCss&&angular.element(document).find("head").prepend('<style type="text/css">.uib-time input{width:50px;}</style>'),angular.$$uibTimepickerCss=!0}),angular.module("ui.bootstrap.typeahead").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTypeaheadCss&&angular.element(document).find("head").prepend('<style type="text/css">[uib-typeahead-popup].dropdown-menu{display:block;}</style>'),angular.$$uibTypeaheadCss=!0}); 6983/** 6984 * angular-ui-sortable - This directive allows you to jQueryUI Sortable. 6985 * @version v0.19.0 - 2018-01-14
vendor: 6,481 bytes, lines 6985-6990
6985 6986 * @link http://angular-ui.github.com 6987 * @license MIT 6988 */ 6989 6990!function(a,b,c){"use strict";b.module("ui.sortable",[]).value("uiSortableConfig",{items:"> [ng-repeat],> [data-ng-repeat],> [x-ng-repeat]"}).directive("uiSortable",["uiSortableConfig","$timeout","$log",function(a,d,e){return{require:"?ngModel",scope:{ngModel:"=",uiSortable:"=",create:"&uiSortableCreate",start:"&uiSortableStart",activate:"&uiSortableActivate",beforeStop:"&uiSortableBeforeStop",update:"&uiSortableUpdate",remove:"&uiSortableRemove",receive:"&uiSortableReceive",deactivate:"&uiSortableDeactivate",stop:"&uiSortableStop"},link:function(f,g,h,i){function j(a,b){var c="function"==typeof a,d="function"==typeof b;return c&&d?function(){a.apply(this,arguments),b.apply(this,arguments)}:d?b:a}function k(a){var b=a.data("ui-sortable");return b&&"object"==typeof b&&"ui-sortable"===b.widgetFullName?b:null}function l(a){a.children().each(function(){var a=b.element(this);a.width(a.width())})}function m(a,b){return b}function n(b,c){return E[b]?("stop"===b&&(c=j(c,function(){f.$apply()}),c=j(c,v)),c=j(E[b],c)):F[b]&&(c=F[b](c)),c||"items"!==b&&"ui-model-items"!==b||(c=a.items),c}function o(a,d,e){function f(a,b){b in C||(C[b]=null)}b.forEach(E,f);var g=null;if(d){var h;b.forEach(d,function(d,e){if(!(a&&e in a)){if(e in D)return void("ui-floating"===e?C[e]="auto":C[e]=n(e,c));h||(h=b.element.ui.sortable().options);var f=h[e];f=n(e,f),g||(g={}),g[e]=f,C[e]=f}})}return a=b.extend({},a),b.forEach(a,function(b,c){if(c in D){if("ui-floating"!==c||b!==!1&&b!==!0||!e||(e.floating=b),"ui-preserve-size"===c&&(b===!1||b===!0)){var d=C.helper;a.helper=function(a,b){return C["ui-preserve-size"]===!0&&l(b),(d||m).apply(this,arguments)}}C[c]=n(c,b)}}),b.forEach(a,function(a,b){b in D||(a=n(b,a),g||(g={}),g[b]=a,C[b]=a)}),g}function p(a){var c=a.sortable("option","placeholder");if(c&&c.element&&"function"==typeof c.element){var d=c.element();return d=b.element(d)}return null}function q(a,b){var c=C["ui-model-items"].replace(/[^,]*>/g,""),d=a.find('[class="'+b.attr("class")+'"]:not('+c+")");return d}function r(a,b){var c=a.sortable("option","helper");return"clone"===c||"function"==typeof c&&b.item.sortable.isCustomHelperUsed()}function s(a,b){var c=null;return r(a,b)&&"parent"===a.sortable("option","appendTo")&&(c=B),c}function t(a){return/left|right/.test(a.css("float"))||/inline|table-cell/.test(a.css("display"))}function u(a,b){for(var c=0;c<a.length;c++){var d=a[c];if(d.element[0]===b[0])return d}}function v(a,b){b.item.sortable._destroy()}function w(a){return a.parent().find(C["ui-model-items"]).index(a)}function x(){f.$watchCollection("ngModel",function(){d(function(){k(g)&&g.sortable("refresh")},0,!1)}),E.start=function(a,d){if("auto"===C["ui-floating"]){var e=d.item.siblings(),f=k(b.element(a.target));f.floating=t(e)}var h=w(d.item);d.item.sortable={model:i.$modelValue[h],index:h,source:g,sourceList:d.item.parent(),sourceModel:i.$modelValue,cancel:function(){d.item.sortable._isCanceled=!0},isCanceled:function(){return d.item.sortable._isCanceled},isCustomHelperUsed:function(){return!!d.item.sortable._isCustomHelperUsed},_isCanceled:!1,_isCustomHelperUsed:d.item.sortable._isCustomHelperUsed,_destroy:function(){b.forEach(d.item.sortable,function(a,b){d.item.sortable[b]=c})},_connectedSortables:[],_getElementContext:function(a){return u(this._connectedSortables,a)}}},E.activate=function(a,b){var c=b.item.sortable.source===g,d=c?b.item.sortable.sourceList:g,e={element:g,scope:f,isSourceContext:c,savedNodesOrigin:d};b.item.sortable._connectedSortables.push(e),A=d.contents(),B=b.helper;var h=p(g);if(h&&h.length){var i=q(g,h);A=A.not(i)}},E.update=function(a,b){if(!b.item.sortable.received){b.item.sortable.dropindex=w(b.item);var c=b.item.parent().closest("[ui-sortable], [data-ui-sortable], [x-ui-sortable]");b.item.sortable.droptarget=c,b.item.sortable.droptargetList=b.item.parent();var d=b.item.sortable._getElementContext(c);b.item.sortable.droptargetModel=d.scope.ngModel,g.sortable("cancel")}var e=!b.item.sortable.received&&s(g,b,A);e&&e.length&&(A=A.not(e));var h=b.item.sortable._getElementContext(g);A.appendTo(h.savedNodesOrigin),b.item.sortable.received&&(A=null),b.item.sortable.received&&!b.item.sortable.isCanceled()&&(f.$apply(function(){i.$modelValue.splice(b.item.sortable.dropindex,0,b.item.sortable.moved)}),f.$emit("ui-sortable:moved",b))},E.stop=function(a,c){var d="dropindex"in c.item.sortable&&!c.item.sortable.isCanceled();if(d&&!c.item.sortable.received)f.$apply(function(){i.$modelValue.splice(c.item.sortable.dropindex,0,i.$modelValue.splice(c.item.sortable.index,1)[0])}),f.$emit("ui-sortable:moved",c);else if(!d&&!b.equals(g.contents().toArray(),A.toArray())){var e=s(g,c,A);e&&e.length&&(A=A.not(e));var h=c.item.sortable._getElementContext(g);A.appendTo(h.savedNodesOrigin)}A=null,B=null},E.receive=function(a,b){b.item.sortable.received=!0},E.remove=function(a,b){"dropindex"in b.item.sortable||(g.sortable("cancel"),b.item.sortable.cancel()),b.item.sortable.isCanceled()||f.$apply(function(){b.item.sortable.moved=i.$modelValue.splice(b.item.sortable.index,1)[0]})},b.forEach(E,function(a,b){E[b]=j(E[b],function(){var a,c=f[b];"function"==typeof c&&("uiSortable"+b.substring(0,1).toUpperCase()+b.substring(1)).length&&"function"==typeof(a=c())&&a.apply(this,arguments)})}),F.helper=function(a){return a&&"function"==typeof a?function(d,e){var f=e.sortable,h=w(e);e.sortable={model:i.$modelValue[h],index:h,source:g,sourceList:e.parent(),sourceModel:i.$modelValue,_restore:function(){b.forEach(e.sortable,function(a,b){e.sortable[b]=c}),e.sortable=f}};var j=a.apply(this,arguments);return e.sortable._restore(),e.sortable._isCustomHelperUsed=e!==j,j}:a},f.$watchCollection("uiSortable",function(a,b){var c=k(g);if(c){var d=o(a,b,c);d&&g.sortable("option",d)}},!0),o(C)}function y(){i?x():e.info("ui.sortable: ngModel not provided!",g),g.sortable(C)}function z(){return f.uiSortable&&f.uiSortable.disabled?!1:(y(),z.cancelWatcher(),z.cancelWatcher=b.noop,!0)}var A,B,C={},D={"ui-floating":c,"ui-model-items":a.items,"ui-preserve-size":c},E={create:null,start:null,activate:null,beforeStop:null,update:null,remove:null,receive:null,deactivate:null,stop:null},F={helper:null};return b.extend(C,D,a,f.uiSortable),b.element.fn&&b.element.fn.jquery?(z.cancelWatcher=b.noop,void(z()||(z.cancelWatcher=f.$watch("uiSortable.disabled",z)))):void e.error("ui.sortable: jQuery should be included before AngularJS!")}}}])}(window,window.angular);
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.