1 (function (angular
, $, _
) {
2 var partialUrl = function (relPath
) {
3 return CRM
.resourceUrls
['civicrm'] + '/partials/crmMailing2/' + relPath
;
6 var crmMailing2
= angular
.module('crmMailing2');
8 // The following directives have the same simple implementation -- load
9 // a template and export a "mailing" object into scope.
11 crmMailingBlockHeaderFooter
: partialUrl('headerFooter.html'),
12 crmMailingBlockMailing
: partialUrl('mailing.html'),
13 crmMailingBlockPreview
: partialUrl('preview.html'),
14 crmMailingBlockPublication
: partialUrl('publication.html'),
15 crmMailingBlockResponses
: partialUrl('responses.html'),
16 crmMailingBlockReview
: partialUrl('review.html'),
17 crmMailingBlockSchedule
: partialUrl('schedule.html'),
18 crmMailingBlockSummary
: partialUrl('summary.html'),
19 crmMailingBlockTracking
: partialUrl('tracking.html'),
20 crmMailingBodyHtml
: partialUrl('body_html.html'),
21 crmMailingBodyText
: partialUrl('body_text.html')
23 _
.each(simpleBlocks
, function(templateUrl
, directiveName
){
24 crmMailing2
.directive(directiveName
, function ($parse
) {
29 templateUrl
: templateUrl
,
30 link: function (scope
, elm
, attr
) {
31 var model
= $parse(attr
.crmMailing
);
32 scope
.mailing
= model(scope
.$parent
);
33 scope
.crmMailingConst
= CRM
.crmMailing
;
34 scope
.ts
= CRM
.ts('CiviMail');
40 crmMailing2
.directive('crmMailingReviewBool', function () {
46 template
: '<span ng-class="spanClasses"><span class="icon" ng-class="iconClasses"></span>{{crmTitle}} </span>',
47 link: function (scope
, element
, attrs
) {
49 if (scope
.$parent
.$eval(attrs
.crmOn
)) {
50 scope
.spanClasses
= {'crmMailing2-active': true};
51 scope
.iconClasses
= {'ui-icon-check': true};
54 scope
.spanClasses
= {'crmMailing2-inactive': true};
55 scope
.iconClasses
= {'ui-icon-close': true};
57 scope
.crmTitle
= scope
.$parent
.$eval(attrs
.crmTitle
);
61 scope
.$parent
.$watch(attrs
.crmOn
, refresh
);
62 scope
.$parent
.$watch(attrs
.crmTitle
, refresh
);
67 // example: <input name="subject" /> <input crm-mailing-token crm-for="subject"/>
68 // WISHLIST: Instead of global CRM.crmMailing.mailTokens, accept token list as an input
69 crmMailing2
.directive('crmMailingToken', function (crmUiId
) {
74 template
: '<input type="text" class="crmMailingToken" />',
75 link: function (scope
, element
, attrs
) {
76 // 1. Find the corresponding input element (crmFor)
78 var form
= $(element
).closest('form');
79 var crmForEl
= $('input[name="' + attrs
.crmFor
+ '"],textarea[name="' + attrs
.crmFor
+ '"]', form
);
80 if (form
.length
!= 1 || crmForEl
.length
!= 1) {
82 console
.log('crmMailingToken cannot be matched to input element. Expected to find one form and one input.', form
.length
, crmForEl
.length
);
87 // 2. Setup the token selector
90 dropdownAutoWidth
: true,
91 data
: CRM
.crmMailing
.mailTokens
,
92 placeholder
: ts('Insert')
94 $(element
).on('select2-selecting', function (e
) {
95 var id
= crmUiId(crmForEl
);
96 if (CKEDITOR
.instances
[id
]) {
97 CKEDITOR
.instances
[id
].insertText(e
.val
);
98 $(element
).select2('close').select2('val', '');
99 CKEDITOR
.instances
[id
].focus();
102 var origVal
= crmForEl
.val();
103 var origPos
= crmForEl
[0].selectionStart
;
104 var newVal
= origVal
.substring(0, origPos
) + e
.val
+ origVal
.substring(origPos
, origVal
.length
);
105 crmForEl
.val(newVal
);
106 var newPos
= (origPos
+ e
.val
.length
);
107 crmForEl
[0].selectionStart
= newPos
;
108 crmForEl
[0].selectionEnd
= newPos
;
110 $(element
).select2('close').select2('val', '');
111 crmForEl
.triggerHandler('change');
121 // example: <select multiple crm-mailing-recipients crm-mailing="mymailing" crm-avail-groups="myGroups" crm-avail-mailings="myMailings"></select>
122 // FIXME: participate in ngModel's validation cycle
123 crmMailing2
.directive('crmMailingRecipients', function () {
127 crmAvailGroups
: '@', // available groups
128 crmAvailMailings
: '@', // available mailings
129 crmMailing
: '@' // the mailing for which we are choosing recipients
131 templateUrl
: partialUrl('directive/recipients.html'),
132 link: function (scope
, element
, attrs
) {
133 scope
.mailing
= scope
.$parent
.$eval(attrs
.crmMailing
);
134 scope
.groups
= scope
.$parent
.$eval(attrs
.crmAvailGroups
);
135 scope
.mailings
= scope
.$parent
.$eval(attrs
.crmAvailMailings
);
137 scope
.ts
= CRM
.ts('CiviMail');
139 /// Convert MySQL date ("yyyy-mm-dd hh:mm:ss") to JS date object
140 scope
.parseDate = function (date
) {
141 if (!angular
.isString(date
)) {
144 var p
= date
.split(/[\- :]/);
145 return new Date(p
[0], p
[1], p
[2], p
[3], p
[4], p
[5]);
148 /// Remove {value} from {array}
149 function arrayRemove(array
, value
) {
150 var idx
= array
.indexOf(value
);
152 array
.splice(idx
, 1);
156 // @param string id an encoded string like "4 civicrm_mailing include"
157 // @return Object keys: entity_id, entity_type, mode
158 function convertValueToObj(id
) {
159 var a
= id
.split(" ");
160 return {entity_id
: parseInt(a
[0]), entity_type
: a
[1], mode
: a
[2]};
163 // @param Object mailing
164 // @return array list of values like "4 civicrm_mailing include"
165 function convertMailingToValues(mailing
) {
167 angular
.forEach(mailing
.groups
.include
, function (v
) {
168 r
.push(v
+ " civicrm_group include");
170 angular
.forEach(mailing
.groups
.exclude
, function (v
) {
171 r
.push(v
+ " civicrm_group exclude");
173 angular
.forEach(mailing
.mailings
.include
, function (v
) {
174 r
.push(v
+ " civicrm_mailing include");
176 angular
.forEach(mailing
.mailings
.exclude
, function (v
) {
177 r
.push(v
+ " civicrm_mailing exclude");
182 // Update $(element) view based on latest data
183 function refreshUI() {
184 $(element
).select2('val', convertMailingToValues(scope
.mailing
));
187 /// @return string HTML representingn an option
188 function formatItem(item
) {
190 // return `text` for optgroup
193 var option
= convertValueToObj(item
.id
);
194 var icon
= (option
.entity_type
=== 'civicrm_mailing') ? 'EnvelopeIn.gif' : 'group.png';
195 var spanClass
= (option
.mode
== 'exclude') ? 'crmMailing2-exclude' : 'crmMailing2-include';
196 return "<img src='../../sites/all/modules/civicrm/i/" + icon
+ "' height=12 width=12 /> <span class='" + spanClass
+ "'>" + item
.text
+ "</span>";
200 dropdownAutoWidth
: true,
201 placeholder
: "Groups or Past Recipients",
202 formatResult
: formatItem
,
203 formatSelection
: formatItem
,
204 escapeMarkup: function (m
) {
209 $(element
).on('select2-selecting', function (e
) {
210 var option
= convertValueToObj(e
.val
);
211 var typeKey
= option
.entity_type
== 'civicrm_mailing' ? 'mailings' : 'groups';
212 if (option
.mode
== 'exclude') {
213 scope
.mailing
[typeKey
].exclude
.push(option
.entity_id
);
214 arrayRemove(scope
.mailing
[typeKey
].include
, option
.entity_id
);
217 scope
.mailing
[typeKey
].include
.push(option
.entity_id
);
218 arrayRemove(scope
.mailing
[typeKey
].exclude
, option
.entity_id
);
221 $(element
).select2('close');
225 $(element
).on("select2-removing", function (e
) {
226 var option
= convertValueToObj(e
.val
);
227 var typeKey
= option
.entity_type
== 'civicrm_mailing' ? 'mailings' : 'groups';
228 scope
.$parent
.$apply(function () {
229 arrayRemove(scope
.mailing
[typeKey
][option
.mode
], option
.entity_id
);
234 scope
.$watchCollection(attrs
.crmMailing
+ ".groups.include", refreshUI
);
235 scope
.$watchCollection(attrs
.crmMailing
+ ".groups.exclude", refreshUI
);
236 scope
.$watchCollection(attrs
.crmMailing
+ ".mailings.include", refreshUI
);
237 scope
.$watchCollection(attrs
.crmMailing
+ ".mailings.exclude", refreshUI
);
238 setTimeout(refreshUI
, 50);
243 })(angular
, CRM
.$, CRM
._
);