1 (function (angular
, $, _
) {
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'
10 _
.each(simpleDirectives
, function (templateUrl
, directiveName
) {
11 angular
.module('crmMailingAB').directive(directiveName
, function ($parse
, crmMailingABCriteria
) {
12 var scopeDesc
= {crmAbtest
: '@'};
13 scopeDesc
[directiveName
] = '@';
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);
25 var fieldsModel
= $parse(attr
[directiveName
]);
26 scope
.fields
= fieldsModel(scope
.$parent
);
32 // example: <div crm-mailing-ab-slider ng-model="abtest.ab.group_percentage"></div>
33 angular
.module('crmMailingAB').directive('crmMailingAbSlider', function () {
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
);
44 scope
.ts
= CRM
.ts(null);
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
);
67 slide
: function slideTest(event
, ui
) {
68 event
.preventDefault();
73 slide
: function slideWinner(event
, ui
) {
74 event
.preventDefault();
75 setValue(Math
.round((100 - ui
.value
) / 2));
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
);
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.
92 // example: <div crm-mailing-ab-stats="{split_count: 6, criteria:'Open'}" crm-abtest="myabtest" />
93 // options (see also: Mailing.graph_stats API)
96 // - target_date: string, date
97 // - target_url: string
98 angular
.module('crmMailingAB').directive('crmMailingAbStats', function (crmApi
, $parse
, crmNow
) {
101 crmMailingAbStats
: '@',
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'
113 scope
.$watch(attrs
.crmAbtest
, refresh
);
116 var abtest
= abtestModel(scope
.$parent
);
118 console
.log('failed to draw stats - missing abtest');
131 for (var i
= 1; i
<= options
.split_count
; i
++) {
132 var result
= crmApi('MailingAB', 'graph_stats', {
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
141 result
.then(function (data
) {
144 for (var key
in data
.values
.A
) {
147 var t
= data
.values
.A
[temp
].time
.split(" ");
150 var day
= t
[1].substr(0, t
[1].length
- 3);
151 var t1
, hur
, hour
, min
;
152 if (_
.isEmpty(t
[3])) {
153 t1
= t
[4].split(":");
162 hour
= parseInt(hur
) + 12;
167 t1
= t
[3].split(":");
176 hour
= parseInt(hur
) + 12;
220 var tp
= new Date(year
, month
, day
, hour
, min
, 0, 0);
221 scope
.graph_data
[temp
- 1] = {
223 x
: data
.values
.A
[temp
].count
,
224 y
: data
.values
.B
[temp
].count
227 if (keep_cnt
== options
.split_count
) {
228 scope
.graphload
= true;
229 data
= scope
.graph_data
;
231 // set up a colour variable
232 var color
= d3
.scale
.category10();
234 // map one colour each to x, y and z
235 // keys grabs the key value or heading of each key value pair in the json
237 color
.domain(d3
.keys(data
[0]).filter(function (key
) {
238 return key
!== "time";
241 // create a nested series for passing to the line generator
242 // it's best understood by console logging the data
243 var series
= color
.domain().map(function (name
) {
246 values
: data
.map(function (d
) {
255 // Set the dimensions of the canvas / graph
262 width
= 550 - margin
.left
- margin
.right
,
263 height
= 350 - margin
.top
- margin
.bottom
;
266 //var x = d3.time.scale().range([0, width]).domain([0,10]);
267 var x
= d3
.time
.scale().range([0, width
]);
268 var y
= d3
.scale
.linear().range([height
, 0]);
271 var xAxis
= d3
.svg
.axis().scale(x
)
272 .orient("bottom").ticks(10);
274 var yAxis
= d3
.svg
.axis().scale(y
)
275 .orient("left").ticks(5);
278 // Note you plot the time / score pair from each key you created ealier
279 var valueline
= d3
.svg
.line()
287 // Adds the svg canvas
288 var svg
= d3
.select($('.crm-mailing-ab-stats', element
)[0])
290 .attr("width", width
+ margin
.left
+ margin
.right
)
291 .attr("height", height
+ margin
.top
+ margin
.bottom
)
293 .attr("transform", "translate(" + margin
.left
+ "," + margin
.top
+ ")");
295 // Scale the range of the data
296 x
.domain(d3
.extent(data
, function (d
) {
300 // note the nested nature of this you need to dig an additional level
302 d3
.min(series
, function (c
) {
303 return d3
.min(c
.values
, function (v
) {
307 d3
.max(series
, function (c
) {
308 return d3
.max(c
.values
, function (v
) {
313 svg
.append("text") // text label for the x axis
314 .attr("x", width
/ 2)
315 .attr("y", height
+ margin
.bottom
)
316 .style("text-anchor", "middle")
319 svg
.append("text") // text label for the x axis
320 .style("text-anchor", "middle")
321 .text(scope
.winnercriteria
).attr("transform",function (d
) {
322 return "rotate(-90)";
323 }).attr("x", -height
/ 2)
326 // create a variable called series and bind the date
327 // for each series append a g element and class it as series for css styling
328 series
= svg
.selectAll(".series")
331 .attr("class", "series");
333 // create the path for each series in the variable series i.e. x, y and z
334 // pass each object called x, y nad z to the lne generator
335 series
.append("path")
336 .attr("class", "line")
337 .attr("d", function (d
) {
338 // console.log(d); // to see how d3 iterates through series
339 return valueline(d
.values
);
341 .style("stroke", function (d
) {
342 return color(d
.name
);
346 svg
.append("g") // Add the X Axis
347 .attr("class", "x axis")
348 .attr("transform", "translate(0," + height
+ ")")
351 .attr("transform", function (d
) {
352 return "rotate(-30)";
356 svg
.append("g") // Add the Y Axis
357 .attr("class", "y axis")
366 })(angular
, CRM
.$, CRM
._
);