How to extract target hostname during onbeforeunload event - javascript

I'd like to warn the user when he/she tries to navigate away from my webapp when his/her project is unsaved. Currently I don't care about edit boxes which are currently under edit. I know the project's dirty state from the session.
I quickly realized that if I set a function for the window.onbeforeunload, it'll disturb the user even if he/sh navigates to another page within my webapp. In such case no disturbance needed, the session will be still alive.
I tried a solution derived from Fire onbeforeunload only when window is closed (includes working example)
window.onbeforeunload = function (e) {
var targetHost = new URL(e.target.URL).hostname;
if (targetHost != window.location.host)
return "Project is unsaved!!!";
else
return null;
};
The new URL(e.target.URL).hostname results in (at least on IE11):
JavaScript runtime error: Object doesn't support this action
And mysteriously I can't find anything about no new URL(e.target.URL).hostname by any search.
But at the same time I hope this is not impossible, I cannot believe others didn't experience this.

There are two problems in your script:
window.onbeforeunload = function (e) {
var targetHost = new URL(e.target.URL).hostname;
if (targetHost != window.location.host) {
return "Project is unsaved!!!";
else
return null;
};
The first is that you did not close your if. The second is that e.target.URL is apparently not supported by IE. You need to have an A and a B plan.
A plan: Applies to the case when e.target.URL exists. In this case, you handle the case as you initially implemented, but with the syntax fix mentioned above.
B plan: Applies to the case when e.target.URL does not exist. For this case, you should assume that the user leaves the site. You can improve the approach by adding knowledge when the user uses your control to navigate, for instance, you know that a given anchor will stay on the site. In this case, you can define a click event and set a boolean value to true (and the default value of that boolean should be false...), so that later your onbeforeunload event will know that your user stayed on the site even if he or she uses IE.
So you need a logic like this:
var targetHost = !!e.target.URL ? (new URL(e.target.URL).hostname) : ((myFlag) ? (window.location.hostname) : ("elsewhere"));

Related

Angular - Pass info how an opend window was closed to the component from which the window was invoked

I am opening a new window (let's call it New Calculation) from a component that is not related to it (Calculation List).
const windowHandler: Window = window.open(appUrl);
const calculateSnackbarInterval = setInterval((): void => {
this.calculateOpened = true;
this.ref.detectChanges();
if (windowHandler.closed) {
this.snackBarService.openSnackBar({
type: 'info',
message: this.translate.instant(this.getTranslateKeyForSnackBar()),
actions: [
{
label: this.translate.instant(`snackbar.actions.abort`),
action: SnackbarAction.ABORT,
},
],
});
this.calculateOpened = false;
this.ref.detectChanges();
this.pullingIntervalDelete?.unsubscribe();
clearInterval(this.maximumPullingInterval);
this.startPullingInterval();
clearInterval(calculateSnackbarInterval);
}
}, 1000);
}
The invoked New Calculation window can be closed by "Cancel" button, using the browser, or pressing the "Calculate" button.
Currently a snackbar appears in Calculation List when closing the New Calculation, it doesn't matter which way was chosen. I would like to invoke this snackbar, only when the New Calculation window is closed using the "Calculate" button.
I have thought about 2 possible solutions:
Using local storage to save the information which button was used to close the window, and read it from the windowHandler.
Cons: Not sure about the security aspect, I would like to avoid the user tampering with it, and the user can edit local storage.
Build a dedicated service that reacts to an event assigned to "Calculate" button.
Cons: This one seems like an overkill for me with all of the listeners and dependencies.
Is there a preferred method to achieve this?
Thank you in advance.
I've came up with a simple solution. It's not the prettiest practice, however
it doesn't seem to be an overkill and user won't be able to tamper with it easily.
On the button triggering a new calculation I've added:
(this.window as any).calculationStarted = true;
and simply changed the receiver to
if (windowHandler.closed && (windowHandler as any).calculationStarted) {

Video.JS - Multiple events firing on exact time once

I'm developing a web application in which I've got a video element. This, as I've had good experiences with it, is controlled via the VideoJS scripts.
However, on my app, there are some points in which the video needs to be paused until played by the user (or by a script, it's not relevant), but not after.
What I have
Basically - and the basic information you need - what I have is an array of items, let's say item_array, with objects in it. Each object has an id, a media object with some information of it, including a src, and an indications array - which would be the pause-times objects. Something like that:
var items_array = [
{
id: 1234,
media: {
id: 2345,
src: 'video_source.mp4',
duration: 302.56
},
indications: [
{
id: 1,
time: 65.380
},
{
id: 2,
time: 89.238
},
{
id: 3,
time: 123.765
}
]
}
]
Of course, I've got the video variable, which stores the VideoJS reference and is accessible all along the code as a global variable (maybe it's useful for you to know that).
What I need
Basically, I need to be able to load a video source and tell the script that the video must pause at 1:05.380, 1:29.238 and 2:03.765, for example.
Once paused, the user can play the video again, and it will not be paused (at least, by the script) again until the next time mark is reached.
Then, the user can specify a new video to load, with its new pause-points, so old pause-points should be removed and new ones set.
Also, it would be necessary to perform other actions apart from pausing the video (such as showing a message, etc.). But it's not necessary for you to know any of these - just I have to be able to write some actions to do on every pause-point.
What I've tried
Well, I've been playing with timeupdate. The first problem is that this only fires every now and then, so video.currentTime() == NN.NNN will not always work.
The script video.currentTime() >= NN.NNN is not possible neither, because the user must be able to play further the pause-time (yes, the event can be set off once reached, but if the user skips it on the time line, the video must not be paused).
So, if the pause-time is at 1:05 and the user skips from 0:45 to 1:25, the pause-time set to 1:05 must not be triggered, thus the video must not be paused - so the line written before does not work as I'd like to.
Thank you everyone in advance for your time and help! If you need any further information, please ask me and I will submit it as soon as I can!
Best regards.
Finally, I've come up with a possible solution. Sure it's not the best in performance, but it does what it's meant to.
I write it here and maybe someone will find it useful.
The solution
What I do is to fire a custom event everytime the timeupdate event is triggered, so I can delete all actions attached to this custom event without breaking VideoJS default event actions.
Then, I set a time_range, so I can make the comparison between video.currentTime() with the desired indication.time without being exact, but with some space to happen.
Finally, when it happens, I pause the video (and show the indication, those actions I told you I needed to do) and set a new property of the indication, has_shown to true.
At every custom_timeupdate I check if the video.currentTime() is inside that space of time I told you and if that indication.has_shown is set to false. If so, the process triggers. If not, nothing happens (so if an indication has already been shown it does not pause the video again).
But, as I wanted it to show it again if the user returned to that point, I set a last check so if video.currentTime() is - a little bit - far from that point, the property has_shown is set to false again.
This is done, of course, for every indication we have.
Here you have the code:
var time_range = 0.125
video.on("timeupdate", function(){
video.trigger("indications_timeupdate")
})
function load_indications(indications){
/*
Function actions ...
*/
// If the video is defined, as we are loading them new, we set off old Indication timers
if(video !== undefined) video.off("indications_timeupdate")
$.each(indications, function(i, ind){
video.on("indications_timeupdate", function(){
if(video.currentTime() >= ind.time - time_range && video.currentTime() <= ind.time + time_range && !ind.has_shown){
video.pause()
ind.has_shown = true
}else if(video.currentTime() >= ind.time + time_range || video.currentTime() <= ind.time - time_range){
ind.has_shown = false
}
})
})
}
As I said, maybe it's not the best solution, but it's the best one that I've been able to think of.
Thank you for your time to everyone who has tried to think of an approach for this question!
Best regards.

How to Refresh (F5) and get another page with AngularJS?

In my web application, I got a form on 2 different pages, purchase1 and purchase2.
If a customer refreshes the page at purchase2, I want the location to be changed back to purchase1.
I haven't found a way to do so, I've tried to make a config like that:
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.when('/purchase2', '/purchase1');
}
But obviously, that way I can never get to purchase2 page.
I need it to happen only on manual user Refresh.
Is there any way to do so? Some built-in Angular function that happens on Refresh?
Something like
$scope.onRefresh = function(){
$location.path('/dashboard/purchase1');
}
Haven't found anything like it.
You can listen for beforeunload event. beforeunload event will be triggered when someone hits a F5 or refreshes the page anyhow. Do something like,
var windowElement = angular.element($window);
windowElement.on('beforeunload', function (event) {
event.preventDefault();
//below is the redirect part.
$window.location.href = '/purchase1';
});
Put this code on purchase2 page.
Yes, you can do it.
Register a global listener for state change:
$scope.$on('$stateChangeStart', function(event, toState, toParams, fromState) {
// If fromState is null that means it is a page refresh otherwise
// The fromState will be the purchase1
if (toState.name == 'purchase2' && !fromState) {
$state.go('purchase1');
event.preventDefault();
return;
}
});
By global, I mean register the above in such a controller whose scope is available throughout your application like a controller added to your body or html tag.
Also, I'm assuming the purchase1 & purchase2 are the name of your states for both the pages. If they are parameters to a common state you can alter the code above.
What happens on refresh is the same as what happens on first load.
What you can do is check whether the user has been here already, e.g. by setting a cookie and reading it later.
However, I think you should rethink your design. Why would you invest your time into making refresh function behave worse?
I think, javascript isnt the right way because javascript does not know anything about the previous page, because it will be rendered when page is already loaded. So try to check this in your server side application like in php or jsp and read the request-header because there you can get current url and redirect user to the new url
You can try to have a flag say purchase2-loaded and keep this variable in localStorage. You can then write an IIFE, which will check value of this variable and reroute to purchase1.
Also on purchase1, reset it to false.
Note:This is a pure JS Code and relies on localStorage.
Fiddle.
Sample Code
(function(){
var isPurchase2Loaded = localStorage.getItem("Purchase2");
if(isPurchase2Loaded || isPurchase2Loaded === undefined){
document.write("Rerouting to purchase 1...");
}
else{
document.write("Loading for the first Time...");
localStorage.setItem("Purchase2", true);
}
})()
Eventually, if someone is interested, I fixed it like that:
In purchase1.js I've added this to the submit() function:
$rootscope.demographic=1;
In purchase2.js I've added this code to the controller:
var init = function(){
if(angular.isUndefined($rootScope.demographic)){
$location.path('/purchase1');
}
};
init();
It is working because Refresh(F5) completely restarting the application, therefore also resets the $rootscope and makes "Demographic" Undefined.
Upon entering purchase2, "init" function will start, if we came from purchase1 everything will be ok and demographic will be defined, otherwise we will just load purchase1 again.
:)
$state.go('purchase1');
this should resolve your problem.

What are the (dis)advantages of simulating keypresses? Is there a better way? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
Suppose I have the following code, which listens to an input element, specifically executing a callback when the user hits Enter:
...
document.getElementById('human_input').onkeyup = (function (indicate) {
return function(e) {
var e = e || window.event; // for IE8
if (e.keyCode === 13) { // if user presses 'Enter' key*
indicate('User has submitted text');
}
}
})(indicate);
[*keyCode reference here.]
...
What indicate does isn't really the point here but some code is applied to the argument 'User has submitted text' to display it somewhere.
Now, what if I want some script to listen in the same way to an 'invisible input' which is to be filled out, not by a human but by a separate part of the code?
Say that a black box script function called fillHidden() fills out the hidden_input field at some specified point.
document.getElementById('hidden_input').value = 'All filled out.'; // for example
How would a very separate listener script module find out about the value. Would it be enough to say do this...
document.getElementById('hidden_input').onkeyup = (function (indicate) {
return function(e) {
var e = e || window.event; // for IE8
if (e.keyCode === 13) { // if user presses 'Enter' key
indicate('User has submitted text');
}
}
})(indicate);
And have the fillHidden() code do this:
document.getElementById('hidden_input').value = 'All filled out.'; // for example
document.getElementById('hidden_input').onkeyup({keyCode: 13}); // simulate keypress
Or is that bad practice? There have been questions about how to do this or even simulate keyboard shortcuts but I could find nothing on the actual merits or otherwise of simulating keypresses.
Is there a better way, a pattern perhaps, or an interlocking system of callbacks, that can enable these modules to interact whilst separate? I have seen videos of Nicholas Zakas talking about this kind of architecture as 'loose coupling' and a book I have called JavaScript Patterns (Stefanov, 2010) alludes to possibilities of a subscriber/publisher model. What would be the advantages of these compared with a simulated keypress, especially when the simulated keypress makes for a kind of symmetry with the user's side of events? And what kind of disadvantages/dangers might one expect when simulating keypresses?
This is all very... abstract.
Which is all well and good.
It's not necessarily the goal of SO to come up with solutions to abstractions like this, because there are a million ways of doing them.
However, a few key points:
Unless you're linking simulated code to user-generated events in some way, the browser is likely going to prevent your simulations from taking over for the user
(imagine browsers letting any JS on the page simulate any keypress or any mouse-click at any time).
This typically means that you're tightly-bound, using some library which isn't meant for consumer use, using browser-specific (ie: FireFox/Chrome/IE) plug-ins which users must install/run, or bust. Pick one (or more).
Custom-Events, with callbacks, as a rule are what will allow you to keep your programs separate, but have them function together.
Zakas does some great talks on Sandboxing, but those are very enterprise-level, end-game type things. They're fantastic, and library builders and system-engineers should totally consider them, but for making the average page, it'd be better to complete the 100 lines you need to write, rather than build out a full framework, with a library which both wraps every module in an enclosure, and injects itself into that module.
That's where Pub-Sub (and Observer)/Moderator(or Mediator) come into play.
Any of the above might also be called an "emitter" or a "generator", et cetera, depending on the library.
Each one of the above is created in basically the same way, and does the same sort of thing.
The goal is to relay results to an audience who wants to listen.
The speaker chooses when to notify the audience, and what to tell them.
The audience chooses to tune in at any time, to await the next broadcast (which might never come, but at least they were paying attention for it), or they can choose to tune out and stop listening for broadcasts.
The differences between them, then, is how the "speaker" knows about the thing that's happening.
Publisher-Subscriber
In publisher-subscriber, the speaker IS the object that makes things happen.
Look at Twitter.
You sign up for a Twitter account. Once you have it, you can follow anyone you want.
Any time they tweet, you get notified about it.
Anybody can follow you, so that any time you tweet, they are notified about it.
The person who is doing the action publishes that action to any subscribers who want to hear it. There might be 3000 subscribers and one publisher (a newsletter), or 3000 publishers and one subscriber...
There might be publishers who won't subscribe, or subscribers who won't publish... ...but that's the paradigm.
Observer
In observer, you're talking about an object which is coupled to the thing doing the work.
It might be tight. It might be loose. But there's a thing doing something, and there's a thing that knows exactly what it's doing. Then people bug the watcher for updates.
Think of the days of Baseball, where people listened to the games on radio.
The observer would be the radio-commentator.
He wasn't the one hitting the ball or stealing bases. He was the one in the booth, who saw everything going on, and knew what it all meant, and turned it into user-friendly information for all of the people listening at home.
These days, players can tweet about plays as they're making them, directly to all of their fans (pub-sub), and I'm sure that FourSquare will find a way to get their geo down to the per-base accuracy, for hands-off updating of who the king of third-base is (for once, it's not Jeff, in his cramped Z28).
Mediator/Moderator
In this case, we're talking about an object which everybody knows about, but nobody knows about one another.
Imagine a call-in talk-radio show.
Everybody knows the show. Everybody can call into the show, and talk with the host. But other than coincidence, nobody knows anything about the other listeners.
It's a little bit different than pub-sub, because everybody's a publisher, but you don't have to know somebody's Twitter handle to hear from them. You can say Hey Twitter, any time anybody in the world mentions #browns, let me know. I'm starving..
It's a little different from observer because while the moderator IS watching the person who does the work, anybody can be doing the work at any time.
Which one is the right one?
It all depends on what you need, and what you're actually intending to do with it.
Here's how we might make a Moderator:
var Moderator = function () {
var events = {},
notify = function (evtName, data) {
var evt = events[evtName];
if (!evt) { return; }
evt.forEach(function (func) { func(data); });
},
listen = function (evtName, callback) {
events[evtName] = events[evtName] || [];
events[evtName].push(callback);
},
ignore = function (evtName, callback) {
var evt = events[evtName];
if (!evt) { return; }
evt.forEach(function (func, i, arr) {
if (func === callback) { arr.splice(i, 1); }
});
};
return { ignore : ignore,
listen : listen,
notify : notify };
};
Pretty simple and straightforward, right?
Of course, this isn't particularly filled with bells and whistles, like subscribing to only the next time an event fires, or the next-3 times, or whatever...
We might use it like this:
var Game = function () {
var game_moderator = Moderator(),
scoreboard = Scoreboard(),
messages = MessageBoard(),
player_one = Player(),
player_two = Player();
function initialize () {
player_one.initialize(game_moderator);
player_two.initialize(game_moderator);
game_moderator.listen("player:death", scoreboard.update);
game_moderator.listen("player:death", messages.add_kill);
game_moderator.listen("chat:input", messages.add_msg );
}
function start() {}
/* update... draw... etc... */
return {
load : load,
initialize : initialize,
start : start
};
};
var game = Game(),
loading = game.load();
loading.done(function () {
var initializing = game.initialize();
initializing.done(game.start);
});
Meanwhile, Player might look like this:
var Player = function (name) {
var system,
health = 30,
damage = [],
attack = function () { /* ... */ },
hurt = function (amt, type, from) {
health -= amt;
damage.push({ amount : amt, type : type, from : from });
},
die = function () {
var killing_blow = damage[damage.length - 1];
killing_blow.player = name;
system.notify("player:death", killing_blow);
},
update = function () {
if (health <= 0) { die(); }
},
draw = function () {},
initialize = function (sys) { system = sys; };
return {
initialize : initialize,
update : update,
draw : draw,
hurt : hurt
/* ... */
};
};
So looking back into the Game.initialize function, we can see that we've got a scoreboard and a message panel which are both going to do things with "player:death" events.
Because of the way the players are called and defined, I'm injecting a reference to the moderator, during their initialization (so that I can keep everything separate: dependency-injection).
But player_one knows nothing about player_two, scoreboard knows nothing about anything, except that something occasionally calls its .update method and passes in kill information, and messages gets all kinds of love, but it's the kind where everybody's a stranger...
Going back to your original problem:
If your hidden-input is being filled in by spying on key-presses, why not build an Observer?
Build an observer which connects to a keyup event-listener and a keydown event-listener.
Have that observer turn those events into useful information (for instance: when you hold a key down, the keydown event fires dozens of times per second -- you probably don't want that... so alert when a new key is added, or alert when a pressed key is released).
Have the hidden-input subscribe to that.
When the hidden-input is full, or however your requirements are operating... ...and you want to fire an event off, have a global-moderator (or a moderator which is at the top level of the system that hidden-input is a part of).
From there, fire an event called "hidden-input-filled" or whatever is meaningful.
The people who care about that happening can subscribe to that event through the moderator.
That is, of course, if your program is built in such a way that nobody should know about the hidden-input, BUT there are people who should know about hidden-input's events.
If there are only a select group of things which should know about hidden-input, and those are the only things which should know about its events, and hidden-input should also be able to know something about them, then make them pub-sub.
Or mix and match your connections:
The idea is to build communication which makes sense and tells people what they need to know, and no more.
So if Twitter-users should be sub-pub, but different widgets on the page (timeline vs search vs recent-pictures, etc) shouldn't know much about one another (and certainly not make every picture able to share with every timeline update), then make a global moderator that the whole widget can communicate to other widgets through (like when timelines need to update based on search results), and inside of each widget, have a moderator and/or pub-sub for different connections, between components.
Hope that helps, and I hope that explains why it's easier to engineer a loosely-coupled, large program by doing this, rather than by hijacking real events, and then firing fake ones down the line, intended to target different areas of your program.
In all honesty, if your full site, with all of its programs comes down to: "I've got this input that does this thing, and another input that does another thing", the answer is that it really doesn't matter too much.
When you get to: "I've got a page with 8 spaces for different widgets, and there are 16 possible widgets which could be loaded in any of those 8 slots at any time, and there are some major actions in some widgets which should cause responses in other widgets, and there are lots of events that each widget needs to control internally, and we need to pipe in an AJAX library and a DOM/MVC(or MVVM) libarary to control all of the stuff that goes on inside of each widget, itself, and there's only one of me..."
That's when it's a great idea to hammer out this stuff and hammer out Promises/Deferreds/Futures, and break your big ideas out into smaller pieces, spread out over different points in the life of the running application.

Callback for a popup window in JavaScript

I hope I did my homework well, searching the Internets for the last couple of hours and trying everything before posting here, but I'm really close to call it impossible, so this is my last resort.
I want a simple thing (but seems like hard in JavaScript):
Click button -> Open Window (using window.open)
Perform an action in the popup window and return the value to parent (opener)
But I want to achieve it in a systematic way, having a callback defined for this popup; something like:
var wnd = window.open(...)
wnd.callback = function(value) {
console.log(value);
};
I've tried defining the callback property in popup window JS code:
var callback = null;
Unfortunately, that does not work, as...
$('#action').click(function() {
console.log(callback);
});
... returns just that "null" I set initially.
I've also tried setting the callback in a parent window after window load (both thru window.onload=... and $(window).ready()), none worked.
I've also tried defining some method in child window source code to register callback internally:
function registerCallback(_callback)
{
callback = _callback; // also window.callback = _callback;
}
But with the same result.
And I don't have any more ideas. Sure, it would be simple setting the value using window.opener, but I'll loose much of a flexibility I need for this child window (actually an asset selector for DAM system).
If you have some ideas, please share them.
Thank you a million!
HTML5's postMessage comes to mind. It's designed to do exactly what you're trying to accomplish: post messages from one window and process it in another.
https://developer.mozilla.org/en/DOM/window.postMessage
The caveat is that it's a relatively new standard, so older browsers may not support this functionality.
http://caniuse.com/#feat=x-doc-messaging
It's pretty simple to use:
To send a message from the source window:
window.postMessage("message", "*");
//'*' is the target origin, and should be specified for security
To listen for messages in a target window:
window.addEventListener
("message", function(e) {
console.log(e.data); //e.data is the string message that was sent.
}, true);
After few more hours of experiments, I think, I've found a viable solution for my problem.
The point is to reference jQuery from parent window and trigger a jQuery event on this window (I'm a Mac user but I suppose, jQuery has events working cross-platform, so IE compatibility is not an issue here).
This is my code for click handler on anchor...
$(this).find('a[x-special="select-asset"]').click(function() {
var evt = jQuery.Event('assetSelect', {
url: 'this is url',
closePopup: true,
});
var _parent = window.opener;
_parent.jQuery(_parent.document).trigger(evt);
});
... and this is the code of event handler:
$(document).bind('assetSelect', function (evt) {
console.log(evt);
});
This solution is fine, if you don't need to distinguish between multiple instances of the asset selection windows (only one window will dispatch "assetSelect" event). I have not found a way to pass a kind of tag parameter to window and then pass it back in event.
Because of this, I've chosen to go along with (at the end, better and visually more pleasant) solution, Fancybox. Unfortunately, there is no way - by default - to distinguish between instances either. Therefore, I've extended Fancybox as I've described in my blog post. I'm not including the full text of blog post here, because is not the topic of this question.
URL of the blog post: http://82517.tumblr.com/post/23798369533/using-fancybox-with-iframe-as-modal-dialog-on-a-web

Categories

Resources