I need analog of Chrome chrome.tabs.update or Firefox loadURI in Safari.
I've tried safari.application.activeBrowserWindow.activeTab.url = newURL but it doesn't work with bookmarklets urls like javascript:...
That's right, it doesn't work. You're going to have to pass a message to an injected script in a web page to open the bookmarklet. For example:
in the global page's script:
var activeTab = safari.application.activeBrowerWindow.activeTab;
var url = "javascript:alert('hello sucker')";
activeTab.page.dispatchMessage("pleaseLoadThisUrl", url);
in the injected script:
safari.self.addEventListener("message", handleMessage, false);
function handleMessage(e) {
if (e.name == "pleaseLoadThisUrl") {
window.location = e.message;
}
}
That will work.
Related
I am currently using VueJS 2.x and have not gone VueRouter yet (and am not able anyway).
Quite simply, I want Vue to detect a URL fragment like https://example.com/mypage#record-17 and for example simulate the click of the following modal link:
<a :id="record.id" :click="openModal(record.id)">Open this record</a>
Should I just parse window.location myself or is there a more elegant way to do it? I also want to not use jQuery.
This is our script for parsing args from the hash. It lets you put a query string after the hash, that will be parsed by the script. If, like us, you also need to pass an url that you don't want parsed, put it at the end in a 'src' argument.
var args = (function () {
var returnVal = {};
var argString = window.location.hash;
//everything after src belongs as part of the url, not to be parsed
var argsAndSrc = argString.split(/src=/);
returnVal["src"] = argsAndSrc[1];
//everything before src is args for this page.
var argArray = argsAndSrc[0].split("&");
for (var i = 0; i < argArray.length; i++) {
var nameVal = argArray[i].split("=");
//strip the hash
if (i == 0) {
var name = nameVal[0];
nameVal[0] = name.slice(1);
}
returnVal[nameVal[0]] = decodeURI(nameVal[1]);
}
return returnVal
})();
I'm trying to figure out how to make a Google Apps Script deployed as a web app download a PDF that's generated on a click. It almost works, but the resulting file isn't valid. I can't figure out if it's an encoding issue or something else.
In Apps Script the code looks simple:
function makePDF() {
...
var pdfBlob = doc.getAs('application/pdf');
return Utilities.base64Encode(pdfBlob.getBytes());
}
In the browser, there's a click handler:
function clickHandler(ev) {
ev.preventDefault();
google.script.run
.withSuccessHandler(function(data) {
var pdf = new Blob([window.atob(data)]);
var href = window.URL.createObjectURL(pdf);
var link = document.querySelector('#hiddenLink');
link.href = href;
link.click();
})
.makePDF();
}
Any suggestions?
Thanks!
I figured it out, so posting the answer if anyone else is trying to pass a PDF from Apps Script to the client javascript. It's all much simpler than I had made it.
Rather than messing around with base64 encodings, just pass back the bytes array:
function makePDF() {
...
var pdfBlob = DocumentApp.openById('1234').getAs('application/pdf');
return pdfBlob.getBytes();
}
Now, on the client side, construct a new Blob from an ArrayBuffer. That's easy too:
function clickHandler(ev) {
google.script.run
.withSuccessHandler(function(data) {
var arr = new Uint8Array(data);
var blob = new Blob([arr.buffer], {type: 'application/pdf'});
var obj_url = window.URL.createObjectURL(blob);
var hiddenLink = document.getElementById('hiddenPDFLink');
hiddenLink.setAttribute('href', obj_url);
hiddenLink.setAttribute('download', 'filename.pdf');
hiddenLink.click();
})
.makePDF();
}
And that's it! Hope someone else finds this helpful.
I assume that your makePDF function is doing some other stuffs/Calculation and at the end you need that document to be downloaded to local computer.
What you can do is inside success handler
var link = document.querySelector('#hiddenLink');
link.href = "https://docs.google.com/feeds/download/documents/export/Export?id=**TheIdOfDocumenToBeDownloaded**&exportFormat=pdf";
link.click();
It will then give you a prompt to save document on to local computer.
This is my code. It works in Firefox and Chrome but not Safari. I get no errors.
<script>
var cleanData = new FormData();
cleanData.append("test", "test");
alert(cleanData.get("test"));
</script>
Does anyone know a workaround?
Apparently, Safari has no means of getting values stored in FormData objects at this time. There is no workaround at this time, and apparently it's not practical to polyfill.
Sorry :(
Notes:
https://developer.mozilla.org/en-US/docs/Web/API/FormData/get#Browser_compatibility
https://www.bountysource.com/issues/27573236-is-it-possible-to-polyfill-missing-formdata-methods
I solved this by conditionally (if Safari is the browser) iterating through the elements property of an actual form. For all other browser, my wrapper just iterates through FormData entries(). The end result of my function, in either case, is a simple javascript object (JSON) which amounts to name/value pairs.
function FormDataNameValuePairs(FormName)
{
var FormDaytaObject={};
var FormElement=$('#'+FormName).get(0);
if (IsSafariBrowser())
{
var FormElementCollection=FormElement.elements;
//console.log('namedItem='+FormElementCollection.namedItem('KEY'));
var JQEle,EleType;
for (ele=0; (ele < FormElementCollection.length); ele++)
{
JQEle=$(FormElementCollection.item(ele));
EleType=JQEle.attr('type');
// https://github.com/jimmywarting/FormData/blob/master/FormData.js
if ((! JQEle.attr('name')) ||
(((EleType == 'checkbox') || (EleType == 'radio')) &&
(! JQEle.prop('checked'))))
continue;
FormDaytaObject[JQEle.attr('name')]=JQEle.val();
}
}
else
{
var FormDayta=new FormData(FormElement);
for (var fld of FormDayta.entries())
FormDaytaObject[fld[0]]=fld[1];
}
return FormDaytaObject;
}
where IsSafariBrowser() is implemented by whatever your favorite method is, but I chose this:
function IsSafariBrowser()
{
var VendorName=window.navigator.vendor;
return ((VendorName.indexOf('Apple') > -1) &&
(window.navigator.userAgent.indexOf('Safari') > -1));
}
Example usage in OP's case, assuming that you have an actual form called CleanDataForm instead of creating a FormData from scratch:
var cleanData=FormDataNameValuePairs('CleanDataForm');
alert(cleanData.test);
I'm working on a web application where I need to access elements of an iFrame using JavaScript. To do that, the iFrame has to send an "Allow-Control-Allow-Origin: *" header to the browser.
Unfortunately this doesn't happen, that's why I'm using an extension to modify the response headers, but for some reason, setResponseHeader doesn't work.
It gets even more confusing since I'm using setResponseHeader to strip X-Frame-Options, but when I'm setting a custom header, it just won't work.
I'm using Firefox's "Inspect Element"'s Network tab to observe the requests, and while it shows the request header being set correctly, it doesn't show the response header.
That's how I'm setting the request and response headers.
var chrome = require("chrome");
chrome.Cc["#mozilla.org/observer-service;1"].getService( chrome.Ci.nsIObserverService ).addObserver({
observe : function(subject, topic, data) {
var channel = subject.QueryInterface( chrome.Ci.nsIHttpChannel );
channel.setRequestHeader("x-mysite-extended", "somedata", false);
}
},"http-on-modify-request",false);
chrome.Cc["#mozilla.org/observer-service;1"].getService( chrome.Ci.nsIObserverService ).addObserver({
observe : function(subject, topic, data) {
var channel = subject.QueryInterface( chrome.Ci.nsIHttpChannel );
channel.setResponseHeader("x-mysite-extended", "somedata", false);
}
},"http-on-examine-response",false);
Again, the request header works according to the Network tab. I tried http-on-modify-request to set the response header but that didn't work as well.
That's how I'm stripping of the X-Frame-Options header, which works.
let myListener =
{
observe : function (aSubject, aTopic, aData)
{
console.log(aTopic);
if (aTopic == "http-on-examine-response")
{
let channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
try
{ // getResponseHeader will throw if the header isn't set
let hasXFO = channel.getResponseHeader('X-Frame-Options');
if (hasXFO)
{
// Header found, disable it
channel.setResponseHeader('X-Frame-Options', '', false);
}
}
catch (e) {}
}
}
}
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.addObserver(myListener, "http-on-examine-response", false);
I've been trying to solve this for two hours now so any help is appreciated. Thanks.
You're adding obserer for http-on-examine-response, with this you can only getResponseHeader
change it to http-on-modify-request. then you can setRequestHeader, you cant getResponseHeader in on modify request though.
This is scrap code but it worked for me:
observe : function(aSubject, aTopic, aData) {
// Make sure it is our connection first.
if (aSubject == channel) {
//this is our channel
//alert('is my mine');
cdxFire.myChannel = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
if (cdxFire.myChannel.requestMethod == 'GET') {
//alert('its a get so need to removeObserver now');
//cdxFire.observerService.removeObserver(modHeaderListener, "http-on-modify-request");
}
if (aTopic == 'http-on-modify-request' && cdxFire.myChannel.requestMethod == 'POST') {
//can set headers here including cookie
try {
var xContentLength = httpChannel.getRequestHeader('Content-Length');
var xContentType = httpChannel.getRequestHeader('Content-Type');
//alert('content length is there so change it up');
cdxFire.myChannel.setRequestHeader('Content-Type','',false);
cdxFire.myChannel.setRequestHeader('Content-Type',xContentType,false);
cdxFire.myChannel.setRequestHeader('Content-Length','',false);
cdxFire.myChannel.setRequestHeader('Content-Length',xContentLength,false);
i have this code, execcommand for BOLD, and it is not working under opera. anybody have some idea why ? thanks
<script type="text/javascript">
function SetToBold () {
Editor.execCommand ('bold', false, null);
}
function start() {
frames.iView.document.designMode = "On";
var myIFrame = document.getElementById("iView");
browser = navigator.appName;
if (document.all) { //IE
var iframe_window = window.frames["iView"];
iframe_window.document.body.focus();
}
else { //Firefox
field = myIFrame.contentWindow.document.getElementsByTagName("body")[0];
setTimeout("field.focus()",250);
}
if (browser == "Netscape") {
Editor = myIFrame.contentWindow.document;
Editor.designMode = 'On';
Editor.body.contentEditable = 'true';
}
else if(browser == "Microsoft Internet Explorer") {
frames['iView'].document.designMode='On';
Editor = frames['iView'].document;
}
}
i call start function with body onload. it is ok firefox and ie, but doesnt work in opera.
anybody knows about some restrictions of execcommand in opera ? thanks
In the quoted script, the variable "Editor" is given a value only inside the browser sniffing section. In Opera "Editor" will be undefined and hence trying to do "Editor.execCommand()" will throw an error.
Rather than
if (browser == "Netscape") {
Editor = myIFrame.contentWindow.document;
Editor.designMode = 'On';
Editor.body.co`enter code here`ntentEditable = 'true';
}
else if(browser == "Microsoft Internet Explorer") {
frames['iView'].document.designMode='On';
Editor = frames['iView'].document;
}
you should drop the browser sniffing and do something like
if ( myIFrame && myIFrame.contentWindow ) {
Editor = myIFrame.contentWindow.document;
Editor.designMode = 'On';
}
else if( frames['iView'] ) {
frames['iView'].document.designMode='On';
Editor = frames['iView'].document;
}
Hope that helps :)
before giving an answer I want to be sure about what you are asking. The script is testing Netscape or IE. Opening dragonfly console, you can discover the values of your script. I have the feeling you will have difficulties with Webkit too.
>>> var browser = navigator.appName
undefined
>>> browser
"Opera"
ive noted that the newer versions of opera appear as NETSCAPE when you ask for it with navigator.appName. i remember that was not always so because i also got some older "ifs" around my source where i used the regular name. cant say since when this is, just saying.