Multiple Angular modules - Remove partialUrl() helper
[civicrm-core.git] / js / angular-crmMailingAB / directives.js
1 (function (angular, $, _) {
2
3 // example:
4 // scope.myAbtest = new CrmMailingAB();
5 // <crm-mailing-ab-block-mailing="{fromAddressA: 1, fromAddressB: 1}" crm-abtest="myAbtest" />
6 var simpleDirectives = {
7 crmMailingAbBlockMailing: '~/crmMailingAB/joint-mailing.html',
8 crmMailingAbBlockSetup: '~/crmMailingAB/setup.html'
9 };
10 _.each(simpleDirectives, function (templateUrl, directiveName) {
11 angular.module('crmMailingAB').directive(directiveName, function ($parse, crmMailingABCriteria) {
12 var scopeDesc = {crmAbtest: '@'};
13 scopeDesc[directiveName] = '@';
14
15 return {
16 scope: scopeDesc,
17 templateUrl: templateUrl,
18 link: function (scope, elm, attr) {
19 var model = $parse(attr.crmAbtest);
20 scope.abtest = model(scope.$parent);
21 scope.crmMailingConst = CRM.crmMailing;
22 scope.crmMailingABCriteria = crmMailingABCriteria;
23 scope.ts = CRM.ts(null);
24
25 var fieldsModel = $parse(attr[directiveName]);
26 scope.fields = fieldsModel(scope.$parent);
27 }
28 };
29 });
30 });
31
32 // example: <div crm-mailing-ab-slider ng-model="abtest.ab.group_percentage"></div>
33 angular.module('crmMailingAB').directive('crmMailingAbSlider', function () {
34 return {
35 require: '?ngModel',
36 scope: {},
37 templateUrl: '~/crmMailingAB/slider.html',
38 link: function (scope, element, attrs, ngModel) {
39 var TEST_MIN = 1, TEST_MAX = 50;
40 var sliders = $('.slider-test,.slider-win', element);
41 var sliderTests = $('.slider-test', element);
42 var sliderWin = $('.slider-win', element);
43
44 scope.ts = CRM.ts(null);
45 scope.testValue = 0;
46 scope.winValue = 100;
47
48 // set the base value (following a GUI event)
49 function setValue(value) {
50 value = Math.min(TEST_MAX, Math.max(TEST_MIN, value));
51 scope.$apply(function () {
52 ngModel.$setViewValue(value);
53 scope.testValue = value;
54 scope.winValue = 100 - (2 * scope.testValue);
55 sliderTests.slider('value', scope.testValue);
56 sliderWin.slider('value', scope.winValue);
57 });
58 }
59
60 sliders.slider({
61 min: 0,
62 max: 100,
63 range: 'min',
64 step: 1
65 });
66 sliderTests.slider({
67 slide: function slideTest(event, ui) {
68 event.preventDefault();
69 setValue(ui.value);
70 }
71 });
72 sliderWin.slider({
73 slide: function slideWinner(event, ui) {
74 event.preventDefault();
75 setValue(Math.round((100 - ui.value) / 2));
76 }
77 });
78
79 ngModel.$render = function () {
80 scope.testValue = ngModel.$viewValue;
81 scope.winValue = 100 - (2 * scope.testValue);
82 sliderTests.slider('value', scope.testValue);
83 sliderWin.slider('value', scope.winValue);
84 };
85 }
86 };
87 });
88
89 // FIXME: This code is long and hasn't been fully working for me, but I've moved it into a spot
90 // where it at least fits in a bit better.
91
92 // example: <div crm-mailing-ab-stats="{split_count: 6, criteria:'Open'}" crm-abtest="myabtest" />
93 // options (see also: Mailing.graph_stats API)
94 // - split_count: int
95 // - criteria: string
96 // - target_date: string, date
97 // - target_url: string
98 angular.module('crmMailingAB').directive('crmMailingAbStats', function (crmApi, $parse, crmNow) {
99 return {
100 scope: {
101 crmMailingAbStats: '@',
102 crmAbtest: '@'
103 },
104 template: '<div class="crm-mailing-ab-stats"></div>',
105 link: function (scope, element, attrs) {
106 var abtestModel = $parse(attrs.crmAbtest);
107 var optionModel = $parse(attrs.crmMailingAbStats);
108 var options = angular.extend({}, optionModel(scope.$parent), {
109 criteria: 'Open', // e.g. 'Open', 'Total Unique Clicks'
110 split_count: 5
111 });
112
113 scope.$watch(attrs.crmAbtest, refresh);
114 function refresh() {
115 var now = crmNow();
116 var abtest = abtestModel(scope.$parent);
117 if (!abtest) {
118 console.log('failed to draw stats - missing abtest');
119 return;
120 }
121
122 scope.graph_data = [
123 {},
124 {},
125 {},
126 {},
127 {}
128 ];
129 var keep_cnt = 0;
130
131 for (var i = 1; i <= options.split_count; i++) {
132 var result = crmApi('MailingAB', 'graph_stats', {
133 id: abtest.ab.id,
134 target_date: abtest.ab.declare_winning_time ? abtest.ab.declare_winning_time : now,
135 target_url: null, // FIXME
136 criteria: options.criteria,
137 split_count: options.split_count,
138 split_count_select: i
139 });
140 result.then(function (data) {
141 var temp = 0;
142 keep_cnt++;
143 for (var key in data.values.A) {
144 temp = key;
145 }
146 var t = data.values.A[temp].time.split(" ");
147 var m = t[0];
148 var year = t[2];
149 var day = t[1].substr(0, t[1].length - 3);
150 var t1, hur, hour, min;
151 if (t[3] == "") {
152 t1 = t[4].split(":");
153 hur = t1[0];
154 if (t[5] == "AM") {
155 hour = hur;
156 if (hour == 12) {
157 hour = 0;
158 }
159 }
160 if (t[5] == "PM") {
161 hour = parseInt(hur) + 12;
162 }
163 min = t1[1];
164 }
165 else {
166 t1 = t[3].split(":");
167 hur = t1[0];
168 if (t[4] == "AM") {
169 hour = hur;
170 if (hour == 12) {
171 hour = 0;
172 }
173 }
174 if (t[4] == "PM") {
175 hour = parseInt(hur) + 12;
176 }
177 min = t1[1];
178 }
179 var month = 0;
180 switch (m) {
181 case "January":
182 month = 0;
183 break;
184 case "February":
185 month = 1;
186 break;
187 case "March":
188 month = 2;
189 break;
190 case "April":
191 month = 3;
192 break;
193 case "May":
194 month = 4;
195 break;
196 case "June":
197 month = 5;
198 break;
199 case "July":
200 month = 6;
201 break;
202 case "August":
203 month = 7;
204 break;
205 case "September":
206 month = 8;
207 break;
208 case "October":
209 month = 9;
210 break;
211 case "November":
212 month = 10;
213 break;
214 case "December":
215 month = 11;
216 break;
217
218 }
219 var tp = new Date(year, month, day, hour, min, 0, 0);
220 scope.graph_data[temp - 1] = {
221 time: tp,
222 x: data.values.A[temp].count,
223 y: data.values.B[temp].count
224 };
225
226 if (keep_cnt == options.split_count) {
227 scope.graphload = true;
228 data = scope.graph_data;
229
230 // set up a colour variable
231 var color = d3.scale.category10();
232
233 // map one colour each to x, y and z
234 // keys grabs the key value or heading of each key value pair in the json
235 // but not time
236 color.domain(d3.keys(data[0]).filter(function (key) {
237 return key !== "time";
238 }));
239
240 // create a nested series for passing to the line generator
241 // it's best understood by console logging the data
242 var series = color.domain().map(function (name) {
243 return {
244 name: name,
245 values: data.map(function (d) {
246 return {
247 time: d.time,
248 score: +d[name]
249 };
250 })
251 };
252 });
253
254 // Set the dimensions of the canvas / graph
255 var margin = {
256 top: 30,
257 right: 20,
258 bottom: 40,
259 left: 75
260 },
261 width = 550 - margin.left - margin.right,
262 height = 350 - margin.top - margin.bottom;
263
264 // Set the ranges
265 //var x = d3.time.scale().range([0, width]).domain([0,10]);
266 var x = d3.time.scale().range([0, width]);
267 var y = d3.scale.linear().range([height, 0]);
268
269 // Define the axes
270 var xAxis = d3.svg.axis().scale(x)
271 .orient("bottom").ticks(10);
272
273 var yAxis = d3.svg.axis().scale(y)
274 .orient("left").ticks(5);
275
276 // Define the line
277 // Note you plot the time / score pair from each key you created ealier
278 var valueline = d3.svg.line()
279 .x(function (d) {
280 return x(d.time);
281 })
282 .y(function (d) {
283 return y(d.score);
284 });
285
286 // Adds the svg canvas
287 var svg = d3.select($('.crm-mailing-ab-stats', element)[0])
288 .append("svg")
289 .attr("width", width + margin.left + margin.right)
290 .attr("height", height + margin.top + margin.bottom)
291 .append("g")
292 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
293
294 // Scale the range of the data
295 x.domain(d3.extent(data, function (d) {
296 return d.time;
297 }));
298
299 // note the nested nature of this you need to dig an additional level
300 y.domain([
301 d3.min(series, function (c) {
302 return d3.min(c.values, function (v) {
303 return v.score;
304 });
305 }),
306 d3.max(series, function (c) {
307 return d3.max(c.values, function (v) {
308 return v.score;
309 });
310 })
311 ]);
312 svg.append("text") // text label for the x axis
313 .attr("x", width / 2)
314 .attr("y", height + margin.bottom)
315 .style("text-anchor", "middle")
316 .text("Time");
317
318 svg.append("text") // text label for the x axis
319 .style("text-anchor", "middle")
320 .text(scope.winnercriteria).attr("transform",function (d) {
321 return "rotate(-90)";
322 }).attr("x", -height / 2)
323 .attr("y", -30);
324
325 // create a variable called series and bind the date
326 // for each series append a g element and class it as series for css styling
327 series = svg.selectAll(".series")
328 .data(series)
329 .enter().append("g")
330 .attr("class", "series");
331
332 // create the path for each series in the variable series i.e. x, y and z
333 // pass each object called x, y nad z to the lne generator
334 series.append("path")
335 .attr("class", "line")
336 .attr("d", function (d) {
337 // console.log(d); // to see how d3 iterates through series
338 return valueline(d.values);
339 })
340 .style("stroke", function (d) {
341 return color(d.name);
342 });
343
344 // Add the X Axis
345 svg.append("g") // Add the X Axis
346 .attr("class", "x axis")
347 .attr("transform", "translate(0," + height + ")")
348 .call(xAxis)
349 .selectAll("text")
350 .attr("transform", function (d) {
351 return "rotate(-30)";
352 });
353
354 // Add the Y Axis
355 svg.append("g") // Add the Y Axis
356 .attr("class", "y axis")
357 .call(yAxis);
358 }
359 });
360 }
361 }
362 } // link()
363 };
364 });
365 })(angular, CRM.$, CRM._);