diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..2b7bafa --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-react"] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..4b8b3d2 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +mocoapp-browser-extension +========================= + +Documentation +------------- + +* https://checklyhq.com/blog/2018/08/creating-a-chrome-extension-in-2018-the-good-the-bad-and-the-meh/ +* https://developer.chrome.com/extensions +* https://developer.chrome.com/extensions/api_index + +Development +----------- + +* run `yarn` +* run `yarn webpack --watch` +* load unpacked extension in Chrome (ignore node_modules errors) +* reload Chrome extension after change + +Release +------- + +* bump version in `manifest.json` +* bump version in `package.json` +* run `yarn release` +* zip contents in `/release` +* upload Chrome extension diff --git a/background.js b/background.js new file mode 100644 index 0000000..449bb9e --- /dev/null +++ b/background.js @@ -0,0 +1,162 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./src/js/background.js"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./src/js/background.js": +/*!******************************!*\ + !*** ./src/js/background.js ***! + \******************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _services_DomainCheck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./services/DomainCheck */ "./src/js/services/DomainCheck.js"); + +chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { + // inject files only after the page is fully loaded + if (changeInfo.status != 'complete') return; // inject files only for supported websites + + var domainCheck = new _services_DomainCheck__WEBPACK_IMPORTED_MODULE_0__["default"](tab.url); + if (!domainCheck.hasMatch) return; // inject css + js + + chrome.tabs.insertCSS(tabId, { + file: "/styles.css" + }, function () { + chrome.tabs.executeScript(tabId, { + code: "const div = document.createElement('div'); div.setAttribute('id', 'moco'); document.body.appendChild(div)" + }, function () { + chrome.tabs.executeScript(tabId, { + file: "/popup.js" + }, function () { + console.log("inejected /popup.js"); + }); + }); + }); +}); + +/***/ }), + +/***/ "./src/js/services/DomainCheck.js": +/*!****************************************!*\ + !*** ./src/js/services/DomainCheck.js ***! + \****************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var DomainCheck = +/*#__PURE__*/ +function () { + function DomainCheck(url) { + _classCallCheck(this, DomainCheck); + + this.url = url; + } + + _createClass(DomainCheck, [{ + key: "hasMatch", + get: function get() { + return this.url.match(/github/); + } + }]); + + return DomainCheck; +}(); + +/* harmony default export */ __webpack_exports__["default"] = (DomainCheck); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/bundle.js b/bundle.js deleted file mode 100644 index 8b36d6c..0000000 --- a/bundle.js +++ /dev/null @@ -1,198 +0,0 @@ -/* -------------------------------------------------- */ -/* Start of Webpack Chrome Hot Extension Middleware */ -/* ================================================== */ -/* This will be converted into a lodash templ., any */ -/* external argument must be provided using it */ -/* -------------------------------------------------- */ -(function (chrome, window) { - var signals = JSON.parse('{"SIGN_CHANGE":"SIGN_CHANGE","SIGN_RELOAD":"SIGN_RELOAD","SIGN_RELOADED":"SIGN_RELOADED","SIGN_LOG":"SIGN_LOG","SIGN_CONNECT":"SIGN_CONNECT"}'); - var config = JSON.parse('{"RECONNECT_INTERVAL":2000,"SOCKET_ERR_CODE_REF":"https://tools.ietf.org/html/rfc6455#section-7.4.1"}'); - var reloadPage = "true" === "true"; - var wsHost = "ws://localhost:9090"; - var SIGN_CHANGE = signals.SIGN_CHANGE, - SIGN_RELOAD = signals.SIGN_RELOAD, - SIGN_RELOADED = signals.SIGN_RELOADED, - SIGN_LOG = signals.SIGN_LOG, - SIGN_CONNECT = signals.SIGN_CONNECT; - var RECONNECT_INTERVAL = config.RECONNECT_INTERVAL, - SOCKET_ERR_CODE_REF = config.SOCKET_ERR_CODE_REF; - var runtime = chrome.runtime, - tabs = chrome.tabs; - - var manifest = runtime.getManifest(); - var formatter = function formatter(msg) { - return '[ WCER: ' + msg + ' ]'; - }; - var logger = function logger(msg) { - var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "info"; - return console[level](formatter(msg)); - }; - var timeFormatter = function timeFormatter(date) { - return date.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1"); - }; - function contentScriptWorker() { - runtime.sendMessage({ type: SIGN_CONNECT }, function (msg) { - return console.info(msg); - }); - runtime.onMessage.addListener(function (_ref) { - var type = _ref.type, - payload = _ref.payload; - - switch (type) { - case SIGN_RELOAD: - logger("Detected Changes. Reloading ..."); - reloadPage && window.location.reload(); - break; - case SIGN_LOG: - console.info(payload); - break; - } - }); - } - function backgroundWorker(socket) { - runtime.onMessage.addListener(function (action, sender, sendResponse) { - if (action.type === SIGN_CONNECT) { - sendResponse(formatter("Connected to Chrome Extension Hot Reloader")); - } - }); - socket.addEventListener("message", function (_ref2) { - var data = _ref2.data; - - var _JSON$parse = JSON.parse(data), - type = _JSON$parse.type, - payload = _JSON$parse.payload; - - if (type === SIGN_CHANGE) { - tabs.query({ status: "complete" }, function (loadedTabs) { - loadedTabs.forEach(function (tab) { - return tabs.sendMessage(tab.id, { type: SIGN_RELOAD }); - }); - socket.send(JSON.stringify({ - type: SIGN_RELOADED, - payload: formatter(timeFormatter(new Date()) + ' - ' + manifest.name + ' successfully reloaded') - })); - runtime.reload(); - }); - } else { - runtime.sendMessage({ type: type, payload: payload }); - } - }); - socket.addEventListener("close", function (_ref3) { - var code = _ref3.code; - - logger('Socket connection closed. Code ' + code + '. See more in ' + SOCKET_ERR_CODE_REF, "warn"); - var intId = setInterval(function () { - logger("WEPR Attempting to reconnect ..."); - var ws = new WebSocket(wsHost); - ws.addEventListener("open", function () { - clearInterval(intId); - logger("Reconnected. Reloading plugin"); - runtime.reload(); - }); - }, RECONNECT_INTERVAL); - }); - } - runtime.reload ? backgroundWorker(new WebSocket(wsHost)) : contentScriptWorker(); -})(chrome, window); -/* ----------------------------------------------- */ -/* End of Webpack Chrome Hot Extension Middleware */ -/* ----------------------------------------------- *//******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./src/js/index.js"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./src/js/index.js": -/*!*************************!*\ - !*** ./src/js/index.js ***! - \*************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("alert(\"ok\")\n\n\n//# sourceURL=webpack:///./src/js/index.js?"); - -/***/ }) - -/******/ }); \ No newline at end of file diff --git a/manifest.json b/manifest.json index 63b5f9c..f059e49 100644 --- a/manifest.json +++ b/manifest.json @@ -16,11 +16,19 @@ "https://*.mocoapp.com/*", "background", "storage", - "tabs" + "tabs", + "https://*/" ], "optional_permissions": [ "*://*/" ], + "background": { + "scripts": [ + // "node_modules/jquery/dist/jquery.min.js", + // "node_modules/select2/select2.js", + "background.js" + ] + }, "browser_action": { "default_icon": "src/images/logo.png", "default_title": "MOCO Time Tracking", diff --git a/options.html b/options.html index c6ce10f..6df3f1d 100644 --- a/options.html +++ b/options.html @@ -5,6 +5,7 @@
- + A dedicated page for customising settings of your extension. This page should persist any settings to the store, to be fetched again by other parts of your plugin. +