I'm trying to make a sign that says how many users are currently on my website at a time using Javascript for my Meteor app (I don't know if that makes a difference). I have this code for tracking how many users are on the site at a time:
Presences.find({online: true}).count();
But how do I display this on my website with HTML? I think with the way Meteor is organized it will make things more difficult.
Oh and I should also mention that I have the iron-router package installed in this project so the templates are a little different. This may affect the code?
Its a bit tricky, you need a custom publish function and a virtual collection on the client side (userCount)
This only publishes the count of the number of users online, and changes it as it changes using the Observer query.
This way you don't have to publish all the documents of the users who are online :) so as you have more users it wont slow down your client as much.
Server side code
Meteor.publish("userCount", function() {
var self = this;
var presences = Presences.find({online: true});
var count = presences.count();
var handle = presences.observe({
added: function() {
if(!handle) return;
count++;
self.changed('userCount', 'count', {count: count});
},
removed: function() {
count--;
self.changed('userCount', 'count', {count: count});
}
});
self.added('userCount', 'count', {count: count});
self.onStop(function() {
handle.stop();
});
this.ready();
});
Client side code
var userCount = new Meteor.Collection("userCount");
Meteor.subscribe("userCount");
HTML (example)
<template name="example">
{{userCount}}
</template>
Template Helper
Template.example.userCount = function() {
var count_doc = userCount.findOne({_id:'count'});
return (count_doc && count_doc.count) || 0;
}
Simple, but NOT efficient solution:
PresenceTemplate.js:
Template.PresenceTemplate.helpers({
userCounts:function(){
return Presences.find({online: true}).count();
}
})
PresenceTemplate.html:
<template name="PresenceTemplate">
{{userCounts}}
</template>
Related
We have an application running that currently works with both 3D and 2D files, and do not experience any issues when loading 3D files and DWG.
But when trying to load a PDF neither my "onItemLoadSuccess" or "onItemLoadFail" gets run
Autodesk.Viewing.Initializer(options, function onInitialized() {
// Select the container for the viewer
viewerApp = new Autodesk.Viewing.ViewingApplication(container);
// Load settings, i.e extension manager
viewerApp.registerViewer(viewerApp.k3D,
Autodesk.Viewing.Private.GuiViewer3D, { extensions: [ 'ExtensionManager'] });
// Select model to load defined by URN
viewerApp.loadDocument(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
});
}
function onDocumentLoadSuccess(doc) {
var viewables = viewerApp.bubble.search({ 'type': 'geometry' });
if (viewables.length === 0) {
console.error('Document contains no viewables.');
return;
}
// Choose any of the avialble viewables
viewerApp.selectItem(viewables[0], onItemLoadSuccess, onItemLoadFail);
}
function onItemLoadSuccess(viewer, item) {
console.log('onItemLoadSuccess()!');
}
function onItemLoadFail(errorCode) {
console.error('onItemLoadFail() - errorCode:' + errorCode);
}
The PDF file will still open and load, so I am wondering if there might be a different way to run an onItemLoadSuccess function, or we have to do something a bit differently to ensure that our PDF's also gets loaded correctly.
Any help is highly appreciated!
Starting from Viewer v6.3 you can load PDF directly with Autodesk.PDF and pass in callbacks to loadModel like you do other models:
Autodesk.Viewing.Initializer(options, function() {
viewer.start()
viewer.loadExtension('Autodesk.PDF').then(function() {
viewer.loadModel('/path/to/pdf', { page: 1 }, onLoadSuccess, onLoadFail);
});
});
See release notes here: https://forge.autodesk.com/blog/viewer-release-notes-v-63
(Adding to Bryan's answer...)
I wrote a blog post about this. Take a look at the DEMO and sample code to help answer your question about 'onItemLoadSuccess / onItemLoadFail' events.
BLOG: https://forge.autodesk.com/blog/fast-pdf-viewingmarkup-inside-forge-viewer
DEMO: https://wallabyway.github.io/offline-pdf-markup/
Hope that helps!
First of all I would like to say I have very few experience in Django and plotly, so excuse me if I may be asking something which makes no sense or has very few meaning.
In Django enviroment I know that programming logic takes place in the "Views.py" file, and all variables set on this file can be transfered to the "template.html" file and use them inside double curly braces "{{ VariableFromView }}".
I was wondering if there is any way, once the plot is created, to retrieve the limits of the displayed area in the plotly plot, either on the "view.py" or "template.py" files, and save them in a variable which could be used in the "template.html" file, such as:
Variable definition example:
xbrush = [plotly.xmin, plotly.xmax]
ybrush = [plotly.ymin, plotly.ymax]
Variable call in template
{{ xbrush }}
{{ ybrush }}
In my case I amb using Python code for the "Views.py" and Javascript and html for the "templates.html" file (as well as a little bit of Django template language for dealing with variables)
Thank you very much for your help!
There are multiple ways to achieve the same. I would suggest you to go through django tutorial once again to get better understanding of template context and variables. Since you already have variables xbrush and ybrush in your views.py, just make sure you return these in the context of your response. Like render('template.html', {'xbrush':xbrush, 'ybrush': ybrush}).Now in your template.html you can access these values using {{ xbrush }}. So now all you need now is create a script tag in your html file and now you can assign these values to javascript variables like var xbrush = {{ xbrush }}
You'll need to do this using JavaScript, since rendering and all interaction with a Plotly plot is done in the browser. There's a full example (including a CodePen) that's close to what you're trying to do in the Plotly documentation here: https://plot.ly/javascript/zoom-events/
var plot = document.getElementById('plot').contentWindow;
var plotTwo = document.getElementById('plotTwo').contentWindow;
document.getElementById('plot').onload = function() {
pinger = setInterval(function(){
plot.postMessage({task: 'ping'}, 'https://plot.ly')
}, 100);
};
window.addEventListener('message', function(e) {
var message = e.data;
if(message.pong) {
console.log('Initial pong, frame is ready to receive');
clearInterval(pinger);
// Listening for zoom events, but you can also listen
// for click and hover by adding them to the array
plot.postMessage( {
task: 'listen',
events: ['zoom']
}, 'https://plot.ly');
plotTwo.postMessage({
task: 'relayout',
'update': {
'xaxis.fixedrange': true,
'yaxis.fixedrange': true
},
}, 'https://plot.ly');
}
else if( message.type == 'zoom' ){
console.log('zoom', message);
drawRectangle( message['ranges'] );
}
else {
console.log(message);
}
});
function drawRectangle( ranges ){
var rect = {
'type': 'rect',
'x0': ranges['x'][0],
'y0': ranges['y'][0],
'x1': ranges['x'][1],
'y1': ranges['y'][1],
'fillcolor': 'rgba(128, 0, 128, 0.7)',
}
plotTwo.postMessage({
'task': 'relayout',
'update': { shapes: [rect] },
}, 'https://plot.ly');
}
function newPlot(){
var plotURL = document.getElementById('plotURL').value + '.embed';
var iframe = document.getElementById('plot');
iframe.src = plotURL;
var iframeTwo = document.getElementById('plotTwo');
iframeTwo.src = plotURL;
}
My intention is to retrieve one random entry from a collection and display it on the website - if all sentences are through (read: the user has "seen" them), display something else (therefore a dummy sentence gets returned). But, on server start and on button-click events, this helper gets fired at least twice. Here is some code:
In client.js:
Template.registerHelper('random_sentence', function() {
fetched = _.shuffle(Sentences.find({
users: {
$nin: [this.userId]
}
}).fetch())[0];
if (fetched === undefined) {
return {
sentence: "done",
_id: 0,
done: true
};
}
Session.set('question', fetched._id);
console.log(fetched);
return fetched;
});
The helper function for the template:
sent: function(){
sent = Session.get('question');
return Sentences.findOne(sent);
}
in main template:
{{#with random_sentence}}
{{#if done}}
<!-- Display something else -->
{{else}}
<div class="container">
{{> question}}
</div>
{{/if}}
{{/with}}
the "question" template:
<div class="well">
<div class="panel-body text-center">
<h3>{{sent.sentence}}</h3>
</div>
</div>
If I don't return anything in the "random_sentences"-function,nothing get's displayed.
I don't know where my "logic failure" is situated? I'm new to meteor - so I might overlook something obvious.
Thanks in advance :-)
UPDATE: This is how I intended to get the new sentence and display it:
Template.answer.events({
'click': function(event) {
var text = event.target.getAttribute('id');
if (text !== null) {
var question = Session.get('question');
var setModifier = {
$inc: {}
};
setModifier.$inc[text] = 1;
Sentences.update(question, setModifier);
Meteor.call('update_user', question);
Notifications.success('Danke!', 'Deine Beurteilung wurde gespeichert.');
Blaze.render(Template.question, document.head);
}
}
});
In server.js (updating the question and a counter on the user):
Meteor.methods({
update_user: function(question) {
Sentences.update(question, {
$push: {
"users": this.userId
}
});
Meteor.users.update({
_id: this.userId
}, {
$inc: {
"profile.counter": 1
}
});
},
});
I found the Blaze.render function somewhere on the web. the "document.head" part is simply because this function needs a DOM Element to render to, and since document.body just "multiplies" the body, I ust moved it to the head. (DOM logic isn't my strong part).
An Idea I had: would it make the whole idea simpler to implement with iron-router? atm. I wanted to create a "one-page app" - I therefore thought that I don't need a router there.
Another problem: Getting this logic to work (User gets one random sentence, which he has not seen) and publishing small sets of the collection (so the Client don't have to download 5 MB of data before using).
Template helpers can be called multiple times so it's good to avoid making them stateful. You're better off selecting the random entry in an onCreated or onRendered template handler. There you can do your random select, update the state, and put your choice in a Session variable to be retrieved by the helper.
I was thinking on rewriting our legacy app which is using pure jQuery. It renders log data which it gets by websocket, it shows only last 100 records by removing old ones and appending new ones.
Because rendering speed matters, I've tried first to render random incoming data and Ractive's twice slower than our jQuery code. By benchmarks jQuery renders 1000 records in 15 seconds and Ractive's version in 30 seconds. ( our backend code pushes each event with 0.01 s delay )
Thus I wonder is there any tweak settings? The code I use is simple:
var LogApp = Ractive.extend({
template: '#items',
init: function() {
var self = this;
socket.bind("logs", function(data_raw) {
var data = JSON.parse(data_raw);
if (self.data.items.length > 100) {
self.pop('items');
}
self.unshift('items', data);
});
}
});
var ractive = new LogApp({
el: react,
data: {
items: []
}
});
<script id='items' type='text/ractive'>
{{#each items:i}} {{>item}} {{/each}}
</script>
<script id='item' type='text/ractive'>
<tr>
<td>{{id}}</td>
<td>{{log_level}}</td>
<td>{{log_message}}</td>
</tr>
</script>
With Ractive 0.7, performance is now better. It performs at ~11 seconds, with each item being about 10ms (See http://jsfiddle.net/aqe53ocm/).
You can also try using a merge instead of two operations, pop and unshift:
var copy = self.get('items').slice();
if (copy.length > 100) {
copy.pop();
}
copy.unshift(data);
self.merge('items', copy);
See http://jsfiddle.net/56hfm4bt/.
For example and timings having the dev tools open will impact times because it's console.time logging each item, so try without.
For the curious, there are changes coming in 0.8 that will spead this up to ~1ms per item.
Let me just preface by saying it's actually my crappy code that's leaking and crashing my browser, I just thought I better make the languages being used as clear as I could from the outset.
I have a test page here and the javascript can be found here. My problem is that when I try and drag and drop either one of the red pieces more than a few times it sucks up all browser resources and crashes the browser. I'm fairly certain the culprit is something in the following function in the Tracker() object but I'm absolutely stuck on how to debug this.
My current most likely culprit:
function register_draggable(ob) {
ob.config.jqId.draggable({cursor: 'move',
grid:[ob.config.size, ob.config.size],
containment: '#chessboard',
revert: 'invalid',
start: function() {
check_allowable_moves(ob.config.jqLocation,
ob.config.jqId,
ob);
},
stop: function() {
remove_allowable_moves();
}
});
}
If anyone could take a quick look and give me any suggestions on what I should be looking for, it would be enormously appreciated.
Solution
Turns out register_draggable() was the culprit. I registered a new draggable every time the location of a piece updated and all those draggables on the same object were doing nasty things.
Currently I now explicity destroy the old draggable before creating a new one. Current code is
function register_draggable(ob) {
ob.config.jqId.draggable('destroy');
ob.config.jqId.draggable({cursor: 'move',
grid:[ob.config.size, ob.config.size],
containment: '#chessboard',
revert: 'invalid',
start: function() {
check_allowable_moves(ob.config.jqLocation,
ob.config.jqId,
ob);
},
stop: function() {
remove_allowable_moves();
}
});
}
I don't think this is actually your problem, but it seems like your making an extra method call on register and check_ allowable_moves
return {
register_map: function(ob) { map = ob; },
register_piece: function(ob) {
ob.config.tracker = this;
register_draggable(ob);
},
register_draggable: function(ob) { register_draggable(ob); },
check_allowable_moves: function(location, jqPiece, ob) { check_allowable_moves(location, jqPiece, ob); }
}
can be shortened to
return {
register_map: function(ob) { map = ob; },
register_piece: function(ob) {
ob.config.tracker = this;
register_draggable(ob);
},
register_draggable: register_draggable,
check_allowable_moves: check_allowable_moves
}
Also
you are doing a double lookup here:
function remove_allowable_moves() {
$('.allowable').droppable('destroy');
$('.allowable').removeClass('allowable');
}
should be
function remove_allowable_moves() {
$('.allowable').droppable('destroy')
.removeClass('allowable');
}
Also
Whats the purpose of parsing and int into a float? Take off the parseFloat.
var x = parseInt(locs[1]);
var y = parseInt(locs[2]);
var x_min = parseFloat(x)-2;
var y_min = parseFloat(y)-2;
Finally
Why are you re-registering as draggable on drop? This could be the culprit, if your registering the draggable multiple times and only destroying it once.
jqCell.droppable({ accept: '#'+jqPiece.attr('id'),
drop: function(ev, ui) {
ob.config.jqLocation = $(this);
register_draggable(ob); // why this?
}
});
Other thoughts
Another thing I don't know if its going to help your performance, but it could clean up your code. the jquery selector allows commas so instead of
$('#coord-1-1').doStuff();
$('#coord-1-2').doStuff();
$('#coord-1-3').doStuff();
you could do
$('#coord-1-1, #coord-1-2, #coord-1-3').doStuff();
so your loop would only be concerned with generating the selector string and then you could run you operation on the entire set.
IMO a cleaner init
instead of
var map = new Map('content');
var piece1 = new Piece(map);
var piece2 = new Piece(map);
var tracker = new Tracker;
tracker.register_map(map);
map.render();
piece1.render('coord-4-4', '1');
piece2.render('coord-1-1', '2');
tracker.register_piece(piece1);
tracker.register_piece(piece2);
I'd like to see
$(document).ready(function() {
$('#content').MapGame({
pieces : { '1' : 'coord-4-4', '2' : 'coord-1-1' }
});
});
Now implementing that is a strech from what you have now, but when building a component for jQuery I like to start with a simple init and work from their. Thats one of the big goals of jQuery is to hide all the junk from the user and just let them spin up and instance of your plugin easily.
It feels like some event-handlers are registered multiple times, but I'm unsure. (Reason below.)
That doesn't answer the question, but you absolutely should put as much code outside of $(document).ready(…) as possible, in no case put all your code in there as you do now.
I fear that your code is so ineligible that it's too much work to understand it. Could you restructure it (All those function-in-function are really horrible to read.) and add some comments.
Maybe it's just me, but I find it too hard to read and understand. It's surely going to be a disaster to maintain.