Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dataTable_rowactions.js « javascripts « CoreHome « plugins - github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: ab07537e2f84a4b4b3b711edbc17af6d41448d6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/**
 * Registry for row actions
 *
 * Plugins can call DataTable_RowActions_Registry.register() from their JS
 * files in order to add new actions to arbitrary data tables. The register()
 * method takes an object containing:
 * - name: string identifying the action. must be short, no spaces.
 * - dataTableIcon: path to the icon for the action
 * - createInstance: a factory method to create an instance of the appropriate
 *                   subclass of DataTable_RowAction
 * - isAvailable: a method to determine whether the action is available in a
 *                given row of a data table
 */
var DataTable_RowActions_Registry = {

    registry: [],

    register: function (action) {
        var createInstance = action.createInstance;
        action.createInstance = function (dataTable, param) {
            var instance = createInstance(dataTable, param);
            instance.actionName = action.name;
            return instance;
        };

        this.registry.push(action);
    },

    getAvailableActionsForReport: function (dataTableParams, tr) {
        if (dataTableParams.disable_row_actions == '1') {
            return [];
        }

        var available = [];
        for (var i = 0; i < this.registry.length; i++) {
            if (this.registry[i].isAvailableOnReport(dataTableParams, tr)) {
                available.push(this.registry[i]);
            }
        }
        available.sort(function (a, b) {
            return b.order - a.order;
        });
        return available;
    },

    getActionByName: function (name) {
        for (var i = 0; i < this.registry.length; i++) {
            if (this.registry[i].name == name) {
                return this.registry[i];
            }
        }
        return false;
    }

};

// Register Row Evolution (also servers as example)
DataTable_RowActions_Registry.register({

    name: 'RowEvolution',

    dataTableIcon: 'plugins/Morpheus/images/row_evolution.png',
    dataTableIconHover: 'plugins/Morpheus/images/row_evolution_hover.png',

    order: 50,

    dataTableIconTooltip: [
        _pk_translate('General_RowEvolutionRowActionTooltipTitle'),
        _pk_translate('General_RowEvolutionRowActionTooltip')
    ],

    createInstance: function (dataTable, param) {
        if (dataTable !== null && typeof dataTable.rowEvolutionActionInstance != 'undefined') {
            return dataTable.rowEvolutionActionInstance;
        }

        if (dataTable === null && param) {
            // when row evolution is triggered from the url (not a click on the data table)
            // we look for the data table instance in the dom
            var report = param.split(':')[0];
            var div = $(require('piwik/UI').DataTable.getDataTableByReport(report));
            if (div.size() > 0 && div.data('uiControlObject')) {
                dataTable = div.data('uiControlObject');
                if (typeof dataTable.rowEvolutionActionInstance != 'undefined') {
                    return dataTable.rowEvolutionActionInstance;
                }
            }
        }

        var instance = new DataTable_RowActions_RowEvolution(dataTable);
        if (dataTable !== null) {
            dataTable.rowEvolutionActionInstance = instance;
        }
        return instance;
    },

    isAvailableOnReport: function (dataTableParams) {
        return (
            typeof dataTableParams.disable_row_evolution == 'undefined'
                || dataTableParams.disable_row_evolution == "0"
            ) && (
            typeof dataTableParams.flat == 'undefined'
                || dataTableParams.flat == "0"
            );
    },

    isAvailableOnRow: function (dataTableParams, tr) {
        return true;
    }

});

/**
 * DataTable Row Actions
 *
 * The lifecycle of an action is as follows:
 * - for each data table, a new instance of the action is created using the factory
 * - when the table is loaded, initTr is called for each tr
 * - when the action icon is clicked, trigger is called
 * - the label is put together and performAction is called
 * - performAction must call openPopover on the base class
 * - openPopover calls back doOpenPopover after doing general stuff
 *
 * The two template methods are performAction and doOpenPopover
 */

//
// BASE CLASS
//

function DataTable_RowAction(dataTable) {
    this.dataTable = dataTable;

    // has to be overridden in subclasses
    this.trEventName = 'piwikTriggerRowAction';

    // set in registry
    this.actionName = 'RowAction';
}

/** Initialize a row when the table is loaded */
DataTable_RowAction.prototype.initTr = function (tr) {
    var self = this;

    // For subtables, we need to make sure that the actions are always triggered on the
    // action instance connected to the root table. Otherwise sharing data (e.g. for
    // for multi-row evolution) wouldn't be possible. Also, sub-tables might have different
    // API actions. For the label filter to work, we need to use the parent action.
    // We use jQuery events to let subtables access their parents.
    tr.bind(self.trEventName, function (e, params) {
        self.trigger($(this), params.originalEvent, params.label);
    });
};

/**
 * This method is called from the click event and the tr event (see this.trEventName).
 * It derives the label and calls performAction.
 */
DataTable_RowAction.prototype.trigger = function (tr, e, subTableLabel) {
    var label = this.getLabelFromTr(tr);

    // if we have received the event from the sub table, add the label
    if (subTableLabel) {
        var separator = ' > '; // LabelFilter::SEPARATOR_RECURSIVE_LABEL
        label += separator + subTableLabel;
    }

    // handle sub tables in nested reports: forward to parent
    var subtable = tr.closest('table');
    if (subtable.is('.subDataTable')) {
        subtable.closest('tr').prev().trigger(this.trEventName, {
            label: label,
            originalEvent: e
        });
        return;
    }

    // ascend in action reports
    if (subtable.closest('div.dataTableActions').length) {
        var allClasses = tr.attr('class');
        var matches = allClasses.match(/level[0-9]+/);
        var level = parseInt(matches[0].substring(5, matches[0].length), 10);
        if (level > 0) {
            // .prev(.levelX) does not work for some reason => do it "by hand"
            var findLevel = 'level' + (level - 1);
            var ptr = tr;
            while ((ptr = ptr.prev()).size() > 0) {
                if (!ptr.hasClass(findLevel) || ptr.hasClass('nodata')) {
                    continue;
                }
                ptr.trigger(this.trEventName, {
                    label: label,
                    originalEvent: e
                });
                return;
            }
        }
    }

    this.performAction(label, tr, e);
};

/** Get the label string from a tr dom element */
DataTable_RowAction.prototype.getLabelFromTr = function (tr) {
    var label = tr.find('span.label');

    // handle truncation
    var value = label.data('originalText');

    if (!value) {
        value = label.text();
    }
    value = value.trim();
    value = encodeURIComponent(value);

    // if tr is a terminal node, we use the @ operator to distinguish it from branch nodes w/ the same name
    if (!tr.hasClass('subDataTable')) {
        value = '@' + value;
    }

    return value;
};

/**
 * Base method for opening popovers.
 * This method will remember the parameter in the url and call doOpenPopover().
 */
DataTable_RowAction.prototype.openPopover = function (parameter) {
    broadcast.propagateNewPopoverParameter('RowAction', this.actionName + ':' + parameter);
};

broadcast.addPopoverHandler('RowAction', function (param) {
    var paramParts = param.split(':');
    var rowActionName = paramParts[0];
    paramParts.shift();
    param = paramParts.join(':');

    var rowAction = DataTable_RowActions_Registry.getActionByName(rowActionName);
    if (rowAction) {
        rowAction.createInstance(null, param).doOpenPopover(param);
    }
});

/** To be overridden */
DataTable_RowAction.prototype.performAction = function (label, tr, e) {
};
DataTable_RowAction.prototype.doOpenPopover = function (parameter) {
};

//
// ROW EVOLUTION
//

function DataTable_RowActions_RowEvolution(dataTable) {
    this.dataTable = dataTable;
    this.trEventName = 'piwikTriggerRowEvolution';

    /** The rows to be compared in multi row evolution */
    this.multiEvolutionRows = [];
}

/** Static helper method to launch row evolution from anywhere */
DataTable_RowActions_RowEvolution.launch = function (apiMethod, label) {
    var param = 'RowEvolution:' + apiMethod + ':0:' + label;
    broadcast.propagateNewPopoverParameter('RowAction', param);
};

DataTable_RowActions_RowEvolution.prototype = new DataTable_RowAction;

DataTable_RowActions_RowEvolution.prototype.performAction = function (label, tr, e) {
    if (e.shiftKey) {
        // only mark for multi row evolution if shift key is pressed
        this.addMultiEvolutionRow(label);
        return;
    }

    this.addMultiEvolutionRow(label);

    // check whether we have rows marked for multi row evolution
    var extraParams = {};
    if (this.multiEvolutionRows.length > 1) {
        extraParams.action = 'getMultiRowEvolutionPopover';
        label = this.multiEvolutionRows.join(',');
    }

    // check if abandonedCarts is in the dataTable params and if so, propagate to row evolution request
    if (this.dataTable.param.abandonedCarts !== undefined) {
        extraParams['abandonedCarts'] = this.dataTable.param.abandonedCarts;
    }

    var apiMethod = this.dataTable.param.module + '.' + this.dataTable.param.action;
    this.openPopover(apiMethod, extraParams, label);
};

DataTable_RowActions_RowEvolution.prototype.addMultiEvolutionRow = function (label) {
    if ($.inArray(label, this.multiEvolutionRows) == -1) {
        this.multiEvolutionRows.push(label);
    }
};

DataTable_RowActions_RowEvolution.prototype.openPopover = function (apiMethod, extraParams, label) {
    var urlParam = apiMethod + ':' + encodeURIComponent(JSON.stringify(extraParams)) + ':' + label;
    DataTable_RowAction.prototype.openPopover.apply(this, [urlParam]);
};

DataTable_RowActions_RowEvolution.prototype.doOpenPopover = function (urlParam) {
    var urlParamParts = urlParam.split(':');

    var apiMethod = urlParamParts.shift();

    var extraParamsString = urlParamParts.shift(),
        extraParams = {}; // 0/1 or "0"/"1"
    try {
        extraParams = JSON.parse(decodeURIComponent(extraParamsString));
    } catch (e) {
        // assume the parameter is an int/string describing whether to use multi row evolution
        if (extraParamsString == '1') {
            extraParams.action = 'getMultiRowEvolutionPopover';
        } else if (extraParamsString != '0') {
            extraParams.action = 'getMultiRowEvolutionPopover';
            extraParams.column = extraParamsString;
        }
    }

    var label = urlParamParts.join(':');

    this.showRowEvolution(apiMethod, label, extraParams);
};

/** Open the row evolution popover */
DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function (apiMethod, label, extraParams) {

    var self = this;

    // open the popover
    var box = Piwik_Popover.showLoading('Row Evolution');
    box.addClass('rowEvolutionPopover');

    // prepare loading the popover contents
    var requestParams = {
        apiMethod: apiMethod,
        label: label,
        disableLink: 1
    };

    var callback = function (html) {
        Piwik_Popover.setContent(html);

        // use the popover title returned from the server
        var title = box.find('div.popover-title');
        if (title.size() > 0) {
            Piwik_Popover.setTitle(title.html());
            title.remove();
        }

        Piwik_Popover.onClose(function () {
            // reset rows marked for multi row evolution on close
            self.multiEvolutionRows = [];
        });

        if (self.dataTable !== null) {
            // remember label for multi row evolution
            box.find('.rowevolution-startmulti').click(function () {
                Piwik_Popover.onClose(false); // unbind listener that resets multiEvolutionRows
                Piwik_Popover.close();
                return false;
            });
        } else {
            // when the popover is launched by copy&pasting a url, we don't have the data table.
            // in this case, we can't remember the row marked for multi row evolution so
            // we disable the picker.
            box.find('.compare-container, .rowevolution-startmulti').remove();
        }

        // switch metric in multi row evolution
        box.find('select.multirowevoltion-metric').change(function () {
            var metric = $(this).val();
            Piwik_Popover.onClose(false); // unbind listener that resets multiEvolutionRows
            var extraParams = {action: 'getMultiRowEvolutionPopover', column: metric};
            self.openPopover(apiMethod, extraParams, label);
            return true;
        });
    };

    requestParams.module = 'CoreHome';
    requestParams.action = 'getRowEvolutionPopover';
    requestParams.colors = JSON.stringify(piwik.getSparklineColors());

    var idDimension = broadcast.getValueFromHash('idDimension');
    if (idDimension) {
        requestParams.idDimension = parseInt(idDimension, 10);
    }

    $.extend(requestParams, extraParams);

    var ajaxRequest = new ajaxHelper();
    ajaxRequest.addParams(requestParams, 'get');
    ajaxRequest.setCallback(callback);
    ajaxRequest.setFormat('html');
    ajaxRequest.send(false);
};