Merge pull request #19382 from totten/master-maxfile
[civicrm-core.git] / ext / afform / admin / ang / afGuiEditor.js
CommitLineData
f6c0358e 1(function(angular, $, _) {
881d52bb 2 "use strict";
cb46dc65
CW
3 angular.module('afGuiEditor', CRM.angRequires('afGuiEditor'))
4
d132f0a6 5 .service('afAdmin', function(crmApi4, $parse, $q) {
cb46dc65
CW
6
7 // Parse strings of javascript that php couldn't interpret
8 function evaluate(collection) {
9 _.each(collection, function(item) {
10 if (_.isPlainObject(item)) {
11 evaluate(item['#children']);
12 _.each(item, function(node, idx) {
13 if (_.isString(node)) {
14 var str = _.trim(node);
15 if (str[0] === '{' || str[0] === '[' || str.slice(0, 3) === 'ts(') {
16 item[idx] = $parse(str)({ts: CRM.ts('afform')});
17 }
18 }
19 });
20 }
21 });
22 }
23
24 function getStyles(node) {
25 return !node || !node.style ? {} : _.transform(node.style.split(';'), function(styles, style) {
26 var keyVal = _.map(style.split(':'), _.trim);
27 if (keyVal.length > 1 && keyVal[1].length) {
28 styles[keyVal[0]] = keyVal[1];
29 }
30 }, {});
31 }
32
33 function setStyle(node, name, val) {
34 var styles = getStyles(node);
35 styles[name] = val;
36 if (!val) {
37 delete styles[name];
38 }
39 if (_.isEmpty(styles)) {
40 delete node.style;
41 } else {
42 node.style = _.transform(styles, function(combined, val, name) {
43 combined.push(name + ': ' + val);
44 }, []).join('; ');
45 }
46 }
47
48 // Turns a space-separated list (e.g. css classes) into an array
49 function splitClass(str) {
50 if (_.isArray(str)) {
51 return str;
52 }
53 return str ? _.unique(_.trim(str).split(/\s+/g)) : [];
54 }
55
56 function modifyClasses(node, toRemove, toAdd) {
57 var classes = splitClass(node['class']);
58 if (toRemove) {
59 classes = _.difference(classes, splitClass(toRemove));
60 }
61 if (toAdd) {
62 classes = _.unique(classes.concat(splitClass(toAdd)));
63 }
64 node['class'] = classes.join(' ');
65 }
66
67 return {
68 // Initialize/refresh data about the current afform + available blocks
69 initialize: function(afName) {
70 var promise = crmApi4('Afform', 'get', {
71 layoutFormat: 'shallow',
72 formatWhitespace: true,
73 where: [afName ? ["OR", [["name", "=", afName], ["block", "IS NOT NULL"]]] : ["block", "IS NOT NULL"]]
74 });
75 promise.then(function(afforms) {
76 CRM.afGuiEditor.blocks = {};
77 _.each(afforms, function(form) {
78 evaluate(form.layout);
79 if (form.block) {
80 CRM.afGuiEditor.blocks[form.directive_name] = form;
81 }
82 });
83 });
84 return promise;
85 },
86
87 meta: CRM.afGuiEditor,
88
89 getField: function(entityType, fieldName) {
90 return CRM.afGuiEditor.entities[entityType].fields[fieldName];
91 },
92
93 // Recursively searches a collection and its children using _.filter
94 // Returns an array of all matches, or an object if the indexBy param is used
95 findRecursive: function findRecursive(collection, predicate, indexBy) {
96 var items = _.filter(collection, predicate);
97 _.each(collection, function(item) {
98 if (_.isPlainObject(item) && item['#children']) {
99 var childMatches = findRecursive(item['#children'], predicate);
100 if (childMatches.length) {
101 Array.prototype.push.apply(items, childMatches);
102 }
103 }
104 });
105 return indexBy ? _.indexBy(items, indexBy) : items;
106 },
107
108 // Applies _.remove() to an item and its children
109 removeRecursive: function removeRecursive(collection, removeParams) {
110 _.remove(collection, removeParams);
111 _.each(collection, function(item) {
112 if (_.isPlainObject(item) && item['#children']) {
113 removeRecursive(item['#children'], removeParams);
114 }
115 });
116 },
117
118 splitClass: splitClass,
119 modifyClasses: modifyClasses,
120 getStyles: getStyles,
d132f0a6
CW
121 setStyle: setStyle,
122
123 pickIcon: function() {
124 var deferred = $q.defer();
125 $('#af-gui-icon-picker').off('change').siblings('.crm-icon-picker-button').click();
126 $('#af-gui-icon-picker').on('change', function() {
127 deferred.resolve($(this).val());
128 });
129 return deferred.promise;
130 }
cb46dc65
CW
131 };
132 });
f6c0358e 133
d132f0a6
CW
134 // Shoehorn in a non-angular widget for picking icons
135 $(function() {
136 $('#crm-container').append('<div style="display:none"><input id="af-gui-icon-picker"></div>');
137 CRM.loadScript(CRM.config.resourceBase + 'js/jquery/jquery.crmIconPicker.js').done(function() {
138 $('#af-gui-icon-picker').crmIconPicker();
139 });
140 });
141
b4def6e9
CW
142 // Connect bootstrap dropdown.js with angular
143 // Allows menu content to be conditionally rendered only if open
144 // This gives a large performance boost for a page with lots of menus
145 angular.module('afGuiEditor').directive('afGuiMenu', function() {
146 return {
147 restrict: 'A',
148 link: function($scope, element, attrs) {
149 $scope.menu = {};
150 element
151 .on('show.bs.dropdown', function() {
152 $scope.$apply(function() {
153 $scope.menu.open = true;
154 });
155 })
156 .on('hidden.bs.dropdown', function() {
157 $scope.$apply(function() {
158 $scope.menu.open = false;
159 });
160 });
161 }
162 };
163 });
164
f6c0358e 165})(angular, CRM.$, CRM._);