GWT wrap a table and manipulate contents programmatically - html

I have a well formatted HTML table (the HTML text is generated by a reporting tool) and I want to turn it into a "programmable" table in GWT.
I need to show that content as it is provided, but I also need to inspect the DOM, get the tables, and add ClickHandler's to the rows and cells.
I am able to do something similar with images:
Html html = new HTML(htmlText);
ImageElement domElement = getChildImageByTagAndId(html.getElement(), "img", "blah");
Image image = Image.wrap(domElement);
image.addClickHandler(...);
My question is: What is the correct way to do this with tables?
I could not find a wrap() method for <table>, <tr>, and <td> elements.
Note: this question was asked (and not answered) in the comments in the accepted answer here.

I am unaware of an easy way to wrap an existing Table with the sub tr and td elements.
Given the constraint that you are relying on a third party tool, I recommend trying to build a custom table parser which will parse the existing table, build a FlexTable, and then replace the existing table with the new FlexTable.
As ugly as this is, I am not sure there is a better way...
It may depend on how exactly the table is setup, but you may try playing around with something like the below example to go through the existing table and use the getNodeValue() to build the content of the FlexTable....
Element table = DOM.getElementById("someTableId");
int numTopNodes = table.getChildNodes().getLength();
for(int topNode = 0; topNode < numTopNodes; topNode++){
Node top = table.getChildNodes().getItem(topNode);
System.out.println("Top Node: "+top);
for(int subNode = 0; subNode < top.getChildCount(); subNode ++){
Node sub = top.getChildNodes().getItem(subNode);
System.out.println("Sub Node: "+sub);
for(int rows = 0; rows < sub.getChildCount(); rows ++){
Node row = sub.getChildNodes().getItem(rows);
System.out.println("Row: "+row);
for(int cells = 0; cells < row.getChildCount(); cells++ ){
Node cell = row.getChildNodes().getItem(cells);
System.out.println("CELL: "+cell.getNodeValue()); // use value to build to build a new FlexTable
}
}
}
}

Related

Microsoft edge multi element copy

Does anyone know a fast way to copy multiple elements from an inspect page?
What I mean by that is I have a number of elements with same qualities i.e. same class and I want to copy all of them to my clipboard. Is there a way within inspect tool to do such a "trick" ? :)
Thank you in advance!
There's no specific simple way to do this, you can only using code to extract the elements you want.
For example if you want to get elements with the same class name, you can use the following code:
var outputText = "";
var targets = document.getElementsByClassName('classname');
for( var i = 0; i < targets.length; i++ ) {
outputText += targets[i].outerHTML;
}
console.log(outputText);
Then you can copy the output in the console.

Flash AS3 How to display a random item from an xml list then remove that item from the list so it will not be reused

I'm using Flash AS3 trying to display random questions from an xml list on stage. When the user clicks an option it should move on to another question but the one they got should be removed from the list so it will not come back.
I have the randomize part okay but can't figure out how to remove the question from the list.
Here is the section I have.
function randomizeQuestion():void {
var numOfQuestions:Number = xmlData.item.length();
var shuffledNumbers:Array = new Array(randomAns.length);
var randomPos:Number = 0;
//Randomizes selected question
currentQuestion = int(Math.random() * numOfQuestions);
//Randomizes answer numbers
for (var i:int = 0; i < shuffledNumbers.length; i++)
{
randomPos = int(Math.random() * randomAns.length);
shuffledNumbers[i] = randomAns.splice(randomPos, 1)[0];
}
randomAns = shuffledNumbers;
correctAns = xmlData.item[currentQuestion].children().(hasOwnProperty("#correct"));
}
Try to get index:
correctAnsIndex = xmlData.item[currentQuestion].children().(hasOwnProperty("#correct")).childIndex();
or if more elements, to get first one:
correctAnsIndex = xmlData.item[currentQuestion].children().(hasOwnProperty("#correct"))[0].childIndex();
then use delete where appropriate, like here:
delete xmlData.item[correctAnsIndex];
I would add another array to your code in which you can store all the questions that are eligible. As a question gets asked, remove it from that array. So you'll have one array of allQuestions and another array of eligibleQuestions. allQuestions can just be a comprehensive list. Then, push all the questions you want to go through into the eligibleQuestions array. As the questions get answered, splice them from the array.
It would be good to also share an example of your XML file that you're loading, but let's use the example below.
<data>
<item>
<question></question>
<option1></option1>
<option2></option2>
</item>
<item>
<question></question>
<option1></option1>
<option2></option2>
</item>
</data>
Based on the code you have, I assume your function is being called each time you want to randomize a question.
Now what you'd want to do is create an array that holds the number of <item> tags you have outside of this function so that you can remove values from it whenever you want.
You can create this var numOfQuestions:Array = new Array(); as a global variable and then initialize it using a loop before you enter the randomizeQuestions() function.
for(var i = 0; i < xmlData['item'].length(); i++)
{
numOfQuestions.push(i);
}
Basically this array will serve as a method of calling a specific item and removing it from the program without altering the actual XML file in any way.
Then whenever you wish to remove an element you use
numOfQuestions.splice(numOfQuestions.indexOf(valueToBeRemoved), 1);
This will search the array for the element you wish to remove and then remove it from the array.
Lastly the randomizeQuestion function has to be modified.
currentQuestion = int(Math.random() * numOfQuestions.length); //since numOfQuestions is now an array instead of a Number

Vertical html table without repeating th tags

I'm generating a table using xslt, but for this question I'll keep that side out of it, as it relates more to the actual generated structure of a html table.
What I do is make a vertical table as follows, which suits the layout needed for the data concerned that originated in a spreadsheet. Example is contrived for brevity, actual data fields contain lengthy strings and many more fields.
Title: something or rather bla bla
Description: very long desription
Field1: asdfasdfasdfsdfsd
Field2: asdfasfasdfasdfsdfjasdlfksdjaflk
Title: another title
Description: another description
Field1:
Field2: my previous field was blank but this one is not, anyways
etc.
The only way so far I found to generate such a html table is using repeating tags for every field and every record e.g.:
<tr><th>Title</th><td>something or rather bla bla</td></tr>
<tr><th>Description</th><td>very long desription</td></tr>
...
<tr><th>Title</th><td>another title</td></tr>
<tr><th>Description</th><td>another description</td></tr>
...
Of course this is semantically incorrect but produces correct visual layout. I need it to be semantically correct html, as that's the only sane way of later attaching a filtering javascript facility.
The following correct semantically produces an extremely wide table with a single set of field headers on the left:
<tr><th>Title</th><td>something or rather bla bla</td><td>another title</td></tr>
<tr><th>Description</th><td>very long desription</td><td>another description</td></tr>
...
So to summarise, need a html table (or other html structure) where it's one record under another (visually) with repeating field headers, but the field headers must not be repeated in actual code because that would wreck any record based filtering to be added later on.
Yo. Thanks for updating your question, and including some code. Typically you'd also post what you've tried to correct this issue - but I'm satisfied enough with this post.
Since you want the repeating headers in vertical layout (not something I've seen often, but I can understand the desire), you don't have to modify the HTML formatting, just use a bit more JavaScript to figure it out. I haven't gone through and checked to see if I'm doing things efficiently (I'm probably not, since there are so many loops), but in my testing the following can attach to a vertical table and filter using a couple variables to indicate how many rows there are in each entry.
Firstly, here's the HTML I'm testing this one with. Notice I have a div with the id of filters, and each of my filter inputs has a custom attribute named filter that matches the header of the rows they are supposed to filter:
<div id='filters'>
Title: <input filter='Title'><br>
Desc: <input filter='Description'>
</div>
<table>
<tr><th>Title</th><td>abcd</td></tr>
<tr><th>Description</th><td>efgh</td></tr>
<tr><th>Title</th><td>ijkl</td></tr>
<tr><th>Description</th><td>mnop</td></tr>
<tr><th>Title</th><td>ijkl</td></tr>
<tr><th>Description</th><td>mdep</td></tr>
<tr><th>Title</th><td>ijkl</td></tr>
<tr><th>Description</th><td>mnop</td></tr>
<tr><th>Title</th><td>ijkl</td></tr>
<tr><th>Description</th><td>mnop</td></tr>
</table>
Here are the variables I use at the start:
var filterTable = $('table');
var rowsPerEntry = 2;
var totalEntries = filterTable.find('tbody tr').size() / rowsPerEntry;
var currentEntryNumber = 1;
var currentRowInEntry = 0;
And this little loop will add a class for each entry (based on the rowsPerEntry as seen above) to group the rows together (this way all rows for an entry can be selected together with a class selector in jQuery):
filterTable.find('tbody tr').each(function(){
$(this).addClass('entry' + currentEntryNumber);
currentRowInEntry += 1;
if(currentRowInEntry == rowsPerEntry){
currentRowInEntry = 0;
currentEntryNumber += 1;
}
});
And the magic; on keyup for the filters run a loop through the total number of entries, then a nested loop through the filters to determine if that entry does not match either filter's input. If either field for the entry does not match the corresponding filter value, then we add the entry number to our hide array and move along. Once we've determined which entries should be hidden, we can show all of the entries, and hide the specific ones that should be hidden:
$('#filters input').keyup(function(){
var hide = [];
for(var i = 0; i < totalEntries; i++){
var entryNumber = i + 1;
if($.inArray(entryNumber, hide) == -1){
$('#filters input').each(function(){
var val = $(this).val().toLowerCase();
var fHeader = $(this).attr('filter');
var fRow = $('.entry' + entryNumber + ' th:contains(' + fHeader + ')').closest('tr');
if(fRow.find('td').text().toLowerCase().indexOf(val) == -1){
hide.push(entryNumber);
return false;
}
});
}
}
filterTable.find('tbody tr').show();
$.each(hide, function(k, v){
filterTable.find('.entry' + v).hide();
});
});
It's no masterpiece, but I hope it'll get you started down the right path.
Here's a fiddle too: https://jsfiddle.net/bzjyfejc/

IE8 select population performance issues - solutions needed

I have an application that is having issue when populating selects with over 100 items. This problem only occurs in IE8. I am using angularjs to do the population, but my research shows that this is a general problem with IE8. What solutions have others used to deal with this problem. We have over 40,000 users tied to IE8 for the foreseeable future (Fortune 200 company) so moving to another browser is not an option.
Some thoughts I had.
Create a series of option tags as a one long string in memory and replace the innerHTML of the . But running some people samples this does not appear to solve the issue.
Originally populating the select with a few and then adding the rest as the user scrolls down. I am not sure if this is possible, or how to implement this
I am sure others have run into this issue. Does anyone have some ideas?
Thanks,
Jerry
Another solution that preserves the original <select> is to set the <option> values after adding the options to the <select>.
Theory
Add the <option> elements to a document fragment.
Add the document fragment to the <select>.
Set the value for each <option>.
Practice
In practice we end up with a couple issues we have to work around to get this to work:
IE11 is very slow when setting the value for each individual <option>.
IE8 has selection bugs because it isn't properly doing a re-flow/layout on the <select>.
Result
To handle these what we really do is something like the following:
Add the <option> tags to a document fragment. Make sure to set the values so that step 3 is a no-op in IE11.
Add the document fragment to the <select>.
Set the value for each <option>. In IE8 this will set the values, in IE11 this is a no-op.
In a setTimeout add and remove a dummy <option>. This forces a re-flow.
Code
function setSelectOptions(select, options)
{
select.innerHTML = ''; // Blank the list.
// 1. Add the options to a document fragment.
var docFrag = document.createDocumentFragement();
for (var i = 0; i < options.length; i++)
{
var opt = document.createElement('option');
opt.text = options[i];
docFrag.appendChild(opt);
}
// 2. Add the document fragment to the select.
select.appendChild(docFrag);
// 3. Set the option values for IE8. This is a no-op in IE11.
for (i = 0; i < options.length; i++)
select.options[i].text = options[i];
// 4. Force a re-flow/layout to fix IE8 selection bugs.
window.setTimeout(function()
{
select.add(document.createElement('option'));
select.remove(select.options.length - 1);
}, 0);
}
The best solution seems to be to create the Select and it's options as a text string and add that string as the innerHTML of the containing tag such as a DIV. Below is some code.
<div id="selectHome" ></div>
In JS (from angular controller)
function insertSelect(divForSelect) {
var str = "<select id='myselect'>";
for (var i = 0; i < data.length; i++) {
str += '<option>' + data[i] + '</data>';
}
str += '</select>';
divForSelect.innnerHTML = str;
}
Note that inserting options into a existing Select is very slow (8,000 msecs for 2000 items). But, if the select and the options are inserted as a single string it is very fast (12 msec for 2000 items).

AdvancedDataGrid total sum of branch nodes

Introduction:
I have an AdvancedDataGrid displaying hierarchical data illustrated by the image below:
The branch nodes "Prosjekt" and "Tiltak" display the sum of the leaf nodes below.
Problem: I want the root node "Tavle" to display the total sum of the branch nodes below. When i attempted to do this by adding the same SummaryRow the sum of the root node was not calculcated correctly(Every node's sum was calculated twice).
dg_Teknikktavles = new AutoSizingAdvancedDataGrid();
dg_Teknikktavles.sortExpertMode="true";
dg_Teknikktavles.headerHeight = 50;
dg_Teknikktavles.variableRowHeight = true;
dg_Teknikktavles.addEventListener(ListEvent.ITEM_CLICK,dg_TeknikktavlesItemClicked);
dg_Teknikktavles.editable="false";
dg_Teknikktavles.percentWidth=100;
dg_Teknikktavles.minColumnWidth =0.8;
dg_Teknikktavles.height = 1000;
var sumFieldArray:Array = new Array(context.brukerList.length);
for(var i:int = 0; i < context.brukerList.length; i++)
{
var sumField:SummaryField2 = new SummaryField2();
sumField.dataField = Ressurstavle.ressursKey + i;
sumField.summaryOperation = "SUM";
sumFieldArray[i] = sumField;
}
var summaryRow:SummaryRow = new SummaryRow();
summaryRow.summaryPlacement = "group";
summaryRow.fields = sumFieldArray;
var summaryRow2:SummaryRow = new SummaryRow();
summaryRow2.summaryPlacement = "group";
summaryRow2.fields = sumFieldArray;
var groupField1:GroupingField = new GroupingField();
groupField1.name = "tavle";
//groupField1.summaries = [summaryRow2];
var groupField2:GroupingField = new GroupingField();
groupField2.name = "kategori";
groupField2.summaries = [summaryRow];
var group:Grouping = new Grouping();
group.fields = [groupField1, groupField2];
var groupCol:GroupingCollection2 = new GroupingCollection2();
groupCol.source = ressursTavle;
groupCol.grouping = group;
groupCol.refresh();
Main Question: How do i get my AdvancedDataGrid's (dg_Teknikktavles) root node "Tavle" to correctly display the sum of the two branch nodes below?
Side Question: How do i add a red color to the numbers of the root node's summary row that exceed 5? E.g the column displaying 8 will exceed 5 in the root node's summary row, and should therefore be marked red
Thanks in advance!
This is a general answer, without code examples, but I had to do the same just couple of days ago, so my memory is still fresh :) Here's what I did:
Created a class A to represent an item renderer data, extended it from Proxy (I had field names defined at run time), and let it contain a collection of values as it's data member. Once accessed through flash_proxy::getPropery(fieldName) it would find a corresponding value in the data member containing the values and return it. Special note: implement IUID, just do it, it'll save you couple of days of frustration.
Extended A in B, added a children property containing ArrayCollection of A (don't try to experiment with other collection types, unless you want to find yourself examining tons of framework code, trust me, it's ugly and is impossible to identify as interesting). Let B override flash_proxy::getPropery - depending of your compiler this may, or may not be possible, if not possible - call some function from A.flash_proxy::getPropery() that you can override in B. Let this function query every instance of A, which is a child of B, asking the same thing, as DataGrid itself would, when building item renderers - this way you would get the total.
When creating a data provider. Create an ArrayCollection of B (again, don't try to experiment with other collections--unless you are ready for lots of frustration). Create Hierarchical data that uses this array collection as a source.
Colors - that's what you use item renderers for, just look up any tutorial on using item renderers, that must be pretty basic.
In case someone else has the same problem:
The initial problem that everything was summed twice, was the result of using the same Array of SummaryField2 (sumFieldArray in the code) for both grouping fields(GropingField2 tavle and kategori)
The Solution to the main question: was to create a new array of summaryfields for the root node(in my intial for loop):
//Summary fields for root node
var sumFieldRoot:SummaryField2 = new SummaryField2();
sumFieldRoot.dataField = Ressurstavle.ressursKey + i;
sumFieldRoot.summaryOperation = "SUM";
sumFieldArrayRoot[i] = sumFieldRoot;
Answer to the side question:
This was pretty much as easy as pointed out by wvxyw. Code for this solution below:
private function summary_styleFunction(data:Object, col:AdvancedDataGridColumn):Object
{
var output:Object;
var field:String = col.dataField;
if ( data.children != null )
{
if(data[field] >5){
output = {color:0xFF0000, fontWeight:"bold"}
}
else {
output = {color:0x006633, fontWeight:"bold"}
}
//output = {color:0x081EA6, fontWeight:"bold", fontSize:14}
}
return output;
}