I have a problem with events, when open pdf in external window. They are not triggered even with 'viewerId' attribute. Here is my code:
HTML
<a *ngIf="document.s3_link" class="document-title" (click)="openDocument(document)">{{ document.description }}</a
>
<ng2-pdfjs-viewer
#externalPdfViewer
viewerId="MyUniqueID"
[externalWindow]="true"
(onDocumentLoad)="highlightSearchTerm()"
></ng2-pdfjs-viewer>
TypeScript
openDocument(document): void {
this.getDocumentBlob(document.s3_link).subscribe(res => {
this.externalPdfViewer.pdfSrc = res
this.externalPdfViewer.downloadFileName = document.description
this.externalPdfViewer.refresh()
})
}
getDocumentBlob(link): Observable<any> {
let headers = new HttpHeaders()
headers = headers.set("Accept", "application/pdf")
return this.http.get(link, { headers: headers, responseType: "blob" })
}
highlightSearchTerm() {
this.externalPdfViewer.PDFViewerApplication.findController.executeCommand(
"find",
{
caseSensitive: false,
findPrevious: undefined,
highlightAll: true,
phraseSearch: true,
query: this.initQuery,
}
)
}
Found this in documentation in one of the issues.
When you are opening PDF in a new window, events cannot be emitted back to former window.
Please see this SO: Communication between tabs or windows
Documentation needs to be updated to reflect this. Using above techniques, it may be achieved, but that would require an improvement/implementation.
Related
So, basically I'm trying to receive a call from provider to my app. For that purpose Quickblox gives us a listener to receive the upcoming calls onCallListener. So here is my code snippet that should work but doesn't.
const calleesIds = [4104]
const sessionType = QB.webrtc.CallType.VIDEO
const additionalOptions = {}
let callSession = QB.webrtc.createNewSession(calleesIds, sessionType, null, additionalOptions)
console.log(callSession, "SESSION")
const mediaParams = {
audio: true,
video: true,
options: {
muted: true,
mirror: true,
},
elemId: "myVideoStream"
}
QB.webrtc.onCallListener = function(session: any, extension: object) {
callSession = session
console.log('asdasd')
// if you are going to take a call
session.getUserMedia(mediaParams, function (error: object, stream: object) {
if (error) {
console.error(error)
} else {
session.accept(extension)
session.attachMediaStream("videoStream", stream)
}
})
}
P.S. I also integrated chat which works perfect!
Found the solution by myself! Whenever you create a user and dialog id, search that user in the quickblox dashboard by the dialogId and change its settings: you will see that userId and providerId is the same which is wrong. So put your userId in the userId field and save that. After that you video calling listeners will work fine!)
P. S. also in the backend replace provider token with user token.
In my typescript which is attached to the HTML being run by my app, I am creating a new window for my settings. From what I can tell, the preloaded script is also being loaded onto the new window once it's opened, but the window isn't receiving IPC messages from the main script.
Here is the preloaded script:
const { contextBridge, ipcRenderer } = require("electron");
console.log("preloaded!");
contextBridge.exposeInMainWorld("api", {
send: (channel, data) => {
let validChannels = ["toMain", "select-dirs", "toSettings", "fromSettings"];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
let validChannels = ["fromMain", "toSettings", "fromSettings"];
if (validChannels.includes(channel)) {
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
},
});
And here is the typescript file that I've attached to the second window's HTML.
(<any>window).api.receive("toSettings", (data: any) => {
console.log(data);
})
var closeButton: HTMLButtonElement;
var settings = "";
var settignsDir = "";
document.onreadystatechange = () => {
if (document.readyState == "interactive") {
(<any>window).api.send("fromSettings", "ready")
closeButton = <HTMLButtonElement>document.getElementById("closeButton");
closeButton.addEventListener("click", () => {
(<any>window).api.send("toMain", "refresh");
self.close();
});
}
};
I'm using the same preloaded script for my renderer and using the same receiving code, it's working just fine. And from the second window's typescript, I can properly send IPC messages to the main process. But I can't receive any messages on the second window. I'm thinking I need to re-preload the file directly to the second window via the features array in window.open(). Oh and here's the code that is opening the settings window.
window.open(
"./html/settings.html",
"_blank",
"top=200,left=600,frame=false,nodeIntegration=no"
);
According to the Electron documentation, you can also include a preload in the third-string but I can't figure out how as the documentation fails to have an example and I can't find one anywhere.
In response to Kdau:
Here is the requested code:
(<any>window).api.receive("fromSettings", (data: any) => {
(<any>window).api.send("toSettings", "WHAT!");
})
I was mainly using it to see if the settings or child window as you called it was receiving the message. If you could clarify what you mean by address to the child window because I thought that the preload script should be automatically parsing, "ok so this channel needs to go to this receiver".
I would like to point out that in the code snippet that you included returns this error:
Argument of type '({ url }: HandlerDetails) => { frame: boolean; webPreferences: { nodeIntegration: boolean; preload: string; }; } | undefined' is not assignable to parameter of type '(details: HandlerDetails) => { action: "deny"; } | { action: "allow"; overrideBrowserWindowOptions?: BrowserWindowConstructorOptions | undefined; }'.
Type '{ frame: boolean; webPreferences: { nodeIntegration: boolean; preload: string; }; } | undefined' is not assignable to type '{ action: "deny"; } | { action: "allow"; overrideBrowserWindowOptions?: BrowserWindowConstructorOptions | undefined; }'.
Type 'undefined' is not assignable to type '{ action: "deny"; } | { action: "allow"; overrideBrowserWindowOptions?: BrowserWindowConstructorOptions | undefined; }'.
I don't know what to do with this.
While you can pass a preload in the third argument, the more flexible and reliable approach is to use setWindowOpenHandler. So, just after constructing your main window (assuming the variable is mainWindow):
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url === './html/settings.html') {
return {
action: 'allow',
overrideBrowserWindowOptions: {
frame: false,
webPreferences: {
nodeIntegration: false,
preload: 'my-child-window-preload-script.js'
}
}
}
}
})
However, most (all??) options are inherited from the parent window anyway, and I would imagine that your preload has been inherited and that is why you can send messages from the child window.
As for not receiving messages in the child window, the messages aren't being addressed to that window's webContents by your code. Sending to the main window's webContents won't work. Unfortunately, the IPC in Electron isn't a broadcast system where every process receives every message filtered only by channel. The channel feature sits on top of process-based targeting. Each render process can only send messages to the main process, and the main process must address one specific render process at a time when sending.
So, to communicate between two render processes, use the main process as a relay. In the main process, to get the webContents of a window that was opened through window.open, you can listen for a message from that process (such as one you send as soon as it loads) and/or call webContents.getAllWebContents() and iterate through to find the right one.
We've got this rather large'ish and old'ish asp web application, that started to behave poorly because of (as i understand) XHR requests in beforeunload event handler. There is quite a logic tied to this, so i cannot just throw it away.
The code in beforeunload was like this:
$(window).on('beforeunload', function () {
$.ajax('/site/$hdr/unload', { async: true, cache: false });
});
and in server side something like this:
private class HttpModule : IHttpModule
{
private static void PostAcquireRequestState(object sender, EventArgs e)
{
var context = HttpContext.Current;
var path = context.Request.AppRelativeCurrentExecutionFilePath;
if (path.StartsWith("~/$hdr/unload"))
{
//do things
}
}
I'm trying to use the fetch api instead of synchronous ajax request used before. In chrome everything works fine, but FireFox 89.0 behaves strangely.
Code in beforeunload is like this:
let i;
let data = new Headers();
if (window.FMS && window.FMS.HttpHeaders) {
for (i in window.FMS.HttpHeaders) {
if (FMS.HttpHeaders.hasOwnProperty(i)) {
data.append('X-FMS-' + i, FMS.HttpHeaders[i]);
}
}
}
fetch(FMS.UnloadUrl + '?_=' + jQuery.now(), {
method: 'GET',
keepalive: true,
cache: 'no-cache',
headers: data }
).then(response => response.ok)
.then(IsOk => console.debug(IsOk.toString()))
.catch((error) => {console.error('Error:', error)});
If my understanding is correct - the server side now have to receive OPTIONS preflight request first? I never receive this. Sometimes i receive the GET request, but usually in the FireFox console i can see TypeError: NetworkError when attempting to fetch resource error or notification that the XHR request has been blocked by DevTools.
I tried opening a PDF file using the window.open(), but the window opens and closes automatically and the file is downloaded like any other file. How to make the pdf file open in new tab? There are no ad blockers installed.
From #barbsan idea, I changed the http headers and received a blob and used that to display the blob as pdf using window.open(). It worked.
Here is my sample code.
In service file
downloadPDF(url): any {
const options = { responseType: ResponseContentType.Blob };
return this.http.get(url, options).map(
(res) => {
return new Blob([res.blob()], { type: 'application/pdf' });
});
}
In component file
this.dataService.downloadPDF(url).subscribe(res => {
const fileURL = URL.createObjectURL(res);
window.open(fileURL, '_blank');
});
One liner solution to open a pdf file in new tab. You just need to have file url and use this function on button click.
window.open(url, '_blank');
you can display pdf fle in new tab by the line:
window.open(fileUrl, '_blank');
The fileUrl is a variable that contains the file path.
For the Angular 13 version
downloadPDF(url: string): Observable<Blob> {
const options = { responseType: 'blob' as 'json' };
return this.httpClient
.get<Blob>(url, options)
.pipe(map(res => new Blob([res], { type: 'application/pdf' })));
}
you need user the "target="_blank" in the tag ;
exemple: <a target="_blank" href="https://www.google.com/"> </a>
How to make it work in Angular 10, changes just a little bit, this in the service file from #K Harish answer
import { map } from 'rxjs/operators';
return this.http.get(url, options).pipe(map(
(res) => {
return new Blob([res], { type: 'application/pdf' });
}));
I'm trying the Wikipedia client login flow depicted in the API:Login docs, but something wrong happens:
1) I correctly get a token raised with the HTTP GET https://en.wikipedia.org/w/api.php?action=query&meta=tokens&type=login&format=json
and I get a valid logintoken string.
2.1) I then try the clientlogin like:
HTTP POST /w/api.php?action=clientlogin&format=json&lgname=xxxx&lgtoken=xxxx%2B%5C
and the POST BODY was
{
"lgpassword" : "xxxxx",
"lgtoken" : "xxxxx"
}
But I get an error:
{
"error": {
"code": "notoken",
"info": "The \"token\" parameter must be set."
},
"servedby": "mw1228"
}
If I try to change lgtoken to token I get the same result.
2.2) I have then tried the old method i.e. action=login and passing the body, but it does not work, since it gives me back another login token: HTTP POST https://en.wikipedia.org/w/api.php?action=login&format=json&lgname=xxxx
and the same POST BODY
I then get
{
"warnings": {}
},
"login": {
"result": "NeedToken",
"token": "xxxxx+\\"
}
where the docs here states that
NeedToken if the lgtoken parameter was not provided or no session was active (e.g. your cookie handling is broken).
but I have passed the lgtoken in the json body as showed.
I'm using Node.js and the built-in http module, that is supposed to pass and keep session Cookies in the right way (with other api it works ok).
I have found a similar issue on a the LrMediaWiki client here.
[UPDATE]
This is my current implementation:
Wikipedia.prototype.loginUser = function (username, password) {
var self = this;
return new Promise((resolve, reject) => {
var cookies = self.cookies({});
var headers = {
'Cookie': cookies.join(';'),
'Accept': '*/*',
'User-Agent': self.browser.userAgent()
};
// fetch login token
self.api.RequestGetP('/w/api.php', headers, {
action: 'query',
meta: 'tokens',
type: 'login',
format: 'json'
})
.then(response => { // success
if (response.query && response.query.tokens && response.query.tokens['logintoken']) {
self.login.logintoken = response.query.tokens['logintoken'];
self.logger.info("Wikipedia.login token:%s", self.login);
return self.api.RequestPostP('/w/api.php', headers, {
action: 'login',
format: 'json',
lgname: username
},
{
lgpassword: password,
lgtoken: self.login.logintoken
});
} else {
var error = new Error('no logintoken');
return reject(error);
}
})
.then(response => { // success
return resolve(response);
})
.catch(error => { // error
self.logger.error("Wikipedia.login error%s\n%#", error.message, error.stack);
return reject(error);
});
});
}//loginUser
where this.api is a simple wrapper of the Node.js http, the source code is available here and the api signatures are like:
Promise:API.RequestGetP(url,headers,querystring)
Promise:API.RequestPostP(url,headers,querystring,body)
If the currently accepted answer isn't working for someone, the following method will definitely work. I've used the axios library to send requests. Any library can be used but the key lies in formatting the body and headers correctly.
let url = "https://test.wikipedia.org/w/api.php";
let params = {
action: "query",
meta: "tokens",
type: "login",
format: "json"
};
axios.get(url, { params: params }).then(resp => {
let loginToken = resp.data.query.tokens.logintoken
let cookie = resp.headers["set-cookie"].join(';');
let body = {
action: 'login',
lgname: 'user_name',
lgpassword: 'password',
lgtoken: loginToken,
format: 'json'
}
let bodyData = new URLSearchParams(body).toString();
axios.post(url, bodyData, {
headers: {
Cookie: cookie,
}
}).then(resp => {
// You're now logged in!
// You'll have to add the following cookie in the headers again for any further requests that you might make
let cookie = resp.headers["set-cookie"].join(';')
console.log(resp.data)
})
})
And you should be seeing a response like
{
login: { result: 'Success', lguserid: 0000000, lgusername: 'Username' }
}
The second post request was where I got stuck for several hours, trying to figure out what was wrong. You need to send the data in an encoded form by using an API like URLSearchParams, or by just typing up the body as a string manually yourself.
I think from what you are saying you have lgtoken and lgname in the URL you are using, and then lgpassword and lgtoken (again!) in a JSON-encoded POST body.
This is not how the Mediawiki API works.
You submit it all as POST parameters. JSON is never involved, except when you ask for the result to come back in that format. I can't help you fix your code as you don't provide it, but that's what you need to do. (If you edit your question with your code, I'll do my best to help you.)
After seeing your code, I'll presume (without knowing the detail of your code) that you want something like this:
return self.api.RequestPostP('/w/api.php', headers, {
action: 'login',
format: 'json',
lgname: username,
lgpassword: password,
lgtoken: self.login.logintoken
});