From a2ed4e5acdc9c04216eb6125b26ab74d0de1b7b1 Mon Sep 17 00:00:00 2001 From: Mario Basic Date: Sun, 7 Jun 2015 21:53:47 +0200 Subject: [PATCH] Minor optimizations. --- .gitignore | 2 + README.md | 15 +- assets/js/UrlHelper.js | 3 +- assets/js/WakaTime.js | 71 ++-- assets/js/app.js | 4 +- assets/js/components/MainList.react.js | 67 ++-- assets/js/components/Navbar.react.js | 4 +- assets/js/components/WakaTime.react.js | 93 +++-- assets/js/events.js | 20 +- assets/js/helpers/changeExtensionIcon.js | 5 +- assets/js/helpers/currentTimestamp.js | 2 - assets/js/options.js | 69 ++-- public/js/bundle.js | 325 +++++++++++------- public/js/events.js | 89 ++--- public/js/options.js | 74 ++-- sc_1.png => screenshots/sc_1.png | Bin .../sc_2-options.png | Bin .../sc_3_closed.png | Bin sc_3_open.png => screenshots/sc_3_open.png | Bin screenshots/sc_4-options.png | Bin 0 -> 19535 bytes 20 files changed, 450 insertions(+), 393 deletions(-) rename sc_1.png => screenshots/sc_1.png (100%) rename sc_2-options.png => screenshots/sc_2-options.png (100%) rename sc_3_closed.png => screenshots/sc_3_closed.png (100%) rename sc_3_open.png => screenshots/sc_3_open.png (100%) create mode 100644 screenshots/sc_4-options.png diff --git a/.gitignore b/.gitignore index ef0c08e..8dc72a4 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ node_modules .DS_Store vendor/ + +.idea \ No newline at end of file diff --git a/README.md b/README.md index 5253cfc..f4df5bc 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,11 @@ Installation ## Screenshots -![SC closed](./sc_3_closed.png) +![SC closed](./screenshots/sc_3_closed.png) -![SC open](./sc_3_open.png) +![SC open](./screenshots/sc_3_open.png) -![Options SC](./sc_2-options.png) +![Options SC](./screenshots/sc_4-options.png) ## Development instructions @@ -55,10 +55,11 @@ gulp watch ### Load unpacked in Chrome -1. Go to `Settings` -> `Extensions` -2. Enable `Developer mode` -3. Click `Load unpacked extension...` -4. Select repository directory +1. Clone repository to disk +2. Go to `Settings` -> `Extensions` +3. Enable `Developer mode` +4. Click `Load unpacked extension...` +5. Select repository directory ### Issues diff --git a/assets/js/UrlHelper.js b/assets/js/UrlHelper.js index 52e3b06..5043592 100644 --- a/assets/js/UrlHelper.js +++ b/assets/js/UrlHelper.js @@ -1,7 +1,6 @@ class UrlHelper { - static getDomainFromUrl(url) - { + static getDomainFromUrl(url) { var parts = url.split('/'); return parts[0] + "//" + parts[2]; diff --git a/assets/js/WakaTime.js b/assets/js/WakaTime.js index 97d2820..efee8c4 100644 --- a/assets/js/WakaTime.js +++ b/assets/js/WakaTime.js @@ -1,9 +1,7 @@ -var UrlHelper = require('./UrlHelper'); - -var $ = require('jquery'); - -var currentTimestamp = require('./helpers/currentTimestamp'); -var changeExtensionIcon = require('./helpers/changeExtensionIcon'); +import UrlHelper from './UrlHelper.js'; +import $ from 'jquery'; +import currentTimestamp from './helpers/currentTimestamp.js'; +import changeExtensionIcon from './helpers/changeExtensionIcon.js'; class WakaTime { @@ -18,16 +16,15 @@ class WakaTime { /** * Checks if the user is logged in. * - * @return $.promise() + * @returns {*} */ - checkAuth() - { + checkAuth() { var deferredObject = $.Deferred(); $.ajax({ url: this.currentUserApiUrl, dataType: 'json', - success: (data) => { + success: (data) => { deferredObject.resolve(data.data); @@ -46,14 +43,11 @@ class WakaTime { /** * Depending on various factors detects the current active tab URL or domain, * and sends it to WakaTime for logging. - * - * @return null */ - recordHeartbeat() - { + recordHeartbeat() { this.checkAuth().done(data => { - if(data !== false){ + if (data !== false) { // User is logged in. // Change extension icon to default color. @@ -61,8 +55,7 @@ class WakaTime { chrome.idle.queryState(this.detectionIntervalInSeconds, (newState) => { - if(newState === 'active') - { + if (newState === 'active') { // Get current tab URL. chrome.tabs.query({active: true}, (tabs) => { this.sendHeartbeat(tabs[0].url); @@ -83,13 +76,13 @@ class WakaTime { /** * 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 + * @param entity + * @param type + * @param debug + * @returns {*} + * @private */ - _preparePayload(entity, type, debug = false) - { + _preparePayload(entity, type, debug = false) { return JSON.stringify({ entity: entity, type: type, @@ -98,18 +91,19 @@ class WakaTime { }); } + /** * Returns a promise with logging type variable. * - * @return $.promise + * @returns {*} + * @private */ - _getLoggingType() - { + _getLoggingType() { var deferredObject = $.Deferred(); chrome.storage.sync.get({ loggingType: this.loggingType - }, function(items) { + }, function (items) { deferredObject.resolve(items.loggingType); }); @@ -120,20 +114,21 @@ class WakaTime { * Given the entity and logging type it creates a payload and * sends an ajax post request to the API. * - * @param string entity - * @return null + * @param entity */ - sendHeartbeat(entity) - { + sendHeartbeat(entity) { + + var payload = null; + this._getLoggingType().done((loggingType) => { // Get only the domain from the entity. // And send that in heartbeat - if(loggingType == 'domain') { + if (loggingType == 'domain') { var domain = UrlHelper.getDomainFromUrl(entity); - var payload = this._preparePayload(domain, 'domain'); + payload = this._preparePayload(domain, 'domain'); console.log(payload); @@ -142,7 +137,7 @@ class WakaTime { } // Send entity in heartbeat else if (loggingType == 'url') { - var payload = this._preparePayload(entity, 'url'); + payload = this._preparePayload(entity, 'url'); console.log(payload); @@ -155,9 +150,9 @@ class WakaTime { /** * Sends AJAX request with payload to the heartbeat API as JSON. * - * @param JSON payload - * @param string method = 'POST' - * @return $.promise + * @param payload + * @param method + * @returns {*} */ sendAjaxRequestToApi(payload, method = 'POST') { @@ -169,7 +164,7 @@ class WakaTime { contentType: 'application/json', method: method, data: payload, - success: (response) => { + success: (response) => { deferredObject.resolve(this); diff --git a/assets/js/app.js b/assets/js/app.js index b00cd30..8eb8340 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -2,8 +2,8 @@ global.jQuery = require('jquery'); require('bootstrap'); -var React = require('react'); -var WakaTime = require('./components/WakaTime.react'); +import React from 'react'; +import WakaTime from './components/WakaTime.react.js'; React.render( , diff --git a/assets/js/components/MainList.react.js b/assets/js/components/MainList.react.js index 0b1a404..f7e92ed 100644 --- a/assets/js/components/MainList.react.js +++ b/assets/js/components/MainList.react.js @@ -1,53 +1,51 @@ -var React = require('react'); +import React from 'react'; -class MainList extends React.Component -{ - componentDidMount() - { +class MainList extends React.Component { + componentDidMount() { } - _openOptionsPage() - { - if (chrome.runtime.openOptionsPage) { - // New way to open options pages, if supported (Chrome 42+). - chrome.runtime.openOptionsPage(); - } else { - // Reasonable fallback. - window.open(chrome.runtime.getURL('options.html')); - } + _openOptionsPage() { + if (chrome.runtime.openOptionsPage) { + // New way to open options pages, if supported (Chrome 42+). + chrome.runtime.openOptionsPage(); + } else { + // Reasonable fallback. + window.open(chrome.runtime.getURL('options.html')); + } } - render() - { + render() { var loginLogoutButton = () => { - if(this.props.loggedIn === true) - { + if (this.props.loggedIn === true) { return (
- - Custom Rules - - - Dashboard - - - Logout - + + + Custom Rules + + + + Dashboard + + + + Logout +
); } return ( - Login + + Login ); }; var signedInAs = () => { - if(this.props.loggedIn === true) - { + if (this.props.loggedIn === true) { return (
@@ -56,7 +54,9 @@ class MainList extends React.Component
- Signed in as {this.props.user.full_name}
+ Signed in as + {this.props.user.full_name} +
{this.props.user.email}
@@ -66,14 +66,15 @@ class MainList extends React.Component } }; - return( + return (
{signedInAs()}
- Options + + Options {loginLogoutButton()} diff --git a/assets/js/components/Navbar.react.js b/assets/js/components/Navbar.react.js index 3c8af6a..c88e042 100644 --- a/assets/js/components/Navbar.react.js +++ b/assets/js/components/Navbar.react.js @@ -1,6 +1,6 @@ -var React = require('react'); +import React from 'react'; -class Navbar extends React.Component{ +class Navbar extends React.Component { render() { return ( diff --git a/assets/js/components/WakaTime.react.js b/assets/js/components/WakaTime.react.js index 55ccd8a..cd31cb9 100644 --- a/assets/js/components/WakaTime.react.js +++ b/assets/js/components/WakaTime.react.js @@ -1,15 +1,11 @@ -var React = require("react"); -var $ = require('jquery'); +import React from "react"; +import $ from 'jquery'; +import NavBar from './NavBar.react.js'; +import MainList from './MainList.react.js'; +import changeExtensionIcon from '../helpers/changeExtensionIcon.js'; +import WakaTimeOriginal from '../WakaTime.js'; -var NavBar = require('./NavBar.react'); -var MainList = require('./MainList.react'); - -var changeExtensionIcon = require('../helpers/changeExtensionIcon'); - -var WakaTimeOriginal = require('../WakaTime'); - -class WakaTime extends React.Component -{ +class WakaTime extends React.Component { logoutUserUrl = 'https://wakatime.com/logout'; state = { @@ -21,56 +17,49 @@ class WakaTime extends React.Component loggedIn: false }; - componentDidMount() - { - chrome.storage.sync.get({ - theme: 'light' - }, function(items) { - if(items.theme == 'light') { - changeExtensionIcon(); - } - else { - changeExtensionIcon('white'); - } - }); + componentDidMount() { + chrome.storage.sync.get({ + theme: 'light' + }, function (items) { + if (items.theme == 'light') { + changeExtensionIcon(); + } + else { + changeExtensionIcon('white'); + } + }); - var wakatime = new WakaTimeOriginal; + var wakatime = new WakaTimeOriginal; - wakatime.checkAuth().done(data => { + wakatime.checkAuth().done(data => { - if(data !== false){ + if (data !== false) { - this.setState({ - user: { - full_name: data.full_name, - email: data.email, - photo: data.photo - }, - loggedIn: true - }); + this.setState({ + user: { + full_name: data.full_name, + email: data.email, + photo: data.photo + }, + loggedIn: true + }); - changeExtensionIcon(); - - } - else { - - changeExtensionIcon('red'); - - //TODO: Redirect user to wakatime login page. - // - } - }); + changeExtensionIcon(); + } + else { + changeExtensionIcon('red'); + } + }); } - logoutUser() - { + logoutUser() { var deferredObject = $.Deferred(); $.ajax({ url: this.logoutUserUrl, method: 'GET', - success: () => { + success: () => { deferredObject.resolve(this); @@ -86,8 +75,7 @@ class WakaTime extends React.Component return deferredObject.promise(); } - _logoutUser() - { + _logoutUser() { this.logoutUser().done(() => { this.setState({ @@ -104,9 +92,8 @@ class WakaTime extends React.Component }); } - render() - { - return( + render() { + return (
diff --git a/assets/js/events.js b/assets/js/events.js index d16e988..900d87b 100644 --- a/assets/js/events.js +++ b/assets/js/events.js @@ -1,4 +1,4 @@ -var WakaTime = require('./WakaTime'); +import WakaTime from "./WakaTime.js"; /** * Whenever an alarms sets off, this function @@ -29,15 +29,15 @@ 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.onActivated.addListener(function (activeInfo) { - chrome.tabs.get(activeInfo.tabId, function(tab) { + chrome.tabs.get(activeInfo.tabId, function (tab) { - console.log('recording a heartbeat - active tab changed'); + console.log('recording a heartbeat - active tab changed'); - var wakatime = new WakaTime; + var wakatime = new WakaTime; - wakatime.recordHeartbeat(); + wakatime.recordHeartbeat(); }); }); @@ -46,15 +46,13 @@ chrome.tabs.onActivated.addListener(function(activeInfo) { * 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) { +chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { - if(changeInfo.status === 'complete') - { + 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) - { + if (tabId == tabs[0].id) { console.log('recording a heartbeat - tab updated'); var wakatime = new WakaTime; diff --git a/assets/js/helpers/changeExtensionIcon.js b/assets/js/helpers/changeExtensionIcon.js index 0b9310f..82e2b51 100644 --- a/assets/js/helpers/changeExtensionIcon.js +++ b/assets/js/helpers/changeExtensionIcon.js @@ -1,13 +1,10 @@ /** * It changes the extension icon color. * Supported values are: 'red', 'white' and ''. - * - * @param string color = '' - * @return null */ export default function changeExtensionIcon(color = '') { - if(color !== ''){ + if (color !== '') { color = '-' + color; } diff --git a/assets/js/helpers/currentTimestamp.js b/assets/js/helpers/currentTimestamp.js index 7a9c124..e441a49 100644 --- a/assets/js/helpers/currentTimestamp.js +++ b/assets/js/helpers/currentTimestamp.js @@ -1,7 +1,5 @@ /** * Returns UNIX timestamp - * - * @return integer */ export default function(){ return Math.round((new Date()).getTime() / 1000); diff --git a/assets/js/options.js b/assets/js/options.js index 450d4c5..43a68f0 100644 --- a/assets/js/options.js +++ b/assets/js/options.js @@ -2,16 +2,13 @@ global.jQuery = require('jquery'); require('bootstrap'); -var $ = require('jquery'); +import $ from "jquery"; -function detectCheckedRadio(name) -{ - for(var i = 0; i < document.getElementsByName(name).length; i++) - { +function detectCheckedRadio(name) { + for (var i = 0; i < document.getElementsByName(name).length; i ++) { var button = document.getElementsByName(name)[i]; - if(button.checked === true) - { + if (button.checked === true) { return button.value; } } @@ -19,45 +16,45 @@ function detectCheckedRadio(name) // Saves options to chrome.storage.sync. function save_options(e) { - e.preventDefault(); + e.preventDefault(); - var theme = document.getElementById('theme').value; - var blacklist = document.getElementById('blacklist').value; - var loggingType = detectCheckedRadio('loggingType'); + var theme = document.getElementById('theme').value; + var blacklist = document.getElementById('blacklist').value; + var loggingType = detectCheckedRadio('loggingType'); - chrome.storage.sync.set({ - theme: theme, - blacklist: blacklist, - loggingType: loggingType - }, function() { - // Update status to let user know options were saved. - var status = $('#status'); - status.html('Well done! Options have been saved.'); + chrome.storage.sync.set({ + theme: theme, + blacklist: blacklist, + loggingType: loggingType + }, function () { + // Update status to let user know options were saved. + var status = $('#status'); + status.html('Well done! Options have been saved.'); - status.fadeIn(1500, function() { - setTimeout(function() { - status.fadeOut(1500, function() { - status.html(''); + status.fadeIn(1500, function () { + setTimeout(function () { + status.fadeOut(1500, function () { + status.html(''); + }); + }, 750); }); - }, 750); - }); - }); + }); } // Restores select box and checkbox state using the preferences // stored in chrome.storage. function restore_options() { - // Use default value color = 'red' and likesColor = true. - chrome.storage.sync.get({ - theme: 'light', - blacklist: '', - loggingType: 'domain' - }, function(items) { - document.getElementById('theme').value = items.theme; - document.getElementById('blacklist').value = items.blacklist; - document.getElementById(items.loggingType + 'Type').checked = true; - }); + // Use default value color = 'red' and likesColor = true. + chrome.storage.sync.get({ + theme: 'light', + blacklist: '', + loggingType: 'domain' + }, function (items) { + document.getElementById('theme').value = items.theme; + document.getElementById('blacklist').value = items.blacklist; + document.getElementById(items.loggingType + 'Type').checked = true; + }); } document.addEventListener('DOMContentLoaded', restore_options); diff --git a/public/js/bundle.js b/public/js/bundle.js index 9854f8d..94f65f0 100644 --- a/public/js/bundle.js +++ b/public/js/bundle.js @@ -3,16 +3,23 @@ /* This is a fix for Bootstrap requiring jQuery */ 'use strict'; +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _componentsWakaTimeReactJs = require('./components/WakaTime.react.js'); + +var _componentsWakaTimeReactJs2 = _interopRequireDefault(_componentsWakaTimeReactJs); + global.jQuery = require('jquery'); require('bootstrap'); -var React = require('react'); -var WakaTime = require('./components/WakaTime.react'); - -React.render(React.createElement(WakaTime, null), document.getElementById('wakatime')); +_react2['default'].render(_react2['default'].createElement(_componentsWakaTimeReactJs2['default'], null), document.getElementById('wakatime')); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./components/WakaTime.react":6,"bootstrap":9,"jquery":22,"react":178}],2:[function(require,module,exports){ +},{"./components/WakaTime.react.js":6,"bootstrap":9,"jquery":22,"react":178}],2:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -52,14 +59,25 @@ Object.defineProperty(exports, '__esModule', { var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } -var UrlHelper = require('./UrlHelper'); +var _UrlHelperJs = require('./UrlHelper.js'); -var $ = require('jquery'); +var _UrlHelperJs2 = _interopRequireDefault(_UrlHelperJs); -var currentTimestamp = require('./helpers/currentTimestamp'); -var changeExtensionIcon = require('./helpers/changeExtensionIcon'); +var _jquery = require('jquery'); + +var _jquery2 = _interopRequireDefault(_jquery); + +var _helpersCurrentTimestampJs = require('./helpers/currentTimestamp.js'); + +var _helpersCurrentTimestampJs2 = _interopRequireDefault(_helpersCurrentTimestampJs); + +var _helpersChangeExtensionIconJs = require('./helpers/changeExtensionIcon.js'); + +var _helpersChangeExtensionIconJs2 = _interopRequireDefault(_helpersChangeExtensionIconJs); var WakaTime = (function () { function WakaTime() { @@ -73,12 +91,18 @@ var WakaTime = (function () { _createClass(WakaTime, [{ key: 'checkAuth', + + /** + * Checks if the user is logged in. + * + * @returns {*} + */ value: function checkAuth() { var _this = this; - var deferredObject = $.Deferred(); + var deferredObject = _jquery2['default'].Deferred(); - $.ajax({ + _jquery2['default'].ajax({ url: this.currentUserApiUrl, dataType: 'json', success: function success(data) { @@ -97,6 +121,11 @@ var WakaTime = (function () { } }, { key: 'recordHeartbeat', + + /** + * Depending on various factors detects the current active tab URL or domain, + * and sends it to WakaTime for logging. + */ value: function recordHeartbeat() { var _this2 = this; @@ -104,22 +133,15 @@ var WakaTime = (function () { if (data !== false) { - console.log('user is logged id.'); // User is logged in. - changeExtensionIcon(); - - console.log('recording heartbeat.'); + // Change extension icon to default color. + (0, _helpersChangeExtensionIconJs2['default'])(); chrome.idle.queryState(_this2.detectionIntervalInSeconds, function (newState) { - console.log(newState); - if (newState === 'active') { - // Get current tab URL. chrome.tabs.query({ active: true }, function (tabs) { - console.log(tabs[0].url); - _this2.sendHeartbeat(tabs[0].url); }); } @@ -127,31 +149,44 @@ var WakaTime = (function () { } else { // User is not logged in. - changeExtensionIcon('red'); - - console.log('user is not logged id.'); - - //TODO: Redirect user to wakatime login page. - // + // Change extension icon to red color. + (0, _helpersChangeExtensionIconJs2['default'])('red'); } }); } }, { key: '_preparePayload', + + /** + * Creates payload for the heartbeat and returns it as JSON. + * + * @param entity + * @param type + * @param debug + * @returns {*} + * @private + */ value: function _preparePayload(entity, type) { var debug = arguments[2] === undefined ? false : arguments[2]; return JSON.stringify({ entity: entity, type: type, - time: currentTimestamp(), + time: (0, _helpersCurrentTimestampJs2['default'])(), is_debugging: debug }); } }, { key: '_getLoggingType', + + /** + * Returns a promise with logging type variable. + * + * @returns {*} + * @private + */ value: function _getLoggingType() { - var deferredObject = $.Deferred(); + var deferredObject = _jquery2['default'].Deferred(); chrome.storage.sync.get({ loggingType: this.loggingType @@ -163,48 +198,60 @@ var WakaTime = (function () { } }, { key: 'sendHeartbeat', + + /** + * Given the entity and logging type it creates a payload and + * sends an ajax post request to the API. + * + * @param entity + */ value: function sendHeartbeat(entity) { var _this3 = this; + var payload = null; + this._getLoggingType().done(function (loggingType) { + // Get only the domain from the entity. + // And send that in heartbeat if (loggingType == 'domain') { - console.log('sending entity with type domain'); - // Get only the domain from the entity. - // And send that in heartbeat - console.log(UrlHelper.getDomainFromUrl(entity)); + var domain = _UrlHelperJs2['default'].getDomainFromUrl(entity); - var domain = UrlHelper.getDomainFromUrl(entity); - - var payload = _this3._preparePayload(domain, 'domain'); + payload = _this3._preparePayload(domain, 'domain'); console.log(payload); - //this.sendAjaxRequestToApi(payload); - } else if (loggingType == 'url') { - console.log('sending entity with type url'); - - // Send entity in heartbeat - - var payload = _this3._preparePayload(entity, 'url'); + _this3.sendAjaxRequestToApi(payload); + } + // Send entity in heartbeat + else if (loggingType == 'url') { + payload = _this3._preparePayload(entity, 'url'); console.log(payload); - //this.sendAjaxRequestToApi(payload); + _this3.sendAjaxRequestToApi(payload); } }); } }, { key: 'sendAjaxRequestToApi', + + /** + * Sends AJAX request with payload to the heartbeat API as JSON. + * + * @param payload + * @param method + * @returns {*} + */ value: function sendAjaxRequestToApi(payload) { var _this4 = this; var method = arguments[1] === undefined ? 'POST' : arguments[1]; - var deferredObject = $.Deferred(); + var deferredObject = _jquery2['default'].Deferred(); - $.ajax({ + _jquery2['default'].ajax({ url: this.heartbeatApiUrl, dataType: 'json', contentType: 'application/json', @@ -235,7 +282,7 @@ module.exports = exports['default']; //default -},{"./UrlHelper":2,"./helpers/changeExtensionIcon":7,"./helpers/currentTimestamp":8,"jquery":22}],4:[function(require,module,exports){ +},{"./UrlHelper.js":2,"./helpers/changeExtensionIcon.js":7,"./helpers/currentTimestamp.js":8,"jquery":22}],4:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -244,11 +291,15 @@ Object.defineProperty(exports, '__esModule', { var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } -var React = require('react'); +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); var MainList = (function (_React$Component) { function MainList() { @@ -282,64 +333,64 @@ var MainList = (function (_React$Component) { var loginLogoutButton = function loginLogoutButton() { if (_this.props.loggedIn === true) { - return React.createElement( + return _react2['default'].createElement( 'div', null, - React.createElement( + _react2['default'].createElement( 'a', { target: '_blank', href: 'https://wakatime.com/settings/rules', className: 'list-group-item' }, - React.createElement('i', { className: 'fa fa-fw fa-filter' }), - ' Custom Rules' + _react2['default'].createElement('i', { className: 'fa fa-fw fa-filter' }), + 'Custom Rules' ), - React.createElement( + _react2['default'].createElement( 'a', { target: '_blank', href: 'https://wakatime.com/dashboard', className: 'list-group-item' }, - React.createElement('i', { className: 'fa fa-fw fa-tachometer' }), - ' Dashboard' + _react2['default'].createElement('i', { className: 'fa fa-fw fa-tachometer' }), + 'Dashboard' ), - React.createElement( + _react2['default'].createElement( 'a', { href: '#', className: 'list-group-item', onClick: _this.props.logoutUser }, - React.createElement('i', { className: 'fa fa-fw fa-sign-out' }), - ' Logout' + _react2['default'].createElement('i', { className: 'fa fa-fw fa-sign-out' }), + 'Logout' ) ); } - return React.createElement( + return _react2['default'].createElement( 'a', { target: '_blank', href: 'https://wakatime.com/login', className: 'list-group-item' }, - React.createElement('i', { className: 'fa fa-fw fa-sign-in' }), - ' Login' + _react2['default'].createElement('i', { className: 'fa fa-fw fa-sign-in' }), + 'Login' ); }; var signedInAs = function signedInAs() { if (_this.props.loggedIn === true) { - return React.createElement( + return _react2['default'].createElement( 'div', { className: 'panel panel-default' }, - React.createElement( + _react2['default'].createElement( 'div', { className: 'panel-body' }, - React.createElement( + _react2['default'].createElement( 'div', { className: 'row' }, - React.createElement( + _react2['default'].createElement( 'div', { className: 'col-xs-2' }, - React.createElement('img', { className: 'img-circle', width: '48', height: '48', src: _this.props.user.photo }) + _react2['default'].createElement('img', { className: 'img-circle', width: '48', height: '48', src: _this.props.user.photo }) ), - React.createElement( + _react2['default'].createElement( 'div', { className: 'col-xs-10' }, - 'Signed in as ', - React.createElement( + 'Signed in as', + _react2['default'].createElement( 'b', null, _this.props.user.full_name ), - React.createElement('br', null), + _react2['default'].createElement('br', null), _this.props.user.email ) ) @@ -348,18 +399,18 @@ var MainList = (function (_React$Component) { } }; - return React.createElement( + return _react2['default'].createElement( 'div', null, signedInAs(), - React.createElement( + _react2['default'].createElement( 'div', { className: 'list-group' }, - React.createElement( + _react2['default'].createElement( 'a', { href: '#', className: 'list-group-item', onClick: this._openOptionsPage }, - React.createElement('i', { className: 'fa fa-fw fa-cogs' }), - ' Options' + _react2['default'].createElement('i', { className: 'fa fa-fw fa-cogs' }), + 'Options' ), loginLogoutButton() ) @@ -368,7 +419,7 @@ var MainList = (function (_React$Component) { }]); return MainList; -})(React.Component); +})(_react2['default'].Component); exports['default'] = MainList; module.exports = exports['default']; @@ -382,11 +433,15 @@ Object.defineProperty(exports, "__esModule", { var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } -var React = require("react"); +var _react = require("react"); + +var _react2 = _interopRequireDefault(_react); var Navbar = (function (_React$Component) { function Navbar() { @@ -402,68 +457,68 @@ var Navbar = (function (_React$Component) { _createClass(Navbar, [{ key: "render", value: function render() { - return React.createElement( + return _react2["default"].createElement( "nav", { className: "navbar navbar-default", role: "navigation" }, - React.createElement( + _react2["default"].createElement( "div", { className: "container-fluid" }, - React.createElement( + _react2["default"].createElement( "div", { className: "navbar-header" }, - React.createElement( + _react2["default"].createElement( "button", { type: "button", className: "navbar-toggle collapsed", "data-toggle": "collapse", "data-target": "#bs-example-navbar-collapse-1" }, - React.createElement( + _react2["default"].createElement( "span", { className: "sr-only" }, "Toggle navigation" ), - React.createElement("i", { className: "fa fa-fw fa-cogs" }) + _react2["default"].createElement("i", { className: "fa fa-fw fa-cogs" }) ), - React.createElement( + _react2["default"].createElement( "a", { target: "_blank", className: "navbar-brand", href: "https://wakatime.com" }, "WakaTime", - React.createElement("img", { src: "graphics/wakatime-logo-48.png" }) + _react2["default"].createElement("img", { src: "graphics/wakatime-logo-48.png" }) ) ), - React.createElement( + _react2["default"].createElement( "div", { className: "collapse navbar-collapse", id: "bs-example-navbar-collapse-1" }, - React.createElement( + _react2["default"].createElement( "ul", { className: "nav navbar-nav" }, - React.createElement( + _react2["default"].createElement( "li", { className: "dropdown" }, - React.createElement( + _react2["default"].createElement( "a", { href: "#", className: "dropdown-toggle", "data-toggle": "dropdown", role: "button", "aria-expanded": "false" }, - React.createElement("i", { className: "fa fa-fw fa-info" }), + _react2["default"].createElement("i", { className: "fa fa-fw fa-info" }), "About", - React.createElement("span", { className: "caret" }) + _react2["default"].createElement("span", { className: "caret" }) ), - React.createElement( + _react2["default"].createElement( "ul", { className: "dropdown-menu", role: "menu" }, - React.createElement( + _react2["default"].createElement( "li", null, - React.createElement( + _react2["default"].createElement( "a", { target: "_blank", href: "https://github.com/wakatime/chrome-wakatime/issues" }, - React.createElement("i", { className: "fa fa-fw fa-bug" }), + _react2["default"].createElement("i", { className: "fa fa-fw fa-bug" }), "Report an Issue" ) ), - React.createElement( + _react2["default"].createElement( "li", null, - React.createElement( + _react2["default"].createElement( "a", { target: "_blank", href: "https://github.com/wakatime/chrome-wakatime" }, - React.createElement("i", { className: "fa fa-fw fa-github" }), + _react2["default"].createElement("i", { className: "fa fa-fw fa-github" }), "View on GitHub" ) ) @@ -477,7 +532,7 @@ var Navbar = (function (_React$Component) { }]); return Navbar; -})(React.Component); +})(_react2["default"].Component); exports["default"] = Navbar; module.exports = exports["default"]; @@ -491,19 +546,35 @@ Object.defineProperty(exports, '__esModule', { var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } -var React = require('react'); -var $ = require('jquery'); +var _react = require('react'); -var NavBar = require('./NavBar.react'); -var MainList = require('./MainList.react'); +var _react2 = _interopRequireDefault(_react); -var changeExtensionIcon = require('../helpers/changeExtensionIcon'); +var _jquery = require('jquery'); -var WakaTimeOriginal = require('../WakaTime'); +var _jquery2 = _interopRequireDefault(_jquery); + +var _NavBarReactJs = require('./NavBar.react.js'); + +var _NavBarReactJs2 = _interopRequireDefault(_NavBarReactJs); + +var _MainListReactJs = require('./MainList.react.js'); + +var _MainListReactJs2 = _interopRequireDefault(_MainListReactJs); + +var _helpersChangeExtensionIconJs = require('../helpers/changeExtensionIcon.js'); + +var _helpersChangeExtensionIconJs2 = _interopRequireDefault(_helpersChangeExtensionIconJs); + +var _WakaTimeJs = require('../WakaTime.js'); + +var _WakaTimeJs2 = _interopRequireDefault(_WakaTimeJs); var WakaTime = (function (_React$Component) { function WakaTime() { @@ -535,13 +606,13 @@ var WakaTime = (function (_React$Component) { theme: 'light' }, function (items) { if (items.theme == 'light') { - changeExtensionIcon(); + (0, _helpersChangeExtensionIconJs2['default'])(); } else { - changeExtensionIcon('white'); + (0, _helpersChangeExtensionIconJs2['default'])('white'); } }); - var wakatime = new WakaTimeOriginal(); + var wakatime = new _WakaTimeJs2['default'](); wakatime.checkAuth().done(function (data) { @@ -556,13 +627,9 @@ var WakaTime = (function (_React$Component) { loggedIn: true }); - changeExtensionIcon(); + (0, _helpersChangeExtensionIconJs2['default'])(); } else { - - changeExtensionIcon('red'); - - //TODO: Redirect user to wakatime login page. - // + (0, _helpersChangeExtensionIconJs2['default'])('red'); } }); } @@ -571,9 +638,9 @@ var WakaTime = (function (_React$Component) { value: function logoutUser() { var _this2 = this; - var deferredObject = $.Deferred(); + var deferredObject = _jquery2['default'].Deferred(); - $.ajax({ + _jquery2['default'].ajax({ url: this.logoutUserUrl, method: 'GET', success: function success() { @@ -606,26 +673,26 @@ var WakaTime = (function (_React$Component) { loggedIn: false }); - changeExtensionIcon('red'); + (0, _helpersChangeExtensionIconJs2['default'])('red'); }); } }, { key: 'render', value: function render() { - return React.createElement( + return _react2['default'].createElement( 'div', null, - React.createElement(NavBar, null), - React.createElement( + _react2['default'].createElement(_NavBarReactJs2['default'], null), + _react2['default'].createElement( 'div', { className: 'container' }, - React.createElement( + _react2['default'].createElement( 'div', { className: 'row' }, - React.createElement( + _react2['default'].createElement( 'div', { className: 'col-md-12' }, - React.createElement(MainList, { user: this.state.user, logoutUser: this._logoutUser.bind(this), loggedIn: this.state.loggedIn }) + _react2['default'].createElement(_MainListReactJs2['default'], { user: this.state.user, logoutUser: this._logoutUser.bind(this), loggedIn: this.state.loggedIn }) ) ) ) @@ -634,18 +701,15 @@ var WakaTime = (function (_React$Component) { }]); return WakaTime; -})(React.Component); +})(_react2['default'].Component); exports['default'] = WakaTime; module.exports = exports['default']; -},{"../WakaTime":3,"../helpers/changeExtensionIcon":7,"./MainList.react":4,"./NavBar.react":5,"jquery":22,"react":178}],7:[function(require,module,exports){ +},{"../WakaTime.js":3,"../helpers/changeExtensionIcon.js":7,"./MainList.react.js":4,"./NavBar.react.js":5,"jquery":22,"react":178}],7:[function(require,module,exports){ /** * It changes the extension icon color. * Supported values are: 'red', 'white' and ''. - * - * @param string color = '' - * @return null */ 'use strict'; @@ -671,14 +735,17 @@ function changeExtensionIcon() { module.exports = exports['default']; },{}],8:[function(require,module,exports){ +/** + * Returns UNIX timestamp + */ "use strict"; Object.defineProperty(exports, "__esModule", { - value: true + value: true }); exports["default"] = function () { - return Math.round(new Date().getTime() / 1000); + return Math.round(new Date().getTime() / 1000); }; module.exports = exports["default"]; diff --git a/public/js/events.js b/public/js/events.js index fe722b6..b6cc437 100644 --- a/public/js/events.js +++ b/public/js/events.js @@ -1,7 +1,11 @@ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;oWell done! Options have been saved.'); + chrome.storage.sync.set({ + theme: theme, + blacklist: blacklist, + loggingType: loggingType + }, function () { + // Update status to let user know options were saved. + var status = (0, _jquery2['default'])('#status'); + status.html('Well done! Options have been saved.'); - status.fadeIn(1500, function () { - setTimeout(function () { - status.fadeOut(1500, function () { - status.html(''); + status.fadeIn(1500, function () { + setTimeout(function () { + status.fadeOut(1500, function () { + status.html(''); + }); + }, 750); }); - }, 750); }); - }); } // Restores select box and checkbox state using the preferences // stored in chrome.storage. function restore_options() { - // Use default value color = 'red' and likesColor = true. - chrome.storage.sync.get({ - theme: 'light', - blacklist: '', - loggingType: 'domain' - }, function (items) { - document.getElementById('theme').value = items.theme; - document.getElementById('blacklist').value = items.blacklist; - document.getElementById(items.loggingType + 'Type').checked = true; - }); + // Use default value color = 'red' and likesColor = true. + chrome.storage.sync.get({ + theme: 'light', + blacklist: '', + loggingType: 'domain' + }, function (items) { + document.getElementById('theme').value = items.theme; + document.getElementById('blacklist').value = items.blacklist; + document.getElementById(items.loggingType + 'Type').checked = true; + }); } document.addEventListener('DOMContentLoaded', restore_options); diff --git a/sc_1.png b/screenshots/sc_1.png similarity index 100% rename from sc_1.png rename to screenshots/sc_1.png diff --git a/sc_2-options.png b/screenshots/sc_2-options.png similarity index 100% rename from sc_2-options.png rename to screenshots/sc_2-options.png diff --git a/sc_3_closed.png b/screenshots/sc_3_closed.png similarity index 100% rename from sc_3_closed.png rename to screenshots/sc_3_closed.png diff --git a/sc_3_open.png b/screenshots/sc_3_open.png similarity index 100% rename from sc_3_open.png rename to screenshots/sc_3_open.png diff --git a/screenshots/sc_4-options.png b/screenshots/sc_4-options.png new file mode 100644 index 0000000000000000000000000000000000000000..b12273567574b9b8716dadaf8a536afb482e8852 GIT binary patch literal 19535 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 HQ$iB}HA3Jv literal 0 HcmV?d00001