Why can't I call goog.require() in the same code block in which I call a function from the loaded library? - google-closure-library

Here is my html.
<html>
<head>
<script src="closure-library/closure/goog/base.js"></script>
<script src="hello.js"></script>
</head>
<body onload="sayHi()">
</body>
</html>
And here is my hello.js file.
var options = {
"style" : "background:#EEE;"
}
function sayHi() {
goog.require("goog.dom");
var header = goog.dom.createDom("h1", options, "hello world");
goog.dom.appendChild(document.body, header);
}
Why does Chrome Console issue this error?
Uncaught TypeError: Cannot call method 'createDom' of undefined
In general, I don't think I can require a library in the same code block in which I call a function from the library. I just saw this in the documentation, but I was wondering why.

In uncompiled Closure code, each goog.require(NAMESPACE) results in a call to:
document.write('<script src="SCRIPT_PROVIDING_NAMESPACE"></script>');
The dependencies are defined with calls to goog.addDependency(relativePath, provides, requires) in deps files generated with depswriter.py. Using the Chrome Developer Tools, the Elements tab shows all of the <script> elements dynamically inserted based on the Closure Library dependencies defined in deps.js.
In your example, when sayHi() is triggered by the body onload event, a new <script> element is inserted in the header. However, this new <script> element has not yet loaded goog.dom. Therefore, goog.dom is undefined.
If you change hello.js as follows:
goog.require("goog.dom");
var options = {
"style" : "background:#EEE;"
}
function sayHi() {
var header = goog.dom.createDom("h1", options, "hello world");
goog.dom.appendChild(document.body, header);
}
Then goog.require("goog.dom") gets executed prior to the onload event and the inserted <script> element has a chance to load dom.js prior to the call to sayHi().

Related

Pass object to browser with window.open in Edge

In case of Edge browser say Browser One, passing a custom argument to second Browser.
if I pass a string it is available in the second window. But, if I pass an object (say XMLDocument) in the second window, I could not serialzetoString.
var myWin = window.open(...);
myWin.customArg = 'string parameter' // Works
myWin.customArg = xmlObject // Doesnt Work
in the second window,
new XMLSerializer().serializeToString(xmlDoc)
throws xml parser exception.
Can any one help in resolving this?
Same code works fine for Chrome.
Edit - Sample code of Parent Window is here -
<html>
<head>
<script type="text/javascript">
function OpenWindow()
{
var objXML = '<SelectedCharts><Chart ColumnNo="1" ChartName="E0PK" GroupName="test" OrderNo="1" /></SelectedCharts>';
var xmlDoc = new DOMParser().parseFromString(objXML,'text/xml');
var dialog = window.open("Child_Window.htm", "title", "width=550px, height= 350px,left=100,top=100,menubar=no,status=no,toolbar=no");
dialog.dialogArguments = xmlDoc ;
dialog.opener = window;
}
</script>
</head>
<body>
<span>Passing an XML Object to the child window:</span>
<input type="button" value="Open Popup" onclick="OpenWindow()" />
</body>
</html>
And the sample code of Child window is here -
<html>
<head>
<script type="text/javascript">
function onBodyLoad()
{
alert(new XMLSerializer().serializeToString(window.dialogArguments));
}
</script>
</head>
<body onload="onBodyLoad()">
<span>This is child window.</span>
</body>
</html>
The code snippet shown in the question works fine for Chrome browser. And to pass the context to another window in case of Edge browser, follow the below method.
declare a global variable and set it in the parent window
And, access the varable in the child window using window.opener.
And sample code is provided in Pass custom arguments to window.open in case of Edge browser

DOM onLoad event without jquery

HTML code :
<html>
<head>
</head>
<body>
<script type="text/javascript" src="/js/colorbook.js"></script>
<script type="text/javascript">
book.init();
</script>
</body>
</html>
JS code :
var book = (function(){
init = function(){
console.log ( "initialized")
}return init();
}());
Question : The above code works. But I am unable to understand how?. Can any of JS guys help me here or guide me how should I start debug this code to understand it.
Ok so as per your comment. The Javascript you have there is all executed sequentially as the browser reads it from the incoming data stream. That being said, All javascript contained within the first script tag will be executed. Then the second script tag will be executed in sequence as well.
So right now you can look at the book.init() as being the last provided javascript call to be executed.
I tried your JS code in jsfiddle and could not get it to work check This Fiddle to see what I mean.
What is happening in your JS code is the last () at the end of the var book declaration executes the anonymous function which will print the line to the console. However from the code you supplied the book variable never gets a book.init() method. So once that call is reached it will throw an error of undefined.

Firefox extensions and full file paths from HTML form?

I have built a Firefox extension using the Addon SDK that opens up a new tab with a HTML page from the extensions directory and attaches a content script to it:
function openHtmlLoadFormTab(htmlFileName, jsWorkerFileName) {
tabs.open({
url: data.url(htmlFileName),
onReady: function(tab) {
var tabWorker = tab.attach({
contentScriptFile: [ data.url(jsJquery), data.url(jsWorkerFileName) ]
});
}
});
}
I have an <input type="file"> in the HTML file and some code that handles the "submit" event in the JS file (these files are given by htmlFileName and jsWorkerFileName respectively)
Because of security reasons, I cannot access the full file path in JS with document.getElementById('uploadid').value. I only get the file's name.
However, since this is a Firefox extension, I'm wondering if there is anyway to override this restriction?
I have been looking into netscape.security.PrivilegeManager.enablePrivilege("UniversalFileRead") and mozFullPath but I haven't been able to get it to work. I believe it's deprecated anyway?
The other solution is to build an XUL-based UI and prompt for the file there somehow, but I would like to know for sure if there is anyway to get this to work in HTML.
First edit with small example code
I built a small sample extension to illustrate how I'm doing things.
lib/main.js
var self = require('self');
var tabs = require('tabs');
var data = self.data;
var jsLoadForm = "load-form.js", htmlLoadForm = "load-form.html";
var jsJquery = 'jquery-1.8.0.min.js';
exports.onUnload = function(reason) {};
exports.main = function(options, callbacks) {
// TODO: remove this debugging line
openHtmlLoadFormTab(htmlLoadForm, jsLoadForm);
};
function openHtmlLoadFormTab(htmlFileName, jsWorkerFileName) {
tabs.open({
url: data.url(htmlFileName),
onReady: function(tab) {
var tabWorker = tab.attach({
contentScriptFile: [ data.url(jsJquery), data.url(jsWorkerFileName) ]
});
}
});
}
data/load-form.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form</title>
<script lang="text/javascript">
function fileChanged(e) {
// this is just the file name
alert("html js: files[0].name: " + e.files[0].name);
// mozFullPath is indeed empty, NOT undefined
alert("html js: files[0].mozFullPath: " + e.files[0].mozFullPath);
}
</script>
</head>
<body>
<form name="my-form" id="my-form" action="">
<div>
<label for="uploadid1" id="uploadlabel1">File (JS in HTML):</label>
<input type="file" name="uploadid1" id="uploadid1" onchange="fileChanged(this)"/>
</div>
<div>
<label for="uploadid2" id="uploadlabel2">File (JS in content script): </label>
<input type="file" name="uploadid2" id="uploadid2" onchange="fileChangedInContentScript(this)"/>
</div>
<div>
<label for="uploadid3" id="uploadlabel3">File (JS using jQuery in content script):</label>
<input type="file" name="uploadid3" id="uploadid3" />
</div>
</form>
</body>
</html>
data/load-form.js
$(document).ready(function() {
$("#uploadid3").change(function(e) {
// in jquery, e.files is null
if(e.files != null)
console.log("jquery: e.files is defined");
else
console.log("jquery: e.files is null");
// this works, prints the file name though
console.log("$('#uploadid3').val(): " + $("#uploadid3").val());
// this is undefined
console.log("$('#uploadid3').mozFullPath: " + $("#uploadid3").mozFullPath);
});
});
// this handler never gets called
function fileChangedInContentScript(e) {
alert("js content script: filechanged in content script called");
}
As you can see in main.js, I used jquery-1.8.0.min.js, downloaded from the jQuery website.
Note: I also tried these without jQuery included as a content script when I opened the tab in main.js, but no luck.
The conclusion is that mozFullPath is indeed empty when I access it from JS embedded in the HTML page and I cannot find a way to access mozFullPath from jQuery, nor can I find a way to add a onchange handler in load-form.html that's defined in load-form.js
Second edit with onchange handler in the load-form.js content-script
I added the following code to load-form.js to catch the onchange event.
I also removed the jQuery content script from main.js
document.addEventListener("DOMContentLoaded", function() {
try {
document.getElementById("uploadid2").addEventListener('change', function(e) {
console.log("addeventlistener worked!");
console.log("e: " + e);
console.log("e.target: " + e.target);
console.log("e.target.files: " + e.target.files);
console.log("e.target.files[0].name: " + e.target.files[0].name);
console.log("e.target.files[0].mozFullPath: " + e.target.files[0].mozFullPath);
});
console.log('added event listener')
} catch(e) {
console.log('adding event listener failed: ' + e);
}
}, false);
This still outputs an empty string for mozFullPath:
info: added event listener
info: addeventlistener worked!
info: e: [object Event]
info: e.target: [object HTMLInputElement]
info: e.target.files: [object FileList]
info: e.target.files[0].name: test.sh
info: e.target.files[0].mozFullPath:
Is there anyway to acquire the needed permissions? How can I get my hands on that full path? I need the full path so I can pass it to an application the extension launches. (There are workaround solutions where I can do without the full path, but they decrease the quality of the extension)
fileInput.value property is meant to be accessible to web pages so it will only give you the file name, not the full path - web pages have no reason to know the full path on your machine. However, as a privileged extension you should be able to access the File.mozFullPath property. In this particular case you would do it like this:
var files = document.getElementById('uploadid').files;
if (files.length > 0)
{
// Assuming that only one file can be selected
// we care only about the first entry
console.log(files[0].mozFullPath);
}
The big question of course is whether your code is allowed to access File.mozFullPath. I suspect that a content script in the Add-on SDK won't have the necessary privileges. The main extension code will have the privileges but getting to the input field from there is hard...

Call function from external .js file

I have a draw.js file with me which has a function as
var drawArrow=function(x1,y1,x2,y2)
{
// some code
}
In my html page in head I wrote
<script type="text/javascript" src="draw.js"></script>
<script type="text/javascript">
function callme() {
// insert code here
}
</script>
Please tell me waht code do I need to write in callme() to call drawArrow function of draw.js.
Nothing is external when you call js file all methods are easily accessible as it is create on same page now simply do this
<script type="text/javascript">
function callme() {
drawArrow=function(x1,y1,x2,y2);
// insert code here
}
</script>

tinymce jquery plugin error tinymce is not a function

I'm using tinymce jquery plugin and trying to access the api after initializing an instance of tinymce over a textarea.
In this example, I have a hide button, which when clicked on is supposed to hide the tinymce editor, but instead I get an error.
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script type="text/javascript" src="js/tinymce/jquery.tinymce.js"></script>
<script type="text/javascript" src="js/test.js"></script>
</head>
<body>
<div><textarea id="textEditor" class="tinymce" disabled="disabled"></textarea></div>
<input type ="button" id="hide" value="Hide tinymce">
</body>
</html>
$(document).ready(function(){
//textEditor
$("#textEditor")
.tinymce({
// Location of TinyMCE script
script_url : 'js/tinymce/tiny_mce.js',
theme : "advanced",
theme_advanced_buttons1 : "bold,italic,underline,",
theme_advanced_resizing : false
})
//... see below ...//
});
Update: I have 2 versions now, one that works by wrapping the $("#textEditor").tinymce().hide(); line in a click function, and one that gives me tinyMCE not defined with just the line itself.
Works:
$("#hide").click(function(){
$("#textEditor").tinymce().hide();
})
Doesn't work:
$("#textEditor").tinymce().hide(); //error tinyMCE is not defined
You could try
tinymce.get("textEditor").hide();
To verify if you are useing the correct tinymce id can alert all tinymce ids present at your page using
for (var i = 0; i < tinymce.editors.length; i++) {
alert(tinymce.editors[i].id);
}
EDIT:
This:
/** Option Block A error **/
// $("#textEditor").tinymce().hide(); //error tinyMCE is not defined
/** Option Block A error **/
does not work because it will get called before the tinymce editor is initialized. At this point there is no tinymce.get("textEditor").
I think that the path to your jquery plugin is not correct, because, the $.tinymce() method is provided there. If the file is not to be found, so is this method.
Also you should ensure that the path specified inside the *script_url* field is valid, as the plugin will try to load it on the fly.

Resources