diff --git a/app/assets/javascripts/discourse/components/click_track.js b/app/assets/javascripts/discourse/components/click_track.js
index 5f79398041e..41cad2cb0ed 100644
--- a/app/assets/javascripts/discourse/components/click_track.js
+++ b/app/assets/javascripts/discourse/components/click_track.js
@@ -53,7 +53,10 @@ Discourse.ClickTrack = {
if (!ownLink) {
var $badge = $('span.badge', $link);
if ($badge.length === 1) {
- $badge.html(parseInt($badge.html(), 10) + 1);
+ // don't update counts in oneboxes (except when we force it)
+ if ($link.closest(".onebox-result").length === 0 || $link.hasClass("track-link")) {
+ $badge.html(parseInt($badge.html(), 10) + 1);
+ }
}
}
@@ -79,7 +82,7 @@ Discourse.ClickTrack = {
}
// If we're on the same site, use the router and track via AJAX
- if (href.indexOf(window.location.origin) === 0) {
+ if (href.indexOf(Discourse.URL.origin()) === 0) {
Discourse.ajax(Discourse.getURL("/clicks/track"), {
data: {
url: href,
@@ -97,7 +100,7 @@ Discourse.ClickTrack = {
var win = window.open(trackingUrl, '_blank');
win.focus();
} else {
- window.location = trackingUrl;
+ Discourse.URL.redirectTo(trackingUrl);
}
return false;
diff --git a/app/assets/javascripts/discourse/components/url.js b/app/assets/javascripts/discourse/components/url.js
index 4191b874006..917a929c9be 100644
--- a/app/assets/javascripts/discourse/components/url.js
+++ b/app/assets/javascripts/discourse/components/url.js
@@ -21,7 +21,7 @@ Discourse.URL = {
@method router
**/
- router: function(path) {
+ router: function() {
return Discourse.__container__.lookup('router:main');
},
@@ -105,6 +105,30 @@ Discourse.URL = {
var router = this.router();
router.router.updateURL(path);
return router.handleURL(path);
+ },
+
+ /**
+ @private
+
+ Get the origin of the current location.
+ This has been extracted so it can be tested.
+
+ @method origin
+ **/
+ origin: function() {
+ return window.location.origin;
+ },
+
+ /**
+ @private
+
+ Redirect to a URL.
+ This has been extracted so it can be tested.
+
+ @method redirectTo
+ **/
+ redirectTo: function(url) {
+ window.location = url;
}
};
diff --git a/app/assets/javascripts/discourse/controllers/preferences_username_controller.js b/app/assets/javascripts/discourse/controllers/preferences_username_controller.js
index 0ec57d910a1..d67319581b7 100644
--- a/app/assets/javascripts/discourse/controllers/preferences_username_controller.js
+++ b/app/assets/javascripts/discourse/controllers/preferences_username_controller.js
@@ -55,12 +55,12 @@ Discourse.PreferencesUsernameController = Discourse.ObjectController.extend({
if (result) {
_this.set('saving', true);
return _this.get('content').changeUsername(_this.get('newUsername')).then(function() {
- window.location = Discourse.getURL("/users/") + (_this.get('newUsername').toLowerCase()) + "/preferences";
+ var url = Discourse.getURL("/users/") + _this.get('newUsername').toLowerCase() + "/preferences";
+ Discourse.URL.redirectTo(url);
}, function() {
- /* Error
- */
+ // error
_this.set('error', true);
- return _this.set('saving', false);
+ _this.set('saving', false);
});
}
});
diff --git a/app/assets/javascripts/discourse/views/modal/edit_category_view.js b/app/assets/javascripts/discourse/views/modal/edit_category_view.js
index 279e09297be..57cd0bafc30 100644
--- a/app/assets/javascripts/discourse/views/modal/edit_category_view.js
+++ b/app/assets/javascripts/discourse/views/modal/edit_category_view.js
@@ -95,7 +95,8 @@ Discourse.EditCategoryView = Discourse.ModalBodyView.extend({
this.get('category').save().then(function(result) {
// success
$('#discourse-modal').modal('hide');
- window.location = Discourse.getURL("/category/") + (Discourse.Utilities.categoryUrlId(result.category));
+ var url = Discourse.getURL("/category/") + (Discourse.Utilities.categoryUrlId(result.category));
+ Discourse.URL.redirectTo(url);
}, function(errors) {
// errors
if(errors.length === 0) errors.push(Em.String.i18n("category.creation_error"));
@@ -112,7 +113,7 @@ Discourse.EditCategoryView = Discourse.ModalBodyView.extend({
if (result) {
categoryView.get('category').destroy().then(function(){
// success
- window.location = Discourse.getURL("/categories");
+ Discourse.URL.redirectTo(Discourse.getURL("/categories"));
}, function(jqXHR){
// error
$('#discourse-modal').modal('show');
diff --git a/app/assets/javascripts/discourse/views/post_view.js b/app/assets/javascripts/discourse/views/post_view.js
index f274c8784b5..ab298a45e56 100644
--- a/app/assets/javascripts/discourse/views/post_view.js
+++ b/app/assets/javascripts/discourse/views/post_view.js
@@ -186,7 +186,10 @@ Discourse.PostView = Discourse.View.extend({
postView.$(".cooked a[href]").each(function() {
var link = $(this);
if (link.attr('href') === lc.url) {
- return link.append("" + lc.clicks + "");
+ // don't display badge counts in oneboxes (except when we force it)
+ if (link.closest(".onebox-result").length === 0 || link.hasClass("track-link")) {
+ link.append("" + lc.clicks + "");
+ }
}
});
}
diff --git a/spec/javascripts/components/click_track_spec.js b/spec/javascripts/components/click_track_spec.js
new file mode 100644
index 00000000000..0a37080dab7
--- /dev/null
+++ b/spec/javascripts/components/click_track_spec.js
@@ -0,0 +1,215 @@
+/*global expect:true describe:true it:true beforeEach:true afterEach:true spyOn:true */
+
+describe("Discourse.ClickTrack", function() {
+
+ var track = Discourse.ClickTrack.trackClick,
+ clickEvent,
+ html = [
+ '
'].join("\n");
+
+ var generateClickEventOn = function(selector) {
+ return $.Event("click", { currentTarget: $(selector)[0] });
+ }
+
+ beforeEach(function() {
+ $('body').html(html);
+ });
+
+ afterEach(function() {
+ $('#topic').remove();
+ });
+
+ describe("lightboxes", function() {
+
+ beforeEach(function() {
+ clickEvent = generateClickEventOn('.lightbox');
+ });
+
+ it("does not track clicks on lightboxes", function() {
+ expect(track(clickEvent)).toBe(true);
+ });
+
+ it("does not call preventDefault", function() {
+ spyOn(clickEvent, "preventDefault");
+ track(clickEvent);
+ expect(clickEvent.preventDefault).not.toHaveBeenCalled();
+ });
+
+ });
+
+ it("calls preventDefault", function() {
+ clickEvent = generateClickEventOn('a');
+ spyOn(clickEvent, "preventDefault");
+ track(clickEvent);
+ expect(clickEvent.preventDefault).toHaveBeenCalled();
+ });
+
+ it("does not track clicks on back buttons", function() {
+ clickEvent = generateClickEventOn('.back');
+ expect(track(clickEvent)).toBe(true);
+ });
+
+ it("does not track clicks on quote buttons", function() {
+ clickEvent = generateClickEventOn('.quote-other-topic');
+ expect(track(clickEvent)).toBe(true);
+ });
+
+ it("removes the href and put it as a data attribute", function() {
+ clickEvent = generateClickEventOn('a');
+ track(clickEvent);
+ var $link = $('a').first();
+ expect($link.hasClass('no-href')).toBe(true);
+ expect($link.data('href')).toEqual("http://www.google.com");
+ expect($link.attr('href')).toBeUndefined();
+ expect($link.data('auto-route')).toBe(true);
+ });
+
+ describe("badges", function() {
+
+ it("does not update badge clicks on my own link", function() {
+ spyOn(Discourse, "get").andReturn(314);
+ track(generateClickEventOn('#with-badge'));
+ var $badge = $('span.badge', $('#with-badge').first());
+ expect(parseInt($badge.html(), 10)).toEqual(1);
+ });
+
+ it("does not update badge clicks on links in my own post", function() {
+ spyOn(Discourse, "get").andReturn(3141);
+ track(generateClickEventOn('#with-badge-but-not-mine'));
+ var $badge = $('span.badge', $('#with-badge-but-not-mine').first());
+ expect(parseInt($badge.html(), 10)).toEqual(1);
+ });
+
+ describe("oneboxes", function() {
+
+ it("does not update badge clicks in oneboxes", function() {
+ track(generateClickEventOn('#inside-onebox'));
+ var $badge = $('span.badge', $('#inside-onebox').first());
+ expect(parseInt($badge.html(), 10)).toEqual(1);
+ });
+
+ it("updates badge clicks in oneboxes when forced", function() {
+ track(generateClickEventOn('#inside-onebox-forced'));
+ var $badge = $('span.badge', $('#inside-onebox-forced').first());
+ expect(parseInt($badge.html(), 10)).toEqual(2);
+ });
+
+ });
+
+ it("updates badge clicks", function() {
+ track(generateClickEventOn('#with-badge'));
+ var $badge = $('span.badge', $('#with-badge').first());
+ expect(parseInt($badge.html(), 10)).toEqual(2);
+ });
+
+ });
+
+ describe("right click", function() {
+
+ beforeEach(function(){
+ clickEvent = generateClickEventOn('a');
+ clickEvent.which = 3;
+ });
+
+ it("detects right clicks", function() {
+ expect(track(clickEvent)).toBe(true);
+ });
+
+ it("changes the href", function() {
+ track(clickEvent);
+ var $link = $('a').first();
+ expect($link.attr('href')).toEqual("http://www.google.com");
+ });
+
+ it("tracks external right clicks", function() {
+ Discourse.SiteSettings.track_external_right_clicks = true;
+ track(clickEvent);
+ var $link = $('a').first();
+ expect($link.attr('href')).toEqual("/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42");
+ // reset
+ Discourse.SiteSettings.track_external_right_clicks = false;
+ });
+
+ });
+
+ describe("new tab", function() {
+
+ beforeEach(function(){
+ clickEvent = generateClickEventOn('a');
+ spyOn(Discourse, 'ajax');
+ spyOn(window, 'open');
+ });
+
+ it("opens in a new tab when pressing alt", function() {
+ clickEvent.metaKey = true;
+ expect(track(clickEvent)).toBe(false);
+ expect(Discourse.ajax).toHaveBeenCalled();
+ expect(window.open).toHaveBeenCalledWith('http://www.google.com', '_blank');
+ });
+
+ it("opens in a new tab when pressing ctrl", function() {
+ clickEvent.ctrlKey = true;
+ expect(track(clickEvent)).toBe(false);
+ expect(Discourse.ajax).toHaveBeenCalled();
+ expect(window.open).toHaveBeenCalledWith('http://www.google.com', '_blank');
+ });
+
+ it("opens in a new tab when middle clicking", function() {
+ clickEvent.which = 2;
+ expect(track(clickEvent)).toBe(false);
+ expect(Discourse.ajax).toHaveBeenCalled();
+ expect(window.open).toHaveBeenCalledWith('http://www.google.com', '_blank');
+ });
+
+ });
+
+ it("tracks via AJAX if we're on the same site", function() {
+ // setup
+ clickEvent = generateClickEventOn('#same-site');
+ spyOn(Discourse, 'ajax');
+ spyOn(Discourse.URL, 'routeTo');
+ spyOn(Discourse.URL, 'origin').andReturn('http://discuss.domain.com');
+ // test
+ expect(track(clickEvent)).toBe(false);
+ expect(Discourse.ajax).toHaveBeenCalled();
+ expect(Discourse.URL.routeTo).toHaveBeenCalledWith('http://discuss.domain.com');
+ });
+
+ describe("tracks via custom URL", function() {
+
+ beforeEach(function() {
+ clickEvent = generateClickEventOn('a');
+ });
+
+ it("in another window", function() {
+ // spies
+ spyOn(Discourse, 'get').andReturn(true);
+ spyOn(window, 'open').andCallFake(function() { return { focus: function() {} } });
+ spyOn(window, 'focus');
+ // test
+ expect(track(clickEvent)).toBe(false);
+ expect(Discourse.get).toHaveBeenCalledWith('currentUser.external_links_in_new_tab');
+ expect(window.open).toHaveBeenCalledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42', '_blank');
+ });
+
+ it("in the same window", function() {
+ spyOn(Discourse.URL, 'redirectTo');
+ expect(track(clickEvent)).toBe(false);
+ expect(Discourse.URL.redirectTo).toHaveBeenCalledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42');
+ });
+
+ });
+
+});