Commit | Line | Data |
---|---|---|
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._); |