window addEventListener keydown not firing - javascript

I'm making the 2048 game, but the window.addEventListener("keydown", handleInput, {once:true}); does not firing. But if I console.log(e.key), I get the message that I pressed the button (ArrowUp, -Down and os on), but the tile does not move. Can anyone help please?
This is the index.js file that contains the eventlistener.
import Grid from "./grid.js";
import Tile from "./tile.js";
const gameBoard = document.getElementById('game-board');
const grid = new Grid(gameBoard);
grid.randomEmptyCell().tile = new Tile(gameBoard);
grid.randomEmptyCell().tile = new Tile(gameBoard);
setupInput();
function setupInput() {
window.addEventListener("keydown", handleInput, { once: true });
}
async function handleInput(e) {
console.log(e.key);
switch (e.key) {
case "ArrowUp":
if(!canMoveUp()) {
setupInput();
return;
}
await moveUp()
break
case "ArrowDown":
if(!canMoveDown()) {
setupInput();
return;
}
await moveDown()
break
case "ArrowLeft":
if(!canMoveLeft()) {
setupInput();
return;
}
await moveLeft()
break
case "ArrowRight":
if(!canMoveRight()) {
setupInput();
return;
}
await moveRight()
break
default:
setupInput()
return
}
grid.cells.forEach(cell => cell.merTiles());
const newTile = new Tile(gameBoard);
grid.randomEmptyCell().tile = newTile;
if (!canMoveUp() && !canMoveDown() && !canMoveLeft() && !canMoveRight()) {
newTile.waitForTransition(true).then(() => {
alert("You lose!");
});
} return
setupInput();
}
function moveUp() {
return slideTiles(grid.cellsByColumn);
}
function moveDown() {
return slideTiles(grid.cellsByColumn.map(column => [...column].reverse()));
}
function moveLeft() {
return slideTiles(grid.cellsByRow);
}
function moveRight() {
return slideTiles(grid.cellsByRow.map(row => [...row].reverse()));
}
function slideTiles(cells) {
return Promise.all(
cells.flatMap(group => {
const promises = [];
for (let i = 1; i < group.length; i++) {
const cell = group[i];
if (cell.tile == null) continue;
let lastValidCell;
for (let j = i - 1; j >= 0; j--) {
const moveToCell = group[j];
if (!moveToCell.canAccept(cell.tile)) break;
lastValidCell = moveToCell;
}
if (lastValidCell != null) {
promises.push(cell.tile.waitForTransition());
if (lastValidCell.tile != null) {
lastValidCell.mergeTile = cell.tile;
} else {
lastValidCell.tile = cell.tile;
}
cell.tile = null;
}
}
return promises;
}));
}
function canMoveUp() {
return canMove(grid.cellsByColumn);
}
function canMoveDown() {
return canMove(grid.cellsByColumn.map(column => [...column].reverse()));
}
function canMoveLeft() {
return canMove(grid.cellsByRow);
}
function canMoveRight() {
return canMove(grid.cellsByRow.map(row => [...row].reverse()));
}
function canMove(cells) {
return cells.some(group => {
return group.some((cell, index) => {
if (index === 0) return false;
if (cell.tile == null) return false;
const moveToCell = group[index - 1];
return moveToCell.canAccept(cell.tile);
});
});
}

See mdn page:
EventTarget.addEventListener()
A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked. If not specified, defaults to false.
if you remove {once: true}, it will can work more than once.
Also one more thing, you use console.log(e.key) but you use e.keyCode in switch cases. On MDN, keyCodes are actually codes.Code values for keyboard events
so, you should either change your switch case conditions to e.key to use the same values: Key values for keyboard events
or change the case values from text to keyCode

Related

Using Asyc/Await with Firebase Cloud Functions

I have the following function that I wish to turn into a promise:
const blockRequest = (context) => {
let blockRequest = false;
let limitLocation;
const generalLocation = "/limits/general/limits";
let generalTime;
let generalNum;
let limitTime;
let limitNum;
const newTime = new Date().getTime();
if (context.auth != undefined) {
limitLocation = "/users/"+context.auth.uid+"/limits";
} else {
limitLocation = "/limits/"+context.rawRequest.ip+"/limits";
}
admin.database().ref(generalLocation+"/generalNum")
.set(admin.database.ServerValue.increment(1))
.then(()=>{
return admin.database().ref(generalLocation).once("value");
})
.then((snapshot)=>{
if (snapshot.val() == undefined) {
generalNum = 0;
generalTime = newTime;
} else {
generalTime = snapshot.val().generalTime;
generalNum = snapshot.val().generalNum;
}
if ( newTime - generalTime>3000) {
generalNum = 1;
} else if (newTime - generalTime <=300 &&
generalNum < 300) {
} else {
console.log("General Request Blocked");
blockRequest = true;
}
return admin.database().ref(generalLocation)
.set({generalTime: newTime, generalNum: generalNum});
})
.then(()=>{
if (blockRequest) {
return blockRequest;
} else {
return admin.database().ref(limitLocation+"/limitNum")
.set(admin.database.ServerValue.increment(1));
}
})
.then(()=>{
return admin.database().ref(limitLocation).once("value");
})
.then((snapshot)=>{
if (snapshot.val().limitTime == undefined) {
limitNum = snapshot.val().limitNum;
limitTime = newTime;
} else {
limitTime = snapshot.val().limitTime;
limitNum = snapshot.val().limitNum;
}
if ( newTime - limitTime>10000) {
limitNum = 1;
} else if (newTime - limitTime <=10000 &&
limitNum < 20) {
} else {
console.log("Specific Request Blocked");
blockRequest = true;
}
return admin.database().ref(limitLocation)
.set({limitTime: newTime, limitNum: limitNum});
})
.then(()=>{
console.log(blockRequest)
return blockRequest;
})
.catch((error)=>{
console.log(error.message);
return true;
});
};
I also have a function that I would like to call that looks like:
exports.limiterTest = functions.https.onCall(async(data, context)=>{
const result = await blockRequest(context)
console.log("hello")
});
I would like blockRequest to finish running before anything else in limiterTest runs. I think in order for this to function as I intend, I need blockRequest to return a promise. However, I am not sure how to construct this, especially with the chaining .then and .catch statements.
Thanks for your time!

Run function only once until onmouseout

I have an anchor tag that plays a sound everytime the link is hovered:
<a onmouseenter="playAudio();">LINK</a>
However, when hovered, the link creates some characters that, if I keep moving the mouse inside the element, it keeps playing the sound over and over again.
Is there a way to play the onmouseenter function only once until it has detected the event onmouseout?
I've tried adding:
<a onmouseenter="playAudio();this.onmouseenter = null;">LINK</a>
but then it only plays the sound once.
Here goes the TextScramble animation function:
// ——————————————————————————————————————————————————
// TextScramble
// ——————————————————————————————————————————————————
class TextScramble {
constructor(el) {
this.el = el
this.chars = '!<>-_\\/[]{}—=+*^?#________'
this.update = this.update.bind(this)
}
setText(newText) {
const oldText = this.el.innerText
const length = Math.max(oldText.length, newText.length)
const promise = new Promise((resolve) => this.resolve = resolve)
this.queue = []
for (let i = 0; i < length; i++) {
const from = oldText[i] || ''
const to = newText[i] || ''
const start = Math.floor(Math.random() * 40)
const end = start + Math.floor(Math.random() * 40)
this.queue.push({
from,
to,
start,
end
})
}
cancelAnimationFrame(this.frameRequest)
this.frame = 0
this.update()
return promise
}
update() {
let output = ''
let complete = 0
for (let i = 0, n = this.queue.length; i < n; i++) {
let {
from,
to,
start,
end,
char
} = this.queue[i]
if (this.frame >= end) {
complete++
output += to
} else if (this.frame >= start) {
if (!char || Math.random() < 0.28) {
char = this.randomChar()
this.queue[i].char = char
}
output += `<span class="dud">${char}</span>`
} else {
output += from
}
}
this.el.innerHTML = output
if (complete === this.queue.length) {
this.resolve()
} else {
this.frameRequest = requestAnimationFrame(this.update)
this.frame++
}
}
randomChar() {
return this.chars[Math.floor(Math.random() * this.chars.length)]
}
}
// ——————————————————————————————————————————————————
// Configuration
// ——————————————————————————————————————————————————
const phrases = [
'MAIN_HUB'
]
const el = document.querySelector('.link_mainhub')
const fx = new TextScramble(el)
let counter = 0
const next = () => {
fx.setText(phrases[counter]).then(() => {});
}
el.addEventListener('mouseenter', next);
<a class="link_mainhub" onmouseenter="playAudio();">LINK</a>
Thanks.
Set a variable when you start playing the audio, and check for this before playing it again. Have the animation unset the variable when it's done.
function audioPlaying = false;
function playAudio() {
if (!audioPlaying) {
audioPlaying = true;
audioEl.play();
}
}
const next = () => {
fx.setText(phrases[counter]).then(() => {
audioPlaying = false;
});
}

How to send data from Node.js(webpack) to pure Node.js(express)?

I am getting data in a local storage of Webpack in JSON format. Node.js of Webpack is not allowing me to create a connection with MySQL. So, I am thinking if there is a way to send data to a backend(pure Node/Express server) like REST API.
I cannot use MySQL directly in Webpack node.js file
1.ref stackoveflow
2.ref GitHub
Below is my Webpack code I want save my localstorage data to database like MySQL, mongoDB.
import twitter from 'twitter-text';
import PDFJSAnnotate from '../';
import initColorPicker from './shared/initColorPicker';
/*import mysql from '../';
var con = mysql.createConnection({
host: "localhost",
user: "ali",
password: "demopassword"
});
con.connect(function(err) {
if (err) throw err;
console.log("Connected!");
});*/
//var hummus = require('HummusJS/hummus');
const { UI } = PDFJSAnnotate;
const documentId = 'question.pdf';
let PAGE_HEIGHT;
let RENDER_OPTIONS = {
documentId,
pdfDocument: null,
scale: parseFloat(localStorage.getItem(`${documentId}/scale`), 10) || 1.33,
rotate: parseInt(localStorage.getItem(`${documentId}/rotate`), 10) || 0
};
PDFJSAnnotate.setStoreAdapter(new PDFJSAnnotate.LocalStoreAdapter());
PDFJS.workerSrc = './shared/pdf.worker.js';
//var annotationz = localStorage.getItem(RENDER_OPTIONS.documentId + '/annotations') || [];
//console.log(annotationz);
// Render stuff
let NUM_PAGES = 0;
document.getElementById('content-wrapper').addEventListener('scroll', function (e) {
let visiblePageNum = Math.round(e.target.scrollTop / PAGE_HEIGHT) + 1;
let visiblePage = document.querySelector(`.page[data-page-number="${visiblePageNum}"][data-loaded="false"]`);
if (visiblePage) {
setTimeout(function () {
UI.renderPage(visiblePageNum, RENDER_OPTIONS);
});
}
});
function render() {
PDFJS.getDocument(RENDER_OPTIONS.documentId).then((pdf) => {
RENDER_OPTIONS.pdfDocument = pdf;
let viewer = document.getElementById('viewer');
viewer.innerHTML = '';
NUM_PAGES = pdf.pdfInfo.numPages;
for (let i=0; i<NUM_PAGES; i++) {
let page = UI.createPage(i+1);
viewer.appendChild(page);
}
UI.renderPage(1, RENDER_OPTIONS).then(([pdfPage, annotations]) => {
let viewport = pdfPage.getViewport(RENDER_OPTIONS.scale, RENDER_OPTIONS.rotate);
PAGE_HEIGHT = viewport.height;
});
});
}
render();
// Text stuff
(function () {
let textSize;
let textColor;
// let fontface;
function initText() {
let size = document.querySelector('.toolbar .text-size');
[8, 9, 10, 11, 12, 14, 18, 24, 30, 36, 48, 60, 72, 96].forEach((s) => {
size.appendChild(new Option (s, s));
});
/*let face = document.querySelector('.toolbar .font-face');
['fontGeorgiaSerif', 'fontPalatinoSerif', 'fontTimesSerif', 'Sans-Serif Fonts', 'fontArialSansSerif', 'fontComicSansMS', 'fontVerdanaSansMS', 'Monospace Fonts', 'fontCourierNew', 'fontLucidaConsole'].forEach((s) => {
face.appendChild(new Option (s, s));
});*/
setText(
localStorage.getItem(`${RENDER_OPTIONS.documentId}/text/size`) || 10,
localStorage.getItem(`${RENDER_OPTIONS.documentId}/text/color`) || '#000000'
//localStorage.getItem(`${RENDER_OPTIONS.documentId}/text/face`) || 'fontGeorgiaSerif'
// console.log(${RENDER_OPTIONS.documentId});
);
initColorPicker(document.querySelector('.text-color'), textColor, function (value) {
setText(textSize, value);
});
}
function setText(size, color) {
let modified = false;
if (textSize !== size) {
modified = true;
textSize = size;
localStorage.setItem(`${RENDER_OPTIONS.documentId}/text/size`, textSize);
document.querySelector('.toolbar .text-size').value = textSize;
}
/* if (fontface !== face) {
modified = true;
fontface = face;
localStorage.setItem(`${RENDER_OPTIONS.documentId}/text/face`, fontface);
document.querySelector('.toolbar .font-face').value = fontface;
}*/
if (textColor !== color) {
modified = true;
textColor = color;
localStorage.setItem(`${RENDER_OPTIONS.documentId}/text/color`, textColor);
let selected = document.querySelector('.toolbar .text-color.color-selected');
if (selected) {
selected.classList.remove('color-selected');
selected.removeAttribute('aria-selected');
}
selected = document.querySelector(`.toolbar .text-color[data-color="${color}"]`);
if (selected) {
selected.classList.add('color-selected');
selected.setAttribute('aria-selected', true);
}
}
if (modified) {
UI.setText(textSize, textColor);
}
}
function handleTextSizeChange(e) {
setText(e.target.value, textColor);
}
document.querySelector('.toolbar .text-size').addEventListener('change', handleTextSizeChange);
initText();
})();
// Pen stuff
(function () {
let penSize;
let penColor;
function initPen() {
let size = document.querySelector('.toolbar .pen-size');
for (let i=0; i<20; i++) {
size.appendChild(new Option(i+1, i+1));
}
setPen(
localStorage.getItem(`${RENDER_OPTIONS.documentId}/pen/size`) || 1,
localStorage.getItem(`${RENDER_OPTIONS.documentId}/pen/color`) || '#000000'
);
initColorPicker(document.querySelector('.pen-color'), penColor, function (value) {
setPen(penSize, value);
});
}
function setPen(size, color) {
let modified = false;
if (penSize !== size) {
modified = true;
penSize = size;
localStorage.setItem(`${RENDER_OPTIONS.documentId}/pen/size`, penSize);
document.querySelector('.toolbar .pen-size').value = penSize;
}
if (penColor !== color) {
modified = true;
penColor = color;
localStorage.setItem(`${RENDER_OPTIONS.documentId}/pen/color`, penColor);
let selected = document.querySelector('.toolbar .pen-color.color-selected');
if (selected) {
selected.classList.remove('color-selected');
selected.removeAttribute('aria-selected');
}
selected = document.querySelector(`.toolbar .pen-color[data-color="${color}"]`);
if (selected) {
selected.classList.add('color-selected');
selected.setAttribute('aria-selected', true);
}
}
if (modified) {
UI.setPen(penSize, penColor);
}
}
function handlePenSizeChange(e) {
setPen(e.target.value, penColor);
}
document.querySelector('.toolbar .pen-size').addEventListener('change', handlePenSizeChange);
initPen();
})();
// Toolbar buttons
(function () {
let tooltype = localStorage.getItem(`${RENDER_OPTIONS.documentId}/tooltype`) || 'cursor';
if (tooltype) {
setActiveToolbarItem(tooltype, document.querySelector(`.toolbar button[data-tooltype=${tooltype}]`));
}
function setActiveToolbarItem(type, button) {
let active = document.querySelector('.toolbar button.active');
if (active) {
active.classList.remove('active');
switch (tooltype) {
case 'cursor':
UI.disableEdit();
break;
case 'draw':
UI.disablePen();
break;
case 'text':
UI.disableText();
break;
case 'point':
UI.disablePoint();
break;
case 'area':
case 'highlight':
case 'strikeout':
UI.disableRect();
break;
}
}
if (button) {
button.classList.add('active');
}
if (tooltype !== type) {
localStorage.setItem(`${RENDER_OPTIONS.documentId}/tooltype`, type);
}
tooltype = type;
switch (type) {
case 'cursor':
UI.enableEdit();
break;
case 'draw':
UI.enablePen();
break;
case 'text':
UI.enableText();
break;
case 'point':
UI.enablePoint();
break;
case 'area':
case 'highlight':
case 'strikeout':
UI.enableRect(type);
break;
}
}
function handleToolbarClick(e) {
if (e.target.nodeName === 'BUTTON') {
setActiveToolbarItem(e.target.getAttribute('data-tooltype'), e.target);
}
}
document.querySelector('.toolbar').addEventListener('click', handleToolbarClick);
})();
// Scale/rotate
(function () {
function setScaleRotate(scale, rotate) {
scale = parseFloat(scale, 10);
rotate = parseInt(rotate, 10);
if (RENDER_OPTIONS.scale !== scale || RENDER_OPTIONS.rotate !== rotate) {
RENDER_OPTIONS.scale = scale;
RENDER_OPTIONS.rotate = rotate;
localStorage.setItem(`${RENDER_OPTIONS.documentId}/scale`, RENDER_OPTIONS.scale);
localStorage.setItem(`${RENDER_OPTIONS.documentId}/rotate`, RENDER_OPTIONS.rotate % 360);
render();
}
}
function handleScaleChange(e) {
setScaleRotate(e.target.value, RENDER_OPTIONS.rotate);
}
function handleRotateCWClick() {
setScaleRotate(RENDER_OPTIONS.scale, RENDER_OPTIONS.rotate + 90);
}
function handleRotateCCWClick() {
setScaleRotate(RENDER_OPTIONS.scale, RENDER_OPTIONS.rotate - 90);
}
document.querySelector('.toolbar select.scale').value = RENDER_OPTIONS.scale;
document.querySelector('.toolbar select.scale').addEventListener('change', handleScaleChange);
document.querySelector('.toolbar .rotate-ccw').addEventListener('click', handleRotateCCWClick);
document.querySelector('.toolbar .rotate-cw').addEventListener('click', handleRotateCWClick);
})();
// Clear toolbar button
(function () {
function handleClearClick(e) {
if (confirm('Are you sure you want to clear annotations?')) {
for (let i=0; i<NUM_PAGES; i++) {
document.querySelector(`div#pageContainer${i+1} svg.annotationLayer`).innerHTML = '';
}
localStorage.removeItem(`${RENDER_OPTIONS.documentId}/annotations`);
}
}
document.querySelector('a.clear').addEventListener('click', handleClearClick);
})();
// Comment stuff
(function (window, document) {
let commentList = document.querySelector('#comment-wrapper .comment-list-container');
let commentForm = document.querySelector('#comment-wrapper .comment-list-form');
let commentText = commentForm.querySelector('input[type="text"]');
function supportsComments(target) {
let type = target.getAttribute('data-pdf-annotate-type');
return ['point', 'highlight', 'area'].indexOf(type) > -1;
}
function insertComment(comment) {
let child = document.createElement('div');
child.className = 'comment-list-item';
child.innerHTML = twitter.autoLink(twitter.htmlEscape(comment.content));
commentList.appendChild(child);
}
function handleAnnotationClick(target) {
if (supportsComments(target)) {
let documentId = target.parentNode.getAttribute('data-pdf-annotate-document');
let annotationId = target.getAttribute('data-pdf-annotate-id');
PDFJSAnnotate.getStoreAdapter().getComments(documentId, annotationId).then((comments) => {
commentList.innerHTML = '';
commentForm.style.display = '';
commentText.focus();
commentForm.onsubmit = function () {
PDFJSAnnotate.getStoreAdapter().addComment(documentId, annotationId, commentText.value.trim())
.then(insertComment)
.then(() => {
commentText.value = '';
commentText.focus();
});
return false;
};
comments.forEach(insertComment);
});
}
}
function handleAnnotationBlur(target) {
if (supportsComments(target)) {
commentList.innerHTML = '';
commentForm.style.display = 'none';
commentForm.onsubmit = null;
insertComment({content: 'No comments'});
}
}
UI.addEventListener('annotation:click', handleAnnotationClick);
UI.addEventListener('annotation:blur', handleAnnotationBlur);
})(window, document);
Simple AJAX was working on the page because Webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging read more...
So in my case Node js was using Webpack module to load javascript in a browser. It is pure javascript. So I just implemented ajax on my javascript code and it is simply working like a REST API.

I want the whatsapp web bot to reply with an image pulled from a url how do I add the function?

The bot replies well when a command is sent.
How do I make the WhatsApp web bot to reply with an image pulled from a URL? I want it to be able to reply with an image pulled from a URL, for example, www.school.com/pic.jpg. On the code if a user text #time it replies with time and Date but I want it to reply with an image.
//
// FUNCTIONS
//
// Get random value between a range
function rand(high, low = 0) {
return Math.floor(Math.random() * (high - low + 1) + low);
}
function getElement(id, parent){
if (!elementConfig[id]){
return false;
}
var elem = !parent ? document.body : parent;
var elementArr = elementConfig[id];
for (var x in elementArr){
var pos = elementArr[x];
if (isNaN(pos*1)){ //dont know why, but for some reason after the last position it loops once again and "pos" is loaded with a function WTF. I got tired finding why and did this
continue;
}
if (!elem.childNodes[pos]){
return false;
}
elem = elem.childNodes[pos];
}
return elem;
}
function getLastMsg(){
var messages = document.querySelectorAll('.msg');
var pos = messages.length-1;
while (messages[pos] && (messages[pos].classList.contains('msg-system') || messages[pos].querySelector('.message-out'))){
pos--;
if (pos <= -1){
return false;
}
}
if (messages[pos] && messages[pos].querySelector('.selectable-text')){
return messages[pos].querySelector('.selectable-text').innerText;
} else {
return false;
}
}
function getUnreadChats(){
var unreadchats = [];
var chats = getElement("chats");
if (chats){
chats = chats.childNodes;
for (var i in chats){
if (!(chats[i] instanceof Element)){
continue;
}
var icons = getElement("chat_icons", chats[i]).childNodes;
if (!icons){
continue;
}
for (var j in icons){
if (icons[j] instanceof Element){
if (!(icons[j].childNodes[0].getAttribute('data-icon') == 'muted' || icons[j].childNodes[0].getAttribute('data-icon') == 'pinned')){
unreadchats.push(chats[i]);
break;
}
}
}
}
}
return unreadchats;
}
function didYouSendLastMsg(){
var messages = document.querySelectorAll('.msg');
if (messages.length <= 0){
return false;
}
var pos = messages.length-1;
while (messages[pos] && messages[pos].classList.contains('msg-system')){
pos--;
if (pos <= -1){
return -1;
}
}
if (messages[pos].querySelector('.message-out')){
return true;
}
return false;
}
// Call the main function again
const goAgain = (fn, sec) => {
// const chat = document.querySelector('div.chat:not(.unread)')
// selectChat(chat)
setTimeout(fn, sec * 1000)
}
// Dispath an event (of click, por instance)
const eventFire = (el, etype) => {
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent(etype, true, true, window,0, 0, 0, 0, 0, false, false, false, false, 0, null);
el.dispatchEvent(evt);
}
// Select a chat to show the main box
const selectChat = (chat, cb) => {
const title = getElement("chat_title",chat).title;
eventFire(chat.firstChild.firstChild, 'mousedown');
if (!cb) return;
const loopFewTimes = () => {
setTimeout(() => {
const titleMain = getElement("selected_title").title;
if (titleMain !== undefined && titleMain != title){
console.log('not yet');
return loopFewTimes();
}
return cb();
}, 300);
}
loopFewTimes();
}
// Send a message
const sendMessage = (chat, message, cb) => {
//avoid duplicate sending
var title;
if (chat){
title = getElement("chat_title",chat).title;
} else {
title = getElement("selected_title").title;
}
ignoreLastMsg[title] = message;
messageBox = document.querySelectorAll("[contenteditable='true']")[0];
//add text into input field
messageBox.innerHTML = message.replace(/ /gm,'');
//Force refresh
event = document.createEvent("UIEvents");
event.initUIEvent("input", true, true, window, 1);
messageBox.dispatchEvent(event);
//Click at Send Button
eventFire(document.querySelector('span[data-icon="send"]'), 'click');
cb();
}
//
// MAIN LOGIC
//
const start = (_chats, cnt = 0) => {
// get next unread chat
const chats = _chats || getUnreadChats();
const chat = chats[cnt];
var processLastMsgOnChat = false;
var lastMsg;
if (!lastMessageOnChat){
if (false === (lastMessageOnChat = getLastMsg())){
lastMessageOnChat = true; //to prevent the first "if" to go true everytime
} else {
lastMsg = lastMessageOnChat;
}
} else if (lastMessageOnChat != getLastMsg() && getLastMsg() !== false && !didYouSendLastMsg()){
lastMessageOnChat = lastMsg = getLastMsg();
processLastMsgOnChat = true;
}
if (!processLastMsgOnChat && (chats.length == 0 || !chat)) {
console.log(new Date(), 'nothing to do now... (1)', chats.length, chat);
return goAgain(start, 3);
}
// get infos
var title;
if (!processLastMsgOnChat){
title = getElement("chat_title",chat).title + '';
lastMsg = (getElement("chat_lastmsg", chat) || { innerText: '' }).innerText; //.last-msg returns null when some user is typing a message to me
} else {
title = getElement("selected_title").title;
}
// avoid sending duplicate messaegs
if (ignoreLastMsg[title] && (ignoreLastMsg[title]) == lastMsg) {
console.log(new Date(), 'nothing to do now... (2)', title, lastMsg);
return goAgain(() => { start(chats, cnt + 1) }, 0.1);
}
// what to answer back?
let sendText
if (lastMsg.toUpperCase().indexOf('#HELP') > -1){
sendText = `
Cool ${title}! Some commands that you can send me:
1. *#TIME*
2. *#JOKE*`
}
if (lastMsg.toUpperCase().indexOf('#About') > -1){
sendText = `
Cool ${title}! Some commands that you can send me:
*${new Date()}*`
}
if (lastMsg.toUpperCase().indexOf('#TIME') > -1){
sendText = `
Don't you have a clock, dude?
*${new Date()}*`
}
if (lastMsg.toUpperCase().indexOf('#JOKE') > -1){
sendText = jokeList[rand(jokeList.length - 1)];
}
// that's sad, there's not to send back...
if (!sendText) {
ignoreLastMsg[title] = lastMsg;
console.log(new Date(), 'new message ignored -> ', title, lastMsg);
return goAgain(() => { start(chats, cnt + 1) }, 0.1);
}
console.log(new Date(), 'new message to process, uhull -> ', title, lastMsg);
// select chat and send message
if (!processLastMsgOnChat){
selectChat(chat, () => {
sendMessage(chat, sendText.trim(), () => {
goAgain(() => { start(chats, cnt + 1) }, 0.1);
});
})
} else {
sendMessage(null, sendText.trim(), () => {
goAgain(() => { start(chats, cnt + 1) }, 0.1);
});
}
}
start();

Jquery Auto-Complete Limited Search Results (JSON)

I'm using the FoxyComplete plugin with Jquery Auto-Complete to do a simple search. Everything is setup and working great but I'm finding the search results extremely limiting.
I use a big JSON file for all of my data and the problem I'm having is in the following case: Let's assume that my JSON file has the following description:
{
"title": "Brand new black car"
},
If a user searches "black car", he or she will get the result perfectly and the search is great. BUT, if a user search "car new", nothing comes up even though both keywords are in my JSON file.
Any help would be great. I poured through the Jquery AutoComplete docs and couldn't find a solution either. My Jquery autocomplete js is below:
;
(function ($) {
$.fn.extend({
autocomplete: function (urlOrData, options) {
var isUrl = typeof urlOrData == "string";
options = $.extend({}, $.Autocompleter.defaults, {
url: isUrl ? urlOrData : null,
data: isUrl ? null : urlOrData,
delay: isUrl ? $.Autocompleter.defaults.delay : 10,
max: options && !options.scroll ? 10 : 150
}, options);
// if highlight is set to false, replace it with a do-nothing function
options.highlight = options.highlight || function (value) {
return value;
};
// if the formatMatch option is not specified, then use formatItem for backwards compatibility
options.formatMatch = options.formatMatch || options.formatItem;
return this.each(function () {
new $.Autocompleter(this, options);
});
},
result: function (handler) {
return this.bind("result", handler);
},
search: function (handler) {
return this.trigger("search", [handler]);
},
flushCache: function () {
return this.trigger("flushCache");
},
setOptions: function (options) {
return this.trigger("setOptions", [options]);
},
unautocomplete: function () {
return this.trigger("unautocomplete");
}
});
$.Autocompleter = function (input, options) {
var KEY = {
UP: 38,
DOWN: 40,
DEL: 46,
TAB: 9,
RETURN: 13,
ESC: 27,
COMMA: 188,
PAGEUP: 33,
PAGEDOWN: 34,
BACKSPACE: 8
};
// Create $ object for input element
var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
var timeout;
var previousValue = "";
var cache = $.Autocompleter.Cache(options);
var hasFocus = 0;
var lastKeyPressCode;
var config = {
mouseDownOnSelect: false
};
var select = $.Autocompleter.Select(options, input, selectCurrent, config);
var blockSubmit;
// prevent form submit in opera when selecting with return key
$.browser.opera && $(input.form).bind("submit.autocomplete", function () {
if (blockSubmit) {
blockSubmit = false;
return false;
}
});
// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function (event) {
// a keypress means the input has focus
// avoids issue where input had focus before the autocomplete was applied
hasFocus = 1;
// track last key pressed
lastKeyPressCode = event.keyCode;
switch (event.keyCode) {
case KEY.UP:
event.preventDefault();
if (select.visible()) {
select.prev();
} else {
onChange(0, true);
}
break;
case KEY.DOWN:
event.preventDefault();
if (select.visible()) {
select.next();
} else {
onChange(0, true);
}
break;
case KEY.PAGEUP:
event.preventDefault();
if (select.visible()) {
select.pageUp();
} else {
onChange(0, true);
}
break;
case KEY.PAGEDOWN:
event.preventDefault();
if (select.visible()) {
select.pageDown();
} else {
onChange(0, true);
}
break;
// matches also semicolon
case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
case KEY.TAB:
case KEY.RETURN:
if (selectCurrent()) {
// stop default to prevent a form submit, Opera needs special handling
event.preventDefault();
blockSubmit = true;
return false;
}
break;
case KEY.ESC:
select.hide();
break;
default:
clearTimeout(timeout);
timeout = setTimeout(onChange, options.delay);
break;
}
}).focus(function () {
// track whether the field has focus, we shouldn't process any
// results if the field no longer has focus
hasFocus++;
}).blur(function () {
hasFocus = 0;
if (!config.mouseDownOnSelect) {
hideResults();
}
}).click(function () {
// show select when clicking in a focused field
if (hasFocus++ > 1 && !select.visible()) {
onChange(0, true);
}
}).bind("search", function () {
// TODO why not just specifying both arguments?
var fn = (arguments.length > 1) ? arguments[1] : null;
function findValueCallback(q, data) {
var result;
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
if (data[i].result.toLowerCase() == q.toLowerCase()) {
result = data[i];
break;
}
}
}
if (typeof fn == "function") fn(result);
else $input.trigger("result", result && [result.data, result.value]);
}
$.each(trimWords($input.val()), function (i, value) {
request(value, findValueCallback, findValueCallback);
});
}).bind("flushCache", function () {
cache.flush();
}).bind("setOptions", function () {
$.extend(options, arguments[1]);
// if we've updated the data, repopulate
if ("data" in arguments[1]) cache.populate();
}).bind("unautocomplete", function () {
select.unbind();
$input.unbind();
$(input.form).unbind(".autocomplete");
});
function selectCurrent() {
var selected = select.selected();
if (!selected) return false;
var v = selected.result;
previousValue = v;
if (options.multiple) {
var words = trimWords($input.val());
if (words.length > 1) {
var seperator = options.multipleSeparator.length;
var cursorAt = $(input).selection().start;
var wordAt, progress = 0;
$.each(words, function (i, word) {
progress += word.length;
if (cursorAt <= progress) {
wordAt = i;
return false;
}
progress += seperator;
});
words[wordAt] = v;
// TODO this should set the cursor to the right position, but it gets overriden somewhere
//$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
v = words.join(options.multipleSeparator);
}
v += options.multipleSeparator;
}
$input.val(v);
hideResultsNow();
$input.trigger("result", [selected.data, selected.value]);
return true;
}
function onChange(crap, skipPrevCheck) {
if (lastKeyPressCode == KEY.DEL) {
select.hide();
return;
}
var currentValue = $input.val();
if (!skipPrevCheck && currentValue == previousValue) return;
previousValue = currentValue;
currentValue = lastWord(currentValue);
if (currentValue.length >= options.minChars) {
$input.addClass(options.loadingClass);
if (!options.matchCase) currentValue = currentValue.toLowerCase();
request(currentValue, receiveData, hideResultsNow);
} else {
stopLoading();
select.hide();
}
};
function trimWords(value) {
if (!value) return [""];
if (!options.multiple) return [$.trim(value)];
return $.map(value.split(options.multipleSeparator), function (word) {
return $.trim(value).length ? $.trim(word) : null;
});
}
function lastWord(value) {
if (!options.multiple) return value;
var words = trimWords(value);
if (words.length == 1) return words[0];
var cursorAt = $(input).selection().start;
if (cursorAt == value.length) {
words = trimWords(value)
} else {
words = trimWords(value.replace(value.substring(cursorAt), ""));
}
return words[words.length - 1];
}
// fills in the input box w/the first match (assumed to be the best match)
// q: the term entered
// sValue: the first matching result
function autoFill(q, sValue) {
// autofill in the complete box w/the first match as long as the user hasn't entered in more data
// if the last user key pressed was backspace, don't autofill
if (options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE) {
// fill in the value (keep the case the user has typed)
$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
// select the portion of the value not typed by the user (so the next character will erase)
$(input).selection(previousValue.length, previousValue.length + sValue.length);
}
};
function hideResults() {
clearTimeout(timeout);
timeout = setTimeout(hideResultsNow, 200);
};
function hideResultsNow() {
var wasVisible = select.visible();
select.hide();
clearTimeout(timeout);
stopLoading();
if (options.mustMatch) {
// call search and run callback
$input.search(
function (result) {
// if no value found, clear the input box
if (!result) {
if (options.multiple) {
var words = trimWords($input.val()).slice(0, - 1);
$input.val(words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : ""));
} else {
$input.val("");
$input.trigger("result", null);
}
}
});
}
};
function receiveData(q, data) {
if (data && data.length && hasFocus) {
stopLoading();
select.display(data, q);
autoFill(q, data[0].value);
select.show();
} else {
hideResultsNow();
}
};
function request(term, success, failure) {
if (!options.matchCase) term = term.toLowerCase();
var data = cache.load(term);
// recieve the cached data
if (data && data.length) {
success(term, data);
// if an AJAX url has been supplied, try loading the data now
} else if ((typeof options.url == "string") && (options.url.length > 0)) {
var extraParams = {
timestamp: +new Date()
};
$.each(options.extraParams, function (key, param) {
extraParams[key] = typeof param == "function" ? param() : param;
});
$.ajax({
// try to leverage ajaxQueue plugin to abort previous requests
mode: "abort",
// limit abortion to this input
port: "autocomplete" + input.name,
dataType: options.dataType,
url: options.url,
data: $.extend({
q: lastWord(term),
limit: options.max
}, extraParams),
success: function (data) {
var parsed = options.parse && options.parse(data) || parse(data);
cache.add(term, parsed);
success(term, parsed);
}
});
} else {
// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
select.emptyList();
failure(term);
}
};
function parse(data) {
var parsed = [];
var rows = data.split("\n");
for (var i = 0; i < rows.length; i++) {
var row = $.trim(rows[i]);
if (row) {
row = row.split("|");
parsed[parsed.length] = {
data: row,
value: row[0],
result: options.formatResult && options.formatResult(row, row[0]) || row[0]
};
}
}
return parsed;
};
function stopLoading() {
$input.removeClass(options.loadingClass);
};
};
$.Autocompleter.defaults = {
inputClass: "ac_input",
resultsClass: "ac_results",
loadingClass: "ac_loading",
minChars: 1,
delay: 400,
matchCase: false,
matchSubset: true,
matchContains: false,
cacheLength: 10,
max: 100,
mustMatch: false,
extraParams: {},
selectFirst: true,
formatItem: function (row) {
return row[0];
},
formatMatch: null,
autoFill: false,
width: 0,
multiple: false,
multipleSeparator: ", ",
highlight: function (value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
},
scroll: true,
scrollHeight: 180
};
$.Autocompleter.Cache = function (options) {
var data = {};
var length = 0;
function matchSubset(s, sub) {
if (!options.matchCase) s = s.toLowerCase();
var i = s.indexOf(sub);
if (options.matchContains == "word") {
i = s.toLowerCase().search("\\b" + sub.toLowerCase());
}
if (i == -1) return false;
return i == 0 || options.matchContains;
};
function add(q, value) {
if (length > options.cacheLength) {
flush();
}
if (!data[q]) {
length++;
}
data[q] = value;
}
function populate() {
if (!options.data) return false;
// track the matches
var stMatchSets = {},
nullData = 0;
// no url was specified, we need to adjust the cache length to make sure it fits the local data store
if (!options.url) options.cacheLength = 1;
// track all options for minChars = 0
stMatchSets[""] = [];
// loop through the array and create a lookup structure
for (var i = 0, ol = options.data.length; i < ol; i++) {
var rawValue = options.data[i];
// if rawValue is a string, make an array otherwise just reference the array
rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
var value = options.formatMatch(rawValue, i + 1, options.data.length);
if (value === false) continue;
var firstChar = value.charAt(0).toLowerCase();
// if no lookup array for this character exists, look it up now
if (!stMatchSets[firstChar]) stMatchSets[firstChar] = [];
// if the match is a string
var row = {
value: value,
data: rawValue,
result: options.formatResult && options.formatResult(rawValue) || value
};
// push the current match into the set list
stMatchSets[firstChar].push(row);
// keep track of minChars zero items
if (nullData++ < options.max) {
stMatchSets[""].push(row);
}
};
// add the data items to the cache
$.each(stMatchSets, function (i, value) {
// increase the cache size
options.cacheLength++;
// add to the cache
add(i, value);
});
}
// populate any existing data
setTimeout(populate, 25);
function flush() {
data = {};
length = 0;
}
return {
flush: flush,
add: add,
populate: populate,
load: function (q) {
if (!options.cacheLength || !length) return null;
/*
* if dealing w/local data and matchContains than we must make sure
* to loop through all the data collections looking for matches
*/
if (!options.url && options.matchContains) {
// track all matches
var csub = [];
// loop through all the data grids for matches
for (var k in data) {
// don't search through the stMatchSets[""] (minChars: 0) cache
// this prevents duplicates
if (k.length > 0) {
var c = data[k];
$.each(c, function (i, x) {
// if we've got a match, add it to the array
if (matchSubset(x.value, q)) {
csub.push(x);
}
});
}
}
return csub;
} else
// if the exact item exists, use it
if (data[q]) {
return data[q];
} else if (options.matchSubset) {
for (var i = q.length - 1; i >= options.minChars; i--) {
var c = data[q.substr(0, i)];
if (c) {
var csub = [];
$.each(c, function (i, x) {
if (matchSubset(x.value, q)) {
csub[csub.length] = x;
}
});
return csub;
}
}
}
return null;
}
};
};
$.Autocompleter.Select = function (options, input, select, config) {
var CLASSES = {
ACTIVE: "ac_over"
};
var listItems,
active = -1,
data,
term = "",
needsInit = true,
element,
list;
// Create results
function init() {
if (!needsInit) return;
element = $("<div/>").hide().addClass(options.resultsClass).css("position", "absolute")
//.attr('id', 'benjamin')
.appendTo(document.body);
list = $("<ul/>").appendTo(element).mouseover(function (event) {
if (target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
$(target(event)).addClass(CLASSES.ACTIVE);
}
}).click(function (event) {
$(target(event)).addClass(CLASSES.ACTIVE);
select();
// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
input.focus();
return false;
}).mousedown(function () {
config.mouseDownOnSelect = true;
}).mouseup(function () {
config.mouseDownOnSelect = false;
});
if (options.width > 0) element.css("width", options.width);
needsInit = false;
}
function target(event) {
var element = event.target;
while (element && element.tagName != "LI")
element = element.parentNode;
// more fun with IE, sometimes event.target is empty, just ignore it then
if (!element) return [];
return element;
}
function moveSelect(step) {
listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
movePosition(step);
var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
if (options.scroll) {
var offset = 0;
listItems.slice(0, active).each(function () {
offset += this.offsetHeight;
});
if ((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
} else if (offset < list.scrollTop()) {
list.scrollTop(offset);
}
}
};
function movePosition(step) {
active += step;
if (active < 0) {
active = listItems.size() - 1;
} else if (active >= listItems.size()) {
active = 0;
}
}
function limitNumberOfItems(available) {
return options.max && options.max < available ? options.max : available;
}
function fillList() {
list.empty();
var max = limitNumberOfItems(data.length);
for (var i = 0; i < max; i++) {
if (!data[i]) continue;
var formatted = options.formatItem(data[i].data, i + 1, max, data[i].value, term);
if (formatted === false) continue;
var li = $("<li/>").html(options.highlight(formatted, term)).addClass(i % 2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
$.data(li, "ac_data", data[i]);
}
listItems = list.find("li");
if (options.selectFirst) {
listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
active = 0;
}
// apply bgiframe if available
if ($.fn.bgiframe) list.bgiframe();
}
return {
display: function (d, q) {
init();
data = d;
term = q;
fillList();
},
next: function () {
moveSelect(1);
},
prev: function () {
moveSelect(-1);
},
pageUp: function () {
if (active != 0 && active - 8 < 0) {
moveSelect(-active);
} else {
moveSelect(-8);
}
},
pageDown: function () {
if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
moveSelect(listItems.size() - 1 - active);
} else {
moveSelect(8);
}
},
hide: function () {
element && element.hide();
listItems && listItems.removeClass(CLASSES.ACTIVE);
active = -1;
},
visible: function () {
return element && element.is(":visible");
},
current: function () {
return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
},
show: function () {
var offset = $(input).offset();
element.css({
width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
top: offset.top + input.offsetHeight,
left: offset.left
}).show();
if (options.scroll) {
list.scrollTop(0);
list.css({
maxHeight: options.scrollHeight,
overflow: 'auto'
});
if ($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
var listHeight = 0;
listItems.each(function () {
listHeight += this.offsetHeight;
});
var scrollbarsVisible = listHeight > options.scrollHeight;
list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight);
if (!scrollbarsVisible) {
// IE doesn't recalculate width when scrollbar disappears
listItems.width(list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")));
}
}
}
},
selected: function () {
var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
return selected && selected.length && $.data(selected[0], "ac_data");
},
emptyList: function () {
list && list.empty();
},
unbind: function () {
element && element.remove();
}
};
};
$.fn.selection = function (start, end) {
if (start !== undefined) {
return this.each(function () {
if (this.createTextRange) {
var selRange = this.createTextRange();
if (end === undefined || start == end) {
selRange.move("character", start);
selRange.select();
} else {
selRange.collapse(true);
selRange.moveStart("character", start);
selRange.moveEnd("character", end);
selRange.select();
}
} else if (this.setSelectionRange) {
this.setSelectionRange(start, end);
} else if (this.selectionStart) {
this.selectionStart = start;
this.selectionEnd = end;
}
});
}
var field = this[0];
if (field.createTextRange) {
var range = document.selection.createRange(),
orig = field.value,
teststring = "<->",
textLength = range.text.length;
range.text = teststring;
var caretAt = field.value.indexOf(teststring);
field.value = orig;
this.selection(caretAt, caretAt + textLength);
return {
start: caretAt,
end: caretAt + textLength
}
} else if (field.selectionStart !== undefined) {
return {
start: field.selectionStart,
end: field.selectionEnd
}
}
};
})(jQuery);

Categories

Resources