Since extensions can not access unsafeWindow, like Firefox can, to hook into DOM scripts am I looking for other ideas so I come to SO for help!
How about using some code to inject into DOM and sending the intercepted response to a background page, which then does some initial processing before calling a content script for final processing. When done, it answers to the background with a modified response, or the original (it depends), and the background page sends the response back to DOM which handles it to the DOM script response function.
There is just one problem with this, a background page cant communicate with the DOM.
I did a small test with injecting some code, where I output something to the console and an alert. The result wasnt good, as the alert fired but the console was empty - not even an error, which makes me wonder - what console received the output ?
function injectCode(fn){ // Executing an anonymous script
var script = document.createElement('script');
script.type = 'application/javascript';
script.textContent = '(' + fn + ')();';
document.documentElement.appendChild(script); // run the script
document.documentElement.removeChild(script); // clean up
}
var code = function(){
console.log('dom',window);
alert('code injected');
}
injectCode(code);
I also tried addEventListener, with DOMAttrModified DOMSubtreeModified DOMNodeInserted, on DOM elements that change when the DOM ajax response is fully parsed but all failed to fire.
Am I trying to do the impossible, by any means ?
Before continuing, make sure that you know the differences between the script contexts in an extension.
To inject a script from the background page, you have to execute a Content script, which on his turn injects the script as mentioned in your question / here.
Examples (using chrome.tabs.executeScript):
// null = current active tab
// Simple code, background:
chrome.tabs.executeScript(null, {
code: [
'var s = document.createElement("script");',
's.textContent = "console.log(window);";',
'(document.head||document.documentElement).appendChild(s);',
's.parentNode.removeChild(s);'
].join('\n')
});
I can imagine that this method is not doable for a big chuck of code. For a set of pre-defined scripts, you can then use two scripts: the code itself, and a helper script:
// config.js
var fn_code = function() {
console.log(window); ....
};
// helper.js
var s = document.createElement('script');
s.textContent = '(' + fn_code + ')();';
(document.head||document.documentElement).appendChild(s);
s.parentNode.removeChild(s);
// Background:
chrome.tabs.executeScript(null, {file: 'config.js'}, function() {
chrome.tabs.executeScript(null, {file: 'helper.js'});
});
Note: I did not directly link to "config.js", because that complicates the use when using manifest version 2, see "web_accessible_resources".
The previous method only shows how to execute code in one direction (background -> page). If there's a need to activate a background's function from the injected script, you have to define and listen to a custom event handler. See this answer + demo.
Because the code is injected, thus runs in the scope of the page, you have to check the console at the page.
When chrome.tabs.executeScript fails to execute the Content script (eg. because the extension does not have the permission to access a certain page), an error is logged at the console in the background page. This console can be accessed by following these steps.
Related
I need to be able to change some variable's value, when app is closed.
I'm using exitEvent described here:
https://docs.nativescript.org/core-concepts/application-lifecycle
Also, i'm using local-storage plugin that works similar to javasctip's localstorage.
https://github.com/NathanaelA/nativescript-localstorage
My simple code looks like this:
var Observable = require("data/observable").Observable;
require("nativescript-dom");
var LS = require("nativescript-localstorage");
const application = require("tns-core-modules/application");
var page = require("ui/page");
exports.load = function (args) {
var page = args.object;
var model = new Observable();
var frame = require('ui/frame');
var ff = frame.getFrameById("dashboard");
var topmost = frame.topmost();
// This is exit event
application.on(application.exitEvent, (args) => {
LS.setItem('XX','111')
});
// What i will exit app, and run app again this will newer become 111,
it's null all the time
console.log(LS.getItem('XX'));
}
So, my question is - is possible to set any flag on app exit - it do not have to be localstorage (i've tested global variables to), to detect if exit was made, and based on this i can make a decisions that will help me ?
One of scenarios may be - i'm holding some flag in Localstorage that is TURE is user tapped "rememebr me" on the login screen.
So on exit i can check if he want's to be rememebered, if not i want to send user to login page and not dashboard when app is lauching....
Thank you.
EDIT:
I've tried applications-settings too, it will not work.
application.on(application.exitEvent, (args) => {
applicationSettings.setString("Name", "John Doe");
});
console.log(applicationSettings.getString("Name")); // not working
I suspect it's the issue with the nativescript-localstorage plugin. It writes the changes to file after a 250ms delay. At exit event you will give you very limited amount of time before your app is completely killed by system.
May be the Author had a reason for setting up this delay but I think its too much of time at least in this particular scenario so the changes are never written to file. You may raise a issue at the plugin's Github repo.
If you are looking for an immediate workaround, copy localstorage.js to your app and export internalSaveData from the file, so you could directly call it right after you finish setting your values.
I am using the file.saveURL in a loop and its working good but I am seeing some strage things. Basically I loop over about 70 images and then grab the uri to them after they are saved and store that locally so I can then use it to display in the app
What happens is that once the loop is done I display the images out but randomly some of the images are the same. I have validated the correct URL is being passed but its as if and I don't know for sure but maybe the function is not done with the previous and is somehow overwriting it?
This makes the most sence because the issue usually happens with images right next to each other.
So I guess my question is, does the file.saveURL only work on a one to one aspect, like it has to by synchronous?
If that is the case what would be the recommended approach for looping over and saving these images.
Thanks!
EDIT
This is a basic sample (I have some conditional stuff in there but this is the main part)
I have the JSON object stored and I loop over it
$(data).each(function(i){
var slcval = 'speaker' + this.SID;
var imageID = 'simageid' + this.IMAGE;
var speakerImage = "http://mydomain.com/users/images/speakers/" + this.IMAGE;
//then I call the save url function
saveURLImage(speakerImage,slcval,'speaker',this.SID,imageID);
}
this loops over my images and calls the save image function that then does the save URL function
function saveURLImage(url,ID,type,extraVal,imageID){
forge.file.saveURL(url, function (file) {
forge.file.URL(file, function (url) {
var fileObject = JSON.stringify(url);
localStorage.setItem(ID, fileObject);
})
});
}
This is a simple version of it, I have some other parts that set some localstorage vars but this is the main call.
So my problem was a scoping issue
so if anyone else comes arrocss this I found this thred that helped out
Javascript: function in setTimeout and references
Basically what I did was creat a function that has an announmous function in it so the scope would be correct
function saveURLImage(url,ID,type,extraVal,imageID) {
(function() {
saveURLImageScoped(url,ID,type,extraVal,imageID)
})();
}
so the function name is still the same as before but I renamed the main function saveURLImageScoped and now it has its own variable scope
I am using the following jQuery plugin :
http://pvdspek.github.com/jquery.autoellipsis/, and in general it works very well.
The problem comes when I need to update the text of the element. One would assume that changing the text of the element and calling the plugin again would perform the same action the initial call performed.
But, as can be seen in this fiddle - it doesn't.
The code is very simple
var container = $(".container");
container.text("This is a long text that should have text ellipsis");
//this works fine
container.ellipsis();
$("button").click(function()
{
container.text("This is the modified text that should also have ellipsis");
//this doesn't work
container.ellipsis();
});
The only way I could make it work is by deleting the data stored on the element, and by this
making the plugin run "from scratch".
any ideas?
Clear the data stored by autoellipsis: container.data('jqae', null);
var container = $(".container");
container.text("This is a long text that should have text ellipsis");
//this works fine
container.ellipsis();
$("button").click(function()
{
container.data('jqae', null);
container.text("This is the modified text that should also have ellipsis");
//this doesn't work
container.ellipsis();
});
I am trying to remove an element on AJAX success which was loaded and attached to the document during a previous AJAX call.
My code looks something like this:
$("#jobs-table-body").on("click", ".one-rc-button", function() {
var ctx = $.parseJSON($(this).siblings(".context").html());
$("#one-rc-candidate-id").val(ctx.candidateId);
$("#one-rc-job-id").val(ctx.jobId);
var loader = $("#wrapper").loader();
$.post($("#one-rc-form").attr("action"), $("#one-rc-form").serialize(), function(result) {
loader.remove();
if(result.success) {
// This works and returns 1
alert($("#candidate-row-" + result.rejectedCandidateId).length);
// This doesn't seem to be doing anything
$("#candidate-row-" + result.rejectedCandidateId).remove();
} else {
//$("#one-jc-messages").html(result.error);
}
});
});
The elements .one-rc-button and #candidate-row-<candidateId> were loaded by a previous AJAX call and they are attached to the document as I can very well see them on my page.
Now, on click of the previously generated .one-rc-button, I trigger a second AJAX call (which works fine) and on result.success, I want to delete the #candidate-row-<candidateId> (which is within the previously generated parent element).
The alert works and returns 1. So I know for sure that the selector is fine and it is matching one unique element.
What I don't understand is why it is unable to remove the element from the page.
Observations
I use Firefox 10.0.2 where this problem is reproducible.
On IE 8, it works (element gets removed)
On debugging the script on Firebug, I can verify that I have got a handle to the right eleemnt.
Try using FireBug to set a breakpoint on that line so you can see exactly what it's getting from that selector. Ideally break up the statement first, like this:
var unwantedDiv = $("#candidate-row-" + result.rejectedCandidateId);
unwantedDiv.remove(); // <-- Set a breakpoint on this line
You can then look at the unwantedDiv variable in the watch pane on the right of the firebug debugger and see what it is, what methods it has/has not got etc. I would assume that you are not getting back exactly what you think you are, possibly because of how you attached the div after the previous AJAX call. More information about JavaScript debugging with FireBug here.
Another option is to turn on strict warnings in the firebug console and see if you get any 'undefined method' errors, which don't stop the show on FireFox, but just bounce you out of that function. Do you get an error in IE?
Solved it by a really ugly workaround. I am still not sure what causes this behaviour.
if(result.success) {
var removeThis = $("#candidate-row-" + result.rejectedCandidateId);
removeThis.remove();
removeThis = $("#candidate-row-" + result.rejectedCandidateId);
if(removeThis.length != 0) {
removeThis.remove();
}
}
Now it works on both Firefox and IE.
The page i have is very simple, but all of the links are loaded with ajax, what i am wondering is there anyway to load an external script once one of the links is clicked, but make sure that the script is only loaded once? Basically just for the sake of performance.
You can load an external script by creating a script element and appending it to head:
var script=$("<script>");
script.attr("type", "text/javascript");
script.attr("src", "some_external_script.js");
script.appendTo("head");
You can use a simple variable to make sure it's only included once.
var addedExternalScript=false;
// ...somewhere else where loadedExternalScript is still in scope...
if(!addedExternalScript) {
// load the script
}
// script is in the document
Note that having the script element in the document won't necessarily mean it's loaded. You may need to bind to the load event if you want to know when the script has loaded.
Is this code helpful to you?
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'script.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(s);
I used this code before, and it worked:
var t=document;
var o=t.createElement('script');
o=t.standardCreateElement('script');
o.setAttribute('type','text/javascript');
o.setAttribute('src','http://www.example.com/js/jquery-1.3.2.js');
t.lastChild.firstChild.appendChild(o);