Aframe: cycle through colors using array - aframe

I've been trying to cycle through colors using a custom component.
<script>
AFRAME.registerComponent('floor-cycle', {
init: function () {
var sceneEl = document.querySelector('a-scene');
var floorEl = sceneEl.querySelector('#floor')
status = 1;
floorEl.addEventListener('click', function () {
if(status==1) {
floorEl.setAttribute('color', 'red'); status = 2
}
else if(status==2) {
floorEl.setAttribute('color', 'blue'); status = 3;
}
else if(status==3) {
floorEl.setAttribute('color', 'green'); status = 1
}
}
);
}
});
</script>
The component uses status to set the color attribute on click event, however this seems inefficient. Can this be implemented using an array rather than status?
demo - https://codepen.io/MannyMeadows/pen/GWzJRB

You can make an array ['red','green','blue'] and Cycle through it:
colors = ['red','green','blue'];
let i = 0;
floorEl.addEventListener('click',function(){
floorEl.setAttribute('material','color', colors[i]);
function add(){(i==colors.length-1) ? i = 0 : i++;}
add();
});
Seems better as the array is now dynamic, not sure how about the performance.
working fiddle here: https://jsfiddle.net/gftruj/g9wfLgab/2/

Related

FullCalendar: show reversed list views

How can I reverse the events in the list views, so that the event with the most futuristic date appears at the beginning (top)?
#F.Mora your solution is almost perfect but in our case we add some custom classNames and have multiple items under each headline.
Here is our enhanced version :
eventAfterAllRender: function(view) {
var renderedEvents = $('.fc-list-table tr');
var reorderedEvents = [];
var blockEvents = null;
renderedEvents.map(function(key, event) {
if ($(event).hasClass('fc-list-heading')) {
if (blockEvents) {
reorderedEvents.unshift(blockEvents.children());
}
blockEvents = $('<tbody></tbody>');
}
blockEvents.append(event);
});
reorderedEvents.unshift(blockEvents.children());
$('.fc-list-table tbody').html(reorderedEvents);
}
#CarComp,
As ADyson commented in the comment to the OP, your best bet if you want do not want to deal with the dom after the html has been rendered is to download the source and make the modification there in the ListView renderSegList function.
Reverse the order of iteration through the list that it is being created and then you will have what you are looking for.
This will, of course, apply to all ListView implementations of the calendar. There would need to be an option added to toggle back and forth, which would be a bit more involved.
For anyone still looking for this, inverted event lists using jquery:
eventAfterAllRender: function(view) {
var eventosRendered = $('#timeline tr');
var eventosInversa = [];
var headingPendiente = null;
eventosRendered.map(function(key, evento) {
switch(evento.className) {
case 'fc-list-heading':
if (headingPendiente) {
eventosInversa.unshift(headingPendiente);
}
headingPendiente = evento;
break;
case 'fc-list-item':
eventosInversa.unshift(evento);
break;
}
});
eventosInversa.unshift(headingPendiente);
$('#timeline tbody').append(eventosInversa);
}
Here's the version I use (fullCalendar v4):
datesRender: function(info) {
var list = $(info.el).find('.fc-list-table tbody');
list.find('.fc-list-heading').each((i,heading) => {
var children = $(heading).nextUntil('.fc-list-heading')
list.prepend(children)
list.prepend(heading)
})
},
I used this for fullCalendar v5. It´s based on #Yo1 answer
eventsSet: function(dateInfo){
var renderedEvents = $('.fc-list-table tr');
var reorderedEvents = [];
var blockEvents = null;
renderedEvents.map(function(key, event) {
if ($(event).hasClass('fc-list-day')) {
if (blockEvents) {
reorderedEvents.unshift(blockEvents.children());
}
blockEvents = $('<tbody></tbody>');
}
blockEvents.append(event);
});
if (blockEvents){
reorderedEvents.unshift(blockEvents.children());
$('.fc-list-table tbody').html(reorderedEvents);
}
},

Infinite scrolling with Meteor

I am trying to load 12 items only each time, until the user scroll all the way down and load another 12 elements
For some reason my code doesn't work. When i upload another item, i can see it in the admin panel so it is successfully uploaded but i can't see it in the normal user view. i can only view the first 12 items uploaded and it doesn't load anymore items when i scroll.
Here is my code in the client side
if (Meteor.isClient) {
var ITEMS_INCREMENT = 12; //this one refers to the number of elements to load
Session.setDefault('itemsLimit', ITEMS_INCREMENT);
Deps.autorun(function() {
Meteor.subscribe('items', Session.get('itemsLimit'));
});
Template.list_products.helpers({
applications: function () {
var limit = Session.get("itemsLimit");
//return Products.find({}, { sort: {createdAt: -1},limit: limit }); // render latest first
return Products.find({}, { sort: {createdAt: 1},limit: limit }); // render first first
}
});
Template.list_products.moreResults = function() {
// If, once the subscription is ready, we have less rows than we
// asked for, we've got all the rows in the collection.
return Products.find({}, { sort: {createdAt: -1},limit: limit });
}
// whenever #showMoreResults becomes visible, retrieve more results
function showMoreVisible() {
var threshold, target = $("#showMoreResults");
if (!target.length) return;
threshold = $(window).scrollTop() + $(window).height() - target.height();
if (target.offset().top < threshold) {
if (!target.data("visible")) {
// console.log("target became visible (inside viewable area)");
target.data("visible", true);
Session.set("itemsLimit",
Session.get("itemsLimit") + ITEMS_INCREMENT);
}
} else {
if (target.data("visible")) {
// console.log("target became invisible (below viewable arae)");
target.data("visible", false);
}
}
}
// The below line is to run the above func every time the user scrolls
$(window).scroll(showMoreVisible);
}
Here how i solved it:
if(Meteor.isClient) {
Session.set("itemsLimit", 9); // to set the limit to 9
lastScrollTop = 0;
$(window).scroll(function(event){
if($(window).scrollTop() + $(window).height() > $(document).height() - 100) { // to detect scroll event
var scrollTop = $(this).scrollTop();
if(scrollTop > lastScrollTop){ // detect scroll down
Session.set("itemsLimit", Session.get("itemsLimit") + 9); // when it reaches the end, add another 9 elements
}
lastScrollTop = scrollTop;
}
});
}
It works like a charm now :)
You can implement it like this:
HTML File:
<template name="yourTemplateName">
<div id="divId">
{{#each dataArr}}
//your view here.
{{/each}}
</div>
{{#if noMoreItem}}
<span>No more items to show</span>
{{/if}}
</template>
JS File:
var pageNumber = new ReactiveVar(0);
var noMoreItem = new ReactiveVar(false);
var mainContainer = // Your element here example: document.getElementById('divId')
mainContainer.addEventListener('scroll', function(){
if(mainContainer.scrollHeight - mainContainer.scrollTop === mainContainer.clientHeight) {
getMoreItems();
}
});
var getMoreItems = function () {
if(pageNumber.get() < Math.floor(Counts.get('countItems')/12)) {
pageNumber.set(Number(pageNumber.get())+1);
Meteor.subscribe('pubName', pageNumber.get(), 12);
} else {
noMoreItem.set(true);
}
}
Template.yourTemplateName.rendered = function () {
pageNumber.set(0);
Meteor.subscribe('pubName', pageNumber.get(), 12);
}
Template.yourTemplateName.helpers({
'dataArr': function () {
return CollectionName.find();
},
'noMoreItem': function () {
return noMoreItem.get();
}
})
Publication:
Meteor.publish("pubName", function (pageNumber, pageSize) {
Counts.publish(this, 'countItems', Meteor.users.find(filter), {
noReady: true
});
return CollectionName.find({}, {
skip: pageNumber > 0 ? ((pageNumber) * pageSize) : 0,
limit: pageSize
})
});

How to return number of items in collection?

I'm new to Meteor and I want to create a slideshow with items from a collection, in this case simple words. The slideshow should be controlled by back and forward buttons and replace the current word.
In JavaScript/jQuery I would create an array of objects and a control index, with limits via if-statements, so the index never can drop below zero or overflow the length of the array.
See fiddle for working example:
http://jsfiddle.net/j0pqd26w/8/
$(document).ready(function() {
var wordArray = ["hello", "yes", "no", "maybe"];
var arrayIndex = 0;
$('#word').html(wordArray[arrayIndex]);
$("#previous").click(function(){
if (arrayIndex > 0) {
arrayIndex -= 1;
}
$('#word').html(wordArray[arrayIndex]);
});
$("#next").click(function(){
if (arrayIndex < wordArray.length) {
arrayIndex += 1;
}
$('#word').html(wordArray[arrayIndex]);
});
});
Meteor
I'm curious how to implement this in regards to best practice in meteor and abide to the reactive pattern as I'm still trying to wrap my head around this interesting framework. My first hurdle is to translate the
if (arrayIndex < wordArray.length)
// to
if (Session.get("wordIndex") < ( (((length of collection))) )
According to the docs I should do a find on the collection, but I have only manage to return an empty array later with fetch. Sorry if this got long, but any input would be appreciated to help me figure this out.
collection.find([selector], [options])
cursor.fetch()
This is the code I have so far:
Words = new Mongo.Collection("words");
if (Meteor.isClient) {
// word index starts at 0
Session.setDefault("wordIndex", 0);
Template.body.helpers({
words: function () {
return Words.find({});
},
wordIndex: function () {
return Session.get("wordIndex");
}
});
Template.body.events({
"submit .new-word": function (event) {
// This function is called when the word form is submitted
var text = event.target.text.value;
Words.insert({
text: text,
createdAt: new Date() //current time
});
// Clear form
event.target.text.value = "";
// Prevent default form submit
return false;
},
'click #previous': function () {
// decrement the word index when button is clicked
if (Session.get("wordIndex") > 0) {
Session.set("wordIndex", Session.get("wordIndex") - 1);
}
},
'click #next': function () {
// increment the word index when button is clicked
if (Session.get("wordIndex") < 10 ) {
Session.set("wordIndex", Session.get("wordIndex") + 1);
}
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
});
}
.count() will return the number of documents in a collection.
`db.collection.count()`
There is something called Collection helpers, which works similar to other helpers (eg., template, etc.,). More elaborate explanation is covered here: https://medium.com/space-camp/meteor-doesnt-need-an-orm-2ed0edc51bc5

how to trigger JQuery .draggable() on elements created by templates?

I have a standard template in an Html file like:
<template name="cards">
{{#each all_cards}}
{{> card_item}}
{{/each}}
</template>
<template name="card_item">
<div class="card" style="left:{{position.x}}px; top:{{position.y}}px">
{{title}}
</div>
</template>
I want to have the cards (css selector .card) become draggable with JQuery.
Now since Meteor automagically updates the DOM using the template, when and how do I know where to call .draggable() on what??
EDIT: This is so far my solution which makes pending movements on other client visible with a wobble animation (using CSS3):
Template.card_item.events = {
'mouseover .card': function (e) {
var $target = $(e.target);
var $cardContainer = $target.hasClass('card') ? $target : $target.parents('.card');
$cardContainer.draggable({containment: "parent", distance: 3});
},
'dragstart .card': function (e) {
Session.set("dragging_id", e.target.id);
$(e.target).addClass("drag");
pos = $(e.target).position();
Events.insert({type: "dragstart", id:e.target.id, left: pos.left, top: pos.top});
},
'dragstop .card': function (e) {
pos = $(e.target).position();
Events.insert({type: "dragstop", id:e.target.id, left: pos.left, top: pos.top});
Cards.update(e.target.id, {$set: {left:pos.left, top:pos.top}});
Session.set("dragging_id", null);
}
}
Events.find().observe({
added: function(event) {
if (event.type == "dragstart" && !Session.equals("dragging_id", event.id)) {
$("#"+event.id).draggable({disabled: true});
$("#"+event.id).addClass("wobble");
}
if (event.type == "dragstop" && !Session.equals("dragging_id", event.id)) {
$("#"+event.id).animate({left: event.left, top: event.top}, 250);
Events.remove({id:this.id});
$("#"+event.id).draggable({disabled: false});
}
}
});
EDIT: This approach doesn't seem to work in the latest versions of Meteor, e.g. v0.5.0. See my comment below.
Looks like we're working on similar things! I've got a working proof of concept for a simple Magic: The Gathering app. Here's how I have dragging implemented at the moment:
In a <head> section in one of your html files, include the jQuery UI script:
<script src="jquery-ui-1.8.20.custom.min.js"></script>
Then, in a js file, make sure elements become draggable on mouseover (note: this is sub-optimal on touchscreens since it requires two touches to drag... I'm looking for a better touchscreen solution):
Template.card_item.events['mouseover .card, touchstart .card'] = function (e) {
var $target = $(e.target);
if (!$target.data('isDraggable')) {
$target.data('isDraggable', true).draggable();
}
};
And finally, handle the drag and dragstop events:
var prevDraggedTime = 0
Template.card_item.events['drag .card'] = function (e) {
// get the cardId from e
var now = new Date().getTime();
var position;
if (now - prevDraggedTime > 250) {
position = $(e.target).position();
Cards.update(cardId, {$set: {x: position.top, y: position.left}});
prevDraggedTime = now;
}
}
Template.card_item.events['dragstop .card'] = function (e) {
// get the cardId from e
var position = $(e.target).position();
Cards.update(cardId, {$set: {x: position.top, y: position.left}});
}

how to concisely write this javascript to show/hide a list of elements?

How to write this type of code in loop? Actually I don't want to write the same same line again and again, Is their any way to compress this code? can we write this code in loop?
function showCandidates()
{document.getElementById("cand9").style.display="block";
document.getElementById("cand10").style.display="block";
document.getElementById("cand11").style.display="block";
document.getElementById("cand12").style.display="block";
document.getElementById("cand13").style.display="block";
document.getElementById("cand14").style.display="block";
document.getElementById("cand15").style.display="block";
document.getElementById("hide_cand").style.display="block";
document.getElementById("view_cand").style.display="none";
}
function hideCandidates()
{document.getElementById("cand9").style.display="none";
document.getElementById("cand10").style.display="none";
document.getElementById("cand11").style.display="none";
document.getElementById("cand12").style.display="none";
document.getElementById("cand13").style.display="none";
document.getElementById("cand14").style.display="none";
document.getElementById("cand15").style.display="none";
document.getElementById("hide_cand").style.display="none";
document.getElementById("view_cand").style.display="block";
}
I suggest this way:
var show_ids = ["cand9", "cand10", "cand11"] // ... and so on
funciton showCandidates() {
for (var index in show_ids) {
var id = show_ids[index];
document.getElementById(id).style.display="none";
}
}
similar for hideCandidates
You should assign to your html elements a class for example
<div class="hideable" >content </div>
Then either you use JQuery or plain javascript to get all the elements that have the "hideable class attribute:
document.getElementsByClassName('hideable')
or
>$(".hideable")
Since your the two previous methods will return an array, you will have to loop through the array and apply the appropriate style attribute.
Firstly, this can be all encapsulated into one function. The function can take a parameter to assign to the display property. And obviously use some if statement in there to deal with the view_cand elements' display.
I would look into using jquery for this though, it makes selecting DOM elements (especially sets of DOM elements) a damn site easier.
I'd write the code for you here but I don't know anything about the elements you're selecting or the structure to your DOM.
Something like this?
for(i=0;i<candNumber;i++){
id= "cand" + i;
document.getElementById(id).style.display="block";
}
Try this .It'll hide/show ( the wayas you requested) by parameter given to function.
setVisibilityByClass("visible"/"invisible") - shows/hides by changing class
setVisibility("block"/"none") - shows/hides by changing styles directly
CHOOSE ONLY ONE.
css classes:
.vissible{ display: block; } .invissible{ display: none; }
Js functions:
function setVisibility(val) {
var not = new Array;
not["none"] = "block";
not["block"] = "none";
for (i = 9; i <= 15; i++){
document.getElementById("cand" + i).style.display = val;
}
document.getElementById("hide_cand").style.display = val;
document.getElementById("view_cand").style.display = not[val];
}
function setVisibilityByClass(val) {
var not = new Array;
not["invissible"] = "vissible";
not["vissible"] = "invissible";
for (i = 9; i <= 15; i++){
document.getElementById("cand" + i).setAttribute("class", val);
}
document.getElementById("hide_cand").setAttribute("class", val);
document.getElementById("view_cand").setAttribute("class", not[val]);
}
I hope this helps:
(function() {
"use strict";
var candidates = {
idx: 0,
getElement: function(id) { return document.getElementById(id); },
toggle: function(elmnts, obj) {
var idx = candidates.idx,
getElement = function(id) { return candidates.getElement(id); };
if (elmnts.length) {
while ( idx < elmnts.length ) {
getElement(elmnts[idx]).style.display = obj.display;
idx++;
}
}
}
};
var idsToHide = [
"cand9", "cand10", "cand11", "cand12",
"cand13", "cand14", "cand15", "hide_cand"
];
var idsToShow = [
"cand9", "cand10", "cand11", "cand12",
"cand13", "cand14", "cand15", "hide_cand"
];
function showCandidates() {
candidates.toggle(idsToShow, {
display: "block"
});
candidates.toggle(["view_cand"], { display: "none" });
}
function hideCandidates() {
candidates.toggle(idsToHide, {
display: "none"
});
candidates.toggle(["view_cand"], { display: "block" });
}
})();
Easy to do with jQuery:
$(document).ready(function(){
$("#candidates").toggle(function (){
$(this).text('Hide Candidates');
$.each($('.candidate'), function() {
$(this).show();
});
}, function() {
$(this).text('Show Candidates');
$.each($('.candidate'), function() {
$(this).hide();
});
});
});
HTML:
Show Candidates
<div class='candidate' id='1'>
<h1>Hello</h1>
</div>
<div class='candidate' id='2'>
<h1>Hello</h1>
</div>
<div class='candidate' id='3'>
<h1>Hello</h1>
</div>
CSS:
.candidate { display: none }
Here's a JS fiddle: http://jsfiddle.net/vbh5T/
If you don't want to use jQuery then please ignore my answer.
(1) First of all, doing these kinds of lookups is best done with jquery. Apart from being easier (see code below), it also allows you pre-calculate the set of elements to act on. This matters, because lookups by ID scan the whole document tree. Accordingly, the more elements in the page, the slower it is to recalculate the set of elements to act on.
(2) Rather than setting individual properties, it is much better to use a css class.
<style>
.invisible {display:none !important;}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8"> // <![CDATA[
$(document).ready(function(){
var hide = function(i) {i.addClass('invisible');};
var show = function(i) {i.removeClass('invisible');};
var candidates = $("#cand9, #cand10 /* etc. [...] */");
/* or, if you rejig this to set a class on all candidate elements:
var candidates = $(".candidate"); */
var hide_cand = $("#hide_cand");
var view_cand = $("#view_cand");
function showCandidates()
{
show(candidates);
show(view_cand);
hide(hide_cand);
}
});
// ]]>
</script>
I leave the corresponding hideCandidates as an exercise for the reader.

Resources