I need to remove fresh chat window from all pages except the home page. I put the script inside the home component mounted part. But once it loads it never closes. If I refresh the page it disappears. I think it's because of the SPA. I think once it loads the JS it does not clear the header script.
mounted() {
function initFreshChat() {
window.fcWidget.init({
token: "***",
host: "https://wchat.freshchat.com"
});
}
function initialize(i, t) {
var e;
i.getElementById(t) ? initFreshChat() : ((e = i.createElement("script")).id = t, e.async = !0, e.src = "https://wchat.freshchat.com/js/widget.js", e.onload = initFreshChat, i.head.appendChild(e))
}
function initiateCall() {
initialize(document, "freshchat-js-sdk")
}
window.addEventListener ? window.addEventListener("load", initiateCall, !1) : window.attachEvent("load", initiateCall, !1);
}
Try window.fcWidget.destroy(); to unload freshchat. Learn more at https://developers.freshchat.com/web-sdk/ They provide all required events and functions to open and close chatbox.
I did something similar and added it to the useEffect hook in react, just specifying on each page the argument of true if I wanted it hidden.
const addFreshchat = (hiddenOnLoad) => {
const hideFreshChat = {
config: {
headerProperty: {
hideChatButton: true,
},
},
token: 'your-account-token',
host: 'https://<freshchatUrl>',
};
const showFreshChat = {
token: 'your-account-token',
host: 'https://<freshchatUrl>',
};
const freshChatConfig = hiddenOnLoad ? hideFreshChat : showFreshChat;
function initFreshChat() {
window?.fcWidget?.init(freshChatConfig);
}
function initialize(i, t) {
let e;
const initiate = i.getElementById(t)
? initFreshChat()
: ((e = i.createElement('script')).id = t, e.async = !0, e.src = 'https://wchat.eu.freshchat.com/js/widget.js', e.onload = initFreshChat, i.head.appendChild(e));
return initiate;
}
function initiateCall() {
initialize(document, 'freshchat-js-sdk');
}
function addSDK() {
const addListener = window.addEventListener ? window.addEventListener('load', initiateCall, !1) : window.attachEvent('load', initiateCall, !1);
return addListener;
}
addSDK();
};
I just integrated fresh chat in my app:
window.fcWidget.show() // show widget
window.fcWidget.hide() // hide widget
window.fcWidget.init({
token: 'your token',
host: 'host'
}) // initialize
window.fcWidget.destroy() //destroy
Related
I dont know much about vue/bootstrap and reading docs does not help me to understand how it all works.
How to open a modal that is created after the page was loaded. From user input. User clicks button then the modal loads into a list prop and then renders into DOM and then it opens up.
Im at the point where i created event when user clicks the button that loads the modal into the list, but how do you catch the "modal has been added to DOM" event and then you can use getElementByID to instantiate the modal and then use .show() to show it?
I can see that the card that supposed to render on the page loads/renders, but the method get null. Im guessing that the method runs before the page/DOM has been re-rendered. So how do you run another method with parameter of sorts after the custom event that added the item to list has been triggered?
The code is too big and convoluted to post. But if need be i could try to trim it down, but its a mess.
App.vue
<template>
<div class="container-center">
<AnimeList />
</div>
</template>
AnimeList.vue
<template>
<div class="containerlist">
<AnimeCardModal
v-for="anime in animeList"
:anime="anime"
#checkAnimeListForRelatedEvent="checkAnimeListForRelated"
/>
</div>
</template>
<script setup>
import { defineComponent } from "vue";
import AnimeCardModal from "./AnimeCardModal.vue";
import axios from "axios";
</script>
<script>
export default defineComponent({
name: "AnimeList",
data() {
return {
animeList: [],
limit: 30,
page: 1,
reachedEnd: false,
};
},
methods: {
async getAnimeLsit() {
const res = await axios.get("/api", {
params: { page: this.page, limit: this.limit },
});
this.animeList = res.data.data;
this.page = res.data.next.page;
this.limit = res.data.next.limit;
},
async getNextBatch() {
let bottomOfWindow =
document.documentElement.scrollTop + window.innerHeight ===
document.documentElement.offsetHeight;
if (bottomOfWindow && !this.reachedEnd) {
const res = await axios.get("/api", {
params: { page: this.page, limit: this.limit },
});
res.data.data.map((item) => {
this.animeList.push(item);
});
if (!res.data.next) {
this.reachedEnd = true;
} else {
this.page = res.data.next.page;
this.limit = res.data.next.limit;
}
}
},
async checkAnimeListForRelated(animeID) {
if (!this.animeList.filter((anime) => anime.id === animeID).length > 0) {
const res = await axios.get("/api/anime", {
params: { id: animeID },
});
if (res.data.data.length > 0) {
this.animeList.push(res.data.data[0]);
console.log("added to list");
}
}
// Add the anime to the list
},
},
created() {
window.addEventListener("scroll", this.getNextBatch);
},
deactivated() {
window.removeEventListener("scroll", this.getNextBatch);
},
async mounted() {
await this.getAnimeLsit();
},
components: {
AnimeCardModal,
},
});
</script>
Here is the method that gets triggered by the user click event where it loads the Not in main list data and should render on page/DOM.
async checkAnimeListForRelated(animeID) {
if (!this.animeList.filter((anime) => anime.id === animeID).length > 0) {
const res = await axios.get("/api/anime", {
params: { id: animeID },
});
if (res.data.data.length > 0) {
this.animeList.push(res.data.data[0]); <--------------------------------------
console.log("added to list");
}
}
// Add the anime to the list
},
The added item is a modal with element id. I want to instantiate this element as new Modal() and open it with .show().
But the i get error that the element does not exist = null and i cant get it, but i can see it on screen.
EDIT:1
Ok so like as per usual, once i post on SO i find an answer to my problem, but it turns into another problem.
SO to get the rendered element i used this:
async checkAnimeListForRelated(animeID) {
if (!this.animeList.filter((anime) => anime.id === animeID).length > 0) {
const res = await axios.get("/api/anime", {
params: { id: animeID },
});
if (res.data.data.length > 0) {
this.animeList.push(res.data.data[0]);
console.log("added to list");
this.$parent.$nextTick(() => { <----------------------
const myModal = new Modal(
document.getElementById("anime-card-modal-" + animeID)
);
myModal.show();
}
}else{
const myModal = new Modal(
document.getElementById("anime-card-modal-" + animeID)
);
myModal.show();
}
// Add the anime to the list
},
It works, but now the modals overlay each other, seems like its not working like when you add the attributes to the card element that opens modal:
:data-bs-target="'#anime-card-modal-' + anime.id"
data-bs-toggle="modal"
Is there a way to get the same effect from method as with these attributes?
I want to open a modal, by clicking an element with those attributes, then when i click another element with them attributes (different target id) it closes previously opened modal and opens the target modal.
Alright, i found a solution, works pretty good.
Instead of using myModal.show() i used myModal.toggle("anime-card-modal-" + animeID) and the else statement is not needed in the event method:
async checkAnimeListForRelated(animeID) {
if (!this.animeList.filter((anime) => anime.id === animeID).length > 0) {
const res = await axios.get("/api/anime", {
params: { id: animeID },
});
if (res.data.data.length > 0) {
this.animeList.push(res.data.data[0]);
console.log("added to list");
this.$parent.$nextTick(() => {
const myModal = new Modal(
document.getElementById("anime-card-modal-" + animeID)
);
myModal.toggle("anime-card-modal-" + animeID) <---------------
}
}
// Add the anime to the list
},
I am trying to integrate the webSDK from https://www.pollfish.com/docs/webplugin in our Vue app.
Ideally I want to load jquery only in one component.
I wrote the following code but when I click the button it doesnt work.
Here is an example with working code that does NOT use Vue https://github.com/pollfish/webplugin-rewarded-example/blob/master/index.html but does run locally.
I get no errors and I can console.log(Pollfish) inside the the showFullSurvey method.
My code is:
<template>
<div class="container" v-if="isFreePlan">
<h2>Remove ads and save unlimited projects for 5 days</h2>
<button #click="showFullSurvey">Take {{lengthOfInteraction}} Survey Now</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
data() {
return {
surveyAvailable: false,
lengthOfInteraction: ''
}
},
methods: {
showFullSurvey() {
Pollfish.showFullSurvey();
console.log('show survey')
}
},
mounted() {
const pollFishConfig = {
api_key: "api-key",
debug: process.env.NODE_ENV === 'production' ? false : true,
ready: () => {},
uuid: this.userId,
surveyAvailable: onSurveyAvailable,
surveyNotAvailable: onSurveyNotAvailable,
surveyCompletedCallback: onSurveyCompleted,
userNotEligibleCallback: onUserDisqualified
};
console.log('POllfish config');
const onSurveyAvailable = (data) => {
console.log('SUrvey Available');
};
const onSurveyNotAvailable = () => {
console.log('SUrvey Not Available');
};
const onSurveyCompleted = () => {
console.log('SUrvey Completed');
};
const onUserDisqualified = () => {
console.log('USer Disqualified');
};
this.addJQuery;
this.addPollFishSDK;
},
computed: {
...mapGetters("session", ['userId']),
...mapGetters("account", ["isFreePlan"]),
addJQuery() {
const url = 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js';
if(document.querySelector(`script[src='${url}']`)){ return; }
let jquery = document.createElement('script');
jquery.setAttribute('src', url);
document.body.appendChild(jquery);
console.log('jquery script')
},
addPollFishSDK() {
const url = 'https://storage.googleapis.com/pollfish_production/sdk/webplugin/pollfish.min.js';
if(document.querySelector(`script[src='${url}']`)){ return; }
let pollFishSdk = document.createElement('script');
pollFishSdk.setAttribute('src', url);
document.body.appendChild(pollFishSdk);
console.log('pollfish script')
}
}
}
</script>
In order to integrate our web plugin in your Vue.js app, you need to set the pollfishConfig object in the window. Please be careful with the object's name to be exactly the same as the following example.
window.pollfishConfig = {
api_key: "api-key",
debug: process.env.NODE_ENV === 'production' ? false : true,
ready: () => {},
uuid: this.userId,
surveyAvailable: onSurveyAvailable,
surveyNotAvailable: onSurveyNotAvailable,
surveyCompletedCallback: onSurveyCompleted,
userNotEligibleCallback: onUserDisqualified
};
Also, based on your example, you need to be sure that the jQuery library is loaded first and be available for our WebPlugin SDK. So you need to handle the onload event. An example solution based on your code is the following:
const addScript = (url, onLoad) => {
const scriptExists = document.querySelector(`script[src='${url}']`);
if (!scriptExists) {
let script = document.createElement('script');
document.body.appendChild(script);
script.onload = () => {
onLoad && onLoad();
}
script.src = url;
}
}
addScript('https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js', () => {
addScript('https://storage.googleapis.com/pollfish_production/sdk/webplugin/pollfish.min.js')
});
I have a pretty simple requirement to click on a phone number hyperlink and have my web-app open the AWS connect soft-phone dialer with the selected number, ready for the person to press the "call button"
I have enabled an AWS connect account and I am hosting a custom CCP site via an S3 bucket (as illustrated here)
My plan is to initiate a link to the CCP page and embed a URL Search Param
"?number=04125412,customTag=helloWorld"
I have used this code on the CCP Page
Also, within the index page, I add some code to receive the input params:
<script>
var urlParams = new URLSearchParams(window.location.search);
console.log(urlParams.get('number')); //the phone number for the dialer
console.log(urlParams.get('customTag')); // the call notes for the CTR custom Attributes
</script>
I Am struggling to understand how I can interact with A: the Dialer to pre-fill the number and B: to post custom attributes to the AWS contact record during the call.
Any help would be appreciated.
I set this up in my React application but you should be able to repurpose for your needs
import React from "react";
import {connect} from 'react-redux'
import Button from "components/CustomButtons/Button.jsx";
import {receiveCallAttr, initCall, callFlow} from 'store/apps/AppSettings/actions';
class AmazonConnect extends React.Component {
constructor(props) {
super(props);
this.state = {
active:false,
reloadAttempts:0,
activeCall:{},
cip:false,
agentQueueNumber:"xxxxxxxxxx",
recordingQueueNumber:"xxxxxxxxxx"
};
this.awsConnect = this.awsConnect.bind(this)
this.loginWindow = this.loginWindow.bind(this);
this.activeWindow = this.activeWindow.bind(this);
this.initCall = this.initCall.bind(this)
this.initContact = this.initContact.bind(this)
this.redirect = this.redirect.bind(this)
}
componentWillReceiveProps(newProps){
const {AppSettings, initCall, callFlow} = newProps
const {cip, active} = this.state
if( active && !cip){
this.setState({activeCall: AppSettings.call})
if(AppSettings.call.number){
console.log("init call")
this.initCall(AppSettings.call.number)
initCall({})
}
else{
console.log("Invalid Phone number")
}
if( AppSettings.flow !== "" ){
this.setState({activeFlow: AppSettings.flow})
this.initCallFlow(AppSettings.flow)
callFlow("")
}
}
}
initCallFlow = flow => new Promise((res, rej) => {
if(this.contact){
console.log(this.contact)
let endpoint;
switch(flow){
case "agentQueue":
endpoint = window.connect.Endpoint.byPhoneNumber(this.state.agentQueueNumber);
this.contact.addConnection(endpoint, {
success: function() {
this.contact.conferenceConnections({
success: function() {
console.log("confrence success")
res("successfullly init ssn flow")
},
failure: function() {
console.log("confrence failure")
res("successfullly init ssn flow")
}
});
},
failure: function() {
rej("failed to init ssn flow")
}
});
break
case "recordingQueue":
endpoint = window.connect.Endpoint.byPhoneNumber(this.state.recordingQueueNumber);
this.contact.addConnection(endpoint, {
success: function() {
res("successfullly init recording flow")
},
failure: function() {
rej("failed to init recording flow")
}
});
break
default:
res()
break
}
}
else{
rej("no contact available")
}
})
awsConnect = () => new Promise((res, rej) => {
window.connect.core.initCCP(document.getElementById("softPhone"), {
ccpUrl: process.env.REACT_APP_AWS_CONNECT_URL, /*REQUIRED*/
loginPopup: true, /*optional, default TRUE*/
softphone: { /*optional*/
disableRingtone: false, /*optional*/
allowFramedSoftphone: true
}
});
this.bus = window.connect.core.getEventBus();
this.bus.subscribe(window.connect.AgentEvents.INIT, (agent) => {
this.activeWindow()
});
this.bus.subscribe(window.connect.EventType.TERMINATED, () => {
console.log("TERMINATED")
this.setState({cip:false})
this.logout()
});
this.bus.subscribe(window.connect.EventType.AUTH_FAIL, () => {
console.log("AUTH_FAIL")
this.logout()
})
window.connect.agent(function(agent) {
const w = window.open('', window.connect.MasterTopics.LOGIN_POPUP);
if (w) {
w.close()
}
});
window.connect.contact((contact) => {
this.contact = contact
const {receiveCallAttr} = this.props
try{
var attr = contact.getAttributes()
attr.active = true
console.log(attr)
receiveCallAttr(attr)
this.redirect()
}
catch(err){
console.log(err)
}
contact.onEnded(() => {
console.log("call ended")
receiveCallAttr({active:false})
this.setState({cip:false})
this.contact = null
})
});
res()
})
initContact = () => {
this.setState({cip:false})
}
redirect = () => {
const {location, auth, history} = this.props
switch(auth.user.type){
case "Agent":
if(location.pathname !== "/agent/management"){
history.push({
pathname: '/agent/management',
search: '',
state: {}
})
}
break;
case "Service":
//handle redirect to service page
if(location.pathname !== "/service/dashboard"){
history.push({
pathname: "/service/dashboard",
search: '',
state: {}
})
}
break;
default:
break
}
}
initCall = (phone) => {
this.initContact()
window.connect.agent(function(agent) {
const endpoint = window.connect.Endpoint.byPhoneNumber(phone)
agent.connect(endpoint , {
queueARN : process.env.CONNECT_QUEUE_ARN,
success : function(){
console.log("Success call!!!!!!")
},
failure : function(){
console.log("Call failed!!!!!!!")
}
});
});
}
logout(){
this.setState({cip:false})
this.loginWindow()
this.agent = null
this.contact = null
window.connect.core.terminate();
window.connect.core.client = new window.connect.NullClient();
window.connect.core.masterClient = new window.connect.NullClient();
window.connect.core.eventBus = new window.connect.EventBus();
window.connect.core.initialized = false;
this.bus = false;
var myNode = document.getElementById("softPhone")
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
}
componentWillUnmount() {
console.log("terminating aws connect session")
this.logout()
}
loginWindow(){
this.setState({active:false})
}
activeWindow(){
this.setState({active:true})
}
render() {
const displaylogin = this.state.active? "none":"block";
const displayConnect = this.state.active? "block":"none";
return (
<div>
<Button color={"rose"} onClick={this.awsConnect} style={{display:displaylogin, width:320}}>Login to AWS Connect</Button>
<div id="softPhone" style={{height:465,width:320, display:displayConnect}}>
</div>
</div>
);
}
}
function mapStateToProps(state){
return state
}
export default connect(mapStateToProps, {receiveCallAttr, initCall, callFlow})(AmazonConnect);
The previous answer by Ethan Harris helped me to reach the solution, but to distill it to allow a link to dial a number. You find the ARN in the Amazon Connect UI here:
Using the ARN copied from the Connect UI, this function seems to work for automating dialing a number. This took way more effort to figure out than I ever expected.
function dial_number(phone) {
connect.agent(function (agent) {
agent.connect(connect.Endpoint.byPhoneNumber(phone),
{
queueARN: arn
});
});
}
Hi As I am new to angular 4 I am facing some issue like "Uncaught Error: Can't resolve all parameters for" or Cyclic dependence . I have written a service which does a calculation and in setTimeout I am calling to open the MatDialog . Matdialog has two option 'Yes' or 'no' on click of the 'Yes' doing some service call and doing some calculation and again setting clearTimeout and setting new setTimeout which again open the popup after some time.
And I want to also check on each service call and depending on some condition I have again clearTimeout and set a new setTimeout which open the MatDialog .
I trying this since long time but would not find a solution . I want know Where is the right place that I can place my code and How to write service to open the Matdialog .
Written this code in main.components.ts
setTimer() {
this.notifyTime = expiryValue - 120000;
this.date1 = new Date();
this.date2 = new Date(this.notifyTime);
this.diff = this.date2.getTime() - this.date1.getTime();
let _self = this;
this.timerVar = setTimeout(function () {
let dialogRef = _self.dialog.open(DialogModalComponent, {
data: {
timer: _self.timerVar,
va: true
}
});
}, this.diff);
}
clearTimer() {
clearTimeout(this.timerVar);
}
The above is a piece of code I am using to setTimeout() and clearTimeout()
Written this code in global service where
temp points to another to main.component.ts
autoLoad() {
if (this.expiryValue) {
this.date1 = new Date();
this.diff = this.expiryValue - this.date1.getTime();
if (this.diff < 600000 && this.diff > 120000) {
this.getUpUrl('refresh').then(result => {
if (result.status == 'success') {
this.temp.clearTimer();
this.temp.showDialog(result.sessionExpiry);
}
});
}
}
And in the dialog.component.ts
ok() {
this.dialog.close();
this.temp.clearTimer();
this.temp.setTimer();
}
cancel() {
this.dialog.close();
}
The above code I am using in the dialog. temp points to my main.component.ts
you can use a setTimeout function to open a dialog after some time
this example based on angular material examples
constructor(public _dialogService: DialogService) { }
openDialog(): void {
this.clicked = true;
this._dialogService.open({ name: this.name, animal: this.animal }, () => this.clicked = false)
.then(result => {
console.log('The dialog was closed');
this.animal = result;
})
}
dialog service
#Injectable({
providedIn:'root'
})
export class DialogService {
constructor(private dialog: MatDialog) { }
open(data , onOpenCallBack , timeout:number = 2500) : Promise<any> {
return new Promise((resolve) => {
setTimeout(() => {
const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
width: '250px',
data: data
});
if (onOpenCallBack) {
onOpenCallBack()
}
dialogRef.afterClosed().subscribe(result => {
resolve(result)
});
}, timeout)
})
}
}
stackblitz demo
How can I alter the HTTP response body in a Firefox extension? I have setup an http-on-examine-response observer and an nsIStreamListener object with the code below. After I get the data, parse it, and alter it, how do I push the altered response back to the firefox browser? For example, let's say I go to Google.com with my extension enabled, the extension should intercept the response and change every occurence of "google" to "goggle". So when the page is loaded, the user will see "goggle" everywhere.
function TmSteroidsObserver()
{
this.register();
}
TmSteroidsObserver.prototype = {
observe: function(subject, topic, data) {
if (topic == "http-on-examine-response") {
}
else if (topic == "http-on-modify-request") {
var channel = subject.QueryInterface(Components.interfaces.nsIChannel);
var listener = new StreamListener(channel);
}
},
register: function() {
var observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(listener, "http-on-modify-request", false);
observerService.addObserver(listener, "http-on-examine-response", false);
},
unregister: function() {
var observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "http-on-modify-request");
observerService.removeObserver(this, "http-on-examine-response");
},
QueryInterface : function(aIID) {
if (aIID.equals(Components.interfaces.nsISupports) ||
aIID.equals(Components.interfaces.nsIObserver))
return this;
throw Components.results.NS_NOINTERFACE;
}
}
function StreamListener(channel) {
channel.notificationCallbacks = listener;
channel.asyncOpen(listener, null);
}
StreamListener.prototype = {
mData: "",
mChannel: null,
// nsIStreamListener
onStartRequest: function (aRequest, aContext) {
this.mData = "";
},
onDataAvailable: function (aRequest, aContext, aStream, aSourceOffset, aLength) {
var scriptableInputStream =
Components.classes["#mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
scriptableInputStream.init(aStream);
this.mData += scriptableInputStream.read(aLength);
},
onStopRequest: function (aRequest, aContext, aStatus) {
if (Components.isSuccessCode(aStatus)) {
// request was successfull
this.mCallbackFunc(this.mData);
} else {
// request failed
this.mCallbackFunc(null);
}
this.mChannel = null;
},
// nsIChannelEventSink
onChannelRedirect: function (aOldChannel, aNewChannel, aFlags) {
// if redirecting, store the new channel
this.mChannel = aNewChannel;
},
// nsIInterfaceRequestor
getInterface: function (aIID) {
try {
return this.QueryInterface(aIID);
} catch (e) {
throw Components.results.NS_NOINTERFACE;
}
},
// nsIProgressEventSink (not implementing will cause annoying exceptions)
onProgress : function (aRequest, aContext, aProgress, aProgressMax) { },
onStatus : function (aRequest, aContext, aStatus, aStatusArg) { },
// nsIHttpEventSink (not implementing will cause annoying exceptions)
onRedirect : function (aOldChannel, aNewChannel) { },
// we are faking an XPCOM interface, so we need to implement QI
QueryInterface : function(aIID) {
if (aIID.equals(Components.interfaces.nsISupports) ||
aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
aIID.equals(Components.interfaces.nsIChannelEventSink) ||
aIID.equals(Components.interfaces.nsIProgressEventSink) ||
aIID.equals(Components.interfaces.nsIHttpEventSink) ||
aIID.equals(Components.interfaces.nsIStreamListener))
return this;
throw Components.results.NS_NOINTERFACE;
}
};
You can use nsITraceableChannel to intercept the response.
You should modify the data which is available to what you need and pass it to the innerListener's OnDataAvailable
Below links would help you understand this better.
http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/
For future readers looking for a way to do this in Firefox Quantum, there is an API that lets you filter responses. Using the method for long documents mentioned here, I was able to reliably change what I needed in my (temporary) plugin's background.js like so:
browser.webRequest.onBeforeRequest.addListener(
function fixenator(details) {
let filter = browser.webRequest.filterResponseData(details.requestId);
let decoder = new TextDecoder("utf-8");
let encoder = new TextEncoder();
let str = '';
filter.ondata = event => {
str += decoder.decode(event.data, {stream: true});
};
filter.onstop = event => {
str = str.replace(/searchPattern/g, 'replace pattern');
filter.write(encoder.encode(str));
filter.close();
}
return {};
},
{
urls: ['https://example.com/path/to/url']
//, types: ['main_frame', 'script', 'sub_frame', 'xmlhttprequest', 'other'] // optional
}
, ['blocking']
);
The observer service just call your listeners. Firefox will receive the requests,call your listeners, and send responses. see Mozilla docs Creating HTTP POSTs.