/*! * Piwik - free/libre analytics platform * * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ function _pk_translate(translationStringId, values) { function sprintf (translation, values) { var index = 0; return (translation+'').replace(/(%(.\$)?s+)/g, function(match, number) { var replaced = match; if (match != '%s') { index = parseInt(match.substr(1, 1)) - 1; } if (typeof values[index] != 'undefined') { replaced = values[index]; } index++; return replaced; }); } if( typeof(piwik_translations[translationStringId]) != 'undefined' ){ var translation = piwik_translations[translationStringId]; if (typeof values != 'undefined' && values && values.length) { return sprintf(translation, values); } return translation; } return "The string "+translationStringId+" was not loaded in javascript. Make sure it is added in the Translate.getClientSideTranslationKeys hook."; } var piwikHelper = { htmlDecode: function(value) { return $('
').html(value).text(); }, /** * a nice cross-browser logging function */ log: function() { try { console.log.apply(console, arguments); // Firefox, Chrome } catch (e) { try { opera.postError.apply(opera, arguments); // Opera } catch (f) { // don't alert as log is not considered to be important enough // (as opposed to piwikHelper.error) //alert(Array.prototype.join.call(arguments, ' ')); // MSIE } } }, error: function() { try { console.error.apply(console, arguments); // Firefox, Chrome } catch (e) { try { opera.postError.apply(opera, arguments); // Opera } catch (f) { alert(Array.prototype.join.call(arguments, ' ')); // MSIE } } }, htmlEntities: function(value) { var findReplace = [[/&/g, "&"], [//g, ">"], [/"/g, """]]; for(var item in findReplace) { value = value.replace(findReplace[item][0], findReplace[item][1]); } return value; }, /** * Add break points to a string so that it can be displayed more compactly */ addBreakpoints: function(text, breakpointMarkup) { return text.replace(/([\/&=?\.%#:_-])/g, '$1' + (typeof breakpointMarkup == 'undefined' ? '​' : breakpointMarkup)); // ​ is for internet explorer }, /** * Add breakpoints to a URL * urldecodes and encodes htmlentities to display utf8 urls without XSS vulnerabilities */ addBreakpointsToUrl: function(url) { try { url = decodeURIComponent(url); } catch (e) { // might throw "URI malformed" } url = piwikHelper.addBreakpoints(url, '|||'); url = $(document.createElement('p')).text(url).html(); url = url.replace(/\|\|\|/g, '​'); // ​ is for internet explorer return url; }, /** * As we still have a lot of old jQuery code and copy html from node to node we sometimes have to trigger the * compiling of angular components manually. * * @param selector */ compileAngularComponents: function (selector) { var $element = $(selector); angular.element(document).injector().invoke(function($compile) { var scope = angular.element($element).scope(); $compile($element)(scope); }); }, /** * Displays a Modal dialog. Text will be taken from the DOM node domSelector. * Given callback handles will be mapped to the buttons having a role attriute * * Dialog will be closed when a button is clicked and callback handle will be * called, if one was given for the clicked role * * @param {string} domSelector domSelector for modal window * @param {object} handles callback functions for available roles * @return {void} */ modalConfirm: function( domSelector, handles ) { var domElem = $(domSelector); var buttons = []; $('[role]', domElem).each(function(){ var role = $(this).attr('role'); var title = $(this).attr('title'); var text = $(this).val(); var button = {text: text}; if(typeof handles[role] == 'function') { button.click = function(){$(this).dialog("close"); handles[role].apply()}; } else { button.click = function(){$(this).dialog("close");}; } if (title) { button.title = title; } buttons.push(button); $(this).hide(); }); domElem.dialog({ resizable: false, modal: true, buttons: buttons, width: 650, position: ['center', 90] }); }, getQueryStringWithParametersModified: function (queryString, newParameters) { if (queryString != '') { var r, i, keyvalue, keysvalues = newParameters.split('&'); var appendUrl = ''; for (i = 0; i < keysvalues.length; i++) { keyvalue = keysvalues[i].split('='); r = new RegExp('(^|[?&])' + keyvalue[0] + '=[^&]*'); queryString = queryString.replace(r, ''); // empty value, eg. &segment=, we remove the parameter from URL entirely if (keyvalue[1].length == 0) { continue; } appendUrl += '&' + keyvalue[0] + '=' + keyvalue[1]; } queryString += appendUrl; if (queryString[0] == '&') { queryString = '?' + queryString.substring(1); } } else { queryString = '?' + newParameters; } return queryString; }, /** * Returns the current query string with the given parameters modified * @param {String} newparams parameters to be modified * @return {String} */ getCurrentQueryStringWithParametersModified: function(newparams) { var queryString = String(window.location.search); if (newparams) { queryString = this.getQueryStringWithParametersModified(queryString, newparams); } return String(window.location.pathname) + queryString; }, /** * Given param1=v1¶m2=k2 * returns: { "param1": "v1", "param2": "v2" } * * @param query string * @return {Object} */ getArrayFromQueryString: function (query) { var params = {}; var vars = query.split("&"); for (var i=0;i $(window).scrollTop()+$(window).height() || forceScroll ) { // scroll the page smoothly to the graph $.scrollTo(elem, time); } }, /** * Returns the filtered/converted content of a textarea to be used for api requests * @param {string} textareaContent * @return {string} */ getApiFormatTextarea: function (textareaContent) { if(typeof textareaContent == 'undefined') { return ''; } return textareaContent.trim().split("\n").join(','); } }; String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,""); }; /** * Returns true if the event keypress passed in parameter is the ENTER key * @param {Event} e current window event * @return {boolean} */ function isEnterKey(e) { return (window.event?window.event.keyCode:e.which)==13; } // workarounds (function($){ try { // this code is not vital, so we make sure any errors are ignored //-------------------------------------- // // monkey patch that works around bug in arc function of some browsers where // nothing gets drawn if angles are 2 * PI apart and in counter-clockwise direction. // affects some versions of chrome & IE 8 // //-------------------------------------- var oldArc = CanvasRenderingContext2D.prototype.arc; CanvasRenderingContext2D.prototype.arc = function(x, y, r, sAngle, eAngle, clockwise) { if (Math.abs(eAngle - sAngle - Math.PI * 2) < 0.000001 && !clockwise) eAngle -= 0.000001; oldArc.call(this, x, y, r, sAngle, eAngle, clockwise); }; //-------------------------------------- // // Array.reduce is not available in IE8 but used in Jqplot // //-------------------------------------- if ('function' !== typeof Array.prototype.reduce) { Array.prototype.reduce = function(callback, opt_initialValue){ 'use strict'; if (null === this || 'undefined' === typeof this) { // At the moment all modern browsers, that support strict mode, have // native implementation of Array.prototype.reduce. For instance, IE8 // does not support strict mode, so this check is actually useless. throw new TypeError( 'Array.prototype.reduce called on null or undefined'); } if ('function' !== typeof callback) { throw new TypeError(callback + ' is not a function'); } var index, value, length = this.length >>> 0, isValueSet = false; if (1 < arguments.length) { value = opt_initialValue; isValueSet = true; } for (index = 0; length > index; ++index) { if (this.hasOwnProperty(index)) { if (isValueSet) { value = callback(value, this[index], index, this); } else { value = this[index]; isValueSet = true; } } } if (!isValueSet) { throw new TypeError('Reduce of empty array with no initial value'); } return value; }; } } catch (e) {} }(jQuery));