From 1001bc057e487871e5a066b72768296cb89468df Mon Sep 17 00:00:00 2001 From: BeezyT Date: Sat, 13 Oct 2012 14:42:40 +0000 Subject: refs #534: bringing the new popover behavior to row evolution * the popover parameter is used to remember the current state of row evolution * Piwik_Popover is used for controlling the popover * ajax helper methods are refactored from Transitions to piwikHelper and reused in row evolution * simplified logic for remembering rows for multi row evolution * applied code formatter (hence the whitespace changes) git-svn-id: http://dev.piwik.org/svn/trunk@7179 59fd770c-687e-43c8-a1e3-f5a4ff64c105 --- plugins/CoreHome/templates/datatable.js | 4 + plugins/CoreHome/templates/datatable_rowactions.js | 252 +++++++++++---------- plugins/CoreHome/templates/popover.js | 28 ++- plugins/Transitions/templates/transitions.js | 86 +++---- themes/default/common.js | 69 ++++++ 5 files changed, 260 insertions(+), 179 deletions(-) diff --git a/plugins/CoreHome/templates/datatable.js b/plugins/CoreHome/templates/datatable.js index ed2289a516..a4c078432b 100644 --- a/plugins/CoreHome/templates/datatable.js +++ b/plugins/CoreHome/templates/datatable.js @@ -35,6 +35,8 @@ dataTable.prototype = this.isEmpty = $('.pk-emptyDataTable', domElem).length > 0; this.bindEventsAndApplyStyle(domElem); this.initialized = true; + + domElem.data('piwikDataTable', this); }, //function triggered when user click on column sort @@ -1509,6 +1511,8 @@ actionDataTable.prototype = this.workingDivId = workingDivId; this.bindEventsAndApplyStyle(domElem); this.initialized = true; + + domElem.data('piwikDataTable', this); }, //see dataTable::bindEventsAndApplyStyle diff --git a/plugins/CoreHome/templates/datatable_rowactions.js b/plugins/CoreHome/templates/datatable_rowactions.js index 5221fda827..addee7b4ac 100644 --- a/plugins/CoreHome/templates/datatable_rowactions.js +++ b/plugins/CoreHome/templates/datatable_rowactions.js @@ -1,6 +1,6 @@ /** * 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: @@ -12,26 +12,25 @@ * given row of a data table */ var DataTable_RowActions_Registry = { - + registry: [], - + register: function(action) { var createInstance = action.createInstance; - action.createInstance = function(dataTable) { - var instance = createInstance(dataTable); + 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') - { + 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)) { @@ -40,7 +39,7 @@ var DataTable_RowActions_Registry = { } return available; }, - + getActionByName: function(name) { for (var i = 0; i < this.registry.length; i++) { if (this.registry[i].name == name) { @@ -49,36 +48,57 @@ var DataTable_RowActions_Registry = { } return false; } - + }; // Register Row Evolution (also servers as example) DataTable_RowActions_Registry.register({ - + name: 'RowEvolution', - + dataTableIcon: 'themes/default/images/row_evolution.png', dataTableIconHover: 'themes/default/images/row_evolution_hover.png', - + dataTableIconTooltip: [ _pk_translate('CoreHome_RowEvolutionRowActionTooltipTitle_js'), _pk_translate('CoreHome_RowEvolutionRowActionTooltip_js') ], - - createInstance: function(dataTable) { - return new DataTable_RowActions_RowEvolution(dataTable); + + 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 divId = param.split(':')[0].replace(/\./, ''); + var div = $('#' + divId + '.dataTable'); + if (div.size() > 0 && div.data('piwikDataTable')) { + dataTable = div.data('piwikDataTable'); + 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" - ) && ( + || dataTableParams.disable_row_evolution == "0" + ) && ( typeof dataTableParams.flat == 'undefined' - || dataTableParams.flat == "0" - ); + || dataTableParams.flat == "0" + ); }, - + isAvailableOnRow: function(dataTableParams, tr) { return true; } @@ -86,7 +106,6 @@ DataTable_RowActions_Registry.register({ }); - /** * DataTable Row Actions * @@ -108,10 +127,10 @@ DataTable_RowActions_Registry.register({ function DataTable_RowAction(dataTable) { this.dataTable = dataTable; - + // has to be overridden in subclasses this.trEventName = 'piwikTriggerRowAction'; - + // set in registry this.actionName = 'RowAction'; } @@ -137,12 +156,12 @@ DataTable_RowAction.prototype.initTr = function(tr) { DataTable_RowAction.prototype.trigger = function(tr, e, subTableLabel) { var label = this.getLabelFromTr(tr); - label = label.trim(); - // if we have received the event from the sub table, add the label + label = label.trim(); + // if we have received the event from the sub table, add the label if (subTableLabel) { - var separator = ' > '; // Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL - label += separator + subTableLabel.trim(); - } + var separator = ' > '; // Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL + label += separator + subTableLabel.trim(); + } // handle sub tables in nested reports: forward to parent var subtable = tr.closest('table'); @@ -206,10 +225,10 @@ broadcast.addPopoverHandler('RowAction', function(param) { var rowActionName = paramParts[0]; paramParts.shift(); param = paramParts.join(':'); - + var rowAction = DataTable_RowActions_Registry.getActionByName(rowActionName); if (rowAction) { - rowAction.createInstance().doOpenPopover(param); + rowAction.createInstance(null, param).doOpenPopover(param); } }); @@ -223,14 +242,11 @@ DataTable_RowAction.prototype.doOpenPopover = function(parameter) { // // ROW EVOLUTION // -// TODO: use openPopover of base class. when opening a row evolution popover from -// the url, omit "compare records" because we don't know which data table to use. -// function DataTable_RowActions_RowEvolution(dataTable) { this.dataTable = dataTable; this.trEventName = 'piwikTriggerRowEvolution'; - + /** The rows to be compared in multi row evolution */ this.multiEvolutionRows = []; } @@ -238,108 +254,100 @@ function DataTable_RowActions_RowEvolution(dataTable) { DataTable_RowActions_RowEvolution.prototype = new DataTable_RowAction; DataTable_RowActions_RowEvolution.prototype.performAction = function(label, tr, e) { - this.showRowEvolution(tr, label, null, e.shiftKey); + if (e.shiftKey) { + // only mark for multi row evolution if shift key is pressed + this.addMultiEvolutionRow(label); + return; + } + + // check whether we have rows marked for multi row evolution + var isMultiRowEvolution = '0'; + this.addMultiEvolutionRow(label); + if (this.multiEvolutionRows.length > 1) { + isMultiRowEvolution = '1'; + label = this.multiEvolutionRows.join(','); + } + + var apiMethod = this.dataTable.param.module + '.' + this.dataTable.param.action; + var urlParam = apiMethod + ':' + isMultiRowEvolution + ':' + label; + this.openPopover(urlParam); +}; + +DataTable_RowActions_RowEvolution.prototype.addMultiEvolutionRow = function(label) { + if ($.inArray(label, this.multiEvolutionRows) == -1) { + this.multiEvolutionRows.push(label); + } +}; + +DataTable_RowActions_RowEvolution.prototype.doOpenPopover = function(urlParam) { + var urlParamParts = urlParam.split(':'); + + var apiMethod = urlParamParts[0]; + urlParamParts.shift(); + + var isMultiRowEvolution = (urlParamParts[0] == '1'); + urlParamParts.shift(); + + var label = urlParamParts.join(':'); + + this.showRowEvolution(apiMethod, label, isMultiRowEvolution); }; /** Open the row evolution popover */ -DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function(tr, label, metric, onlyMarkForMulti) { - var self = this; +DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function(apiMethod, label, isMultiRowEvolution, metric) { - // this happens when shift-clicking a row - if (onlyMarkForMulti) { - self.multiEvolutionRows.push(label); - return; - } + var self = this; // open the popover - var loading = $('div.loadingPiwik:first').clone(); - var box = $(document.createElement('div')).addClass('rowEvolutionPopover').html(loading); - box.dialog({ - title: '', - modal: true, - width: '900px', - position: ['center', 'center'], - resizable: false, - autoOpen: true, - open: function(event, ui) { - $('.ui-widget-overlay').on('click.rowEvolution',function(){ - $('.rowEvolutionPopover').dialog('close'); - }) - }, - close: function(event, ui) { - // reset multi evolution if regular close button has been used - if (typeof event.originalEvent != 'undefined') { - self.multiEvolutionRows = []; - } - piwikHelper.abortQueueAjax(); - $('.ui-widget-overlay').off('click.rowEvolution'); - box.find('div.jqplot-target').trigger('piwikDestroyPlot'); - box.dialog('destroy').remove(); - } - }); + var box = Piwik_Popover.showLoading('Row Evolution'); + box.addClass('rowEvolutionPopover'); // load the popover contents - var request = this.dataTable.buildAjaxRequest(function(html) { - box.html(html); - box.dialog({position: ['center', 'center']}); + var requestParams = { + apiMethod: apiMethod, + label: label, + disableLink: 1 + }; + + if (metric) { + requestParams.column = metric; + } + + var action = (isMultiRowEvolution ? 'getMultiRowEvolutionPopover' : 'getRowEvolutionPopover'); + + piwikHelper.ajaxCall('CoreHome', action, requestParams, function(html) { + Piwik_Popover.setContent(html); var title = box.find('div.popover-title'); if (title.size() > 0) { - box.dialog({title: title.html()}); + Piwik_Popover.setTitle(title.html()); title.remove(); } - // remember label for multi row evolution - box.find('a.rowevolution-startmulti').click(function() { - box.dialog('close'); - if ($.inArray(label, self.multiEvolutionRows) == -1) { - self.multiEvolutionRows.push(label); - } - return false; + 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('a.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(); - box.dialog('close'); - self.showRowEvolution(tr, label, metric); + self.showRowEvolution(apiMethod, label, isMultiRowEvolution, metric); return true; }); - }); - - var requestLabel = label; - var action = 'getRowEvolutionPopover'; - - if (self.multiEvolutionRows.length > 0) { - if ($.inArray(label, self.multiEvolutionRows) == -1) { - self.multiEvolutionRows.push(label); - } - if (self.multiEvolutionRows.length > 1) { - box.dialog({title: ''}); - action = 'getMultiRowEvolutionPopover'; - requestLabel = self.multiEvolutionRows.join(','); - } - } - - request.data = { - apiMethod: request.data.module + '.' + request.data.action, - module: 'CoreHome', - action: action, - date: request.data.date, - idSite: request.data.idSite, - period: request.data.period, - label: requestLabel, - segment: request.data.segment, - disableLink: 1 - }; - - if (metric) { - request.data.column = metric; - } - var token_auth = broadcast.getValueFromUrl('token_auth'); - if (token_auth.length && token_auth != 'anonymous') { - request.data.token_auth = token_auth; - } - - piwikHelper.queueAjaxRequest($.ajax(request)); -}; + }, alert, 'html'); +}; \ No newline at end of file diff --git a/plugins/CoreHome/templates/popover.js b/plugins/CoreHome/templates/popover.js index dacf309a7f..3120230132 100644 --- a/plugins/CoreHome/templates/popover.js +++ b/plugins/CoreHome/templates/popover.js @@ -9,6 +9,7 @@ var Piwik_Popover = (function() { var container = false; var isOpen = false; + var closeCallback = false; var createContainer = function() { if (container === false) { @@ -32,12 +33,17 @@ var Piwik_Popover = (function() { }); }, close: function(event, ui) { + container.find('div.jqplot-target').trigger('piwikDestroyPlot'); container[0].innerHTML = ''; // IE8 fix - container.dialog('destroy'); + container.dialog('destroy').remove(); piwikHelper.abortQueueAjax(); $('.ui-widget-overlay').off('click.popover'); isOpen = false; broadcast.propagateNewPopoverParameter(false); + if (typeof closeCallback == 'function') { + closeCallback(); + closeCallback = false; + } } }); @@ -86,6 +92,7 @@ var Piwik_Popover = (function() { } this.setContent(loading); + this.setTitle(''); if (height) { var offset = loading.height() - p1.outerHeight(); @@ -103,7 +110,7 @@ var Piwik_Popover = (function() { /** Add a help button to the current popover */ addHelpButton: function(helpUrl) { if (!isOpen) { - return false; + return; } var titlebar = container.parent().find('.ui-dialog-titlebar'); @@ -112,12 +119,20 @@ var Piwik_Popover = (function() { button.attr({href: helpUrl, target: '_blank'}); titlebar.append(button); - - //alert(titlebar.find('.ui-dialog-titlebar-close').outerWidth()); }, + /** Set the title of the popover */ + setTitle: function(titleHtml) { + container.dialog({title: titleHtml}); + }, + /** Set inner HTML of the popover */ setContent: function(html) { + if (typeof closeCallback == 'function') { + closeCallback(); + closeCallback = false; + } + container[0].innerHTML = ''; // IE8 fix container.html(html); centerPopover(); @@ -150,6 +165,11 @@ var Piwik_Popover = (function() { this.setContent(error); }, + + /** Add a callback for the next time the popover is closed or the content changes */ + onClose: function(callback) { + closeCallback = callback; + }, /** Close the popover */ close: function() { diff --git a/plugins/Transitions/templates/transitions.js b/plugins/Transitions/templates/transitions.js index 31825c7156..308507787b 100644 --- a/plugins/Transitions/templates/transitions.js +++ b/plugins/Transitions/templates/transitions.js @@ -242,7 +242,7 @@ Piwik_Transitions.prototype.prepareCanvas = function(canvasId, width, height) { var canvas; if (typeof Piwik_Transitions.canvasCache == 'undefined' - || typeof window.G_vmlCanvasManager != "undefined") { + || typeof window.G_vmlCanvasManager != "undefined") { // no recycling for excanvas because they are disposed properly anyway and // recycling doesn't work this way in IE8 Piwik_Transitions.canvasCache = {}; @@ -1373,66 +1373,46 @@ Piwik_Transitions_Ajax.prototype.callTransitionsController = function(action, ca }; Piwik_Transitions_Ajax.prototype.callApi = function(method, params, callback) { - params.module = 'API'; - params.method = method; - params.date = piwik.currentDateString; - params.idSite = piwik.idSite; - params.period = piwik.period; - params.token_auth = piwik.token_auth; + var self = this; + params.format = 'JSON'; - if (params.period == 'range') { - params.date = piwik.startDateString + ',' + params.date; - } + + piwikHelper.ajaxCallApi(method, params, callback, function(errorName) { + var showError = function() { + var errorTitle, errorMessage, errorBack; + if (typeof Piwik_Transitions_Translations[errorName] == 'undefined') { + errorTitle = 'Exception'; + errorMessage = errorName; + errorBack = '<<<'; + } else { + errorTitle = Piwik_Transitions_Translations[errorName]; + errorMessage = Piwik_Transitions_Translations[errorName + 'Details']; + errorBack = Piwik_Transitions_Translations[errorName + 'Back']; + } - var segment = broadcast.getValueFromHash('segment', window.location.href); - if (segment) { - params.segment = segment; - } + if (typeof params.actionName != 'undefined') { + var url = params.actionName; + url = Piwik_Transitions_Util.addBreakpointsToUrl(url); + errorTitle = errorTitle.replace(/%s/, '' + url + ''); + } - var self = this; - piwikHelper.queueAjaxRequest($.post('index.php', params, function(result) { - if (typeof result.result != 'undefined' && result.result == 'error') { - var errorName = result.message; - - var showError = function() { - var errorTitle, errorMessage, errorBack; - if (typeof Piwik_Transitions_Translations[errorName] == 'undefined') { - errorTitle = 'Exception'; - errorMessage = errorName; - errorBack = '<<<'; - } else { - errorTitle = Piwik_Transitions_Translations[errorName]; - errorMessage = Piwik_Transitions_Translations[errorName + 'Details']; - errorBack = Piwik_Transitions_Translations[errorName + 'Back']; - } + errorMessage = errorMessage.replace(/%s/g, '
'); + Piwik_Popover.showError(errorTitle, errorMessage, errorBack); + }; - if (typeof params.actionName != 'undefined') { - var url = params.actionName; - url = Piwik_Transitions_Util.addBreakpointsToUrl(url); - errorTitle = errorTitle.replace(/%s/, '' + url + ''); + if (typeof Piwik_Transitions_Translations == 'undefined') { + self.callApi('Transitions.getTranslations', {}, function(response) { + if (typeof response[0] == 'object') { + Piwik_Transitions_Translations = response[0]; + } else { + Piwik_Transitions_Translations = {}; } - - errorMessage = errorMessage.replace(/%s/g, '
'); - Piwik_Popover.showError(errorTitle, errorMessage, errorBack); - }; - - if (typeof Piwik_Transitions_Translations == 'undefined') { - self.callApi('Transitions.getTranslations', {}, function(response) { - if (typeof response[0] == 'object') { - Piwik_Transitions_Translations = response[0]; - } else { - Piwik_Transitions_Translations = {}; - } - showError(); - }); - } else { showError(); - } - + }); } else { - callback(result); + showError(); } - }, 'json')); + }); }; diff --git a/themes/default/common.js b/themes/default/common.js index 1f341e4b89..7fdc6c6783 100644 --- a/themes/default/common.js +++ b/themes/default/common.js @@ -79,6 +79,75 @@ var piwikHelper = { } }, + /** + * Call an API method + * + * @param method API method name, i.e. Plugin.Method + * @param params parameters for the request + * @param callback regular callback + * @param exceptionCallback callback for when the API returns an error, i.e. PHP Exception + * + * @return {void} + */ + ajaxCallApi: function(method, params, callback, exceptionCallback) + { + params.method = method; + piwikHelper.ajaxCall('API', false, params, callback, exceptionCallback); + }, + + /** + * Do an AJAX request + * + * @param module plugin name + * @param action method name + * @param params parameters for the request + * @param callback regular callback + * @param exceptionCallback callback for when the API returns an error, i.e. PHP Exception + * @param format response format, default json + * + * @return {void} + */ + ajaxCall: function(module, action, params, callback, exceptionCallback, format) { + params.module = module; + if (action) { + params.action = action; + } + + params.date = piwik.currentDateString; + params.idSite = piwik.idSite; + params.period = piwik.period; + params.token_auth = piwik.token_auth; + if (params.period == 'range') + { + params.date = piwik.startDateString + ',' + params.date; + } + + var segment = broadcast.getValueFromHash('segment', window.location.href); + if (segment) + { + params.segment = segment; + } + + piwikHelper.queueAjaxRequest($.get('index.php', params, function(result) + { + if (typeof result.result != 'undefined' && result.result == 'error') + { + if (typeof exceptionCallback == 'function') + { + exceptionCallback(result.message); + } + else + { + alert(result.message); + } + } + else + { + callback(result); + } + }, format || 'json')); + }, + /** * Aborts all registered running ajax requests * @return {Boolean} -- cgit v1.2.3