Unified
Split
Showing
with
564 additions
and 43 deletions.
- +73 −0 h/browser/chrome/content/settings.css
- +31 −0 h/browser/chrome/content/settings.html
- +163 −0 h/browser/chrome/lib/extension-settings.js
- +6 −2 h/browser/chrome/lib/extension.js
- +152 −35 h/browser/chrome/lib/hypothesis-chrome-extension.js
- +23 −0 h/browser/chrome/lib/install.js
- +13 −0 h/browser/chrome/lib/message-types.js
- +28 −0 h/browser/chrome/lib/settings-ui.js
- +17 −1 h/browser/chrome/lib/sidebar-injector.js
- +15 −1 h/browser/chrome/lib/tab-state.js
- +35 −0 h/browser/chrome/lib/util.js
- +8 −4 h/browser/chrome/manifest.json.jinja2
| @@ -0,0 +1,73 @@ | ||
| @font-face { | ||
| font-family:'Source Sans Pro'; | ||
| font-style:normal; | ||
| font-weight:400; | ||
| src:local('Source Sans Pro'),local('SourceSansPro-Regular'),url(https://fonts.gstatic.com/s/sourcesanspro/v9/ODelI1aHBYDBqgeIAH2zlNzbP97U9sKh0jjxbPbfOKg.ttf) format('truetype') | ||
| } | ||
| * { | ||
| box-sizing: border-box; | ||
| } | ||
| body { | ||
| font-family: 'Source Sans Pro'; | ||
| background-color: #d3d3d3; | ||
| margin-top: 0px; | ||
| margin-bottom: 0px; | ||
| } | ||
| .content { | ||
| width: 90%; | ||
| height: 100%; | ||
| margin-left: auto; | ||
| margin-right: auto; | ||
| background-color: white; | ||
| padding-top: 20px; | ||
| padding-left: 20px; | ||
| padding-right: 20px; | ||
| border-left: 1px solid #aaa; | ||
| border-right: 1px solid #aaa; | ||
| } | ||
| .settings-header { | ||
| margin-bottom: 30px; | ||
| display: flex; | ||
| } | ||
| .settings-header__title { | ||
| margin-left: 7px; | ||
| font-size: 19px; | ||
| color: #888; | ||
| } | ||
| .setting-row { | ||
| display: flex; | ||
| align-items: start; | ||
| flex-direction: row; | ||
| } | ||
| .setting-label { | ||
| cursor: pointer; | ||
| display: inline-block; | ||
| margin-left: 10px; | ||
| font-size: 16px; | ||
| } | ||
| .setting-description { | ||
| color: #888; | ||
| } | ||
| /* TODO - Import this from header.css */ | ||
| .header-logo { | ||
| display:flex; | ||
| flex-direction:row; | ||
| font-size:19px; | ||
| font-weight:700; | ||
| color:#3A3A3A; | ||
| align-items:center; | ||
| letter-spacing:-.2 | ||
| } | ||
| .header-logo__dot { | ||
| color:#bd1c2b | ||
| } |
| @@ -0,0 +1,31 @@ | ||
| <head> | ||
| <title>Hypothesis Extension Settings</title> | ||
| <link rel="stylesheet" href="/content/settings.css"> | ||
| <meta charset="UTF-8"> | ||
| </head> | ||
| <body> | ||
| <div class="content"> | ||
| <div class="settings-header"> | ||
| <div class="header-logo" title="Hypothesis Logo"> | ||
| hypothes<span class="header-logo__dot">.</span>is | ||
| </div> | ||
| <div class="settings-header__title"> | Settings</div> | ||
| </div> | ||
| <div class="setting-row"> | ||
| <input type="checkbox" id="showAnnotationCounts"> | ||
| <label for="showAnnotationCounts" class="setting-label"> | ||
| Show whether pages I visit have been annotated | ||
| <p class="setting-description">When enabled the addresses of pages you visit will be sent to Hypothesis. | ||
| <a href="https://hypothes.is/privacy">More info…</a></p> | ||
| </body> | ||
| </div> | ||
| <div class="setting-row"> | ||
| <input type="checkbox" id="keepActiveOnPageChange"> | ||
| <label for="keepActiveOnPageChange" class="setting-label"> | ||
| Keep Hypothesis active when visiting a new page | ||
| <p class="setting-description">This requires Hypothesis to have access to read pages you visit.</p> | ||
| </label> | ||
| </div> | ||
| </div> | ||
| <script src="/lib/extension-bundle.js"></script> | ||
| </body> |
| @@ -0,0 +1,163 @@ | ||
| /** | ||
| * Provides functions to read extension-specific settings | ||
| * and display the settings dialog. | ||
| */ | ||
| var assign = require('core-js/modules/$.object-assign'); | ||
| var util = require('./util'); | ||
| /** | ||
| * A dictionary mapping extension settings to their current values. | ||
| * The settings must be initialized by calling init() | ||
| */ | ||
| var values = { | ||
| keepActiveOnPageChange: false, | ||
| showAnnotationCounts: false, | ||
| }; | ||
| var keys = Object.keys(values).reduce(function (keys, key) { | ||
| keys[key] = key; | ||
| return keys; | ||
| }, {}); | ||
| var requestFn = util.promisify(chrome.permissions.request); | ||
| var revokeFn = util.promisify(chrome.permissions.remove); | ||
| function permissionsForSettings(settings) { | ||
| var permissions = []; | ||
| var origins = []; | ||
| if (settings.keepActiveOnPageChange || settings.showAnnotationCounts) { | ||
| permissions.push('tabs', 'webNavigation'); | ||
| } | ||
| if (settings.keepActiveOnPageChange) { | ||
| origins.push('<all_urls>'); | ||
| } | ||
| return { | ||
| permissions: permissions, | ||
| origins: origins, | ||
| } | ||
| } | ||
| function requestPermissions(settings) { | ||
| console.log('requesting permissions', permissionsForSettings(settings)); | ||
| return requestFn(permissionsForSettings(settings)); | ||
| } | ||
| function removePermissions(oldSettings, newSettings) { | ||
| var oldPermissions = permissionsForSettings(oldSettings); | ||
| var newPermissions = permissionsForSettings(newSettings); | ||
| function removedItems(a, b) { | ||
| return a.filter(function (item) { | ||
| return b.indexOf(item) == -1; | ||
| }); | ||
| } | ||
| var removed = { | ||
| permissions: removedItems(oldPermissions.permissions, | ||
| newPermissions.permissions), | ||
| origins: removedItems(oldPermissions.origins, | ||
| newPermissions.origins), | ||
| }; | ||
| console.log('removing permissions', removed); | ||
| return revokeFn(removed); | ||
| } | ||
| function notifySettingChanged(setting, value) { | ||
| values[setting] = value; | ||
| chrome.runtime.sendMessage({ | ||
| type: 'SETTING_CHANGED', | ||
| setting: setting, | ||
| value: value, | ||
| }); | ||
| } | ||
| /** | ||
| * Returns a Promise for an object mapping settings keys | ||
| * to their current values. | ||
| */ | ||
| function getAll() { | ||
| return new Promise(function (resolve, reject) { | ||
| chrome.runtime.sendMessage({ | ||
| type: 'GET_SETTINGS' | ||
| }, function (response) { | ||
| resolve(response.values); | ||
| }); | ||
| }); | ||
| } | ||
| function installChangeHandler() { | ||
| chrome.runtime.onMessage.addListener(function (request, _, sendResponse) { | ||
| if (request.type === 'SETTING_CHANGE_REQUEST') { | ||
| var newSettings = assign({}, values); | ||
| newSettings[request.setting] = request.value; | ||
| // request any new permissions needed for this setting | ||
| requestPermissions(newSettings).then(function (granted) { | ||
| window.localStorage.setItem("settings", JSON.stringify(values)); | ||
| notifySettingChanged(request.setting, request.value); | ||
| }).catch(function (err) { | ||
| console.error('Error requesting permissions for new settings', err); | ||
| }); | ||
| // remove any permissions that are no longer required | ||
| removePermissions(values, newSettings).catch(function (err) { | ||
| console.error('Error revoking permissions', err); | ||
| }); | ||
| } else if (request.type === 'GET_SETTINGS') { | ||
| sendResponse({values: values}); | ||
| } | ||
| }); | ||
| } | ||
| /** Show the extension settings dialog in a popup window. | ||
| * | ||
| * Returns a promise which resolves once the popup window has | ||
| * been dismissed. | ||
| */ | ||
| function showSettingsDialog() { | ||
| var settingsWindowId; | ||
| chrome.windows.create({ | ||
| url: '/content/settings.html', | ||
| type: 'popup', | ||
| width: 600, | ||
| height: 400, | ||
| }, function (window) { | ||
| settingsWindowId = window.id; | ||
| }); | ||
| return new Promise(function (resolve, reject) { | ||
| var closedListener = function (windowId) { | ||
| if (windowId === settingsWindowId) { | ||
| chrome.windows.onRemoved.removeListener(closedListener); | ||
| resolve(); | ||
| } | ||
| }; | ||
| chrome.windows.onRemoved.addListener(closedListener); | ||
| }); | ||
| } | ||
| function init() { | ||
| installChangeHandler(); | ||
| try { | ||
| // TODO - Verify here that the saved settings are consistent with | ||
| // the extension's current permissions and reset any settings | ||
| // for which we do not have sufficient permissions | ||
| var savedSettings = JSON.parse(window.localStorage.getItem("settings")); | ||
| assign(values, savedSettings); | ||
| } catch (err) { | ||
| console.error('Error loading settings', err); | ||
| } | ||
| } | ||
| module.exports = { | ||
| init: init, | ||
| showSettingsDialog: showSettingsDialog, | ||
| values: values, | ||
| getAll: getAll, | ||
| }; |
| @@ -1,2 +1,6 @@ | ||
| require('./hypothesis-chrome-extension'); | ||
| require('./install'); | ||
| if (location.pathname === '/content/settings.html') { | ||
| require('./settings-ui'); | ||
| } else { | ||
| require('./hypothesis-chrome-extension'); | ||
| require('./install'); | ||
| } |
Oops, something went wrong.