1 (function (angular
, $, _
) {
2 var partialUrl = function (relPath
) {
3 return CRM
.resourceUrls
['civicrm'] + '/partials/crmMailingAB/' + relPath
;
7 // scope.myAbtest = new CrmMailingAB();
8 // <crm-mailing-ab-block-mailing="{fromAddressA: 1, fromAddressB: 1}" crm-abtest="myAbtest" />
9 var simpleDirectives
= {
10 crmMailingAbBlockMailing
: partialUrl('joint-mailing.html'),
11 crmMailingAbBlockSetup
: partialUrl('setup.html')
13 _
.each(simpleDirectives
, function (templateUrl
, directiveName
) {
14 angular
.module('crmMailingAB').directive(directiveName
, function ($parse
, crmMailingABCriteria
) {
15 var scopeDesc
= {crmAbtest
: '@'};
16 scopeDesc
[directiveName
] = '@';
20 templateUrl
: templateUrl
,
21 link: function (scope
, elm
, attr
) {
22 var model
= $parse(attr
.crmAbtest
);
23 scope
.abtest
= model(scope
.$parent
);
24 scope
.crmMailingConst
= CRM
.crmMailing
;
25 scope
.crmMailingABCriteria
= crmMailingABCriteria
;
26 scope
.ts
= CRM
.ts('CiviMail');
28 var fieldsModel
= $parse(attr
[directiveName
]);
29 scope
.fields
= fieldsModel(scope
.$parent
);
35 // example: <div crm-mailing-ab-slider ng-model="abtest.ab.group_percentage"></div>
36 angular
.module('crmMailingAB').directive('crmMailingAbSlider', function () {
40 templateUrl
: partialUrl('slider.html'),
41 link: function (scope
, element
, attrs
, ngModel
) {
42 var TEST_MIN
= 1, TEST_MAX
= 50;
43 var sliders
= $('.slider-test,.slider-win', element
);
44 var sliderTests
= $('.slider-test', element
);
45 var sliderWin
= $('.slider-win', element
);
47 scope
.ts
= CRM
.ts('CiviMail');
51 // set the base value (following a GUI event)
52 function setValue(value
) {
53 value
= Math
.min(TEST_MAX
, Math
.max(TEST_MIN
, value
));
54 scope
.$apply(function () {
55 ngModel
.$setViewValue(value
);
56 scope
.testValue
= value
;
57 scope
.winValue
= 100 - (2 * scope
.testValue
);
58 sliderTests
.slider('value', scope
.testValue
);
59 sliderWin
.slider('value', scope
.winValue
);
70 slide
: function slideTest(event
, ui
) {
71 event
.preventDefault();
76 slide
: function slideWinner(event
, ui
) {
77 event
.preventDefault();
78 setValue(Math
.round((100 - ui
.value
) / 2));
82 ngModel
.$render = function () {
83 scope
.testValue
= ngModel
.$viewValue
;
84 scope
.winValue
= 100 - (2 * scope
.testValue
);
85 sliderTests
.slider('value', scope
.testValue
);
86 sliderWin
.slider('value', scope
.winValue
);
92 // FIXME: This code is long and hasn't been fully working for me, but I've moved it into a spot
93 // where it at least fits in a bit better.
95 // example: <div crm-mailing-ab-stats="{split_count: 6, criteria:'Open'}" crm-abtest="myabtest" />
96 // options (see also: Mailing.graph_stats API)
99 // - target_date: string, date
100 // - target_url: string
101 angular
.module('crmMailingAB').directive('crmMailingAbStats', function (crmApi
, $parse
, crmNow
) {
104 crmMailingAbStats
: '@',
107 template
: '<div class="crm-mailing-ab-stats"></div>',
108 link: function (scope
, element
, attrs
) {
109 var abtestModel
= $parse(attrs
.crmAbtest
);
110 var optionModel
= $parse(attrs
.crmMailingAbStats
);
111 var options
= angular
.extend({}, optionModel(scope
.$parent
), {
112 criteria
: 'Open', // e.g. 'Open', 'Total Unique Clicks'
116 scope
.$watch(attrs
.crmAbtest
, refresh
);
119 var abtest
= abtestModel(scope
.$parent
);
121 console
.log('failed to draw stats - missing abtest');
134 for (var i
= 1; i
<= options
.split_count
; i
++) {
135 var result
= crmApi('MailingAB', 'graph_stats', {
137 target_date
: abtest
.ab
.declare_winning_time
? abtest
.ab
.declare_winning_time
: now
,
138 target_url
: null, // FIXME
139 criteria
: options
.criteria
,
140 split_count
: options
.split_count
,
141 split_count_select
: i
143 result
.then(function (data
) {
146 for (var key
in data
.values
.A
) {
149 var t
= data
.values
.A
[temp
].time
.split(" ");
152 var day
= t
[1].substr(0, t
[1].length
- 3);
154 var t1
= t
[4].split(":");
163 hour
= parseInt(hur
) + 12;
168 var t1
= t
[3].split(":");
177 hour
= parseInt(hur
) + 12;
221 var tp
= new Date(year
, month
, day
, hour
, min
, 0, 0);
222 scope
.graph_data
[temp
- 1] = {
224 x
: data
.values
.A
[temp
].count
,
225 y
: data
.values
.B
[temp
].count
228 if (keep_cnt
== options
.split_count
) {
229 scope
.graphload
= true;
230 var data
= scope
.graph_data
;
232 // set up a colour variable
233 var color
= d3
.scale
.category10();
235 // map one colour each to x, y and z
236 // keys grabs the key value or heading of each key value pair in the json
238 color
.domain(d3
.keys(data
[0]).filter(function (key
) {
239 return key
!== "time";
242 // create a nested series for passing to the line generator
243 // it's best understood by console logging the data
244 var series
= color
.domain().map(function (name
) {
247 values
: data
.map(function (d
) {
256 // Set the dimensions of the canvas / graph
263 width
= 550 - margin
.left
- margin
.right
,
264 height
= 350 - margin
.top
- margin
.bottom
;
267 //var x = d3.time.scale().range([0, width]).domain([0,10]);
268 var x
= d3
.time
.scale().range([0, width
]);
269 var y
= d3
.scale
.linear().range([height
, 0]);
272 var xAxis
= d3
.svg
.axis().scale(x
)
273 .orient("bottom").ticks(10);
275 var yAxis
= d3
.svg
.axis().scale(y
)
276 .orient("left").ticks(5);
279 // Note you plot the time / score pair from each key you created ealier
280 var valueline
= d3
.svg
.line()
288 // Adds the svg canvas
289 var svg
= d3
.select($('.crm-mailing-ab-stats', element
)[0])
291 .attr("width", width
+ margin
.left
+ margin
.right
)
292 .attr("height", height
+ margin
.top
+ margin
.bottom
)
294 .attr("transform", "translate(" + margin
.left
+ "," + margin
.top
+ ")");
296 // Scale the range of the data
297 x
.domain(d3
.extent(data
, function (d
) {
301 // note the nested nature of this you need to dig an additional level
303 d3
.min(series
, function (c
) {
304 return d3
.min(c
.values
, function (v
) {
308 d3
.max(series
, function (c
) {
309 return d3
.max(c
.values
, function (v
) {
314 svg
.append("text") // text label for the x axis
315 .attr("x", width
/ 2)
316 .attr("y", height
+ margin
.bottom
)
317 .style("text-anchor", "middle")
320 svg
.append("text") // text label for the x axis
321 .style("text-anchor", "middle")
322 .text(scope
.winnercriteria
).attr("transform",function (d
) {
324 }).attr("x", -height
/ 2)
327 // create a variable called series and bind the date
328 // for each series append a g element and class it as series for css styling
329 var series
= svg
.selectAll(".series")
332 .attr("class", "series");
334 // create the path for each series in the variable series i.e. x, y and z
335 // pass each object called x, y nad z to the lne generator
336 series
.append("path")
337 .attr("class", "line")
338 .attr("d", function (d
) {
339 // console.log(d); // to see how d3 iterates through series
340 return valueline(d
.values
);
342 .style("stroke", function (d
) {
343 return color(d
.name
);
347 svg
.append("g") // Add the X Axis
348 .attr("class", "x axis")
349 .attr("transform", "translate(0," + height
+ ")")
352 .attr("transform", function (d
) {
353 return "rotate(-30)";
357 svg
.append("g") // Add the Y Axis
358 .attr("class", "y axis")
367 })(angular
, CRM
.$, CRM
._
);