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

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Overlay/client/followingpages.js')
-rw-r--r--plugins/Overlay/client/followingpages.js1018
1 files changed, 509 insertions, 509 deletions
diff --git a/plugins/Overlay/client/followingpages.js b/plugins/Overlay/client/followingpages.js
index edb9c40e1e..e20252463f 100644
--- a/plugins/Overlay/client/followingpages.js
+++ b/plugins/Overlay/client/followingpages.js
@@ -1,511 +1,511 @@
-var Piwik_Overlay_FollowingPages = (function() {
-
- /** jQuery */
- var $ = jQuery;
-
- /** Info about the following pages */
- var followingPages = [];
-
- /** List of excluded get parameters */
- var excludedParams = [];
-
- /** Index of the links on the page */
- var linksOnPage = {};
-
- /** Reference to create element function */
- var c;
-
- /** Load the following pages */
- function load(callback) {
- // normalize current location
- var location = window.location.href;
- location = Piwik_Overlay_UrlNormalizer.normalize(location);
- location = (("https:" == document.location.protocol) ? 'https' : 'http') + '://' + location;
-
- var excludedParamsLoaded = false;
- var followingPagesLoaded = false;
-
- // load excluded params
- Piwik_Overlay_Client.api('getExcludedQueryParameters', function(data) {
- for (var i = 0; i < data.length; i++) {
- if (typeof data[i] == 'object') {
- data[i] = data[i][0];
- }
- }
- excludedParams = data;
-
- excludedParamsLoaded = true;
- if (followingPagesLoaded) {
- callback();
- }
- });
-
- // load following pages
- Piwik_Overlay_Client.api('getFollowingPages', function(data) {
- followingPages = data;
- processFollowingPages();
-
- followingPagesLoaded = true;
- if (excludedParamsLoaded) {
- callback();
- }
- }, 'url=' + encodeURIComponent(location));
- }
-
- /** Normalize the URLs of following pages and aggregate some stats */
- function processFollowingPages() {
- var totalClicks = 0;
- for (var i = 0; i < followingPages.length; i++) {
- var page = followingPages[i];
- // though the following pages are returned without the prefix, downloads
- // and outlinks still have it.
- page.label = Piwik_Overlay_UrlNormalizer.removeUrlPrefix(page.label);
- totalClicks += followingPages[i].referrals;
- }
- for (i = 0; i < followingPages.length; i++) {
- followingPages[i].clickRate = followingPages[i].referrals / totalClicks * 100;
- }
- }
-
- /**
- * Build an index of links on the page.
- * This function is passed to $('a').each()
- */
- var processLinkDelta = false;
-
- function processLink() {
- var a = $(this);
- a[0].piwikDiscovered = true;
-
- var href = a.attr('href');
- href = Piwik_Overlay_UrlNormalizer.normalize(href);
-
- if (href) {
- if (typeof linksOnPage[href] == 'undefined') {
- linksOnPage[href] = [a];
- }
- else {
- linksOnPage[href].push(a);
- }
- }
-
- if (href && processLinkDelta !== false) {
- if (typeof processLinkDelta[href] == 'undefined') {
- processLinkDelta[href] = [a];
- }
- else {
- processLinkDelta[href].push(a);
- }
- }
- }
-
- var repositionTimeout = false;
- var resizeTimeout = false;
-
- function build(callback) {
- // build an index of all links on the page
- $('a').each(processLink);
-
- // add tags to known following pages
- createLinkTags(linksOnPage);
-
- // position the tags
- positionLinkTags();
-
- callback();
-
- // check on a regular basis whether new links have appeared.
- // we use a timeout instead of an interval to make sure one call is done before
- // the next one is triggered
- var repositionAfterTimeout;
- repositionAfterTimeout = function() {
- repositionTimeout = window.setTimeout(function() {
- findNewLinks();
- positionLinkTags(repositionAfterTimeout);
- }, 1800);
- };
- repositionAfterTimeout();
-
- // reposition link tags on window resize
- $(window).resize(function() {
- if (repositionTimeout) {
- window.clearTimeout(repositionTimeout);
- }
- if (resizeTimeout) {
- window.clearTimeout(resizeTimeout);
- }
- resizeTimeout = window.setTimeout(function() {
- positionLinkTags();
- repositionAfterTimeout();
- }, 70);
- });
- }
-
- /** Create a batch of link tags */
- function createLinkTags(links) {
- var body = $('body');
- for (var i = 0; i < followingPages.length; i++) {
- var url = followingPages[i].label;
- if (typeof links[url] != 'undefined') {
- for (var j = 0; j < links[url].length; j++) {
- createLinkTag(links[url][j], url, followingPages[i], body);
- }
- }
- }
- }
-
- /** Create the link tag element */
- function createLinkTag(linkTag, linkUrl, data, body) {
- if (typeof linkTag[0].piwikTagElement != 'undefined' && linkTag[0].piwikTagElement !== null) {
- // this link tag already has a tag element. happens in rare cases.
- return;
- }
-
- linkTag[0].piwikTagElement = true;
-
- var rate = data.clickRate;
- if (rate < 10) {
- rate = Math.round(rate * 10) / 10;
- } else {
- rate = Math.round(rate);
- }
-
- var span = c('span').html(rate + '%');
- var tagElement = c('div', 'LinkTag').append(span).hide();
- body.prepend(tagElement);
-
- linkTag.add(tagElement).hover(function() {
- highlightLink(linkTag, linkUrl, data);
- }, function() {
- unHighlightLink(linkTag, linkUrl);
- });
-
- // attach the tag element to the link element. we can't use .data() because jquery
- // would remove it when removing the link from the dom. but we still need to find
- // the tag element to remove it as well.
- linkTag[0].piwikTagElement = tagElement;
- }
-
- /** Position the link tags next to the links */
- function positionLinkTags(callback) {
- var url, linkTag, tagElement, offset, top, left, isRight, hasOneChild, inlineChild;
- var tagWidth = 36, tagHeight = 21;
- var tagsToRemove = [];
-
- for (var i = 0; i < followingPages.length; i++) {
- url = followingPages[i].label;
- if (typeof linksOnPage[url] != 'undefined') {
- for (var j = 0; j < linksOnPage[url].length; j++) {
- linkTag = linksOnPage[url][j];
- tagElement = linkTag[0].piwikTagElement;
-
- if (linkTag.closest('html').length == 0 || !tagElement) {
- // the link has been removed from the dom
- if (tagElement) {
- tagElement.hide();
- }
- // mark for deletion. don't delete it now because we
- // are iterating of the array it's in. it will be deleted
- // below this for loop.
- tagsToRemove.push({
- index1: url,
- index2: j
- });
- continue;
- }
-
- hasOneChild = checkHasOneChild(linkTag);
- inlineChild = false;
- if (hasOneChild && linkTag.css('display') != 'block') {
- inlineChild = linkTag.children().eq(0);
- }
-
- if (getVisibility(linkTag) == 'hidden' || (
- // in case of hasOneChild: jquery always returns linkTag.is(':visible')=false
- !linkTag.is(':visible') && !(hasOneChild && inlineChild && inlineChild.is(':visible'))
- )) {
- // link is not visible
- tagElement.hide();
- continue;
- }
-
- tagElement.attr('class', 'PIS_LinkTag'); // reset class
- if (tagElement[0].piwikHighlighted) {
- tagElement.addClass('PIS_Highlighted');
- }
-
- // see comment in highlightLink()
- if (hasOneChild && linkTag.find('> img').size() == 1) {
- offset = linkTag.find('> img').offset();
- if (offset.left == 0 && offset.top == 0) {
- offset = linkTag.offset();
- }
- } else if (inlineChild !== false) {
- offset = inlineChild.offset();
- } else {
- offset = linkTag.offset();
- }
-
- top = offset.top - tagHeight + 6;
- left = offset.left - tagWidth + 10;
-
- if (isRight = (left < 2)) {
- tagElement.addClass('PIS_Right');
- left = offset.left + linkTag.outerWidth() - 10;
- }
-
- if (top < 2) {
- tagElement.addClass(isRight ? 'PIS_BottomRight' : 'PIS_Bottom');
- top = offset.top + linkTag.outerHeight() - 6;
- }
-
- tagElement.css({
- top: top + 'px',
- left: left + 'px'
- }).show();
-
- }
- }
- }
-
- // walk tagsToRemove from back to front because it contains the indexes in ascending
- // order. removing something from the front will impact the indexes that come after-
- // wards. this can be avoided by starting in the back.
- for (var k = tagsToRemove.length - 1; k >= 0; k--) {
- var tagToRemove = tagsToRemove[k];
- linkTag = linksOnPage[tagToRemove.index1][tagToRemove.index2];
- // remove the tag element from the dom
- if (linkTag && linkTag[0] && linkTag[0].piwikTagElement) {
- tagElement = linkTag[0].piwikTagElement;
- if (tagElement[0].piwikHighlighted) {
- unHighlightLink(linkTag, tagToRemove.index1);
- }
- tagElement.remove();
- linkTag[0].piwikTagElement = null;
- }
- // remove the link from the index
- linksOnPage[tagToRemove.index1].splice(tagToRemove.index2, 1);
- if (linksOnPage[tagToRemove.index1].length == 0) {
- delete linksOnPage[tagToRemove.index1];
- }
- }
-
- if (typeof callback == 'function') {
- callback();
- }
- }
-
- /** Get the visibility of an element */
- function getVisibility(el) {
- var visibility = el.css('visibility');
- if (visibility == 'inherit') {
- el = el.parent();
- if (el.size() > 0) {
- return getVisibility(el);
- }
- }
- return visibility;
- }
-
- /**
- * Find out whether a link has only one child. Using .children().size() == 1 doesn't work
- * because it doesn't take additional text nodes into account.
- */
- function checkHasOneChild(linkTag) {
- var hasOneChild = (linkTag.children().size() == 1);
- if (hasOneChild) {
- // if the element contains one tag and some text, hasOneChild is set incorrectly
- var contents = linkTag.contents();
- if (contents.size() > 1) {
- // find non-empty text nodes
- contents = contents.filter(function() {
- return this.nodeType == 3 && // text node
- $.trim(this.data).length > 0; // contains more than whitespaces
- });
- if (contents.size() > 0) {
- hasOneChild = false;
- }
- }
- }
- return hasOneChild;
- }
-
- /** Check whether new links have been added to the dom */
- function findNewLinks() {
- var newLinks = $('a').filter(function() {
- return typeof this.piwikDiscovered == 'undefined' || this.piwikDiscovered === null;
- });
-
- if (newLinks.size() == 0) {
- return;
- }
-
- processLinkDelta = {};
- newLinks.each(processLink);
- createLinkTags(processLinkDelta);
- processLinkDelta = false;
- }
-
- /** Dom elements used for drawing a box around the link */
- var highlightElements = [];
-
- /** Highlight a link on hover */
- function highlightLink(linkTag, linkUrl, data) {
- if (highlightElements.length == 0) {
- highlightElements.push(c('div', 'LinkHighlightBoxTop'));
- highlightElements.push(c('div', 'LinkHighlightBoxRight'));
- highlightElements.push(c('div', 'LinkHighlightBoxLeft'));
-
- highlightElements.push(c('div', 'LinkHighlightBoxText'));
-
- var body = $('body');
- for (var i = 0; i < highlightElements.length; i++) {
- body.prepend(highlightElements[i].css({display: 'none'}));
- }
- }
-
- var width = linkTag.outerWidth();
-
- var offset, height;
- var hasOneChild = checkHasOneChild(linkTag);
- if (hasOneChild && linkTag.find('img').size() == 1) {
- // if the <a> tag contains only an <img>, the offset and height methods don't work properly.
- // as a result, the box around the image link would be wrong. we use the image to derive
- // the offset and height instead of the link to get correct values.
- var img = linkTag.find('img');
- offset = img.offset();
- height = img.outerHeight();
- }
- if (hasOneChild && linkTag.css('display') != 'block') {
- // if the <a> tag is not displayed as block and has only one child, using the child to
- // derive the offset and dimensions is more robust.
- var child = linkTag.children().eq(0);
- offset = child.offset();
- height = child.outerHeight();
- width = child.outerWidth();
- } else {
- offset = linkTag.offset();
- height = linkTag.outerHeight();
- }
-
- highlightElements[0].width(width).css({top: offset.top - 2, left: offset.left}).show();
- highlightElements[1].height(height + 4).css({top: offset.top - 2, left: offset.left + width}).show();
- highlightElements[2].height(height + 4).css({top: offset.top - 2, left: offset.left - 2}).show();
-
- var numLinks = linksOnPage[linkUrl].length;
- var text;
- if (numLinks > 1) {
- text = Piwik_Overlay_Translations.get('clicksFromXLinks')
- .replace(/%1\$s/, data.referrals)
- .replace(/%2\$s/, numLinks);
- } else if (data.referrals == 1) {
- text = Piwik_Overlay_Translations.get('oneClick');
- } else {
- text = Piwik_Overlay_Translations.get('clicks')
- .replace(/%s/, data.referrals);
- }
-
- var padding = '&nbsp;&nbsp;';
- highlightElements[3].html(padding + text + padding).css({
- width: 'auto',
- top: offset.top + height,
- left: offset.left - 2
- }).show();
- if (highlightElements[3].width() < width + 4) {
- // we cannot use minWidth because of IE7
- highlightElements[3].width(width + 4);
- }
-
- for (var j = 0; j < numLinks; j++) {
- var tag = linksOnPage[linkUrl][j][0].piwikTagElement;
- tag.addClass('PIS_Highlighted');
- tag[0].piwikHighlighted = true;
- }
-
- // Sometimes it fails to remove the notification when the hovered element is removed.
- // To make sure we don't display more than one location at a time, we hide all before showing the new one.
- Piwik_Overlay_Client.hideNotifications('LinkLocation');
-
- // we don't use .data() because jquery would remove the callback when the link tag is removed
- linkTag[0].piwikHideNotification = Piwik_Overlay_Client.notification(
- Piwik_Overlay_Translations.get('link') + ': ' + linkUrl, 'LinkLocation');
- }
-
- /** Remove highlight from link */
- function unHighlightLink(linkTag, linkUrl) {
- for (var i = 0; i < highlightElements.length; i++) {
- highlightElements[i].hide();
- }
-
- var numLinks = linksOnPage[linkUrl].length;
- for (var j = 0; j < numLinks; j++) {
- var tag = linksOnPage[linkUrl][j][0].piwikTagElement;
- if (tag) {
- tag.removeClass('PIS_Highlighted');
- tag[0].piwikHighlighted = false;
- }
- }
-
- if ((typeof linkTag[0].piwikHideNotification) == 'function') {
- linkTag[0].piwikHideNotification();
- linkTag[0].piwikHideNotification = null;
- }
- }
-
-
- return {
-
- /**
- * The main method
- */
- initialize: function(finishCallback) {
- c = Piwik_Overlay_Client.createElement;
- Piwik_Overlay_Client.loadScript('plugins/Overlay/client/urlnormalizer.js', function() {
- Piwik_Overlay_UrlNormalizer.initialize();
- load(function() {
- Piwik_Overlay_UrlNormalizer.setExcludedParameters(excludedParams);
- build(function() {
- finishCallback();
- })
- });
- });
- },
-
- /**
- * Remove everything from the dom and terminate timeouts.
- * This can be used from the console in order to load a new implementation for debugging afterwards.
- * If you add `Piwik_Overlay_FollowingPages.remove();` to the beginning and
- * `Piwik_Overlay_FollowingPages.initialize(function(){});` to the end of this file, you can just
- * paste it into the console to inject the new implementation.
- */
- remove: function() {
- for (var i = 0; i < followingPages.length; i++) {
- var url = followingPages[i].label;
- if (typeof linksOnPage[url] != 'undefined') {
- for (var j = 0; j < linksOnPage[url].length; j++) {
- var linkTag = linksOnPage[url][j];
- var tagElement = linkTag[0].piwikTagElement;
- if (tagElement) {
- tagElement.remove();
- }
- linkTag[0].piwikTagElement = null;
-
- $(linkTag).unbind('mouseenter').unbind('mouseleave');
- }
- }
- }
- for (i = 0; i < highlightElements.length; i++) {
- highlightElements[i].remove();
- }
- if (repositionTimeout) {
- window.clearTimeout(repositionTimeout);
- }
- if (resizeTimeout) {
- window.clearTimeout(resizeTimeout);
- }
- $(window).unbind('resize');
- }
-
- };
+var Piwik_Overlay_FollowingPages = (function () {
+
+ /** jQuery */
+ var $ = jQuery;
+
+ /** Info about the following pages */
+ var followingPages = [];
+
+ /** List of excluded get parameters */
+ var excludedParams = [];
+
+ /** Index of the links on the page */
+ var linksOnPage = {};
+
+ /** Reference to create element function */
+ var c;
+
+ /** Load the following pages */
+ function load(callback) {
+ // normalize current location
+ var location = window.location.href;
+ location = Piwik_Overlay_UrlNormalizer.normalize(location);
+ location = (("https:" == document.location.protocol) ? 'https' : 'http') + '://' + location;
+
+ var excludedParamsLoaded = false;
+ var followingPagesLoaded = false;
+
+ // load excluded params
+ Piwik_Overlay_Client.api('getExcludedQueryParameters', function (data) {
+ for (var i = 0; i < data.length; i++) {
+ if (typeof data[i] == 'object') {
+ data[i] = data[i][0];
+ }
+ }
+ excludedParams = data;
+
+ excludedParamsLoaded = true;
+ if (followingPagesLoaded) {
+ callback();
+ }
+ });
+
+ // load following pages
+ Piwik_Overlay_Client.api('getFollowingPages', function (data) {
+ followingPages = data;
+ processFollowingPages();
+
+ followingPagesLoaded = true;
+ if (excludedParamsLoaded) {
+ callback();
+ }
+ }, 'url=' + encodeURIComponent(location));
+ }
+
+ /** Normalize the URLs of following pages and aggregate some stats */
+ function processFollowingPages() {
+ var totalClicks = 0;
+ for (var i = 0; i < followingPages.length; i++) {
+ var page = followingPages[i];
+ // though the following pages are returned without the prefix, downloads
+ // and outlinks still have it.
+ page.label = Piwik_Overlay_UrlNormalizer.removeUrlPrefix(page.label);
+ totalClicks += followingPages[i].referrals;
+ }
+ for (i = 0; i < followingPages.length; i++) {
+ followingPages[i].clickRate = followingPages[i].referrals / totalClicks * 100;
+ }
+ }
+
+ /**
+ * Build an index of links on the page.
+ * This function is passed to $('a').each()
+ */
+ var processLinkDelta = false;
+
+ function processLink() {
+ var a = $(this);
+ a[0].piwikDiscovered = true;
+
+ var href = a.attr('href');
+ href = Piwik_Overlay_UrlNormalizer.normalize(href);
+
+ if (href) {
+ if (typeof linksOnPage[href] == 'undefined') {
+ linksOnPage[href] = [a];
+ }
+ else {
+ linksOnPage[href].push(a);
+ }
+ }
+
+ if (href && processLinkDelta !== false) {
+ if (typeof processLinkDelta[href] == 'undefined') {
+ processLinkDelta[href] = [a];
+ }
+ else {
+ processLinkDelta[href].push(a);
+ }
+ }
+ }
+
+ var repositionTimeout = false;
+ var resizeTimeout = false;
+
+ function build(callback) {
+ // build an index of all links on the page
+ $('a').each(processLink);
+
+ // add tags to known following pages
+ createLinkTags(linksOnPage);
+
+ // position the tags
+ positionLinkTags();
+
+ callback();
+
+ // check on a regular basis whether new links have appeared.
+ // we use a timeout instead of an interval to make sure one call is done before
+ // the next one is triggered
+ var repositionAfterTimeout;
+ repositionAfterTimeout = function () {
+ repositionTimeout = window.setTimeout(function () {
+ findNewLinks();
+ positionLinkTags(repositionAfterTimeout);
+ }, 1800);
+ };
+ repositionAfterTimeout();
+
+ // reposition link tags on window resize
+ $(window).resize(function () {
+ if (repositionTimeout) {
+ window.clearTimeout(repositionTimeout);
+ }
+ if (resizeTimeout) {
+ window.clearTimeout(resizeTimeout);
+ }
+ resizeTimeout = window.setTimeout(function () {
+ positionLinkTags();
+ repositionAfterTimeout();
+ }, 70);
+ });
+ }
+
+ /** Create a batch of link tags */
+ function createLinkTags(links) {
+ var body = $('body');
+ for (var i = 0; i < followingPages.length; i++) {
+ var url = followingPages[i].label;
+ if (typeof links[url] != 'undefined') {
+ for (var j = 0; j < links[url].length; j++) {
+ createLinkTag(links[url][j], url, followingPages[i], body);
+ }
+ }
+ }
+ }
+
+ /** Create the link tag element */
+ function createLinkTag(linkTag, linkUrl, data, body) {
+ if (typeof linkTag[0].piwikTagElement != 'undefined' && linkTag[0].piwikTagElement !== null) {
+ // this link tag already has a tag element. happens in rare cases.
+ return;
+ }
+
+ linkTag[0].piwikTagElement = true;
+
+ var rate = data.clickRate;
+ if (rate < 10) {
+ rate = Math.round(rate * 10) / 10;
+ } else {
+ rate = Math.round(rate);
+ }
+
+ var span = c('span').html(rate + '%');
+ var tagElement = c('div', 'LinkTag').append(span).hide();
+ body.prepend(tagElement);
+
+ linkTag.add(tagElement).hover(function () {
+ highlightLink(linkTag, linkUrl, data);
+ }, function () {
+ unHighlightLink(linkTag, linkUrl);
+ });
+
+ // attach the tag element to the link element. we can't use .data() because jquery
+ // would remove it when removing the link from the dom. but we still need to find
+ // the tag element to remove it as well.
+ linkTag[0].piwikTagElement = tagElement;
+ }
+
+ /** Position the link tags next to the links */
+ function positionLinkTags(callback) {
+ var url, linkTag, tagElement, offset, top, left, isRight, hasOneChild, inlineChild;
+ var tagWidth = 36, tagHeight = 21;
+ var tagsToRemove = [];
+
+ for (var i = 0; i < followingPages.length; i++) {
+ url = followingPages[i].label;
+ if (typeof linksOnPage[url] != 'undefined') {
+ for (var j = 0; j < linksOnPage[url].length; j++) {
+ linkTag = linksOnPage[url][j];
+ tagElement = linkTag[0].piwikTagElement;
+
+ if (linkTag.closest('html').length == 0 || !tagElement) {
+ // the link has been removed from the dom
+ if (tagElement) {
+ tagElement.hide();
+ }
+ // mark for deletion. don't delete it now because we
+ // are iterating of the array it's in. it will be deleted
+ // below this for loop.
+ tagsToRemove.push({
+ index1: url,
+ index2: j
+ });
+ continue;
+ }
+
+ hasOneChild = checkHasOneChild(linkTag);
+ inlineChild = false;
+ if (hasOneChild && linkTag.css('display') != 'block') {
+ inlineChild = linkTag.children().eq(0);
+ }
+
+ if (getVisibility(linkTag) == 'hidden' || (
+ // in case of hasOneChild: jquery always returns linkTag.is(':visible')=false
+ !linkTag.is(':visible') && !(hasOneChild && inlineChild && inlineChild.is(':visible'))
+ )) {
+ // link is not visible
+ tagElement.hide();
+ continue;
+ }
+
+ tagElement.attr('class', 'PIS_LinkTag'); // reset class
+ if (tagElement[0].piwikHighlighted) {
+ tagElement.addClass('PIS_Highlighted');
+ }
+
+ // see comment in highlightLink()
+ if (hasOneChild && linkTag.find('> img').size() == 1) {
+ offset = linkTag.find('> img').offset();
+ if (offset.left == 0 && offset.top == 0) {
+ offset = linkTag.offset();
+ }
+ } else if (inlineChild !== false) {
+ offset = inlineChild.offset();
+ } else {
+ offset = linkTag.offset();
+ }
+
+ top = offset.top - tagHeight + 6;
+ left = offset.left - tagWidth + 10;
+
+ if (isRight = (left < 2)) {
+ tagElement.addClass('PIS_Right');
+ left = offset.left + linkTag.outerWidth() - 10;
+ }
+
+ if (top < 2) {
+ tagElement.addClass(isRight ? 'PIS_BottomRight' : 'PIS_Bottom');
+ top = offset.top + linkTag.outerHeight() - 6;
+ }
+
+ tagElement.css({
+ top: top + 'px',
+ left: left + 'px'
+ }).show();
+
+ }
+ }
+ }
+
+ // walk tagsToRemove from back to front because it contains the indexes in ascending
+ // order. removing something from the front will impact the indexes that come after-
+ // wards. this can be avoided by starting in the back.
+ for (var k = tagsToRemove.length - 1; k >= 0; k--) {
+ var tagToRemove = tagsToRemove[k];
+ linkTag = linksOnPage[tagToRemove.index1][tagToRemove.index2];
+ // remove the tag element from the dom
+ if (linkTag && linkTag[0] && linkTag[0].piwikTagElement) {
+ tagElement = linkTag[0].piwikTagElement;
+ if (tagElement[0].piwikHighlighted) {
+ unHighlightLink(linkTag, tagToRemove.index1);
+ }
+ tagElement.remove();
+ linkTag[0].piwikTagElement = null;
+ }
+ // remove the link from the index
+ linksOnPage[tagToRemove.index1].splice(tagToRemove.index2, 1);
+ if (linksOnPage[tagToRemove.index1].length == 0) {
+ delete linksOnPage[tagToRemove.index1];
+ }
+ }
+
+ if (typeof callback == 'function') {
+ callback();
+ }
+ }
+
+ /** Get the visibility of an element */
+ function getVisibility(el) {
+ var visibility = el.css('visibility');
+ if (visibility == 'inherit') {
+ el = el.parent();
+ if (el.size() > 0) {
+ return getVisibility(el);
+ }
+ }
+ return visibility;
+ }
+
+ /**
+ * Find out whether a link has only one child. Using .children().size() == 1 doesn't work
+ * because it doesn't take additional text nodes into account.
+ */
+ function checkHasOneChild(linkTag) {
+ var hasOneChild = (linkTag.children().size() == 1);
+ if (hasOneChild) {
+ // if the element contains one tag and some text, hasOneChild is set incorrectly
+ var contents = linkTag.contents();
+ if (contents.size() > 1) {
+ // find non-empty text nodes
+ contents = contents.filter(function () {
+ return this.nodeType == 3 && // text node
+ $.trim(this.data).length > 0; // contains more than whitespaces
+ });
+ if (contents.size() > 0) {
+ hasOneChild = false;
+ }
+ }
+ }
+ return hasOneChild;
+ }
+
+ /** Check whether new links have been added to the dom */
+ function findNewLinks() {
+ var newLinks = $('a').filter(function () {
+ return typeof this.piwikDiscovered == 'undefined' || this.piwikDiscovered === null;
+ });
+
+ if (newLinks.size() == 0) {
+ return;
+ }
+
+ processLinkDelta = {};
+ newLinks.each(processLink);
+ createLinkTags(processLinkDelta);
+ processLinkDelta = false;
+ }
+
+ /** Dom elements used for drawing a box around the link */
+ var highlightElements = [];
+
+ /** Highlight a link on hover */
+ function highlightLink(linkTag, linkUrl, data) {
+ if (highlightElements.length == 0) {
+ highlightElements.push(c('div', 'LinkHighlightBoxTop'));
+ highlightElements.push(c('div', 'LinkHighlightBoxRight'));
+ highlightElements.push(c('div', 'LinkHighlightBoxLeft'));
+
+ highlightElements.push(c('div', 'LinkHighlightBoxText'));
+
+ var body = $('body');
+ for (var i = 0; i < highlightElements.length; i++) {
+ body.prepend(highlightElements[i].css({display: 'none'}));
+ }
+ }
+
+ var width = linkTag.outerWidth();
+
+ var offset, height;
+ var hasOneChild = checkHasOneChild(linkTag);
+ if (hasOneChild && linkTag.find('img').size() == 1) {
+ // if the <a> tag contains only an <img>, the offset and height methods don't work properly.
+ // as a result, the box around the image link would be wrong. we use the image to derive
+ // the offset and height instead of the link to get correct values.
+ var img = linkTag.find('img');
+ offset = img.offset();
+ height = img.outerHeight();
+ }
+ if (hasOneChild && linkTag.css('display') != 'block') {
+ // if the <a> tag is not displayed as block and has only one child, using the child to
+ // derive the offset and dimensions is more robust.
+ var child = linkTag.children().eq(0);
+ offset = child.offset();
+ height = child.outerHeight();
+ width = child.outerWidth();
+ } else {
+ offset = linkTag.offset();
+ height = linkTag.outerHeight();
+ }
+
+ highlightElements[0].width(width).css({top: offset.top - 2, left: offset.left}).show();
+ highlightElements[1].height(height + 4).css({top: offset.top - 2, left: offset.left + width}).show();
+ highlightElements[2].height(height + 4).css({top: offset.top - 2, left: offset.left - 2}).show();
+
+ var numLinks = linksOnPage[linkUrl].length;
+ var text;
+ if (numLinks > 1) {
+ text = Piwik_Overlay_Translations.get('clicksFromXLinks')
+ .replace(/%1\$s/, data.referrals)
+ .replace(/%2\$s/, numLinks);
+ } else if (data.referrals == 1) {
+ text = Piwik_Overlay_Translations.get('oneClick');
+ } else {
+ text = Piwik_Overlay_Translations.get('clicks')
+ .replace(/%s/, data.referrals);
+ }
+
+ var padding = '&nbsp;&nbsp;';
+ highlightElements[3].html(padding + text + padding).css({
+ width: 'auto',
+ top: offset.top + height,
+ left: offset.left - 2
+ }).show();
+ if (highlightElements[3].width() < width + 4) {
+ // we cannot use minWidth because of IE7
+ highlightElements[3].width(width + 4);
+ }
+
+ for (var j = 0; j < numLinks; j++) {
+ var tag = linksOnPage[linkUrl][j][0].piwikTagElement;
+ tag.addClass('PIS_Highlighted');
+ tag[0].piwikHighlighted = true;
+ }
+
+ // Sometimes it fails to remove the notification when the hovered element is removed.
+ // To make sure we don't display more than one location at a time, we hide all before showing the new one.
+ Piwik_Overlay_Client.hideNotifications('LinkLocation');
+
+ // we don't use .data() because jquery would remove the callback when the link tag is removed
+ linkTag[0].piwikHideNotification = Piwik_Overlay_Client.notification(
+ Piwik_Overlay_Translations.get('link') + ': ' + linkUrl, 'LinkLocation');
+ }
+
+ /** Remove highlight from link */
+ function unHighlightLink(linkTag, linkUrl) {
+ for (var i = 0; i < highlightElements.length; i++) {
+ highlightElements[i].hide();
+ }
+
+ var numLinks = linksOnPage[linkUrl].length;
+ for (var j = 0; j < numLinks; j++) {
+ var tag = linksOnPage[linkUrl][j][0].piwikTagElement;
+ if (tag) {
+ tag.removeClass('PIS_Highlighted');
+ tag[0].piwikHighlighted = false;
+ }
+ }
+
+ if ((typeof linkTag[0].piwikHideNotification) == 'function') {
+ linkTag[0].piwikHideNotification();
+ linkTag[0].piwikHideNotification = null;
+ }
+ }
+
+
+ return {
+
+ /**
+ * The main method
+ */
+ initialize: function (finishCallback) {
+ c = Piwik_Overlay_Client.createElement;
+ Piwik_Overlay_Client.loadScript('plugins/Overlay/client/urlnormalizer.js', function () {
+ Piwik_Overlay_UrlNormalizer.initialize();
+ load(function () {
+ Piwik_Overlay_UrlNormalizer.setExcludedParameters(excludedParams);
+ build(function () {
+ finishCallback();
+ })
+ });
+ });
+ },
+
+ /**
+ * Remove everything from the dom and terminate timeouts.
+ * This can be used from the console in order to load a new implementation for debugging afterwards.
+ * If you add `Piwik_Overlay_FollowingPages.remove();` to the beginning and
+ * `Piwik_Overlay_FollowingPages.initialize(function(){});` to the end of this file, you can just
+ * paste it into the console to inject the new implementation.
+ */
+ remove: function () {
+ for (var i = 0; i < followingPages.length; i++) {
+ var url = followingPages[i].label;
+ if (typeof linksOnPage[url] != 'undefined') {
+ for (var j = 0; j < linksOnPage[url].length; j++) {
+ var linkTag = linksOnPage[url][j];
+ var tagElement = linkTag[0].piwikTagElement;
+ if (tagElement) {
+ tagElement.remove();
+ }
+ linkTag[0].piwikTagElement = null;
+
+ $(linkTag).unbind('mouseenter').unbind('mouseleave');
+ }
+ }
+ }
+ for (i = 0; i < highlightElements.length; i++) {
+ highlightElements[i].remove();
+ }
+ if (repositionTimeout) {
+ window.clearTimeout(repositionTimeout);
+ }
+ if (resizeTimeout) {
+ window.clearTimeout(resizeTimeout);
+ }
+ $(window).unbind('resize');
+ }
+
+ };
})(); \ No newline at end of file