Modify javascript that was downloaded as part of a web page - javascript

I would like to modify the Javascript that is downloaded as part of a web page.
I have this page:
https://discussions.apple.com/thread/7629335
I would like to modify the function jspaginate.init. I've gotten this far:
console.log(window.jspaginate)
Object { data: Object, loading: false, init: jspaginate.init(), update: jspaginate.update(), pushState: jspaginate.pushState(), loadingSequence: jspaginate.loadingSequence(), removeLoading: jspaginate.removeLoading(), updateUI: jspaginate.updateUI(), getData: jspaginate.getData() }
undefined
console.log(window.jspaginate["init"])
function jspaginate.init()
console.log(window.jspaginate["init"].toString())
function (action, last){
var view = this,
target, current;
... clipped ...
background:
This page includes lots of javascript. The function jspaginate is download from the site server. Nevertheless, I need to change the function jspaginate. I do not have access to the server. I want to change my copy of jspaginate. I know that I need to change the function every time it is downloaded.
I'm using GreaseMonkey to insert some javascript.

Just override the init function with its new definition, like below:
window.jspaginate.init = function() {
console.log('there you go');
}

Here is the code I used. Since I had converted jspaginate.init to a string and modified the string, I had to convert the string to a function. I did the conversion via the eval function.
var debug = 1;
/* Get the string of the function to change */
var stringed = jspaginate["init"].toString();
if ( debug ) console.log ("--> cloneGotoChange: \n" + stringed);
/* Place the added code as the last statement in the function jspaginate["init"]
Find the function closing } */
position = stringed.lastIndexOf("}");
/* Change the downloaded javascript */
var newCode = stringed.substr(0, position)
+ " console.log (\"--> before \" ); cloneGotoUpate(); console.log (\"--> after \" ); "
+ stringed.substr(position);
/* Inject the changed code
need to use the eval function to make a function assignment instead of a string assignment */
eval( 'jspaginate["init"] =' + newCode);
if ( debug ) console.log (console.log(window.jspaginate["init"].toString()));

Related

javascript var into onClick in html

I have a var I need to be sent as a JSON string for AS3. It has to be sent as printed out in the link. How can I get my var to be in the onClick of an html as seen below?
<script>
var NeedMyVarHere = {
One:"This is var one!",
Two:"This is JSON Object 2"
};
NeedMyVarHere = JSON.stringify(NeedMyVarHere);
</script>
<a href="javascript:void(0);" onclick="AirBridge.evoke('myCustomFunction(NeedMyVarHere)')" return false;>Testing!</a>
UPDATE to clarify.
The AS requires a "string be sent" but you can declare a custom function in a string. I didn't write the class. I just need a JSON object written in String format inside my onClick. Like this:
onClick="AirBridge.evoke('myCustomFunction({One:"This is var one!",Two:"This is var Two"})"
But I am hoping to use a single var to print it out like...
onClick="AirBridge.evoke('myCustomFunction(NeedMyVarHere)"
I hope that makes sense.
This is explained in details here https://github.com/myflashlab/webView-ANE/issues/48 but yet, here's a copy:
in your JS, create a button or link to call a JS function
<button type="button" onClick="sendComplexDataToAir()">Send Complex Data To Air</button>
in your js code, create your Json and send it back to Air like this:
function sendComplexDataToAir()
{
var myJSONObject =
{
"someData":
[
{"var1": "value 1"},
{"var2": "value 2"},
{"var3": "value 3"},
{"var3": "As long as you have a valid Json in JS, you can have it received on the Air side with no problem!"}
]
};
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
var jsonStr = JSON.stringify(myJSONObject);
// http://www.w3schools.com/jsref/jsref_encodeURI.asp
var encodedStr = encodeURIComponent(jsonStr);
// and finally send it to flash
AirBridge.evoke('parseJson('+ encodedStr +')');
}
then, receive it on the Air side like this
private function onReceivedMassage(e:RichWebViewEvent):void
{
DynamicFunc.run(this, e.param);
}
public function parseJson($str:String):void
{
trace(decodeURIComponent($str));
}
Better to use unobtrusive JavaScript using addEventListener. But for this solution, I would say:
onclick="AirBridge.evoke('myCustomFunction(NeedMyVarHere)')" return false;
//---------------------------------------------------------^ " prematurely closed.
Change to:
onclick="AirBridge.evoke('myCustomFunction(' + NeedMyVarHere.One + ')'); return false;"
Or:
onclick="AirBridge.evoke('myCustomFunction(NeedMyVarHere)'); return false;"
Can you not do something like illustrated below?
<script>
var NeedMyVarHere = {
One:"This is var one!",
Two:"This is JSON Object 2"
};
NeedMyVarHere = JSON.stringify(NeedMyVarHere);
function onClickFunction() {
Airbridge.evoke(myCustomFunction(NeedMyVarHere));
}
</script>
Testing!
This way the onClick-function doesn't need to accept an argument but can call the parameterless function 'onClickFunction' which then calls the function (with the argument) you wanted to call in the first place.
The problem with your code is it is not passing the reference to the variable since it is just a hardcoded string. Since it needs to be a string, you are better off using addEventListener and build the string instead of an inline event.
xxxx
and the script
document.getElementById("myLink").addEventListener("click", function (e) {
AirBridge.evoke("myCustomFunction('" + JSON.stringify(NeedMyVarHere) + "')");
return false; // or e.preventDefault();
});
Make sure this line runs after the element is added to the page (document ready, onload, or just in the body after the element.)

How to use an external variable in MongoDB 'where' query via Javascript?

I have a MongoDB query that searches all properties for a value defined in the search variable. It works the following way:
db.collection.findOne({
$where: function() {
var search = 'searchstring';
for (var key in this) {
if (this[key] === search) {
return true;
}
return false;
}
}
});
However, I would like to define the search variable outside the query.
But when I do so, I get an error that it is not referenced (i.e. scoping issue):
"ReferenceError: search is not defined near '[key] === search
How can I use or pass the variable to the query filter?
You can try something like this:
var searchstring = 'whatever';
var params = {};
params.$where = 'function() {' +
'var search = "' + searchstring + '";' +
'for (var key in this) {' +
'if (this[key] === search) {' +
'return true;' +
'}' +
'return false;' +
'}' +
'}';
db.collection.findOne(params);
(Stringify your function and concat with external variable)
Worked for me via mongoose
search is a variable that you define in your client, may be the shell, or any client API.
The function that you define for the $where clause, will not be executed on the client side, but on the mongodb server.
so, when the function is being interpreted in the server side and it looks for the search variable, it was never defined on the server and hence you get the error.
In your case, you want the variable search, to be replaced with its content, by the client, before being executed on the server. And this is not possible, unless you build the function content itself in the client side.
The client never really interprets anything you write inside the anonymous function. The server does. The error you see is from the server. You can validate it by firing this query and looking onto the server logs:
2015-12-22T19:03:44.011-0800 I QUERY [conn1] assertion 16722 ReferenceError:
searc is not defined
at _funcs1 (_funcs1:1:39) near 's.key === searc){retu' ns:test.t query:{ $w
here: function (){if(this.key === searc){return true}} }
There is a better way to write what you wish to achieve using the $exists operator.
var search = "title";
var find = {};
find[search] = {$exists:true};
db.collection.findOne(find);
This works, because, you build the query parameter fully on the client side, before passing it on to the findOne() method.
You can solve this problem using mongodb map reduce and scope functionality. Scope allows you to pass variables into map reduce job.
function map() {
for (var key in this) {
if (this[key] === search) {
return emit(this._id, this);
}
}
}
function reduce(key, values) {
return values[0];
}
db.collection.mapReduce(map, reduce, {
out: {inline: 1},
scope: {
search: 'searchstring'
}
}
);

log object in log4javascript

I want to log objects using log4javascript. For example consider the following code:
function LogObject() {
var blah = {
one: 42,
two: "486"
};
logger.Info(blah);
Assuming that logger is instance of log4javascript logger that is properly set up:
var logger = log4javascript.getLogger("InternalLogger");
var ajaxAppender = new log4javascript.AjaxAppender(url),
jsonLayout = new log4javascript.JsonLayout(false, false);
ajaxAppender.setLayout(jsonLayout);
ajaxAppender.addHeader("Content-Type", "application/json");
logger.addAppender(ajaxAppender);
I am expecting the result to the following: request payload contains array of messages first of which is my object serialized into JSON. What I see is array of messages first of which has string "Object object" (like toString() method was invoked). How can I achieve that?
JsonLayout formats the logging event (which includes log level, timestamp and logger name in addition to the log message(s)) as JSON rather than the log message, which is pretty much assumed to be a string. The reason for this is to avoid a dependency on a JSON library for older browsers; generating JSON for the simple, known data that JsonLayout deals with is no problem without a JSON library but handling arbitrary objects definitely requires one.
The workaround I'd suggest is simply to format the message before you pass it to the logging call:
logger.info( JSON.stringify(blah) );
We were following #Tim Down's suggestion
logger.info( JSON.stringify(blah) );
But we had performance issues since the JSON.stringify happens before logger.info is called, therefore it will always happen even if the logging level is set to ignore this log.
In order to work around this I wrote a new lazy layout so that the stringification only happens if the log is actually output. In order to be more flexible it also alows passing a function, in which case it outputs the result of running said function.
Usage:
logger.trace("Received ", widget, " which has ", () => countFrimbles(widget), ' frimbles');
Implementation:
function LazyFormatLayout() { }
LazyFormatLayout.prototype = new log4javascript.Layout();
LazyFormatLayout.prototype.format = function (loggingEvent) {
var time = loggingEvent.timeStamp.toTimeString().split(/\s/)[0];
var head = time + ' ' + loggingEvent.logger.name + ' [' + loggingEvent.level.name + '] - ';
var body = loggingEvent.messages.map(function (arg) {
try {
switch (typeof (arg)) {
case 'function':
return arg();
case 'object':
return JSON.stringify(arg);
}
}
catch (e) {
return '<<error while logging: ' + e.stack + '>>';
}
return arg;
}).join('');
if (!loggingEvent.exception)
return head + body;
return head + body + ' ==> Exception: ' + loggingEvent.exception.stack;
}
LazyFormatLayout.prototype.ignoresThrowable = function () { return false; };
LazyFormatLayout.prototype.toString = function () { return "LazyFormatLayout"; };
Question is somewhat dated, but a simple google search turned up this question and there seems to be a build-in way to log objects:
var log = log4javascript.getDefaultLogger();
log.info("log following object",{ data:5, text:"bla" });
output
12:49:43 INFO - log following object {
data: 5,
text: bla
}

How do I use a custom function in background.js for a Chrome Extension?

I'm writing a Chrome extension and I want to call a function in my background.js.
This is the function:
function getUrlVars(url) {
var vars = {};
var parts = url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
if(vars[key]){
if(vars[key] instanceof Array)
vars[key].push(value);
else
vars[key] = [vars[key], value];
}else
vars[key] = value;
});
return vars;
}
It returns a parameter of the url.
I put this function in background.js, but when I call it, it doesn't work.
I call the function here:
chrome.webRequest.onBeforeRequest.addListener(function(details){
chrome.tabs.get(details.tabId, function (tab) {
source=getUrlVars(details.url)[iapisource];
id=getUrlVars(details.url)[iapiid];
redirectUrl=tab.url+"?iapisource="+source+"&iapiid="+id;
});
return {cancel : true , redirectUrl : redirectUrl};
},
// block requests matching this url
{urls: ["*://*/*iapisource*iapiid*"]},["blocking"]);
Here I take the URL before the request and append to it the parameters of the new URL.
Where do I have to put the function and how can I call it?
I could give you a fish but I'll teach you how to fish instead.
1) You should debug your code if it doesn't work. StackOverflow is not an online debugger.
Go to chrome://extensions/, find your extensions, click on your background page, go to the Console tab and investigate the errors you see there.
2) For one thing instead of this:
source=getUrlVars(details.url)[iapisource];
I think you what you wanted is this:
source=getUrlVars(details.url)['iapisource'];
or better yet:
var params = getUrlVars(details.url);
var redirectUrl = tab.url+"?iapisource=" + params.iapisource + "&iapiid=" + params.iapiid;
3) The tabs.get callback function will only run after you've already returned from the onBeforeRequest
// 1.
chrome.webRequest.onBeforeRequest.addListener(function(details){
// 2.
chrome.tabs.get(details.tabId, function (tab) {
// 4.
source=getUrlVars(details.url)[iapisource];
id=getUrlVars(details.url)[iapiid];
redirectUrl=tab.url+"?iapisource="+source+"&iapiid="+id;
});
// 3.
return {cancel : true , redirectUrl : redirectUrl}; // redirectUrl is undefined!
},
{urls: ["*://*/*iapisource*iapiid*"]},
["blocking"]
);
You can use extension.getBackgroundPage to gain access to the window object of your background page which would normally contain your global functions in the background page.
This issue is further discussed here.
If you can't do that, you should look into message passing.

Why does the DOM remain unchanged, even though the console outputs what the correct result should be when trying to change link attributes?

function makeLinks(context, url) {
var sets = context ? $(context + ' a') : $('a'),
prot = url || window.location.protocol;
if(prot.match(/https/)){
lockup(sets);
}
function lockup(elem) {
elem.each(function(){
var self = $(this),
href = self.attr('href');
if(href.match(/http[^s]/)){
// This logs the correct output
console.log(href.replace('http','https'));
// This fails
href.replace('http','https');
}
});
}
}
The purpose of the function is to check the current protocol of the window object.
If it is 'https:', then I want the anchors that have a href of 'http' to be 'https'.
The function is called like this: makeLinks('#wrapper', 'https:');
The second parameter is just for testing, otherwise it would use window.location.protocol
Currently, when I call this function on markup that looks like this:
<div id="wrapper">
</div>
The console logs exactly what I want to happen, however, the actual href remains unchanged inside of the DOM.
I've tested this out in Chrome and Firefox with the same result
I'm pretty sure that my logic is wrong somewhere (or everywhere)
If someone can help me understand why, I would be very appreciative :)
You're not doing anything with the result of the .replace().
You need to set the new value.
self.attr('href', href.replace('http','https'));
or a nicer way is like this:
function lockup(elem) {
elem.attr('href', function(i, href) {
return href.match(/http[^s]/) ? href.replace('http','https') : href;
});
}
Should this not be something Like Below
if(href.match(/http[^s]/)){
// This logs the correct output
console.log(href.replace('http','https'));
// This fails
href.replace('http','https');
}
});
Change to:
if(href.indexOf('https') == -1) {
console.log(href.replace('http','https'));
href.replace('http', 'https');
}

Categories

Resources