pushing record in between when dropping on Kendo Grid - javascript

I am trying to use this example as shown HERE in JSFIDDLE, the problem i am having is that I would like when dropping to squeeze the new record in between and not swap. I tried adding a for loop in the drop function that increases the destination record if the target is less than that one or decreases it if the target is more than the destination.
The problem is that I am ending up with repeated positions like 4,4 or 5, 5, 5. Are there any examples of Kendo UI that i can use with this functionality?, instead of swaping?.
example:
id text position
1 world 1
2 cup 2
3 Brazil 3
4 2014 4
5 Soccer 5
If i move record 4 to the top i would like to
id text position
4 2014 1
1 World 2
2 Cup 3
3 Brazil 4
5 Soccer 5
I would appreciate if anyone could point me in the right direction.

The way to insert a row when drag/drop is to use the datasource insert (or add) capability. At the high level you are inserting the row into the new location, and removing it from the old location. Kendo's grid will automatically refresh the display - you just need to get the data right. The next challenge is to get the target row number. You've done this by adding a column in the grid, and executing target.get("position")). I've used the datasource.indexOf method for this, and removed the Position column - cleaning up the display. Ref jsfiddle. Following is a small excerpt of the code (thanks to Lars below for improvements!)
grid.table.kendoDropTargetArea({
filter: "td",
group: "gridGroup",
drop: function (e) {
e.draggable.hint.hide();
var target = dataSource.getByUid($(e.draggable.currentTarget).data("uid")),
destElement = $(e.dropTarget).closest("tr"),
dest = dataSource.getByUid(destElement.data("uid")),
destPosition = dataSource.indexOf(dest);
//not on same item
if (target.get("id") !== dest.get("id")) {
dataSource.remove(target);
dataSource.insert(destPosition, target);
}
}
});

Since you're only trying to modify the order of the rows, your original approach should work. Instead of swapping the position, you need to adjust the position of all other rows depending on their relative position to the source row and target row.
Your drop target definition might then look something like this:
grid.table.kendoDropTargetArea({
filter: "td",
group: "gridGroup",
drop: function (e) {
e.draggable.hint.hide();
var target = dataSource.getByUid($(e.draggable.currentTarget).data("uid")),
destElement = $(e.dropTarget).closest("tr"),
dest = dataSource.getByUid(destElement.data("uid")),
sourcePosition,
targetPosition,
position,
item;
//not on same item
if (target.get("id") !== dest.get("id")) {
// set new position for dropped item
sourcePosition = target.get("position");
targetPosition = dest.get("position");
if (targetPosition > sourcePosition) {
targetPosition -= 1;
}
target.set("position", targetPosition);
// update positions for all other items
for (var i = 0, max = dataSource.total(); i < max; i++) {
item = dataSource.at(i);
if (item.uid === target.uid) continue;
position = item.position;
// items which had a higher position than the source item need to move down by one
if (position >= sourcePosition) {
position -= 1;
}
// items which had a higher position than the target position need to move up by one
if (position >= targetPosition) {
position += 1;
}
item.set("position", position);
}
dataSource.sort({
field: "position",
dir: "asc"
});
}
}
});
(demo)

Related

Adding and deleting content between two points on a document

I currently have a document, with help from StackOverflow users already, that randomly generates questions, adds it to the end of a document, and then has the ability to delete all the questions posted. This is based on deleting everything under a horizontal rule.
Link to GDrive containing example document & code: LINK TO GDRIVE
You can also see what it currently does here: https://imgur.com/QVrOZKu
However, I now want to only want to add content after a certain point in the document, as well as only delete content between two certain points. You can see the two horizontal rules in an image below in which I want to add/delete
content.
The first horizontal rule in the picture is the third horizontal rule in the document.
Has anyone got any ideas how I can delete and add content between those two points? I've tried using child index's but failed miserably.
This is similar to Deleting all content down from the second horizontal line in a document so I adapt the solutions. First function deletes the paragraphs between the 3rd and 4th line. It counts horizontal lines as we loop through paragraphs. When the count reaches 3, start deleting subsequent paragraphs. When it exceeds 3, stop the loop.
function deleteFrom3to4() {
var body = DocumentApp.getActiveDocument().getBody();
body.appendParagraph('');
var para = body.getParagraphs();
var ruleCount = 0;
for (var i = 0; i < para.length - 1; i++) {
if (para[i].findElement(DocumentApp.ElementType.HORIZONTAL_RULE)) {
ruleCount++;
}
else if (ruleCount == 3) {
body.removeChild(para[i]);
}
if (ruleCount > 3) {
break;
}
}
}
And this one inserts a paragraph after the 3rd horizontal line. Again, it loops until the 3rd line is found; inserts a paragraph after it (expressed by body.getChildIndex(para[i]) + 1 child index) and stops.
function insertAfter3() {
var body = DocumentApp.getActiveDocument().getBody();
body.appendParagraph('');
var para = body.getParagraphs();
var ruleCount = 0;
for (var i = 0; i < para.length - 1; i++) {
if (para[i].findElement(DocumentApp.ElementType.HORIZONTAL_RULE)) {
ruleCount++;
}
if (ruleCount == 3) {
body.insertParagraph(body.getChildIndex(para[i]) + 1, "Here is a new paragraph");
break;
}
}
}

Autoplay two Bootstrap Popover at a time instead of one

http://jsfiddle.net/umpe9a9j/
Currently it auto-plays 1 Popover at a time in a loop. However, I would like to have it auto played 2 Popover at a time. Of course, in a loop.
More Popovers will be added. How do I get this going?
HTML
<div class="container">
Hover Left |
Hover Right |
Click Me |
Click Me
</div>
JS
$(document).ready(function () {
var time = 1000;
var len = $('.myclass').length;
var count = 0;
var fun = setInterval(function () {
count++;
if (count > len) {
$('.p' + (count - 1)).popover('hide');
count = 1;
//clearInterval(fun);
}
$('.p' + count).popover('show');
if (count > 1) {
var pre = count - 1;
$('.p' + pre).popover('hide');
}
}, time);
});
I got a working example of what you are looking for. You can specify the number of popOver items to simultaneously show, and it will continue down the chain (and loop back if necessary) for each interval. The first thing I changed are the popOver class names. They now go from p0-p1-p2-p3, making it consistent with a 0 index array. This makes for less -1's in the code. So Html looks like:
<div class="container">
Hover Left |
Hover Right |
Click Me |
Click Me
</div>
Now the js function is straight forward but a might be a little confusing to look at. You first important variable is numConcrPopOver, this defines the number of simultaneous popOver items you want shown. Then in the interval function the code fills in 2 arrays of indexes; one for the number of popOver items to show and another for the items to hide that were previously shown. Using a for loop and the numConcrPopOver defined, it creates these lists. Take note of the modulo operator used multiple times in this section, its to ensure that elements to show and hide remain within the length of the total number of popOver items, looping back to the beginning when its over this length.
After these 2 arrays have been populated, first we need to remove any items in the popToHide array that also exist in the popsToShow array. This is done for scenarios where the number of simultaneous items to show is greater than half the total items. In this case because of the way the popsToHide array is first filled, it will contain indices that also belong in the popsToShow array. So we just filter through the popsToHide array and remove the duplicates to only hide popOver items that were previously shown but not also being currently shown.
As an example of the sequence of popOver items; if you have 4 total popOver items, and you want to show 3 at a time. The expected order of shown popOvers per interval is:
0-1-2 -> 1-2-3 -> 2-3-0 -> 3-0-1 ...
The javascript for this is:
$(document).ready(function(){
var time = 1000;
var popOverLength = $('.myclass').length;
var popOverIdx = 0;
var numConcrPopOver = 2;
var fun = setInterval(function(){
var popsToShow = []; //Array that will hold index of popOvver items to show
var popsToHide = []; //Array that will hold index of popOvver items to hide
//Loop for the number of simultanious popOver you want to show
for(var popNum=0; popNum<numConcrPopOver; popNum++){
var currPopIdx = popOverIdx+popNum; //Index o fthe current popOver to show
popsToShow.push(currPopIdx%popOverLength); //Alwyas mod index to keep within lenght of popOver items
var hidePopIdx = popOverIdx-1-popNum; //The index of the previous popOver item to hide
if(hidePopIdx < 0){
hidePopIdx = popOverLength-1-popNum
}
popsToHide.push(hidePopIdx%popOverLength);
}
popOverIdx+=numConcrPopOver;
popOverIdx%=popOverLength;
//Remove from popToHide array any items in the popToShow array.
//This is done for the scenarios where the numebr of popovers to
//Show in greater than half the total number of popovers,
//otherwise will hide immediatly after showing
popsToHide = popsToHide.filter(function(itm) {return popsToShow.indexOf(itm) < 0;});
popsToShow.forEach(function(itm){ //Iteratre of popOver items to show them
$('.p'+itm).popover('show');
});
popsToHide.forEach(function(itm){ //Iteratre of popOver items to hide them
$('.p'+itm).popover('hide');
});
}, time);
});
You can test out diffrent numbers of simultaneous popOvers by altering the numConcrPopOver variable. I've updated yous jsfiddle to include the new code: here

Javascript - Dynamic Expand/Collapse All

I have a jQuery Tree Report that I am trying to create 'expand/collapse all' buttons for.
The following two pieces of code are fired when the corresponding buttons are pressed and work great:
for (i = 1; i < 100; i++) {
var el = $('#dtt_2597807651112537_table tbody tr')[i - 1];
// store current level
var level = Number($(el).attr('dtt_level'));
// change icon
$(el).find('span.dtt_icon').removeClass('dtt_collapsed_span');
$(el).find('span.dtt_icon').addClass('dtt_expanded_span');
while ($($(el).next()).attr('dtt_level') != null) {
var el = $(el).next();
if ($(el).attr('dtt_level') == (level + 1)) {
// change display
el.removeClass('dtt_collapsed_tr');
el.addClass('dtt_expanded_tr');
} else if ($(el).attr('dtt_level') == level) {
break;
}
}
}
for (i = 1; i < 100; i++) {
// get related table row
var el = $('#dtt_2597807651112537_table tbody tr')[i - 1];
// store current level
var level = Number($(el).attr('dtt_level'));
// change icon
$(el).find('span.dtt_icon').addClass('dtt_collapsed_span');
$(el).find('span.dtt_icon').removeClass('dtt_expanded_span');
while ($($(el).next()).attr('dtt_level') != null) {
var el = $(el).next();
if ($(el).attr('dtt_level') > level) {
// change display
el.addClass('dtt_collapsed_tr');
el.removeClass('dtt_expanded_tr');
// change icon
$(el).find('span.dtt_icon').addClass('dtt_collapsed_span');
$(el).find('span.dtt_icon').removeClass('dtt_expanded_span');
} else if ($(el).attr('dtt_level') == level) {
break;
}
}
};
However, I was wondering if anyone had a nice way to:
1) Get the number of rows that need to be looped through - I just put 100 as a large number to prove my code worked and I don't want to just increase this to an even larger number.
2) Get the class name from the page source - The large number in "dtt_2597807651112537_table" is a report ID generated by the application. This is static for now but I want to eliminate any problems if it changes.
Thanks.
This is all wrong. Well, it's working against how jQuery works, in any case.
jQuery's credo is:
Select elements
Do stuff to them
Drop your loops. You don't need them.
For example. To toggle the icon on all span.dtt_icon in your document, do
var collapsed = true;
$("#dtt_2597807651112537_table span.dtt_icon") // select elements
.toggleClass('dtt_collapsed_span', collapsed) // do stuff to them
.toggleClass('dtt_expanded_span', !collapsed);
or, as a function that can both collapse and expand:
function toggleTree(tree, collapsed) {
$(tree).find("span.dtt_icon")
.toggleClass('dtt_collapsed_span', collapsed)
.toggleClass('dtt_expanded_span', !collapsed);
}
To collapse only the currently expanded ones...
$("#dtt_2597807651112537_table span.dtt_icon.dtt_expanded_span")
.toggleClass('dtt_collapsed_span', true)
.toggleClass('dtt_expanded_span', false);
and so on.
You can boil down your entire code into a few lines that way, and you don't need to write a single loop: Use smart element selection (via jQuery selectors and any of jQuerys find, filter and traversal functions) to single out the elements you want to manipulate and then manipulate them all at once in a single step.
To your second question. There are many ways, pick one:
use known page structure to determine the right table (e.g. $("div.main > table:first") or something to that effect)
use known table contents to determine the right table (e.g. $("table:has(span.dtt_icon)"))
use the table's other classes ($("table.treeReport") maybe?) or for example the table's ID with and a "starts-with" selector ($("table[id^=dtt_]")).
Again it's all about selecting your elements smartly. A dive into the jQuery API documentation, in this case the part about selectors, is recommended.

Google Chart - setSelection does not scroll to selected row in Table visualisation

I am using Google Chart Tools to display a Table visulisation on my webpage along side a Google Map. When the user clicks a map location, the callback automatically selects the corresponding location from a list of locations in the table.
This works fine, but the table does not automatically scroll the table so the selected row is visible (there are a lot of points so only a limited amount are shown with a scrollbar on the right hand side of the Table vis.)
I can't see any way of setting the current 'position' of the viewport to the Table. Is there a way to do this?
Thanks!
Code snippet below:
arrBuoyLocs = new google.visualization.DataTable();
vTable = new google.visualization.Table(document.getElementById('table_div'));
// do initialisation, etc...
.
.
.
function updateSelectedTableItem(buoyid) {
console.log('Searching for %s', buoyid);
idx = -1;
nRows = arrBuoyLocs.getNumberOfRows();
for(iRow = 0; iRow < nRows; iRow++) {
if(arrBuoyLocs.getValue(iRow, 0) == buoyid) {
console.log('Got match for %s at idx %d', buoyid, iRow);
idx = iRow;
break;
}
}
if(idx >= 0) {
vTable.setSelection([{row: idx, column: null}]);
// This highlights the row but does not show it if the row is
// scrolled off the screen. How do I scroll the Table to show
// the selected row?
}
}
I have no idea how to do it with google chart api, but heres a thought that might help:
if you can find the screen height, the table row height and calculate how many table rows can fit into your screen, then you can calculate how much the scrollbar needs to scroll to show your selected table row.
so if you have a screen height that can have 20 rows shown, and you selected row 25, then you scroll the scrollbar 5 * rowHeight + margin. this can probably be done in javascript or in an ajax file.
Basically you already have all the data you need to do it, just need to findout how to scroll the scrollbar programatically in javascript.
I found this, which works.
https://groups.google.com/forum/#!searchin/google-visualization-api/table$20setSelection/google-visualization-api/8_atzTNPmL4/etL7VZWjdVQJ
Edit:
From which, this will scroll to the selected column of table in 'table_div'.
I found this sufficient, with some fine tuning to ensure the row I want is clearly in view.
function on_row_select() {
// get the container div for the overflowed table
var container = $('#table_div').find('.google-visualization-table-table:eq(0)').parent();
// get the container div for the fixed header
var header = $('#table_div').find('.google-visualization-table-table:eq(1)').parent();
// get the selected row
var row = $('.google-visualization-table-tr-sel');
// set the scroll position of the overflowed div based on the offset position of the row and the height of the fixed header
$(container).prop('scrollTop', $(row).prop('offsetTop') - $(header).height());
}
Something like this one. This answer a) no need jQuery and b) do not touch scroller when the selected row already on screen.
scrollOnToSelectPosition=function(element) {
var tableElement=element.querySelectorAll("table.google-visualization-table-table")[0],
selection=tableElement.querySelectorAll("tr.google-visualization-table-tr-sel");
if (selection.length) {
var parentDiv=tableElement.parentElement,
tableHead=tableElement.querySelectorAll("thead")[0],
offset=selection[0].offsetTop-tableHead.clientHeight,
viewHeight=parentDiv.clientHeight-tableHead.clientHeight;
if (offset>parentDiv.scrollTop && offset<parentDiv.scrollTop+viewHeight) {
if (offset+selection[0].clientHeight>parentDiv.scrollTop+viewHeight) {
parentDiv.scrollTop=offset+selection[0].clientHeight-viewHeight;
}
}
else {
parentDiv.scrollTop=offset;
}
}
},

if there are more than x elements, divide it and append what exceed in another div

$(document).ready(function(){
var unique_price = $(".price_value").get()
var number_price = unique_price.length;
if (number_price >=7 ){
$(".price_list").each(function(index) {
$(".price_value_left").append($(".price_value:eq(0)"),$(".price_value:eq(1)"),$(".price_value:eq(2)"),$(".price_value:eq(3)"),$(".price_value:eq(4)"));
$(".price_value_right").append($(".price_value:eq(5)"),$(".price_value:eq(6)"),$(".price_value:eq(7)"),$(".price_value:eq(8)"),$(".price_value:eq(9)"));
});
} else if (number_price >9){
$(".price_list").each(function(index) {
$(".price_value_left").append($(".price_value:eq(0)"),$(".price_value:eq(1)"),$(".price_value:eq(2)"),$(".price_value:eq(3)"),$(".price_value:eq(4)"),$(".price_value:eq(5)"),$(".price_value:eq(6)"));
$(".price_value_right").append($(".price_value:eq(7)"),$(".price_value:eq(8)"),$(".price_value:eq(9)"),$(".price_value:eq(10)"),$(".price_value:eq(11)"),$(".price_value:eq(12)"));
});
}
else {
$(".price_value_left").append($(".price_value:eq(0)"),$(".price_value:eq(1)"),$(".price_value:eq(2)"),$(".price_value:eq(3)"),$(".price_value:eq(4)"),$(".price_value:eq(5)"),$(".price_value:eq(6)"));
}
});
I have some menu where there are some items ('li') in it.
I made this code. If there are equal or more than 7 elements in the div, I get the others and put in another. Floats with an enlarge function in the should resolve but i could not do this because there are vertical order that I need maintain.
I created the 'more than 9 elements' so they stay more equilibrated - a style thing.
But I think this code is very ugly and not dynamically acceptable.
Is there a better way to resolve this and maintain the equilibrium design?
jQuery(function($){
var unique_price = $('.price_value');
if( unique_price.length > 6 ) {
var half = ceil(unique_price.length / 2); // In case we have an odd number, we want more elements in the left "column", so we round to next higher number.
$('.price_list .price_value_left').append($('.price_value:lt(' + (half + 1) + ')');
$('.price_list .price_value_right').append($('.price_value:gt(' + half + ')');
}
else {
$('.price_list .price_value_left').append(unique_price);
}
});
What you basically want is the greater than (:gt()) and lower than (:lt()) selectors. Unfortunately, there is no :lte selector, that's why I put that (half + 1) there. I guess there is no need to explain what they do.
I'm not sure about the balance but you can use split selected elements using .slice() [docs]:
$('#left').append($elements.slice(0,7));
$('#right').append($elements.slice(7));

Categories