/*! * Matomo - free/libre analytics platform * * @link https://matomo.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ function widgetsHelper() { } // a Promise for the first call to getAvailableWidgets. this should not be aborted, // so any code that aborts all ajax requests should make sure this promise is resolved // first. widgetsHelper.firstGetAvailableWidgetsCall = null; /** * Returns the available widgets fetched via AJAX (if not already done) * * @return {object} object containing available widgets */ widgetsHelper.getAvailableWidgets = function (callback) { function mergeCategoriesAndSubCategories(availableWidgets) { var categorized = {}; $.each(availableWidgets, function (index, widget) { var category = widget.category.name; if (!categorized[category]) { categorized[category] = {'-': []}; } var subcategory = '-'; if (widget.subcategory && widget.subcategory.name) { subcategory = widget.subcategory.name; } if (!categorized[category][subcategory]) { categorized[category][subcategory] = []; } categorized[category][subcategory].push(widget); }); var moved = {}; $.each(categorized, function (category, widgets) { $.each(widgets, function (subcategory, subwidgets) { var categoryToUse = category; if (subwidgets.length >= 3 && subcategory !== '-') { categoryToUse = category + ' - ' + subcategory; } if (!moved[categoryToUse]) { moved[categoryToUse] = []; } $.each(subwidgets, function (index, widget) { moved[categoryToUse].push(widget); }); }); }); return moved; } var promise = new Promise(function (resolve, reject) { if (!widgetsHelper.availableWidgets) { var ajaxRequest = new ajaxHelper(); ajaxRequest._mixinDefaultGetParams = function (params) { return params; }; ajaxRequest.addParams({ module: 'API', method: 'API.getWidgetMetadata', filter_limit: '-1', format: 'JSON', deep: '1', idSite: piwik.idSite || broadcast.getValueFromUrl('idSite') }, 'get'); ajaxRequest.setCallback( function (data) { widgetsHelper.availableWidgets = mergeCategoriesAndSubCategories(data); resolve(); } ); ajaxRequest.setErrorCallback(function (deferred, status) { if (status == 'abort' || !deferred || deferred.status < 400 || deferred.status >= 600) { return; } $('#loadingError').show(); reject(); }); ajaxRequest.send(); return; } resolve(); }); if (!widgetsHelper.firstGetAvailableWidgetsCall) { widgetsHelper.firstGetAvailableWidgetsCall = promise; } promise.then(function () { if (callback) { callback(widgetsHelper.availableWidgets); } }); }; /** * Determines the complete widget object by its unique id and sends it to callback method * * @param {string} uniqueId * @param {function} callback */ widgetsHelper.getWidgetObjectFromUniqueId = function (uniqueId, callback) { widgetsHelper.getAvailableWidgets(function(widgets){ for (var widgetCategory in widgets) { var widgetInCategory = widgets[widgetCategory]; for (var i in widgetInCategory) { if (widgetInCategory[i]["uniqueId"] == uniqueId) { callback(widgetInCategory[i]); return; } } } callback(false); }); }; /** * Determines the name of a widget by its unique id and sends it to the callback * * @param {string} uniqueId unique id of the widget * @param {function} callback * @return {string} */ widgetsHelper.getWidgetNameFromUniqueId = function (uniqueId, callback) { this.getWidgetObjectFromUniqueId(uniqueId, function(widget) { if (widget == false) { callback(false); } callback(widget["name"]); }); }; /** * Sends and ajax request to query for the widgets html * * @param {string} widgetUniqueId unique id of the widget * @param {object} widgetParameters parameters to be used for loading the widget * @param {function} onWidgetLoadedCallback callback to be executed after widget is loaded * @return {object} */ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWidgetLoadedCallback, onWidgetErrorCallback) { var disableLink = broadcast.getValueFromUrl('disableLink'); widgetParameters['disableLink'] = disableLink.length || $('body#standalone').length; widgetParameters['widget'] = 1; var ajaxRequest = new ajaxHelper(); ajaxRequest.addParams(widgetParameters, 'get'); ajaxRequest.setCallback(onWidgetLoadedCallback); if (onWidgetErrorCallback) { ajaxRequest.setErrorCallback(onWidgetErrorCallback); } ajaxRequest.setFormat('html'); ajaxRequest.send(); return ajaxRequest; }; (function ($, require) { var exports = require('piwik/UI/Dashboard'); /** * Singleton instance that creates widget elements. Normally not needed even * when embedding/re-using dashboard widgets, but it can be useful when creating * elements with the same look and feel as dashboard widgets, but different * behavior (such as the widget preview in the dashboard manager control). * * @constructor */ var WidgetFactory = function () { // empty }; /** * Creates an HTML element for displaying a widget. * * @param {string} uniqueId unique id of the widget * @param {string} widgetName name of the widget * @return {Element} the empty widget */ WidgetFactory.prototype.make = function (uniqueId, widgetName) { var $result = this.getWidgetTemplate().clone(); $result.attr('id', uniqueId).find('.widgetName').append(widgetName); return $result; }; /** * Returns the base widget template element. The template is stored in the * element with id == 'widgetTemplate'. * * @return {Element} the widget template */ WidgetFactory.prototype.getWidgetTemplate = function () { if (!this.widgetTemplate) { this.widgetTemplate = $('#widgetTemplate').find('>.widget').detach(); } return this.widgetTemplate; }; exports.WidgetFactory = new WidgetFactory(); })(jQuery, require); /** * widgetPreview jQuery Extension * * Converts an dom element to a widget preview * Widget preview contains an categorylist, widgetlist and a preview */ (function ($) { $.extend({ widgetPreview: new function () { /** * Default settings for widgetPreview * @type {object} */ var defaultSettings = { /** * handler called after a widget preview is loaded in preview element * @type {function} */ onPreviewLoaded: function () {}, /** * handler called on click on element in widgetlist or widget header * @type {function} */ onSelect: function () {}, /** * callback used to determine if a widget is available or not * unavailable widgets aren't chooseable in widgetlist * @type {function} */ isWidgetAvailable: function (widgetUniqueId) { return true; }, /** * should the lists and preview be reset on widget selection? * @type {boolean} */ resetOnSelect: false, /** * css classes for various elements * @type {string} */ baseClass: 'widgetpreview-base', categorylistClass: 'widgetpreview-categorylist', widgetlistClass: 'widgetpreview-widgetlist', widgetpreviewClass: 'widgetpreview-preview', choosenClass: 'widgetpreview-choosen', unavailableClass: 'widgetpreview-unavailable' }; /** * Returns the div to show category list in * - if element doesn't exist it will be created and added * - if element already exist it's content will be removed * * @return {$} category list element */ function createWidgetCategoryList(widgetPreview, availableWidgets) { var settings = widgetPreview.settings; if (!$('.' + settings.categorylistClass, widgetPreview).length) { $(widgetPreview).append('