How can I loop over all resources of a web page without Casper Default Time limit of 5 seconds ?
If i loop over Web Page' resources with
casper.waitForResource(function check(resource){
....
});
after 5000ms casper raise a test fail.
casper.waitForResource is not for looping over resources. Instead, it is for waiting for a specific resource that has some property. If you use a check function, you will have access to resources that are seen up to the point when the matching resource is found.
What you want is resource.received or similar event listeners. The question is what you want to do with the resource information. Keep in mind that CasperJS and the underlying PhantomJS do not expose the resource content. You will need to download it separately with __utils__.sendAJAX inside of the page context.
If you want the resource list right in the test flow for resources after a specific action, you could do something like this:
var resources = [],
collectResources = false;
casper.on('resource.received', function(resource) {
if (!collectResources) { return; }
resources.push(resource);
});
// later...
casper.then(function(){
collectResources = true;
this.click("#someAction");
}).wait(5000).then(function(){
collectResources = false;
resources.forEach(function(resource){
// do something with it
});
});
Related
I'm working on a map project under React, using react-leaflet, and leaflet-pixi-overlay. Markers are implemented using the PIXI overlay (React 16.13.1, pixi.js 5.3.0, leaflet 1.6.0, leaflet-pixi-overlay 1.8.1).
I am struggling a bit with the PIXI documentation. I would like to use this PIXI.Texture.fromURL method (http://pixijs.download/release/docs/PIXI.Texture.html#.fromURL)
However neither my VS Code environment, nor my compiled source can access this method.
I am using instead PIXI.Texture.from(imageUrl), as well as PIXI.Texture.fromLoader(imageUrl). Both seem to work, but I don't get the difference between the two? The docs don't show these as being promises, yet they seem to work well with an async await?
Then, when a load fails, I don't see how to tell that things went wrong. Actually what I don't see is how to tell that things went right!
If I do:
let failed = false;
let newTexture;
try {
newTexture = await PIXI.Texture.from(url);
} catch (err) {
console.log(`FAILED loading texture from ${url}, err=${err}`);
failed = true;
}
console.log(`valid=${texture.valid}`);
Then:
texture.valid is always false, even when the texture loaded and displays just fine
no error is ever thrown when the url points to nowhere
Any pointers, is there a site with good (recent as of 2020) PIXI references? Am I missing something basic? Thanks.
Edit 07/06/2020:
Issues were largely due to both my IDE and webpack not 'seeing' that I had update pixi.js to 5.3.0, restart of both gave me access to Texture.fromURL.
The Texture.from call is a synchronous one. My understanding is that by default, it will load a 'valid' texture of 1x1 px in case of failure. Texture.fromURL was added to provide an async solution, see https://github.com/pixijs/pixi.js/issues/6514
Looks to me like Texture.fromURL still needs a bit of work, as it doesn't seem to ever return on a failed fetch (at least for relative paths, which is what I use). I see the same thing when using the following approach:
const texture = PIXI.Texture.from(url, {
resourceOptions: { autoLoad: false }}
);
await texture.baseTexture.resource.load();
With a bad relative path, the load function never returns in my test environment.
Then, when a load fails, I don't see how to tell that things went wrong. Actually what I don't see is how to tell that things went right! If I do:
...
Then:
texture.valid is always false, even when the texture loaded and displays just fine
no error is ever thrown when the url points to nowhere
Ok, first thing: please use the newest version of PIXI which is now 5.3.0: https://github.com/pixijs/pixi.js/releases/tag/v5.3.0
Texture.fromUrl was added in this PR: https://github.com/pixijs/pixi.js/pull/6687/files . Please read description of this PR: https://github.com/pixijs/pixi.js/pull/6687 - user bigtimebuddy describes 3 ways to load Texture synchronously. From this you should understand why it didnt worked in your code.
Also see: https://gamedev.stackexchange.com/questions/175313/determine-when-a-pixi-texture-is-loaded-to-clone-it
About error handling, catching errors and checking if Texture is "valid": please try running following example (modified version of yours) :
let failed = false;
let newTexture;
try {
newTexture = await PIXI.Texture.fromURL('https://loremflickr.com/100/100');
// to see how failure works comment above line and uncomment line below:
// newTexture = await PIXI.Texture.fromURL('http://not-existing-site-0986756.com/not_existing.jpg');
} catch (err) {
console.log(`FAILED loading texture`);
console.log(err);
failed = true;
}
console.log('failed: ' + (failed ? 'yes' : 'no'));
console.log(`valid=${typeof newTexture !== 'undefined' ? newTexture.valid : 'variable newTexture is undefined'}`);
console.log(newTexture ? newTexture : 'n/a');
And lastly about method not found in IDE:
I would like to use this PIXI.Texture.fromURL method (http://pixijs.download/release/docs/PIXI.Texture.html#.fromURL)
However neither my VS Code environment, nor my compiled source can access this method.
I use PhpStorm (but other IntelliJ editor should be similar - for example: WebStorm) and it finds this method:
/**
* Useful for loading textures via URLs. Use instead of `Texture.from` because
* it does a better job of handling failed URLs more effectively. This also ignores
* `PIXI.settings.STRICT_TEXTURE_CACHE`. Works for Videos, SVGs, Images.
* #param {string} url The remote URL to load.
* #param {object} [options] Optional options to include
* #return {Promise<PIXI.Texture>} A Promise that resolves to a Texture.
*/
Texture.fromURL = function (url, options) {
var resourceOptions = Object.assign({ autoLoad: false }, options === null || options === void 0 ? void 0 : options.resourceOptions);
var texture = Texture.from(url, Object.assign({ resourceOptions: resourceOptions }, options), false);
var resource = texture.baseTexture.resource;
// The texture was already loaded
if (texture.baseTexture.valid) {
return Promise.resolve(texture);
}
// Manually load the texture, this should allow users to handle load errors
return resource.load().then(function () { return Promise.resolve(texture); });
};
Do you use the development build of Pixi, or production one? ( https://github.com/pixijs/pixi.js/releases ).
Update 2020-07-06:
Your comment:
One thing still not clear to me though: when using an approach based on PIXI.Loader, do the sprites using a given texture get automatically refreshed once the texture has been loaded, or is there a manual refresh process required?
If you use "PIXI.Loader" approach then you can set the "load" callback - in which you should have all resources / textures already loaded. See: https://pixijs.download/dev/docs/PIXI.Loader.html
First you define which resources need to be loaded:
// Chainable `add` to enqueue a resource
loader.add('bunny', 'data/bunny.png')
.add('spaceship', 'assets/spritesheet.json');
loader.add('scoreFont', 'assets/score.fnt');
and then you define the callback:
// The `load` method loads the queue of resources, and calls the passed in callback called once all
// resources have loaded.
loader.load((loader, resources) => {
// resources is an object where the key is the name of the resource loaded and the value is the resource object.
// They have a couple default properties:
// - `url`: The URL that the resource was loaded from
// - `error`: The error that happened when trying to load (if any)
// - `data`: The raw data that was loaded
// also may contain other properties based on the middleware that runs.
sprites.bunny = new PIXI.TilingSprite(resources.bunny.texture);
sprites.spaceship = new PIXI.TilingSprite(resources.spaceship.texture);
sprites.scoreFont = new PIXI.TilingSprite(resources.scoreFont.texture);
});
You can try this way and inside this callback you can observe that texture of each resource is valid - for example: resources.bunny.texture.valid - it should be true.
Also, as you see in that doc, you can use other more advanced features like middleware or other callbacks for error handling etc.
I could not wrap my head around my problem, which I finally understand I believe.
I must initialize user data from a SQLite database, which I am running through the cordova sqlite plugin. My problem is that reading the DB is in an asynchronous deferrable this.loadData function (below). ( I may be "old fashioned", but my major complaint here - why can't DB access be in a synchronous function like all other sane implementations of databases? Without the deferrable it would be straight forward.)
My controller reacts to $stateChangeSuccess where I display a leaflet.js map, that is immediately started by $urlRouterProvider.otherwise('/app/map'); within the state controller.
My problem is that I do not know how to stall the view controller load until all my data is loaded. I tried putting loading the SQLite data into $ionicPlatform.ready so that load would be triggered immediately, but on the device loading the view overtakes the background SQLite thread.
I also tried to load the data within the module during module instantiation by calling this.loadData() at the end. However, the cordova module would not be initialized until then and fail.
What's the best way? Can I make a custom event that would fire when everything is loaded? Should I add a intermediate view after the splash screen that waits out all progress?
Essentially I have to make the whole app loading procedure wait till my data is loaded and do not know how to handle this when SQLite forces one to make all data access a promise. I really would prefer to have a choice of sync / async DB access somehow.
//this is in a module dataService
this.loadData = function(){
if(!myDB)
myDB = $cordovaSQLite.openDB('my.db');
var deferred = $q.defer();
myDB.transaction(function(transaction1) {
transaction1.executeSql('SELECT ID,Data FROM myDB;', [],
function(tx, result) {
console.log(result.rows.length);
var dataJSON = result.rows.item(0).Data;
var dataParsed = JSON.parse(dataJSON);
deferred.resolve(dataParsed);
},
function(error) {
console.log("SELECT failed. Must create table first");
//do other stuff ...
});
});
return deferred.promise;
}
// in my MapViewController the app reacts to
$scope.$on("$stateChangeSuccess", function() {
// init views with the data that should be available now
}
//this is in the starter module
angular.module('starter',[]).config(function($stateProvider, $urlRouterProvider){
\\... states ...
$urlRouterProvider.otherwise('/app/map');
}
As you return a promise, you have to resolve it on then statament, and inside it you can do a redirect.
loadData
.then(loadDataSuccess)
.catch(loadDataError)
function loadDataSuccess(data){
$state.go('map')
}
function loadDataError(error){
//do stuff
}
So I have a controller to load all the tasks for example:
$scope.tasks = taskService.all;
$scope.tasks.$loaded().then(function(data){
console.log (data);
});
//--------Destroy all the AngularFire connection------//
$scope.$on("$destroy", function(){
$scope.tasks.$destroy();
});
The taskService is as below:
var ref = new Firebase(FirebaseUrl);
var tasks = $firebaseArray(ref.child('tasks'));
var uid = Users.LoginUserId();
var Task = {
all: tasks,
getAllTasks: function() {
console.log ("Run get All tasks.");
return $firebaseArray(ref.child('tasks'));
},
... ...
...
Use $scope.tasks = taskService.all; I think I am getting a tasks array - which is ONE firebaseArray. When I logout using Auth.$unauth(), there is no permission errors. When I log back in and go to the same view, $scope.tasks is a empty array = []. I don't know why the taskService don't go get the tasks firebaseArray again. It will only go get the firebase array when I force refresh the browser.
If I use
$scope.tasks = taskService.getAllTasks();
in the taskService - a method / function to return firebaseArray. This tasks will not be empty array after I logout and log back in. But I can tell when I logout, there are many permission errors - look like there are many firebaseArray(s) each time I come to this view (waste of resources since there are multiple same copy of tasks array?!?)
So which is the right way to handle it? Why using a method in services/factory will produce multiple duplicated firebaseAarray when I navigate around my app and back to the same views? Is that really bad? And why using a factory.value way will cause issue when logout and log back in? I can not find any clear explanation about this. Firebase expert please help.
EDIT FOR FINIAL SOLUTION:
Thanks to #nicolas here is my finial solution:
The code provided by nicolas can't work on my solution since this.$$tasks = [] will still be true. It is not NULL. It is just an empty array. So I have to do this instead:
getAllTasks: function() {
if (tasks.length == 0) {
console.log ("Logout and Log back in. Tasks is empty!");
tasks = $firebaseArray(ref.child('tasks'));
} else {
console.log ("Navigate around. Tasks is NOT empty!");
tasks = tasks;
}
return tasks;
},
tasks is already inside a factory and it is private. I don't think I need to use $$.
when you logout you should basically call $destroy on all your angularfire instances, because each of them keep some socket open. So if you have set up some security rules based on your current user id and you log out , you may break them and get exceptions.
Refering to your task service , i would cache the array once instanciated (since firebase will automatically sync it), and listen to child_added events.
var Task = {
getAllTasks: function() {
this.$$tasks = (this.$$tasks || firebaseArray(ref.child('tasks')));
return this.$$tasks;
}
I would like to track VideoJS using Google Tag Manager. Since I am fairly new to GTM I have no idea how to proceed. I have only done some basic stuff like tracking mailto links and PDF downloads using tutorials.
I found a project on Github called videojs-ga which looks promising, now how would I correctly connect and set this up in GTM? Idealy we would end up tracking the play count and how long the video's get watched. There is no need to track multiple videos per page since there is only one video on a single page at any time.
Also if there is a better way to track VideoJS with GTM I am open to suggestions!
I suggest you make a fork from the code in that repository/ download the file from src and make the changes locally. In line 104 where it says:
sendbeacon = function(action, nonInteraction, value) {
if (window.ga) {
ga('send', 'event', {
'eventCategory': eventCategory,
'eventAction': action,
'eventLabel': eventLabel,
'eventValue': value,
'nonInteraction': nonInteraction
});
} else if (window._gaq) {
_gaq.push(['_trackEvent', eventCategory, action, eventLabel, value, nonInteraction]);
} else if (options.debug) {
console.log("Google Analytics not detected");
}
};
you replace the stuff within the sendbeacton function object in the following way:
sendbeacon = function(action, nonInteraction, value) {
dataLayer.push(
'eventCategory': eventCategory,
'eventAction': action,
'eventLabel': eventLabel,
'event' : 'videojs'
);
};
(make sure your dataLayer variable is declared somewhere before your GTM code). Create a script tag that links the modified script to our page.
Then create three variables of the "dataLayer" type which read their values from eventCategory, eventAction and eventLabel respectively. Set up a Google Analytics tag and configure it for event tracking, and pass in the variables to the respective fields. Create a trigger type custom event, event eq 'videojs' and use it to fire the GA event tracking tag.
I'm developing a web application using GeoExt, OpenLayers and having my own GeoServer to serve various maps. Still, I want to let the user add other WMS's if needed, to be able to play around with all desired layers.
Thus, my problem with the GetFeatureInfo request. Right now I have a toolbar button attached to geoext's map panel,
new GeoExt.Action({
iconCls: "feature",
map: map,
toggleGroup: "tools",
tooltip: "Feature",
control: featureControl
})
its control attribute being
var featureControl = new OpenLayers.Control.WMSGetFeatureInfo({
queryVisible: true,
drillDown: true,
infoFormat:"application/vnd.ogc.gml"
});
I've also defined an event listener to do what I really want once I receive the responses, but that is not relevant here. My problem is the following:
Considering the user clicks on a point where there are 2+ visible layers and at least one of them is from a different source, OpenLayers will have to do one AJAX request per different source and, from OpenLayers own documentation,
Triggered when a GetFeatureInfo response is received. The event
object has a text property with the body of the response (String), a
features property with an array of the parsed features, an xy property
with the position of the mouse click or hover event that triggered the
request, and a request property with the request itself. If drillDown
is set to true and multiple requests were issued to collect feature
info from all layers, text and request will only contain the response
body and request object of the last request.
so, yeah, it will obviously wont work like that right away. Having a look at the debugger I can clearly see that, giving two layers from different sources, it actually DOES the request, it's just that it doesn't wait for the first's response and jumps for the next one (obviously, being asynchronous). I've thought about doing the requests one-by-one, meaning doing the first one as stated above and once it's finished and the response saved, go for the next one. But I'm still getting used to the data structure GeoExt uses.
Is there any API (be it GeoExt or OpenLayers) option/method I'm missing? Any nice workarounds?
Thanks for reading :-)
PS: I'm sorry if I've not been clear enough, english is not my mother tongue. Let me know if something stated above was not clear enough :)
i Hope this help to someone else, I realized that: you're rigth this control make the request in asynchronous mode, but this is ok, no problem with that, the real problem is when the control handle the request and trigger the event "getfeatureinfo" so, i modified 2 methods for this control and it works!, so to do this i declare the control first, and then in the savage mode i modified the methods here is de code:
getInfo = new OpenLayers.Control.WMSGetFeatureInfo({ drillDown:true , queryVisible: true , maxFeatures:100 });
//then i declare a variable that help me to handle more than 1 request.....
getInfo.responses = [];
getInfo.handleResponse=function(xy, request) { var doc = request.responseXML;
if(!doc || !doc.documentElement) { doc = request.responseText; }
var features = this.format.read(doc);
if (this.drillDown === false) {
this.triggerGetFeatureInfo(request, xy, features);
} else {
this._requestCount++;
this._features = (this._features || []).concat(features);
if( this._numRequests > 1){
//if the num of RQ, (I mean more than 1 resource ), i put the Request in array, this is for maybe in a future i could be need other properties or methods from RQ, i dont know.
this.responses.push(request);}
else{
this.responses = request;}
if (this._requestCount === this._numRequests) {
//here i change the code....
//this.triggerGetFeatureInfo(request, xy, this._features.concat());
this.triggerGetFeatureInfo(this.responses, xy, this._features.concat());
delete this._features;
delete this._requestCount;
delete this._numRequests;
// I Adding this when the all info is done 4 reboot
this.responses=[];
}
}
}
getInfo.triggerGetFeatureInfo= function( request , xy , features) {
//finally i added this code for get all request.responseText's
if( isArray( request ) ){
text_rq = '';
for(i in request ){
text_rq += request[i].responseText;
}
}
else{
text_rq = request.responseText;
}
this.events.triggerEvent("getfeatureinfo", {
//text: request.responseText,
text : text_rq,
features: features,
request: request,
xy: xy
});
// Reset the cursor.
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");}
Thanks, you bring me a way for discover my problem and here is the way i solved, i hope this can help to somebody else.
saheka's answer was almost perfect! Congratulations and thank you, I had the same problem, and with it I finally managed to solve it.
What I would change in your code:
isArray() does not work, change it like this: if(request instanceof Array) {...} at the first line of getInfo.triggerGetFeatureInfo()
to show the results in a popup this is the way:
My code:
getInfo.addPopup = function(map, text, xy) {
if(map.popups.length > 0) {
map.removePopup(map.popups[0]);
}
var popup = new OpenLayers.Popup.FramedCloud(
"anything",
map.getLonLatFromPixel(xy),
null,
text,
null,
true
);
map.addPopup(popup);
}
and in the getInfo.triggerGetFeatureInfo() function, after the last line, append:
this.addPopup(map, text_rq, xy);
A GetFeatureInfo request is send as a JavaScript Ajax call to the external server. So, the requests are likely blocked for security reasons. You'll have to send the requests to the external servers by a proxy on your own domain.
Then, configure this proxy in openlayers by setting OpenLayers.ProxyHost to the proper path. For example:
OpenLayers.ProxyHost = "/proxy_script";
See http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost for more background information.