I want to represent my wordpress and github activity as a graph network using the javascript library Sigmajs. I am using the google feed api to get RSS feeds of all activity and translating them into nodes and edges on the graph.
But the feed api returns rss results asynchronously. As far as I know sigmajs does not natively support this so im getting undefined references. At this point its only around 20-30 nodes. Some possible solutions are:
Force google feed api to return results synchronously.
(not sure how to do this but im assuming it has something to do with appropriate closures?)
Create sigma instance for every feed result and push all graph objects into a single instance.
(Not sure its possible and library not well documented enough to try)
Fire an event each time result is returned to ensure sigma only processes one at a time.
(Again, not sure how to go about doing this)
Any guidance is very much appreciated. Thanks.
Here is my work so far http://fraseraddison.com
More examples and source at http://sigmajs.org/
The solution I went with was firing a custom event. Seems to be working because of javascript's synchronous event handling queue.
function getFeed()
{
return function callback(result)
{
if (!result.error)
{
console.log("Feed retrieved.");
fireFeed(result.feed);
}
else
console.log("Feed retrieval failed!");
}
}
function fireFeed(feed)
{
//console.log(feed);
var event = new CustomEvent(
"newFeed",
{
detail: {
message: feed
},
bubbles: true,
cancelable: true
}
);
document.dispatchEvent(event);
}
document.addEventListener('newFeed', function(e)
{
var feed = e.detail.message;
console.log('feed triggered');
//console.log(feed);
buildFeed(feed);
},true);
Related
I have a pub that wraps and external API. Client subs the external api pub. There is a 'ACTIVATE' button they can push to activate a billing method. Button calls an update method that updates the collection. The pub updates the external api. Simulation runs and updates the client collection. Button changes to 'DEACTIVATE' as expected. This is where the issue comes in. The external api takes some time to return with the updated doc. Within 100-200ms of the button turning to 'DEACTIVATE' it will flip back to 'ACTIVATE' and then 500ms latter back to 'DEACTIVATE' where it should be assuming there were no issues with the external api.
I'm sure I could come up with some hacky solution to deal with this in the client but wondering if there is a way to tell the simulation/client collection that the pub is slow and to not update quite as often? Thus, giving the pub/external api more time to complete it's updates.
This turned out to be really simple.
Client side simulation alone is not enough. The trick is to do server side simulation as well. To accomplish this first setup a hook to the Meteor.publish this object something like this.
_initServer() {
if (Meteor.isServer) {
console.log(`Server initializing external collection "${this.name}"`)
let self = this
Meteor.publish(this.name, function (selector, options) {
check(selector, Match.Optional(Match.OneOf(undefined, null, Object)))
check(options, Match.Optional(Match.OneOf(undefined, null, Object)))
self.publication = this
self._externalApi.fetchAll()
.then((docs)=>docs.forEach((doc)=>this.added(self.name, doc._id, doc)))
.then(()=>this.ready())
// todo handle error
.catch((error)=>console.error(`${self.name}._initServer: self._externalApi.fetchAll`, error))
})
}
}
Then in your update function you can simulate on both the client and server like so:
this.update = new ValidatedMethod({
name: `${self.name}.update`,
validate: (validators && validators.update) ? validators.update : self.updateSchema.validator({clean: true}),
run(doc) {
console.log(`${self.name}.update `, doc)
if (Meteor.isServer && self._externalApi.update) {
// server side simulation
self.changed(doc)
self._externalApi.update(doc._id, doc)
.then(self.changed)
.catch((error)=>handleError(`${self.name}.update`, 'externalApi.update', error))
} else {
// client side simulation
self.collection.update(doc._id, {$set: doc})
}
},
})
Apologizes if this is over simplified these examples are from a large library we use for external api's.
Using Meteor, I'd like to understand the most efficient way to use JQuery UI's Autocomplete with large volumes of server-side data.
I have two working proposals and would like to hear opinions on the differences and if there are any better ways to do the same thing.
Using pub/sub:
// Server
Meteor.publish("autocompleteData", function (theSearchTerm) {
var query = {
name: { $regex: theSearchTerm, $options: 'i'}
};
return MyData.find(query, options);
});
// Client
Template.myTemplate.rendered = function() {
initAutocomplete($(this.find('.my.autocomplete')));
};
var initAutocomplete = function(element){
element.customAutocomplete({
source: function(request, callback){
var sub = Meteor.subscribe('autocompleteData', request.term, function(){
var results = MyData.find({}, {limit: 50}).fetch();
sub.stop();
callback(results);
});
},
select: function(event, ui){
// Do stuff with selected value
}
});
};
Using remote functions (Meteor.Methods):
// Server
Meteor.methods({
getData: function(theSearchTerm) {
var query = {
name: { $regex: theSearchTerm, $options: 'i'}
};
return MyData.find(query, {limit: 50}).fetch();
});
});
// Client
Template.myTemplate.rendered = function() {
initAutocomplete($(this.find('.my.autocomplete')));
};
var initAutocomplete = function(element){
element.customAutocomplete({
source: function(request, callback){
Meteor.call('getData', request.term, function(err, results){
callback(results);
});
},
select: function(event, ui){
// Do stuff with selected value
}
});
};
Which, if either, is the the most efficient way to setup a server-side autocomplete using Meteor with a large dataset?
For what it's worth, I'll offer a few of my thoughts on the subject. As a disclaimer, I'm just a Meteor enthusiast and not an expert, so please correct me if I've said something faulty.
To me, it seems like a potential advantage of pub/sub in cases like these is that data is cached. So when subscribing to the same record set, lookup will be near instantaneous since the client can search the local cache instead of asking the server for data again (publication is smart enough not to push repeated data to the client).
However, the advantage is lost here since you're stopping the subscription, so every time the user types the same search term, data is again pushed to the client (at least, the cursor's added event fires again for every document). In this case I would expect the pub/sub to be on nearly equal footing with Meteor.call.
If you want to cache the data of pub/sub, one way is to take out the sub.stop(). But unless your users have the tendency to search similar terms, caching the data is actually worse since with every letter the user types more data will be stored on the client, perhaps never to be seen again (unless searching is such a prominent feature in your app that the user would benefit from this?).
Overall, I see no compelling advantage with using pub/sub over Meteor methods, though I'm not versed in Meteor well enough to offer more specific advantages/disadvantages between the two. I personally think Meteor methods looks cleaner though.
If you're trying to implement a search feature though, I personally like the easy-search package, which supports this type of server-side search with autocomplete. In any case, I hope you get your question resolved! I'm curious to know the answer too.
So, for a little backstory, I'm using the jRecorder plugin for jQuery on a page that is dynamically fed some HTML via ajax. The user can record a .wav narration for an image in a slideshow, then (via ajax) flip over to another image, record another narration, rinse and repeat. End backstory!
The question itself is geared more towards jQ/JS in general, I believe. When I run the 'record' function again, I get this error in the console:
Uncaught TypeError: Object # has no method 'jStartRecording'
This only appears when I switch to the second image and click "record". The first narration records just fine, and can be played back and whatnot with no issue. I'm thinking I'm receiving the error because I need to re-initialize or reload the jRecorder plugin/method/function since I am trying to call jRecorder again using a different set of options (after switching to another image in the slideshow). Since I'm calling it again, it would appear that something hasn't completely loaded, or there is some sort of conflict between each instance of the plugin's operation.
Bottom line: is there a way to reinitialize a jQuery plugin so it can be run again after dynamically switching parameters?
Perhaps I'm barking up the wrong tree entirely -- I don't know. Any insight from a different set of eyes would be greatly appreciated. Thanks so much!
EDIT: As requested, here's a little bit of code. And here is the plugin source. Hope it helps!
renderPanes() is called each time the widget containing the narration recorder is refreshed with a new image's data. Obviously some things have been edited out for brevity and security.
function renderPanes(type, bucket_id) {
var isRecording = false;
$.getJSON( [endpoint] )
.done(function(json){
[....]
$.jRecorder({
host : [endpoint],
callback_started_recording: function(){callback_started(); },
callback_stopped_recording: function(){callback_stopped(); },
callback_activityLevel: function(level){callback_activityLevel(level); },
callback_activityTime: function(time){callback_activityTime(time); },
callback_finished_sending: function(time){ callback_finished_sending() },
swf_path : '/assets/js/jrecorder/jRecorder.swf',
});
$("#narration_play").click(function(){
var audio = new Audio("[folder path]/"+bucket_id+".wav");
audio.play();
});
$("#narration_record").click(function(){
if (!isRecording) {
$.jRecorder.record(30);
$("#narration_record").html("<i class='icon-stop'></i> Stop");
isRecording = true;
} else {
isRecording = false;
$("#narration_record").html("<i class='icon-comment'></i> Record");
$("#narration_play").removeClass('disabled');
$.jRecorder.stop();
$.jRecorder.sendData();
}
});
});
}
Currently I'm loading data asynchronously via data.js as provided by the Grid app template. The problem exists where groupedItems.js (the "Hub" page) calls _initializeLayout in the ready handler before the Data in the global WinJS namespace is set due to the asynchronous nature of the StorageFile class.
In data.js:
fileNames.forEach(function (val, index, arr) {
var uri = new Windows.Foundation.Uri('ms-appx:///data/' + val + '.geojson');
Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).then(function (file) {
Windows.Storage.FileIO.readTextAsync(file).then(function (contents) {
// ... read, parse, and organize the data ...
// Put the data into the global namespace
WinJS.Namespace.define("Data", {
items: groupedItems,
groups: groupedItems.groups,
getItemReference: getItemReference,
getItemsFromGroup: getItemsFromGroup,
resolveGroupReference: resolveGroupReference,
resolveItemReference: resolveItemReference
});
});
});
}
In groupedItems.js:
// ...
// This function updates the ListView with new layouts
_initializeLayout: function (listView, viewState) {
/// <param name="listView" value="WinJS.UI.ListView.prototype" />
if (viewState === appViewState.snapped) {
listView.itemDataSource = Data.groups.dataSource;
listView.groupDataSource = null;
listView.layout = new ui.ListLayout();
} else {
listView.itemDataSource = Data.items.dataSource;
listView.groupDataSource = Data.groups.dataSource;
listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" });
}
},
// ....
Seeing as I cannot move this code out of this file into the done() function of the Promise in data.js, how do I make the application wait until Data is initialized in the WinJS namespace prior to initializing the layout?
You have two asynchronous operations in progress (loading of the data and loading of the page) and one action (initializing the grid) that needs to happen only after both asynchronous operations are complete (page is loaded, data is available). There are a lot of approaches to solve this depending upon what architectural approach you want to take.
The brute force method is that you create a new function that checks to see if both the document is ready and the data is loaded and, if so, it calls _initializeLayout(). You then call that function in both places (where the doc is loaded and when the data is available) and it will execute only when both conditions are satisfied. It appears that you can tell if the data is loaded by checking for the existence of the global Data item and the its relevant properties.
There are more involved solutions that are architecturally a little cleaner. For example, in your doc ready handler, you can check to see if the data is available yet. If it is, you just initialize the layout. If, not you install a notification so that when the data is available, your callback will get called and you can then initialize the layout. If the data loading code doesn't currently have a notification scheme, then you create one that can be used by any client who wants to be called when the data has been loaded. This has the advantage over the first method in that the data loading code doesn't have to know anything about the grid. The grid does have to know about the data - which makes sense because the grid requires the data.
There are surely ways to use the promise/done system to do this too though I'm not personally familiar enough with it to suggest a good way to do it using that.
I recently ran into a familiar javascript/jQuery timing bug and spent too long debugging it. What I need is a smarter debugging path for this problem.
In specific, my issue was that user inputs were supposed to be causing a Mongo database call and the results were sent, after a little math, to displayed outputs. But the displayed outputs were crazily wrong. However, once I added a FireBug break point the problem went away. At that point I knew I had a timing issue, but not how to solve it.
Here are the relavant pieces of code before the error:
handleDataCallBack : function(transport) {
var response = $.parseJSON(transport);
if(!hasErrors) { this.updatePage(response); }
},
accessDatabase : function(){
var params = { ... };
DAL.lookupDatabaseInfo(this.handleCallBackOutputPanel, this, params);
},
calculateValues: function() {
// some numerical values were updated on the page
}
onDomReady : function() {
// ...
//bind drop-down select change events
$('#someDropDown').change(function() {
me.accessDatabase();
me.calculateValues();
});
}
To fix the problem, all I had to do was move the "calculateValues" method from the onDomReady inside the call back:
handleDataCallBack : function(transport) {
var response = $.parseJSON(transport);
this.calculateValues();
if(!hasErrors) { this.updatePage(response); }
},
The problem was that the database hadn't responded before the calculations were started. Sure, that's easy to spot in retrospect. But what methods can I use to debug asynchronous timing issues in javascript/jQuery in the future? This seems well outside the context of IDE tools. And FireBug didn't help. Are there any tools for tracking down asynchronous web development issues? Or maybe some time-tested methods?
i assume your problem is caused here:
$('#someDropDown').change(function() {
me.accessDatabase();
me.calculateValues();
});
this issue is that your calculations are done just right after the call. seeing that the DB call is async, calculate does not wait for it. however, you can do it using "callbacks". i see you do try to implement it and yes, it is correct. however, i find this more elegant:
calculateValues: function() {
// some numerical values were updated on the page
},
//since this is your general callback hander
//you hand over the return data AND the callbackAfter
handleDataCallBack: function(transport, callbackAfter) {
var response = $.parseJSON(transport);
//you may need to use apply, im lost in scoping here
callbackAfter();
//or
callbackAfter.apply(scope);
if (!hasErrors) {
this.updatePage(response);
}
},
accessDatabase: function(callbackAfter) {
var params = {};
//pass callbackAfter to the function,
//after this is done, pass it to the handler
DAL.lookupDatabaseInfo(this.handleCallBackOutputPanel, this, params, callbackAfter);
},
onDomReady: function() {
$('#someDropDown').change(function() {
me.accessDatabase(function() {
//send over what you want done after.
//we'll call it "callbackAfter" for easy tracing
me.calculateValues();
});
});
}