Merge pull request #94 from tophf/localization-embed

Localization: embed in html, autorun, apply earlier
This commit is contained in:
Jason Barnabe 2015-05-04 12:33:41 -05:00
commit 2e02b73c77
8 changed files with 166 additions and 75 deletions

View File

@ -205,6 +205,14 @@
"message": "Show number of styles active for the current site on the toolbar button", "message": "Show number of styles active for the current site on the toolbar button",
"description": "Label for the checkbox controlling toolbar badge text." "description": "Label for the checkbox controlling toolbar badge text."
}, },
"search": {
"message": "Search",
"description": "Label before the search input field in the editor shown on Ctrl-F"
},
"searchRegexp": {
"message": "Use /re/ syntax for regexp search",
"description": "Label after the search input field in the editor shown on Ctrl-F"
},
"sectionAdd": { "sectionAdd": {
"message": "Add another section", "message": "Add another section",
"description": "Label for the button to add a section" "description": "Label for the button to add a section"

View File

@ -285,47 +285,47 @@
</head> </head>
<body id="stylish-edit"> <body id="stylish-edit">
<div id="header"> <div id="header">
<h1 id="heading"></h1> <h1 id="heading">&nbsp;</h1> <!-- nbsp allocates the actual height which prevents page shift -->
<section id="basic-info"> <section id="basic-info">
<div id="basic-info-name"> <div id="basic-info-name">
<input id="name" class="style-contributor"> <input id="name" class="style-contributor" i18n-placeholder="styleMissingName">
</div> </div>
<div id="basic-info-enabled"> <div id="basic-info-enabled">
<input type="checkbox" id="enabled" class="style-contributor"> <input type="checkbox" id="enabled" class="style-contributor">
<label for="enabled" id="enabled-label"></label> <label for="enabled" id="enabled-label" i18n-text="styleEnabledLabel"></label>
</div> </div>
</section> </section>
<section id="actions"> <section id="actions">
<div><button id="to-mozilla"></button><img id="to-mozilla-help" src="help.png"></div> <div><button id="to-mozilla" i18n-text="styleToMozillaFormat"></button><img id="to-mozilla-help" src="help.png"></div>
<div><a href="manage.html"><button id="cancel-button"></button></a></div> <div><a href="manage.html"><button id="cancel-button" i18n-text="styleCancelEditLabel"></button></a></div>
<div><button id="save-button" title="Ctrl-S"></button></div> <div><button id="save-button" title="Ctrl-S" i18n-text="styleSaveLabel"></button></div>
</section> </section>
<section id="options"> <section id="options">
<h2 id="options-heading"></h2> <h2 id="options-heading" i18n-text="optionsHeading"></h2>
<div class="option"> <div class="option">
<input data-option="lineWrapping" id="editor.lineWrapping" type="checkbox"> <input data-option="lineWrapping" id="editor.lineWrapping" type="checkbox">
<label id="lineWrapping-label" for="editor.lineWrapping"></label> <label id="lineWrapping-label" for="editor.lineWrapping" i18n-text="cm_lineWrapping"></label>
</div> </div>
<div class="option"> <div class="option">
<input data-option="smartIndent" id="editor.smartIndent" type="checkbox"> <input data-option="smartIndent" id="editor.smartIndent" type="checkbox">
<label id="smartIndent-label" for="editor.smartIndent"></label> <label id="smartIndent-label" for="editor.smartIndent" i18n-text="cm_smartIndent"></label>
</div> </div>
<div class="option"> <div class="option">
<input data-option="indentWithTabs" id="editor.indentWithTabs" type="checkbox"> <input data-option="indentWithTabs" id="editor.indentWithTabs" type="checkbox">
<label id="indentWithTabs-label" for="editor.indentWithTabs"></label> <label id="indentWithTabs-label" for="editor.indentWithTabs" i18n-text="cm_indentWithTabs"></label>
</div> </div>
<div class="option aligned"> <div class="option aligned">
<label id="tabSize-label" for="editor.tabSize"></label> <label id="tabSize-label" for="editor.tabSize" i18n-text="cm_tabSize"></label>
<input data-option="tabSize" id="editor.tabSize" type="number" min="0"> <input data-option="tabSize" id="editor.tabSize" type="number" min="0">
</div> </div>
<div class="option aligned"> <div class="option aligned">
<label id="keyMap-label" for="editor.keyMap"></label> <label id="keyMap-label" for="editor.keyMap" i18n-text="cm_keyMap"></label>
<select data-option="keyMap" id="editor.keyMap"></select> <select data-option="keyMap" id="editor.keyMap"></select>
</div> </div>
</section> </section>
</div> </div>
<section id="sections"> <section id="sections">
<h2><span id="sections-heading"></span> <img id="sections-help" src="help.png"></h2> <h2><span id="sections-heading" i18n-text="styleSectionsTitle"></span><img id="sections-help" src="help.png" i18n-alt="helpAlt"></h2>
</section> </section>
<script src="edit.js"></script> <script src="edit.js"></script>
</body> </body>

56
edit.js
View File

@ -11,16 +11,44 @@ var propertyToCss = {urls: "url", urlPrefixes: "url-prefix", domains: "domain",
var CssToProperty = {"url": "urls", "url-prefix": "urlPrefixes", "domain": "domains", "regexp": "regexps"}; var CssToProperty = {"url": "urls", "url-prefix": "urlPrefixes", "domain": "domains", "regexp": "regexps"};
// templates // templates
var appliesToTemplate = document.createElement("li"); var appliesToTemplate = tHTML('\
appliesToTemplate.innerHTML = '<select name="applies-type" class="applies-type style-contributor"><option value="url">' + t("appliesUrlOption") + '</option><option value="url-prefix">' + t("appliesUrlPrefixOption") + '</option><option value="domain">' + t("appliesDomainOption") + '</option><option value="regexp">' + t("appliesRegexpOption") + '</option></select><input name="applies-value" class="applies-value style-contributor"><button class="remove-applies-to">' + t("appliesRemove") + '</button><button class="add-applies-to">' + t("appliesAdd") + '</button>'; <li>\
<select name="applies-type" class="applies-type style-contributor">\
<option value="url" i18n-text="appliesUrlOption"></option>\
<option value="url-prefix" i18n-text="appliesUrlPrefixOption"></option>\
<option value="domain" i18n-text="appliesDomainOption"></option>\
<option value="regexp" i18n-text="appliesRegexpOption"></option>\
</select>\
<input name="applies-value" class="applies-value style-contributor">\
<button class="remove-applies-to" i18n-text="appliesRemove"></button>\
<button class="add-applies-to" i18n-text="appliesAdd"></button>\
</li>\
');
var appliesToEverythingTemplate = document.createElement("li"); var appliesToEverythingTemplate = tHTML('\
appliesToEverythingTemplate.className = "applies-to-everything"; <li class="applies-to-everything" i18n-html="appliesToEverything")>\
appliesToEverythingTemplate.innerHTML = t("appliesToEverything") + ' <button class="add-applies-to">' + t("appliesSpecify") + '</button>'; <button class="add-applies-to" i18n-text="appliesSpecify"></button>\
</li>\
');
var sectionTemplate = document.createElement("div"); var sectionTemplate = tHTML('\
sectionTemplate.innerHTML = '<label>' + t('sectionCode') + '</label><textarea class="code"></textarea><br><div class="applies-to"><label>' + t("appliesLabel") + ' <img class="applies-to-help" src="help.png" alt="' + t('helpAlt') + '"></label><ul class="applies-to-list"></ul></div><button class="remove-section">' + t('sectionRemove') + '</button><button class="add-section">' + t('sectionAdd') + '</button>'; <div>\
<label i18n-text="sectionCode"></label>\
<textarea class="code"></textarea>\
<br>\
<div class="applies-to">\
<label i18n-text="appliesLabel">\
&nbsp;<img class="applies-to-help" src="help.png" i18n-alt="helpAlt">\
</label>\
<ul class="applies-to-list"></ul>\
</div>\
<button class="remove-section" i18n-text="sectionRemove"></button>\
<button class="add-section" i18n-text="sectionAdd"></button>\
</div>\
');
var findTemplate = t("search") + ': <input type="text" style="width: 10em" class="CodeMirror-search-field"/>&nbsp;' +
'<span style="color: #888" class="CodeMirror-search-hint">(' + t("searchRegexp") + ')</span>';
// make querySelectorAll enumeration code readable // make querySelectorAll enumeration code readable
["forEach", "some", "indexOf"].forEach(function(method) { ["forEach", "some", "indexOf"].forEach(function(method) {
@ -153,7 +181,6 @@ function initCodeMirror() {
controlOptions = ["smartIndent", "indentWithTabs", "tabSize", "keyMap", "lineWrapping"]; controlOptions = ["smartIndent", "indentWithTabs", "tabSize", "keyMap", "lineWrapping"];
controlOptions.forEach(function(option) { controlOptions.forEach(function(option) {
controlPrefs["editor." + option] = CM.defaults[option]; controlPrefs["editor." + option] = CM.defaults[option];
tE(option + "-label", "cm_" + option);
}); });
loadPrefs(controlPrefs); loadPrefs(controlPrefs);
@ -417,7 +444,7 @@ function setupGlobalSearch() {
function find(activeCM) { function find(activeCM) {
var originalOpenDialog = activeCM.openDialog; var originalOpenDialog = activeCM.openDialog;
activeCM.openDialog = function(template, callback, options) { activeCM.openDialog = function(template, callback, options) {
originalOpenDialog.call(activeCM, template, function(query) { originalOpenDialog.call(activeCM, findTemplate, function(query) {
activeCM.openDialog = originalOpenDialog; activeCM.openDialog = originalOpenDialog;
callback(query); callback(query);
var state = activeCM.state.search; var state = activeCM.state.search;
@ -554,7 +581,6 @@ function nextPrevBuffer(cm, direction) {
window.addEventListener("load", init, false); window.addEventListener("load", init, false);
function init() { function init() {
tE("sections-help", "helpAlt", "alt");
var params = getParams(); var params = getParams();
if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses
// This is an add // This is an add
@ -582,7 +608,7 @@ function init() {
function initWithStyle(style) { function initWithStyle(style) {
document.getElementById("name").value = style.name; document.getElementById("name").value = style.name;
document.getElementById("enabled").checked = style.enabled == "true"; document.getElementById("enabled").checked = style.enabled == "true";
document.getElementById("heading").innerHTML = t("editStyleHeading"); tE("heading", "editStyleHeading", null, false);
// if this was done in response to an update, we need to clear existing sections // if this was done in response to an update, we need to clear existing sections
document.querySelectorAll("#sections > div").forEach(function(div) { document.querySelectorAll("#sections > div").forEach(function(div) {
div.parentNode.removeChild(div); div.parentNode.removeChild(div);
@ -781,14 +807,6 @@ chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
} }
}); });
tE("name", "styleMissingName", "placeholder");
tE("enabled-label", "styleEnabledLabel");
tE("to-mozilla", "styleToMozillaFormat");
tE("save-button", "styleSaveLabel");
tE("cancel-button", "styleCancelEditLabel");
tE("sections-heading", "styleSectionsTitle");
tE("options-heading", "optionsHeading");
document.getElementById("to-mozilla").addEventListener("click", showMozillaFormat, false); document.getElementById("to-mozilla").addEventListener("click", showMozillaFormat, false);
document.getElementById("to-mozilla-help").addEventListener("click", showToMozillaHelp, false); document.getElementById("to-mozilla-help").addEventListener("click", showToMozillaHelp, false);
document.getElementById("save-button").addEventListener("click", save, false); document.getElementById("save-button").addEventListener("click", save, false);

View File

@ -1,3 +1,5 @@
tDocLoader();
function t(key, params) { function t(key, params) {
var s = chrome.i18n.getMessage(key, params) var s = chrome.i18n.getMessage(key, params)
if (s == "") { if (s == "") {
@ -17,3 +19,55 @@ function tE(id, key, attr, esc) {
document.getElementById(id).innerHTML = t(key); document.getElementById(id).innerHTML = t(key);
} }
} }
function tHTML(html) {
var node = document.createElement("div");
node.innerHTML = html.replace(/>\s+</g, '><'); // spaces are removed; use &nbsp; for an explicit space
tNodeList(node.querySelectorAll("*"));
var child = node.removeChild(node.firstElementChild);
node.remove();
return child;
}
function tNodeList(nodes) {
for (var n = 0; n < nodes.length; n++) {
var node = nodes[n];
if (node.nodeType != 1) { // not an ELEMENT_NODE
continue;
}
for (var a = 0; a < node.attributes.length; a++) {
var name = node.attributes[a].nodeName;
if (name.indexOf("i18n-") != 0) {
continue;
}
name = name.substr(5); // "i18n-".length
var value = t(node.attributes[a].nodeValue);
switch (name) {
case "text":
node.insertBefore(document.createTextNode(value), node.firstChild);
break;
case "html":
node.insertAdjacentHTML("afterbegin", value);
break;
default:
node.setAttribute(name, value);
}
}
}
}
function tDocLoader() {
// localize HEAD
tNodeList(document.querySelectorAll("*"));
// localize BODY
var observer = new MutationObserver(function(mutations) {
for (var m = 0; m < mutations.length; m++) {
tNodeList(mutations[m].addedNodes);
}
});
observer.observe(document, {subtree: true, childList: true});
document.addEventListener("DOMContentLoaded", function() {
observer.disconnect();
});
}

View File

@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title></title> <title i18n-text="manageTitle"></title>
<style> <style>
body { body {
margin: 0; margin: 0;
@ -127,25 +127,24 @@
<script src="apply.js"></script> <script src="apply.js"></script>
</head> </head>
<body id="stylish-manage"> <body id="stylish-manage">
<div id="header"> <div id="header">
<h1 id="manage-heading"></h1> <h1 id="manage-heading" i18n-text="manageHeading"></h1>
<p id="manage-text"></p> <p id="manage-text" i18n-html="manageText"></p>
<fieldset> <fieldset>
<legend id="filters"></legend> <legend id="filters" i18n-text="manageFilters"></legend>
<div><input id="manage.onlyEnabled" type="checkbox"><label id="manage.onlyEnabled-label" for="manage.onlyEnabled"></label></div> <div><input id="manage.onlyEnabled" type="checkbox"><label id="manage.onlyEnabled-label" for="manage.onlyEnabled" i18n-text="manageOnlyEnabled"></label></div>
<div><input id="manage.onlyEdited" type="checkbox"><label id="manage.onlyEdited-label" for="manage.onlyEdited"></label></div> <div><input id="manage.onlyEdited" type="checkbox"><label id="manage.onlyEdited-label" for="manage.onlyEdited" i18n-text="manageOnlyEdited"></label></div>
</fieldset> </fieldset>
<p><button id="check-all-updates"></button></p> <p><button id="check-all-updates" i18n-text="checkAllUpdates"></button></p>
<p> <p>
<button id="apply-all-updates" class="hidden"></button> <button id="apply-all-updates" class="hidden" i18n-text="applyAllUpdates"></button>
<span id="update-all-no-updates" class="hidden"></span> <span id="update-all-no-updates" class="hidden" i18n-text="updateAllCheckSucceededNoUpdate"></span>
</p> </p>
<p><a href="edit.html"><button id="add-style-label"></button></a></p> <p><a href="edit.html"><button id="add-style-label" i18n-text="addStyleLabel"></button></a></p>
<div id="options"> <div id="options">
<h2 id="options-heading"></h2> <h2 id="options-heading" i18n-text="optionsHeading"></h2>
<input id="show-badge" type="checkbox"><label id="show-badge-label" for="show-badge"></label> <input id="show-badge" type="checkbox"><label id="show-badge-label" for="show-badge" i18n-text="prefShowBadge"></label>
<div><input id="popup.stylesFirst" type="checkbox"><label id="stylesFirst-label" for="popup.stylesFirst"></label></div> <div><input id="popup.stylesFirst" type="checkbox"><label id="stylesFirst-label" for="popup.stylesFirst" i18n-text="popupStylesFirst"></label></div>
</div> </div>
</div> </div>
<div id="installed"></div> <div id="installed"></div>

View File

@ -1,5 +1,18 @@
var styleTemplate = document.createElement("div"); var styleTemplate = tHTML('\
styleTemplate.innerHTML = "<h2 class='style-name'></h2><p class='applies-to'></p><p class='actions'><a class='style-edit-link' href='edit.html?id='><button>" + t('editStyleLabel') + "</button></a><button class='enable'>" + t('enableStyleLabel') + "</button><button class='disable'>" + t('disableStyleLabel') + "</button><button class='delete'>" + t('deleteStyleLabel') + "</button><button class='check-update'>" + t('checkForUpdate') + "</button><button class='update'>" + t('installUpdate') + "</button><span class='update-note'></span></p>"; <div>\
<h2 class="style-name"></h2>\
<p class="applies-to"></p>\
<p class="actions">\
<a class="style-edit-link" href="edit.html?id="><button i18n-text="editStyleLabel"></button></a>\
<button class="enable" i18n-text="enableStyleLabel"></button>\
<button class="disable" i18n-text="disableStyleLabel"></button>\
<button class="delete" i18n-text="deleteStyleLabel"></button>\
<button class="check-update" i18n-text="checkForUpdate"></button>\
<button class="update" i18n-text="installUpdate"></button>\
<span class="update-note"></span>\
</p>\
</div>\
');
var lastUpdatedStyleId = null; var lastUpdatedStyleId = null;
@ -415,20 +428,6 @@ function jsonEquals(a, b, property) {
} }
} }
document.title = t("manageTitle");
tE("manage-heading", "manageHeading");
tE("manage-text", "manageText", null, false);
tE("check-all-updates", "checkAllUpdates");
tE("apply-all-updates", "applyAllUpdates");
tE("update-all-no-updates", "updateAllCheckSucceededNoUpdate");
tE("add-style-label", "addStyleLabel");
tE("options-heading", "optionsHeading");
tE("show-badge-label", "prefShowBadge");
tE("manage.onlyEnabled-label", "manageOnlyEnabled");
tE("manage.onlyEdited-label", "manageOnlyEdited");
tE("filters", "manageFilters");
tE("stylesFirst-label", "popupStylesFirst");
document.getElementById("check-all-updates").addEventListener("click", checkUpdateAll, false); document.getElementById("check-all-updates").addEventListener("click", checkUpdateAll, false);
document.getElementById("apply-all-updates").addEventListener("click", applyUpdateAll, false); document.getElementById("apply-all-updates").addEventListener("click", applyUpdateAll, false);

View File

@ -60,6 +60,9 @@
#installed.disabled .style-name { #installed.disabled .style-name {
text-decoration: line-through; text-decoration: line-through;
} }
#installed .actions a {
margin-right: 0.5em;
}
body > .actions { body > .actions {
margin-top: 0.5em; margin-top: 0.5em;
} }
@ -73,6 +76,7 @@
#unavailable { #unavailable {
border: none; border: none;
display: none;
} }
body.blocked #installed, body.blocked #installed,
body.blocked #find-styles, body.blocked #find-styles,
@ -134,7 +138,7 @@
</head> </head>
<body id="stylish-popup"> <body id="stylish-popup">
<div id="unavailable"><div class="left-gutter"></div><div class="main-controls"><span id="unavailable-message"></span></div></div> <div id="unavailable"><div class="left-gutter"></div><div class="main-controls"><span id="unavailable-message" i18n-text="stylishUnavailableForURL"></span></div></div>
<div id="installed"></div> <div id="installed"></div>
@ -144,14 +148,14 @@
<input id="disableAll" type="checkbox"> <input id="disableAll" type="checkbox">
</div> </div>
<div class="main-controls"> <div class="main-controls">
<label id="disableAll-label" for="disableAll"></label> <label id="disableAll-label" for="disableAll" i18n-text="disableAllStyles"></label>
</div> </div>
</div> </div>
<div class="left-gutter"></div> <div class="left-gutter"></div>
<div class="main-controls"> <div class="main-controls">
<div id="find-styles"><a id="find-styles-link" href="#"></a></div> <div id="find-styles"><a id="find-styles-link" href="#" i18n-text="findStylesForSite"></a></div>
<div id="manage-styles"><a id="open-manage-link" href="manage.html"></a></div> <div id="manage-styles"><a id="open-manage-link" href="manage.html" i18n-text="openManage"></a></div>
<div id="write-style"><span id="write-style-for"></span></div> <div id="write-style"><span id="write-style-for" i18n-text="writeStyleFor"></span></div>
</div> </div>
</div> </div>

View File

@ -1,5 +1,19 @@
var styleTemplate = document.createElement("div"); var styleTemplate = tHTML('\
styleTemplate.innerHTML = "<div class='left-gutter'><input class='checker' type='checkbox'></div><div class='main-controls'><label class='style-name'></label><div class='actions'><a href='#' class='enable'>" + t('enableStyleLabel') + "</a> <a href='#' class='disable'>" + t('disableStyleLabel') + "</a> <a class='style-edit-link' href='edit.html?id='>" + t('editStyleLabel') + "</a> <a href='#' class='delete'>" + t('deleteStyleLabel') + "</a></div></div>"; <div>\
<div class="left-gutter">\
<input class="checker" type="checkbox">\
</div>\
<div class="main-controls">\
<label class="style-name"></label>\
<div class="actions">\
<a href="#" class="enable" i18n-text="enableStyleLabel"></a>\
<a href="#" class="disable" i18n-text="disableStyleLabel"></a>\
<a class="style-edit-link" href="edit.html?id=" i18n-text="editStyleLabel"></a>\
<a href="#" class="delete" i18n-text="deleteStyleLabel"></a>\
</div>\
</div>\
</div>\
');
var writeStyleTemplate = document.createElement("a"); var writeStyleTemplate = document.createElement("a");
writeStyleTemplate.className = "write-style-link"; writeStyleTemplate.className = "write-style-link";
@ -26,7 +40,7 @@ function updatePopUp(url) {
var urlWillWork = /^(file|http|https|chrome\-extension):/.exec(url); var urlWillWork = /^(file|http|https|chrome\-extension):/.exec(url);
if (!urlWillWork) { if (!urlWillWork) {
document.body.classList.add("blocked"); document.body.classList.add("blocked");
tE("unavailable-message", "stylishUnavailableForURL"); document.getElementById("unavailable").style.display = "block";
return; return;
} }
@ -214,11 +228,6 @@ chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
} }
}); });
tE("open-manage-link", "openManage");
tE("write-style-for", "writeStyleFor");
tE("find-styles-link", "findStylesForSite");
tE("disableAll-label", "disableAllStyles");
["find-styles-link", "open-manage-link"].forEach(function(id) { ["find-styles-link", "open-manage-link"].forEach(function(id) {
document.getElementById(id).addEventListener("click", openLink, false); document.getElementById(id).addEventListener("click", openLink, false);
}); });