I created this script, however the cache cleaning warning appears in the debug console which is not defined. How can I solve it?
I uploaded the code here https://codepen.io/stiac/pen/ExPjgwe
class NotificationBanner {
constructor(el) {
this.storageKey = 'notifications'
this.el = el
this.id = this.el.dataset.id
this.el.querySelector(".closebutton").onclick = () => this.close()
this.showUnlessDismissed()
}
show() {
this.el.hidden = false
}
close() {
this.el.remove()
this.updateLocalStorage()
}
showUnlessDismissed() {
if(this.getLocalStorage().includes(this.id)) {
this.close()
}
else {
this.show()
}
}
updateLocalStorage() {
const dismissedNotifications = this.getLocalStorage()
if(!dismissedNotifications.includes(this.id)) {
dismissedNotifications.push(this.id)
localStorage.setItem(this.storageKey, JSON.stringify(dismissedNotifications))
}
}
getLocalStorage() {
return JSON.parse(localStorage.getItem(this.storageKey)) || []
}
}
class NotificationBanners {
constructor() {
const notifications = [...document.querySelectorAll(".notification-banner")];
notifications.forEach(function(notification) {
return new NotificationBanner(notification);
})
}
}
new NotificationBanners()
clearcache.onclick = e => localStorage.setItem('notifications', JSON.stringify([]))
It is a script to hide a message. I wish I could set a deadline to make it appear after a few days.
Related
I am learning WebGL and started developing an Sketch app using p5.js and angular. I have defined boolean variables in the component file so that based on that i want to trigger specific function like ellipse, rectangle, line etc in the draw function. These boolean variables are managed by buttons in another component.
I am getting error as core.js:6014 ERROR TypeError: Cannot read property 'isRectangleMode' of undefined
Component file:
import { Component, OnInit } from '#angular/core';
import * as p5 from 'p5';
import { Subscription } from 'rxjs';
import { HomeService } from '../home.service';
#Component({
selector: 'app-doodle-area',
templateUrl: './doodle-area.component.html',
styleUrls: ['./doodle-area.component.css']
})
export class DoodleAreaComponent implements OnInit {
private p5Init : any;
modeSubs : Subscription;
modeSelected : string = null;
isCircleMode : boolean = false;
isEllipseMode : boolean = false;
isRectangleMode : boolean = false;
isLineMode : boolean = false;
isPointMode : boolean = false;
isBrushMode : boolean = false;
isPenMode : boolean = false;
constructor(private homeService : HomeService) { }
ngOnInit() {
this.createCanvas();
this.homeService.modeSelected
.subscribe(modeSelected => {
this.modeSelected = modeSelected;
console.log(this.modeSelected);
if(this.modeSelected) {
this.modeReset();
if(this.modeSelected === "circle") {
this.isCircleMode = true;
} else if(this.modeSelected === 'ellipse') {
this.isEllipseMode = true;
} else if(this.modeSelected === 'rectangle') {
this.isRectangleMode = true;
} else if(this.modeSelected === 'line') {
this.isLineMode = true;
} else if(this.modeSelected === 'point') {
this.isPointMode = true;
} else if(this.modeSelected === 'brush') {
this.isBrushMode = true;
} else if(this.modeSelected === 'pen') {
this.isPenMode = true;
}
}
});
}
private modeReset() {
this.isCircleMode = false;
this.isEllipseMode = false;
this.isRectangleMode = false;
this.isLineMode = false;
this.isPointMode = false;
this.isBrushMode = false;
this.isPenMode = false;
}
private createCanvas() {
this.p5Init = new p5(this.doodleArea);
}
private doodleArea(p : any) {
p.setup = () => {
p.createCanvas(p.windowWidth - 440, p.windowHeight - 200).parent('doodle-area');
p.background(206,214,224);
p.createP("Hello");
}
p.draw = () => {
if(this.isRectangleMode) {
console.log("Rectangle");
}
p.stroke(0);
if(p.mouseIsPressed === true) {
p.line(p.mouseX, p.mouseY, p.pmouseX, p.pmouseY);
}
}
}
}
Screenshot of the console:
This is issue is because of the scope. Here inside the callback function doodleArea, the scope is not component scoped (this). Here this is undefined and we can not access isRectangleMode of undefined. The possible solution for this is:
In .ts modify the createCanvas() with code given below:
private createCanvas() {
const doodleArea = s => {
s.setup = () => {
let canvas = s.createCanvas(s.windowWidth - 440, s.windowHeight - 200);
canvas.parent("doodle-area");
s.draw = () => {
if (this.isRectangleMode) {
console.log("Rectangle");
}
s.stroke(0);
if (s.mouseIsPressed === true) {
s.line(s.mouseX, s.mouseY, s.pmouseX, s.pmouseY);
}
};
s.keyPressed = () => {
if (s.key === 'c') {
window.location.reload();
}};};
};
this.p5Init = new p5(doodleArea);
}
Below is the sample code of the same:
https://stackblitz.com/edit/angular-s-p5-angular?file=src%2Fapp%2Fapp.component.ts
https://angular-s-p5-angular.stackblitz.io
Hope this will help.
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
});
});
}
I want to create an app that has an alert for check connection with two button one is exit for the exit app and two is try again for check connection again,
I searched about it and I tried about it, but I can n't solved this problem please help me.
import { Injectable } from '#angular/core';
import { Network } from '#ionic-native/network/ngx';
import { AlertController } from '#ionic/angular';
#Injectable({
providedIn: 'root'
})
export class CheckInternetService {
public base: string; // this will be set in the constructor based on if we're in dev or prod
timer: any;
constructor(private network: Network, private alertCtrl: AlertController) {}
async presentAlert() {
const alert = await this.alertCtrl.create({
header: 'خطا',
backdropDismiss: false,
subHeader: 'قطعی انترنت',
message: 'لطفا انترنت خودرا چک کنید',
buttons: [{
text: 'خروج',
handler: () => {
navigator['app'].exitApp();
}
},
{
text: 'تلاش مجدد',
handler: () => {
this.doSomething().then(res => {
this.checkConnection();
});
}
}
],
});
await alert.present();
}
doSomething() {
return new Promise((resolve, reject) => {
// pretend a long-running task
this.timer = setTimeout(() => { resolve(true); }, 3000);
});
}
checkConnection(): boolean {
if (document.URL.includes('https://') || document.URL.includes('http://')) {
this.base = 'http://127.0.0.1:3001/';
} else {
this.base = 'https://url.to_actual_URL.com/';
}
const type = this.network.type;
let online;
if (type === 'unknown' || type === 'none' || type === undefined) {
online = false;
this.presentAlert();
} else {
online = true;
clearTimeout(this.timer);
}
this.network.onDisconnect().subscribe( () => {
online = false;
this.presentAlert();
});
this.network.onConnect().subscribe( () => {
online = true;
clearTimeout(this.timer);
});
return online;
}
}
This is my code that I was trying on, I work on this code but I do n't any answer, please help me.
You can make try again button with out any timer, you can use this code for your problem:
async presentAlert() {
this.alertCtrl.dismiss();
const alert = await this.alertCtrl.create({
header: 'خطا',
backdropDismiss: false,
subHeader: 'قطعی انترنت',
message: 'لطفا انترنت خودرا چک کنید',
buttons: [{
text: 'خروج',
handler: () => {
navigator['app'].exitApp();
}
},
{
text: 'تلاش مجدد',
// role: 'cancel',
handler: () => {
// this.doSomething().then(res => {
// this.checkConnection();
// });
const type = this.network.type;
if (type === 'unknown' || type === 'none' || type === undefined) {
this.presentAlert();
}
},
}
],
});
await alert.present();
}
I don't think you can have a alert box with two buttons because Alert box gives only one button "OK" to select and proceed. . You can show a modal instead of a alert box with as much button as you want.
I am doing a task where I need to wire up a search field to a simple JS application that displays a few items and the user can search through and filter them.
There are three classes - App, ProductsPanel and Search. Both Search and ProductsPanel are being initialised inside the App class.
The ProductsPanel class holds an array with 10 products.
I want to call a method of ProductsPanel from inside Search that filters through the products. How can I do that?
I've tried using this.productsPanel = new productsPanel() inside the constructor of the first class, but that brings up a new instance which doesn't have the array of all of the products.
Here's the App class:
class App {
constructor() {
this.modules = {
search: {
type: Search,
instance: null
},
filter: {
type: Filter,
instance: null
},
productsPanel: {
type: ProductsPanel,
instance: null
},
shoppingCart: {
type: ShoppingCart,
instance: null
}
};
}
init() {
const placeholders = document.querySelectorAll("#root [data-module]");
for (let i = 0; i < placeholders.length; i++) {
const root = placeholders[i];
const id = root.dataset.module;
const module = this.modules[id];
if (module.instance) {
throw new Error(`module ${id} has already been started`);
}
module.instance = new module.type(root);
module.instance.init();
// console.info(`${id} is running...`);
}
}
}
app = new App();
app.init();
And here are the Search:
export default class Search {
constructor(root) {
this.input = root.querySelector("#search-input");
}
// addEventListener is an anonymous function that encapsulates code that sends paramaters to handleSearch() which actually handles the event
init() {
this.input.addEventListener("input", () => {
this.handleSearch();
});
}
handleSearch() {
const query = this.input.value;
app.modules.productsPanel.instance.performSearch(query);
}
}
And ProductsPanel classes:
export default class ProductsPanel {
constructor(root) {
this.view = new ProductsPanelView(root, this);
this.products = [];
}
init() {
this.products = new ProductsService().products;
this.products.forEach(x => this.view.addProduct(x));
}
performSearch(query) {
query = query.toLowerCase();
this.products.forEach(p => {
if (query === p.name) {
this.view.showProduct(p.id);
} else {
this.view.hideProduct(p.id);
}
});
}
addToCart(id) {
const product = this.products.filter(p => p.id === id)[0];
if (product) {
app.modules.shoppingCart.instance.addProduct(product);
}
}
}
I want to call ProductsPanel's performSearch method but on the instance created by the App class. I have no clue on how I can do that.
Try below custom event handler class
class CustomEventEmitter {
constructor() {
this.eventsObj = {};
}
emit(eName, data) {
const event = this.eventsObj[eName];
if( event ) {
event.forEach(fn => {
fn.call(null, data);
});
}
}
subscribe(eName, fn) {
if(!this.eventsObj[eName]) {
this.eventsObj[eName] = [];
}
this.eventsObj[eName].push(fn);
return () => {
this.eventsObj[eName] = this.events[eName].filter(eventFn => fn !== eventFn);
}
}
}
How to use?
create the object of CustomEventEmitter class
let eventEmitter = new CustomEventEmitter()
Subscribe an event
emitter.subscribe('event: do-action', data => {
console.log(data.message);
});
call the event
emitter.emit('event: do-action',{message: 'My Custom Event handling'});
Hope this helps!
I Need to refactor an IIFE in ES6. In ES6 let and const have a block scope, so I really need an IIFE in ES6? This is the ES5 version of the code:
var oojs = (function(oojs) {
var createToolbarItems = function(itemElements) {
var items = [];
[].forEach.call(itemElements,
function(el, index, array) {
var item = {
toggleActiveState: function() {
this.activated = !this.activated;
}
};
Object.defineProperties(item, {
el: {
value: el
},
enabled: {
get: function() {
return !this.el.classList.contains('disabled');
},
set: function(value) {
if (value) {
this.el.classList.remove('disabled');
} else {
this.el.classList.add('disabled');
}
}
},
activated: {
get: function() {
return this.el.classList.contains('active');
},
set: function(value) {
if (value) {
this.el.classList.add('active');
} else {
this.el.classList.remove('active');
}
}
}
});
items.push(item);
});
return items;
};
oojs.createToolbar = function(elementId) {
var element = document.getElementById(elementId);
var items = element.querySelectorAll('.toolbar-item');
return {
items: createToolbarItems(items)
}
};
return oojs;
}(oojs || {}));
What is the best way to translate this code in ES6? I tried many solution but I miss something, and I get an error: oojs is not defined.
Maybe I can use a Class instead? As you can see from the code I'm writing a Toolbar API in a OOP way (I think...)
Thanks for any help
EDIT: Thanks to georg, I try to refactor my code using classes. This is the new ES6 version:
class Toolbar {
constructor(elementId) {
this.elementId = elementId;
}
get items() {
const element = document.getElementById(this.elementId);
return element.querySelectorAll(".toolbar-item");
}
createToolbarItems() {
return [...this.items].map(el => new ToolbarItem(el));
}
}
class ToolbarItem {
constructor(el) {
this.el = el;
}
get enabled() {
return !this.el.classList.contains('disabled');
}
set enabled(value) {
if (value) {
this.el.classList.remove('disabled');
} else {
this.el.classList.add('disabled');
}
}
get activated() {
return this.el.classList.contains('active');
}
set activated(value) {
if (value) {
this.el.classList.add('active');
} else {
this.el.classList.remove('active');
}
}
toggleActiveState() {
this.activated = !this.activated;
}
}
// const toolbar = new Toolbar('myToolbar');
// const toolbarItems = toolbar.createToolbarItems();
EDIT: please check if is the right way to write this code, I'm pretty new to ES6
Thanks again
You can start by factoring out the toolbar item code (var item and below):
class ToolbarItem
{
constructor(element) {
....
}
}
Now, decide if you want to keep enabled and activated as properties or refactor them to explicit methods like isEnabled and setEnabled. In the former case it would be,
class ToolbarItem {
get enabled() {
...
}
set enabled(value) {
...
}
}
while ordinary methods can be defined like this:
class ToolbarItem {
isEnabled() {
...
}
setEnabled(value) {
...
}
}
Once you get this sorted out, replace your item initialization code with items.push(new ToolbarItem(el)) and test.
Hope this helps you getting started, good luck!