How would I make only one tab open at a time in this accordion? - accordion

The original accordion is from the W3schools site. https://www.w3schools.com/howto/howto_js_accordion.asp.
I am trying to figure out how to only have one tab open a time? Any help would be greatly appreciated. Thanks.
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight){
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}

Answering my own question.
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
var elems = document.getElementsByClassName("accordion");
for(var it of elems) {
it.classList.remove("active");
it.nextElementSibling.style.maxHeight = null;
}
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight){
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
this.nextUntil().addClass( "close" );
}
});
}

Related

Can't figure out how to select items in the Facebook newsfeed for a Chome Extension

I'm working on a Chrome extension that inserts a button on every item in the Facebook newsfeed. I started off by using Tampermonkey to install a script that installs a button next to the subscribe video of every Youtube page (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/ask.html?aid=994ff494-1242-452f-a334-1bd616e18bb6), which worked fine.
Then I tried to modify it so it acts on the Facebook newsfeed, rather than the Youtube subscribe button. I changed the // match to go to facebook.com and targeting it to go after class='_3vuz', the Facebook div that houses the "like" button. But nothing happens when I go on Facebook; no button appears.
Here's my Tampermonkey code. Many thanks!
// ==UserScript==
// #name Facebook Fake News Button
// #namespace https://www.youtubeinmp3.com
// #version 1.2.2
// #description Adds a button to show you whether a Facebook article is true
// #author Ilana
// #match https://www.facebook.com/*
// #run-at document-end
// ==/UserScript==
function polymerInject(){
/* Create button */
var buttonDiv = document.createElement("div");
buttonDiv.style.width = "100%";
buttonDiv.id = "parentButton";
var addButton = document.createElement("button");
addButton.appendChild(document.createTextNode("Fake News"));
if(typeof(document.getElementById("iframeDownloadButton")) != 'undefined' && document.getElementById("iframeDownloadButton") !== null){
document.getElementById("iframeDownloadButton").remove();
}
addButton.style.width = "100%";
addButton.style.backgroundColor = "#181717";
addButton.style.color = "white";
addButton.style.textAlign = "center";
addButton.style.padding = "10px 0";
addButton.style.marginTop = "5px";
addButton.style.fontSize = "14px";
addButton.style.border = "0";
addButton.style.cursor = "pointer";
addButton.style.borderRadius = "2px";
addButton.style.fontFamily = "Roboto, Arial, sans-serif";
addButton.onclick = function () {
this.remove();
/* Add large button on click */
var addIframe = document.createElement("iframe");
addIframe.src = '//www.convertmp3.io/widget/button/?color=ba1717&video=' + window.location.href;
addIframe.style.width = "100%";
addIframe.style.border = "none";
addIframe.style.height = "60px";
addIframe.style.marginTop = "10px";
addIframe.style.overflow = "hidden";
addIframe.scrolling = "no";
addIframe.id = "iframeDownloadButton";
var targetElement = document.querySelectorAll("[id='meta']");
for(var i = 0; i < targetElement.length; i++){
if(targetElement[i].className.indexOf("ytd-watch") > -1){
targetElement[i].insertBefore(addIframe, targetElement[i].childNodes[0]);
}
}
};
buttonDiv.appendChild(addButton);
/* Find and add to target */
var targetElement = document.querySelectorAll("[class='_3vuz']");
for(var i = 0; i < targetElement.length; i++){
if(targetElement[i].className.indexOf("ytd-video-secondary-info-renderer") > -1){
targetElement[i].appendChild(buttonDiv);
}
}
/* Fix hidden description bug */
var descriptionBox = document.querySelectorAll("ytd-video-secondary-info-renderer");
if(descriptionBox[0].className.indexOf("loading") > -1){
descriptionBox[0].classList.remove("loading");
}
}
function standardInject() {
var pagecontainer=document.getElementById('page-container');
if (!pagecontainer) return;
if (/^https?:\/\/www\.facebook.com\/watch\?/.test(window.location.href)) run();
var isAjax=/class[\w\s"'-=]+spf\-link/.test(pagecontainer.innerHTML);
var logocontainer=document.getElementById('logo-container');
if (logocontainer && !isAjax) { // fix for blocked videos
isAjax=(' '+logocontainer.className+' ').indexOf(' spf-link ')>=0;
}
var content=document.getElementById('content');
if (isAjax && content) { // Ajax UI
var mo=window.MutationObserver||window.WebKitMutationObserver;
if(typeof mo!=='undefined') {
var observer=new mo(function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.addedNodes!==null) {
for (var i=0; i<mutation.addedNodes.length; i++) {
if (mutation.addedNodes[i].id=='watch7-container' ||
mutation.addedNodes[i].id=='watch7-main-container') { // old value: movie_player
run();
break;
}
}
}
});
});
observer.observe(content, {childList: true, subtree: true}); // old value: pagecontainer
} else { // MutationObserver fallback for old browsers
pagecontainer.addEventListener('DOMNodeInserted', onNodeInserted, false);
}
}
}
function onNodeInserted(e) {
if (e && e.target && (e.target.id=='watch7-container' ||
e.target.id=='watch7-main-container')) { // old value: movie_player
run();
}
}
function finalButton(){
var buttonIframeDownload = document.createElement("iframe");
buttonIframeDownload.src = '//www.convertmp3.io/widget/button/?color=ba1717&video=' + window.location.href;
buttonIframeDownload.id = "buttonIframe";
buttonIframeDownload.style.width = "100%";
buttonIframeDownload.style.height = "60px";
buttonIframeDownload.style.paddingTop = "20px";
buttonIframeDownload.style.paddingBottom = "20px";
buttonIframeDownload.style.overflow = "hidden";
buttonIframeDownload.scrolling = "no";
document.getElementById("watch-header").appendChild(buttonIframeDownload);
}
function run(){
if(!document.getElementById("parentButton") && window.location.href.substring(0, 25).indexOf("facebook.com") > -1 && window.location.href.indexOf("watch?") > -1){
var parentButton = document.createElement("div");
parentButton.className = "yt-uix-button yt-uix-button-default";
parentButton.id = "parentButton";
parentButton.style.height = "23px";
parentButton.style.marginLeft = "28px";
parentButton.style.paddingBottom = "1px";
parentButton.onclick = function () {
this.remove();
finalButton();
};
document.getElementById("watch7-user-header").appendChild(parentButton);
var childButton = document.createElement("span");
childButton.appendChild(document.createTextNode("Download MP3"));
childButton.className = "yt-uix-button-content";
childButton.style.lineHeight = "25px";
childButton.style.fontSize = "12px";
parentButton.appendChild(childButton);
}
}
if(document.getElementById("polymer-app") || document.getElementById("masthead") || window.Polymer){
setInterval(function(){
if(window.location.href.indexOf("watch?v=") < 0){
return false;
}
if(document.getElementById("count") && document.getElementById("parentButton") === null){
polymerInject();
}
}, 100);
}
else{
standardInject();
}

JavaScript - Hangman - Logic Issue

I'm trying to get my Hangman game to check if the letter is found in the word, but as of now it's checking if that letter is found in every character of the word. It won't allow me to guess again after the first guess.
setScreen("WelcomeScreen");
//variables
var WordArray = ["apple", "word", "quiz"];
var currentWord = "";
var wrongCounter = 0;
var bodyPartCounter = 0;
var Guess = "";
//Welcome Screen Code
onEvent("letsGoBtn", "click", function () {
setScreen("playingHangmanScreen");
generateWord();
setUpScreenElements();
});
function generateWord() {
currentWord = WordArray[randomNumber(0,2)];
console.log(currentWord);
}
function setUpScreenElements(){
for (var i = 0; i < currentWord.length; i++) {
showElement("letterArea" + [i]);
}
var showCorrectHint = "hintWord" + currentWord;
showElement(showCorrectHint);
console.log(showCorrectHint);
}
//Guessing Code
onEvent("submitBtn", "click", function () {
Guess = getText("guessInputTxt");
console.log("The guess for " + currentWord + " is: " + Guess);
checkGuess();
});
function checkGuess() {
for (var i = 0; i < currentWord.length; i++) {
if (Guess == currentWord.charAt([i])) {
setText("letterArea" + [i], Guess);
console.log("Correctly guessed letter is: " + currentWord.charAt([i]));
}
else {
wrongCounter++;
console.log("wrongCounter for " + currentWord + " is: " + wrongCounter);
if (wrongCounter == currentWord.length) {
bodyPartCounter++;
showElement("wrongGuessImg" + bodyPartCounter);
setText("livesLeftNumber", (6-bodyPartCounter));
}
if (bodyPartCounter === 6) {
setScreen("gameOverScreen");
}
}
}
resetGuessInput();
}
function resetGuessInput () {
setText("guessInputTxt", " ");
Guess = " ";
wrongCounter = 0;
}
//Game Over Screen and Play Again Button
onEvent("playAgainBtn", "click", function () {
hideElement("hintWord" + currentWord);
generateWord();
setUpScreenElements();
setScreen("playingHangmanScreen");
for (var i = 0; i <currentWord.length; i++) {
setText("letterArea" + [i], " ");
}
for (var j = 1; j < 7; j++) {
hideElement("wrongGuessImg" + [j]);
}
bodyPartCounter = 0;
wrongCounter = 0;
setText("livesLeftNumber", "6");
});
//Victory Screen
onEvent("goHomeBtn", "click", function() {
setScreen("WelcomeScreen");
});
I've attached the code, as well as, included the link to see the app in action.
Here's a link to the app
Thanks in advance!
The checkGuess function needs to first determine where the letter appears in the word (or not), and then update the board one time, instead of updating it for each character:
function checkGuess() {
var foundAtIndex = -1;
for (var i = 0; i < currentWord.length; i++) {
if (Guess == currentWord.charAt([i])) {
foundAtIndex = i;
break;
}
}
if (foundAtIndex >= 0) {
setText("letterArea" + [foundAtIndex], Guess);
// etc...
Luckily, Javascript provides a nice IndexOf function for strings, so you can remove the loop entirely and condense that first part into:
var foundAtIndex = currentWord.indexOf(Guess);

Can't get tumblr feed to show up

Here is the Script i'm using to stream my tumblr blog posts to my website, i have the correct API Key, and Base host name, still nothing appears. Is there some other information I'm bypassing ? any help will be greatly appreciated.
<script type="text/javascript">
var $_api_key = 'PhLtoFx1aCBtu9IG3pIuT9BPd0Vxb602nPZtdMKxHvHM89VH28',
$_base_hostname = 'http://amongtheprimeblog.tumblr.com',
$_full_width = 1170;
$_rows = 2
$_cols = 3;
$_min_width = $_full_width / $_cols,
$_nsfw_tag = 'nsfw';
$_display_limit = $_rows * $_cols,
$_post_limit = $_display_limit * 2,
$_photos_api = 'https://api.tumblr.com/v2/blog/'+$_base_hostname+'/posts/photo?limit='+$_post_limit+'&api_key='+$_api_key,
jQuery.ajax($_photos_api,
{
dataType: 'jsonp',
success: function ($_posts) {
//console.log($_posts);
var $_photos = [],
$_displayed = 0 ;
for(var i=0; i < $_post_limit; i++) {
var $_post_url = $_posts.response.posts[i].post_url.replace('http://','https://'),
$_tag_count = $_posts.response.posts[i].tags.length,
$_photo_count = $_posts.response.posts[i].photos[0].alt_sizes.length,
$_post_photo_big = $_posts.response.posts[i].photos[0].original_size.url.replace('http://','https://'),
$_post_photo_big_width = $_posts.response.posts[i].photos[0].original_size.width,
$_post_photo_big_height = $_posts.response.posts[i].photos[0].original_size.height,
$_can_display = true;
// Check NSFW to keep Adroll happy!
for (var k=0; k < $_tag_count; k++ ){
if($_posts.response.posts[i].tags[k] == $_nsfw_tag) {
$_can_display = false;
}
}
// Iterate for most suitable image size.
if($_can_display) {
for (var j=0; j < $_photo_count; j++ ) {
if($_posts.response.posts[i].photos[0].alt_sizes[j].width > $_min_width) {
$_post_photo = $_posts.response.posts[i].photos[0].alt_sizes[j].url.replace('http://','https://');
} else {
break;
}
}
$_photos.push('<a id="tumblr_'+i+'" class="tumblr_post" href="'+$_post_url+'" target="_blank" data-src-big="'+$_post_photo_big+'" data-src-big-width="'+$_post_photo_big_width+'" data-src-big-height="'+$_post_photo_big_height+'"><img src="'+$_post_photo+'"/></a>');
$_displayed++;
if( $_displayed == $_display_limit) {
break;
}
}
}
jQuery('#tumblr_images').html($_photos.join(''));
},
}
);
function loadTumblrModal(id) {
var $_post = jQuery('#'+id),
$_post_next = $_post.next('.tumblr_post').attr('id'),
$_post_prev = $_post.prev('.tumblr_post').attr('id'),
$_post_link = $_post.attr('href'),
$_modal = jQuery('#tumblrModal'),
$_post_photo_src = $_post.attr('data-src-big').replace('http://','https://'),
$_post_photo_height = parseInt($_post.attr('data-src-big-height')),
$_post_photo_width = parseInt($_post.attr('data-src-big-width')),
$_post_photo_ratio = $_post_photo_width / $_post_photo_height,
$_window_height = jQuery(window).height(),
$_dialog_padding = 20,
$_dialog_margin = 20,
$_dialog_height = jQuery(window).height() - ($_dialog_margin * 2),
$_follow_iframe = '<iframe class="follow-button " src="https://platform.tumblr.com/v1/follow_button.html?button_type=1&tumblelog=theiloveuglyblog&color_scheme=dark" frameborder="0" scrolling="no" width="118" height="25"></iframe>';
// Re-Calibrate Dialog if Image is smaller than Window
if($_post_photo_height < $_dialog_height) {
$_dialog_height = $_post_photo_height;
}
// Set Dialog & Image Dimensions
var $_image_height = $_dialog_height - ($_dialog_padding *2),
$_image_width = $_image_height * $_post_photo_ratio,
$_dialog_width = $_image_width + ($_dialog_padding * 2);
// Force First/Last if Undefined for Next/Prev. Creates looping
if(typeof $_post_next === 'undefined') {
$_post_next = $_post.parent().find('.tumblr_post').first().attr('id');
}
if(typeof $_post_prev === 'undefined') {
$_post_prev = $_post.parent().find('.tumblr_post').last().attr('id');
}
// FireUp all the Modal
$_modal.find('.modal-dialog').css({
"width" : $_dialog_width + 'px',
"height" : $_dialog_height + 'px',
"margin-left" : -($_dialog_width/2) + 'px',
"margin-top" : -($_dialog_height/2) + 'px',
});
$_modal.find('.tumblr_img').html('<img src="'+$_post_photo_src+'" width="'+$_image_width+'" height="'+$_image_height+'"/>');
$_modal.find('.tumblr_iframe').html($_follow_iframe);
$_modal.find('#tumblr-next').attr('data-id',$_post_next);
$_modal.find('#tumblr-prev').attr('data-id',$_post_prev);
}
jQuery('#tumblr_images').on('click','a',function(e){
var $_modal = jQuery('#tumblrModal');
e.preventDefault();
loadTumblrModal(jQuery(this).attr('id'));
$_modal.modal('show');
});
jQuery(document).on('click','.tumblrLink',function(e){
loadTumblrModal(jQuery(this).attr('data-id'));
});
</script>

chrome.serial receiveTimeout Not working.

Below code is a copy with minor edits from https://github.com/GoogleChrome/chrome-app-samples/tree/master/serial/ledtoggle. I am able to send a byte and receive a reply. I am not able to get an TimeoutError event in case of reply is not sent by the client. I have set timeout to 50 ms.
this.receiveTimeout = 50;
Entire code follows.
const DEVICE_PATH = 'COM1';
const serial = chrome.serial;
var ab2str = function(buf) {
var bufView = new Uint8Array(buf);
var encodedString = String.fromCharCode.apply(null, bufView);
return decodeURIComponent(escape(encodedString));
};
var str2ab = function(str) {
var encodedString = unescape((str));
var bytes = new Uint8Array(1);
bytes[0] = parseInt(encodedString);
}
return bytes.buffer;
};
var SerialConnection = function() {
this.connectionId = -1;
this.lineBuffer = "";
this.receiveTimeout =50;
this.boundOnReceive = this.onReceive.bind(this);
this.boundOnReceiveError = this.onReceiveError.bind(this);
this.onConnect = new chrome.Event();
this.onReadLine = new chrome.Event();
this.onError = new chrome.Event();
};
SerialConnection.prototype.onConnectComplete = function(connectionInfo) {
if (!connectionInfo) {
log("Connection failed.");
return;
}
this.connectionId = connectionInfo.connectionId;
chrome.serial.onReceive.addListener(this.boundOnReceive);
chrome.serial.onReceiveError.addListener(this.boundOnReceiveError);
this.onConnect.dispatch();
};
SerialConnection.prototype.onReceive = function(receiveInfo) {
if (receiveInfo.connectionId !== this.connectionId) {
return;
}
this.lineBuffer += ab2str(receiveInfo.data);
var index;
while ((index = this.lineBuffer.indexOf('$')) >= 0) {
var line = this.lineBuffer.substr(0, index + 1);
this.onReadLine.dispatch(line);
this.lineBuffer = this.lineBuffer.substr(index + 1);
}
};
SerialConnection.prototype.onReceiveError = function(errorInfo) {
log('Error');
if (errorInfo.connectionId === this.connectionId) {
log('Error');
this.onError.dispatch(errorInfo.error);
log('Error');
}
log('Error');
};
SerialConnection.prototype.connect = function(path) {
serial.connect(path, this.onConnectComplete.bind(this))
};
SerialConnection.prototype.send = function(msg) {
if (this.connectionId < 0) {
throw 'Invalid connection';
}
serial.send(this.connectionId, str2ab(msg), function() {});
};
SerialConnection.prototype.disconnect = function() {
if (this.connectionId < 0) {
throw 'Invalid connection';
}
serial.disconnect(this.connectionId, function() {});
};
var connection = new SerialConnection();
connection.onConnect.addListener(function() {
log('connected to: ' + DEVICE_PATH);
);
connection.onReadLine.addListener(function(line) {
log('read line: ' + line);
});
connection.onError.addListener(function() {
log('Error: ');
});
connection.connect(DEVICE_PATH);
function log(msg) {
var buffer = document.querySelector('#buffer');
buffer.innerHTML += msg + '<br/>';
}
document.querySelector('button').addEventListener('click', function() {
connection.send(2);
});
Maybe I'm reading the code incorrectly, but at no point do you pass receiveTimeout into chrome.serial. The method signature is chrome.serial.connect(string path, ConnectionOptions options, function callback), where options is an optional parameter. You never pass anything into options. Fix that and let us know what happens.

SuperBoxSelect: Using Shift+Click to Select Multiple Items at Once

When implementing SuperBoxSelect (http://www.sencha.com/forum/showthread.php?69307-3.x-Ext.ux.form.SuperBoxSelect), I've realized that it currently does not support shift + click selection of multiple items. Has anyone been able to implement this functionality or found a similar plugin that offers this functionality?
beforeadditem:function(self, recordValue) {
var start = 0;
var end = 0;
var record = this.findRecord(this.valueField, recordValue);
var recordIndex = this.store.indexOf(record);
if(window.event.shiftKey) {
this.multiSelectMode = true;
if(this.firstChoiceIndex == undefined) {
this.firstChoiceIndex = recordIndex;
this.view.all.item(recordIndex).addClass('x-combo-selected-shift');
return false;
} else {
this.secondChoiceIndex = recordIndex;
if(this.firstChoiceIndex > this.secondChoiceIndex) {
start = this.secondChoiceIndex;
end = this.firstChoiceIndex;
} else if(this.secondChoiceIndex > this.firstChoiceIndex) {
start = this.firstChoiceIndex;
end = this.secondChoiceIndex;
}
var selectedRecords = this.store.getRange(start, end);
Ext.each(selectedRecords, function(item, index, allitems) {
self.addRecord(item)
});
this.firstChoiceIndex = undefined;
this.secondChoiceIndex = undefined;
return false;
}
} else {
this.firstChoiceIndex = undefined;
this.secondChoiceIndex = undefined;
return true;
}
}
Add that listener and it works. The x-combo-selected-shift class is identical to the x-combo-selected class. It's just named different so the highlighting sticks to the item you shift+clicked on after you mouse out.