So I'm making a Safari extension for my own personal use, and it's not working at all.
I'm trying to skip adf.ly and go directly to the website. But it's not doing anything at all.
I've tried alerting the current URL and the supposed new URL, and they aren't even displaying either.
Global.html
<!DOCTYPE html>
<script type='application/javascript'>
// Skip Adf.ly
// Made by Austen Patterson
// For Safari
//(C) Copyright 2013 Austen Patterson.
safari.application.addEventListener("start", performCommand, true);
safari.application.addEventListener("validate", validateCommand, true);
// Function to perform when event is received
function performCommand(event) {
// Make sure event comes from the button
if (event.command == "skip") {
var url = this.activeBrowserWindow.activeTab.url;
var newurl = url.replace(/http:\/\/adf\.ly\/(\d+)\//, '');
location.href(newurl);
window.open(newurl,"_self");
return;
}
}
</script>
and here is my extension builder settings.
You can't use alert from the global page. You can use console.debug, though, so that should help.
The bigger issue though is that you're treating the global page as though it has access to the page you're trying to modify via the window object. It doesn't work that way. location.href doesn't point to anything and window.open will probably not work from the global scope. (These are both leaky globals, which is something you want to avoid.) I haven't tested it, but something like this should work:
safari.application.addEventListener("beforeNavigate", function adflyChecker(event) {
var url = event.url.replace(/http:\/\/adf\.ly\/(\d+)\//, '');
safari.application.activeBrowserWindow.activeTab.url = url;
}, true);
This has the advantage of working on navigate and not requiring you to manually click the button. If you configure your extension to only apply to adf.ly URLs, then you can be sure that your code only fires when it's appropriate.
More information about the architecture of Safari extensions is available in the docs.
Related
Below is my code.
It is resulting in unexpected behaviour.
It navigates to bing.com but it does not fill in the text field. Also, I have noticed that the console get cleared after navigating to a new webpage.
window.location = "https://www.bing.com";
window.onload = function(){
var editSearch = document.getElementById("sb_form_q");
editSearch.value = "Quux";
}
You are binding the onload function to the existing window object.
When the browser loads the new page, it will make a new window object which won't have your property set on it.
JavaScript run in one page (even when you are running it through developer tools) can't persist variables onto a different page.
(Storage mechanisms like localStorage and cookies are available, but you would need code in the subsequent page to look for them).
JavaScript is only valid for the current page you are on. When you are executing code from DevTools console, you are executing code on that page itself. So, when you navigate to another page using window.location you loose the onload handler you have defined.
To add handlers to a different page, it must be connected to your page (the parent) in some way, like an iframe or a popup.
ifrm = document.getElementById('frame');
ifrm.src = 'http://example.com';
ifrm.contentWindow.onload = function () {
// do something here with
// ifrm.contentWindow.document.getElementById('form')
}
As #Quentin said.
But you can do another way like ..
var keyword = "Quux";
window.location = "https://www.bing.com/search?q="+keyword;
I was asked to take a look at what should be a simple problem with one of our web pages for a small dashboard web app. This app just shows some basic state info for underlying backend apps which I work heavily on. The issues is as follows:
On a page where a user can input parameters and request to view a report with the given user input, a button invokes a JS function which opens a new page in the browser to show the rendered report. The code looks like this:
$('#btnShowReport').click(function () {
document.getElementById("Error").innerHTML = "";
var exists = CheckSession();
if (exists) {
window.open('<%=Url.Content("~/Reports/Launch.aspx?Report=Short&Area=1") %>');
}
});
The page that is then opened has the following code which is called from Page_Load:
rptViewer.ProcessingMode = ProcessingMode.Remote
rptViewer.AsyncRendering = True
rptViewer.ServerReport.Timeout = CInt(WebConfigurationManager.AppSettings("ReportTimeout")) * 60000
rptViewer.ServerReport.ReportServerUrl = New Uri(My.Settings.ReportURL)
rptViewer.ServerReport.ReportPath = "/" & My.Settings.ReportPath & "/" & Request("Report")
'Set the report to use the credentials from web.config
rptViewer.ServerReport.ReportServerCredentials = New SQLReportCredentials(My.Settings.ReportServerUser, My.Settings.ReportServerPassword, My.Settings.ReportServerDomain)
Dim myCredentials As New Microsoft.Reporting.WebForms.DataSourceCredentials
myCredentials.Name = My.Settings.ReportDataSource
myCredentials.UserId = My.Settings.DatabaseUser
myCredentials.Password = My.Settings.DatabasePassword
rptViewer.ServerReport.SetDataSourceCredentials(New Microsoft.Reporting.WebForms.DataSourceCredentials(0) {myCredentials})
rptViewer.ServerReport.SetParameters(parameters)
rptViewer.ServerReport.Refresh()
I have omitted some code which builds up the parameters for the report, but I doubt any of that is relevant.
The problem is that, when the user clicks the show report button, and this new page opens up, depending on the types of parameters they use the report could take quite some time to render, and in the mean time, the original page becomes completely unresponsive. The moment the report page actually renders, the main page begins functioning again. Where should I start (google keywords, ReportViewer properties, etc) if I want to fix this behavior such that the other page can load asynchronously without affecting the main page?
Edit -
I tried doing the follow, which was in a linked answer in a comment here:
$.ajax({
context: document.body,
async: true, //NOTE THIS
success: function () {
window.open(Address);
}
});
this replaced the window.open call. This seems to work, but when I check out the documentation, trying to understand what this is doing I found this:
The .context property was deprecated in jQuery 1.10 and is only maintained to the extent needed for supporting .live() in the jQuery Migrate plugin. It may be removed without notice in a future version.
I removed the context property entirely and it didnt seem to affect the code at all... Is it ok to use this ajax call in this way to open up the other window, or is there a better approach?
Using a timeout should open the window without blocking your main page
$('#btnShowReport').click(function () {
document.getElementById("Error").innerHTML = "";
var exists = CheckSession();
if (exists) {
setTimeout(function() {
window.open('<%=Url.Content("~/Reports/Launch.aspx?Report=Short&Area=1") %>');
}, 0);
}
});
This is a long shot, but have you tried opening the window with a blank URL first, and subsequently changing the location?
$("#btnShowReport").click(function(){
If (CheckSession()) {
var pop = window.open ('', 'showReport');
pop = window.open ('<%=Url.Content("~/Reports/Launch.aspx?Report=Short&Area=1") %>', 'showReport');
}
})
use
`$('#btnShowReport').click(function () {
document.getElementById("Error").innerHTML = "";
var exists = CheckSession();
if (exists) {
window.location.href='<%=Url.Content("~/Reports/Launch.aspx?Report=Short&Area=1") %>';
}
});`
it will work.
I have some strange behavior on my window.location redirects in IE and Firefox as part of my angular application. When calling window.location = xyz the first time it works fine in IE/FF/Chrome. On the second call which is supposed to go to google.com, Chrome does what it's supposed to, but IE and FF don't do anything. In the IE web console I can see that the navigation was triggered but the page and URL hasn't changed in my window. Now if I press F5 on this page it goes to the page it's supposed to even though the URL at the top is not pointing there (both in IE and FF).
Has anyone ever encountered this problem and knows how to solve it? I've tried all versions of redirecting (window.location, window.location.href, windows.location.assign(), window.location.replace() and also the angular service $window) with no luck.
First call triggered from a button press (working fine in all browsers):
$scope.pressButton = function() {
var url = 'xyz/index.html';
$window.location = url;
};
Second call triggered by a keypress (only works in Chrome):
function exitModule() {
$window.location = 'http://www.google.com';
console.log('window.location'); // still pointing to the old page
}
Update with code calling the exitModule() function:
Note: The application is built with angularjs.
The exitModule() function gets called in all browsers, it's just the redirect which doesn't happen in IE/FF.
HTML:
<body ng-app="myModule" ng-controller="MainCtrl" ng-keydown="keyPress($event);">
JS:
// Handle global key press
$scope.keyPress = function(event){
if(event.which === 27) { // EscapeKey
exitModule();
} else {
$scope.$broadcast('keyPress', event);
}
};
Alright I found the issue and I'm aware that it was almost impossible to figure this out without having the full code available. The above code was a bit simplified therefore it was missing the problem. The function exitModule gets called as soon as a promise is resolved. The call looks like this:
Correct
dataService.saveModule().then(exitModule);
My code was as shown below with the brackets after exitModule which is wrong. I don't quite understand the behavior of FF/IE compared to Chrome though ...but that's for another day.
Wrong
dataService.saveModule().then(exitModule());
I am trying to start 3 applications from a browser by use of custom protocol names associated with these applications. This might look familiar to other threads started on stackoverflow, I believe that they do not help in resolving this issue so please dont close this thread just yet, it needs a different approach than those suggested in other threads.
example:
ts3server://a.b.c?property1=value1&property2=value2
...
...
to start these applications I would do
location.href = ts3server://a.b.c?property1=value1&property2=value2
location.href = ...
location.href = ...
which would work in FF but not in Chrome
I figured that it might by optimizing the number of writes when there will be effectively only the last change present.
So i did this:
function a ()
{
var apps = ['ts3server://...', 'anotherapp://...', '...'];
b(apps);
}
function b (apps)
{
if (apps.length == 0) return;
location.href = apps[0]; alert(apps[0]);
setTimeout(function (rest) {return function () {b(rest);};} (apps.slice(1)), 1);
}
But it didn't solve my problem (actually only the first location.href assignment is taken into account and even though the other calls happen long enough after the first one (thanks to changing the timeout delay to lets say 10000) the applications do not get started (the alerts are displayed).
If I try accessing each of the URIs separately the apps get started (first I call location.href = uri1 by clicking on one button, then I call location.href = uri2 by clicking again on another button).
Replacing:
location.href = ...
with:
var form = document.createElement('form');
form.action = ...
document.body.appendChild(form);
form.submit();
does not help either, nor does:
var frame = document.createElement('iframe');
frame.src = ...
document.body.appendChild(frame);
Is it possible to do what I am trying to do? How would it be done?
EDIT:
a reworded summary
i want to start MULTIPLE applications after one click on a link or a button like element. I want to achieve that with starting applications associated to custom protocols ... i would hold a list of links (in each link there is one protocol used) and i would try to do "location.src = link" for all items of the list. Which when used with 'for' does optimize to assigning only once (the last value) so i make the function something like recursive function with delay (which eliminates the optimization and really forces 3 distinct calls of location.src = list[head] when the list gets sliced before each call so that all the links are taken into account and they are assigned to the location.src. This all works just fine in Mozilla Firefox, but in google, after the first assignment the rest of the assignments lose effect (they are probably performed but dont trigger the associated application launch))
Are you having trouble looping through the elements? if so try the for..in statement here
Or are you having trouble navigating? if so try window.location.assign(new_location);
[edit]
You can also use window.location = "...";
[edit]
Ok so I did some work, and here is what I got. in the example I open a random ace of spades link. which is a custom protocol. click here and then click on the "click me". The comments show where the JSFiddle debugger found errors.
I am using Ajax and hash for navigation.
Is there a way to check if the window.location.hash changed like this?
http://example.com/blah#123 to http://example.com/blah#456
It works if I check it when the document loads.
But if I have #hash based navigation it doesn't work when I press the back button on the browser (so I jump from blah#456 to blah#123).
It shows inside the address box, but I can't catch it with JavaScript.
The only way to really do this (and is how the 'reallysimplehistory' does this), is by setting an interval that keeps checking the current hash, and comparing it against what it was before, we do this and let subscribers subscribe to a changed event that we fire if the hash changes.. its not perfect but browsers really don't support this event natively.
Update to keep this answer fresh:
If you are using jQuery (which today should be somewhat foundational for most) then a nice solution is to use the abstraction that jQuery gives you by using its events system to listen to hashchange events on the window object.
$(window).on('hashchange', function() {
//.. work ..
});
The nice thing here is you can write code that doesn't need to even worry about hashchange support, however you DO need to do some magic, in form of a somewhat lesser known jQuery feature jQuery special events.
With this feature you essentially get to run some setup code for any event, the first time somebody attempts to use the event in any way (such as binding to the event).
In this setup code you can check for native browser support and if the browser doesn't natively implement this, you can setup a single timer to poll for changes, and trigger the jQuery event.
This completely unbinds your code from needing to understand this support problem, the implementation of a special event of this kind is trivial (to get a simple 98% working version), but why do that when somebody else has already.
HTML5 specifies a hashchange event. This event is now supported by all modern browsers. Support was added in the following browser versions:
Internet Explorer 8
Firefox 3.6
Chrome 5
Safari 5
Opera 10.6
Note that in case of Internet Explorer 7 and Internet Explorer 9 the if statment will give true (for "onhashchange" in windows), but the window.onhashchange will never fire, so it's better to store hash and check it after every 100 millisecond whether it's changed or not for all versions of Internet Explorer.
if (("onhashchange" in window) && !($.browser.msie)) {
window.onhashchange = function () {
alert(window.location.hash);
}
// Or $(window).bind( 'hashchange',function(e) {
// alert(window.location.hash);
// });
}
else {
var prevHash = window.location.hash;
window.setInterval(function () {
if (window.location.hash != prevHash) {
prevHash = window.location.hash;
alert(window.location.hash);
}
}, 100);
}
EDIT -
Since jQuery 1.9, $.browser.msie is not supported. Source: http://api.jquery.com/jquery.browser/
There are a lot of tricks to deal with History and window.location.hash in IE browsers:
As original question said, if you go from page a.html#b to a.html#c, and then hit the back button, the browser doesn't know that page has changed. Let me say it with an example: window.location.href will be 'a.html#c', no matter if you are in a.html#b or a.html#c.
Actually, a.html#b and a.html#c are stored in history only if elements '<a name="#b">' and '<a name="#c">' exists previously in the page.
However, if you put an iframe inside a page, navigate from a.html#b to a.html#c in that iframe and then hit the back button, iframe.contentWindow.document.location.href changes as expected.
If you use 'document.domain=something' in your code, then you can't access to iframe.contentWindow.document.open()' (and many History Managers does that)
I know this isn't a real response, but maybe IE-History notes are useful to somebody.
Firefox has had an onhashchange event since 3.6. See window.onhashchange.
I was using this in a react application to make the URL display different parameters depending what view the user was on.
I watched the hash parameter using
window.addEventListener('hashchange', doSomethingWithChangeFunction);
Then
function doSomethingWithChangeFunction () {
let urlParam = window.location.hash; // Get new hash value
// ... Do something with new hash value
};
Worked a treat, works with forward and back browser buttons and also in browser history.
You could easily implement an observer (the "watch" method) on the "hash" property of "window.location" object.
Firefox has its own implementation for watching changes of object, but if you use some other implementation (such as Watch for object properties changes in JavaScript) - for other browsers, that will do the trick.
The code will look like this:
window.location.watch(
'hash',
function(id,oldVal,newVal){
console.log("the window's hash value has changed from "+oldval+" to "+newVal);
}
);
Then you can test it:
var myHashLink = "home";
window.location = window.location + "#" + myHashLink;
And of course that will trigger your observer function.
Another great implementation is jQuery History which will use the native onhashchange event if it is supported by the browser, if not it will use an iframe or interval appropriately for the browser to ensure all the expected functionality is successfully emulated. It also provides a nice interface to bind to certain states.
Another project worth noting as well is jQuery Ajaxy which is pretty much an extension for jQuery History to add ajax to the mix. As when you start using ajax with hashes it get's quite complicated!
var page_url = 'http://www.yoursite.com/'; // full path leading up to hash;
var current_url_w_hash = page_url + window.location.hash; // now you might have something like: http://www.yoursite.com/#123
function TrackHash() {
if (document.location != page_url + current_url_w_hash) {
window.location = document.location;
}
return false;
}
var RunTabs = setInterval(TrackHash, 200);
That's it... now, anytime you hit your back or forward buttons, the page will reload as per the new hash value.
I've been using path.js for my client side routing. I've found it to be quite succinct and lightweight (it's also been published to NPM too), and makes use of hash based navigation.
path.js NPM
path.js GitHub
SHORT and SIMPLE example
Click on buttons to change hash
window.onhashchange = () => console.log(`Hash changed -> ${window.location.hash}`)
<button onclick="window.location.hash=Math.random()">hash to Math.Random</button>
<button onclick="window.location.hash='ABC'">Hash to ABC</button>
<button onclick="window.location.hash='XYZ'">Hash to XYZ</button>