Files
discourse/app/assets/javascripts/discourse/components/screen_track.js
2013-05-28 11:25:56 -04:00

159 lines
4.2 KiB
JavaScript

/**
We use this class to track how long posts in a topic are on the screen.
@class ScreenTrack
@extends Ember.Object
@namespace Discourse
@module Discourse
**/
Discourse.ScreenTrack = Ember.Object.extend({
// Don't send events if we haven't scrolled in a long time
PAUSE_UNLESS_SCROLLED: 1000 * 60 * 3,
// After 6 minutes stop tracking read position on post
MAX_TRACKING_TIME: 1000 * 60 * 6,
totalTimings: {},
// Elements to track
timings: {},
topicTime: 0,
cancelled: false,
track: function(elementId, postNumber) {
this.timings["#" + elementId] = {
time: 0,
postNumber: postNumber
};
},
// Reset our timers
reset: function() {
this.lastTick = new Date().getTime();
this.lastFlush = 0;
this.cancelled = false;
},
// Start tracking
start: function() {
var _this = this;
this.reset();
this.lastScrolled = new Date().getTime();
this.interval = setInterval(function() {
return _this.tick();
}, 1000);
},
// Cancel and eject any tracking we have buffered
cancel: function() {
this.cancelled = true;
this.timings = {};
this.topicTime = 0;
clearInterval(this.interval);
this.interval = null;
},
// Stop tracking and flush buffered read records
stop: function() {
clearInterval(this.interval);
this.interval = null;
return this.flush();
},
scrolled: function() {
this.lastScrolled = new Date().getTime();
},
flush: function() {
var highestSeenByTopic, newTimings, topicId,
_this = this;
if (this.cancelled) {
return;
}
// We don't log anything unless we're logged in
if (!Discourse.User.current()) return;
newTimings = {};
Object.values(this.timings, function(timing) {
if (!_this.totalTimings[timing.postNumber])
_this.totalTimings[timing.postNumber] = 0;
if (timing.time > 0 && _this.totalTimings[timing.postNumber] < _this.MAX_TRACKING_TIME) {
_this.totalTimings[timing.postNumber] += timing.time;
newTimings[timing.postNumber] = timing.time;
}
timing.time = 0;
});
topicId = this.get('topic_id');
var highestSeen = 0;
$.each(newTimings, function(postNumber){
highestSeen = Math.max(highestSeen, parseInt(postNumber, 10));
});
highestSeenByTopic = Discourse.get('highestSeenByTopic');
if ((highestSeenByTopic[topicId] || 0) < highestSeen) {
highestSeenByTopic[topicId] = highestSeen;
}
if (!Object.isEmpty(newTimings)) {
Discourse.ajax('/topics/timings', {
data: {
timings: newTimings,
topic_time: this.topicTime,
topic_id: topicId
},
cache: false,
type: 'POST',
headers: {
'X-SILENCE-LOGGER': 'true'
}
});
this.topicTime = 0;
}
this.lastFlush = 0;
},
tick: function() {
// If the user hasn't scrolled the browser in a long time, stop tracking time read
var diff, docViewBottom, docViewTop, sinceScrolled,
_this = this;
sinceScrolled = new Date().getTime() - this.lastScrolled;
if (sinceScrolled > this.PAUSE_UNLESS_SCROLLED) {
this.reset();
return;
}
diff = new Date().getTime() - this.lastTick;
this.lastFlush += diff;
this.lastTick = new Date().getTime();
if (this.lastFlush > (Discourse.SiteSettings.flush_timings_secs * 1000)) {
this.flush();
}
// Don't track timings if we're not in focus
if (!Discourse.get("hasFocus")) return;
this.topicTime += diff;
docViewTop = $(window).scrollTop() + $('header').height();
docViewBottom = docViewTop + $(window).height();
// TODO: Eyeline has a smarter more accurate function here
return Object.keys(this.timings, function(id) {
var $element, elemBottom, elemTop, timing;
$element = $(id);
if ($element.length === 1) {
elemTop = $element.offset().top;
elemBottom = elemTop + $element.height();
// If part of the element is on the screen, increase the counter
if (((docViewTop <= elemTop && elemTop <= docViewBottom)) || ((docViewTop <= elemBottom && elemBottom <= docViewBottom))) {
timing = _this.timings[id];
timing.time = timing.time + diff;
}
}
});
}
});