adding options UI (fixes #22, #24)

This commit is contained in:
Jeremy Schomery 2017-02-14 19:05:53 +03:30
parent 902659a889
commit d6ec816ea9
10 changed files with 734 additions and 492 deletions

View File

@ -421,5 +421,36 @@
}, },
"retrieveBckp": { "retrieveBckp": {
"message": "Import Styles" "message": "Import Styles"
},
// options page
"optionsBadgeNormal": {
"message": "Badge background color"
},
"optionsBadgeDisabled": {
"message": "Badge background color (when disabled)"
},
"optionsUpdateInterval": {
"message": "Automatically check for user-style updates (in hours)"
},
"optionsUpdateIntervalNote": {
"message": "To disable the automatic user-style update checks, set interval to zero"
},
"optionsCustomize": {
"message": "UI Customizations"
},
"optionsActions": {
"message": "Actions"
},
"optionsOpenManager": {
"message": "Open styles manager"
},
"optionsCheckUpdate": {
"message": "Check for updates"
},
"optionsOpen": {
"message": "Open"
},
"optionsCheck": {
"message": "Check"
} }
} }

View File

@ -120,7 +120,8 @@ runTryCatch(function() {
chrome.contextMenus.onClicked.addListener(function(info, tab) { chrome.contextMenus.onClicked.addListener(function(info, tab) {
if (info.menuItemId == "disableAll") { if (info.menuItemId == "disableAll") {
disableAllStylesToggle(info.checked); disableAllStylesToggle(info.checked);
} else { }
else {
prefs.set(info.menuItemId, info.checked); prefs.set(info.menuItemId, info.checked);
} }
}); });

View File

@ -238,6 +238,10 @@ function checkUpdateAll() {
} }
}); });
}); });
// notify the automatic updater to reset the next automatic update accordingly
chrome.runtime.sendMessage({
method: 'resetInterval'
});
} }
function checkUpdate(element, callback) { function checkUpdate(element, callback) {

View File

@ -46,7 +46,6 @@
"js": ["install.js"] "js": ["install.js"]
} }
], ],
"options_page": "manage.html",
"browser_action": { "browser_action": {
"default_icon": { "default_icon": {
"16": "16w.png", "16": "16w.png",
@ -57,5 +56,9 @@
"default_title": "Stylus", "default_title": "Stylus",
"default_popup": "popup.html" "default_popup": "popup.html"
}, },
"default_locale": "en" "default_locale": "en",
"options_ui": {
"page": "options/index.html",
"chrome_style": true
}
} }

View File

@ -60,7 +60,9 @@ function updateIcon(tab, styles) {
if (!chrome.runtime.lastError) { if (!chrome.runtime.lastError) {
var t = prefs.get("show-badge") && styles.length ? ("" + styles.length) : ""; var t = prefs.get("show-badge") && styles.length ? ("" + styles.length) : "";
chrome.browserAction.setBadgeText({text: t, tabId: tab.id}); chrome.browserAction.setBadgeText({text: t, tabId: tab.id});
chrome.browserAction.setBadgeBackgroundColor({color: disableAll ? "darkred" : '#006666'}); chrome.browserAction.setBadgeBackgroundColor({
color: prefs.get(disableAll ? 'badgeDisabled' : 'badgeNormal')
});
} }
}); });
//console.log("Tab " + tab.id + " (" + tab.url + ") badge text set to '" + t + "'."); //console.log("Tab " + tab.id + " (" + tab.url + ") badge text set to '" + t + "'.");

22
options/index.css Normal file
View File

@ -0,0 +1,22 @@
body {
margin: 10px;
font-family: "Helvetica Neue",Helvetica,sans-serif;
font-size: 12px;
}
table {
width: 100%;
}
td:last-child {
text-align: right;
}
input[type=number],
button {
width: 80px;
}
.notes {
font-size: 90%;
color: #999;
}

55
options/index.html Normal file
View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>Stylus Options</title>
<link rel="stylesheet" href="index.css">
<script src="../localization.js"></script>
</head>
<body>
<h1 i18n-text="optionsCustomize"></h1>
<table>
<tbody>
<tr>
<td i18n-text="optionsBadgeNormal"></td>
<td><input type="color" id="badgeNormal"></td>
</tr>
<tr>
<td i18n-text="optionsBadgeDisabled"></td>
<td><input type="color" id="badgeDisabled"></td>
</tr>
<tr>
<td i18n-text="optionsUpdateInterval"><sup>1</sup></td>
<td><input type="number" min="0" id="updateInterval"></td>
</tr>
</tbody>
</table>
<div>
<button id="save">Save</button>
<span id="status"></span>
</div>
<h1 i18n-text="optionsActions"></h1>
<table>
<tbody>
<tr>
<td i18n-text="optionsOpenManager"></td>
<td><button type="button" data-cmd="open-manage" i18n-text="optionsOpen"></button></td>
</tr>
<tr>
<td i18n-text="optionsCheckUpdate"></td>
<td>
<span id="update-counter"></span>
<button type="button" data-cmd="check-updates" i18n-text="optionsCheck"></button>
</td>
</tr>
</tbody>
</table>
<div class="notes">
<hr>
1: <span i18n-text="optionsUpdateIntervalNote"></span>
</div>
<script src="index.js"></script>
</body>
</html>

88
options/index.js Normal file
View File

@ -0,0 +1,88 @@
'use strict';
function restore () {
chrome.runtime.getBackgroundPage(bg => {
document.getElementById('badgeDisabled').value = bg.prefs.get('badgeDisabled');
document.getElementById('badgeNormal').value = bg.prefs.get('badgeNormal');
document.getElementById('updateInterval').value = bg.prefs.get('updateInterval');
});
}
function save () {
chrome.runtime.getBackgroundPage(bg => {
bg.prefs.set('badgeDisabled', document.getElementById('badgeDisabled').value);
bg.prefs.set('badgeNormal', document.getElementById('badgeNormal').value);
bg.prefs.set(
'updateInterval',
Math.max(0, +document.getElementById('updateInterval').value)
);
// display notification
let status = document.getElementById('status');
status.textContent = 'Options saved.';
setTimeout(() => status.textContent = '', 750);
});
}
document.addEventListener('DOMContentLoaded', restore);
document.getElementById('save').addEventListener('click', save);
// actions
document.addEventListener('click', e => {
let cmd = e.target.dataset.cmd;
let total = 0, updated = 0;
function update () {
document.getElementById('update-counter').textContent = `${updated}/${total}`;
}
function done (target) {
target.disabled = false;
window.setTimeout(() => {
document.getElementById('update-counter').textContent = '';
}, 750);
}
if (cmd === 'open-manage') {
chrome.tabs.query({
url: chrome.runtime.getURL('manage.html')
}, tabs => {
if (tabs.length) {
chrome.tabs.update(tabs[0].id, {
active: true,
}, () => {
chrome.windows.update(tabs[0].windowId, {
focused: true
});
});
}
else {
chrome.tabs.create({
url: chrome.runtime.getURL('manage.html')
});
}
});
}
else if (cmd === 'check-updates') {
e.target.disabled = true;
chrome.runtime.getBackgroundPage(bg => {
bg.update.perform((cmd, value) => {
if (cmd === 'count') {
total = value;
if (!total) {
done(e.target);
}
}
else if (cmd === 'single-updated' || cmd === 'single-skipped') {
updated += 1;
if (total && updated === total) {
done(e.target);
}
}
update();
});
});
// notify the automatic updater to reset the next automatic update accordingly
chrome.runtime.sendMessage({
method: 'resetInterval'
});
}
});

View File

@ -348,6 +348,11 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
}, },
"editor.lintDelay": 500, // lint gutter marker update delay, ms "editor.lintDelay": 500, // lint gutter marker update delay, ms
"editor.lintReportDelay": 4500, // lint report update delay, ms "editor.lintReportDelay": 4500, // lint report update delay, ms
"badgeDisabled": "darkred", // badge background color when disabled
"badgeNormal": "#006666", // badge background color
"updateInterval": 12 // user-style automatic update interval, hour
}; };
var values = deepCopy(defaults); var values = deepCopy(defaults);

View File

@ -1,4 +1,4 @@
/* globals getStyles, saveStyle */ /* globals getStyles, saveStyle, prefs */
'use strict'; 'use strict';
var update = { var update = {
@ -11,7 +11,7 @@ var update = {
req.onerror = req.ontimeout = () => callback(); req.onerror = req.ontimeout = () => callback();
req.send(data); req.send(data);
}, },
md5Check: (style, callback) => { md5Check: (style, callback, skipped) => {
let req = new XMLHttpRequest(); let req = new XMLHttpRequest();
req.open('GET', style.md5Url, true); req.open('GET', style.md5Url, true);
req.onload = () => { req.onload = () => {
@ -20,16 +20,16 @@ var update = {
callback(style); callback(style);
} }
else { else {
console.log(`"${style.name}" style is up-to-date`); skipped(`"${style.name}" style is up-to-date`);
} }
}; };
req.onerror = req.ontimeout = () => console.log('Error validating MD5 checksum'); req.onerror = req.ontimeout = () => skipped('Error validating MD5 checksum');
req.send(); req.send();
}, },
list: (callback) => { list: (callback) => {
getStyles({}, (styles) => callback(styles.filter(style => style.updateUrl))); getStyles({}, (styles) => callback(styles.filter(style => style.updateUrl)));
}, },
perform: () => { perform: (observe = function () {}) => {
// from install.js // from install.js
function arraysAreEqual (a, b) { function arraysAreEqual (a, b) {
// treat empty array and undefined as equivalent // treat empty array and undefined as equivalent
@ -57,6 +57,7 @@ var update = {
} }
update.list(styles => { update.list(styles => {
observe('count', styles.length);
styles.forEach(style => update.md5Check(style, style => update.fetch(style.updateUrl, response => { styles.forEach(style => update.md5Check(style, style => update.fetch(style.updateUrl, response => {
if (response) { if (response) {
let json = JSON.parse(response); let json = JSON.parse(response);
@ -65,20 +66,50 @@ var update = {
if (json.sections.every((section) => { if (json.sections.every((section) => {
return style.sections.some(installedSection => sectionsAreEqual(section, installedSection)); return style.sections.some(installedSection => sectionsAreEqual(section, installedSection));
})) { })) {
return console.log('everything is the same'); return observe('single-skipped', '2'); // everything is the same
} }
json.method = 'saveStyle'; json.method = 'saveStyle';
json.id = style.id; json.id = style.id;
saveStyle(json, function () { saveStyle(json, function () {
console.log(`"${style.name}" style is updated`); observe('single-updated', style.name);
}); });
} }
else { else {
console.log('style sections mismatch'); return observe('single-skipped', '3'); // style sections mismatch
} }
} }
}))); }), () => observe('single-skipped', '1')));
}); });
} }
}; };
// automatically update all user-styles if "updateInterval" pref is set
window.setTimeout(function () {
let id;
function run () {
update.perform(/*(cmd, value) => console.log(cmd, value)*/);
reset();
}
function reset () {
window.clearTimeout(id);
let interval = prefs.get('updateInterval');
// if interval === 0 => automatic update is disabled
if (interval) {
/* console.log('next update', interval); */
id = window.setTimeout(run, interval * 60 * 60 * 1000);
}
}
if (prefs.get('updateInterval')) {
run();
}
chrome.runtime.onMessage.addListener(request => {
// when user has changed the predefined time interval in the settings page
if (request.method === 'prefChanged' && request.prefName === 'updateInterval') {
reset();
}
// when user just manually checked for updates
if (request.method === 'resetInterval') {
reset();
}
});
}, 10000);