diff --git a/app/assets/javascripts/discourse/components/user-stream.js.es6 b/app/assets/javascripts/discourse/components/user-stream.js.es6
index ad38f8e0405..2afce0b9787 100644
--- a/app/assets/javascripts/discourse/components/user-stream.js.es6
+++ b/app/assets/javascripts/discourse/components/user-stream.js.es6
@@ -1,4 +1,5 @@
import LoadMore from "discourse/mixins/load-more";
+import ClickTrack from 'discourse/lib/click-track';
export default Ember.Component.extend(LoadMore, {
loading: false,
@@ -9,6 +10,38 @@ export default Ember.Component.extend(LoadMore, {
Em.run.schedule('afterRender', () => $(document).scrollTop(0));
}.observes('stream.user.id'),
+ _inserted: function() {
+ this.bindScrolling({name: 'user-stream-view'});
+
+ $(window).on('resize.discourse-on-scroll', () => this.scrolled());
+
+ this.$().on('mouseup.discourse-redirect', '.excerpt a', function(e) {
+ // bypass if we are selecting stuff
+ const selection = window.getSelection && window.getSelection();
+ if (selection.type === "Range" || selection.rangeCount > 0) {
+ if (Discourse.Utilities.selectedText() !== "") {
+ return true;
+ }
+ }
+
+ const $target = $(e.target);
+ if ($target.hasClass('mention') || $target.parents('.expanded-embed').length) { return false; }
+
+ return ClickTrack.trackClick(e);
+ });
+
+ }.on('didInsertElement'),
+
+ // This view is being removed. Shut down operations
+ _destroyed: function() {
+ this.unbindScrolling('user-stream-view');
+ $(window).unbind('resize.discourse-on-scroll');
+
+ // Unbind link tracking
+ this.$().off('mouseup.discourse-redirect', '.excerpt a');
+
+ }.on('willDestroyElement'),
+
actions: {
loadMore() {
if (this.get('loading')) { return; }
diff --git a/app/assets/javascripts/discourse/lib/click-track.js.es6 b/app/assets/javascripts/discourse/lib/click-track.js.es6
index cade30d7408..b8e56f13f7a 100644
--- a/app/assets/javascripts/discourse/lib/click-track.js.es6
+++ b/app/assets/javascripts/discourse/lib/click-track.js.es6
@@ -15,9 +15,9 @@ export default {
if ($link.hasClass('lightbox') || $link.hasClass('mention-group') || $link.hasClass('no-track-link')) { return true; }
var href = $link.attr('href') || $link.data('href'),
- $article = $link.closest('article'),
+ $article = $link.closest('article,.excerpt'),
postId = $article.data('post-id'),
- topicId = $('#topic').data('topic-id'),
+ topicId = $('#topic').data('topic-id') || $article.data('topic-id'),
userId = $link.data('user-id');
if (!href || href.trim().length === 0 || href.indexOf("mailto:") === 0) { return; }
diff --git a/app/assets/javascripts/discourse/templates/components/stream-item.hbs b/app/assets/javascripts/discourse/templates/components/stream-item.hbs
index 4c5609b88db..0018f8a2d84 100644
--- a/app/assets/javascripts/discourse/templates/components/stream-item.hbs
+++ b/app/assets/javascripts/discourse/templates/components/stream-item.hbs
@@ -13,7 +13,7 @@
diff --git a/test/javascripts/lib/click-track-profile-page-test.js.es6 b/test/javascripts/lib/click-track-profile-page-test.js.es6
new file mode 100644
index 00000000000..d224421b2b5
--- /dev/null
+++ b/test/javascripts/lib/click-track-profile-page-test.js.es6
@@ -0,0 +1,206 @@
+import { blank } from 'helpers/qunit-helpers';
+import DiscourseURL from "discourse/lib/url";
+import ClickTrack from "discourse/lib/click-track";
+
+var windowOpen,
+ win,
+ redirectTo;
+
+module("lib:click-track-profile-page", {
+ setup: function() {
+
+ // Prevent any of these tests from navigating away
+ win = {focus: function() { } };
+ redirectTo = sandbox.stub(DiscourseURL, "redirectTo");
+ sandbox.stub(Discourse, "ajax");
+ windowOpen = sandbox.stub(window, "open").returns(win);
+ sandbox.stub(win, "focus");
+
+ fixture().html(
+ `
+ google.com
+ google.com
+
+
google.com
+
forum
+
log.txt
+
#hashtag
+
+
+ google.com
+ google.com
+
+
google.com
+
forum
+
log.txt
+
#hashtag
+ `);
+ }
+});
+
+var track = ClickTrack.trackClick;
+
+// test
+var generateClickEventOn = function(selector) {
+ return $.Event("click", { currentTarget: fixture(selector)[0] });
+};
+
+test("does not track clicks on lightboxes", function() {
+ var clickEvent = generateClickEventOn('.lightbox');
+ sandbox.stub(clickEvent, "preventDefault");
+ ok(track(clickEvent));
+ ok(!clickEvent.preventDefault.calledOnce);
+});
+
+test("it calls preventDefault when clicking on an a", function() {
+ var clickEvent = generateClickEventOn('a');
+ sandbox.stub(clickEvent, "preventDefault");
+ track(clickEvent);
+ ok(clickEvent.preventDefault.calledOnce);
+ ok(DiscourseURL.redirectTo.calledOnce);
+});
+
+test("does not track clicks when forcibly disabled", function() {
+ ok(track(generateClickEventOn('.no-track-link')));
+});
+
+test("does not track clicks on back buttons", function() {
+ ok(track(generateClickEventOn('.back')));
+});
+
+test("does not track clicks on quote buttons", function() {
+ ok(track(generateClickEventOn('.quote-other-topic')));
+});
+
+test("does not track clicks on category badges", () => {
+ ok(!track(generateClickEventOn('.hashtag')));
+});
+
+test("removes the href and put it as a data attribute", function() {
+ track(generateClickEventOn('a'));
+
+ var $link = fixture('a').first();
+ ok($link.hasClass('no-href'));
+ equal($link.data('href'), 'http://www.google.com');
+ blank($link.attr('href'));
+ ok($link.data('auto-route'));
+ ok(DiscourseURL.redirectTo.calledOnce);
+});
+
+asyncTestDiscourse("restores the href after a while", function() {
+ expect(1);
+
+ track(generateClickEventOn('a'));
+
+ setTimeout(function() {
+ start();
+ equal(fixture('a').attr('href'), "http://www.google.com");
+ }, 75);
+});
+
+var trackRightClick = function(target) {
+ var clickEvent = generateClickEventOn(target);
+ clickEvent.which = 3;
+ return track(clickEvent);
+};
+
+test("right clicks change the href", function() {
+ ok(trackRightClick('a'));
+ equal(fixture('a').first().prop('href'), "http://www.google.com/");
+});
+
+test("right clicks are tracked", function() {
+ Discourse.SiteSettings.track_external_right_clicks = true;
+ trackRightClick('a');
+ equal(fixture('.first a').first().attr('href'), "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337");
+});
+
+test("right clicks are tracked for second excerpt", function() {
+ Discourse.SiteSettings.track_external_right_clicks = true;
+ trackRightClick('.second a');
+ equal(fixture('.second a').first().attr('href'), "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331");
+});
+
+test("preventDefault is not called for right clicks", function() {
+ var clickEvent = generateClickEventOn('a');
+ clickEvent.which = 3;
+ sandbox.stub(clickEvent, "preventDefault");
+ ok(track(clickEvent));
+ ok(!clickEvent.preventDefault.calledOnce);
+});
+
+var testOpenInANewTab = function(description, clickEventModifier) {
+ test(description, function() {
+ var clickEvent = generateClickEventOn('a');
+ clickEventModifier(clickEvent);
+ sandbox.stub(clickEvent, "preventDefault");
+ ok(track(clickEvent));
+ ok(Discourse.ajax.calledOnce);
+ ok(!clickEvent.preventDefault.calledOnce);
+ });
+};
+
+testOpenInANewTab("it opens in a new tab when pressing shift", function(clickEvent) {
+ clickEvent.shiftKey = true;
+});
+
+testOpenInANewTab("it opens in a new tab when pressing meta", function(clickEvent) {
+ clickEvent.metaKey = true;
+});
+
+testOpenInANewTab("it opens in a new tab when pressing ctrl", function(clickEvent) {
+ clickEvent.ctrlKey = true;
+});
+
+testOpenInANewTab("it opens in a new tab on middle click", function(clickEvent) {
+ clickEvent.button = 2;
+});
+
+test("tracks via AJAX if we're on the same site", function() {
+ sandbox.stub(DiscourseURL, "routeTo");
+ sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
+
+ ok(!track(generateClickEventOn('#same-site')));
+ ok(Discourse.ajax.calledOnce);
+ ok(DiscourseURL.routeTo.calledOnce);
+});
+
+test("does not track via AJAX for attachments", function() {
+ sandbox.stub(DiscourseURL, "routeTo");
+ sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
+
+ ok(!track(generateClickEventOn('.attachment')));
+ ok(DiscourseURL.redirectTo.calledOnce);
+});
+
+test("tracks custom urls when opening in another window", function() {
+ var clickEvent = generateClickEventOn('a');
+ sandbox.stub(Discourse.User, "currentProp").withArgs('external_links_in_new_tab').returns(true);
+ ok(!track(clickEvent));
+ ok(windowOpen.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337', '_blank'));
+});
+
+test("tracks custom urls on second excerpt when opening in another window", function() {
+ var clickEvent = generateClickEventOn('.second a');
+ sandbox.stub(Discourse.User, "currentProp").withArgs('external_links_in_new_tab').returns(true);
+ ok(!track(clickEvent));
+ ok(windowOpen.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331', '_blank'));
+});
+
+test("tracks custom urls when opening in another window", function() {
+ var clickEvent = generateClickEventOn('a');
+ ok(!track(clickEvent));
+ ok(redirectTo.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337'));
+});
+
+test("tracks custom urls on second excerpt when opening in another window", function() {
+ var clickEvent = generateClickEventOn('.second a');
+ ok(!track(clickEvent));
+ ok(redirectTo.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331'));
+});
diff --git a/test/javascripts/lib/click-track-test.js.es6 b/test/javascripts/lib/click-track-test.js.es6
index 2bcfc673e02..305ae0664ba 100644
--- a/test/javascripts/lib/click-track-test.js.es6
+++ b/test/javascripts/lib/click-track-test.js.es6
@@ -17,7 +17,7 @@ module("lib:click-track", {
sandbox.stub(win, "focus");
fixture().html(
- `
+ `
google.com
google.com
@@ -132,7 +132,7 @@ test("right clicks change the href", function() {
test("right clicks are tracked", function() {
Discourse.SiteSettings.track_external_right_clicks = true;
trackRightClick();
- equal(fixture('a').first().attr('href'), "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42");
+ equal(fixture('a').first().attr('href'), "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337");
});
test("preventDefault is not called for right clicks", function() {
@@ -191,11 +191,11 @@ test("tracks custom urls when opening in another window", function() {
var clickEvent = generateClickEventOn('a');
sandbox.stub(Discourse.User, "currentProp").withArgs('external_links_in_new_tab').returns(true);
ok(!track(clickEvent));
- ok(windowOpen.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42', '_blank'));
+ ok(windowOpen.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337', '_blank'));
});
test("tracks custom urls when opening in another window", function() {
var clickEvent = generateClickEventOn('a');
ok(!track(clickEvent));
- ok(redirectTo.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42'));
+ ok(redirectTo.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337'));
});