angularJs - `$window.location.href` - throws an error in firefox alone - angularjs

In my angular application, I am re-directing the user by conditinal. all are works fine other than firefox browser.
firefox browser considers this $window.location.href - as a server side request and my app trigger the error, because of unknown call. chrome,ie works fine.
here is my code :
fn.redirectToLogin = function() {
var site = fn.env.auth;
var clientId = fn.env.oauth.client_id;
var redirectUri = fn.env.oauth.redirect_uri;
var authorizePath = String( "authorization.oauth2?response_type=token&client_id=" );
var state = fn.env.state;
var URL = encodeURI(site + authorizePath + clientId + '&redirect_uri=&scope=&state=' + encodeURIComponent(JSON.stringify(state)));
$window.location.href = URL;//requesting to server!! throws error
console.log( 'fn.env.state::', fn.env.state );
}
if( !$rootScope.isUserLoggedIn && fn.env.state !== undefined ) {
console.log('going to call auto login!');
fn.redirectToLogin();
}

I believe the way to change URL in Angular is $location.url(URL). Inject $location in your controller and use it instead of directly setting value. Here you can read more about it.

Related

React site, trying to embed app, but I can't figure out how to integrate into a component (works on the static page)

I'm trying to embed the following onto my react site in a specific component/page. When I put the code on the static html page it works beautifully, but it isn't working out when I try to place it onto the component.
The main error I'm getting is saying that chrome won't allow document.write from an external script or something along those lines. I wish I still had the error message, but I've been trying different things and in a fit of frustration deleted it all last night. (Great strategy, I know.) I was able to retrieve the errors, please see at the bottom for details.
<div id="fTelnetContainer" class="fTelnetContainer"></div>
<script>document.write('<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.xfer.js?v=' + (new Date()).getTime() + '"><\/script>');</script>
<script>
var Options = new fTelnetOptions();
Options.BareLFtoCRLF = false;
Options.BitsPerSecond = 57600;
Options.ConnectionType = 'telnet';
Options.Emulation = 'ansi-bbs';
Options.Enter = '\r';
Options.Font = 'CP437';
Options.ForceWss = false;
Options.Hostname = 'mysite.com';
Options.LocalEcho = false;
Options.NegotiateLocalEcho = true;
Options.Port = 1234;
Options.ProxyHostname = 'proxy-us-ga.ftelnet.ca';
Options.ProxyPort = 80;
Options.ProxyPortSecure = 443;
Options.ScreenColumns = 80;
Options.ScreenRows = 25;
Options.SendLocation = true;
var fTelnet = new fTelnetClient('fTelnetContainer', Options);
</script>
I'm not looking for anyone to do this for me, but if you could provide a tip, point me to some documentation, or any hint whatsoever I would be eternally grateful.
This embed is coming from the following site by the way: http://embed-v2.ftelnet.ca/
EDIT
I'm still working on this, so I tried to get back the error messages and here is what happened:
(index):46 A parser-blocking, cross site (i.e. different eTLD+1) script, http://embed-v2.ftelnet.ca/js/ftelnet-loader.norip.xfer.js?v=1599169227014, is invoked via document.write. The network request for this script MAY be blocked by the browser in this or a future page load due to poor network connectivity. If blocked in this page load, it will be confirmed in a subsequent console message. See https://www.chromestatus.com/feature/5718547946799104 for more details.
A cookie associated with a cross-site resource at http://ftelnet.ca/ was set without the `SameSite` attribute. It has been blocked, as Chrome now only delivers cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.
ftelnet-loader.norip.xfer.js?v=1599169227014:1 A parser-blocking, cross site (i.e. different eTLD+1) script, http://embed-v2.ftelnet.ca/ftelnet/ftelnet.norip.xfer.min.js?v=2019-08-31, is invoked via document.write. The network request for this script MAY be blocked by the browser in this or a future page load due to poor network connectivity. If blocked in this page load, it will be confirmed in a subsequent console message. See https://www.chromestatus.com/feature/5718547946799104 for more details.
EDIT2
This is my component at the moment:
class Home extends React.Component {
componentDidMount() {
document.write("<script src='//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.xfer.js?v=" + (new Date()).getTime() + "'></script>" +
"<script>" +
"window.onload(function() {" +
"var Options = new fTelnetOptions();" +
"Options.BareLFtoCRLF = false;" +
"Options.BitsPerSecond = 57600;" +
"Options.ConnectionType = 'telnet';" +
"Options.Emulation = 'ansi-bbs';" +
"Options.Enter = '\r';" +
"Options.Font = 'CP437';" +
"Options.ForceWss = false;" +
"Options.Hostname = 'mysite.com';" +
"Options.LocalEcho = true;" +
"Options.NegotiateLocalEcho = true;" +
"Options.Port = 1234;" +
"Options.ProxyHostname = 'proxy-us-ga.ftelnet.ca';" +
"Options.ProxyPort = 80;" +
"Options.ProxyPortSecure = 443;" +
"Options.ScreenColumns = 80;" +
"Options.ScreenRows = 25;" +
"Options.SendLocation = true;" +
"var fTelnet = new fTelnetClient('fTelnetContainer', Options);" +
"});" +
+ "</script>");
}
render() {
return (
<div>
<h1>TELNET TIME</h1>
<div id="fTelnetContainer" className="fTelnetContainer"></div>
</div>
);
}
}
export default Home;
This also throws the error, Uncaught SyntaxError: Invalid or unexpected token
I encountered a similar issue when I needed to use PayPal scripts (they do not support importing ES6 modules).
First of all, document.write will not work for you because by the time the componentDidMount is invoked, the document is already fully loaded. See https://developer.mozilla.org/en-US/docs/Web/API/Document/write. Therefore calling this again - should the write function push through - will erase everything in the document and write a new one.
To solve your issue, here is what I did: on your initial html document, example index.html, this is where I included your script to load the source codes needed and I assigned the fTelnetOptions object to a window property since you instantiate this later on:
<html>
<head>
<script>
document.write(
'<script src="//embed-v2.ftelnet.ca/js/ftelnet-loader.norip.xfer.js?v=' +
new Date().getTime() +
'"><\/script>'
);
window.fTelnetOptions = fTelnetOptions;
</script>
</head>
Back on your component, do what you have to to initialize the telnet when the component mounts - see the window.fTelnetOptions that we instantiated:
class Home extends React.Component {
componentDidMount() {
var Options = new window.fTelnetOptions();
Options.BareLFtoCRLF = false;
Options.BitsPerSecond = 57600;
Options.ConnectionType = "telnet";
Options.Emulation = "ansi-bbs";
Options.Enter = "\r";
Options.Font = "CP437";
Options.ForceWss = false;
Options.Hostname = "mysite.com";
Options.LocalEcho = true;
Options.NegotiateLocalEcho = true;
Options.Port = 1234;
Options.ProxyHostname = "proxy-us-ga.ftelnet.ca";
Options.ProxyPort = 80;
Options.ProxyPortSecure = 443;
Options.ScreenColumns = 80;
Options.ScreenRows = 25;
Options.SendLocation = true;
var fTelnet = new window.fTelnetClient("fTelnetContainer", Options);
}
render() {
return (
<div>
<h1>TELNET TIME</h1>
<div id="fTelnetContainer" className="fTelnetContainer"></div>
</div>
);
}
}
export default Home;
At this point I am not sure how well this will work with client-side-routing. Refer to my scripts below if you encounter that the fTelnetOptions property in window object is lost when routing:
const script = document.createElement("script");
script.src = `your_script_url`;
script.async = true;
document.body.appendChild(script);
script.onload = () => {
// do your instantiation & logic here
}

Load images as a service

I have service that pulls an object from an API. Some of that object may contain image URLs. The backend currently scans for these, and processes them, (in PHP) by get_file_contents() and translating them to inline data. This is heavily loading the throughput on my server. The reason I am doing this is because I want to cache the images for being offline later, but in a way that I can still just use regular angular to render the object.
I can't do the processing in Javascript in the browser with $http.get() because the site hosting the images is blocking the cross-site request. What I thought to do, then, was to create an <IMG> element in the browser, that called the service back once it was loaded so I can extract the data and process the object with it.
I can't control the service worker to store the get from inside the app, and the URL's are not known by the app at any time before it downloads the API object anyway.
I did think about redoing the service worker to store gets from off my site as well, but that seemed a little bit wrong, and I'm not sure how well it would work anyway, plus, while developing, I switch off the service worker as it means I have to let the entire site load twice for it to refresh completely.
Can anyone help me with a way to get image data via the browser into my service?
If I had found a CORS supportive image host in the first place I may not have needed this and could probably have just used the $http call.
A directive, service and controller are required, as well as a host that supports CORS (Imgur for example). I also used this base64 canvas code.
Here is the javascript code:
// Using this from https://stackoverflow.com/questions/934012/get-image-data-in-javascript
function getBase64Image(img) {
// Create an empty canvas element
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
var dataURL = canvas.toDataURL("image/png");
return dataURL;
// return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
// Used on the img tag to handle the DOM notification feeding into the service
app.directive('notifyimgsvc', function() {
return {restrict : 'A', link : function(scope, element, attrs) {
element.bind('load', function() {
console.log('imgSvc::notify() image is loaded');
console.log("imgSvc::notify(): " + this.src);
imgSvc.notifyLoad(this.src, getBase64Image(this));
});
element.bind('error', function() {
console.log('imgSvc::notify() image could not be loaded');
console.log("imgSvc::notify(): " + this.src);
});
}};
});
// A core service to handle the comms in both directions from requests to data
app.service('imgSvc', [function(netSvc) {
imgSvc = this; // to avoid ambiguoity in some inner function calls
imgSvc.images = {}; // a cache of images
imgSvc.requests = []; // the requests and their callbacks
imgSvc.handlers = []; // handlers that will render images
console.log("imgSvc::init()");
// Allows a controller to be notified of a request for an image and
// a callback to call when an image is added. There should only ever
// be one of these so an array is probaby not needed and any further
// requests should probably throw an error.
imgSvc.registerHandler = function(callback) {
console.log("imgSvc::registerHandler()");
if (imgSvc.requests.length) {
// Already have image requests, so tell the new handler about them
for ( var i in imgSvc.requests) {
callback(imgSvc.requests[i].url);
}
}
// Add the new handler to the stack
imgSvc.handlers.push(callback);
};
// The usage function from your code, provide a callback to get notified
// of the data when it loads.
imgSvc.getImg = function(url, callback) {
console.log("imgSvc::getImg('" + url + "')");
// If we have pre-cached it, send it back immediately.
if (imgSvc.images[url] != undefined) {
console.log("imgSvc::getImg('" + url + "'): Already have data for this one");
callback(url, imgSvc.images[url]);
return;
}
// push an object into the request queue so we can process returned data later.
// Doing it this way als means you can have multiple requests before any data
// is returned and they all get notified easily just by looping through the array.
var obj = {"url" : url, "callback" : callback};
if (imgSvc.handlers.length) {
console.log("imgSvc::getImg('" + url + "'): informing handler");
for ( var i in imgSvc.handlers) {
imgSvc.handlers[i](obj.url);
}
}
imgSvc.requests.push(obj);
};
// Notification of a successful load (or fail if src == null).
imgSvc.notifyLoad = function(url, src) {
console.log("imgSvc.notifyLoad()");
// Save the data to the cache so any further calls can be handled
// immediately without a request being created.
imgSvc.images[url] = src;
// Go though the requests list and call any callbacks that are registered.
if (imgSvc.requests.length) {
console.log("imgSvc.notifyLoadCallback('" + url + "'): scanning requests");
for (var i = 0; i < imgSvc.requests.length; i++) {
if (imgSvc.requests[i].url == url) {
console.log("imgSvc.notifyLoadCallback('" + url + "'): found request");
// found the request so remove it from the request list and call it
var req = imgSvc.requests.splice(i, 1)[0];
i = i - 1;
console.log("imgSvc.notifyLoadCallback('" + url + "')");
req.callback(url, src);
} else {
console.log("imgSvc.notifyLoadCallback('" + url + "'): skipping request for '" + imgSvc.requests[i].url + "'");
}
}
} else {
console.log("imgSvc.notifyLoadCallback('" + url + "'): No requests present??");
}
};
// The notifiy fail is just a logging wrapper around the failure.
imgSvc.notifyFail = function(url) {
console.log("imgSvc.notifyFail()");
imgSvc.notifyLoad(url, null);
};
}]);
// A simple controller to handle the browser loading of images.
// Could probably generate the HTML, but just doing simply here.
app.controller('ImageSvcCtrl', ["$scope", function($scope) {
$scope.images = [];
console.log("imgSvcCtrl::init()");
// Register this handler so as images are pushed to the service,
// this controller can render them using regular angular.
imgSvc.registerHandler(function(url) {
console.log("imgSvcCtrl::addUrlHandler('" + url + "')");
// Only add it if we don't hqve it already. The caching in the
// service will handle multiple request for the same URL, and
// all associated malarkey
if ($scope.images.indexOf(url) == -1) {
$scope.images.push(url);
}
});
}]);
The HTML you need for this is very simple:
<div data-ng-controller="ImageSvcCtrl" style="display:none;">
<img data-ng-repeat="img in images" data-ng-src="{{img}}" alt="loading image" crossorigin="anonymous" notifyimgsvc />
</div>
And you call it within your controllers like this:
var req_url = "https://i.imgur.com/lsRhmIp.jpg";
imgSvc.getImg(req_url, function(url, data) {
if(data) {
logger("MyCtrl.notify('" + url + "')");
} else {
logger("MyCtrl.notifyFailed('" + url + "')");
}
});

How to render an image of an AngularJs website?

I'm using wkhtmltoimage. It works fine for e.g. http://stackoverflow.com
But when I try to run in on any website with AngularJs (e.g. https://material.angularjs.org/latest/):
wkhtmltoimage.exe --no-stop-slow-scripts --javascript-delay 5000 --enable-javascript --debug-javascript https://material.angularjs.org/latest/ C:\\dev\\temp\\0c8fca2c-0990-40c4-8672-12fc71ec6cf6.png
A get an error JS error (thanks to : --debug-javascript flag)
Loading page (1/2)
Warning: undefined:0 Error: [$injector:modulerr] http://errors.angularjs.org/1.5.5/$injector/modulerr?p0=ng&p1='undefined'%20is%20not%20an%20object
Rendering (2/2)
The result image is just a tiny white stripe.
How to ren
It seems that wkhtmltopdf doesn't play nicely with Function.prototype.bind (used in Angular >= 1.5), so you need to provide a "polyfill" to make it works.
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
if (this.prototype) {
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};

Protractor : Error: Error while waiting for Protractor to sync with the page: "window.angular is undefined. If i create new object of PageObject

I am trying to create a object of page object into spec and i am getting the error.
dashboard.pageObject.js
/*global exports,console*/
module.exports = function(){
this.dashboarurl = 'http://localhost:2525/ars-webapp/';
this.createNewReport_Clickhear = element(By.xpath("//a[contains(.,'Click Here')]"));
this.reportInputModel = element(by.model('reportDefCntrl.reportDef.reportname'));
this.reportDescriptionModel = element(by.model('reportDefCntrl.reportDef.reportdesc'));
this.templateListSelect = element(By.xpath("//select[#id='template-list-select']")).click();
this.selectAlarmDashboarTemplate= element(By.xpath("//option[contains(#value,'number:2')]"));
this.durationOfAlarmTemplate = element(By.xpath("//span[#class='ui-select-placeholder text-muted ng-binding']"));
this.duration_Daily = element(By.xpath("//span[contains(.,'Daily')]"));
this.addObject = element(By.xpath("//button[#data-ng-click='reportDefCntrl.addLogObjects()']"));
this.searchInput = element(By.xpath("//input[#type='search']"));
this.searchButton = element(By.xpath("//button[contains(.,'Search')]"));
this.selectAllButton = element(By.xpath("//button[contains(.,'SelectAll')]"));
this.addObjectButton = element(By.xpath("//button[#data-ng-click='addLogObjectsCntrl.submitObjects()']"));
this.saveButton = element(By.xpath("//button[contains(.,'Save')]"));
}
Specfile is
/global require,console/
var Dashboard = require('../pageObjects/dashboard.pageObject.js');
var dashboard = new Dashboard();
describe('angularjs homepage todo list', function() {
it('should add a todo', function() {
browser.get(dashboard.dashboarurl);
}
if i skip the line
var dashboard = new Dashboard();
and provide
browser.get('http://localhost:2525/ars-webapp/');
instead of
browser.get(dashboard.dashboarurl);
it's working .
but i can't use feature of page object.
if i use
var dashboard = new Dashboard();
so i am getting error
E/launcher - Error while waiting for Protractor to sync with the page:
"window.angular is undefined. This could be either because this is a
non-angular page or because your test involves client-side navigation,
which can interfere with Protractor's bootstrapping. See
http://git.io/v4gXM for details"
Oh Man .. found it !!.. Everything was fine in your code. You know what .. The problem was .. you had a click() in this line this.templateListSelect = element(By.xpath("")).click() in your file - dashboard.pageObject.js
Since you have a require() first .. The dashboard file is executed first and click() is trigerred even before browser.get() and thats the reason you were seeing that error

URL from $routeChangeStart route params in angularjs routes

How would it be possible to get the URL hash fragment from route params in $routeChangeStart.
$scope.$on('$routeChangeStart', function (event, next, current) {
// trying to get the url hash fragment from <next> param here
// e.g. to_url_function(next) -> '/my_path/1'
});
Receiving the URL hash fragment would be easy using $locationChangeStart but this is not an option for me.
dasboe: I think I'm answering your question.
I have a app with an authentication/authorization check in the $routeChangeStart event handler. If not authenticated, I present user with modal login page. I want a successful login to send them to their original destination (Beauty of $routeChangeStart is that it will run again and check authorization after the successful login). I save the path built from the next in a user session service that is injected into the modal login controller.
here is the event handler
//before each route change, check if the user is logged in
//and authorized to move onto the next route
$rootScope.$on('$routeChangeStart', function (event, next, prev) {
if (next !== undefined) {
if ('data' in next) {
if ('authorizedRoles' in next.data) {
var authorizedRoles = next.data.authorizedRoles;
if (!SessionService.isAuthorized(authorizedRoles)) {
event.preventDefault();
SessionService.setRedirectOnLogin(BuildPathFromRoute(next));
if (SessionService.isLoggedIn()) {
// user is not allowed
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
} else {
// user is not logged in
$rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
}
}
}
}
}
});
Here is the function that builds the path from the next object
function BuildPathFromRoute(routeObj)
{
var path = routeObj.$$route.originalPath;
for (var property in routeObj.pathParams)
{
if (routeObj.pathParams.hasOwnProperty(property))
{
var regEx = new RegExp(":" + property, "gi");
path = path.replace(regEx, routeObj.pathParams[property].toString());
}
}
return path;
}
Notes:
I'm not keen on my $$route reliance, but I couldn't find any other way to do it. Maybe I missed something easier. I may be inviting trouble in the long term.
The preventDefault() will not work on AngularJS versions before 1.3.7 (see event.preventDefault() not working for routeChangeStart in angularjs app).
Standard caveat: This is all client side and subject to abuse. Make sure authentication/authorization happens server side.
The next Route object (from the event handler) also has a params property. I'm not sure if I should spin through its properties like I do with pathParams.
If you don't want to use hasOwnProperty, you could take advantage of the $$route.keys to get the names of the pathParams fields names:
function getPathFromRoute(routeObj)
{
var path = routeObj.$$route.originalPath;
var keys = routeObj.$$route.keys;
var value;
for (var i = 0; i < keys.length; i++) {
if(angular.isDefined(keys[i]) && angular.isDefined(keys[i].name)){
value = routeObj.pathParams[keys[i].name];
var regEx = new RegExp(":" + keys[i].name, "gi");
path = path.replace(regEx, value.toString());
}
}
return path;
};
Don't use object fields with $$ prefix like in previously given answers, because it's a prefix used by AngularJS for private properties. Use this method for get url from route (not tested):
var buildPathFromRoute = function (route) {
// get original route path
var path = route.originalPath;
// get params keys
var keysLength = route.keys.length;
for (var i=0; i<keysLength; i+=1) {
var param = route.keys[i];
// optional params postfix is '?'
var postfix = param.optional ? '\\?' : '';
var replaceString = ':' + param.name + postfix;
var regex = new RegExp(replaceString, 'g');
var paramValue = route.params[param.name].toString();
// replace param with value
path = path.replace(regex, paramValue);
}
path = path.replace(/\:\S+?\??/g, '');
return path;
};

Resources