/*! * Matomo - free/libre analytics platform * * @link https://matomo.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ var Piwik_Overlay = (function () { var DOMAIN_PARSE_REGEX = /^http(s)?:\/\/(www\.)?([^\/]*)/i; var ORIGIN_PARSE_REGEX = /^https?:\/\/[^\/]*/; var ALLOWED_API_REQUEST_WHITELIST = [ 'Overlay.getTranslations', 'Overlay.getExcludedQueryParameters', 'Overlay.getFollowingPages', ]; var $body, $iframe, $sidebar, $main, $location, $loading, $errorNotLoading; var $rowEvolutionLink, $transitionsLink, $visitorLogLink; var idSite, period, date, segment; var iframeSrcBase; var iframeDomain = ''; var iframeCurrentPage = ''; var iframeCurrentPageNormalized = ''; var iframeCurrentActionLabel = ''; var updateComesFromInsideFrame = false; var iframeOrigin = ''; /** Load the sidebar for a url */ function loadSidebar(currentUrl) { showLoading(); $location.html(' ').unbind('mouseenter').unbind('mouseleave'); iframeCurrentPage = currentUrl; iframeDomain = currentUrl.match(DOMAIN_PARSE_REGEX)[3]; var params = { module: 'Overlay', action: 'renderSidebar', currentUrl: currentUrl }; if (segment) { params.segment = segment; } globalAjaxQueue.abort(); var ajaxRequest = new ajaxHelper(); ajaxRequest.addParams(params, 'get'); ajaxRequest.withTokenInUrl(); // needed because it is calling a controller and not the API ajaxRequest.setCallback( function (response) { hideLoading(); var $response = $(response); var $responseLocation = $response.find('.overlayLocation'); var $url = $responseLocation.find('span'); iframeCurrentPageNormalized = $url.data('normalizedUrl'); iframeCurrentActionLabel = $url.data('label'); $url.html(piwikHelper.addBreakpointsToUrl($url.text())); $location.html($responseLocation.html()).show(); $responseLocation.remove(); var $locationSpan = $location.find('span'); $locationSpan.html(piwikHelper.addBreakpointsToUrl($locationSpan.text())); if (iframeDomain) { // use addBreakpointsToUrl because it also encoded html entities $locationSpan.tooltip({ track: true, items: '*', tooltipClass: 'overlayTooltip', content: '' + Piwik_Overlay_Translations.domain + ': ' + piwikHelper.addBreakpointsToUrl(iframeDomain), show: false, hide: false }); } $sidebar.empty().append($response).show(); if (!$sidebar.find('.overlayNoData').length) { $rowEvolutionLink.show(); $transitionsLink.show(); if ($('#segment').val() && piwik.visitorLogEnabled) { $visitorLogLink.show(); } } } ); ajaxRequest.setErrorCallback(function () { hideLoading(); $errorNotLoading.show(); }); ajaxRequest.setFormat('html'); ajaxRequest.send(); } /** Adjust the dimensions of the iframe */ function adjustDimensions() { $iframe.height($(window).height()); $iframe.width($body.width() - $iframe.offset().left - 2); // -2 because of 2px border } /** Display the loading message and hide other containers */ function showLoading() { $loading.show(); $sidebar.hide(); $location.hide(); $rowEvolutionLink.hide(); $transitionsLink.hide(); $visitorLogLink.hide(); $errorNotLoading.hide(); } /** Hide the loading message */ function hideLoading() { $loading.hide(); $('#overlayDateRangeSelect').prop('disabled', false).material_select(); } function getOverlaySegment(url) { var location = broadcast.getParamValue('segment', url); // angular will encode the value again since it is added as the fragment path, not the fragment query parameter, // so we have to decode it again after getParamValue location = decodeURIComponent(location); return location; } function getOverlayLocationFromHash(urlHash) { var location = broadcast.getParamValue('l', urlHash); // angular will encode the value again since it is added as the fragment path, not the fragment query parameter, // so we have to decode it again after getParamValue location = decodeURIComponent(location); return location; } function setIframeOrigin(location) { var m = location.match(ORIGIN_PARSE_REGEX); iframeOrigin = m ? m[0] : null; var foundValidSiteUrl = false; // unset iframe origin if it is not one of the site URLs var validSiteOrigins = Piwik_Overlay.siteUrls.map(function (url) { if (typeof url === 'string' && url !== "") { foundValidSiteUrl = true; } var siteUrlMatch = url.match(ORIGIN_PARSE_REGEX); if (!siteUrlMatch) { return null; } return siteUrlMatch[0].toLowerCase(); }); if (!foundValidSiteUrl) { $('#overlayErrorNoSiteUrls').show(); } if (iframeOrigin && validSiteOrigins.indexOf(iframeOrigin.toLowerCase()) === -1) { try { console.log('Found invalid iframe origin in hash URL: ' + iframeOrigin); } catch (e) { // ignore } iframeOrigin = null; } } /** $.history callback for hash change */ function hashChangeCallback(urlHash) { var location = getOverlayLocationFromHash(urlHash); location = Overlay_Helper.decodeFrameUrl(location); setIframeOrigin(location); if (location == iframeCurrentPageNormalized) { return; } if (!updateComesFromInsideFrame) { var iframeUrl = iframeSrcBase; if (location) { iframeUrl += '#' + location; } $iframe.attr('src', iframeUrl); showLoading(); } else { loadSidebar(location); } updateComesFromInsideFrame = false; } function handleApiRequests() { window.addEventListener("message", function (event) { if (event.origin !== iframeOrigin || !iframeOrigin) { return; } if (typeof event.data !== 'string') { return; // some other message not intended for us } var strData = event.data.split(':', 3); if (strData[0] !== 'overlay.call') { return; } var requestId = strData[1]; var url = decodeURIComponent(strData[2]); var params = broadcast.getValuesFromUrl(url); Object.keys(params).forEach(function (name) { params[name] = decodeURIComponent(params[name]); }); params.module = 'API'; params.action = 'index'; // these should be sent as post parameters delete params.token_auth; delete params.force_api_session; if (ALLOWED_API_REQUEST_WHITELIST.indexOf(params.method) === -1) { sendResponse({ result: 'error', message: "'" + params.method + "' method is not allowed.", }); return; } angular.element(document).injector().invoke(['piwikApi', function (piwikApi) { piwikApi.withTokenInUrl(); piwikApi.fetch(params) .then(function (response) { sendResponse(response); }).catch(function (err) { sendResponse({ result: 'error', message: err.message || err || 'unknown error', }); }); }]); function sendResponse(data) { var message = 'overlay.response:' + requestId + ':' + encodeURIComponent(JSON.stringify(data)); $iframe[0].contentWindow.postMessage(message, iframeOrigin); } }, false); } return { /** This method is called when Overlay loads */ init: function (iframeSrc, pIdSite, pPeriod, pDate, pSegment) { iframeSrcBase = iframeSrc; idSite = pIdSite; period = pPeriod; date = pDate; segment = pSegment; $body = $('body'); $iframe = $('#overlayIframe'); $sidebar = $('#overlaySidebar'); $location = $('#overlayLocation'); $main = $('#overlayMain'); $loading = $('#overlayLoading'); $errorNotLoading = $('#overlayErrorNotLoading'); $rowEvolutionLink = $('#overlayRowEvolution'); $transitionsLink = $('#overlayTransitions'); $visitorLogLink = $('#overlaySegmentedVisitorLog'); adjustDimensions(); showLoading(); // apply initial dimensions window.setTimeout(function () { adjustDimensions(); }, 50); // handle window resize $(window).resize(function () { adjustDimensions(); }); angular.element(document).injector().invoke(function ($rootScope) { $rootScope.$on('$locationChangeSuccess', function () { hashChangeCallback(broadcast.getHash()); }); hashChangeCallback(broadcast.getHash()); }); if (window.location.href.split('#').length == 1) { hashChangeCallback(''); } handleApiRequests(); // handle date selection var $select = $('select#overlayDateRangeSelect').change(function () { var parts = $(this).val().split(';'); if (parts.length == 2) { period = parts[0]; date = parts[1]; window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, segment, iframeCurrentPage); } }); var optionMatchFound = false; $select.find('option').each(function () { if ($(this).val() == period + ';' + date) { $(this).prop('selected', true); optionMatchFound = true; } }); if (optionMatchFound) { $select.material_select(); } else { $select.prepend('