Vis.js: Stay in addEdgeMode / Event after edge adding - vis.js

I would like to stay in addEdgeMode in vis.js after adding an edge. Is there a way to achieve this?
My idea was to enable addEdgeMode again after adding an edge.
Is there an event that is triggered after adding an edge?
I know that there is the addEdge option in manipulation. However, this is triggered before the insertion.

you can enable the addEdge again right after the previous adding,
like this:
manipulation: {
enabled: false,
addNode: function (data, callback) {
// filling in the popup DOM elements
console.log('add', data);
},
editNode: function (data, callback) {
// filling in the popup DOM elements
console.log('edit', data);
},
addEdge: function (data, callback) {
console.log('add edge', data);
if (data.from == data.to) {
var r = confirm("Do you want to connect the node to itself?");
if (r === true) {
callback(data);
}
}
else {
callback(data);
}
// after each adding you will be back to addEdge mode
network.addEdgeMode();
}
see the last row in this code example.
network.addEdgeMode();
this will enable the addEdge mode right after the callback fired.
see this example in plunker

Related

Update and Remove a Single event of the calendar

I need to edit/remove a single event of the callendar, but with my current code, the update/remove afects all events inside the calendar.
Update Attempt:
$('#btn').click(function(){
var evento = $('#calendario').fullCalendar('clientEvents', function(event){
return event.className == 8;
});
evento[0].backgroundColor = "red";
$('#calendario').fullCalendar('updateEvent', evento[0]);
});
It changes the background color of all events. Also, it changes the events title, all to the same (The one with that ClassName).
Remove Attempt:
$('#btn').click(function(){
$('#calendario').fullCalendar('removeEvents', function(event){
return event.className = 8;
});
});
Also, this method removes ALL events.
How may I target a single event ?
UPDATE
Just found the problem:
Even if I target only that specific event with that specific className, this method updateEvent, will update ALL EVENTS THAT SHARE THE SAME ID. Why?
I don't think it's supposed to happen.
You are right in that you should be able to target events using a filter.
When you attempt to remove the event you are using the assignment operator rather than the equals operator. By changing that, your filter should then return true for removing the event with that class.
The following code worked for adding a class to one event and then removing the event using the class.
eventRender: function (event, element) {
if (event.id === 628) {
event.className = "Test";
}
},
eventClick: function (event, jsEvent, view) {
$('#calendar').fullCalendar('removeEvents', function (event) {
return event.className === "Test";
});
}

Material Design Lite - Programatically Open and Close Toast

I would like to open and close MDL toast rather than use the timeout property as indicated in the MDL usage guide. The reason is that I want the toast to remain while geolocation is occuring, which sometimes takes 10+ seconds and other times happens in 1 second.
Any idea how this could be done?
A q&d solution i found, invoke cleanup_ method on the sb object.
With this solution i can show the sb, click action handler to hide it, then re trigger the action to show it without any problem.
var snackbar = form.querySelector("[class*='snackbar']");
if (snackbar) {
var data = {
message: 'Wrong username or password',
timeout: 20000,
actionHandler: function(ev){
// snackbar.classList.remove("mdl-snackbar--active")
snackbar.MaterialSnackbar.cleanup_()
},
actionText: 'Ok'
};
snackbar.MaterialSnackbar.showSnackbar(data);
}
As cleanup_ is not part of the public api, i guess it worth to enclose this with some small checks to avoid a disaster.
snackbar.MaterialSnackbar.cleanup_
&& snackbar.MaterialSnackbar.cleanup_()
!snackbar.MaterialSnackbar.cleanup_
&& snackbar.classList.remove("mdl-snackbar--active")
Got it working as so: I basically set a 30 second timeout on the toast assuming my geolocation and georesults (GeoFire) will take no more than 30 seconds.
I get the length of the returned array of map markers and multiply that by the javascript timeout events. I finally remove mdl-snackbar--active which hides the toast. So, basically - it works.
UPDATED
The above actually had a major problem in that additional toasts would not display until that long timeout completed. I could not figure out how to apply the clearTimeout() method to fix it so I found a solution that works - trigger the toast up and down by just toggling the mdl-snackbar--active class - no timer setting necessary.
So to call toast as normal using this code, simply tools.toast('hello world',error,3000). To programatically open and close toast call tools.toastUp('hey') and tools.toastDown(), respectively. So, you might call tools.toastDown after a promise resolves or something...
var config = (function() {
return {
timeout: 50, //in milliseconds
radius: 96, //in kilometers
};
})();
var tools = (function() {
return {
toast: function(msg,obj,timeout){
var snackbarContainer = document.querySelector('#toast'); //toast div
if(!obj){obj = ''}
if(!timeout){timeout = 2750}
data = {
message: msg + obj,
timeout: timeout
};
snackbarContainer.MaterialSnackbar.showSnackbar(data);
},
toastUp: function(msg){
var toast = document.querySelector('#toast');
var snackbarText = document.querySelector('.mdl-snackbar__text');
snackbarText.innerHTML = msg;
toast.classList.add("mdl-snackbar--active");
},
toastDown: function(count) {
setTimeout(function () {
var toast = document.getElementById("toast");
toast.classList.remove("mdl-snackbar--active");
}, config.timeout * count);
},
};
})();
In case you want to fire tools.toastDown after a timeout loop, you can do:
function drop(filteredMeetings) {
tools.clearMarkers(true);
for (var i = 0; i < filteredMeetings.length; i++) {
//drop toast once markers all dropped
if(i === filteredMeetings.length - 1) {
tools.toastDown(i);
}
tools.addMarkerWithTimeout(filteredMeetings[i], i * config.timeout);
}
}

Backbone Collection.fetch() returns first item null

I'm using the following code in my view to fetch my collection from the server:
initialize: function () {
_this = this;
this.collection.fetch({
success : function(collection, response) {
_.each(response, function(i){
var todo = new TodosModel({
id: i.id,
content: i.content,
completed: i.completed
});
// Add to collection
_this.collection.add(todo);
// Render
_this.render(todo);
});
},
error : function(collection, response) {
console.log('ERROR GETTING COLLECTION!');
}
});
},
Which seems to work - here's the output from my server:
{
"0": {
"id": 1,
"content": "one",
"completed": false
},
"3": {
"id": 4,
"content": "two",
"completed": true
},
"4": {
"id": 5,
"content": "tester",
"completed": false
}
}
Except for the fact that if I log out my collection there is a null entry in the first position:
Which then causes issues as if I add an item it takes the ID of the last element. I'm new to backbone and am hoping I'm just missing something simple.
Here's my crack at a quick run through of your code. I haven't tested anything so there might be typos. I'm still not sure where the stray empty model is coming from but if you restructure your application as outlined below, I suspect the problem will go away.
The model and collection look okay so let us have a look at your view.
el: $('#todos'),
listBlock: $('#todos-list'),
newTodoField: $('#add input'),
//...
template: $('#todo-template').html(),
//...
events: { /* ... */ },
These should be okay but you need to ensure that all those elements are in the DOM when your view "class" is loaded. Usually you'd compile the template once:
template: _.template($('#todo-template').html()),
and then just use this.template as a function to get your HTML. I'll assume that template is a compiled template function below.
initialize: function () {
_this = this;
You have an accidental global variable here, this can cause interesting bugs. You want to say var _this = this;.
this.el = $(this.el);
Backbone already gives you a jQuery'd version of el in $el so you don't need to do this, just use this.$el.
this.collection.fetch({
success : function(collection, response) {
_.each(response, function(i) {
var todo = new TodosModel({ /* ... */ });
// Add to collection
_this.collection.add(todo);
// Render
_this.render(todo);
});
},
//...
The collection's fetch will add the models to the collection before the success handler is called so you don't have to create new models or add anything to the collection. Generally the render method renders the whole thing rather than rendering just one piece and you bind the view's render to the collection's "reset" event; the fetch call will trigger a "reset" event when it has fetched so the usual pattern looks like this:
initialize: function() {
// So we don't have to worry about the context. Do this before you
// use `render` or you'll have reference problems.
_.bindAll(this, 'render');
// Trigger a call to render when the collection has some stuff.
this.collection.on('reset', this.render);
// And go get the stuff we want. You can put your `error` callback in
// here if you want it, wanting it is a good idea.
this.collection.fetch();
}
Now for render:
render: function (todo) {
var templ = _.template(this.template);
this.listBlock.append(templ({
id: todo.get('id'),
content: todo.get('content'),
completed: todo.get('completed')
}));
// Mark completed
if(todo.get('completed')) {
this.listBlock.children('li[data-id="'+todo.get('id')+'"]')
.addClass('todo-completed');
}
}
Normally this would be split into two pieces:
render to render the whole collection.
Another method, say renderOne, to render a single model. This also allows you to bind renderOne to the collection's "add" event.
So something like this would be typical:
render: function() {
// Clear it out so that we can start with a clean slate. This may or
// may not be what you want depending on the structure of your HTML.
// You might want `this.listBlock.empty()` instead.
this.$el.empty();
// Punt to `renderOne` for each item. You can use the second argument
// to get the right `this` or add `renderOne` to the `_.bindAll` list
// up in `initialize`.
this.collection.each(this.renderOne, this);
},
renderOne: function(todo) {
this.listBlock.append(
this.template({
todo: todo.toJSON()
})
)
// Mark completed
if(todo.get('completed')) {
this.listBlock.find('li[data-id="' + todo.id + '"]')
.addClass('todo-completed');
}
}
Notice the use of toJSON to supply data to the template. Backbone models and collections have a toJSON method to give you a simplified version of the data so you might as well use it. The model's id is available as an attribute so you don't have to use get to get it. You could (and probably should) push the todo-completed logic into the template, just a little
<% if(completed) { %>class="completed"<% } %>
in the right place should do the trick.
addTodo: function (e) {
//...
var todo = new TodosModel({
id: todoID,
content: todoContent,
completed: todoCompleted
});
this.render(todo);
todo.save();
_this.collection.add(todo);
You could bind renderOne to the collection's "add" event to take care of rendering the new model. Then use the save callbacks to finish it off:
var _this = this;
var todo = new TodosModel({ /* ... */ });
todo.save({}, {
wait: true,
success: function(model, response) {
// Let the events deal with rendering...
_this.collection.add(model);
}
});
Again, an error callback on the save might be nice.
completeTodo: function (e) {
//...
todo.save({
completed: todoCompleted
});
}
The save call here will trigger a 'change:completed' event so you could bind to that to adjust the HTML.
removeTodo: function (e) {
//...
}
The destroy call will trigger a "destroy" event on the model and on the collection:
Any event that is triggered on a model in a collection will also
be triggered on the collection directly, for convenience. This
allows you to listen for changes to specific attributes in any model
in a collection, [...]
So you could listen for "destroy" events on the collection and use those to remove the TODO from the display. And destroying the model should remove it from the collection without your intervention.
printColl: function () {
this.collection.each(function (todo) {
console.log('ID: '+todo.get('id')+' | CONTENT: '+todo.get('content')+' | COMPLETED: '+todo.get('completed'));
});
}
You could just console.log(this.collection.toJSON()) instead,
you'd have to click around a little to open up the stuff in the
console but you wouldn't miss anything that way.
All the event binding for the collection would take place in your
view's initialize method. If you're going to remove the view then
you'd want to override the remove to unbind from the collection
to prevent memory leaks:
remove: function() {
// Call this.collection.off(...) to undo all the bindings from
// `initialize`.
//...
// Then do what the default `remove` does.
this.$el.remove()
}
You could also use a separate view for each TODO item but that might be overkill for something simple.

jquery disable a button for a specific time

i want to disable a button for a specific time. how can i do that?
Since this is likely to be a task you might like to repeat, I think the best way to do this would be to extend jQuery like so:
$.fn.timedDisable = function(time) {
if (time == null) { time = 5000; }
return $(this).each(function() {
$(this).attr('disabled', 'disabled');
var disabledElem = $(this);
setTimeout(function() {
disabledElem.removeAttr('disabled');
}, time);
});
};
This will allow you to call a function on a set of matched elements which will temporarily disable them. As it is written, you can simply call the function, and the selected elements will be disabled for 5 seconds. You would do that like so:
$('#some-button').timedDisable();
You can adjust the default time setting by changing the 5000 in the following line:
if (time == null) { time = 5000; }
You can optionally pass in a time value in milliseconds to control how long the elements will be disabled for. For example:
$('#some-button').timedDisable(1000);
Here's a working demo: http://jsfiddle.net/fG2ES/
Disable the button and then use setTimeout to run a function that enables the button after a few seconds.
$('#some-button').attr("disabled", "disabled");
setTimeout('enableButton()', 5000);
function enableButton(){
$('#some-button').removeAttr('disabled');
}
Try this.
(function(){
$('button').on('click',function(){
var $this=$(this);
$this
.attr('disabled','disabled');
setTimeout(function() {
$this.removeAttr('disabled');
}, 3000);
});
})();
You can find a working example here http://jsfiddle.net/informativejavascript/AMqb5/
Might not be the most elegant solution, but I thought I'd play with jQuery queues on this one...
​$.fn.disableFor = function (time) {
var el = this, qname = 'disqueue';
el.queue(qname, function () {
el.attr('disabled', 'disabled');
setTimeout( function () {
el.dequeue(qname);
}, time || 3000);
})
.queue(qname, function () {
el.removeAttr('disabled');
})
.dequeue(qname);
};
$('#btn').click( function () {
​$(this).disableFor(2000);​​​​
});
​
This is where I worked it out... http://jsfiddle.net/T9QJM/
And, for reference, How do I chain or queue custom functions using JQuery?

jQuery: wait to perform a task until an animation finished

I have a div which is placed in any pages. When you click on this div, it will be closed by using jquery checking on its css class:
$('.content-box-header').click(function
() {
$(this).parent().children('.content-box-content').slideFadeToggle(200);
}
In several pages, I need to set that div with a specific ID in order to perform some tasks after that div closed. For example:
$('#divleft').live('click', function
(e) { runTask(); }
The above sample is trigger on that div with the specific ID = divleft.
The problem is that, I would like to check something ONLY after the div is really closed, but in my current situation, runTask() is performed before the div is closed.
SO my question is that how could the method runTask(); is delayed after the div is really closed?
Thanks in advance!!!!
I think what you are looking for is .queue(). See the documentation here: http://api.jquery.com/queue/
You can call this on a set of matched elements to get some information about the remaining effects to be run. So in your case you could do something like this:
$('#divleft').live('click', function (e) {
runTaskAfterAnimation()
});
function runTaskAfterAnimation() {
if ($('.content-box-content').queue('fx').length == 0) {
runTask();
} else {
setTimeout(runTaskAfterAnimation, 10);
}
}
View a demonstration here: http://jsfiddle.net/LeHHj/2/
This time it definitely works ;)
In your case, just use
$('.content-box-header').click(function () { $(this).parent().children('.content-box-content').slideFadeToggle(200, function() { runTask(); }); }
You can store the function on the div using jQuery's data() method.
This lets you set an 'afterClick' function on your element:
$('.content-box-header').click(function () {
var $this = $(this);
$this.parent().children('.content-box-content').slideUp(200, function () {
var after = $this.data('afterClick');
if (after) after();
});
});
$('#divleft').data('afterClick', function () { runTask(); });
You need to check if the item you are wanting to runTask() on is :animated and if so 'register' a callback (via .data()) for when it's done
.live('click', doRunTask);
doRuntask = function() {
if ($(this).is(':animated'))
$(this).data('afterAnimation', runTask);
else
runTask();
});
$('.content-box-header').click(function () {
$(this).parent().children('.content-box-content').slideFadeToggle(200, function() {
var cb = $(this).data('afterAnimation');
cb && cb();
});
}

Resources