1var app = angular.module("fluro", window.dependencies); 2 3var useSiteCache = false; 4 5function getMetaKey(stringKey) { 6 var metas = document.getElementsByTagName("meta"); 7 for (i = 0; i < metas.length; i++) { 8 if (metas[i].getAttribute("property") == stringKey) { 9 return metas[i].getAttribute("content"); 10 } 11 } 12 return ""; 13} 14 15var $globalStates; 16 17app.config(function($controllerProvider, $compileProvider, $filterProvider, $provide, $stateProvider, $httpProvider, FluroProvider, $urlRouterProvider, $analyticsProvider, $locationProvider) { 18 $analyticsProvider.settings.ga.additionalAccountNames = [ "fluro" ]; 19 if (!applicationData) { 20 return; 21 } else {} 22 $globalStates = $stateProvider; 23 $compileProvider.debugInfoEnabled(false); 24 app.components = {}; 25 app.components.controller = $controllerProvider.register; 26 app.components.directive = $compileProvider.directive; 27 app.components.filter = $filterProvider.register; 28 app.components.factory = $provide.factory; 29 app.components.service = $provide.service; 30 var access_token = getMetaKey("fluro_application_key"); 31 var api_url = getMetaKey("fluro_url"); 32 var timezoneOffset = getMetaKey("fluro_timezone_offset"); 33 var timezone = getMetaKey("fluro_timezone"); 34 var configDetails = window.configHandshakeDetails; 35 if (!configDetails) { 36 configDetails = { 37 apiURL: api_url, 38 token: access_token 39 }; 40 } 41 if (timezoneOffset && String(timezoneOffset).length) { 42 configDetails.timezoneOffset = Number(timezoneOffset); 43 } 44 if (timezone && String(timezone).length) { 45 configDetails.timezone = timezone; 46 } 47 FluroProvider.set(configDetails); 48 if (!configDetails.token) { 49 $httpProvider.defaults.withCredentials = true; 50 } 51 $httpProvider.interceptors.push("FluroAuthentication"); 52 $locationProvider.html5Mode(true); 53 $locationProvider.hashPrefix("!"); 54 $urlRouterProvider.otherwise("/"); 55 var siteModel = applicationData.model; 56 function getFlattenedRoutes(array) { 57 return _.chain(array).map(function(route) { 58 if (route.type == "folder") { 59 return getFlattenedRoutes(route.routes); 60 } else { 61 return route; 62 } 63 }).flatten().compact().value(); 64 } 65 var flattenedRoutes = getFlattenedRoutes(siteModel.routes); 66 _.each(flattenedRoutes, function(route) { 67 var template = ""; 68 if (!route.state || !route.state.length) { 69 if (route.title) {} else {} 70 return; 71 } 72 var stateDetails = { 73 name: route.state, 74 url: route.url + "?sv", 75 title: route.title, 76 routeData: route, 77 views: {} 78 }; 79 var viewName = "$site@"; 80 if (route.vid) { 81 viewName = route.vid; 82 } 83 stateDetails.views[viewName] = { 84 template: '<route class="site-page" ng-page="page" ng-model="route" ng-items="items" ng-slug="slug" ng-results="results" ng-collections="collections"></route>', 85 controller: [ "$scope", "route", "items", "slug", "collections", "results", "pageCollections", "pageResults", function($scope, route, items, slug, collections, results, pageCollections, pageResults) { 86 $scope.route = route; 87 $scope.items = items; 88 $scope.results = results; 89 $scope.collections = collections; 90 $scope.slug = slug; 91 $scope.page = { 92 results: pageResults, 93 collections: pageCollections 94 }; 95 } ], 96 resolve: { 97 route: function() { 98 return route; 99 }, 100 slug: [ "$rootScope", "$q", "$stateParams", "FluroContentRetrieval", function($rootScope, $q, $stateParams, FluroContentRetrieval) { 101 var deferred = $q.defer(); 102 if ($stateParams.slug || $stateParams._id) { 103 var appendPostCount = route.appendPostCount; 104 var appendForms = route.appendForms; 105 var appendPosts = route.appendPosts; 106 var appendContactDetail = route.appendContactDetail; 107 var appendProcess = route.appendProcess; 108 var appendTeams = route.appendTeams; 109 var appendAssignments = route.appendAssignments; 110 var includePublicSearchResults = route.includePublic; 111 if (!appendPostCount || !appendPostCount.length) { 112 appendPostCount = "all"; 113 } 114 if (!appendProcess || !appendProcess.length) { 115 appendProcess = "all"; 116 } 117 if (!appendForms || !appendForms.length) { 118 appendForms = "all"; 119 } 120 var queryParams = { 121 includePublic: includePublicSearchResults, 122 searchInheritable: true, 123 appendPostCount: appendPostCount, 124 appendPosts: appendPosts, 125 appendProcess: appendProcess, 126 appendContactDetail: appendContactDetail, 127 appendForms: appendForms, 128 appendAssignments: appendAssignments, 129 appendTeams: appendTeams 130 }; 131 if (route.excludeInheritable) { 132 queryParams.searchInheritable = false;
133 } 134 if (route.noCacheSlug) { 135 queryParams.noCache = true; 136 } 137 if (useSiteCache) { 138 queryParams.noCache = true; 139 } 140 if ($stateParams.from && $stateParams._id) { 141 var promise = FluroContentRetrieval.getItemFromProduct($stateParams._id, $stateParams.from); 142 } else { 143 var queryRequest = {}; 144 if ($stateParams._id && $stateParams._id.length) { 145 queryRequest._id = $stateParams._id; 146 } else { 147 queryRequest.slug = $stateParams.slug; 148 } 149 var promise = FluroContentRetrieval.query(queryRequest, null, null, queryParams); 150 } 151 promise.then(function(slug) { 152 if (_.isArray(slug) && slug.length) { 153 var actualSlugItem = slug[0]; 154 $rootScope.slug = actualSlugItem; 155 return deferred.resolve(actualSlugItem); 156 } 157 if (slug) { 158 $rootScope.slug = slug; 159 return deferred.resolve(slug); 160 } 161 $rootScope.slug = null; 162 deferred.resolve(null); 163 }, deferred.reject); 164 } else { 165 $rootScope.slug = null; 166 deferred.resolve(); 167 } 168 return deferred.promise; 169 } ], 170 items: [ "$http", "Fluro", "FluroContentRetrieval", function($http, Fluro, FluroContentRetrieval) { 171 var contentNids = _.chain(route.sections).map(function(section) { 172 var nids = []; 173 nids = nids.concat(section.content); 174 var keyContent = _.map(section.keys, function(keyGroup) { 175 return keyGroup.content; 176 }); 177 nids = nids.concat(keyContent); 178 return nids; 179 }).flattenDeep().value(); 180 if (route.content && route.content.length) { 181 contentNids = contentNids.concat(route.content); 182 } 183 if (route.keys && route.keys.length) { 184 var keyContent = _.chain(route.keys).map(function(keyGroup) { 185 return keyGroup.content; 186 }).flattenDeep().value(); 187 contentNids = contentNids.concat(keyContent); 188 } 189 if (contentNids && contentNids.length) { 190 return FluroContentRetrieval.get(contentNids, useSiteCache, { 191 searchInheritable: true 192 }); 193 } else { 194 return []; 195 } 196 } ], 197 collections: [ "$http", "Fluro", "$q", "FluroContentRetrieval", function($http, Fluro, $q, FluroContentRetrieval) { 198 var collectionIds = _.chain(route.sections).map(function(section) { 199 return section.collections; 200 }).flatten().value(); 201 if (collectionIds && collectionIds.length) { 202 var deferred = $q.defer(); 203 function collectionsError(err) { 204 deferred.resolve([]); 205 } 206 function collectionsSuccess(res) { 207 deferred.resolve(res); 208 } 209 FluroContentRetrieval.getMultipleCollections(collectionIds, useSiteCache, { 210 searchInheritable: true 211 }).then(collectionsSuccess, collectionsError); 212 return deferred.promise; 213 } else { 214 return []; 215 } 216 } ], 217 results: [ "$http", "$q", "Fluro", "FluroContentRetrieval", "$stateParams", "$parse", "route", "slug", "items", "collections", function($http, $q, Fluro, FluroContentRetrieval, $stateParams, $parse, route, slug, items, collections) { 218 var queryVariables; 219 var queryIds = _.chain(route.sections).map(function(section) { 220 var scopeObject = {}; 221 scopeObject.items = items; 222 scopeObject.collections = collections; 223 scopeObject.stateParams = $stateParams; 224 if (slug) { 225 scopeObject.slug = slug; 226 } 227 scopeObject.route = route; 228 scopeObject.model = section; 229 if (section.queryVariables) { 230 var localQueryVariables = _.mapValues(section.queryVariables, function(value) { 231 var val = $parse(value)(scopeObject); 232 return val; 233 }); 234 localQueryVariables = _.pick(localQueryVariables, _.identity); 235 if (!queryVariables) { 236 queryVariables = localQueryVariables; 237 } else { 238 _.assign(queryVariables, localQueryVariables); 239 } 240 } 241 return section.queries; 242 }
242).flatten().value(); 243 if (queryIds && queryIds.length) { 244 return FluroContentRetrieval.queryMultiple(queryIds, useSiteCache, queryVariables); 245 } else { 246 return []; 247 } 248 } ], 249 pageCollections: [ "$http", "$q", "Fluro", "FluroContentRetrieval", function($http, $q, Fluro, FluroContentRetrieval) { 250 var collectionIds = route.collections; 251 if (collectionIds && collectionIds.length) { 252 var deferred = $q.defer(); 253 function collectionsError(err) { 254 deferred.resolve([]); 255 } 256 function collectionsSuccess(res) { 257 deferred.resolve(res); 258 } 259 FluroContentRetrieval.getMultipleCollections(collectionIds, useSiteCache).then(collectionsSuccess, collectionsError); 260 return deferred.promise; 261 } else { 262 return []; 263 } 264 } ], 265 pageResults: [ "$http", "$q", "Fluro", "FluroContentRetrieval", "$parse", "route", "slug", "items", "collections", function($http, $q, Fluro, FluroContentRetrieval, $parse, route, slug, items, collections) { 266 var scopeObject = {}; 267 scopeObject.items = items; 268 scopeObject.collections = collections; 269 if (slug) { 270 scopeObject.slug = slug; 271 } 272 scopeObject.route = route; 273 scopeObject.model = route; 274 var queryVariables; 275 var queryIds = route.queries; 276 if (route.queryVariables) { 277 var localQueryVariables = _.mapValues(route.queryVariables, function(value) { 278 var val = $parse(value)(scopeObject); 279 return val; 280 }); 281 localQueryVariables = _.pick(localQueryVariables, _.identity); 282 if (!queryVariables) { 283 queryVariables = localQueryVariables; 284 } else { 285 _.assign(queryVariables, localQueryVariables); 286 } 287 } 288 if (queryIds && queryIds.length) { 289 return FluroContentRetrieval.queryMultiple(queryIds, useSiteCache, queryVariables); 290 } else { 291 return []; 292 } 293 } ] 294 } 295 }; 296 $stateProvider.state(route.state, stateDetails); 297 }); 298}); 299 300app.run(function(FluroSocket, $window, FluroTokenService, $state, $location, $localStorage, $sessionStorage, $templateCache, $document, $stateParams, $rootScope, FluroAccess, FluroMenuService, DateTools, FluroScrollService, FluroWindowService, FluroSEO, Asset, $window, $timeout) { 301 var siteModel = applicationData.model; 302 if (siteModel && siteModel.templates && siteModel.templates.length) { 303 _.each(siteModel.templates, function(sectionTemplate) { 304 $templateCache.put(sectionTemplate.key, sectionTemplate.html); 305 }); 306 } 307 if (!FluroTokenService.backup) { 308 FluroTokenService.backup = {}; 309 } 310 var hardcoded_access_token = getMetaKey("fluro_application_key"); 311 if (hardcoded_access_token && hardcoded_access_token.length) { 312 FluroTokenService.backup.token = hardcoded_access_token; 313 } 314 if ($initUser) { 315 $rootScope.user = $initUser; 316 FluroTokenService.backup.user = $initUser; 317 } else { 318 if (siteModel.account) { 319 FluroSocket.join(siteModel.account); 320 } 321 } 322 $rootScope.localStorage = $localStorage; 323 $rootScope.sessionStorage = $sessionStorage; 324 $rootScope.access = FluroAccess; 325 $rootScope.date = DateTools; 326 $rootScope.seo = FluroSEO; 327 $rootScope.asset = Asset; 328 $rootScope.menu = FluroMenuService; 329 $rootScope.scroll = FluroScrollService; 330 $rootScope.window = FluroWindowService; 331 $rootScope.site = applicationData.model; 332 $rootScope.appData = applicationData; 333 $rootScope.$state = $state; 334 $rootScope.$stateParams = $stateParams; 335 var clicked; 336 $rootScope.goBack = function() { 337 clicked = false;
338 var previousState = $rootScope.previousState; 339 if (previousState) { 340 $state.go(previousState.name, previousState.params); 341 } else { 342 if ($state.$current.parent && $state.$current.parent.self.name.length) { 343 $state.go("^"); 344 } else { 345 $state.go("home"); 346 } 347 } 348 }; 349 $document.on("click", function(event) { 350 clicked = true; 351 setTimeout(function() { 352 clicked = false; 353 }, 100); 354 }); 355 $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams, error) { 356 var path = $state.href(fromState, fromParams); 357 FluroScrollService.cache[path] = FluroScrollService.get(); 358 if (!fromState.abstract) { 359 $rootScope.previousState = { 360 name: fromState.name, 361 params: fromParams 362 }; 363 } 364 if (clicked) { 365 var toPath = $state.href(toState, toParams); 366 delete FluroScrollService.cache[toPath]; 367 } 368 }); 369 $rootScope.$on("$stateChangeError", function(event, toState, toParams, fromState, fromParams, error) { 370 throw error; 371 }); 372 $rootScope.$on("$stateChangeSuccess", function(event, toState, toParams, fromState, fromParams, error) { 373 $rootScope.currentRoute = toState.routeData; 374 $rootScope.bodyClasses = "route-" + _.kebabCase(toState.name); 375 var hash = FluroScrollService.getAnchor(); 376 if (hash && hash.length) { 377 $location.hash(hash); 378 $timeout(function() { 379 FluroScrollService.scrollToId(hash, 0); 380 }); 381 } else { 382 var path = $state.href($state.current.name, $stateParams); 383 var cachedValue = FluroScrollService.cache[path]; 384 if (cachedValue) { 385 $timeout(function() { 386 FluroScrollService.scrollTo(cachedValue, 0); 387 delete FluroScrollService.cache[path]; 388 }); 389 } else { 390 $timeout(function() { 391 FluroScrollService.scrollTo(0, 0); 392 }); 393 } 394 } 395 }); 396}); 397 398app.directive("application", function() { 399 return { 400 restrict: "E", 401 replace: true, 402 templateUrl: "application/application.html", 403 controller: "ApplicationController" 404 }; 405}); 406 407app.controller("ApplicationController", function($scope) { 408 $scope.message = "hey"; 409}); 410 411(function() { 412 Date.shortMonths = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; 413 Date.longMonths = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
413November", "December" ]; 414 Date.shortDays = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]; 415 Date.longDays = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]; 416 var replaceChars = { 417 d: function() { 418 return (this.getDate() < 10 ? "0" : "") + this.getDate(); 419 }, 420 D: function() { 421 return Date.shortDays[this.getDay()]; 422 }, 423 j: function() { 424 return this.getDate(); 425 }, 426 l: function() { 427 return Date.longDays[this.getDay()]; 428 }, 429 N: function() { 430 return this.getDay() == 0 ? 7 : this.getDay(); 431 }, 432 S: function() { 433 return this.getDate() % 10 == 1 && this.getDate() != 11 ? "st" : this.getDate() % 10 == 2 && this.getDate() != 12 ? "nd" : this.getDate() % 10 == 3 && this.getDate() != 13 ? "rd" : "th"; 434 }, 435 w: function() { 436 return this.getDay(); 437 }, 438 z: function() { 439 var d = new Date(this.getFullYear(), 0, 1); 440 return Math.ceil((this - d) / 864e5); 441 }, 442 W: function() { 443 var target = new Date(this.valueOf()); 444 var dayNr = (this.getDay() + 6) % 7; 445 target.setDate(target.getDate() - dayNr + 3); 446 var firstThursday = target.valueOf(); 447 target.setMonth(0, 1); 448 if (target.getDay() !== 4) { 449 target.setMonth(0, 1 + (4 - target.getDay() + 7) % 7); 450 } 451 return 1 + Math.ceil((firstThursday - target) / 6048e5); 452 }, 453 F: function() { 454 return Date.longMonths[this.getMonth()]; 455 }, 456 m: function() { 457 return (this.getMonth() < 9 ? "0" : "") + (this.getMonth() + 1); 458 }, 459 M: function() { 460 return Date.shortMonths[this.getMonth()]; 461 }, 462 n: function() { 463 return this.getMonth() + 1; 464 }, 465 t: function() { 466 var d = new Date(); 467 return new Date(d.getFullYear(), d.getMonth(), 0).getDate(); 468 }, 469 L: function() { 470 var year = this.getFullYear(); 471 return year % 400 == 0 || year % 100 != 0 && year % 4 == 0; 472 }, 473 o: function() { 474 var d = new Date(this.valueOf()); 475 d.setDate(d.getDate() - (this.getDay() + 6) % 7 + 3); 476 return d.getFullYear(); 477 }, 478 Y: function() { 479 return this.getFullYear(); 480 }, 481 y: function() { 482 return ("" + this.getFullYear()).substr(2); 483 }, 484 a: function() { 485 return this.getHours() < 12 ? "am" : "pm"; 486 }, 487 A: function() { 488 return this.getHours() < 12 ? "AM" : "PM"; 489 }, 490 B: function() { 491 return Math.floor(((this.getUTCHours() + 1) % 24 + this.getUTCMinutes() / 60 + this.getUTCSeconds() / 3600) * 1e3 / 24); 492 }, 493 g: function() { 494 return this.getHours() % 12 || 12; 495 }, 496 G: function() { 497 return this.getHours(); 498 }, 499 h: function() { 500 return ((this.getHours() % 12 || 12) < 10 ? "0" : "") + (this.getHours() % 12 || 12); 501 }, 502 H: function() { 503 return (this.getHours() < 10 ? "0" : "") + this.getHours(); 504 }, 505 i: function() { 506 return (this.getMinutes() < 10 ? "0" : "") + this.getMinutes(); 507 }, 508 s: function() { 509 return (this.getSeconds() < 10 ? "0" : "") + this.getSeconds(); 510 }, 511 u: function() { 512 var m = this.getMilliseconds(); 513 return (m < 10 ? "00" : m < 100 ? "0" : "") + m; 514 }, 515 e: function() { 516 return "Not Yet Supported"; 517 }, 518 I: function() { 519 var DST = null; 520 for (var i = 0; i < 12; ++i) { 521 var d = new Date(this.getFullYear(), i, 1); 522 var offset = d.getTimezoneOffset(); 523 if (DST === null) DST = offset; else if (offset < DST) { 524 DST = offset; 525 break; 526 } else if (offset > DST) break; 527 } 528 return this.getTimezoneOffset() == DST | 0; 529 }, 530 O: function() { 531 return (-this.getTimezoneOffset() < 0 ? "-" : "+") + (Math.abs(this.getTimezoneOffset() / 60) < 10 ? "0" : "") + Math.abs(this.getTimezoneOffset() / 60) + "00"; 532 }, 533 P: function() { 534 return (-this.getTimezoneOffset() < 0 ? "-" : "+") + (Math.abs(this.getTimezoneOffset() / 60) < 10 ? "0" : "") + Math.abs(this.getTimezoneOffset() / 60) + ":00"; 535 }, 536 T: function() {
537 return this.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/, "$1"); 538 }, 539 Z: function() { 540 return -this.getTimezoneOffset() * 60; 541 }, 542 c: function() { 543 return this.format("Y-m-d\\TH:i:sP"); 544 }, 545 r: function() { 546 return this.toString(); 547 }, 548 U: function() { 549 return this.getTime() / 1e3; 550 } 551 }; 552 Date.prototype.format = function(format) { 553 var date = this; 554 return format.replace(/(\\?)(.)/g, function(_, esc, chr) { 555 return esc === "" && replaceChars[chr] ? replaceChars[chr].call(date) : chr; 556 }); 557 }; 558}).call(this); 559 560app.factory("dateutils", function() { 561 function validateDate(date) { 562 var d = new Date(Date.UTC(date.year, date.month, date.day)); 563 var result = d && (d.getMonth() === date.month && d.getDate() === Number(date.day)); 564 return result; 565 } 566 function changeDate(date) { 567 if (date.day > 28) { 568 date.day--; 569 return date; 570 } else if (date.month > 11) { 571 date.day = 31; 572 date.month--; 573 return date; 574 } 575 } 576 var self = this; 577 this.days = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 ]; 578 this.months = [ { 579 value: 0, 580 name: "January" 581 }, { 582 value: 1, 583 name: "February" 584 }, { 585 value: 2, 586 name: "March" 587 }, { 588 value: 3, 589 name: "April" 590 }, { 591 value: 4, 592 name: "May" 593 }, { 594 value: 5, 595 name: "June" 596 }, { 597 value: 6, 598 name: "July" 599 }, { 600 value: 7, 601 name: "August" 602 }, {
603 value: 8, 604 name: "September" 605 }, { 606 value: 9, 607 name: "October" 608 }, { 609 value: 10, 610 name: "November" 611 }, { 612 value: 11, 613 name: "December" 614 } ]; 615 return { 616 checkDate: function(date) { 617 if (!date.day) { 618 return false; 619 } 620 if (date.month === 0) {} else { 621 if (!date.month) { 622 return false; 623 } 624 } 625 if (!date.year) { 626 return false; 627 } 628 if (validateDate(date)) { 629 return date; 630 } else { 631 return this.checkDate(changeDate(date)); 632 } 633 }, 634 get: function(name) { 635 return self[name]; 636 } 637 }; 638}); 639 640app.directive("dobSelect", [ "dateutils", function(dateutils) { 641 return { 642 restrict: "E", 643 replace: true, 644 require: "ngModel", 645 scope: { 646 model: "=ngModel", 647 hideAge: "=?", 648 hideDates: "=?" 649 }, 650 controller: [ "$scope", "dateutils", function($scope, dateutils) { 651 $scope.days = dateutils.get("days"); 652 $scope.months = dateutils.get("months"); 653 if (!$scope.model) {} else { 654 if (!_.isDate($scope.model)) { 655 $scope.model = new Date($scope.model); 656 } 657 } 658 var dateFields = {}; 659 if ($scope.model) { 660 dateFields.day = new Date($scope.model).getDate(); 661 dateFields.month = new Date($scope.model).getMonth(); 662 dateFields.year = new Date($scope.model).getFullYear(); 663 $scope.dateFields = dateFields; 664 $scope.age = moment().diff($scope.model, "years"); 665 } 666 $scope.checkDate = function() { 667 var date = dateutils.checkDate($scope.dateFields); 668 if (date) { 669 $scope.dateFields = date; 670 } 671 }; 672 function dateFieldsChanged(newDate, oldDate) { 673 if (newDate == oldDate) { 674 return; 675 } 676 stopWatchingAge(); 677 if (!$scope.dateFields) { 678 return; 679 } 680 if (!$scope.dateFields.year) { 681 return; 682 } 683 if ($scope.dateFields.month === 0) {} else { 684 if (!$scope.dateFields.month) { 685 return; 686 } 687 } 688 if (!$scope.dateFields.day) { 689 return; 690 } 691 var date = new Date($scope.dateFields.year, $scope.dateFields.month, $scope.dateFields.day); 692 date.setHours(0, 0, 0, 0); 693 if (!$scope.model) { 694 $scope.model = new Date(); 695 } 696 $scope.model.setDate(date.getDate()); 697 $scope.model.setMonth(date.getMonth()); 698 $scope.model.setFullYear(date.getFullYear()); 699 var diff = moment().diff($scope.model, "years"); 700 $scope.age = parseInt(diff); 701 startWatchingAge(); 702 } 703 function ageChanged(newAge, oldAge) { 704 if (newAge == oldAge) { 705 return; 706 } 707 var today = new Date(); 708 today.setHours(0, 0, 0, 0); 709 var currentYear = today.getFullYear(); 710 var diff = parseInt(currentYear) - parseInt(newAge); 711 if (diff) { 712 stopWatchingDate(); 713 if (!$scope.dateFields) { 714 var dateFields = {}; 715 dateFields.year = diff; 716 $scope.dateFields = dateFields; 717 } else { 718 $scope.dateFields.year = diff; 719 } 720 startWatchingDate(); 721 } else {} 722 } 723 var ageWatcher; 724 function startWatchingAge() { 725 ageWatcher = $scope.$watch("age", ageChanged); 726 } 727 function stopWatchingAge() { 728 if (ageWatcher) { 729 ageWatcher(); 730 } 731 } 732 var dateWatcher; 733 function startWatchingDate() { 734 dateWatcher = $scope.$watch("dateFields", dateFieldsChanged, true); 735 } 736 function stopWatchingDate() { 737 if (dateWatcher) { 738 dateWatcher(); 739 } 740 } 741 startWatchingAge(); 742 startWatchingDate(); 743 } ], 744 templateUrl: "fluro-dob-select/fluro-dob-select.html", 745 link: function(scope, element, attrs, ctrl) { 746 if (attrs.yearText) { 747 scope.yearText = true; 748 } 749 if (attrs.dayDivClass) { 750 angular.element(element[0].children[0]).removeClass("form-group col-xs-3"); 751 angular.element(element[0].children[0]).addClass(attrs.dayDivClass); 752 } 753 if (attrs.dayClass) { 754 angular.element(element[0].children[0].children[0]).removeClass("form-control"); 755 angular.element(element[0].children[0].children[0]).addClass(attrs.dayClass); 756 } 757 if (attrs.monthDivClass) { 758 angular.element(element[0].children[1]).removeClass("form-group col-xs-5"); 759 angular.element(element[0].children[1]).addClass(attrs.monthDivClass); 760 } 761 if (attrs.monthClass) { 762 angular.element(element[0].children[1].children[0]).removeClass("form-control"); 763 angular.element(element[0].children[1].children[0]).addClass(attrs.monthClass); 764 } 765 if (attrs.yearDivClass) { 766 angular.element(element[0].children[2]).removeClass("form-group col-xs-4"); 767 angular.element(element[0].children[2]).addClass(attrs.yearDivClass); 768 } 769 if (attrs.yearClass) { 770 angular.element(element[0].children[2].children[0]).removeClass("form-control"); 771 angular.element(element[0].children[2].children[0]).addClass(attrs.yearClass); 772 } 773 var currentYear = parseInt(attrs.startingYear, 10) || new Date().getFullYear(); 774 var numYears = parseInt(attrs.numYears, 10) || 100; 775 var oldestYear = currentYear - numYears; 776 scope.years = []; 777 for (var i = currentYear; i >= oldestYear; i--) { 778 scope.years.push(i); 779 } 780 scope.$parent.$watch(attrs.ngDisabled, function(newVal) { 781 scope.disableFields = newVal; 782 }); 783 } 784 }; 785} ]); 786 787app.controller("FluroInteractionButtonSelectController", function($scope, FluroValidate) { 788 var to = $scope.to; 789 var opts = $scope.options; 790 $scope.selection = { 791 values: [], 792 value: null 793 }; 794 var definition = $scope.to.definition; 795 var minimum = definition.minimum; 796 var maximum = definition.maximum; 797 if (!minimum) { 798 minimum = 0; 799 } 800 if (!maximum) { 801 maximim = 0; 802 } 803 $scope.multiple = maximum != 1;
804 $scope.dragControlListeners = { 805 orderChanged: function(event) { 806 $scope.model[opts.key] = angular.copy($scope.selection.values); 807 } 808 }; 809 $scope.selectBox = {}; 810 $scope.selectUpdate = function() { 811 if (!$scope.selectBox.item) { 812 return; 813 } 814 $scope.selection.values.push($scope.selectBox.item); 815 $scope.model[opts.key] = angular.copy($scope.selection.values); 816 }; 817 $scope.canAddMore = function() { 818 if (!maximum) { 819 return true; 820 } 821 if ($scope.multiple) { 822 return $scope.selection.values.length < maximum; 823 } else { 824 if (!$scope.selection.value) { 825 return true; 826 } 827 } 828 }; 829 $scope.contains = function(value) { 830 if ($scope.multiple) { 831 return _.includes($scope.selection.values, value); 832 } else { 833 return $scope.selection.value == value; 834 } 835 }; 836 $scope.select = function(value) { 837 if ($scope.multiple) { 838 if (!$scope.canAddMore()) { 839 return; 840 } 841 $scope.selection.values.push(value); 842 } else { 843 $scope.selection.value = value; 844 } 845 }; 846 $scope.deselect = function(value) { 847 if ($scope.multiple) { 848 _.pull($scope.selection.values, value); 849 } else { 850 $scope.selection.value = null; 851 } 852 }; 853 $scope.toggle = function(reference) { 854 if ($scope.contains(reference)) { 855 $scope.deselect(reference); 856 } else { 857 $scope.select(reference); 858 } 859 setModel(); 860 }; 861 $scope.$watch("model", function(newModelValue, oldModelValue) { 862 if (newModelValue != oldModelValue) { 863 var modelValue; 864 if (_.keys(newModelValue).length) { 865 modelValue = newModelValue[opts.key]; 866 if ($scope.multiple) { 867 if (modelValue && _.isArray(modelValue)) { 868 $scope.selection.values = angular.copy(modelValue); 869 } else { 870 $scope.selection.values = []; 871 } 872 } else { 873 $scope.selection.value = angular.copy(modelValue); 874 } 875 } 876 } 877 }, true); 878 function checkValidity() { 879 var validRequired; 880 var validInput = FluroValidate.validate($scope.model[$scope.options.key], definition); 881 if ($scope.multiple) { 882 if ($scope.to.required) { 883 validRequired = _.isArray($scope.model[opts.key]) && $scope.model[opts.key].length > 0; 884 } 885 } else { 886 if ($scope.to.required) { 887 if ($scope.model[opts.key]) { 888 validRequired = true; 889 } 890 } 891 } 892 if ($scope.fc) { 893 $scope.fc.$setValidity("required", validRequired); 894 $scope.fc.$setValidity("validInput", validInput); 895 } 896 } 897 function setModel() { 898 if ($scope.multiple) { 899 $scope.model[opts.key] = angular.copy($scope.selection.values); 900 } else { 901 $scope.model[opts.key] = angular.copy($scope.selection.value); 902 } 903 if ($scope.fc) { 904 $scope.fc.$setTouched(); 905 } 906 checkValidity(); 907 } 908 if (opts.expressionProperties && opts.expressionProperties["templateOptions.required"]) { 909 $scope.$watch(function() { 910 return $scope.to.required; 911 }, function(newValue) { 912 checkValidity(); 913 }); 914 } 915 if ($scope.to.required) { 916 var unwatchFormControl = $scope.$watch("fc", function(newValue) { 917 if (!newValue) { 918 return; 919 } 920 checkValidity(); 921 unwatchFormControl(); 922 }); 923 } 924}); 925 926app.directive("fluroDateSelect", [ function() { 927 return { 928 restrict: "E", 929 replace: true, 930 scope: { 931 model: "=ngModel" 932 }, 933 templateUrl: "fluro-interaction-form/date-select/date-selector.html", 934 controller: [ "$scope", "dateutils", function($scope, dateutils) { 935 $scope.days = dateutils.get("days"); 936 $scope.months = dateutils.get("months"); 937 var dateFields = {}; 938 $scope.$watch("dateFields", dateFieldsChanged, true); 939 $scope.$watch("model", modelChanged); 940 function modelChanged() { 941 if ($scope.model) { 942 if (!_.isDate($scope.model)) { 943 $scope.model = new Date($scope.model); 944 } 945 dateFields.day = new Date($scope.model).getDate(); 946 dateFields.month = new Date($scope.model).getMonth(); 947 dateFields.year = new Date($scope.model).getFullYear(); 948 $scope.dateFields = dateFields; 949 } 950 } 951 function dateFieldsChanged(newDate, oldDate) { 952 if (newDate == oldDate) { 953 return; 954 } 955 if (!$scope.dateFields) { 956 return; 957 } 958 if (!$scope.dateFields.year) { 959 return; 960 } 961 if ($scope.dateFields.month === 0) {} else { 962 if (!$scope.dateFields.month) { 963 return; 964 } 965 } 966 if (!$scope.dateFields.day) { 967 return; 968 } 969 var date = new Date($scope.dateFields.year, $scope.dateFields.month, $scope.dateFields.day); 970 date.setHours(0, 0, 0, 0); 971 if (!$scope.model) { 972 $scope.model = new Date(); 973 } 974 $scope.model.setDate(date.getDate()); 975 $scope.model.setMonth(date.getMonth()); 976 $scope.model.setFullYear(date.getFullYear()); 977 } 978 $scope.checkDate = function() { 979 var date = dateutils.checkDate($scope.dateFields); 980 if (date) { 981 $scope.dateFields = date; 982 } 983 }; 984 } ] 985 }; 986} ]); 987 988app.run(function(formlyConfig, $templateCache) { 989 formlyConfig.setType({ 990 name: "dob-select", 991 overwriteOk: true, 992 templateUrl: "fluro-interaction-form/dob-select/fluro-dob-select.html", 993 wrapper: [ "bootstrapHasError" ] 994 }); 995}); 996 997app.run(function(formlyConfig, $templateCache) { 998 formlyConfig.setType({ 999 name: "embedded", 1000 overwriteOk: true, 1001 templateUrl: "fluro-interaction-form/embedded/fluro-embedded.html",
1002 controller: "FluroInteractionNestedController", 1003 wrapper: [ "bootstrapHasError" ] 1004 }); 1005}); 1006 1007app.directive("interactionForm", function($compile) { 1008 return { 1009 restrict: "E", 1010 replace: true, 1011 scope: { 1012 model: "=ngModel", 1013 integration: "=ngPaymentIntegration", 1014 vm: "=?config", 1015 callback: "=?callback" 1016 }, 1017 transclude: true, 1018 controller: "InteractionFormController", 1019 template: '<div class="fluro-interaction-form" ng-transclude-here />', 1020 link: function($scope, $element, $attrs, $ctrl, $transclude) { 1021 $transclude($scope, function(clone, $scope) { 1022 $element.find("[ng-transclude-here]").append(clone); 1023 }); 1024 } 1025 }; 1026}); 1027 1028app.directive("webform", function($compile) { 1029 return { 1030 restrict: "E", 1031 replace: true, 1032 scope: { 1033 model: "=ngModel", 1034 integration: "=ngPaymentIntegration", 1035 vm: "=?config", 1036 debugMode: "=?debugMode", 1037 callback: "=?callback", 1038 linkedEvent: "=?linkedEvent" 1039 }, 1040 transclude: true, 1041 controller: "InteractionFormController", 1042 templateUrl: "fluro-interaction-form/fluro-web-form.html", 1043 link: function($scope, $element, $attrs, $ctrl, $transclude) { 1044 $transclude($scope, function(clone, $scope) { 1045 $scope.transcludedContent = clone; 1046 }); 1047 } 1048 }; 1049}); 1050 1051app.config(function(formlyConfigProvider) { 1052 formlyConfigProvider.setType({ 1053 name: "currency", 1054 extends: "input", 1055 overwriteOk: true, 1056 controller: [ "$scope", function($scope) {} ], 1057 wrapper: [ "bootstrapLabel", "bootstrapHasError" ], 1058 defaultOptions: { 1059 ngModelAttrs: { 1060 currencyDirective: { 1061 attribute: "ng-currency" 1062 }, 1063 fractionValue: { 1064 attribute: "fraction", 1065 bound: "fraction" 1066 }, 1067 minimum: { 1068 attribute: "min", 1069 bound: "min" 1070 }, 1071 maximum: { 1072 attribute: "max", 1073 bound: "max" 1074 } 1075 }, 1076 templateOptions: { 1077 customAttrVal: "", 1078 required: true, 1079 fraction: 2 1080 }, 1081 validators: { 1082 validInput: { 1083 expression: function($viewValue, $modelValue, scope) { 1084 var numericValue = Number($modelValue); 1085 if (isNaN(numericValue)) { 1086 return false; 1087 } 1088 var minimumAmount = scope.options.data.minimumAmount; 1089 var maximumAmount = scope.options.data.maximumAmount; 1090 if (minimumAmount && numericValue < minimumAmount) { 1091 return false; 1092 } 1093 if (maximumAmount && numericValue > maximumAmount) { 1094 return false; 1095 } 1096 return true; 1097 } 1098 } 1099 } 1100 } 1101 }); 1102}); 1103 1104app.run(function(formlyConfig, $templateCache) { 1105 formlyConfig.templateManipulators.postWrapper.push(function(template, options, scope) { 1106 var fluroErrorTemplate = $templateCache.get("fluro-interaction-form/field-errors.html"); 1107 return "<div>" + template + fluroErrorTemplate + "</div>"; 1108 }); 1109 formlyConfig.setType({ 1110 name: "multiInput", 1111 overwriteOk: true, 1112 templateUrl: "fluro-interaction-form/multi.html", 1113 defaultOptions: { 1114 noFormControl: true, 1115 wrapper: [ "bootstrapLabel", "bootstrapHasError" ], 1116 templateOptions: { 1117 inputOptions: { 1118 wrapper: null 1119 } 1120 } 1121 }, 1122 controller: [ "$scope", function($scope) { 1123 $scope.copyItemOptions = copyItemOptions; 1124 function copyItemOptions() { 1125 return angular.copy($scope.to.inputOptions); 1126 } 1127 } ] 1128 }); 1129 formlyConfig.setType({ 1130 name: "payment", 1131 overwriteOk: true, 1132 templateUrl: "fluro-interaction-form/payment/payment.html", 1133 defaultOptions: { 1134 noFormControl: true 1135 } 1136 }); 1137 formlyConfig.setType({ 1138 name: "custom", 1139 overwriteOk: true, 1140 templateUrl: "fluro-interaction-form/custom.html", 1141 controller: "CustomInteractionFieldController", 1142 wrapper: [ "bootstrapHasError" ] 1143 }); 1144 formlyConfig.setType({ 1145 name: "button-select", 1146 overwriteOk: true, 1147 templateUrl: "fluro-interaction-form/button-select/fluro-button-select.html", 1148 controller: "FluroInteractionButtonSelectController", 1149 wrapper: [ "bootstrapLabel", "bootstrapHasError" ] 1150 }); 1151 formlyConfig.setType({ 1152 name: "date-select", 1153 overwriteOk: true, 1154 templateUrl: "fluro-interaction-form/date-select/fluro-date-select.html", 1155 wrapper: [ "bootstrapLabel", "bootstrapHasError" ] 1156 }); 1157 formlyConfig.setType({ 1158 name: "terms", 1159 overwriteOk: true, 1160 templateUrl: "fluro-interaction-form/fluro-terms.html", 1161 wrapper: [ "bootstrapLabel", "bootstrapHasError" ] 1162 }); 1163 formlyConfig.setType({ 1164 name: "order-select", 1165 overwriteOk: true, 1166 templateUrl: "fluro-interaction-form/order-select/fluro-order-select.html", 1167 controller: "FluroInteractionButtonSelectController", 1168 wrapper: [ "bootstrapLabel", "bootstrapHasError" ] 1169 }); 1170}); 1171 1172app.controller("CustomInteractionFieldController", function($scope, FluroValidate) { 1173 $scope.$watch("model[options.key]", function(value) { 1174 if (value) { 1175 if ($scope.fc) { 1176 $scope.fc.$setTouched(); 1177 } 1178 } 1179 }, true); 1180}); 1181 1182app.controller("InteractionFormController", function($scope, $q, $timeout, $rootScope, FluroAccess, $parse, $filter, formlyValidationMessages, FluroContent, FluroContentRetrieval, FluroValidate, FluroInteraction) { 1183 if (!$scope.vm) { 1184 $scope.vm = {}; 1185 } 1186 $formScope = $scope; 1187 function debugLog() { 1188 if ($scope.debugMode) { 1189 console.log(arguments); 1190 } 1191 } 1192 if ($scope.vm.defaultModel) { 1193 $scope.vm.model = angular.copy($scope.vm.defaultModel); 1194 } else { 1195 $scope.vm.model = {}; 1196 } 1197 $scope.vm.modelFields = []; 1198 $scope.vm.state = "ready"; 1199 $scope.correctPermissions = true; 1200 $scope.readyToSubmit = false;
1201 $scope.$watch("vm.modelForm.$invalid + vm.modelForm.$error", function() { 1202 var interactionForm = $scope.vm.modelForm; 1203 if (!interactionForm) { 1204 return $scope.readyToSubmit = false; 1205 } 1206 if (interactionForm.$invalid) { 1207 return $scope.readyToSubmit = false; 1208 } 1209 if (interactionForm.$error) { 1210 if (interactionForm.$error.required && interactionForm.$error.required.length) { 1211 return $scope.readyToSubmit = false; 1212 } 1213 if (interactionForm.$error.validInput && interactionForm.$error.validInput.length) { 1214 return $scope.readyToSubmit = false; 1215 } 1216 } 1217 $scope.readyToSubmit = true; 1218 }, true); 1219 formlyValidationMessages.addStringMessage("required", "This field is required"); 1220 formlyValidationMessages.messages.validInput = function($viewValue, $modelValue, scope) { 1221 return scope.to.label + " is not a valid value"; 1222 }; 1223 formlyValidationMessages.messages.date = function($viewValue, $modelValue, scope) { 1224 return scope.to.label + " is not a valid date"; 1225 }; 1226 $scope.reset = function() { 1227 if ($scope.vm.defaultModel) { 1228 $scope.vm.model = angular.copy($scope.vm.defaultModel); 1229 } else { 1230 $scope.vm.model = {}; 1231 } 1232 $scope.vm.modelForm.$setPristine(); 1233 $scope.vm.options.resetModel(); 1234 definitionModelChanged(); 1235 $scope.response = null; 1236 $scope.vm.state = "ready"; 1237 $scope.$broadcast("form-reset"); 1238 }; 1239 $scope.$watch("model", definitionModelChanged); 1240 function definitionModelChanged() { 1241 if (!$scope.model || $scope.model.parentType != "interaction") { 1242 return console.log("Model is not an interaction"); 1243 } 1244 if ($scope.vm.defaultModel) { 1245 $scope.vm.model = angular.copy($scope.vm.defaultModel); 1246 } else { 1247 $scope.vm.model = {}; 1248 } 1249 $scope.vm.modelFields = []; 1250 $scope.vm.state = "ready"; 1251 $scope.vm.onSubmit = submitInteraction; 1252 $scope.promises = []; 1253 $scope.submitLabel = "Submit"; 1254 if ($scope.model && $scope.model.data && $scope.model.data.submitLabel && $scope.model.data.submitLabel.length) { 1255 $scope.submitLabel = $scope.model.data.submitLabel; 1256 } 1257 var interactionFormSettings = $scope.model.data; 1258 if (!interactionFormSettings) { 1259 interactionFormSettings = {}; 1260 } 1261 if (!interactionFormSettings.allowAnonymous && !interactionFormSettings.disableDefaultFields) { 1262 interactionFormSettings.requireFirstName = true; 1263 interactionFormSettings.requireLastName = true; 1264 interactionFormSettings.requireGender = true; 1265 interactionFormSettings.requireEmail = true; 1266 switch (interactionFormSettings.identifier) { 1267 case "both": 1268 interactionFormSettings.requireEmail = interactionFormSettings.requirePhone = true; 1269 break; 1270 1271 case "email": 1272 interactionFormSettings.requireEmail = true; 1273 break; 1274 1275 case "phone": 1276 interactionFormSettings.requirePhone = true; 1277 break; 1278 1279 case "either": 1280 interactionFormSettings.askPhone = true; 1281 interactionFormSettings.askEmail = true; 1282 break; 1283 } 1284 } 1285 var firstNameField; 1286 var lastNameField; 1287 var genderField; 1288 if (interactionFormSettings.askGender || interactionFormSettings.requireGender) { 1289 genderField = { 1290 key: "_gender", 1291 type: "select", 1292 templateOptions: { 1293 type: "email", 1294 label: "Title", 1295 placeholder: "Please select a title", 1296 options: [ { 1297 name: "Mr", 1298 value: "male" 1299 }, { 1300 name: "Ms / Mrs", 1301 value: "female" 1302 } ], 1303 required: interactionFormSettings.requireGender, 1304 onBlur: "to.focused=false", 1305 onFocus: "to.focused=true" 1306 }, 1307 validators: { 1308 validInput: function($viewValue, $modelValue, scope) { 1309 var value = $modelValue || $viewValue; 1310 return value == "male" || value == "female"; 1311 } 1312 } 1313 }; 1314 } 1315 if (interactionFormSettings.askFirstName || interactionFormSettings.requireFirstName) { 1316 firstNameField = { 1317 key: "_firstName", 1318 type: "input", 1319 templateOptions: { 1320 type: "text", 1321 label: "First Name", 1322 placeholder: "Please enter your first name", 1323 required: interactionFormSettings.requireFirstName, 1324 onBlur: "to.focused=false", 1325 onFocus: "to.focused=true" 1326 } 1327 }; 1328 } 1329 if (interactionFormSettings.askLastName || interactionFormSettings.requireLastName) { 1330 lastNameField = { 1331 key: "_lastName", 1332 type: "input", 1333 templateOptions: { 1334 type: "text", 1335 label: "Last Name", 1336 placeholder: "Please enter your last name", 1337 required: interactionFormSettings.requireLastName, 1338 onBlur: "to.focused=false", 1339 onFocus: "to.focused=true" 1340 } 1341 }; 1342 } 1343 if (genderField && firstNameField && lastNameField) { 1344 genderField.className = "col-sm-2"; 1345 firstNameField.className = lastNameField.className = "col-sm-5"; 1346 $scope.vm.modelFields.push({ 1347 fieldGroup: [ genderField, firstNameField, lastNameField ], 1348 className: "row" 1349 }); 1350 } else if (firstNameField && lastNameField && !genderField) { 1351 firstNameField.className = lastNameField.className = "col-sm-6"; 1352 $scope.vm.modelFields.push({ 1353 fieldGroup: [ firstNameField, lastNameField ], 1354 className: "row" 1355 }); 1356 } else { 1357 if (genderField) { 1358 $scope.vm.modelFields.push(genderField); 1359 } 1360 if (firstNameField) { 1361 $scope.vm.modelFields.push(firstNameField); 1362 } 1363 if (lastNameField) { 1364 $scope.vm.modelFields.push(lastNameField); 1365 } 1366 } 1367 if (interactionFormSettings.askEmail || interactionFormSettings.requireEmail) { 1368 var newField = { 1369 key: "_email", 1370 type: "input", 1371 templateOptions: { 1372 type: "email", 1373 label: "Email Address", 1374 placeholder: "Please enter a valid email address", 1375 required: interactionFormSettings.requireEmail, 1376 onBlur: "to.focused=false", 1377 onFocus: "to.focused=true" 1378 }, 1379 validators: { 1380 validInput: function($viewValue, $modelValue, scope) { 1381 var value = $modelValue || $viewValue; 1382 return validator.isEmail(value); 1383 } 1384 } 1385 }; 1386 if (interactionFormSettings.identifier == "either") { 1387 newField.expressionProperties = { 1388 "templateOptions.required": function($viewValue, $modelValue, scope) { 1389 if (!scope.model._phoneNumber || !scope.model._phoneNumber.length) { 1390 return true; 1391 } else { 1392 return false;
1393 } 1394 } 1395 }; 1396 } 1397 $scope.vm.modelFields.push(newField); 1398 } 1399 if (interactionFormSettings.askPhone || interactionFormSettings.requirePhone) { 1400 var newField = { 1401 key: "_phoneNumber", 1402 type: "input", 1403 templateOptions: { 1404 type: "tel", 1405 label: "Contact Phone Number", 1406 placeholder: "Please enter a contact phone number", 1407 required: interactionFormSettings.requirePhone, 1408 onBlur: "to.focused=false", 1409 onFocus: "to.focused=true" 1410 } 1411 }; 1412 if (interactionFormSettings.identifier == "either") { 1413 newField.expressionProperties = { 1414 "templateOptions.required": function($viewValue, $modelValue, scope) { 1415 if (!scope.model._email || !scope.model._email.length) { 1416 return true; 1417 } else { 1418 return false; 1419 } 1420 } 1421 }; 1422 } 1423 $scope.vm.modelFields.push(newField); 1424 } 1425 if (interactionFormSettings.askDOB || interactionFormSettings.requireDOB) { 1426 var newField = { 1427 key: "_dob", 1428 type: "dob-select", 1429 templateOptions: { 1430 label: "Date of birth", 1431 placeholder: "Please provide your date of birth", 1432 required: interactionFormSettings.requireDOB, 1433 maxDate: new Date(), 1434 onBlur: "to.focused=false", 1435 onFocus: "to.focused=true" 1436 } 1437 }; 1438 $scope.vm.modelFields.push(newField); 1439 } 1440 function addFieldDefinition(array, fieldDefinition) { 1441 if (fieldDefinition.params && fieldDefinition.params.disableWebform) { 1442 return; 1443 } 1444 var newField = {}; 1445 newField.key = fieldDefinition.key; 1446 if (fieldDefinition.className) { 1447 newField.className = fieldDefinition.className; 1448 } 1449 var templateOptions = {}; 1450 templateOptions.type = "text"; 1451 templateOptions.label = fieldDefinition.title; 1452 templateOptions.description = fieldDefinition.description; 1453 templateOptions.params = fieldDefinition.params; 1454 if (fieldDefinition.errorMessage) { 1455 templateOptions.errorMessage = fieldDefinition.errorMessage; 1456 } 1457 templateOptions.definition = fieldDefinition; 1458 if (fieldDefinition.placeholder && fieldDefinition.placeholder.length) { 1459 templateOptions.placeholder = fieldDefinition.placeholder; 1460 } else if (fieldDefinition.description && fieldDefinition.description.length) { 1461 templateOptions.placeholder = fieldDefinition.description; 1462 } else { 1463 templateOptions.placeholder = fieldDefinition.title; 1464 } 1465 templateOptions.required = fieldDefinition.minimum > 0; 1466 templateOptions.onBlur = "to.focused=false"; 1467 templateOptions.onFocus = "to.focused=true"; 1468 switch (fieldDefinition.directive) { 1469 case "reference-select": 1470 case "value-select": 1471 newField.type = "button-select"; 1472 break; 1473 1474 case "select": 1475 newField.type = "select"; 1476 break; 1477 1478 case "wysiwyg": 1479 newField.type = "textarea"; 1480 break; 1481 1482 default: 1483 newField.type = fieldDefinition.directive; 1484 break; 1485 } 1486 switch (fieldDefinition.type) { 1487 case "reference": 1488 if (fieldDefinition.allowedReferences && fieldDefinition.allowedReferences.length) { 1489 templateOptions.options = _.map(fieldDefinition.allowedReferences, function(ref) { 1490 return { 1491 name: ref.title, 1492 value: ref._id 1493 }; 1494 }); 1495 } else { 1496 templateOptions.options = []; 1497 if (fieldDefinition.sourceQuery) { 1498 var queryId = fieldDefinition.sourceQuery; 1499 if (queryId._id) { 1500 queryId = queryId._id; 1501 } 1502 var options = {}; 1503 if (fieldDefinition.queryTemplate) { 1504 options.template = fieldDefinition.queryTemplate; 1505 if (options.template._id) { 1506 options.template = options.template._id; 1507 } 1508 } 1509 var promise = FluroContentRetrieval.getQuery(queryId, options); 1510 promise.then(function(res) { 1511 templateOptions.options = _.map(res, function(ref) { 1512 return { 1513 name: ref.title, 1514 value: ref._id 1515 }; 1516 }); 1517 }); 1518 } else { 1519 if (fieldDefinition.directive != "embedded") { 1520 if (fieldDefinition.params.restrictType && fieldDefinition.params.restrictType.length) { 1521 FluroContent.resource(fieldDefinition.params.restrictType).query().$promise.then(function(res) { 1522 templateOptions.options = _.map(res, function(ref) { 1523 return { 1524 name: ref.title, 1525 value: ref._id 1526 }; 1527 }); 1528 }); 1529 } 1530 } 1531 } 1532 } 1533 break; 1534 1535 default: 1536 if (fieldDefinition.options && fieldDefinition.options.length) { 1537 templateOptions.options = fieldDefinition.options; 1538 } else { 1539 templateOptions.options = _.map(fieldDefinition.allowedValues, function(val) { 1540 return { 1541 name: val, 1542 value: val 1543 }; 1544 }); 1545 } 1546 break; 1547 } 1548 if (fieldDefinition.attributes && _.keys(fieldDefinition.attributes).length) { 1549 newField.ngModelAttrs = _.reduce(fieldDefinition.attributes, function(results, attr, key) { 1550 var customKey = "customAttr" + key; 1551 results[customKey] = { 1552 attribute: key 1553 }; 1554 templateOptions[customKey] = attr; 1555 return results; 1556 }, {}); 1557 } 1558 if (fieldDefinition.directive != "custom") { 1559 switch (fieldDefinition.type) { 1560 case "boolean": 1561 if (fieldDefinition.params && fieldDefinition.params.storeCopy) { 1562 newField.type = "terms"; 1563 } else { 1564 newField.type = "checkbox"; 1565 } 1566 break; 1567 1568 case "number": 1569 case "float": 1570 case "integer": 1571 case "decimal": 1572 templateOptions.type = "input"; 1573 if (!newField.ngModelAttrs) { 1574 newField.ngModelAttrs = {}; 1575 } 1576 if (fieldDefinition.type == "integer") { 1577 templateOptions.type = "number"; 1578 templateOptions.baseDefaultValue = 0; 1579 newField.ngModelAttrs.customAttrpattern = { 1580 attribute: "pattern" 1581 }; 1582 newField.ngModelAttrs.customAttrinputmode = { 1583 attribute: "inputmode" 1584 }; 1585 templateOptions.customAttrpattern = "[0-9]*"; 1586 templateOptions.customAttrinputmode = "numeric"; 1587 if (fieldDefinition.params) { 1588 if (parseInt(fieldDefinition.params.maxValue) !== 0) { 1589 templateOptions.max = fieldDefinition.params.maxValue; 1590 } 1591 if (parseInt(fieldDefinition.params.minValue) !== 0) { 1592 templateOptions.min = fieldDefinition.params.minValue; 1593 } else { 1594 templateOptions.min = 0; 1595 } 1596 } 1597 } 1598 break; 1599 } 1600 } 1601 if (fieldDefinition.maximum == 1) { 1602 if (fieldDefinition.type == "reference" && fieldDefinition.directive != "embedded") { 1603 if (fieldDefinition.defaultReferences && fieldDefinition.defaultReferences.length) { 1604 if (fieldDefinition.directive == "search-select") { 1605 templateOptions.baseDefaultValue = fieldDefinition.defaultReferences[0]; 1606 } else { 1607 templateOptions.baseDefaultValue = fieldDefinition.defaultReferences[0]._id; 1608 } 1609 } 1610 } else { 1611 if (fieldDefinition.defaultValues && fieldDefinition.defaultValues.length) { 1612 if (templateOptions.type == "number") { 1613 templateOptions.baseDefaultValue = Number(fieldDefinition.defaultValues[0]); 1614 } else { 1615 templateOptions.baseDefaultValue = fieldDefinition.defaultValues[0]; 1616 } 1617 } 1618 } 1619 } else { 1620 if (fieldDefinition.type == "reference" && fieldDefinition.directive != "embedded") { 1621 if (fieldDefinition.defaultReferences && fieldDefinition.defaultReferences.length) { 1622 if (fieldDefinition.directive == "search-select") { 1623 templateOptions.baseDefaultValue = fieldDefinition.defaultReferences; 1624 } else { 1625 templateOptions.baseDefaultValue = _.map(fieldDefinition.defaultReferences, function(ref) { 1626 return ref._id; 1627 }); 1628 } 1629 } else { 1630 templateOptions.baseDefaultValue = []; 1631 } 1632 } else { 1633 if (fieldDefinition.defaultValues && fieldDefinition.defaultValues.length) { 1634 if (templateOptions.type == "number") { 1635 templateOptions.baseDefaultValue = _.map(fieldDefinition.defaultValues, function(val) { 1636 return Number(val); 1637 }); 1638 } else { 1639 templateOptions.baseDefaultValue = fieldDefinition.defaultValues; 1640 } 1641 } 1642 } 1643 } 1644 newField.templateOptions = templateOptions; 1645 newField.validators = { 1646 validInput: function($viewValue, $modelValue, scope) { 1647 var value = $modelValue || $viewValue; 1648 if (!value) { 1649 return true; 1650 }
1651 var valid = FluroValidate.validate(value, fieldDefinition); 1652 if (!valid) {} 1653 return valid; 1654 } 1655 }; 1656 if (fieldDefinition.directive == "embedded") { 1657 newField.type = "embedded"; 1658 if (fieldDefinition.maximum == 1 && fieldDefinition.minimum == 1) { 1659 templateOptions.baseDefaultValue = { 1660 data: {} 1661 }; 1662 } else { 1663 var askCount = 0; 1664 if (fieldDefinition.askCount) { 1665 askCount = fieldDefinition.askCount; 1666 } 1667 if (fieldDefinition.minimum && askCount < fieldDefinition.minimum) { 1668 askCount = fieldDefinition.minimum; 1669 } 1670 if (fieldDefinition.maximum && askCount > fieldDefinition.maximum) { 1671 askCount = fieldDefinition.maximum; 1672 } 1673 var initialArray = []; 1674 if (askCount) { 1675 _.times(askCount, function() { 1676 initialArray.push({}); 1677 }); 1678 } 1679 templateOptions.baseDefaultValue = initialArray; 1680 } 1681 newField.data = { 1682 fields: [], 1683 dataFields: [], 1684 replicatedFields: [] 1685 }; 1686 var fieldContainer = newField.data.fields; 1687 var dataFieldContainer = newField.data.dataFields; 1688 if (fieldDefinition.fields && fieldDefinition.fields.length) { 1689 _.each(fieldDefinition.fields, function(sub) { 1690 addFieldDefinition(fieldContainer, sub); 1691 }); 1692 } 1693 var promise = FluroContent.endpoint("defined/" + fieldDefinition.params.restrictType).get().$promise; 1694 promise.then(function(embeddedDefinition) { 1695 if (embeddedDefinition && embeddedDefinition.fields && embeddedDefinition.fields.length) { 1696 var childFields = embeddedDefinition.fields; 1697 if (fieldDefinition.params.excludeKeys && fieldDefinition.params.excludeKeys.length) { 1698 childFields = _.reject(childFields, function(f) { 1699 return _.includes(fieldDefinition.params.excludeKeys, f.key); 1700 }); 1701 } 1702 _.each(childFields, function(sub) { 1703 addFieldDefinition(dataFieldContainer, sub); 1704 }); 1705 } 1706 }); 1707 $scope.promises.push(promise); 1708 } 1709 if (fieldDefinition.type == "group" && fieldDefinition.fields && fieldDefinition.fields.length || fieldDefinition.asObject) { 1710 var fieldContainer; 1711 if (fieldDefinition.asObject) { 1712 newField.type = "nested"; 1713 if (fieldDefinition.key && fieldDefinition.maximum == 1 && fieldDefinition.minimum == 1) { 1714 templateOptions.baseDefaultValue = {}; 1715 } else { 1716 var askCount = 0; 1717 if (fieldDefinition.askCount) { 1718 askCount = fieldDefinition.askCount; 1719 } 1720 if (fieldDefinition.minimum && askCount < fieldDefinition.minimum) { 1721 askCount = fieldDefinition.minimum; 1722 } 1723 if (fieldDefinition.maximum && askCount > fieldDefinition.maximum) { 1724 askCount = fieldDefinition.maximum; 1725 } 1726 var initialArray = []; 1727 if (askCount) { 1728 _.times(askCount, function() { 1729 initialArray.push({}); 1730 }); 1731 } 1732 templateOptions.baseDefaultValue = initialArray; 1733 } 1734 newField.data = { 1735 fields: [], 1736 replicatedFields: [] 1737 }; 1738 fieldContainer = newField.data.fields; 1739 } else { 1740 newField = { 1741 fieldGroup: [], 1742 className: fieldDefinition.className 1743 }; 1744 fieldContainer = newField.fieldGroup; 1745 } 1746 _.each(fieldDefinition.fields, function(sub) { 1747 addFieldDefinition(fieldContainer, sub); 1748 }); 1749 } 1750 if (fieldDefinition.expressions && _.keys(fieldDefinition.expressions).length) { 1751 if (fieldDefinition.hideExpression && fieldDefinition.hideExpression.length) { 1752 fieldDefinition.expressions.hide = fieldDefinition.hideExpression; 1753 } 1754 var allExpressions = _.values(fieldDefinition.expressions).join("+"); 1755 newField.watcher = { 1756 expression: function(field, scope) { 1757 return $parse(allExpressions)(scope); 1758 }, 1759 listener: function(field, newValue, oldValue, scope, stopWatching) { 1760 var checkScope = { 1761 model: scope.model, 1762 data: $scope.vm.model, 1763 interaction: $scope.vm.model 1764 }; 1765 _.each(fieldDefinition.expressions, function(expression, key) { 1766 var retrievedValue = $parse(expression)(checkScope); 1767 debugLog("expression:", retrievedValue, checkScope); 1768 var fieldKey = field.key; 1769 switch (key) { 1770 case "defaultValue": 1771 if (!field.formControl || !field.formControl.$dirty) { 1772 return scope.model[fieldKey] = retrievedValue; 1773 } 1774 break; 1775 1776 case "value": 1777 return scope.model[fieldKey] = retrievedValue; 1778 break; 1779 1780 case "required": 1781 return field.templateOptions.required = retrievedValue; 1782 break; 1783 1784 case "hide": 1785 return field.hide = retrievedValue; 1786 break; 1787 } 1788 }); 1789 } 1790 }; 1791 } 1792 if (fieldDefinition.hideExpression && fieldDefinition.hideExpression.length) { 1793 newField.hideExpression = function($viewValue, $modelValue, scope) { 1794 var checkScope = { 1795 model: scope.model, 1796 data: $scope.vm.model, 1797 interaction: $scope.vm.model 1798 }; 1799 var parsedValue = $parse(fieldDefinition.hideExpression)(checkScope); 1800 return parsedValue; 1801 }; 1802 } 1803 if (!newField.fieldGroup) { 1804 newField.defaultValue = angular.copy(templateOptions.baseDefaultValue); 1805 } 1806 if (newField.type == "pathlink") { 1807 return; 1808 } 1809 array.push(newField); 1810 } 1811 _.each($scope.model.fields, function(fieldDefinition) { 1812 addFieldDefinition($scope.vm.modelFields, fieldDefinition); 1813 }); 1814 if (!$scope.model.paymentDetails) { 1815 $scope.model.paymentDetails = {}; 1816 } 1817 var paymentSettings = $scope.model.paymentDetails; 1818 if (paymentSettings.required || paymentSettings.allow) { 1819 var paymentWrapperFields = []; 1820 var paymentCardFields = []; 1821 if (paymentSettings.required) { 1822 debugLog("-- DEBUG -- Payment Settings Required"); 1823 paymentWrapperFields.push({ 1824 templateUrl: "fluro-interaction-form/payment/payment-summary.html", 1825 controller: [ "$scope", "$parse", function($scope, $parse) { 1826 $scope.paymentDetails = paymentSettings; 1827 var requiredAmount = paymentSettings.amount; 1828 $formScope.vm.model.calculatedTotal = requiredAmount; 1829 $scope.calculatedTotal = requiredAmount; 1830 var watchString = ""; 1831 var modelVariables = _.chain(paymentSettings.modifiers).map(function(paymentModifier) { 1832 var string = "(" + paymentModifier.expression + ") + (" + paymentModifier.condition + ")"; 1833 return string; 1834 }
1834).flatten().compact().uniq().value(); 1835 if (modelVariables.length) { 1836 watchString = modelVariables.join(' + "a" + '); 1837 } 1838 if (watchString.length) { 1839 debugLog("Watching changes", watchString); 1840 $scope.$watch(watchString, calculateTotal); 1841 } else { 1842 $scope.calculatedTotal = requiredAmount; 1843 $scope.modifications = []; 1844 } 1845 function calculateTotal(watchString) { 1846 debugLog("Recalculate total", watchString); 1847 $scope.calculatedTotal = requiredAmount; 1848 $scope.modifications = []; 1849 if (!paymentSettings.modifiers || !paymentSettings.modifiers.length) { 1850 debugLog("No payment modifiers set"); 1851 return; 1852 } 1853 _.each(paymentSettings.modifiers, function(modifier) { 1854 var parsedValue = $parse(modifier.expression)($scope); 1855 parsedValue = Number(parsedValue); 1856 if (isNaN(parsedValue)) { 1857 debugLog("Payment modifier error", modifier.title, parsedValue); 1858 return; 1859 } 1860 var parsedCondition = true; 1861 if (modifier.condition && String(modifier.condition).length) { 1862 parsedCondition = $parse(modifier.condition)($scope); 1863 } 1864 if (!parsedCondition) { 1865 debugLog("inactive", modifier.title, modifier, $scope); 1866 return; 1867 } 1868 var operator = ""; 1869 var operatingValue = "$" + parseFloat(parsedValue / 100).toFixed(2); 1870 switch (modifier.operation) { 1871 case "add": 1872 operator = "+"; 1873 $scope.calculatedTotal = $scope.calculatedTotal + parsedValue; 1874 break; 1875 1876 case "subtract": 1877 operator = "-"; 1878 $scope.calculatedTotal = $scope.calculatedTotal - parsedValue; 1879 break; 1880 1881 case "divide": 1882 operator = "÷"; 1883 operatingValue = parsedValue; 1884 $scope.calculatedTotal = $scope.calculatedTotal / parsedValue; 1885 break; 1886 1887 case "multiply": 1888 operator = "×"; 1889 operatingValue = parsedValue; 1890 $scope.calculatedTotal = $scope.calculatedTotal * parsedValue; 1891 break; 1892 1893 case "set": 1894 $scope.calculatedTotal = parsedValue; 1895 break; 1896 } 1897 $scope.modifications.push({ 1898 title: modifier.title, 1899 total: $scope.calculatedTotal, 1900 description: operator + " " + operatingValue, 1901 operation: modifier.operation 1902 }); 1903 }); 1904 if (!$scope.calculatedTotal || isNaN($scope.calculatedTotal) || $scope.calculatedTotal < 0) { 1905 $scope.calculatedTotal = 0; 1906 } 1907 $timeout(function() { 1908 $formScope.vm.model.calculatedTotal = $scope.calculatedTotal; 1909 }); 1910 } 1911 } ] 1912 }); 1913 } else { 1914 var amountDescription = "Please enter an amount (" + String(paymentSettings.currency).toUpperCase() + ")"; 1915 var minimum = paymentSettings.minAmount; 1916 var maximum = paymentSettings.maxAmount; 1917 var defaultAmount = paymentSettings.amount; 1918 var paymentErrorMessage = "Invalid amount"; 1919 if (minimum) { 1920 minimum = parseInt(minimum) / 100; 1921 paymentErrorMessage = "Amount must be a number at least " + $filter("currency")(minimum, "$"); 1922 amountDescription += "Enter at least " + $filter("currency")(minimum, "$") + " " + String(paymentSettings.currency).toUpperCase(); 1923 } 1924 if (maximum) { 1925 maximum = parseInt(maximum) / 100; 1926 paymentErrorMessage = "Amount must be a number less than " + $filter("currency")(maximum, "$"); 1927 amountDescription += "Enter up to " + $filter("currency")(maximum, "$") + " " + String(paymentSettings.currency).toUpperCase(); 1928 } 1929 if (minimum && maximum) { 1930 amountDescription = "Enter a numeric amount between " + $filter("currency")(minimum) + " and " + $filter("currency")(maximum) + " " + String(paymentSettings.currency).toUpperCase(); 1931 paymentErrorMessage = "Amount must be a number between " + $filter("currency")(minimum) + " and " + $filter("currency")(maximum); 1932 } 1933 var fieldConfig = { 1934 key: "_paymentAmount", 1935 type: "currency", 1936 templateOptions: { 1937 type: "text", 1938 label: "Amount", 1939 description: amountDescription, 1940 placeholder: "0.00", 1941 required: true, 1942 errorMessage: paymentErrorMessage, 1943 min: minimum, 1944 max: maximum, 1945 onBlur: "to.focused=false", 1946 onFocus: "to.focused=true" 1947 }, 1948 data: { 1949 customMaxLength: 8, 1950 minimumAmount: minimum, 1951 maximumAmount: maximum 1952 } 1953 }; 1954 if (minimum) { 1955 fieldConfig.defaultValue = minimum; 1956 } 1957 paymentWrapperFields.push({ 1958 template: "<hr/><h3>Payment Details</h3>" 1959 }); 1960 paymentWrapperFields.push(fieldConfig); 1961 } 1962 var defaultCardName; 1963 var defaultCardNumber; 1964 var defaultCardExpMonth; 1965 var defaultCardExpYear; 1966 var defaultCardCVN; 1967 if ($scope.debugMode) { 1968 defaultCardName = "John Citizen"; 1969 defaultCardNumber = "4242424242424242"; 1970 defaultCardExpMonth = "05"; 1971 defaultCardExpYear = "2020"; 1972 defaultCardCVN = "123"; 1973 } 1974 paymentCardFields.push({ 1975 key: "_paymentCardName", 1976 type: "input", 1977 defaultValue: defaultCardName, 1978 templateOptions: { 1979 type: "text", 1980 label: "Name on Card", 1981 placeholder: "eg. (John Smith)", 1982 required: paymentSettings.required, 1983 onBlur: "to.focused=false", 1984 onFocus: "to.focused=true" 1985 } 1986 }); 1987 paymentCardFields.push({ 1988 key: "_paymentCardNumber", 1989 type: "input", 1990 defaultValue: defaultCardNumber, 1991 templateOptions: { 1992 type: "text", 1993 label: "Card Number", 1994 placeholder: "Card Number (No dashes or spaces)", 1995 required: paymentSettings.required, 1996 onBlur: "to.focused=false", 1997 onFocus: "to.focused=true" 1998 }, 1999 validators: { 2000 validInput: function($viewValue, $modelValue, scope) { 2001 var luhnChk = function(a) { 2002 return function(c) { 2003 if (!c) { 2004 return false;
2005 } 2006 for (var l = c.length, b = 1, s = 0, v; l; ) v = parseInt(c.charAt(--l), 10), s += (b ^= 1) ? a[v] : v; 2007 return s && 0 === s % 10; 2008 }; 2009 }([ 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 ]); 2010 var value = $modelValue || $viewValue; 2011 var valid = luhnChk(value); 2012 return valid; 2013 } 2014 } 2015 }); 2016 paymentCardFields.push({ 2017 className: "row clearfix", 2018 fieldGroup: [ { 2019 key: "_paymentCardExpMonth", 2020 className: "col-xs-6 col-sm-5", 2021 type: "input", 2022 defaultValue: defaultCardExpMonth, 2023 templateOptions: { 2024 type: "text", 2025 label: "Expiry Month", 2026 placeholder: "MM", 2027 required: paymentSettings.required, 2028 onBlur: "to.focused=false", 2029 onFocus: "to.focused=true" 2030 } 2031 }, { 2032 key: "_paymentCardExpYear", 2033 className: "col-xs-6 col-sm-5", 2034 type: "input", 2035 defaultValue: defaultCardExpYear, 2036 templateOptions: { 2037 type: "text", 2038 label: "Expiry Year", 2039 placeholder: "YYYY", 2040 required: paymentSettings.required, 2041 onBlur: "to.focused=false", 2042 onFocus: "to.focused=true" 2043 } 2044 }, { 2045 key: "_paymentCardCVN", 2046 className: "col-xs-4 col-sm-2", 2047 type: "input", 2048 defaultValue: defaultCardCVN, 2049 templateOptions: { 2050 type: "text", 2051 label: "CVN", 2052 placeholder: "CVN", 2053 required: paymentSettings.required, 2054 onBlur: "to.focused=false", 2055 onFocus: "to.focused=true" 2056 } 2057 } ] 2058 }); 2059 var cardDetailsField = { 2060 className: "payment-details", 2061 fieldGroup: paymentCardFields, 2062 hideExpression: function($viewValue, $modelValue, scope) { 2063 if ($formScope.vm.model.calculatedTotal === 0) { 2064 return true; 2065 } 2066 } 2067 }; 2068 if (paymentSettings.allowAlternativePayments && paymentSettings.paymentMethods && paymentSettings.paymentMethods.length) { 2069 var methodSelection = { 2070 className: "payment-method-select", 2071 data: { 2072 fields: [ cardDetailsField ], 2073 settings: paymentSettings 2074 }, 2075 controller: [ "$scope", function($scope) { 2076 $scope.settings = paymentSettings; 2077 $scope.methodOptions = _.map(paymentSettings.paymentMethods, function(method) { 2078 return method; 2079 }); 2080 $scope.methodOptions.unshift({ 2081 title: "Pay with Card", 2082 key: "card" 2083 }); 2084 if (!$scope.model._paymentMethod) { 2085 $scope.model._paymentMethod = "card"; 2086 } 2087 $scope.selected = { 2088 method: $scope.methodOptions[0] 2089 }; 2090 $scope.selectMethod = function(method) { 2091 $scope.settings.showOptions = false; 2092 $scope.selected.method = method; 2093 $scope.model._paymentMethod = method.key; 2094 }; 2095 } ], 2096 templateUrl: "fluro-interaction-form/payment/payment-method.html", 2097 hideExpression: function($viewValue, $modelValue, scope) { 2098 if ($formScope.vm.model.calculatedTotal === 0) { 2099 return true; 2100 } 2101 } 2102 }; 2103 paymentWrapperFields.push(methodSelection); 2104 } else {
2105 paymentWrapperFields.push(cardDetailsField); 2106 } 2107 $scope.vm.modelFields.push({ 2108 fieldGroup: paymentWrapperFields 2109 }); 2110 } 2111 if (!$scope.promises.length) { 2112 console.log("NO PROMISES"); 2113 $scope.promisesResolved = true; 2114 } else { 2115 $scope.promisesResolved = false; 2116 $q.all($scope.promises).then(function() { 2117 console.log("Promises have been resolved"); 2118 $scope.promisesResolved = true; 2119 }); 2120 } 2121 } 2122 function getAllErrorFields(array) { 2123 return _.chain(array).map(function(field) { 2124 if (field.fieldGroup && field.fieldGroup.length) { 2125 return getAllErrorFields(field.fieldGroup); 2126 } else if (field.data && (field.data.fields && field.data.fields.length || field.data.dataFields && field.data.dataFields || field.data.replicatedFields && field.data.replicatedFields)) { 2127 var combined = []; 2128 combined = combined.concat(field.data.fields, field.data.dataFields, field.data.replicatedFields); 2129 combined = _.compact(combined); 2130 return getAllErrorFields(combined); 2131 } else { 2132 return field; 2133 } 2134 }).flatten().value(); 2135 } 2136 $scope.$watch("vm.modelFields", function(fields) { 2137 $scope.errorList = getAllErrorFields(fields); 2138 }, true); 2139 function submitInteraction() { 2140 $scope.vm.state = "sending"; 2141 var interactionKey = $scope.model.definitionName; 2142 var interactionDetails = angular.copy($scope.vm.model); 2143 var requiresPayment; 2144 var allowsPayment; 2145 var paymentConfiguration = $scope.model.paymentDetails; 2146 if (paymentConfiguration) { 2147 requiresPayment = paymentConfiguration.required; 2148 allowsPayment = paymentConfiguration.allow; 2149 } 2150 if (requiresPayment || allowsPayment) { 2151 var paymentDetails = {}; 2152 if (paymentConfiguration.allowAlternativePayments && paymentConfiguration.paymentMethods) { 2153 var selectedMethod = interactionDetails._paymentMethod; 2154 if (selectedMethod && selectedMethod != "card") { 2155 paymentDetails.method = selectedMethod; 2156 return processRequest(); 2157 } 2158 } 2159 if (requiresPayment) { 2160 if (!$formScope.vm.model.calculatedTotal) { 2161 return processRequest(); 2162 } 2163 } 2164 var paymentIntegration = $scope.integration; 2165 if (!paymentIntegration || !paymentIntegration.publicDetails) { 2166 alert("This form has not been configured properly. Please notify the administrator of this website immediately."); 2167 $scope.vm.state = "ready"; 2168 return; 2169 } 2170 paymentDetails.integration = paymentIntegration._id; 2171 switch (paymentIntegration.module) { 2172 case "eway": 2173 if (!window.eCrypt) { 2174 return $scope.vm.state = "ready"; 2175 } 2176 var key = paymentIntegration.publicDetails.publicKey; 2177 var cardDetails = {}; 2178 cardDetails.name = interactionDetails._paymentCardName; 2179 cardDetails.number = eCrypt.encryptValue(interactionDetails._paymentCardNumber, key); 2180 cardDetails.cvc = eCrypt.encryptValue(interactionDetails._paymentCardCVN, key); 2181 var expiryMonth = String(interactionDetails._paymentCardExpMonth); 2182 var expiryYear = String(interactionDetails._paymentCardExpYear); 2183 if (expiryMonth.length < 1) { 2184 expiryMonth = "0" + expiryMonth; 2185 } 2186 cardDetails.exp_month = expiryMonth; 2187 cardDetails.exp_year = expiryYear.slice(-2); 2188 paymentDetails.details = cardDetails; 2189 return processRequest(); 2190 break; 2191 2192 case "stripe": 2193 if (!window.Stripe) { 2194 return $scope.vm.state = "ready"; 2195 } 2196 var liveKey = paymentIntegration.publicDetails.livePublicKey; 2197 var sandboxKey = paymentIntegration.publicDetails.testPublicKey; 2198 var key = liveKey; 2199 if (paymentIntegration.publicDetails.sandbox) { 2200 key = sandboxKey; 2201 } 2202 Stripe.setPublishableKey(key);
2203 var cardDetails = {}; 2204 cardDetails.name = interactionDetails._paymentCardName; 2205 cardDetails.number = interactionDetails._paymentCardNumber; 2206 cardDetails.cvc = interactionDetails._paymentCardCVN; 2207 cardDetails.exp_month = interactionDetails._paymentCardExpMonth; 2208 cardDetails.exp_year = interactionDetails._paymentCardExpYear; 2209 Stripe.card.createToken(cardDetails, function(status, response) { 2210 if (response.error) { 2211 $scope.processErrorMessages = [ response.error.message ]; 2212 $scope.vm.state = "error"; 2213 } else { 2214 paymentDetails.details = response; 2215 return processRequest(); 2216 } 2217 }); 2218 break; 2219 } 2220 } else { 2221 return processRequest(); 2222 } 2223 function processRequest() { 2224 delete interactionDetails._paymentCardCVN; 2225 delete interactionDetails._paymentCardExpMonth; 2226 delete interactionDetails._paymentCardExpYear; 2227 delete interactionDetails._paymentCardName; 2228 delete interactionDetails._paymentCardNumber; 2229 if (interactionDetails._paymentAmount) { 2230 paymentDetails.amount = parseFloat(interactionDetails._paymentAmount) * 100; 2231 } 2232 var request = FluroInteraction.interact($scope.model.title, interactionKey, interactionDetails, paymentDetails, $scope.linkedEvent); 2233 request.then(submissionSuccess, submissionFail); 2234 function submissionSuccess(res) { 2235 if ($scope.vm.defaultModel) { 2236 $scope.vm.model = angular.copy($scope.vm.defaultModel); 2237 } else { 2238 $scope.vm.model = {}; 2239 } 2240 $scope.vm.modelForm.$setPristine(); 2241 $scope.vm.options.resetModel(); 2242 $formScope = $scope; 2243 $scope.response = res; 2244 $scope.vm.state = "complete"; 2245 } 2246 function submissionFail(res) { 2247 $scope.vm.state = "error"; 2248 if (!res.data) { 2249 return $scope.processErrorMessages = [ "Error: " + res ]; 2250 } 2251 if (res.data.error) { 2252 if (res.data.error.message) { 2253 return $scope.processErrorMessages = [ res.error.message ]; 2254 } else { 2255 return $scope.processErrorMessages = [ res.error ]; 2256 } 2257 } 2258 if (res.data.errors) { 2259 return $scope.processErrorMessages = _.map(res.data.errors, function(error) { 2260 return error.message; 2261 }); 2262 } 2263 if (_.isArray(res.data)) { 2264 return $scope.processErrorMessages = res.data; 2265 } else { 2266 $scope.processErrorMessages = [ res.data ]; 2267 } 2268 } 2269 } 2270 } 2271}); 2272 2273app.directive("postForm", function($compile) { 2274 return { 2275 restrict: "E", 2276 scope: { 2277 model: "=ngModel", 2278 host: "=hostId", 2279 reply: "=?reply", 2280 thread: "=?thread", 2281 userStore: "=?user", 2282 vm: "=?config", 2283 debugMode: "=?debugMode", 2284 callback: "=?callback" 2285 }, 2286 transclude: true, 2287 controller: "PostFormController", 2288 templateUrl: "fluro-interaction-form/fluro-web-form.html", 2289 link: function($scope, $element, $attrs, $ctrl, $transclude) { 2290 $transclude($scope, function(clone, $scope) { 2291 $scope.transcludedContent = clone; 2292 }); 2293 } 2294 }; 2295}); 2296 2297app.directive("recaptchaRender", function($window) { 2298 return { 2299 restrict: "A", 2300 link: function($scope, $element, $attrs, $ctrl) { 2301 if ($scope.model.data && $scope.model.data.recaptcha) { 2302 var element = $element[0]; 2303 var cancelWatch; 2304 if (window.grecaptcha) { 2305 activateRecaptcha(window.grecaptcha); 2306 } else { 2307 cancelWatch = $scope.$watch(function() { 2308 return window.grecaptcha; 2309 }, activateRecaptcha); 2310 } 2311 function activateRecaptcha(recaptcha) { 2312 if (cancelWatch) { 2313 cancelWatch(); 2314 } 2315 if (recaptcha) { 2316 $scope.vm.recaptchaID = recaptcha.render(element, { 2317 sitekey: "6LelOyUTAAAAADSACojokFPhb9AIzvrbGXyd-33z" 2318 }); 2319 } 2320 } 2321 } 2322 } 2323 }; 2324}); 2325 2326app.controller("PostFormController", function($scope, $rootScope, $q, $http, Fluro, FluroAccess, $parse, $filter, formlyValidationMessages, FluroContent, FluroContentRetrieval, FluroValidate, FluroInteraction) { 2327 if (!$scope.thread) { 2328 $scope.thread = []; 2329 } 2330 if (!$scope.vm) { 2331 $scope.vm = {}; 2332 } 2333 $scope.promisesResolved = true; 2334 $scope.correctPermissions = true; 2335 if ($scope.vm.defaultModel) { 2336 $scope.vm.model = angular.copy($scope.vm.defaultModel); 2337 } else { 2338 $scope.vm.model = {}; 2339 } 2340 $scope.vm.modelFields = []; 2341 $scope.vm.state = "ready"; 2342 $scope.readyToSubmit = false;
2343 $scope.$watch("vm.modelForm.$invalid + vm.modelForm.$error", function() { 2344 var interactionForm = $scope.vm.modelForm; 2345 if (!interactionForm) { 2346 return $scope.readyToSubmit = false; 2347 } 2348 if (interactionForm.$invalid) { 2349 return $scope.readyToSubmit = false; 2350 } 2351 if (interactionForm.$error) { 2352 if (interactionForm.$error.required && interactionForm.$error.required.length) { 2353 return $scope.readyToSubmit = false; 2354 } 2355 if (interactionForm.$error.validInput && interactionForm.$error.validInput.length) { 2356 return $scope.readyToSubmit = false; 2357 } 2358 } 2359 $scope.readyToSubmit = true; 2360 }, true); 2361 formlyValidationMessages.addStringMessage("required", "This field is required"); 2362 formlyValidationMessages.messages.validInput = function($viewValue, $modelValue, scope) { 2363 return scope.to.label + " is not a valid value"; 2364 }; 2365 formlyValidationMessages.messages.date = function($viewValue, $modelValue, scope) { 2366 return scope.to.label + " is not a valid date"; 2367 }; 2368 function resetCaptcha() { 2369 var recaptchaID = $scope.vm.recaptchaID; 2370 if (window.grecaptcha && recaptchaID) { 2371 window.grecaptcha.reset(recaptchaID); 2372 } 2373 } 2374 $scope.reset = function() { 2375 if ($scope.vm.defaultModel) { 2376 $scope.vm.model = angular.copy($scope.vm.defaultModel); 2377 } else { 2378 $scope.vm.model = {}; 2379 } 2380 $scope.vm.modelForm.$setPristine(); 2381 $scope.vm.options.resetModel(); 2382 resetCaptcha(); 2383 $scope.response = null; 2384 $scope.vm.state = "ready"; 2385 $scope.$broadcast("form-reset"); 2386 }; 2387 $scope.$watch("model", function(newData, oldData) { 2388 if (!$scope.model || $scope.model.parentType != "post") { 2389 return; 2390 } 2391 if ($scope.vm.defaultModel) { 2392 $scope.vm.model = angular.copy($scope.vm.defaultModel); 2393 } else { 2394 $scope.vm.model = {}; 2395 } 2396 $scope.vm.modelFields = []; 2397 $scope.vm.state = "ready"; 2398 $scope.vm.onSubmit = submitPost; 2399 $scope.promises = []; 2400 $scope.submitLabel = "Submit"; 2401 if ($scope.model && $scope.model.data && $scope.model.data.submitLabel && $scope.model.data.submitLabel.length) { 2402 $scope.submitLabel = $scope.model.data.submitLabel; 2403 } 2404 var interactionFormSettings = $scope.model.data; 2405 if (!interactionFormSettings) { 2406 interactionFormSettings = {}; 2407 } 2408 function addFieldDefinition(array, fieldDefinition) { 2409 if (fieldDefinition.params && fieldDefinition.params.disableWebform) { 2410 return; 2411 } 2412 var newField = {}; 2413 newField.key = fieldDefinition.key; 2414 if (fieldDefinition.className) { 2415 newField.className = fieldDefinition.className; 2416 } 2417 var templateOptions = {}; 2418 templateOptions.type = "text"; 2419 templateOptions.label = fieldDefinition.title; 2420 templateOptions.description = fieldDefinition.description; 2421 templateOptions.params = fieldDefinition.params; 2422 if (fieldDefinition.errorMessage) { 2423 templateOptions.errorMessage = fieldDefinition.errorMessage; 2424 } 2425 templateOptions.definition = fieldDefinition; 2426 if (fieldDefinition.placeholder && fieldDefinition.placeholder.length) { 2427 templateOptions.placeholder = fieldDefinition.placeholder; 2428 } else if (fieldDefinition.description && fieldDefinition.description.length) { 2429 templateOptions.placeholder = fieldDefinition.description; 2430 } else { 2431 templateOptions.placeholder = fieldDefinition.title; 2432 } 2433 templateOptions.required = fieldDefinition.minimum > 0; 2434 templateOptions.onBlur = "to.focused=false"; 2435 templateOptions.onFocus = "to.focused=true"; 2436 switch (fieldDefinition.directive) { 2437 case "reference-select": 2438 case "value-select": 2439 newField.type = "button-select"; 2440 break; 2441 2442 case "select": 2443 newField.type = "select"; 2444 break; 2445 2446 case "wysiwyg": 2447 newField.type = "textarea"; 2448 break; 2449 2450 default: 2451 newField.type = fieldDefinition.directive; 2452 break; 2453 } 2454 switch (fieldDefinition.type) { 2455 case "reference": 2456 if (fieldDefinition.allowedReferences && fieldDefinition.allowedReferences.length) { 2457 templateOptions.options = _.map(fieldDefinition.allowedReferences, function(ref) { 2458 return { 2459 name: ref.title, 2460 value: ref._id 2461 }; 2462 }); 2463 } else { 2464 templateOptions.options = []; 2465 if (fieldDefinition.sourceQuery) { 2466 var queryId = fieldDefinition.sourceQuery; 2467 if (queryId._id) { 2468 queryId = queryId._id; 2469 } 2470 var options = {}; 2471 if (fieldDefinition.queryTemplate) { 2472 options.template = fieldDefinition.queryTemplate; 2473 if (options.template._id) { 2474 options.template = options.template._id; 2475 } 2476 } 2477 var promise = FluroContentRetrieval.getQuery(queryId, options); 2478 promise.then(function(res) { 2479 templateOptions.options = _.map(res, function(ref) { 2480 return { 2481 name: ref.title, 2482 value: ref._id 2483 }; 2484 }); 2485 }); 2486 } else { 2487 if (fieldDefinition.directive != "embedded") { 2488 if (fieldDefinition.params.restrictType && fieldDefinition.params.restrictType.length) { 2489 FluroContent.resource(fieldDefinition.params.restrictType).query().$promise.then(function(res) { 2490 templateOptions.options = _.map(res, function(ref) { 2491 return { 2492 name: ref.title, 2493 value: ref._id 2494 }; 2495 }); 2496 }); 2497 } 2498 } 2499 } 2500 } 2501 break; 2502 2503 default: 2504 if (fieldDefinition.options && fieldDefinition.options.length) { 2505 templateOptions.options = fieldDefinition.options; 2506 } else { 2507 templateOptions.options = _.map(fieldDefinition.allowedValues, function(val) { 2508 return { 2509 name: val, 2510 value: val 2511 }; 2512 }); 2513 } 2514 break; 2515 } 2516 if (fieldDefinition.attributes && _.keys(fieldDefinition.attributes).length) { 2517 newField.ngModelAttrs = _.reduce(fieldDefinition.attributes, function(results, attr, key) { 2518 var customKey = "customAttr" + key; 2519 results[customKey] = { 2520 attribute: key 2521 }; 2522 templateOptions[customKey] = attr; 2523 return results; 2524 }, {}); 2525 } 2526 if (fieldDefinition.directive != "custom") { 2527 switch (fieldDefinition.type) { 2528 case "boolean": 2529 if (fieldDefinition.params && fieldDefinition.params.storeCopy) { 2530 newField.type = "terms"; 2531 } else { 2532 newField.type = "checkbox"; 2533 } 2534 break; 2535 2536 case "number": 2537 case "float": 2538 case "integer": 2539 case "decimal": 2540 templateOptions.type = "input"; 2541 if (!newField.ngModelAttrs) { 2542 newField.ngModelAttrs = {}; 2543 } 2544 if (fieldDefinition.type == "integer") { 2545 templateOptions.type = "number"; 2546 templateOptions.baseDefaultValue = 0; 2547 newField.ngModelAttrs.customAttrpattern = { 2548 attribute: "pattern" 2549 }; 2550 newField.ngModelAttrs.customAttrinputmode = { 2551 attribute: "inputmode" 2552 }; 2553 templateOptions.customAttrpattern = "[0-9]*"; 2554 templateOptions.customAttrinputmode = "numeric"; 2555 if (fieldDefinition.params) { 2556 if (parseInt(fieldDefinition.params.maxValue) !== 0) { 2557 templateOptions.max = fieldDefinition.params.maxValue; 2558 } 2559 if (parseInt(fieldDefinition.params.minValue) !== 0) { 2560 templateOptions.min = fieldDefinition.params.minValue; 2561 } else { 2562 templateOptions.min = 0; 2563 } 2564 } 2565 } 2566 break; 2567 } 2568 } 2569 if (fieldDefinition.maximum == 1) { 2570 if (fieldDefinition.type == "reference" && fieldDefinition.directive != "embedded") { 2571 if (fieldDefinition.defaultReferences && fieldDefinition.defaultReferences.length) { 2572 if (fieldDefinition.directive == "search-select") { 2573 templateOptions.baseDefaultValue = fieldDefinition.defaultReferences[0]; 2574 } else { 2575 templateOptions.baseDefaultValue = fieldDefinition.defaultReferences[0]._id; 2576 } 2577 } 2578 } else { 2579 if (fieldDefinition.defaultValues && fieldDefinition.defaultValues.length) { 2580 if (templateOptions.type == "number") { 2581 templateOptions.baseDefaultValue = Number(fieldDefinition.defaultValues[0]); 2582 } else { 2583 templateOptions.baseDefaultValue = fieldDefinition.defaultValues[0]; 2584 } 2585 } 2586 } 2587 } else { 2588 if (fieldDefinition.type == "reference" && fieldDefinition.directive != "embedded") { 2589 if (fieldDefinition.defaultReferences && fieldDefinition.defaultReferences.length) { 2590 if (fieldDefinition.directive == "search-select") { 2591 templateOptions.baseDefaultValue = fieldDefinition.defaultReferences; 2592 } else { 2593 templateOptions.baseDefaultValue = _.map(fieldDefinition.defaultReferences, function(ref) { 2594 return ref._id; 2595 }); 2596 } 2597 } else { 2598 templateOptions.baseDefaultValue = []; 2599 } 2600 } else { 2601 if (fieldDefinition.defaultValues && fieldDefinition.defaultValues.length) { 2602 if (templateOptions.type == "number") { 2603 templateOptions.baseDefaultValue = _.map(fieldDefinition.defaultValues, function(val) { 2604 return Number(val); 2605 }); 2606 } else { 2607 templateOptions.baseDefaultValue = fieldDefinition.defaultValues; 2608 } 2609 } 2610 } 2611 } 2612 newField.templateOptions = templateOptions; 2613 newField.validators = { 2614 validInput: function($viewValue, $modelValue, scope) { 2615 var value = $modelValue || $viewValue; 2616 if (!value) { 2617 return true; 2618 }
2619 var valid = FluroValidate.validate(value, fieldDefinition); 2620 if (!valid) {} 2621 return valid; 2622 } 2623 }; 2624 if (fieldDefinition.directive == "embedded") { 2625 newField.type = "embedded"; 2626 if (fieldDefinition.maximum == 1 && fieldDefinition.minimum == 1) { 2627 templateOptions.baseDefaultValue = { 2628 data: {} 2629 }; 2630 } else { 2631 var askCount = 0; 2632 if (fieldDefinition.askCount) { 2633 askCount = fieldDefinition.askCount; 2634 } 2635 if (fieldDefinition.minimum && askCount < fieldDefinition.minimum) { 2636 askCount = fieldDefinition.minimum; 2637 } 2638 if (fieldDefinition.maximum && askCount > fieldDefinition.maximum) { 2639 askCount = fieldDefinition.maximum; 2640 } 2641 var initialArray = []; 2642 if (askCount) { 2643 _.times(askCount, function() { 2644 initialArray.push({}); 2645 }); 2646 } 2647 templateOptions.baseDefaultValue = initialArray; 2648 } 2649 newField.data = { 2650 fields: [], 2651 dataFields: [], 2652 replicatedFields: [] 2653 }; 2654 var fieldContainer = newField.data.fields; 2655 var dataFieldContainer = newField.data.dataFields; 2656 if (fieldDefinition.fields && fieldDefinition.fields.length) { 2657 _.each(fieldDefinition.fields, function(sub) { 2658 addFieldDefinition(fieldContainer, sub); 2659 }); 2660 } 2661 var promise = FluroContent.endpoint("defined/" + fieldDefinition.params.restrictType).get().$promise; 2662 promise.then(function(embeddedDefinition) { 2663 if (embeddedDefinition && embeddedDefinition.fields && embeddedDefinition.fields.length) { 2664 var childFields = embeddedDefinition.fields; 2665 if (fieldDefinition.params.excludeKeys && fieldDefinition.params.excludeKeys.length) { 2666 childFields = _.reject(childFields, function(f) { 2667 return _.includes(fieldDefinition.params.excludeKeys, f.key); 2668 }); 2669 } 2670 _.each(childFields, function(sub) { 2671 addFieldDefinition(dataFieldContainer, sub); 2672 }); 2673 } 2674 }); 2675 $scope.promises.push(promise); 2676 } 2677 if (fieldDefinition.type == "group" && fieldDefinition.fields && fieldDefinition.fields.length || fieldDefinition.asObject) { 2678 var fieldContainer; 2679 if (fieldDefinition.asObject) { 2680 newField.type = "nested"; 2681 if (fieldDefinition.key && fieldDefinition.maximum == 1 && fieldDefinition.minimum == 1) { 2682 templateOptions.baseDefaultValue = {}; 2683 } else { 2684 var askCount = 0; 2685 if (fieldDefinition.askCount) { 2686 askCount = fieldDefinition.askCount; 2687 } 2688 if (fieldDefinition.minimum && askCount < fieldDefinition.minimum) { 2689 askCount = fieldDefinition.minimum; 2690 } 2691 if (fieldDefinition.maximum && askCount > fieldDefinition.maximum) { 2692 askCount = fieldDefinition.maximum; 2693 } 2694 var initialArray = []; 2695 if (askCount) { 2696 _.times(askCount, function() { 2697 initialArray.push({}); 2698 }); 2699 } 2700 templateOptions.baseDefaultValue = initialArray; 2701 } 2702 newField.data = { 2703 fields: [], 2704 replicatedFields: [] 2705 }; 2706 fieldContainer = newField.data.fields; 2707 } else { 2708 newField = { 2709 fieldGroup: [], 2710 className: fieldDefinition.className 2711 }; 2712 fieldContainer = newField.fieldGroup; 2713 } 2714 _.each(fieldDefinition.fields, function(sub) { 2715 addFieldDefinition(fieldContainer, sub); 2716 }); 2717 } 2718 if (fieldDefinition.expressions && _.keys(fieldDefinition.expressions).length) { 2719 if (fieldDefinition.hideExpression && fieldDefinition.hideExpression.length) { 2720 fieldDefinition.expressions.hide = fieldDefinition.hideExpression; 2721 } 2722 var allExpressions = _.values(fieldDefinition.expressions).join("+"); 2723 newField.watcher = { 2724 expression: function(field, scope) { 2725 return $parse(allExpressions)(scope); 2726 }, 2727 listener: function(field, newValue, oldValue, scope, stopWatching) { 2728 if (!scope.interaction) { 2729 scope.interaction = $scope.vm.model; 2730 } 2731 _.each(fieldDefinition.expressions, function(expression, key) { 2732 var retrievedValue = $parse(expression)(scope); 2733 var fieldKey = field.key; 2734 switch (key) { 2735 case "defaultValue": 2736 if (!field.formControl || !field.formControl.$dirty) { 2737 return scope.model[fieldKey] = retrievedValue; 2738 } 2739 break; 2740 2741 case "value": 2742 return scope.model[fieldKey] = retrievedValue; 2743 break; 2744 2745 case "required": 2746 return field.templateOptions.required = retrievedValue; 2747 break; 2748 2749 case "hide": 2750 return field.hide = retrievedValue; 2751 break; 2752 } 2753 }); 2754 } 2755 }; 2756 } 2757 if (fieldDefinition.hideExpression) { 2758 newField.hideExpression = fieldDefinition.hideExpression; 2759 } 2760 if (!newField.fieldGroup) { 2761 newField.defaultValue = angular.copy(templateOptions.baseDefaultValue); 2762 } 2763 if (newField.type == "pathlink") { 2764 return; 2765 } 2766 array.push(newField); 2767 } 2768 _.each($scope.model.fields, function(fieldDefinition) { 2769 addFieldDefinition($scope.vm.modelFields, fieldDefinition); 2770 }); 2771 }); 2772 function getAllErrorFields(array) { 2773 return _.chain(array).map(function(field) { 2774 if (field.fieldGroup && field.fieldGroup.length) { 2775 return getAllErrorFields(field.fieldGroup); 2776 } else if (field.data && (field.data.fields && field.data.fields.length || field.data.dataFields && field.data.dataFields || field.data.replicatedFields && field.data.replicatedFields)) { 2777 var combined = []; 2778 combined = combined.concat(field.data.fields, field.data.dataFields, field.data.replicatedFields); 2779 combined = _.compact(combined); 2780 return getAllErrorFields(combined); 2781 } else { 2782 return field; 2783 } 2784 }
2784).flatten().value(); 2785 } 2786 $scope.$watch("vm.modelFields", function(fields) { 2787 $scope.errorList = getAllErrorFields(fields); 2788 }, true); 2789 function submitPost() { 2790 $scope.vm.state = "sending"; 2791 var submissionKey = $scope.model.definitionName; 2792 var submissionModel = { 2793 data: angular.copy($scope.vm.model) 2794 }; 2795 var hostID = $scope.host; 2796 if ($scope.reply) { 2797 submissionModel.reply = $scope.reply; 2798 } 2799 if (typeof $scope.vm.recaptchaID !== "undefined") { 2800 var response = window.grecaptcha.getResponse($scope.vm.recaptchaID); 2801 submissionModel["g-recaptcha-response"] = response; 2802 } 2803 var request; 2804 if ($scope.userStore) { 2805 $scope.userStore.config().then(function(config) { 2806 var postURL = Fluro.apiURL + "/post/" + hostID + "/" + submissionKey; 2807 request = $http.post(postURL, submissionModel, config); 2808 request.then(function(res) { 2809 return submissionSuccess(res.data); 2810 }, function(res) { 2811 return submissionFail(res.data); 2812 }); 2813 }); 2814 } else { 2815 request = FluroContent.endpoint("post/" + hostID + "/" + submissionKey).save(submissionModel).$promise; 2816 request.then(submissionSuccess, submissionFail); 2817 } 2818 function submissionSuccess(res) { 2819 if ($scope.vm.defaultModel) { 2820 $scope.vm.model = angular.copy($scope.vm.defaultModel); 2821 } else { 2822 $scope.vm.model = { 2823 data: {} 2824 }; 2825 } 2826 $scope.vm.modelForm.$setPristine(); 2827 $scope.vm.options.resetModel(); 2828 resetCaptcha(); 2829 $scope.response = res; 2830 if ($scope.thread) { 2831 $scope.thread.push(res); 2832 } 2833 $scope.vm.state = "complete"; 2834 } 2835 function submissionFail(res) { 2836 $scope.vm.state = "error"; 2837 if (!res.data) { 2838 return $scope.processErrorMessages = [ "Error: " + res ]; 2839 } 2840 if (res.data.error) { 2841 if (res.data.error.message) { 2842 return $scope.processErrorMessages = [ res.error.message ]; 2843 } else { 2844 return $scope.processErrorMessages = [ res.error ]; 2845 } 2846 } 2847 if (res.data.errors) { 2848 return $scope.processErrorMessages = _.map(res.data.errors, function(error) { 2849 return error.message; 2850 }); 2851 } 2852 if (_.isArray(res.data)) { 2853 return $scope.processErrorMessages = res.data; 2854 } else { 2855 $scope.processErrorMessages = [ res.data ]; 2856 } 2857 } 2858 } 2859}); 2860 2861app.directive("postThread", function(FluroContent) { 2862 return { 2863 restrict: "E", 2864 transclude: true, 2865 scope: { 2866 definitionName: "=?type", 2867 host: "=?hostId", 2868 thread: "=?thread" 2869 }, 2870 link: function($scope, $element, $attrs, $ctrl, $transclude) { 2871 $transclude($scope, function(clone, $scope) { 2872 $element.replaceWith(clone); 2873 }); 2874 }, 2875 controller: function($scope, $filter) { 2876 $scope.outer = $scope.$parent; 2877 if (!$scope.thread) { 2878 $scope.thread = []; 2879 } 2880 $scope.$watch("host + definitionName", function() { 2881 var hostID = $scope.host; 2882 var definitionName = $scope.definitionName; 2883 if (!hostID || !definitionName) { 2884 return; 2885 } 2886 var request = FluroContent.endpoint("post/" + hostID + "/" + definitionName).query().$promise; 2887 function postsLoaded(res) { 2888 var allPosts = res; 2889 $scope.thread = _.chain(res).map(function(post) { 2890 post.thread = _.filter(allPosts, function(sub) { 2891 return sub.reply == post._id; 2892 }); 2893 if (!post.reply) { 2894 return post; 2895 } 2896 }
2896).compact().value(); 2897 } 2898 function postsError(res) { 2899 $scope.thread = []; 2900 } 2901 request.then(postsLoaded, postsError); 2902 }); 2903 } 2904 }; 2905}); 2906 2907app.run(function(formlyConfig, $templateCache) { 2908 formlyConfig.setType({ 2909 name: "nested", 2910 overwriteOk: true, 2911 templateUrl: "fluro-interaction-form/nested/fluro-nested.html", 2912 controller: "FluroInteractionNestedController" 2913 }); 2914}); 2915 2916app.controller("FluroInteractionNestedController", function($scope) { 2917 var def = $scope.to.definition; 2918 var minimum = def.minimum; 2919 var maximum = def.maximum; 2920 $scope.$watch("model[options.key]", function(model) { 2921 if (!model) { 2922 resetDefaultValue(); 2923 } 2924 }); 2925 function resetDefaultValue() { 2926 var defaultValue = angular.copy($scope.to.baseDefaultValue); 2927 if (!$scope.model) {} 2928 $scope.model[$scope.options.key] = defaultValue; 2929 } 2930 $scope.$on("form-reset", resetDefaultValue); 2931 $scope.addAnother = function() { 2932 $scope.model[$scope.options.key].push({}); 2933 }; 2934 $scope.canRemove = function() { 2935 if (minimum) { 2936 if ($scope.model[$scope.options.key].length > minimum) { 2937 return true; 2938 } 2939 } else { 2940 return true; 2941 } 2942 }; 2943 $scope.canAdd = function() { 2944 if (maximum) { 2945 if ($scope.model[$scope.options.key].length < maximum) { 2946 return true; 2947 } 2948 } else { 2949 return true; 2950 } 2951 }; 2952 $scope.copyFields = function() { 2953 var copiedFields = angular.copy($scope.options.data.fields); 2954 $scope.options.data.replicatedFields.push(copiedFields); 2955 return copiedFields; 2956 }; 2957 $scope.copyDataFields = function() { 2958 var copiedFields = angular.copy($scope.options.data.dataFields); 2959 $scope.options.data.replicatedFields.push(copiedFields); 2960 return copiedFields; 2961 }; 2962}); 2963 2964app.run(function(formlyConfig, $templateCache) { 2965 formlyConfig.setType({ 2966 name: "search-select", 2967 overwriteOk: true, 2968 templateUrl: "fluro-interaction-form/search-select/fluro-search-select.html", 2969 controller: "FluroSearchSelectController", 2970 wrapper: [ "bootstrapLabel", "bootstrapHasError" ] 2971 }); 2972}); 2973 2974app.controller("FluroSearchSelectController", function($scope, $http, Fluro, $filter, FluroValidate) { 2975 $scope.search = {}; 2976 $scope.proposed = {}; 2977 var to = $scope.to; 2978 var opts = $scope.options; 2979 $scope.selection = {}; 2980 var definition = $scope.to.definition; 2981 if (!definition.params) { 2982 definition.params = {}; 2983 } 2984 var restrictType = definition.params.restrictType; 2985 var searchLimit = definition.params.searchLimit; 2986 if (!searchLimit) { 2987 searchLimit = 10; 2988 } 2989 var minimum = definition.minimum; 2990 var maximum = definition.maximum; 2991 if (!minimum) { 2992 minimum = 0; 2993 } 2994 if (!maximum) { 2995 maximim = 0; 2996 } 2997 $scope.multiple = maximum != 1; 2998 if ($scope.multiple) { 2999 if ($scope.model[opts.key] && _.isArray($scope.model[opts.key])) { 3000 $scope.selection.values = angular.copy($scope.model[opts.key]); 3001 } 3002 } else { 3003 if ($scope.model[opts.key]) { 3004 $scope.selection.value = $scope.model[opts.key]; 3005 } 3006 } 3007 $scope.canAddMore = function() { 3008 if (!maximum) { 3009 return true; 3010 } 3011 if ($scope.multiple) { 3012 return $scope.selection.values.length < maximum; 3013 } else { 3014 if (!$scope.selection.value) { 3015 return true; 3016 } 3017 } 3018 }; 3019 $scope.contains = function(value) { 3020 if ($scope.multiple) { 3021 return _.includes($scope.selection.values, value); 3022 } else { 3023 return $scope.selection.value == value; 3024 } 3025 }; 3026 $scope.$watch("model", function(newModelValue, oldModelValue) { 3027 if (newModelValue != oldModelValue) { 3028 var modelValue; 3029 if (_.keys(newModelValue).length) { 3030 modelValue = newModelValue[opts.key]; 3031 if ($scope.multiple) { 3032 if (modelValue && _.isArray(modelValue)) { 3033 $scope.selection.values = angular.copy(modelValue); 3034 } else { 3035 $scope.selection.values = []; 3036 } 3037 } else { 3038 $scope.selection.value = angular.copy(modelValue); 3039 } 3040 } 3041 } 3042 }, true); 3043 function setModel() { 3044 if ($scope.multiple) { 3045 $scope.model[opts.key] = angular.copy($scope.selection.values); 3046 } else { 3047 $scope.model[opts.key] = angular.copy($scope.selection.value); 3048 } 3049 if ($scope.fc) { 3050 $scope.fc.$setTouched(); 3051 } 3052 checkValidity(); 3053 } 3054 if (opts.expressionProperties && opts.expressionProperties["templateOptions.required"]) { 3055 $scope.$watch(function() { 3056 return $scope.to.required; 3057 }, function(newValue) { 3058 checkValidity(); 3059 }); 3060 } 3061 if ($scope.to.required) { 3062 var unwatchFormControl = $scope.$watch("fc", function(newValue) { 3063 if (!newValue) { 3064 return; 3065 } 3066 checkValidity(); 3067 unwatchFormControl(); 3068 }); 3069 } 3070 function checkValidity() {
3071 var validRequired; 3072 var validInput = FluroValidate.validate($scope.model[$scope.options.key], definition); 3073 if ($scope.multiple) { 3074 if ($scope.to.required) { 3075 validRequired = _.isArray($scope.model[opts.key]) && $scope.model[opts.key].length > 0; 3076 } 3077 } else { 3078 if ($scope.to.required) { 3079 if ($scope.model[opts.key]) { 3080 validRequired = true; 3081 } 3082 } 3083 } 3084 if ($scope.fc) { 3085 $scope.fc.$setValidity("required", validRequired); 3086 $scope.fc.$setValidity("validInput", validInput); 3087 } 3088 } 3089 $scope.select = function(value) { 3090 if ($scope.multiple) { 3091 if (!$scope.canAddMore()) { 3092 return; 3093 } 3094 $scope.selection.values.push(value); 3095 } else { 3096 $scope.selection.value = value; 3097 } 3098 $scope.proposed = {}; 3099 setModel(); 3100 }; 3101 $scope.retrieveReferenceOptions = function(val) { 3102 var searchUrl = Fluro.apiURL + "/content"; 3103 if (restrictType) { 3104 searchUrl += "/" + restrictType; 3105 } 3106 searchUrl += "/search"; 3107 return $http.get(searchUrl + "/" + val, { 3108 ignoreLoadingBar: true, 3109 params: { 3110 limit: searchLimit 3111 } 3112 }).then(function(response) { 3113 var results = response.data; 3114 return _.reduce(results, function(filtered, item) { 3115 var exists = _.some($scope.selection.values, { 3116 _id: item._id 3117 }); 3118 if (!exists) { 3119 filtered.push(item); 3120 } 3121 return filtered; 3122 }, []); 3123 }); 3124 }; 3125 $scope.getValueLabel = function(value) { 3126 if (definition.options && definition.options.length) { 3127 var match = _.find(definition.options, { 3128 value: value 3129 }); 3130 if (match && match.name) { 3131 return match.name; 3132 } 3133 } 3134 return value; 3135 }; 3136 $scope.retrieveValueOptions = function(val) { 3137 if (definition.options && definition.options.length) { 3138 var options = _.reduce(definition.options, function(results, item) { 3139 var exists; 3140 if ($scope.multiple) { 3141 exists = _.includes($scope.selection.values, item.value); 3142 } else { 3143 exists = $scope.selection.value == item.value; 3144 } 3145 if (!exists) { 3146 results.push({ 3147 name: item.name, 3148 value: item.value 3149 }); 3150 } 3151 return results; 3152 }, []); 3153 return $filter("filter")(options, val); 3154 } else if (definition.allowedValues && definition.allowedValues.length) { 3155 var options = _.reduce(definition.allowedValues, function(results, allowedValue) { 3156 var exists; 3157 if ($scope.multiple) { 3158 exists = _.includes($scope.selection.values, allowedValue); 3159 } else { 3160 exists = $scope.selection.value == allowedValue; 3161 } 3162 if (!exists) { 3163 results.push({ 3164 name: allowedValue, 3165 value: allowedValue 3166 }); 3167 } 3168 return results; 3169 }, []); 3170 return $filter("filter")(options, val); 3171 } 3172 }; 3173 $scope.deselect = function(value) { 3174 if ($scope.multiple) { 3175 _.pull($scope.selection.values, value); 3176 } else { 3177 delete $scope.selection.value; 3178 } 3179 setModel(); 3180 }; 3181 $scope.toggle = function(reference) { 3182 if ($scope.contains(reference)) { 3183 $scope.deselect(reference); 3184 } else { 3185 $scope.select(reference); 3186 } 3187 }; 3188}); 3189 3190app.run(function(formlyConfig, $templateCache) { 3191 formlyConfig.setType({ 3192 name: "value", 3193 overwriteOk: true, 3194 templateUrl: "fluro-interaction-form/value/value.html", 3195 wrapper: [ "bootstrapHasError" ] 3196 }); 3197}); 3198 3199app.service("FluroMenuService", function($rootScope) { 3200 var controller = {}; 3201 controller.get = function(string, id) { 3202 var menu; 3203 var result; 3204 if ($rootScope.site) { 3205 menu = _.find($rootScope.site.menus, { 3206 key: string 3207 }); 3208 } 3209 if (menu && id) { 3210 result = _.find(menu.items, { 3211 id: id 3212 }); 3213 } else { 3214 result = menu; 3215 } 3216 return result; 3217 }; 3218 return controller; 3219}); 3220 3221app.directive("scrollActive", function($compile, $timeout, $window, FluroScrollService) { 3222 return { 3223 restrict: "A", 3224 link: function($scope, $element, $attrs) { 3225 var onActive; 3226 var onBefore; 3227 var onAfter; 3228 var onAnchor; 3229 var currentContext = ""; 3230 var anchored; 3231 if ($attrs.onActive) { 3232 onActive = function() { 3233 $scope.$eval($attrs.onActive); 3234 }; 3235 } 3236 if ($attrs.onAnchor) { 3237 onAnchor = function() { 3238 $scope.$eval($attrs.onAnchor); 3239 }; 3240 } 3241 if ($attrs.onAfter) { 3242 onAfter = function() { 3243 $scope.$eval($attrs.onAfter); 3244 }; 3245 } 3246 if ($attrs.onBefore) { 3247 onBefore = function() { 3248 $scope.$eval($attrs.onBefore); 3249 }; 3250 } 3251 var parent = $element.closest("[scroll-active-parent]"); 3252 var body = angular.element("body"); 3253 if (parent.length) { 3254 parent.bind("scroll", updateParentScroll); 3255 $timeout(updateParentScroll, 10); 3256 } else { 3257 $scope.$watch(function() { 3258 return FluroScrollService.getScroll(); 3259 }, updateFromMainScroll); 3260 $timeout(updateFromMainScroll, 10); 3261 }
3262 function setScrollContext(context) { 3263 if (currentContext != context) { 3264 currentContext = context; 3265 $timeout(function() { 3266 switch (context) { 3267 case "active": 3268 $element.removeClass("scroll-after"); 3269 $element.removeClass("scroll-before"); 3270 $element.addClass("scroll-active"); 3271 $scope.scrollActive = true; 3272 $scope.scrollBefore = false; 3273 $scope.scrollAfter = false; 3274 if (onActive) { 3275 onActive(); 3276 } 3277 break; 3278 3279 case "before": 3280 $element.removeClass("scroll-after"); 3281 $element.addClass("scroll-before"); 3282 $element.removeClass("scroll-active"); 3283 $scope.scrollActive = false; 3284 $scope.scrollBefore = true; 3285 $scope.scrollAfter = false; 3286 if (onBefore) { 3287 onBefore(); 3288 } 3289 break; 3290 3291 case "after": 3292 $element.addClass("scroll-after"); 3293 $element.removeClass("scroll-before"); 3294 $element.removeClass("scroll-active"); 3295 $scope.scrollActive = false; 3296 $scope.scrollBefore = false; 3297 $scope.scrollAfter = true; 3298 if (onAfter) { 3299 onAfter(); 3300 } 3301 break; 3302 } 3303 }); 3304 } 3305 } 3306 function updateParentScroll() { 3307 var scrollValue = parent.scrollTop(); 3308 var viewportHeight = parent.height(); 3309 var contentHeight = parent.get(0).scrollHeight; 3310 var viewportHalf = viewportHeight / 2; 3311 var maxScroll = contentHeight - viewportHeight; 3312 var startView = 0; 3313 var endView = startView + viewportHeight; 3314 var halfView = endView - viewportHeight / 2; 3315 var elementHeight = $element.outerHeight(); 3316 var elementStart = $element.position().top; 3317 var elementEnd = elementStart + elementHeight; 3318 var elementHalf = elementStart + elementHeight / 4; 3319 if (onAnchor) { 3320 var start = parseInt(startView); 3321 var rangeStart = parseInt(elementStart); 3322 var rangeEnd = parseInt(elementHalf); 3323 if (start >= rangeStart && start < rangeEnd) { 3324 if (!anchored) { 3325 anchored = true; 3326 if (anchored) { 3327 onAnchor(); 3328 } 3329 } 3330 } else { 3331 anchored = false; 3332 } 3333 } 3334 var entirelyViewable = elementStart >= startView && elementEnd <= endView; 3335 if (entirelyViewable) { 3336 return setScrollContext("active"); 3337 } 3338 if (halfView >= elementEnd) { 3339 return setScrollContext("after"); 3340 } 3341 if (halfView >= elementStart) { 3342 return setScrollContext("active"); 3343 } 3344 if (startView >= maxScroll - 200) { 3345 return setScrollContext("active"); 3346 } 3347 return setScrollContext("before"); 3348 } 3349 function updateFromMainScroll(scrollValue) { 3350 var windowHeight = $window.innerHeight; 3351 var documentHeight = body.height(); 3352 var windowHalf = windowHeight / 2; 3353 var maxScroll = documentHeight - windowHeight; 3354 var startView = scrollValue; 3355 if (!startView) { 3356 startView = 0; 3357 } 3358 var endView = startView + windowHeight; 3359 var halfView = endView - windowHeight / 2; 3360 var elementHeight = $element.outerHeight(); 3361 var elementStart = $element.offset().top; 3362 var elementEnd = elementStart + elementHeight; 3363 var elementHalf = elementStart + elementHeight / 4; 3364 if (onAnchor) { 3365 var start = parseInt(startView); 3366 var rangeStart = parseInt(elementStart); 3367 var rangeEnd = parseInt(elementHalf); 3368 if (start >= rangeStart && start < rangeEnd) { 3369 if (!anchored) { 3370 anchored = true; 3371 if (anchored) { 3372 onAnchor(); 3373 } 3374 } 3375 } else { 3376 anchored = false;
3377 } 3378 } 3379 var entirelyViewable = elementStart >= startView && elementEnd <= endView; 3380 if (entirelyViewable) { 3381 return setScrollContext("active"); 3382 } 3383 if (halfView >= elementEnd) { 3384 return setScrollContext("after"); 3385 } 3386 if (halfView >= elementStart) { 3387 return setScrollContext("active"); 3388 } 3389 if (startView >= maxScroll - 200) { 3390 return setScrollContext("active"); 3391 } 3392 return setScrollContext("before"); 3393 } 3394 } 3395 }; 3396}); 3397 3398app.service("FluroScrollService", function($window, $location, $timeout) { 3399 var controller = {}; 3400 controller.cache = {}; 3401 controller.direction = "down"; 3402 var _value = 0; 3403 var body = angular.element("html,body"); 3404 controller.setAnchor = function(id) { 3405 $location.hash("jump-to-" + id); 3406 }; 3407 controller.getAnchor = function() { 3408 var hash = $location.hash(); 3409 if (_.startsWith(hash, "jump-to-")) { 3410 return hash.substring(8); 3411 } else { 3412 return hash; 3413 } 3414 }; 3415 function updateScroll() { 3416 var v = this.pageYOffset; 3417 if (_value != this.pageYOffset) { 3418 if (v < _value) { 3419 controller.direction = "up"; 3420 } else { 3421 controller.direction = "down"; 3422 } 3423 $timeout(function() { 3424 _value = this.pageYOffset; 3425 }); 3426 } 3427 } 3428 controller.scrollToID = controller.scrollToId = function(id, speed, selector, offset) { 3429 if (speed != 0 && !speed) { 3430 speed = "fast"; 3431 } 3432 var $target = angular.element("#" + id); 3433 if ($target && $target.offset && $target.offset()) { 3434 if (!selector) { 3435 selector = "body,html"; 3436 } 3437 var pos = $target.offset().top; 3438 if (offset) { 3439 pos += Number(offset); 3440 } 3441 angular.element(selector).animate({ 3442 scrollTop: pos 3443 }, speed); 3444 } 3445 }; 3446 controller.scrollToPosition = controller.scrollTo = function(pos, speed, selector, offset) { 3447 if (speed != 0 && !speed) { 3448 speed = "fast"; 3449 } 3450 if (!selector) { 3451 selector = "body,html"; 3452 } 3453 if (offset) { 3454 pos += Number(offset); 3455 } 3456 angular.element(selector).animate({ 3457 scrollTop: pos 3458 }, speed); 3459 }; 3460 controller.get = controller.getScroll = function() { 3461 return _value; 3462 }; 3463 controller.getMax = function(selector) { 3464 if (!selector) { 3465 selector = "body,html"; 3466 } 3467 var bodyHeight = angular.element(selector).height(); 3468 var windowHeight = $window.innerHeight; 3469 return bodyHeight - windowHeight; 3470 }; 3471 controller.getHalfPoint = function() { 3472 return $window.innerHeight / 2; 3473 }; 3474 controller.getWindowHeight = function() { 3475 return $window.innerHeight; 3476 }; 3477 angular.element($window).bind("scroll", updateScroll); 3478 updateScroll(); 3479 return controller; 3480}); 3481 3482angular.module("fluro").service("FluroSEO", function($state, $rootScope, $location, Asset) { 3483 var controller = {}; 3484 controller.headTitle = function() { 3485 var siteTitle = controller.siteTitle(); 3486 var pageTitle = controller.pageTitle(); 3487 if (pageTitle && pageTitle.length) { 3488 return pageTitle + " | " + siteTitle; 3489 } 3490 return siteTitle; 3491 }; 3492 controller.siteTitle = function() { 3493 var site = $rootScope.site; 3494 var _siteTitle = site.title; 3495 if (site.seo) { 3496 if (site.seo.title && site.seo.title.length) { 3497 _siteTitle = site.seo.title; 3498 } 3499 } 3500 return _siteTitle; 3501 }; 3502 controller.getImage = function() { 3503 var site = $rootScope.site; 3504 var page = $rootScope.currentRoute; 3505 var slug = $rootScope.slug; 3506 var imageId; 3507 if (site.seo) { 3508 if (site.seo.image) { 3509 imageId = site.seo.image; 3510 } 3511 } 3512 if (page && page.seo) { 3513 if (page.seo.image) { 3514 imageId = page.seo.image; 3515 } 3516 if (slug) { 3517 if (page.seo.slugImage && page.seo.slugImage.length) { 3518 var key = page.seo.slugImage; 3519 var imageObject = _.get(slug, key); 3520 if (_.isArray(imageObject)) { 3521 imageObject = imageObject[0]; 3522 } 3523 if (imageObject) { 3524 if (imageObject._id) { 3525 imageId = imageObject._id; 3526 } else { 3527 imageId = imageObject; 3528 } 3529 } 3530 } 3531 } 3532 } 3533 if (imageId) { 3534 var imageURL = Asset.imageUrl(imageId, 940, 492, { 3535 extension: "jpg" 3536 }); 3537 return imageURL; 3538 } 3539 }; 3540 controller.getUrl = function() { 3541 return $location.$$absUrl; 3542 }; 3543 controller.pageTitle = function() { 3544 var pageTitle; 3545 var page = $rootScope.currentRoute; 3546 var slug = $rootScope.slug; 3547 if (page) { 3548 pageTitle = page.title; 3549 if (page.seo) { 3550 if (page.seo.title && page.seo.title.length) { 3551 pageTitle = page.seo.title; 3552 } 3553 if (slug) { 3554 if (page.seo.slugTitle && page.seo.slugTitle.length) { 3555 var key = page.seo.slugTitle; 3556 pageTitle = _.get(slug, key); 3557 } 3558 } 3559 } 3560 } 3561 return pageTitle; 3562 }; 3563 controller.metaDescription = function() { 3564 var site = $rootScope.site; 3565 var page = $rootScope.currentRoute; 3566 var slug = $rootScope.slug; 3567 var description = ""; 3568 if (site.seo) { 3569 if (site.seo.description && site.seo.description.length) { 3570 description = site.seo.description; 3571 } 3572 } 3573 if (page && page.seo) { 3574 if (page.seo.description && page.seo.description.length) { 3575 description = page.seo.description; 3576 } 3577 if (slug) { 3578 if (page.seo.slugDescription && page.seo.slugDescription.length) { 3579 var key = page.seo.slugDescription; 3580 description = _.get(slug, key); 3581 } 3582 } 3583 } 3584 return description; 3585 }; 3586 return controller; 3587}); 3588 3589app.service("FluroWindowService", function($window, $timeout) { 3590 var controller = {}; 3591 controller.breakpoint = "xs"; 3592 function updateSize() { 3593 controller.width = $window.innerWidth; 3594 controller.height = $window.innerHeight; 3595 if (controller.width < 768) { 3596 return controller.breakpoint = "xs"; 3597 } 3598 if (controller.width < 992) { 3599 return controller.breakpoint = "sm"; 3600 } 3601 if (controller.width < 1200) { 3602 return controller.breakpoint = "md"; 3603 } 3604 return controller.breakpoint = "lg"; 3605 } 3606 angular.element($window).bind("resize", updateSize); 3607 updateSize(); 3608 return controller; 3609}); 3610 3611app.directive("compileHtml", function($compile) { 3612 return { 3613 restrict: "A", 3614 link: function(scope, element, attrs) { 3615 scope.$watch(function() { 3616 return scope.$eval(attrs.compileHtml); 3617 }, function(value) { 3618 element.html(value); 3619 $compile(element.contents())(scope); 3620 }); 3621 } 3622 }; 3623}); 3624 3625app.filter("age", function() { 3626 return function(date) { 3627 return Number(moment().diff(date, "years")); 3628 }; 3629}); 3630 3631app.filter("kebabcase", function() { 3632 return function(text) { 3633 return _.kebabCase(text); 3634 }; 3635}); 3636 3637app.filter("startFrom", function() { 3638 return function(input, start) { 3639 if (input) { 3640 start = +start; 3641 return input.slice(start); 3642 } 3643 return []; 3644 }; 3645}); 3646 3647app.filter("timeago", function() { 3648 return function(date) { 3649 return moment(date).fromNow(); 3650 }; 3651}); 3652 3653app.directive("route", function($compile) { 3654 return { 3655 restrict: "E", 3656 replace: true, 3657 scope: { 3658 route: "=ngModel", 3659 slug: "=ngSlug", 3660 _injectedItems: "=ngItems", 3661 _injectedResults: "=ngResults", 3662 _injectedCollections: "=ngCollections", 3663 _injectedPageData: "=ngPage" 3664 }, 3665 template: "<div></div>", 3666 controller: function($scope, $rootScope, $stateParams, $controller) { 3667 this.items = $scope._injectedItems; 3668 this.results = $scope._injectedResults; 3669 this.collections = $scope._injectedCollections; 3670 this.slug = $scope.slug || $rootScope.slug;
3671 this.route = $scope.route; 3672 if (!$scope._injectedPageData) { 3673 return; 3674 } 3675 var routeModel = $scope.route; 3676 var populatedItems = $scope._injectedItems; 3677 var extraPageInjectedData = $scope._injectedPageData; 3678 if (routeModel.params) { 3679 $scope.params = routeModel.params; 3680 } else { 3681 $scope.params = {}; 3682 } 3683 $scope.queryResults = []; 3684 $scope.results = _.reduce(routeModel.queries, function(result, queryId) { 3685 var array = extraPageInjectedData.results[queryId]; 3686 $scope.queryResults.push(array); 3687 if (array && array.length) { 3688 _.each(array, function(item) { 3689 result.push(item); 3690 }); 3691 } 3692 return result; 3693 }, []); 3694 if (routeModel.content) { 3695 $scope.items = _.chain(routeModel.content).map(function(id) { 3696 var populated = _.find(populatedItems, { 3697 _id: id 3698 }); 3699 if (!populated) {} 3700 return populated; 3701 }).compact().value(); 3702 } else { 3703 $scope.items = []; 3704 } 3705 if (routeModel.keys) { 3706 $scope.content = _.reduce(routeModel.keys, function(result, keyGroup) { 3707 result[keyGroup.key] = _.chain(keyGroup.content).map(function(nid) { 3708 return _.find(populatedItems, { 3709 _id: nid 3710 }); 3711 }).compact().value(); 3712 return result; 3713 }, {}); 3714 } else { 3715 $scope.content = {}; 3716 } 3717 if (routeModel.collections) { 3718 $scope.collections = _.map(routeModel.collections, function(collectionId) { 3719 return _.find(extraPageInjectedData.collections, { 3720 _id: collectionId 3721 }); 3722 }); 3723 $scope.collectionResults = _.chain(extraPageInjectedData.collections).map(function(collection) { 3724 return collection.items; 3725 }).flatten().uniq().value(); 3726 } else { 3727 $scope.collections = []; 3728 $scope.collectionResults = []; 3729 } 3730 this.page = $scope; 3731 if (routeModel.controllerName && routeModel.controllerName.length) { 3732 $controller(routeModel.controllerName, { 3733 $scope: $scope 3734 }); 3735 } 3736 }, 3737 link: function($scope, $element, $attr) { 3738 var routeModel = $scope.route; 3739 var template = '<route-section ng-model="section" ng-repeat="section in route.sections"></route-section>'; 3740 if (routeModel) { 3741 if (routeModel.html && routeModel.html.length) { 3742 template = routeModel.html; 3743 } 3744 } 3745 var compiled = $compile(template)($scope); 3746 $element.append(compiled); 3747 } 3748 }; 3749}); 3750 3751app.directive("routeSection", function($compile, $templateCache, $rootScope, $timeout, FluroContentRetrieval) { 3752 return { 3753 require: "^route", 3754 restrict: "E", 3755 replace: true, 3756 scope: { 3757 model: "=ngModel" 3758 }, 3759 template: "<section></section>", 3760 link: function($scope, $element, $attr, $routeController) { 3761 var populatedItems = $routeController.items; 3762 $scope.route = $routeController.route; 3763 $scope.page = $routeController.page; 3764 if ($routeController.slug) { 3765 $scope.slug = $routeController.slug; 3766 } 3767 if ($routeController.results) { 3768 $scope.queryResults = []; 3769 $scope.results = _.reduce($scope.model.queries, function(result, queryId) { 3770 var array = $routeController.results[queryId]; 3771 $scope.queryResults.push(array); 3772 if (array && array.length) { 3773 _.each(array, function(item) { 3774 result.push(item); 3775 }); 3776 } 3777 return result; 3778 }, []); 3779 } 3780 $scope.items = _.chain($scope.model.content).map(function(id) { 3781 var populated = _.find(populatedItems, { 3782 _id: id 3783 }); 3784 if (!populated) {} 3785 return populated; 3786 }
3786).compact().value(); 3787 $scope.content = _.reduce($scope.model.keys, function(result, keyGroup) { 3788 result[keyGroup.key] = _.chain(keyGroup.content).map(function(nid) { 3789 return _.find(populatedItems, { 3790 _id: nid 3791 }); 3792 }).compact().value(); 3793 return result; 3794 }, {}); 3795 if ($routeController.collections) { 3796 $scope.collections = _.map($scope.model.collections, function(collectionId) { 3797 return _.find($routeController.collections, { 3798 _id: collectionId 3799 }); 3800 }); 3801 $scope.collectionResults = _.chain($scope.collections).compact().map(function(collection) { 3802 return collection.items; 3803 }).flatten().uniq().value(); 3804 } 3805 if ($scope.model) { 3806 $scope.params = $scope.model.params; 3807 $scope.model.temp = {}; 3808 var html; 3809 if ($scope.model.template && $scope.model.template.length) { 3810 html = $templateCache.get($scope.model.template); 3811 } else { 3812 html = $scope.model.html; 3813 } 3814 if (html && html.length) { 3815 $element.append(html); 3816 $compile($element.contents())($scope); 3817 } 3818 } 3819 } 3820 }; 3821}); 3822 3823app.directive("site", function($compile, FluroContentRetrieval) { 3824 return { 3825 restrict: "E", 3826 replace: true, 3827 template: '<div class="site"/>', 3828 link: function($scope, $element) { 3829 var outerSections = []; 3830 var site = $scope.site; 3831 var siteTemplate = ""; 3832 if (site.outerHTML && site.outerHTML.length) { 3833 siteTemplate = site.outerHTML; 3834 siteTemplate = siteTemplate.replace(/<fluro-header\/>/g, '<route class="site-header" ng-if="!$root.currentRoute.disableHeader && outerContentReady()" ng-model="site.header" ng-items="items" ng-results="results" ng-slug="slug" ng-collections="collections"/>'); 3835 siteTemplate = siteTemplate.replace(/<fluro-content\/>/g, '<div class="site-page-container"><div ui-view="$site"/></div>'); 3836 siteTemplate = siteTemplate.replace(/<fluro-footer\/>/g, '<route class="site-footer" ng-if="!$root.currentRoute.disableFooter && outerContentReady()" ng-model="site.footer" ng-items="items" ng-results="results" ng-slug="slug" ng-collections="collections"/>'); 3837 } else { 3838 siteTemplate = '<route class="site-header" ng-if="!$root.currentRoute.disableHeader && outerContentReady()" ng-model="site.header" ng-items="items" ng-results="results" ng-slug="slug" ng-collections="collections"/>'; 3839 siteTemplate += '<div class="site-page-container"><div ui-view="$site"/></div>'; 3840 siteTemplate += '<route class="site-footer" ng-if="!$root.currentRoute.disableFooter && outerContentReady()" ng-model="site.footer" ng-items="items" ng-results="results" ng-slug="slug" ng-collections="collections"/>'; 3841 } 3842 var compiled = $compile(siteTemplate)($scope); 3843 $element.append(compiled); 3844 if (site.header && site.header.sections && site.header.sections.length) { 3845 outerSections = outerSections.concat(site.header.sections); 3846 } 3847 if (site.footer && site.footer.sections && site.footer.sections.length) { 3848 outerSections = outerSections.concat(site.footer.sections); 3849 } 3850 var contentIds = []; 3851 var collectionIds = []; 3852 var queryIds = []; 3853 var keyIds = []; 3854 _.each(outerSections, function(section) { 3855 contentIds = contentIds.concat(section.content); 3856 queryIds = queryIds.concat(section.queries); 3857 collectionIds = collectionIds.concat(section.collections); 3858 var newKeyIds = _.map(section.keys, function(keyGroup) { 3859 return keyGroup.content; 3860 }); 3861 keyIds = keyIds.concat(newKeyIds); 3862 }); 3863 contentIds = _.chain(contentIds).flatten().compact().value(
3863); 3864 queryIds = _.chain(queryIds).flatten().compact().value(); 3865 collectionIds = _.chain(collectionIds).flatten().compact().value(); 3866 keyIds = _.chain(keyIds).flatten().compact().value(); 3867 if (keyIds.length) { 3868 contentIds = contentIds.concat(keyIds); 3869 } 3870 if (contentIds.length) { 3871 FluroContentRetrieval.get(contentIds, useSiteCache, { 3872 searchInheritable: true 3873 }).then(function(items) { 3874 $scope.items = items; 3875 $scope.contentIsReady = true; 3876 }); 3877 } else { 3878 $scope.contentIsReady = true; 3879 } 3880 if (queryIds.length) { 3881 var queryVariables; 3882 FluroContentRetrieval.queryMultiple(queryIds, useSiteCache, queryVariables).then(function(results) { 3883 $scope.results = results; 3884 $scope.resultsAreReady = true; 3885 }); 3886 } else { 3887 $scope.resultsAreReady = true; 3888 } 3889 if (collectionIds.length) { 3890 FluroContentRetrieval.getMultipleCollections(collectionIds, useSiteCache).then(function(res) { 3891 $scope.collections = res; 3892 $scope.collectionResults = _.chain($scope.collections).map(function(collection) { 3893 return collection.items; 3894 }).flatten().uniq().value(); 3895 $scope.collectionsReady = true; 3896 }); 3897 } else { 3898 $scope.collectionsReady = true; 3899 } 3900 $scope.outerContentReady = function() { 3901 return $scope.collectionsReady && $scope.resultsAreReady && $scope.contentIsReady; 3902 }; 3903 } 3904 }; 3905}); 3906 3907app.directive("compileTemplate", function($compile, $rootScope, $templateCache) { 3908 return { 3909 restrict: "A", 3910 link: function($scope, $element, $attr) { 3911 $attr.$observe("compileTemplate", function(templateName) { 3912 $element.empty(); 3913 var html = $templateCache.get(templateName); 3914 if (html && html.length) { 3915 var template = $compile(html)($scope); 3916 $element.append(template); 3917 } 3918 }); 3919 } 3920 }; 3921}); 3922 3923angular.module("fluro").run([ "$templateCache", function($templateCache) { 3924 "use strict"; 3925 $templateCache.put("application/application.html", "<div class=application><notifications></notifications><div ui-view></div></div>"); 3926 $templateCache.put("fluro-dob-select/fluro-dob-select.html", "<div><div class=form-group ng-hide=hideAge><label>Age</label><input type=number select-on-focus ng-model-options=\"{debounce: { 'default': 400, 'blur': 0 } }\" class=form-control pattern=[0-9]* placeholder=Age ng-model=age inputmode=numeric></div><div class=form-group ng-hide=hideDates><label>Date of Birth {{$parent.to.required ? '*' : ''}}</label><div class=row><div class=col-xs-3><select name=dateFields.day data-ng-model=dateFields.day placeholder=Day class=form-control ng-options=\"day for day in days\" ng-change=checkDate() ng-disabled=disableFields></select></div><div class=col-xs-5><select name=dateFields.month data-ng-model=dateFields.month placeholder=Month class=form-control ng-options=\"month.value as month.name for month in months\" value={{dateField.month}} ng-change=checkDate() ng-disabled=disableFields></select></div><div class=col-xs-4><select ng-if=!yearText name=dateFields.year data-ng-model=dateFields.year placeholder=Year class=form-control ng-options=\"year for year in years\" ng-change=checkDate() ng-disabled=disableFields></select><input ng-if=yearText name=dateFields.year data-ng-model=dateFields.year placeholder=Year class=form-control ng-disabled=disableFields></div></div></div></div>"); 3927 $templateCache.put("fluro-interaction-form/button-select/fluro-button-select.html", '<div id={{options.id}} class="button-select {{to.definition.directive}}-buttons" ng-model=model[options.key]><a ng-repeat="(key, option) in to.options" ng-class={active:contains(option.value)} class="btn btn-default" id="{{id + \'_\'+ $index}}" ng-click=toggle(option.value)><span>{{option.name}}</span><i class="fa fa-check"></i></a></div>'); 3928 $templateCache.put("fluro-interaction-form/custom.html", "<div ng-model=model[options.key] compile-html=to.definition.template></div>"); 3929 $templateCache.put("fluro-interaction-form/date-select/date-selector.html", '<div class=row><div class=col-xs-3><select name=dateFields.day data-ng-model=dateFields.day placeholder=Day class=form-control ng-options="day for day in days" ng-change=checkDate()><option value="" disabled selected>Day</option></select></div><div class=col-xs-5><select name=dateFields.month data-ng-model=dateFields.month placeholder=Month class=form-control ng-options="month.value as month.name for month in months" value={{dateField.month}} ng-change=checkDate()><option value="" disabled selected>Month</option></select></div><div class=col-xs-4><input type=number name=dateFields.year min=0 max=9999 data-ng-model=dateFields.year placeholder=YYYY class=form-control></div></div>'); 3930 $templateCache.put("fluro-interaction-form/date-select/fluro-date-select.html", "<div><div class=visible-xs-block><input type=date class=form-control ng-model=model[options.key] ng-required=to.required></div><div class=hidden-xs><fluro-date-select ng-model=model[options.key]></fluro-date-select></div></div>"); 3931 $templateCache.put("fluro-interaction-form/dob-select/fluro-dob-select.html", "<div class=fluro-interaction-dob-select><dob-select ng-model=model[options.key] hide-age=to.params.hideAge hide-
3931dates=to.params.hideDates></dob-select></div>"); 3932 $templateCache.put("fluro-interaction-form/embedded/fluro-embedded.html", '<div class=fluro-embedded-form><div class=form-multi-group ng-if="to.definition.maximum != 1"><div class="panel panel-default" ng-init="fields = copyFields(); dataFields = copyDataFields(); " ng-repeat="entry in model[options.key] track by $index"><div class="panel-heading clearfix"><a ng-if=canRemove() class="btn btn-danger btn-sm pull-right" ng-click="model[options.key].splice($index, 1)"><span>Remove {{to.label}}</span><i class="fa fa-times"></i></a><h5>{{to.label}} {{$index + 1}}</h5></div><div class=panel-body><formly-form model=entry fields=fields></formly-form><formly-form model=entry.data fields=dataFields></formly-form></div></div><a class="btn btn-primary btn-sm" ng-if=canAdd() ng-click=addAnother()><span>Add <span ng-if=model[options.key].length>another</span> {{to.label}}</span><i class="fa fa-plus"></i></a></div><div ng-if="to.definition.maximum == 1 && options.key"><formly-form model=model[options.key] fields=options.data.fields></formly-form><formly-form model=model[options.key].data fields=options.data.dataFields></formly-form></div></div>'); 3933 $templateCache.put("fluro-interaction-form/field-errors.html", '<div class=field-errors ng-if="fc.$touched && fc.$invalid"><div ng-show=fc.$error.required class="alert alert-danger" role=alert><span class="fa fa-exclamation" aria-hidden=true></span> <span class=sr-only>Error:</span> {{to.label}} is required.</div><div ng-show=fc.$error.validInput class="alert alert-danger" role=alert><span class="fa fa-exclamation" aria-hidden=true></span> <span class=sr-only>Error:</span> <span ng-if=!to.errorMessage.length>\'{{fc.$viewValue}}\' is not a valid value</span> <span ng-if=to.errorMessage.length>{{to.errorMessage}}</span></div><div ng-show=fc.$error.email class="alert alert-danger" role=alert><span class="fa fa-exclamation" aria-hidden=true></span> <span class=sr-only>Error:</span> <span>\'{{fc.$viewValue}}\' is not a valid email address</span></div></div>'); 3934 $templateCache.put("fluro-interaction-form/fluro-interaction-input.html", '<div class="fluro-input form-group" scroll-active ng-class="{\'fluro-valid\':isValid(), \'fluro-dirty\':isDirty, \'fluro-invalid\':!isValid()}"><label><i class="fa fa-check" ng-if=isValid()></i><i class="fa fa-exclamation" ng-if=!isValid()></i><span>{{field.title}}</span></label><div class="error-message help-block"><span ng-if=field.errorMessage>{{field.errorMessage}}</span> <span ng-if=!field.errorMessage>Please provide valid input for this field</span></div><span class=help-block ng-if="field.description && field.type != \'boolean\'">{{field.description}}</span><div class=fluro-input-wrapper></div></div>'); 3935 $templateCache.put("fluro-interaction-form/fluro-terms.html", '<div class=terms-checkbox><div class=checkbox><label><input type=checkbox ng-model="model[options.key]"> {{to.definition.params.storeData}}</label></div></div>'); 3936 $templateCache.put("fluro-interaction-form/fluro-web-form.html", '<div class=fluro-interaction-form><div ng-if=!correctPermissions class=form-permission-warning><div class="alert alert-warning small"><i class="fa fa-warning"></i> <span>You do not have permission to post {{model.plural}}</span></div></div><div ng-if=promisesResolved><div ng-if=debugMode><div class="btn-group btn-group-justified"><a ng-click="vm.state = \'ready\'" class="btn btn-default">State to ready</a> <a ng-click="vm.state = \'complete\'" class="btn btn-default">State to complete</a> <a ng-click=reset() class="btn btn-default">Reset</a></div><hr></div><div ng-show="vm.state != \'complete\'"><form novalidate ng-submit=vm.onSubmit()><formly-form model=vm.model fields=vm.modelFields form=vm.modelForm options=vm.options><div ng-if=model.data.recaptcha><div recaptcha-render></div></div><div class="form-error-summary form-client-error alert alert-warning" ng-if="vm.modelForm.$invalid && !vm.modelForm.$pristine"><div class=form-error-summary-item ng-repeat="field in errorList" ng-if=field.formControl.$invalid><i class="fa fa-exclamation"></i> <span ng-if=field.templateOptions.definition.errorMessage.length>{{field.templateOptions.definition.errorMessage}}</span> <span ng-if=!field.templateOptions.definition.errorMessage.length>{{field.templateOptions.label}}
3936 has not been provided.</span></div></div><div ng-switch=vm.state><div ng-switch-when=sending><a class="btn btn-primary" ng-disabled=true><span>Processing</span> <i class="fa fa-spinner fa-spin"></i></a></div><div ng-switch-when=error><div class="form-error-summary form-server-error alert alert-danger" ng-if=processErrorMessages.length><div class=form-error-summary-item ng-repeat="error in processErrorMessages track by $index"><i class="fa fa-exclamation"></i> <span>Error processing your submission: {{error}}</span></div></div><button type=submit class="btn btn-primary" ng-disabled=!readyToSubmit><span>Try Again</span> <i class="fa fa-angle-right"></i></button></div><div ng-switch-default><button type=submit class="btn btn-primary" ng-disabled=!readyToSubmit><span>{{submitLabel}}</span> <i class="fa fa-angle-right"></i></button></div></div></formly-form></form></div><div ng-show="vm.state == \'complete\'"><div compile-html=transcludedContent></div></div></div></div>'); 3937 $templateCache.put("fluro-interaction-form/nested/fluro-nested.html", '<div><div class=form-multi-group ng-if="to.definition.maximum != 1"><div class="panel panel-default" ng-init="fields = copyFields()" ng-repeat="entry in model[options.key] track by $index"><div class="panel-heading clearfix"><a ng-if=canRemove() class="btn btn-danger btn-sm pull-right" ng-click="model[options.key].splice($index, 1)"><span>Remove {{to.label}}</span><i class="fa fa-times"></i></a><h5>{{to.label}} {{$index + 1}}</h5></div><div class=panel-body><formly-form model=entry fields=fields></formly-form></div></div><a class="btn btn-primary btn-sm" ng-if=canAdd() ng-click=addAnother()><span>Add <span ng-if=model[options.key].length>another</span> {{to.label}}</span><i class="fa fa-plus"></i></a></div><div ng-if="to.definition.maximum == 1 && options.key"><formly-form model=model[options.key] fields=options.data.fields></formly-form></div></div>'); 3938 $templateCache.put("fluro-interaction-form/order-select/fluro-order-select.html", '<div id={{options.id}} class=fluro-order-select><div ng-if=selection.values.length><p class=help-block>Drag to reorder your choices</p></div><div class=list-group as-sortable=dragControlListeners formly-skip-ng-model-attrs-manipulator ng-model=selection.values><div class="list-group-item clearfix" as-sortable-item ng-repeat="item in selection.values"><div class=pull-left as-sortable-item-handle><i class="fa fa-arrows order-select-handle"></i> <span class="order-number text-muted">{{$index+1}}</span> <span>{{item}}</span></div><div class="pull-right order-select-remove" ng-click=deselect(item)><i class="fa fa-times"></i></div></div></div><div ng-if=canAddMore()><p class=help-block>Choose by selecting options below</p><select class=form-control ng-model=selectBox.item ng-change=selectUpdate()><option ng-repeat="(key, option) in to.options | orderBy:\'value\'" ng-if=!contains(option.value) value={{option.value}}>{{option.value}}</option></select></div></div>'); 3939 $templateCache.put("fluro-interaction-form/payment/payment-method.html", '<hr><div class=payment-method-select><div ng-if=!settings.showOptions><h3 class=clearfix>{{selected.method.title}} <em class="pull-right small" ng-click="settings.showOptions = !settings.showOptions">Other payment options <i class="fa fa-angle-right"></i></em></h3></div><div ng-if=settings.showOptions><h3 class=clearfix>Select payment method <em ng-click="settings.showOptions = false" class="pull-right small">Back <i class="fa fa-angle-up"></i></em></h3><div class="payment-method-list list-group"><div class="payment-method-list-item list-group-item" ng-class="{active:method == selected.method}" ng-click=selectMethod(method) ng-repeat="method in methodOptions"><h5 class=title>{{method.title}}</h5></div></div></div><div ng-if=!settings.showOptions><div ng-if="selected.method.key == \'card\'"><formly-form model=model fields=options.data.fields></formly-form></div><div ng-if="selected.method == method && selected.method.description.length" ng-repeat="method in methodOptions"><div compile-html=method.description></div></div></div></div><hr>'); 3940 $templateCache.put("fluro-interaction-form/payment/payment-summary.html", '<hr><div class=payment-summary><h3>Payment details</h3><div class=form-group><div ng-if=modifications.length class=payment-running-total><div class="row payment-base-row"><div class=col-xs-6><strong>Base Price</strong></div><div class="col-xs-3 col-xs-offset-3">{{paymentDetails.amount / 100 | currency}}</div></div><div class="row text-muted" ng-repeat="mod in modifications"><div class=col-xs-6><em>{{mod.title}}</em></div><div class="col-xs-3 text-right"><em>{{mod.description}}</em></div><div class=col-xs-3><em class=text-muted>{{mod.total / 100 | currency}}</em></div></div><div class="row payment-total-row"><div class=col-xs-6><h4>Total</h4></div><div class="col-xs-3 col-xs-offset-3"><h4>{{calculatedTotal /100 |currency}} <span class="text-uppercase text-muted">{{paymentDetails.currency}}</span></h4></div></div></div><div class=payment-amount ng-if=!modifications.length>{{calculatedTotal /100 |currency}} <span class=text-uppercase>({{paymentDetails.currency}})</span></div></div></div>'); 3941 $templateCache.put("fluro-interaction-form/search-select/fluro-search-select-item.html", '<a class=clearfix><i class="fa fa-{{match.model._type}}"></i> <span ng-bind-html="match.label | trusted | typeaheadHighlight:query"></span> <span ng-if="match.model._type == \'event\' || match.model._type == \'plan\'" class="small text-muted">// {{match.model.startDate | formatDate:\'jS F Y - g:ia\'}}</span></a>'); 3942 $templateCache.put("fluro-interaction-form/search-select/fluro-search-select-value.html", '<a class=clearfix><span ng-bind-html="match.label | trusted | typeaheadHighlight:query"></span></a>'); 3943 $templateCache.put("fluro-interaction-form/search-select/fluro-search-select.html", '<div class=fluro-search-select><div ng-if="to.definition.type == \'reference\'"><div class=list-group ng-if="multiple && selection.values.length"><div class=list-group-item ng-repeat="item in selection.values"><i class="fa fa-times pull-right" ng-click=deselect(item)></i> {{item.title}}</div></div><div class=list-group ng-if="!multiple && selection.value"><div class="list-group-item clearfix"><i class="fa fa-times pull-right" ng-click=deselect(selection.value)></i> {{selection.value.title}}</div></div><div ng-if=canAddMore()><div class=input-group><input class=form-control formly-skip-ng-model-attrs-manipulator ng-model=proposed.value typeahead-template-url=fluro-interaction-form/search-select/fluro-search-select-item.html typeahead-on-select=select($item) placeholder=Search typeahead="item.title for item in retrieveReferenceOptions($viewValue)" typeahead-loading="search.loading"><div class=input-group-addon ng-if=!search.loading ng-click="search.terms = \'\'"><i class=fa ng-class="{\'fa-search\':!search.terms.length, \'fa-times\':search.terms.length}"></i></div><div class=input-group-addon ng-if=search.loading><i class="fa fa-spin fa-spinner"></i></div></div></div></div><div ng-if="to.definition.type != \'reference\'"><div class=list-group ng-if="multiple && selection.values.length"><div class=list-group-item ng-repeat="value in selection.values"><i class="fa fa-times pull-right" ng-click=deselect(value)></i> {{getValueLabel(value)}}</div></div><div class=list-group ng-if="!multiple && selection.value"><div class="list-group-item clearfix"><i class="fa fa-times pull-right" ng-click=deselect(selection.value)></i> {{getValueLabel(selection.value)}}</div></div><div ng-if=canAddMore()><div class=input-group><input class=form-control formly-skip-ng-model-attrs-manipulator ng-model=proposed.value typeahead-template-url=fluro-interaction-form/search-select/fluro-search-select-value.html typeahead-on-select=select($item.value) placeholder=Search typeahead="item.name for item in retrieveValueOptions($viewValue)" typeahead-loading="search.loading"><div class=input-group-addon ng-if=!search.loading ng-click="search.terms = \'\'"><i class=fa ng-class="{\'fa-search\':!search.terms.length, \'fa-times\':search.terms.length}"></i></div><div class=input-group-addon ng-if=search.loading><i class="fa fa-spin fa-spinner"></i></div></div></div></div></div>'); 3944 $templateCache.put("fluro-interaction-form/value/value.html", "<div class=fluro-interaction-value style=display:none><pre>{{model[options.key] | json}}</pre></div>"); 3945 $templateCache.put("site/site.html", "<div class=site></div>"); 3946} ]);
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.