jQuery deferred Script loading, Access App Variable - performance

This is my bootstrap:
LanesBoard2010 = (function () {
var appRoot = this;
appRoot.deferredLoad = new Object(); //holding jqxhr states
appRoot.utils = {}; //namespace for helper classes
appRoot.models = {};
appRoot.data = (function () { } ()); //store data from webservices here
appRoot.app = (function () { } ()); //ViewModel
}();
After some initialisation i run my deferred app loader:
function appStart() {
appRoot.deferredLoad.app = $.getScript('../TaskBoardContent/Script/app/lanesboard.js');
$.when.apply($, appRoot.deferredLoad.app).then(function () {
if (window.console)
if (debug)
console.log('application lanesdashboard was loaded successfully\n');
}, function () {
if (window.console)
if (debug)
console.log('fatal error: unable to load application lanesdashboard\n');
});
};
Now i need to access appRoot, or more general, modify the properties of LanesBoard2010.
the following statement is from my lanesboard.js. It just fails after the first console.log, as i can see both variable names are unknown:
(function () {
if (window.console)
if (true) {
console.log('App has been initialised successfully');
console.log('Status of app access 1: ' + appRoot);
console.log('Status of app access 2: ' + LanesBoard2010);
}
}());
May sound stupid, but how can i safely access my variables? Is there any best practice? How would you do this?

What exactly is your question? How you can prevent to use an undefined variable?
You can either define it before any calls and initialise it to null (var appRoot = LanesBoard2010 = null)
or check the variable before you call console.log() (if (appRoot) console.log('Status of app access 1: ' + appRoot);).
I normally use the first suggestion.
I first set all my caches to null, create the autocompletes, then use $.ajax to load them and when loaded, update the autocomplete sources.

Related

how to integrate google-recaptcha in oracle-jet

I am trying to integrate google-recaptcha but no success.
Getting error
feedback.js:39 Uncaught TypeError: grecaptcha.render is not a function
main.js
'googlerecaptcha':'https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit',
define(['ojs/ojcore', 'knockout', 'jquery', 'appController', 'ckeditor', 'googlerecaptcha', 'ojs/ojlabel',
'ojs/ojknockout', 'ojs/ojinputtext', 'ojs/ojformlayout'],
function (oj, ko, $, app, ckeditor, grecaptcha) {
/**
* The view model for the main content view template
*/
function feedbackViewModel() {
var self = this;
// For small screens: labels on top
// For medium screens and up: labels inline
this.labelEdge = ko.computed(function () {
return app.smScreen ? "top" : "start";
}, this);
onloadCallback = function (a) {
grecaptcha.render('submit', {
'sitekey': 'YOUR_API_KEY',
'callback': self.onSubmit
}, true);
};
this.handleActivated = function (info) {
};
self.onSubmit = function (token) {
console.info("google recatpcha onSubmit", token)
//do validation/application code using token
var data = {secret: grecaptcha, response: recaptchaToken};
$.post({
url: "https://www.google.com/recaptcha/api/siteverify",
form: data
}).then(function (e) {
//recaptcha service called...check result
var resp = JSON.parse(e);
if (resp.success == false) {
console.info("recaptcha token outcome is false")
} else {
console.info("recaptcha token validated")
}
});
};
}
return feedbackViewModel;
});
Do you have a mapping for 'googlerecaptcha' in src/js/path_mapping.json? If I go to https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit, I do not see that it is returning any valid object. So most likely 'grecaptcha' variable is undefined.
reCaptcha + RequireJS
Looks like reCaptcha is a function that has to be executed vs an object that can be interacted with directly. So you may need a different approach, something mentioned in this thread.

Jasmine spyOn function call inside function under test

I am trying to use Jasmine to test the following function:
var Pdba = Class.create();
Pdba.prototype = {
getChangeGroup: function(userId) {
var query = 'active=true^u_change_group=true^u_organization=false^';
var exGroup = new CompanyGroup();
var groups = exGroup.getGroupsByQuery(userId, query); //want to spy/mock this call
if (groups.next()) {
return groups.sys_id.toString();
}
return '';
}
type: 'Pdba'
};
I want to SpyOn the getGroupsByQuery() call, so that it doesn't make the actual call. Below is a collection of various things I have been trying, mostly just to see if I can "spy" and see that it has been called, then work on overriding so that I can replace the call with my own data.
describe('my suite of getChangeGroup tests', function() {
var expPdba;
var validUserId = 'user1';
var expGrp;
var ggbqMoc
beforeEach(function() {
expPdba = new global.Pdba();
coGrp = new CompanyGroup();
spyOn(coGrp, 'getGroupsByQuery');
ggbqMoc = jasmine.createSpy('getGroupsByQuery');
});
it('should return \'\' for empty userId', function() {
coPdba.getChangeGroup('');
expect(coGrp.getGroupsByQuery).toHaveBeenCalled();
expect(ggbqMoc).toHaveBeenCalled();
});
});
Is this possible or do I need to change the function under test to take a 'CompanyGroup' as a parameter?
Thank you
I'm assuming you are using jasmine v3. The syntax for creating a spy is pretty weird now- you have to pass a string that refers to the name of the variable you want to create a spy for, and then you pass an array of function names that should be spied on.
Try this:
describe('my suite of getChangeGroup tests', function() {
var expPdba;
var validUserId = 'user1';
var expGrp;
var spy;
beforeEach(function() {
expPdba = new global.Pdba();
coGrp = new CompanyGroup();
spy = jasmine.createSpyObj('coGrp', ['getGroupsByQuery'])
});
it('should return \'\' for empty userId', function() {
coPdba.getChangeGroup('');
expect(coGrp.getGroupsByQuery).toHaveBeenCalled();
expect(spy).toHaveBeenCalled();
});
});

Jasmine testing, using a constructor in the beforeEach

i am attempting to create a new instance of two classes that i have already written in separate files. when i try to create new instances of them in the beforeEach() section of the test code, the tests return undefined for my newly created objects. however when i create them in each IT section the test run perfectly.
describe("placeStone", function() {
beforeEach(function() {
var go = new Display();
var logic = new Internals(go);
logic.tempBoard = [ array];
});
it("should place a black stone at 0,6", function() {
logic.placeStone(logic.black,0,6);
expect(logic.tempBoard[6][0]).toEqual("B");
});
this returns logic undefined.
describe("placeStone", function() {
it("should place a black stone at 0,6", function() {
var go = new Display();
var logic = new Internals(go);
logic.tempBoard = [ array];
logic.placeStone(logic.black,0,6);
expect(logic.tempBoard[6][0]).toEqual("B");
});
});
this seems to work the way i want. how can i get it to work in the beforeEach() section?
var logic should be defined in the scope of the describe function, then it exists both in the scope of the beforeEach function and the spec (the it function), e.g.
describe('suite', function () {
var myVar;
beforeEach(function(){
myVar = 10;
});
it('checks myVar', function () {
expect(myVar).toEqual(10);
});
});

How can I catch and process the data from the XHR responses using casperjs?

The data on the webpage is displayed dynamically and it seems that checking for every change in the html and extracting the data is a very daunting task and also needs me to use very unreliable XPaths. So I would want to be able to extract the data from the XHR packets.
I hope to be able to extract information from XHR packets as well as generate 'XHR' packets to be sent to the server.
The extracting information part is more important for me because the sending of information can be handled easily by automatically triggering html elements using casperjs.
I'm attaching a screenshot of what I mean.
The text in the response tab is the data I need to process afterwards. (This XHR response has been received from the server.)
This is not easily possible, because the resource.received event handler only provides meta data like url, headers or status, but not the actual data. The underlying phantomjs event handler acts the same way.
Stateless AJAX Request
If the ajax call is stateless, you may repeat the request
casper.on("resource.received", function(resource){
// somehow identify this request, here: if it contains ".json"
// it also also only does something when the stage is "end" otherwise this would be executed two times
if (resource.url.indexOf(".json") != -1 && resource.stage == "end") {
var data = casper.evaluate(function(url){
// synchronous GET request
return __utils__.sendAJAX(url, "GET");
}, resource.url);
// do something with data, you might need to JSON.parse(data)
}
});
casper.start(url); // your script
You may want to add the event listener to resource.requested. That way you don't need to way for the call to complete.
You can also do this right inside of the control flow like this (source: A: CasperJS waitForResource: how to get the resource i've waited for):
casper.start(url);
var res, resData;
casper.waitForResource(function check(resource){
res = resource;
return resource.url.indexOf(".json") != -1;
}, function then(){
resData = casper.evaluate(function(url){
// synchronous GET request
return __utils__.sendAJAX(url, "GET");
}, res.url);
// do something with the data here or in a later step
});
casper.run();
Stateful AJAX Request
If it is not stateless, you would need to replace the implementation of XMLHttpRequest. You will need to inject your own implementation of the onreadystatechange handler, collect the information in the page window object and later collect it in another evaluate call.
You may want to look at the XHR faker in sinon.js or use the following complete proxy for XMLHttpRequest (I modeled it after method 3 from How can I create a XMLHttpRequest wrapper/proxy?):
function replaceXHR(){
(function(window, debug){
function args(a){
var s = "";
for(var i = 0; i < a.length; i++) {
s += "\t\n[" + i + "] => " + a[i];
}
return s;
}
var _XMLHttpRequest = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
this.xhr = new _XMLHttpRequest();
}
// proxy ALL methods/properties
var methods = [
"open",
"abort",
"setRequestHeader",
"send",
"addEventListener",
"removeEventListener",
"getResponseHeader",
"getAllResponseHeaders",
"dispatchEvent",
"overrideMimeType"
];
methods.forEach(function(method){
window.XMLHttpRequest.prototype[method] = function() {
if (debug) console.log("ARGUMENTS", method, args(arguments));
if (method == "open") {
this._url = arguments[1];
}
return this.xhr[method].apply(this.xhr, arguments);
}
});
// proxy change event handler
Object.defineProperty(window.XMLHttpRequest.prototype, "onreadystatechange", {
get: function(){
// this will probably never called
return this.xhr.onreadystatechange;
},
set: function(onreadystatechange){
var that = this.xhr;
var realThis = this;
that.onreadystatechange = function(){
// request is fully loaded
if (that.readyState == 4) {
if (debug) console.log("RESPONSE RECEIVED:", typeof that.responseText == "string" ? that.responseText.length : "none");
// there is a response and filter execution based on url
if (that.responseText && realThis._url.indexOf("whatever") != -1) {
window.myAwesomeResponse = that.responseText;
}
}
onreadystatechange.call(that);
};
}
});
var otherscalars = [
"onabort",
"onerror",
"onload",
"onloadstart",
"onloadend",
"onprogress",
"readyState",
"responseText",
"responseType",
"responseXML",
"status",
"statusText",
"upload",
"withCredentials",
"DONE",
"UNSENT",
"HEADERS_RECEIVED",
"LOADING",
"OPENED"
];
otherscalars.forEach(function(scalar){
Object.defineProperty(window.XMLHttpRequest.prototype, scalar, {
get: function(){
return this.xhr[scalar];
},
set: function(obj){
this.xhr[scalar] = obj;
}
});
});
})(window, false);
}
If you want to capture the AJAX calls from the very beginning, you need to add this to one of the first event handlers
casper.on("page.initialized", function(resource){
this.evaluate(replaceXHR);
});
or evaluate(replaceXHR) when you need it.
The control flow would look like this:
function replaceXHR(){ /* from above*/ }
casper.start(yourUrl, function(){
this.evaluate(replaceXHR);
});
function getAwesomeResponse(){
return this.evaluate(function(){
return window.myAwesomeResponse;
});
}
// stops waiting if window.myAwesomeResponse is something that evaluates to true
casper.waitFor(getAwesomeResponse, function then(){
var data = JSON.parse(getAwesomeResponse());
// Do something with data
});
casper.run();
As described above, I create a proxy for XMLHttpRequest so that every time it is used on the page, I can do something with it. The page that you scrape uses the xhr.onreadystatechange callback to receive data. The proxying is done by defining a specific setter function which writes the received data to window.myAwesomeResponse in the page context. The only thing you need to do is retrieving this text.
JSONP Request
Writing a proxy for JSONP is even easier, if you know the prefix (the function to call with the loaded JSON e.g. insert({"data":["Some", "JSON", "here"],"id":"asdasda")). You can overwrite insert in the page context
after the page is loaded
casper.start(url).then(function(){
this.evaluate(function(){
var oldInsert = insert;
insert = function(json){
window.myAwesomeResponse = json;
oldInsert.apply(window, arguments);
};
});
}).waitFor(getAwesomeResponse, function then(){
var data = JSON.parse(getAwesomeResponse());
// Do something with data
}).run();
or before the request is received (if the function is registered just before the request is invoked)
casper.on("resource.requested", function(resource){
// filter on the correct call
if (resource.url.indexOf(".jsonp") != -1) {
this.evaluate(function(){
var oldInsert = insert;
insert = function(json){
window.myAwesomeResponse = json;
oldInsert.apply(window, arguments);
};
});
}
}).run();
casper.start(url).waitFor(getAwesomeResponse, function then(){
var data = JSON.parse(getAwesomeResponse());
// Do something with data
}).run();
I may be late into the party, but the answer may help someone like me who would fall into this problem later in future.
I had to start with PhantomJS, then moved to CasperJS but finally settled with SlimerJS. Slimer is based on Phantom, is compatible with Casper, and can send you back the response body using the same onResponseReceived method, in "response.body" part.
Reference: https://docs.slimerjs.org/current/api/webpage.html#webpage-onresourcereceived
#Artjom's answer's doesn't work for me in the recent Chrome and CasperJS versions.
Based on #Artjom's answer and based on gilly3's answer on how to replace XMLHttpRequest, I have composed a new solution that should work in most/all versions of the different browsers. Works for me.
SlimerJS cannot work on newer version of FireFox, therefore no good for me.
Here is the the generic code to add a listner to load of XHR (not dependent on CasperJS):
var addXHRListener = function (XHROnStateChange) {
var XHROnLoad = function () {
if (this.readyState == 4) {
XHROnStateChange(this)
}
}
var open_original = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, unk1, unk2) {
this.requestUrl = url
open_original.apply(this, arguments);
};
var xhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
var xhr = this;
if (xhr.addEventListener) {
xhr.removeEventListener("readystatechange", XHROnLoad);
xhr.addEventListener("readystatechange", XHROnLoad, false);
} else {
function readyStateChange() {
if (handler) {
if (handler.handleEvent) {
handler.handleEvent.apply(xhr, arguments);
} else {
handler.apply(xhr, arguments);
}
}
XHROnLoad.apply(xhr, arguments);
setReadyStateChange();
}
function setReadyStateChange() {
setTimeout(function () {
if (xhr.onreadystatechange != readyStateChange) {
handler = xhr.onreadystatechange;
xhr.onreadystatechange = readyStateChange;
}
}, 1);
}
var handler;
setReadyStateChange();
}
xhrSend.apply(xhr, arguments);
};
}
Here is CasperJS code to emit a custom event on load of XHR:
casper.on("page.initialized", function (resource) {
var emitXHRLoad = function (xhr) {
window.callPhantom({eventName: 'xhr.load', eventData: xhr})
}
this.evaluate(addXHRListener, emitXHRLoad);
});
casper.on('remote.callback', function (data) {
casper.emit(data.eventName, data.eventData)
});
Here is a code to listen to "xhr.load" event and get the XHR response body:
casper.on('xhr.load', function (xhr) {
console.log('xhr load', xhr.requestUrl)
console.log('xhr load', xhr.responseText)
});
Additionally, you can also directly download the content and manipulate it later.
Here is the example of the script I am using to retrieve a JSON and save it locally :
var casper = require('casper').create({
pageSettings: {
webSecurityEnabled: false
}
});
var url = 'https://twitter.com/users/username_available?username=whatever';
casper.start('about:blank', function() {
this.download(url, "hop.json");
});
casper.run(function() {
this.echo('Done.').exit();
});

backbone console log event triggering

I've got a underscore/backbone/require application and I would like to output all events that are triggered through backbone to the console (in other words: pass each event through console.log function). I've tried wrapping it with underscore and manually replacing the function. Neither this:
console.log(Backbone.Events.trigger);
var trigger = Backbone.Events.trigger;
Backbone.Events.trigger = function(name) {
console.log('Event', name, 'triggered.');
trigger.apply(this, arguments);
}
nor this:
Backbone.Events.trigger = _.wrap(Backbone.Events.trigger, function(func) {
console.log('EVENT:', Array.prototype.slice.call(arguments));
func(Array.prototype.slice.call(arguments));
});
console.log(Backbone.Events.trigger);
worked. I'd appreciate a javascript (not coffeescript) solution.
Your wrappings fail because Backbone mixes in Backbone.Events behavior on Backbone.Model, Backbone.Collection, etc. For example, Backbone.Model is defined as
var Model = Backbone.Model = function(attributes, options) {
...
};
_.extend(Model.prototype, Events, {
...
};
This means that when you redefine Backbone.Events.trigger, it is already too late.
But all is not lost! You won't be able to redefine all trigger methods in one go, but you can redefine them on class level:
Backbone.Model.prototype.trigger = function() {
console.log('Event', arguments);
Backbone.Events.trigger.apply(this, arguments);
}
and a demo http://jsfiddle.net/nikoshr/G2Qfn/
For a given class, you can override the trigger method:
var M = Backbone.Model.extend({
trigger: function() {
console.log('Event', arguments);
Backbone.Model.prototype.trigger.apply(this, arguments);
}
});
http://jsfiddle.net/nikoshr/G2Qfn/1/
or for a given instance
var M = Backbone.Model.extend({});
var m = new M();
m.trigger = function() {
console.log('Event', arguments);
M.prototype.trigger.apply(this, arguments);
}
http://jsfiddle.net/nikoshr/G2Qfn/2/

Resources