12
assets/js/UrlHelper.js
Normal file
12
assets/js/UrlHelper.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class UrlHelper {
|
||||||
|
|
||||||
|
static getDomainFromUrl(url)
|
||||||
|
{
|
||||||
|
var parts = url.split('/');
|
||||||
|
|
||||||
|
return parts[0] + "//" + parts[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UrlHelper;
|
||||||
@@ -1,6 +1,191 @@
|
|||||||
/**
|
var UrlHelper = require('./UrlHelper');
|
||||||
* I think that I am going to need this.
|
|
||||||
*/
|
var $ = require('jquery');
|
||||||
|
|
||||||
|
var currentTimestamp = require('./helpers/currentTimestamp');
|
||||||
|
var changeExtensionIcon = require('./helpers/changeExtensionIcon');
|
||||||
|
|
||||||
class WakaTime {
|
class WakaTime {
|
||||||
|
|
||||||
|
detectionIntervalInSeconds = 60; //default
|
||||||
|
|
||||||
|
loggingType = 'domain'; //default
|
||||||
|
|
||||||
|
heartbeatApiUrl = 'https://wakatime.com/api/v1/users/current/heartbeats';
|
||||||
|
|
||||||
|
currentUserApiUrl = 'https://wakatime.com/api/v1/users/current';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user is logged in.
|
||||||
|
*
|
||||||
|
* @return $.promise()
|
||||||
|
*/
|
||||||
|
checkAuth()
|
||||||
|
{
|
||||||
|
var deferredObject = $.Deferred();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: this.currentUserApiUrl,
|
||||||
|
dataType: 'json',
|
||||||
|
success: (data) => {
|
||||||
|
|
||||||
|
deferredObject.resolve(data.data);
|
||||||
|
|
||||||
|
},
|
||||||
|
error: (xhr, status, err) => {
|
||||||
|
|
||||||
|
console.error(this.currentUserApiUrl, status, err.toString());
|
||||||
|
|
||||||
|
deferredObject.resolve(false);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferredObject.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Depending on various factors detects the current active tab URL or domain,
|
||||||
|
* and sends it to WakaTime for logging.
|
||||||
|
*
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
recordHeartbeat()
|
||||||
|
{
|
||||||
|
this.checkAuth().done(data => {
|
||||||
|
|
||||||
|
if(data !== false){
|
||||||
|
|
||||||
|
// User is logged in.
|
||||||
|
// Change extension icon to default color.
|
||||||
|
changeExtensionIcon();
|
||||||
|
|
||||||
|
chrome.idle.queryState(this.detectionIntervalInSeconds, (newState) => {
|
||||||
|
|
||||||
|
if(newState === 'active')
|
||||||
|
{
|
||||||
|
// Get current tab URL.
|
||||||
|
chrome.tabs.query({active: true}, (tabs) => {
|
||||||
|
this.sendHeartbeat(tabs[0].url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// User is not logged in.
|
||||||
|
// Change extension icon to red color.
|
||||||
|
changeExtensionIcon('red');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates payload for the heartbeat and returns it as JSON.
|
||||||
|
*
|
||||||
|
* @param string entity
|
||||||
|
* @param string type 'domain' or 'url'
|
||||||
|
* @param boolean debug = false
|
||||||
|
* @return JSON
|
||||||
|
*/
|
||||||
|
_preparePayload(entity, type, debug = false)
|
||||||
|
{
|
||||||
|
return JSON.stringify({
|
||||||
|
entity: entity,
|
||||||
|
type: type,
|
||||||
|
time: currentTimestamp(),
|
||||||
|
is_debugging: debug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise with logging type variable.
|
||||||
|
*
|
||||||
|
* @return $.promise
|
||||||
|
*/
|
||||||
|
_getLoggingType()
|
||||||
|
{
|
||||||
|
var deferredObject = $.Deferred();
|
||||||
|
|
||||||
|
chrome.storage.sync.get({
|
||||||
|
loggingType: this.loggingType
|
||||||
|
}, function(items) {
|
||||||
|
deferredObject.resolve(items.loggingType);
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferredObject.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the entity and logging type it creates a payload and
|
||||||
|
* sends an ajax post request to the API.
|
||||||
|
*
|
||||||
|
* @param string entity
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
sendHeartbeat(entity)
|
||||||
|
{
|
||||||
|
this._getLoggingType().done((loggingType) => {
|
||||||
|
|
||||||
|
// Get only the domain from the entity.
|
||||||
|
// And send that in heartbeat
|
||||||
|
if(loggingType == 'domain') {
|
||||||
|
|
||||||
|
var domain = UrlHelper.getDomainFromUrl(entity);
|
||||||
|
|
||||||
|
var payload = this._preparePayload(domain, 'domain');
|
||||||
|
|
||||||
|
console.log(payload);
|
||||||
|
|
||||||
|
this.sendAjaxRequestToApi(payload);
|
||||||
|
|
||||||
|
}
|
||||||
|
// Send entity in heartbeat
|
||||||
|
else if (loggingType == 'url') {
|
||||||
|
var payload = this._preparePayload(entity, 'url');
|
||||||
|
|
||||||
|
console.log(payload);
|
||||||
|
|
||||||
|
this.sendAjaxRequestToApi(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends AJAX request with payload to the heartbeat API as JSON.
|
||||||
|
*
|
||||||
|
* @param JSON payload
|
||||||
|
* @param string method = 'POST'
|
||||||
|
* @return $.promise
|
||||||
|
*/
|
||||||
|
sendAjaxRequestToApi(payload, method = 'POST') {
|
||||||
|
|
||||||
|
var deferredObject = $.Deferred();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: this.heartbeatApiUrl,
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
method: method,
|
||||||
|
data: payload,
|
||||||
|
success: (response) => {
|
||||||
|
|
||||||
|
deferredObject.resolve(this);
|
||||||
|
|
||||||
|
},
|
||||||
|
error: (xhr, status, err) => {
|
||||||
|
|
||||||
|
console.error(this.heartbeatApiUrl, status, err.toString());
|
||||||
|
|
||||||
|
deferredObject.resolve(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferredObject.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WakaTime;
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ var MainList = require('./MainList.react');
|
|||||||
|
|
||||||
var changeExtensionIcon = require('../helpers/changeExtensionIcon');
|
var changeExtensionIcon = require('../helpers/changeExtensionIcon');
|
||||||
|
|
||||||
|
var WakaTimeOriginal = require('../WakaTime');
|
||||||
|
|
||||||
class WakaTime extends React.Component
|
class WakaTime extends React.Component
|
||||||
{
|
{
|
||||||
currentUserApiUrl = 'https://wakatime.com/api/v1/users/current';
|
|
||||||
|
|
||||||
logoutUserUrl = 'https://wakatime.com/logout';
|
logoutUserUrl = 'https://wakatime.com/logout';
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@@ -34,7 +34,9 @@ class WakaTime extends React.Component
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.checkAuth().done(data => {
|
var wakatime = new WakaTimeOriginal;
|
||||||
|
|
||||||
|
wakatime.checkAuth().done(data => {
|
||||||
|
|
||||||
if(data !== false){
|
if(data !== false){
|
||||||
|
|
||||||
@@ -61,29 +63,6 @@ class WakaTime extends React.Component
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAuth()
|
|
||||||
{
|
|
||||||
var deferredObject = $.Deferred();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: this.currentUserApiUrl,
|
|
||||||
dataType: 'json',
|
|
||||||
success: (data) => {
|
|
||||||
|
|
||||||
deferredObject.resolve(data.data);
|
|
||||||
|
|
||||||
},
|
|
||||||
error: (xhr, status, err) => {
|
|
||||||
|
|
||||||
console.error(this.currentUserApiUrl, status, err.toString());
|
|
||||||
|
|
||||||
deferredObject.resolve(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferredObject.promise();
|
|
||||||
}
|
|
||||||
|
|
||||||
logoutUser()
|
logoutUser()
|
||||||
{
|
{
|
||||||
var deferredObject = $.Deferred();
|
var deferredObject = $.Deferred();
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
var WakaTime = require('./WakaTime');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whenever an alarms sets off, this function
|
||||||
|
* gets called to detect the alarm name and
|
||||||
|
* do appropriate action.
|
||||||
|
*
|
||||||
|
* @param alarm
|
||||||
|
*/
|
||||||
|
function resolveAlarm(alarm) {
|
||||||
|
// |alarm| can be undefined because onAlarm also gets called from
|
||||||
|
// window.setTimeout on old chrome versions.
|
||||||
|
if (alarm && alarm.name == 'heartbeatAlarm') {
|
||||||
|
|
||||||
|
console.log('recording a heartbeat - alarm triggered');
|
||||||
|
|
||||||
|
var wakatime = new WakaTime;
|
||||||
|
|
||||||
|
wakatime.recordHeartbeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a listener to resolve alarms
|
||||||
|
chrome.alarms.onAlarm.addListener(resolveAlarm);
|
||||||
|
|
||||||
|
// Create a new alarm for heartbeats.
|
||||||
|
chrome.alarms.create('heartbeatAlarm', {periodInMinutes: 2});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whenever a active tab is changed it records a heartbeat with that tab url.
|
||||||
|
*/
|
||||||
|
chrome.tabs.onActivated.addListener(function(activeInfo) {
|
||||||
|
|
||||||
|
chrome.tabs.get(activeInfo.tabId, function(tab) {
|
||||||
|
|
||||||
|
console.log('recording a heartbeat - active tab changed');
|
||||||
|
|
||||||
|
var wakatime = new WakaTime;
|
||||||
|
|
||||||
|
wakatime.recordHeartbeat();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whenever any tab is updated it checks if the updated tab is the tab that is
|
||||||
|
* currently active and if it is, then it records a heartbeat.
|
||||||
|
*/
|
||||||
|
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
|
||||||
|
|
||||||
|
if(changeInfo.status === 'complete')
|
||||||
|
{
|
||||||
|
// Get current tab URL.
|
||||||
|
chrome.tabs.query({active: true}, (tabs) => {
|
||||||
|
// If tab updated is the same as active tab
|
||||||
|
if(tabId == tabs[0].id)
|
||||||
|
{
|
||||||
|
console.log('recording a heartbeat - tab updated');
|
||||||
|
|
||||||
|
var wakatime = new WakaTime;
|
||||||
|
|
||||||
|
wakatime.recordHeartbeat();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|||||||
@@ -7,28 +7,13 @@
|
|||||||
*/
|
*/
|
||||||
export default function changeExtensionIcon(color = '') {
|
export default function changeExtensionIcon(color = '') {
|
||||||
|
|
||||||
var canvas = document.getElementById('icon');
|
|
||||||
var context = canvas.getContext('2d');
|
|
||||||
|
|
||||||
var x = 0;
|
|
||||||
var y = 0;
|
|
||||||
var width = 19;
|
|
||||||
var height = 19;
|
|
||||||
var imageObj = new Image();
|
|
||||||
|
|
||||||
imageObj.onload = function() {
|
|
||||||
context.drawImage(imageObj, x, y, width, height);
|
|
||||||
|
|
||||||
var imageData = context.getImageData(x, y, width, height);
|
|
||||||
|
|
||||||
chrome.browserAction.setIcon({
|
|
||||||
imageData: imageData
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if(color !== ''){
|
if(color !== ''){
|
||||||
color = '-' + color;
|
color = '-' + color;
|
||||||
}
|
}
|
||||||
|
|
||||||
imageObj.src = 'graphics/wakatime-logo-48' + color + '.png';
|
var path = './graphics/wakatime-logo-48' + color + '.png';
|
||||||
|
|
||||||
|
chrome.browserAction.setIcon({
|
||||||
|
path: path
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
8
assets/js/helpers/currentTimestamp.js
Normal file
8
assets/js/helpers/currentTimestamp.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Returns UNIX timestamp
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
export default function(){
|
||||||
|
return Math.round((new Date()).getTime() / 1000);
|
||||||
|
}
|
||||||
@@ -4,16 +4,31 @@ require('bootstrap');
|
|||||||
|
|
||||||
var $ = require('jquery');
|
var $ = require('jquery');
|
||||||
|
|
||||||
|
function detectCheckedRadio(name)
|
||||||
|
{
|
||||||
|
for(var i = 0; i < document.getElementsByName(name).length; i++)
|
||||||
|
{
|
||||||
|
var button = document.getElementsByName(name)[i];
|
||||||
|
|
||||||
|
if(button.checked === true)
|
||||||
|
{
|
||||||
|
return button.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Saves options to chrome.storage.sync.
|
// Saves options to chrome.storage.sync.
|
||||||
function save_options(e) {
|
function save_options(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var theme = document.getElementById('theme').value;
|
var theme = document.getElementById('theme').value;
|
||||||
var blacklist = document.getElementById('blacklist').value;
|
var blacklist = document.getElementById('blacklist').value;
|
||||||
|
var loggingType = detectCheckedRadio('loggingType');
|
||||||
|
|
||||||
chrome.storage.sync.set({
|
chrome.storage.sync.set({
|
||||||
theme: theme,
|
theme: theme,
|
||||||
blacklist: blacklist
|
blacklist: blacklist,
|
||||||
|
loggingType: loggingType
|
||||||
}, function() {
|
}, function() {
|
||||||
// Update status to let user know options were saved.
|
// Update status to let user know options were saved.
|
||||||
var status = $('#status');
|
var status = $('#status');
|
||||||
@@ -36,10 +51,12 @@ function restore_options() {
|
|||||||
// Use default value color = 'red' and likesColor = true.
|
// Use default value color = 'red' and likesColor = true.
|
||||||
chrome.storage.sync.get({
|
chrome.storage.sync.get({
|
||||||
theme: 'light',
|
theme: 'light',
|
||||||
blacklist: ''
|
blacklist: '',
|
||||||
|
loggingType: 'domain'
|
||||||
}, function(items) {
|
}, function(items) {
|
||||||
document.getElementById('theme').value = items.theme;
|
document.getElementById('theme').value = items.theme;
|
||||||
document.getElementById('blacklist').value = items.blacklist;
|
document.getElementById('blacklist').value = items.blacklist;
|
||||||
|
document.getElementById(items.loggingType + 'Type').checked = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ elixir(function (mix) {
|
|||||||
mix.copy('vendor/bower_components/font-awesome/less', 'assets/less/font-awesome');
|
mix.copy('vendor/bower_components/font-awesome/less', 'assets/less/font-awesome');
|
||||||
mix.copy('vendor/bower_components/font-awesome/fonts', 'public/fonts');
|
mix.copy('vendor/bower_components/font-awesome/fonts', 'public/fonts');
|
||||||
mix.less('app.less');
|
mix.less('app.less');
|
||||||
mix.browserify('app.js', null, 'assets/js');
|
//mix.browserify('app.js', null, 'assets/js');
|
||||||
//mix.browserify('events.js', 'public/js/events.js', 'assets/js');
|
mix.browserify('events.js', 'public/js/events.js', 'assets/js');
|
||||||
//mix.browserify('options.js', 'public/js/options.js', 'assets/js');
|
//mix.browserify('options.js', 'public/js/options.js', 'assets/js');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,9 +11,11 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
"https://wakatime.com/api/v1/users/current",
|
"https://wakatime.com/api/v1/users/current",
|
||||||
"https://wakatime.com/logout",
|
"https://wakatime.com/logout",
|
||||||
|
"https://wakatime.com/api/v1/users/current/heartbeats",
|
||||||
"alarms",
|
"alarms",
|
||||||
"tabs",
|
"tabs",
|
||||||
"storage"
|
"storage",
|
||||||
|
"idle"
|
||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": [
|
"scripts": [
|
||||||
|
|||||||
19
options.html
19
options.html
@@ -36,6 +36,25 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-lg-2 control-label">Logging type</label>
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="loggingType" id="domainType" value="domain" checked="">
|
||||||
|
Only the domain
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="loggingType" id="urlType" value="url">
|
||||||
|
Entire URL
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-lg-10 col-lg-offset-2">
|
<div class="col-lg-10 col-lg-offset-2">
|
||||||
<button id="save" type="submit" class="btn btn-primary">Save</button>
|
<button id="save" type="submit" class="btn btn-primary">Save</button>
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<canvas id="icon"></canvas>
|
|
||||||
|
|
||||||
<div id="wakatime"></div>
|
<div id="wakatime"></div>
|
||||||
|
|
||||||
<script src="public/js/bundle.js"></script>
|
<script src="public/js/bundle.js"></script>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
9576
public/js/events.js
9576
public/js/events.js
File diff suppressed because it is too large
Load Diff
@@ -8,16 +8,28 @@ require('bootstrap');
|
|||||||
|
|
||||||
var $ = require('jquery');
|
var $ = require('jquery');
|
||||||
|
|
||||||
|
function detectCheckedRadio(name) {
|
||||||
|
for (var i = 0; i < document.getElementsByName(name).length; i++) {
|
||||||
|
var button = document.getElementsByName(name)[i];
|
||||||
|
|
||||||
|
if (button.checked === true) {
|
||||||
|
return button.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Saves options to chrome.storage.sync.
|
// Saves options to chrome.storage.sync.
|
||||||
function save_options(e) {
|
function save_options(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var theme = document.getElementById('theme').value;
|
var theme = document.getElementById('theme').value;
|
||||||
var blacklist = document.getElementById('blacklist').value;
|
var blacklist = document.getElementById('blacklist').value;
|
||||||
|
var loggingType = detectCheckedRadio('loggingType');
|
||||||
|
|
||||||
chrome.storage.sync.set({
|
chrome.storage.sync.set({
|
||||||
theme: theme,
|
theme: theme,
|
||||||
blacklist: blacklist
|
blacklist: blacklist,
|
||||||
|
loggingType: loggingType
|
||||||
}, function () {
|
}, function () {
|
||||||
// Update status to let user know options were saved.
|
// Update status to let user know options were saved.
|
||||||
var status = $('#status');
|
var status = $('#status');
|
||||||
@@ -39,10 +51,12 @@ function restore_options() {
|
|||||||
// Use default value color = 'red' and likesColor = true.
|
// Use default value color = 'red' and likesColor = true.
|
||||||
chrome.storage.sync.get({
|
chrome.storage.sync.get({
|
||||||
theme: 'light',
|
theme: 'light',
|
||||||
blacklist: ''
|
blacklist: '',
|
||||||
|
loggingType: 'domain'
|
||||||
}, function (items) {
|
}, function (items) {
|
||||||
document.getElementById('theme').value = items.theme;
|
document.getElementById('theme').value = items.theme;
|
||||||
document.getElementById('blacklist').value = items.blacklist;
|
document.getElementById('blacklist').value = items.blacklist;
|
||||||
|
document.getElementById(items.loggingType + 'Type').checked = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user