I found a very easy way to implement translation (or localization) of my Google Chrome Extension, but that seems to apply only to .json, css and js files.
But how to localize my html content, say in the popup or an options window?
What you would do is this.
First, in your HTML use the same syntax as Chrome requires anywhere else. So your basic popup.html will be:
<!DOCTYPE html>
<html>
<head>
<title>__MSG_app_title__</title>
</head>
<body>
__MSG_link001__
<!-- Need to call our JS to do the localization -->
<script src="popup.js"></script>
</body>
</html>
Then provide the usual translation in _locales\en\messages.json:
{
"app_title": {
"message": "MyApp",
"description": "Name of the extension"
},
"link001": {
"message": "My link",
"description": "Link name for the page"
},
"prompt001": {
"message": "Click this link",
"description": "User prompt for the link"
}
}
And finally your popup.js will perform the actual localization:
function localizeHtmlPage()
{
//Localize by replacing __MSG_***__ meta tags
var objects = document.getElementsByTagName('html');
for (var j = 0; j < objects.length; j++)
{
var obj = objects[j];
var valStrH = obj.innerHTML.toString();
var valNewH = valStrH.replace(/__MSG_(\w+)__/g, function(match, v1)
{
return v1 ? chrome.i18n.getMessage(v1) : "";
});
if(valNewH != valStrH)
{
obj.innerHTML = valNewH;
}
}
}
localizeHtmlPage();
Plain an simple:
{
"exmaple_key": {
"message": "example_translation"
}
}
<sometag data-locale="example_key">fallback text</sometag>
document.querySelectorAll('[data-locale]').forEach(elem => {
elem.innerText = chrome.i18n.getMessage(elem.dataset.locale)
})
Building from ahmd0's answer. Use a data attribute to allow a hard-coded fallback.
<!DOCTYPE html>
<html>
<head>
<title data-localize="__MSG_app_title__">My Default Title</title>
</head>
<body>
Default link text
<script src="localize.js"></script>
</body>
</html>
Then provide the usual translation in _locales\en\messages.json:
{
"app_title": {
"message": "MyApp",
"description": "Name of the extension"
},
"link001": {
"message": "My link",
"description": "Link name for the page"
},
"prompt001": {
"message": "Click this link",
"description": "User prompt for the link"
}
}
And finally your localize.js will perform the actual localization:
function replace_i18n(obj, tag) {
var msg = tag.replace(/__MSG_(\w+)__/g, function(match, v1) {
return v1 ? chrome.i18n.getMessage(v1) : '';
});
if(msg != tag) obj.innerHTML = msg;
}
function localizeHtmlPage() {
// Localize using __MSG_***__ data tags
var data = document.querySelectorAll('[data-localize]');
for (var i in data) if (data.hasOwnProperty(i)) {
var obj = data[i];
var tag = obj.getAttribute('data-localize').toString();
replace_i18n(obj, tag);
}
// Localize everything else by replacing all __MSG_***__ tags
var page = document.getElementsByTagName('html');
for (var j = 0; j < page.length; j++) {
var obj = page[j];
var tag = obj.innerHTML.toString();
replace_i18n(obj, tag);
}
}
localizeHtmlPage();
The hard-coded fallback avoids the i18n tags being visible while the JavaScript does the replacements. Hard-coding seems to negate the idea of internationalisation, but until Chrome supports i18n use directly in HTML we need to use JavaScript.
As RobW noted in a comment, a feature request for adding i18n support in HTML using the same mechanism was created, but it has since then been rejected due to performance and security concerns. Therefore you can't use the same approach.
The issue mentions one possible workaround: to have separate HTML pages per language and switch between them in the manifest:
"browser_action": {
"default_popup": "__MSG_browser_action_page__"
}
But if that's not a suitable approach, the only way is to translate the page dynamically via JavaScript. You mention a solution the simplest approach, by just tagging elements to translate with ids and replacing them on page load.
You can also employ more sophisticated tools like webL10n in parallel with Chrome's approach. Note that you should probably still minimally implement Chrome's approach, so that Web Store knows that the item is supporting several languages.
Rather than parsing the full DOM, just add a class "localize" to the elements that have to be translated and add a data attribute data-localize="open_dashboard"
<div class="localize" data-localize="open_dashboard" >
Open Dashboard
</div>
JavaScript :
$('.localize').each(function(index,item){
var localizeKey = $(item).data( 'localize' );
$(item).html(chrome.i18n.getMessage(localizeKey));
});
'_locales/en/messages.json' file
{
"open_dashboard": {
"message": "Open Dashboard",
"description": "Opens the app dashboard"
}
}
A workaround to avoid replacements:
Use a simple "redirect"
It works for popups and options
In your manifest, declare the default popup
"default_popup": "popup/redirect.html"
The popup/redirect.html is almost empty. It just includes the script link to the redirect script
<!DOCTYPE html>
<html>
<head></head>
<body>
<script src="redirect.js"></script>
</body>
The popup/redirect.js file is very simple too:
var currentlang = chrome.i18n.getMessage("lang");
var popupUrl = chrome.runtime.getURL("popup/popup-"+currentlang+".html");
window.location.href = popupUrl;
Create multiple popups, already localized:
popup-fr.html
popup-en.html
Go into each of your messages.json files (in _locales) and add a "lang" message with the current language abbreviation as value: en for the english json, fr in the french json...
example for _locales/en/message.json:
"lang": {
"message": "en",
"description": "Locale language of the extension."
},
A simple workaround for very small project... definitely not a good choice for large ones. And it also works for Option pages.
One of the ways to localize your content in popup html is to fetch it from javascript onLoad. Store the strings in the _locales folder under various languages supported by you as mentioned here and do chrome.i18n.getMessage("messagename") to fetch and load the variable strings and set them using javascript/jquery onLoad function for each html element from your background.js or whatever js you load before your html pages loads.
I faced the same problem, but I solved it with a simple approach using custom data attributes.
Implement a localizing class that uses chrome.i18n and call it in the DOMContentLoaded event. In HTML, mark up the element you want to localize with the data-chrome-i18n attribute. (This attribute name is tentatively named.) Specifying the message name as the value of this attribute localizes the text content of the element. If you want to localize an attribute, specify it in the format attribute_name=message_name. Multiple specifications can be specified by separating them with ;.
const i18n = (window.browser || window.chrome || {}).i18n || { getMessage: () => undefined };
class Localizer {
constructor(options = {}) {
const { translate = Localizer.defaultTranslate, attributeName = Localizer.defaultAttributeName, parse = Localizer.defaultParse } = options;
this.translate = translate;
this.attributeName = attributeName;
this.parse = parse;
}
localizeElement(element) {
for (const [destination, name] of this.parse(element.getAttribute(this.attributeName))) {
if (!name)
continue;
const message = this.translate(name) || '';
if (!destination) {
element.textContent = message;
}
else {
element.setAttribute(destination, message);
}
}
}
localize(target = window.document) {
const nodes = target instanceof NodeList ? target : target.querySelectorAll(`[${CSS.escape(this.attributeName)}]`);
for (const node of nodes)
this.localizeElement(node);
}
}
Localizer.defaultTranslate = i18n.getMessage;
Localizer.defaultAttributeName = 'data-chrome-i18n';
Localizer.defaultParse = (value) => {
return (value || '').split(';').map(text => (text.includes('=') ? text.split('=') : ['', text]));
};
const localizer = new Localizer();
window.addEventListener('DOMContentLoaded', () => {
localizer.localize();
});
<!DOCTYPE html>
<html data-chrome-i18n="lang=##ui_locale">
<head>
<meta charset="UTF-8" />
<title data-chrome-i18n="extensionName"></title>
</head>
<body>
<p data-chrome-i18n="foo;title=bar;lang=##ui_locale"></p>
</body>
</html>
There are several things to consider to solve this problem.
Use chrome.i18n (Many people will want to aggregate in messages.json.)
Supports attributes as well as element content
Supports not only popup but also options page
Rendering performance
Security
First, the approach of switching HTML for each language in manifest.json does not work. Even if you give __MSG_*__ to the default_popup field, popup will still show the error "ERR_FILE_NOT_FOUND". I don't know why. There is no detailed reference to default_popup in the Chrome extensions Developer Guide, but MDN mentions that it is a localizable property. Similarly, if you give __MSG _*__ to the page field in options_ui, the extension itself will fail to load.
I intuitively felt that the approach of replacing __MSG_*__ in HTML and rewriting the result usinginnerHTML had performance and security problems.
This answer is cool!
And I want to make some modifications.
For chrome 93.0.4577.63 chrome.i18n.getMessage permalink, link-by-version
chrome.i18n.getMessage(messageName, substitutions, {escapeLt})
So I want to make it support
substitutions
escapeLt
Test Data
// _locales/en/messages.json
{
"hello": {
"message": "<b>Hello</b> $USER$ Welcoming $OUR_SITE$. $EMOJI$",
"description": "Greet the user",
"placeholders": {
"user": {
"content": "$1", // chrome.i18n.getMessage("hello", "variable 1")
"example": "Carson"
},
"our_site": {
"content": "Example.com"
},
"emoji": {
"content": "$2",
"example": "\uD83D\uDE42" // ๐, ๐
}
}
},
"app": {
"message": "My cool APP.",
"description": "description"
}
}
<!-- test.html-->
<script src="my-i18n.js"></script>
<p data-i18n="__MSG_hello__"></p>
<p data-i18n="__MSG_hello__<b>Carson</b>"></p>
<p data-i18n="__MSG_hello__<b>Carson</b>|0"></p>
<p data-i18n="__MSG_hello__<i>Carson</i>|1"></p>
<button title="__MSG_hello__<b>Carson</b>" data-i18n></button>
<button title="__MSG_hello__<b>Carson</b>|0" data-i18n></button>
<button title="__MSG_hello__<b>Carson</b>|1" data-i18n></button>
<p title="__MSG_app__" data-i18n="__MSG_hello__Carson,๐"></p>
output
Script
// my-i18n.js
/**
* #param {string} msg "__MSG_Hello__para1,para2|1" or "__MSG_Hello__para1,para2|0"
* */
function convertMsgAsFuncPara(msg) {
const match = /__MSG_(?<id>\w+)__(?<para>[^|]*)?(\|(?<escapeLt>[01]{1}))?/g.exec(msg) // https://regex101.com/r/OeXezc/1/
if (match) {
let {groups: {id, para, escapeLt}} = match
para = para ?? ""
escapeLt = escapeLt ?? false
return [id, para.split(","), Boolean(Number(escapeLt))]
}
return [undefined]
}
function InitI18nNode() {
const msgNodeArray = document.querySelectorAll(`[data-i18n]`)
msgNodeArray.forEach(msgNode => {
const [id, paraArray, escapeLt] = convertMsgAsFuncPara(msgNode.getAttribute("data-i18n"))
if (id) {
msgNode.innerHTML = chrome.i18n.getMessage(id, paraArray, {escapeLt})
}
// โ handle attr
for (const attr of msgNode.attributes) {
const [attrName, attrValue] = [attr.nodeName, attr.nodeValue]
const [id, paraArray, escapeLt] = convertMsgAsFuncPara(attrValue)
if (!id) {
continue
}
msgNode.setAttribute(attrName, chrome.i18n.getMessage(id, paraArray, {escapeLt}))
}
})
}
(() => {
window.addEventListener("load", InitI18nNode, {once: true})
})()
Modify pseudo-category content in batches.
<div data-content="font"></div>
div::before {
content: attr(data-content);
}
document.querySelectorAll('[data-content]').forEach(el => {
el.dataset.content = chrome.i18n.getMessage(el.dataset.content);
});
Use CSS Internationalization.
<p></p>
p::before {
content: "__MSG_font__";
}
Another workaround - you can use content property in css with __MSG_myText inside.
Use Vue.js:
<html>
<head>
</head>
<body>
<div id="app">{{msgTranslated}}</div>
</body>
</html>
javascript file injected:
new Vue({
el: '#app',
data: {
msgTranslated: chrome.i18n.getMessage("message")
}
})
Related
I'm doing a study using a RSS, but the Web Site gives me a RSS with an unclosed tag then I couldn't get the innerHTML of this tag.
I don't know how to resolve the problem with jquery and make the tag closed or a possible solution like this.
Here is the code :
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" content="xml">
<script type="text/javascript" src="api/jquery.js"></script>
</head>
<body>
<p id="someElement" visibility="hidden"></p>
<p id="anotherElement"></p>
<script type="text/javascript">
var x = new XMLHttpRequest();
x.open("GET", "http://www.lemonde.fr/rss/une.xml", true);
x.onreadystatechange = function () {
if (x.readyState == 4 && x.status == 200)
{
var doc = x.responseXML;
var string = (new XMLSerializer()).serializeToString(doc);
$("#someElement").append(string);
alert("test");
var tag = document.getElementsByTagName("item");
for(var i = 0, max = tag.length; i < max; i++){
var htmli = tag[i];
//alert(htmli.innerHTML);
//uncomment the alert to see the xml got from the rss
var title = htmli.getElementsByTagName("title")[0].innerHTML;
var link = htmli.getElementsByTagName("link")[0].innerHTML;
var description = htmli.getElementsByTagName("description")[0].innerHTML;
var toAdd = "<ul><li> title : " +title+"</li><li> link : "+ link +" </li><li> description :"+description+" </li></ul>";
$("#anotherElement").append(toAdd);
}
}
};
x.send(null);
</script>
</body>
</html>
Any solution to this?
I have jquery in a folder named api.
Thanks a lot !!
(I notice that while you include jQuery in a script tag, you're not actually using it in your code. It's much better practice to use jQuery's functionality to manage AJAX requests and serialization, if you're going to use it at all, as they cover many more situations and browser versions. I'd also recommend retrieving jQuery from a CDN rather than hosting it yourself. jQuery has had the ability to parse XML natively since 1.5. The following was written using 1.12.)
I ran into the same issue with unclosed tags in an RSS feed and came up with a terrible solution to it. I have not tested this cross-browser and would not recommend incorporating it into production code, but it worked to solve a one-time problem for me.
The idea is to take the raw output of the RSS item's text, cram it into the jQuery HTML parser, and then manually inspect its output until we get to an item that it thinks might have been an HTML <link> tag. Because we know the RSS link tag isn't closed, the next thing it encounters should be parsed as an HTML Text object, which we can extract for our permalink URL.
Here's how I would rewrite your script to take better advantage of jQuery and incorporate my hack. (I'm assuming you have set up CORS or something else so that you can actually retrieve the feed from lemonde.fr cross-domain.)
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" content="xml">
<script type="text/javascript" src="//code.jquery.com/jquery-1.12.4.min.js"></script>
</head>
<body>
<p id="someElement" visibility="hidden"></p>
<p id="anotherElement"></p>
<script type="text/javascript">
(function($, window, document) {
function fetchFeed(url) {
// use jQuery to handle AJAX
$.get(url, function(data) {
// parse XML result with jQuery
var $XML = $(data);
$XML.find("item").each(function() {
// ensure that we have a jQuery-wrapped _this_ object and
// create a new object with the properties we want
var $this = $(this),
item = {
title: $this.find("title").text(),
description: $this.find("description").text(),
link: ""
};
// since the XML parser will treat the unclosed <link> as valid,
// we instead send the raw output to the HTML parser and tell it do to its best
var $redigested = $($this.html());
// jQuery should produce an array of HTML DOM objects
for (var i = 0; i < $redigested.length; i++) {
// if we found an HTMLLinkElement--a <link> tag--followed by a Text element, that's our URL
if ($redigested[i] instanceof HTMLLinkElement && $redigested.length >= i + 1 && $redigested[i + 1] instanceof Text) {
item.link = $redigested[i + 1].data;
break;
}
}
console.log("link: " + item.link);
var toAdd = "<ul><li> title: " + item.title + "</li><li> link: " + item.link + " </li><li> description: " + item.description + " </li></ul>";
$("#anotherElement").append(toAdd);
});
});
}
$(function() {
// call the fetch function on DOM ready
fetchFeed("http://www.lemonde.fr/rss/une.xml");
});
})(jQuery, window, document);
</script>
</body>
</html>
As said in the subject, I need to fill a web form using data locally available as excel tables. I am already making that with a combination of python and autohotkey, but I want to have some level of JavaScript control in order to correctly handle loading times and conditionals. As a web development newbie, I first thought I could just have a local iframe controlling the website where the form is, but I discovered soon enough that XSS thing that does not allow such a hack. I do not have access to the server.
The last iteration of my experiences is with Firefox webextensions, with which I hoped to open a local file (through a html5 file input widget), where I would previously have written my js code to fill the form. But apparently there are also limitations here, and I cannot make any sense out the docs I am looking at. My code is currently like that:
popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<input type="file" id="liquida-file" name="liquida">
<br>
<script src="background-script.js"></script>
</body>
</html>
background-script.js
function handleFiles() {
var fileList = this.files; /* now you can work with the file list */
var myFile = fileList[0]
var reader = new FileReader();
reader.onloadend = function(evt){
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
var filedata = evt.target.result;
console.error("Analyzing file data")
console.error(filedata)
var data = JSON.parse(filedata)
console.error(data)
}
};
reader.readAsText(myFile)
}
var inputElement = document.getElementById("liquida-file");
inputElement.addEventListener("change", handleFiles, false);
This works as a standalone file, but not as the popup.html file of my webextension. In this case, none of the console.error lines are ever reached. BTW, here is my manifest.json:
manifest.json
{
"manifest_version": 2,
"name": "My extension",
"version": "1.0",
"description": "Retrieve local data.",
"homepage_url": "http://Nonefornow",
"icons": {
"48": "icons/beautiful-icon.png"
},
"permissions": [
"activeTab"
],
"browser_action": {
"browser_style": true,
"default_icon": "icons/icon.png",
"default_title": "My Ext",
"default_popup": "popup.html"
}
}
Is there any easier way to do what I am doing? I was expecting for this sort of thing to be a common need, am I wrong? And why doesn't my code work?
This problem has been pointed out in this question:
Firefox WebExtensions, get local files content by path.
The solution given there is the following:
function readFile(_path, _cb){
fetch(_path, {mode:'same-origin'}) // <-- important
.then(function(_res) {
return _res.blob();
})
.then(function(_blob) {
var reader = new FileReader();
reader.addEventListener("loadend", function() {
_cb(this.result);
});
reader.readAsText(_blob);
});
};
but in this solution the absolute path has to be passed to the function, like here:
readFile('file:///home/saba/desktop/test.txt', function(_res){
console.log(_res); // <-- result (file content)
});
If you want to load a file from an <input> field you have to pass the path of the file too, because for security reasons you can't retrieve that from the <input> field. My solution was to read the path from an input text field, reducing significantly the usability
html
path: <input type="input" id="importFilePathInput" value="file://" />
<br />
file: <input type="file" id="importFileInput" />
javascript
function importFromfile(){
let filename = jQuery('#importFileInput').val().replace('C:\\fakepath\\', '');
if (!filename) {
console.log('Select a filename');
} else {
let path = jQuery('#importFilePathInput').val() + '/' + filename;
if (!path.startsWith('file://')) {
path = 'file://' + path;
}
fetch(path, {mode:'same-origin'})
.then(function(result){
return result.blob();
})
.then(function(blob){
let reader = new FileReader();
reader.addEventListener('loadend', function(){
Model.save(JSON.parse(this.result)); // your function here
});
reader.readAsText(blob);
});
}
}
Note that unfortunately this solution doesn't work anymore on Firefox 57, giving the error:
TypeError: NetworkError when attempting to fetch resource.
This works as a standalone file, but not as the popup.html file of my webextension.
Aha. I would check the permissions ...
I am currently making a small module for NodeJs. For which I need a small help.
I will tell it like this.
I have a variable with string. It contains a string html value. Now I need to replace $(title) something like this with my object { "title" : "my title" }. This can be expanded to anything with user provide. This is current code.I think that I need RegEx for do this. Can you guys help me with this?
var html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document $(title)</title>
</head>
<body>
<h1>Test file, $(text)</h1>
</body>
</html>`;
function replacer(html, replace) {
// i need a regex to replace these data
//return replacedData;
}
replacer(html, { "title" : "my title", "text" : "text is this" });
You can use a simple template function using regex,
var replacer = function(tpl, data) {
var re = /\$\(([^\)]+)?\)/g, match;
while(match = re.exec(tpl)) {
tpl = tpl.replace(match[0], data[match[1]])
re.lastIndex = 0;
}
return tpl;
}
use like
var result = replacer(html, { "title" : "my title", "text" : "text is this" });
jsfiddle
detail here
EDIT
Actually as torazaburo mentioned in the comment, it can be refactored as
var replacer = function(tpl, data) {
return tpl.replace(/\$\(([^\)]+)?\)/g, function($1, $2) { return data[$2]; });
}
jsfiddle
hope this helps
This solution uses template strings to do everything you want.
This solution has the advantage that, in contrast to the naive roll-your-own regexp-based template replacement strategy as proposed in another answer, it supports arbitrary calculations, as in
replacer("My name is ${name.toUpperCase()}", {name: "Bob"});
In this version of replacer, we use new Function to create a function which takes the object properties as parameters, and returns the template passed in evaluated as a template string. Then we invoke that function with the values of the object properties.
function replacer(template, obj) {
var keys = Object.keys(obj);
var func = Function(...keys, "return `" + template + "`;");
return func(...keys.map(k => obj[k]));
}
We define the template using ${} for substitutions (instead of $()), but escaping as \${ to prevent evaluation. (We could also just specify it as a regular string literal, but would then lose multi-line capability).
var html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document \${title}</title> <!-- escape $ -->
</head>
<body>
<h1>Test file, \${text}</h1> <!-- escape $ -->
</body>
</html>`;
Now things work exactly as you want:
replacer(html, { "title" : "my title", "text" : "text is this" });
Simple example:
> replacer("My name is ${name}", {name: "Bob"})
< "My name is Bob"
Here's an example of calculated fields:
> replacer("My name is ${name.toUpperCase()}", {name: "Bob"})
< "My name is BOB"
or even
> replacer("My name is ${last ? lastName : firstName}",
{lastName: "Jones", firstName: "Bob", last: true})
< "My name is Jones"
Since you are using ES6 template string you can use a feature called 'tagged template strings'. Using tagged template strings you are allowed to modify the output of a template string. You create tagged template string by putting a 'tag' in front of the template string, the 'tag' is a reference to a method that will receive the string parts in a list as the first argument and the interpolation values as remaining arguments. The MDN page on template strings already provides an example template string 'tag' that we can use:
function template(strings, ...keys) {
return (function(...values) {
var dict = values[values.length - 1] || {};
var result = [strings[0]];
keys.forEach(function(key, i) {
var value = Number.isInteger(key) ? values[key] : dict[key];
result.push(value, strings[i + 1]);
});
return result.join('');
});
}
You use the 'tag' by calling:
var tagged = template`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document ${'title'}</title>
</head>
<body>
<h1>Test file, ${'text'}</h1>
</body>
</html>`;
Notice that interpolation of variables uses the syntax ${'key'} instead of $(key). You can now call the produced function to get the desired result:
tagged({ "title" : "my title", "text" : "text is this" });
Run the code example on es6console
var binddingData=function(id,data){
var me=this,
arr=[];
arr=getControlBindding(id);
arr.forEach(function(node){
var content=getBinddingContent(node.getAttribute('DataTemp'),data);
binddingToHtml(node,content);
})
}
var getControlBindding=function(id){
var me=this;
return document.querySelectorAll('[DataTemp]');
}
var getBinddingContent=function(temp,data){
var me=this,
res='',
hasNull=false;
if(temp==null||typeof temp=='undefined'){
return res;
}
res= temp.replace(/\$\{([^\}]+)?\}/g, function($1, $2) {
if(data[$2]==null||typeof data[$2]=='undefined'){
hasNull=true;
}
return data[$2];
});
return hasNull?'':res;
}
var binddingToHtml=function(node,content){
var me=this;
if(node.getAttribute('IsDateTime')){
node.innerText='';//if u want change it to datetime string
return;
}
if(node.getAttribute('AddBr') && content==''){
node.innerText='';
var brTag=document.createElement('br');
node.appendChild(brTag);
return;
}
node.innerText=content;
}
You use the 'tag' by calling:
<div DataTemp="${d1}"></div>
<div DataTemp="${d2}"></div>
<div DataTemp="${d3}"></div>
<div DataTemp="${d3}+ +${d1}"></div>
<div DataTemp="${d3}/${d1}"></div>
<div DataTemp="${d4}\${d1}"></div>
<div DataTemp="(${d5}\${d1})"></div>
<div DataTemp="(${d3}\${d1})"></div>
with data var data={d1:'t1',d2:'t2',d3:'t3'}
I am developing a chrome extensions. What i want to realize is that the popup can display the text from pdf. I have searched the PDF.js and write the following code in backgroud.js of chrome extensions to test:
โuse strictโ;
var pdf = PDFJS.getDocument('http://www.pacer.gov/documents/pacermanual.pdf');
var pdf = PDFJS.getDocument('pacermanual.pdf');
pdf.then(function(pdf) {
var maxPages = pdf.pdfInfo.numPages;
for (var j = 1; j <= maxPages; j++) {
var page = pdf.getPage(j);
// the callback function - we create one per page
var processPageText = function processPageText(pageIndex) {
return function(pageData, content) {
return function(text) {
// bidiTexts has a property identifying whether this
// text is left-to-right or right-to-left
for (var i = 0; i < text.bidiTexts.length; i++) {
str += text.bidiTexts[i].str;
}
if (pageData.pageInfo.pageIndex ===
maxPages - 1) {
// later this will insert into an index
console.log(str);
}
}
}
}(j);
var processPage = function processPage(pageData) {
var content = pageData.getTextContent();
content.then(processPageText(pageData, content));
}
page.then(processPage);
}
});
The manifest is shown as follow:
{
"name": "englishhelper",
"version": "0.0.1",
"description": "",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"background":{
"script":["background.js","PDF.js"]
},
"browser_action":{
"default_icon":"icon_png",
"default_popup":"popup.html"
},
"manifest_version": 2
}
The popup.html is shown as follow:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="popup.css">
<title></title>
</head>
<body>
<script src="background.js"></script>
<script src="PDF.js"></script>
</body>
</html>
The console shows that "PDFJS is not defined". The "PDF.js" has been included in popup.html. Is it possible that chrome extension use PDF.js?
Wrong load order. (Do we have a canonical question for that?)
background.script or content_scripts[i].js key in the manifest is an array, in other words an ordered list.
Scripts are loaded and executed in the sequence defined there; you need to make sure libraries are loaded before they are used.
In your case, you need to swap them around:
"background":{
"script": ["PDF.js", "background.js"]
},
Same applies to the order of <script> tags in HTML, for instance in your popup.html
I have a need to include (through Javascript) different content depending on the major category captured from the url.
The website is laid out like so:
http://example.com/Category/Arts/Other/Sub/Categories/
http://example.com/Category/News/Other/Sub/Categories/
http://example.com/Category/Sports/Other/Sub/Categories/
http://example.com/Category/Business_And_Finance/Other/Sub/Categories/
The different major categories above are:
Arts, News, Sports, and Business_And_Finance
What is the best way to accomplish this in javascript.
What I need may look something like the following,
if (category = Arts) {
alert("Arts");
}else if (category = News) {
alert("News");
}...
Thank you in advance.
Split the location.href then do a switch on the appropriate variable. So, for example:
var url = document.location.href,
split = url.split("/");
/*
Split will resemble something like this:
["http:", "", "example.com", "Category", "Arts", "Other", "Sub", "Categories", ""]
So, you'll find the bit you're interested in at the 4th element in the array
*/
switch(split[4]){
case "Arts":
alert("I do say old chap");
break;
case "News":
alert("Anything interesting on?");
break;
default:
alert("I have no idea what page you're on :O!");
}
you can access the current url like this
document.location.href
you could do a
if ( document.location.href.indexOf("categoryYouWant")>-1){
//whatever you want
}
but you should do a regular expression
category=document.location.href.match(/example\.com\/(\w+)\//i)[1];
var url = window.location.href;
var category = url.split('Category/')[1].split('/')[0];
if (category === 'Arts') {
alert("Arts");
}else if (category === 'News') {
alert("News");
}
I made this example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script type="text/javascript">
function determine(url)
{
var myArray = url.split('/');
if(myArray[4] == "News")
alert(myArray[4]);
}
</script>
</head>
<body>
<div>
http://example.com/Category/News/Other/Sub/Categories/
</div>
</body>
</html>
Saludos ;)