I'm trying to convert the JavaScript code
if (window.ifEdit.editIsDirty()) { }
into Typescript. I got as far as the following
var iframe = document.getElementById('ifEdit');
var iWindow = <HTMLIFrameElement>(iframe).contentWindow;
var _editIsDirty = iWindow.editIsDirty();
I get the red squiggles under 'contentWindow' and 'editIsDirty' saying the method/property does not exist on the type. The .ts doesn't compile to a .js file.
I have searched, but did not manage to find a solution.
For the contentWindow part, the problem with your code is that the casting is done wrong, should be:
var iWindow = (<HTMLIFrameElement> iframe).contentWindow;
As for the editIsDirty, it's not a standard property of Window.
If it's something which is added in the environment in which you are running your javascript then you need to declare it like so:
interface IfEdit {
editIsDirty(): boolean;
}
interface Window {
ifEdit: IfEdit;
}
var iframe = document.getElementById("ifEdit");
var iWindow = (<HTMLIFrameElement> iframe).contentWindow;
var _editIsDirty = iWindow.ifEdit.editIsDirty();
Use the code in Playground.
Casting will be through as. this assures .contentWindow is accessible.
const iframe = document.getElementById('embed-player') as HTMLIFrameElement;
if (!iframe) {
// Handle case where iframe not found
return;
}
const contentWindow = iframe.contentWindow;
// Note: You will likely need more null handling for contentWindow's properties
console.log(contentWindow?.document);
Related
I need to create a hidden iframe in a Firefox add-on using WebExtensions.
This is what I'm trying to do (as suggested here: Load multiple pages in a hidden iframe from a xul-based firefox extension):
var hiddenWindow = Components.classes["#mozilla.org/appshell/appShellService;1"].
getService(Components.interfaces.nsIAppShellService).
hiddenDOMWindow;
I get the error "Components.classes is undefined", and can't seem to work around it.
Help is appreciated. Thank you.
Don't do this. In WEAPI this isn't even possible, you can't run code like this in that scope.
WEAPI
If you are using webextensions use a background page and you don't have to worry about this - https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Background_scripts
Bootstrap
In bootstrap you can do this, but don't. In bootstrap use the same tech used by WEAPI which is windowless browsers which are supported since Firefox 23 -
var webnav = Services.appShell.createWindowlessBrowser(true);
var docshell = webnav.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell);
var systemPrincipal = Cc["#mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
docshell.createAboutBlankContentViewer(systemPrincipal);
var contentWindow = docshell.contentViewer.DOMDocument.defaultView;
// when you are done with it, destroy it
if (webnav.close) { webnav.close() }; // only available in Firefox 46+, and is needed for good measure
webnav = null; // in Firefox <= 45 setting to null will cause it to get GC'ed which will destroy it
https://bugzilla.mozilla.org/show_bug.cgi?id=846906
https://bugzilla.mozilla.org/show_bug.cgi?id=1239822
Here's another example, this is how webextenions use this code above:
let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
this.chromeWebNav = chromeWebNav;
let url;
if (this.page) {
url = this.extension.baseURI.resolve(this.page);
} else {
// TODO: Chrome uses "_generated_background_page.html" for this.
url = this.extension.baseURI.resolve("_blank.html");
}
if (!this.extension.isExtensionURL(url)) {
this.extension.manifestError("Background page must be a file within the extension");
url = this.extension.baseURI.resolve("_blank.html");
}
let system = Services.scriptSecurityManager.getSystemPrincipal();
let chromeShell = chromeWebNav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
chromeShell.createAboutBlankContentViewer(system);
let chromeDoc = chromeWebNav.document;
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let browser = chromeDoc.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("webextension-view-type", "background");
chromeDoc.body.appendChild(browser);
let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
let docShell = frameLoader.docShell;
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
this.webNav = webNav;
webNav.loadURI(url, 0, null, null, null);
let window = webNav.document.defaultView;
this.contentWindow = window;
https://dxr.mozilla.org/mozilla-central/source/toolkit/components/extensions/ext-backgroundPage.js#25-64
I am binding a SELECT HTML tag with some dynamic values using knockout JS. Additionally, i am trying to set a selected choice which is failing. Please suggest where i am going wrong.
self.level1Choices.selectedChoice = ko.observable(2); - this line does not seem to work.
The JSFiddle for this code is at http://jsfiddle.net/oarp7gwj/7/
The dropdown is not loading in the JSFiddle for some reason. I dont think i have referenced the knockout JS correctly. In my local environment, I am able to load the select box with the values. However, i am not able to set the selected value.
#Wayne Ellery, #QBM5 - please advise since you know about this already :)
You should use var to declare your model object to avoid scoping issues
var viewModel = new DataModel();
The main issue was you need to add to the Datamodel by exposing it through the this variable in the Datamodel.
var DataModel = function (client) {
var self = this;
self.level1Choices = ko.observableArray();
};
Take a look at the Helo World example as to how to do this:
http://knockoutjs.com/examples/helloWorld.html
I've scoped this to self as it's a best practice to not worry about this referring to something else mentioned here: http://knockoutjs.com/documentation/computedObservables.html.
I moved the loadAllApprovers method inside the DataModel as this is where it belongs and so that it has access to populate the datamodel.
I added the mobile services client to the constructor so that it can be mocked for testing your model.
var DataModel = function (client) {
var self = this;
self.level1Choices = ko.observableArray();
var loadAllApprovers = function () {
var allAppprovers = client.getTable('TABLE');
var query = allAppprovers.select("ID", "FirstName").read().done(function (approverResults) {
self.level1Choices(approverResults);
}, function (err) {
console.log("Error: " + err);
});
};
loadAllApprovers();
};
You were also missing knockout in your jsfiddle.
http://jsfiddle.net/az4rox0q/6/
I want to share a document with JavaScript and get its share_id programatically.
There is a REST API that can do that but I didn't know how to call it from script.
Any clues?
The following hack will do the trick. (edit: Must be executed from the classpath in the repository)
var ctx = Packages.org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext();
var qsService = ctx.getBean("QuickShareService");
var sId = document.properties['qshare:sharedId'];
if (!sId) {
sId = qsService.shareContent(document.nodeRef).id;
}
PS: It looks even more ugly on 5.0.a due to rhino-1.7.
Is there any way I can get this piece of code to work inside Greasemonkey/Scriptish, or would I have to inject it into the webpage itself?
body = document.getElementsByTagName("body")[0];
fakeConsole = 'window.top._console';
injected = document.getElementById("sandbox") ? true : false;
sandboxframe = injected ? document.getElementsById("sandbox") : document.createElement('iframe');
sandbox = null;
if (!injected) {
body.appendChild(sandboxframe);
sandboxframe.setAttribute('id', 'sandbox');
sandboxframe.setAttribute('style', "display:none")
}
var p = sandboxframe.contentWindow.eval('1 + 1');
console.log(p);
This code does work when using source:
<script type="text/javascript" src="test.js"></script>
But not when using in a Greasemonkey script, I have observed there's some kind of security barrier I'm not quite familiar with and attempted to use unsafeWindow to bypass XPCNativeWrapper.
Please shed some light on this.
Several things:
The code has an error; getElementsById is not a function.
That code otherwise does work in Greasemonkey 1.0 or later, if the #grant none directive applies. More on this below.
For Scriptish, all other browsers, and Greasemonkey scenarios where #grant none is not possible; you will have to "inject" the code. More on this below.
As Jeremy J Starcher says, eval() should be avoided as much as possible. eval() makes performance, maintenance, debugging, and security much harder.
For Greasemonkey 1.0, and later:
In some scenarios, Greasemonkey no longer uses the XPCNativeWrapper. See the doc for the #grant directive.
So this means that (1) If your script uses no GM_ functions and (2) the script specifies #grant none, then your code will run as-is (excepting the getElementsById typo).
Note that no other scripting engine does this. (For darn good reasons. Greasemonkey's new behavior concerning #grant, and the sandbox, is controversial at best.)
If you wish to use GM_ functions, then you must inject the iframe code. See the next section.
For Scriptish, Privileged Greasemonkey, Chrome, etc.:
Scriptish, Sandboxed Greasemonkey, Chrome, etc. all do not handle iframes well from within their respective sandboxes. (See these Q's, for example.)
The only reliable way to run this kind of code from a GM/userscript is to inject it. Like so:
function gmMain () {
body = document.getElementsByTagName("body")[0];
fakeConsole = 'window.top._console';
injected = document.getElementById("sandbox") ? true : false;
sandboxframe = injected ? document.getElementById("sandbox") : document.createElement('iframe');
sandbox = null;
if (!injected) {
body.appendChild(sandboxframe);
sandboxframe.setAttribute('id', 'sandbox');
sandboxframe.setAttribute('style', "display:none")
}
var p = sandboxframe.contentWindow.eval('1 + 1');
console.log(p);
}
addJS_Node (null, null, gmMain);
function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
var D = document;
var scriptNode = D.createElement ('script');
if (runOnLoad) {
scriptNode.addEventListener ("load", runOnLoad, false);
}
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
targ.appendChild (scriptNode);
}
I've created an ASP.Net user control that will get placed more than once inside of web page. In this control I've defined a javascript object such as:
function MyObject( options )
{
this.x = options.x;
}
MyObject.prototype.someFunction=function someFunctionF()
{
return this.x + 1;
}
In the code behind I've created MyObject in a startup script --
var opts = { x: 99 };
var myObject = new MyObject( opts );
When a certain button in the control is pressed it will call myObject.someFunction(). Now lets say the value of x will be 99 for one control but 98 for another control. The problem here is that the var myObject will be repeated and only the last instance will matter. Surely there's a way to make the var myObject unique using some concept I've haven't run across yet. Ideas?
Thanks,
Craig
Your Javascript like this:-
function MyObject(options) { this.x = options.x; }
MyObject.prototype.someFunction = function() { return this.x + 1; }
MyObject.create(id, options) {
if (!this._instances) this._instances = {};
return this._instances[id] = new MyObject(options);
}
MyObject.getInstance(id) { return this._instances[id]; }
Your startup javascript like this:-
MyObject.create(ClientID, {x: 99});
Other code that needs to use an instance (say in the client-side onclick event)
String.Format("onclick=\"MyObject.getInstance('{0}').someFunction()\", ClientID);
Note the low impact on the clients global namespace, only the MyObject identifier is added to the global namespace, regardless of how many instances of your control are added to the page.
If it is just one value, why not have the function take it as a parameter and build your onclick handler so that it puts the correct value in for each control. If it is more complex than that, then consider making options an array and, for each control, insert the correct options into the spot in the array that corresponds to each particular control. Then pass the proper index into the array into the function.
I do this by using ScriptManager.RegisterClientScriptBlock to register a string as a JavaScript block on the client side. I can then modify my script string using {0}, {1}..,{n} place holders to inject necessary ids. It depends on the structure of your code as to if this is the most elegant fashion, but it works in a pinch. You could then inject variable names using references to Me.ClientID.
You can make the value of "x" static and access it anywhere in the code, such as:
function MyObject( options ) { MyObject.x = options.x; }
MyObject.x = 99; // static
MyObject.prototype.someFunction = function () { return MyObject.x + 1; }
This way you can access MyObject.x anywhere in your code, even without re-instanciating MyObject.
Excellent solution Anthony. The other solutions offered were as good and I did consider them but I was looking for something a little more elegant like this solution.
Thanks!