I'm developping a web site using WAMP, this is a part of code in my website. I even checked this block of code mentioned below.
html page
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="Functions/jquery.min.js"></script>
<script type="text/javascript" src="Functions/listControl.js"></script>
<script type="text/javascript">
var users= new regUsers('load','bef','full');
</script>
</head>
<body>
</body>
</html>
javascript page
function regUsers(id,pos,detail)
{
/*
usr001 all users
usr002 search users on keyword
*/
var self=this
this.count=0;
this.id='#' + id;
this.pos=pos;
this.amount=0;
this.detail=detail;
this.countReset= function(){this.count = 0;};
this.getList=function()
{
console.log('count : ' + self.count);
$.ajax({
type:'POST',
url:'Functions/list.php',
data:{req:'usr001',off:this.count,detail:this.detail,amount:self.amount},
success:function(ans){
if(ans=="")
return;
else
{
self.count += parseInt(self.amount);
switch(self.pos)
{
case 'bef':
$(ans).insertBefore(self.id);
break;
case 'app':
$(self.id).append(ans);
console.log(ans);
break;
}
}
}
});
}
this.findRec=function(keyW='',cls,field)
{
if(keyW=='')
{
self.getList();
return;
}
$.ajax({
type:'POST',
url:'Functions/list.php',
data:{req:'usr002',keyW:keyW,detail:this.detail,field:field},
success:function(ans){
self.countReset();
$("."+ cls).remove();
switch(self.pos)
{
case 'bef':
$(ans).insertBefore(self.id);
break;
}
}
});
}
}
This code is properly work in Firefox, but not in internet explorer. In Internet explorer console, it is said that regUsers is not difined.
It could be that Internet Explore loads the JS files after it loads the inline script, I know it may not make much sense to do that but it might be a particular IE issue. What you could do is put your var users= new regUsers('load','bef','full'); in a third JS file or you can assign the value to users in unload of the body tag i.e.
<body onload="users = new regUsers('load', 'bet', 'full')">
. . .
</body>
This will insure that the your initialization code is executed after the webpage has loaded. Since users is declared with var it becomes a global variable, declaring with var users = … would have made it visible only in the scope of onload.
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")
}
})
Below is my html page:
<html>
<head>
<title>My Cat website</title>
</head>
<script type="text/javascript" src="script12.js"></script>
<body>
<h1>
My_first_cat_website
</h1>
</body>
</html>
Below is my JavaScript:
window.onload=initall;
function initall()
{
var ans=document.getElementsByTagName('a')[0].firstChild.data;
alert(ans);
if(ans<10)
{
alert(ans);
}
var newans=ans.subString(0,9)+"...";
}
Here my code is not going into if block. My requirement is if var "ans" length is above 10 then append it with ... else throw an alert directly. Can anyone help me?
Here is Solution using data property
window.onload=initall;
function initall()
{
var ans=document.getElementsByTagName('a')[0].firstChild.data;
if(ans.length<10)
{
alert("hmmm.. its less then 10!");
}
var newans= ans.substring(0,9)+"...";
document.getElementsByTagName('a')[0].firstChild.data = newans;
}
Here is it live view you wise to check example: http://jsbin.com/obeleh
I have never heard of the data property on a DOM element. Thanks to you, I learned it's a property on textNode elements (the same as nodeValue).
Also, using getElementsByTagName when the ID is available is unperformant.
subString doesn't work, it is substring. The case is important for methods as javascript is case sensitive (like most programming languages).
The other thing you're missing is an else. In your code, the var newans... will always be ran.
Here is something working:
window.onload = function() {
var ans = document.getElementById( 'message' ).textContent;
if ( ans.length < 10 ) {
alert( ans );
}
else {
var newans = ans.substring( 0, 9 ) + '...';
}
}
I made a puzzle game using HTML5, just now I tried to add local storage to the game, but failed.
I wrote three functions. The problem is: If I put all the functions in one .js file, none is defined when debugging(via chrome). If I split these three functions to another file and add a tag to the .html file, I'm told these are not defined.
So how to solve this problem?
Sorry for my poor English, hope you understand what I means.
Here are my functions and html file.
<html>
<head>
<meta charset="utf-8">
<title>Puzzle</title>
<script src="puzzle-localstorage.js"></script>
<script src="puzzle.js"></script></script>
</head>
<body onLoad="Init()" onkeydown="keydown()">
<p align="center">
<canvas id="board" height="600" width="600" style="border-style:double">
Your Browser doesn't support canvas
</canvas>
</p>
<p id="moves">Moves: <span id="movecount">0</span>
<input type="number" id="boardEdgeNum" value="3"/>
<input type="file" id="imgSrc"/>
</p>
function supportsLocalStorage() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}
function saveGameState() {
if (!supportsLocalStorage()) { return false; }
localStorage["puzzle.boardEdge"] = boardEdge;
localStorage["puzzle.blockPixel"] = blockPixel;
for (var i = 0; i < boardEdge; i++) {
for(var j=0;j<boardEdge;j++){
localStorage["puzzle.gameStatus."+i+j] = gameStatus[i][j];
}
}
localStorage["puzzle.image.src"]=imgElement.src;
localStorage["puzzle.move.count"] = moveCount.textContent;
if(gameInProgress)localStorage["puzzle.game.in.progress"] = "true";
else localStorage["puzzle.game.in.progress"]="false";
localStorage["puzzle.empty.block.X"] = emptyBlock.X;
localStorage["puzzle.empty.block.Y"] = emptyBlock.Y;
return true;
}
function resumeGame() {
if (!supportsLocalStorage()) {return false;}
if(!localStorage["puzzle.game.in.progress"]=="true"){return false;}
boardEdge=parseInt(localStorage["puzzle.boardEdge"]);
blockPixel=parseInt(localStorage["puzzle.blockPixel"]);
imgElement=new Image();
imgElement.src=localStorage["puzzle.image.src"];
gameStatus=new Array(boardEdge);
gameCompStatus=new Array(boardEdge);
for (var i = 0; i < boardEdge; i++) {
gameStatus[i]=new Array(boardEdge);
gameCompStatus[i]=new Array(boardEdge);
for(var j=0;j<boardEdge;j++){
gameStatus[i][j]=parseInt(localStorage["puzzle.gameStatus."+i+j]);
var x=(gameStatus[i][j]-1)%boardEdge;
var y=(gameStatus[i][j]-1-j)/boardEdge;
drawingContext.drawImage(imgElement,x*blockPixel,y*blockPixel,blockPixel,blockPixel
j*blockPixel,i*blockPixel,blockPixel,blockPixel);
drawLines();
}
}
gameStatus[boardEdge-1][boardEdge-1]=0;
gameCompStatus[boardEdge-1][boardEdge-1]=0;
moveCount.textContent=localStorage["puzzle.move.count"];
gameInProgress=(localStorage["puzzle.game.in.progress"] =="true");
emptyBlock=new Cell(parseInt(localStorage["puzzle.empty.block.X"]),parseInt(localStorage["puzzle.empty.block.Y"]));
return true;
}
<script src="puzzle.js"></script></script>
What is this? Is it typo? Or in your real code it is so too? Try to remove them. Javascript should see your functions. Where is declarations for Init and keydown functions? Does javascript see them?
Have you checked for any errors? I see one in the loop of resumeGame:
drawingContext.drawImage(imgElement,x*blockPixel,y*blockPixel,blockPixel,blockPixel
j*blockPixel,i*blockPixel,blockPixel,blockPixel);
should probably be:
drawingContext.drawImage(imgElement,x*blockPixel,y*blockPixel,blockPixel,blockPixel,
j*blockPixel,i*blockPixel,blockPixel,blockPixel);
Your url might be wrong. You are using a relative url instead of an absolute url, consequently the JS file must be in the same folder as the HTML Document.
Try an absolute url (e.g. http://www.servername.com/puzzle/js/puzzle.js") instead and check if that accomplishes anything.
I run this in Firefox, when clicking on link, Firefox says NS_ERROR_FILE_UNRECOGNIZED_PATH wheread I followed the instruction from here How to open .EXE with Javascript/XPCOM as Windows "Run..."?
<html>
<head>
<script>
function RunExe(path) {
try {
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf("msie") != -1) {
MyObject = new ActiveXObject("WScript.Shell")
MyObject.Run(path);
} else {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var exe = window.Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath(path);
var run = window.Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = [""];
run.run(false, parameters, parameters.length);
}
} catch (ex) {
alert(ex.toString());
}
}
</script>
</head>
<body>
Open Word
</body>
In javascript literals, a backslash indicates the beginning of an escape sequence. If you actually want to represent a backslash, you can escape it with a double backslash.
ie
'C:\\Windows\\System32\\cmd.exe /c start winword.exe'
http://www.javascriptkit.com/jsref/escapesequence.shtml
EDIT:
From the comments on the correct answer from the post you linked, it looks like the way he got it working was:
only pass the path to runexe:
javascript:RunExe('C:\Windows\System32\cmd.exe')
set the params equal to the command args:
var parameters = ["/c start winword.exe"];
So this would work theoretically:
<html>
<head>
<script>
function RunExe(path) {
try {
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf("msie") != -1) {
MyObject = new ActiveXObject("WScript.Shell")
MyObject.Run(path);
} else {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var exe = window.Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath(path);
var run = window.Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["/c start winword.exe"];
run.run(false, parameters, parameters.length);
}
} catch (ex) {
alert(ex.toString());
}
}
</script>
</head>
<body>
Open Word
</body>
Although clearly it would be better to pass in the params as an argument than hardcode them as I've done here (or pass them in as part of the path and parse them out)