Tablesorter plugin, json format issue - json

[{"Id":"8082","Date":"2017-12-09 23:40:39"},{"Id":"7825","Date":"2017-12-09 23:40:17"},{"Id":"3138","Date":"2017-12-09 21:58:03"},{"Id":"4805","Date":"2017-12-09 21:25:01"},{"Id":"2570","Date":"2017-12-09 01:08:15"},{"Id":"8733","Date":"2017-12-08 23:53:24"},{"Id":"8733","Date":"2017-12-08 23:53:18"},{"Id":"8733","Date":"2017-12-08 23:53:13"},{"Id":"8733","Date":"2017-12-08 23:53:07"},{"Id":"8733","Date":"2017-12-08 23:53:02"},{"Id":"8733","Date":"2017-12-08 23:52:56"},{"Id":"7825","Date":"2017-12-08 23:40:01"},{"Id":"8733","Date":"2017-12-07 23:53:00"},{"Id":"8733","Date":"2017-12-07 23:52:54"},{"Id":"8733","Date":"2017-12-07 23:52:48"},{"Id":"8733","Date":"2017-12-07 23:52:42"},{"Id":"8733","Date":"2017-12-07 23:52:36"},{"Id":"7832","Date":"2017-12-07 23:43:27"},{"Id":"8082","Date":"2017-12-07 23:40:54"},{"Id":"4805","Date":"2017-12-07 21:24:53"},{"Id":"8733","Date":"2017-12-06 23:52:35"},{"Id":"8733","Date":"2017-12-06 23:52:30"},{"Id":"7832","Date":"2017-12-06 23:43:05"},{"Id":"8082","Date":"2017-12-06 23:40:35"},{"Id":"7825","Date":"2017-12-06 23:40:13"},{"Id":"3841","Date":"2017-12-06 22:56:18"},{"Id":"3765","Date":"2017-12-06 22:38:20"},{"Id":"4805","Date":"2017-12-06 21:24:51"},{"Id":"2570","Date":"2017-12-06 01:08:16"},{"Id":"1397","Date":"2017-12-06 00:09:34"},{"Id":"8733","Date":"2017-12-05 23:52:20"},{"Id":"8733","Date":"2017-12-05 23:52:15"},{"Id":"8733","Date":"2017-12-05 23:52:08"},{"Id":"7832","Date":"2017-12-05 23:42:36"},{"Id":"7825","Date":"2017-12-05 23:39:42"},{"Id":"3138","Date":"2017-12-05 21:56:47"},{"Id":"4805","Date":"2017-12-05 21:24:41"},{"Id":"8291","Date":"2017-12-05 00:11:44"},{"Id":"8291","Date":"2017-12-05 00:11:39"},{"Id":"8291","Date":"2017-12-05 00:11:33"},{"Id":"8291","Date":"2017-12-05 00:11:28"},{"Id":"8291","Date":"2017-12-05 00:11:22"},{"Id":"8291","Date":"2017-12-05 00:11:17"},{"Id":"8291","Date":"2017-12-05 00:11:11"},{"Id":"8291","Date":"2017-12-05 00:11:05"},{"Id":"8291","Date":"2017-12-05 00:11:00"},{"Id":"8291","Date":"2017-12-05 00:10:55"},{"Id":"8291","Date":"2017-12-05 00:10:48"},{"Id":"8291","Date":"2017-12-05 00:10:43"},{"Id":"8291","Date":"2017-12-05 00:10:37"}]
This is the format of the php array outputs in json. I'm trying my best but unfortunately I can't load the data in tablesorter pager.. I have read almost every single stackoverflow topic related to this and Mottie's pager example. The problem I face seems to be the format of the json.
Is it possible to make tablesorter plugin to read through the json file that I have given example of?
In addition I use ajaxurl to load the file ajaxUrl (everything is loaded in the header):
<script type="test/javascript">
$(function(){
// Initialize tablesorter
// ***********************
$("myTable")
.tablesorter({
theme: 'blue',
widthFixed: true,
sortLocaleCompare: true, // needed for accented characters in the data
sortList: [ [0,1] ],
widgets: ['zebra']
})
// initialize the pager plugin
// ****************************
.tablesorterPager({
// **********************************
// Description of ALL pager options
// **********************************
// target the pager markup - see the HTML block below
container: $(".pager"),
// If you want to use ajaxUrl placeholders, here is an example:
// ajaxUrl: "http:/mydatabase.com?page={page}&size={size}&{sortList:col}"
// where {page} is replaced by the page number (or use {page+1} to get a one-based index),
// {size} is replaced by the number of records to show,
// {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol}
// the filterList to the url into an "fcol" array.
// So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url
// and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url
ajaxUrl : 'http://example.com/json.php?paged={page}&{sortList:column}',
// use this option to manipulate and/or add additional parameters to the ajax url
customAjaxUrl: function(table, url) {
// manipulate the url string as you desire
url += '&currPage=' + window.location.pathname;
// trigger a custom event; if you want
$(table).trigger('changingUrl', url);
// send the server the current page
return url;
},
// ajax error callback from $.tablesorter.showError function
// ajaxError: function( config, xhr, settings, exception ){ return exception; };
// returning false will abort the error message
ajaxError: null,
// add more ajax settings here
// see http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings
ajaxObject: {
type: 'GET', // default setting
dataType: 'json'
},
// process ajax so that the following information is returned:
// [ total_rows (number), rows (array of arrays), headers (array; optional) ]
// example:
// [
// 100, // total rows
// [
// [ "row1cell1", "row1cell2", ... "row1cellN" ],
// [ "row2cell1", "row2cell2", ... "row2cellN" ],
// ...
// [ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
// ],
// [ "header1", "header2", ... "headerN" ] // optional
// ]
// OR
// return [ total_rows, $rows (jQuery object; optional), headers (array; optional) ]
ajaxProcessing: function(data){
if (data && data.hasOwnProperty('rows')) {
var indx, r, row, c, d = data.rows,
// total number of rows (required)
total = data.total_rows,
// array of header names (optional)
headers = data.headers,
// cross-reference to match JSON key within data (no spaces)
headerXref = headers.join(',').replace(/\s+/g,'').split(','),
// all rows: array of arrays; each internal array has the table cell data for that row
rows = [],
// len should match pager set size (c.size)
len = d.length;
// this will depend on how the json is set up - see City0.json
// rows
for ( r=0; r < len; r++ ) {
row = []; // new row array
// cells
for ( c in d[r] ) {
if (typeof(c) === "string") {
// match the key with the header to get the proper column index
indx = $.inArray( c, headerXref );
// add each table cell data to row array
if (indx >= 0) {
row[indx] = d[r][c];
}
}
}
rows.push(row); // add new row array to rows array
}
// in version 2.10, you can optionally return $(rows) a set of table rows within a jQuery object
return [ total, rows, headers ];
}
},
// Set this option to false if your table data is preloaded into the table, but you are still using ajax
processAjaxOnInit: true,
// output string - default is '{page}/{totalPages}';
// possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
// also {page:input} & {startRow:input} will add a modifiable input in place of the value
output: '{startRow} to {endRow} ({totalRows})',
// apply disabled classname (cssDisabled option) to the pager arrows when the rows
// are at either extreme is visible; default is true
updateArrows: true,
// starting page of the pager (zero based index)
page: 1,
// Number of visible rows - default is 10
size: 50,
// Saves the current pager page size and number (requires storage widget)
savePages: true,
// Saves tablesorter paging to custom key if defined.
// Key parameter name used by the $.tablesorter.storage function.
// Useful if you have multiple tables defined
storageKey: 'tablesorter-pager',
// Reset pager to this page after filtering; set to desired page number (zero-based index),
// or false to not change page at filter start
pageReset: 1,
// if true, the table will remain the same height no matter how many records are displayed.
// The space is made up by an empty table row set to a height to compensate; default is false
fixedHeight: false,
// remove rows from the table to speed up the sort of large tables.
// setting this to false, only hides the non-visible rows; needed if you plan to
// add/remove rows with the pager enabled.
removeRows: false,
// If true, child rows will be counted towards the pager set size
countChildRows: false,
// css class names of pager arrows
cssNext : '.next', // next page arrow
cssPrev : '.prev', // previous page arrow
cssFirst : '.first', // go to first page arrow
cssLast : '.last', // go to last page arrow
cssGoto : '.gotoPage', // page select dropdown - select dropdown that set the "page" option
cssPageDisplay : '.pagedisplay', // location of where the "output" is displayed
cssPageSize : '.pagesize', // page size selector - select dropdown that sets the "size" option
// class added to arrows when at the extremes; see the "updateArrows" option
// (i.e. prev/first arrows are "disabled" when on the first page)
cssDisabled : 'disabled', // Note there is no period "." in front of this class name
cssErrorRow : 'tablesorter-errorRow' // error information row
});
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.29.0/js/extras/jquery.tablesorter.pager.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.29.0/js/jquery.tablesorter.js"></script>
<table id="myTable" class="tablesorter">
<thead>
<tr class="tablesorter-ignoreRow">
<td class="pager" colspan="5">
<img src="../addons/pager/icons/first.png" class="first"/>
<img src="../addons/pager/icons/prev.png" class="prev"/>
<span class="pagedisplay"></span> <!-- this can be any element, including an input -->
<img src="../addons/pager/icons/next.png" class="next"/>
<img src="../addons/pager/icons/last.png" class="last"/>
<select class="pagesize">
<option value="50">50</option>
</select>
</td>
</tr>
<tr>
<th>1</th> <!-- thead text will be updated from the JSON; make sure the number of columns matches the JSON data -->
<th>2</th>
</tr>
</thead>
<tbody> <!-- tbody will be loaded via JSON -->
</tbody>
</table>

Related

How to extract invoices data from an image in android app?

My task is to extract text from a scanned document/ JPG and then get only below mentioned 6 values so that I can auto-fill a form-data in my next screen/ activity.
I used google cloud vision api in my android app with a Blaze version(paid), And I got the result as a text block, but I want to extract only some of information out of them, how I can achieve that?
Bills or receipt can be different all the time but I want 6 things out of all the invoices text block for Ex -
Vendor
Account
Description
Due Date
Invoice Number
Amount
Is there any tool/3rd party library available so that I can use in my android development.
Note - I don't think any sample of receipt or bill image needed for this because it can be any type of bill or invoice we just need to extract 6 mentioned things from that extracted text.
In the next scenarios I will create two fictive bill formats, then write the code algorithm to parse them. I will write only the algorithm because I don't know JAVA.
On the first column we have great pictures from two bills. In the second column we have text data obtained from OCR software. It's like a simple text file, with no logic implemented. But we know certain keywords that can make it have meaning. Bellow is the algorithm that translates the meaningless file in a perfect logical JSON.
// Text obtained from BILL format 1
var TEXT_FROM_OCR = "Invoice no 12 Amount 55$
Vendor name BusinessTest 1 Account No 1213113
Due date 2019-12-07
Description Lorem ipsum dolor est"
// Text obtained from BILL format 2
var TEXT_FROM_OCR =" BusinessTest22
Invoice no 19 Amount 12$
Account 4564544 Due date 2019-12-15
Description
Lorem ipsum dolor est
Another description line
Last description line"
// This is a valid JSON object which describes the logic behind the text
var TEMPLATES = {
"bill_template_1": {
"vendor":{
"line_no_start": null, // This means is unknown and will be ignored by our text parsers
"line_no_end": null, // This means is unknown and will be ignored by our text parsers
"start_delimiter": "Vendor name", // Searched value starts immediatedly after this start_delimiters
"end_delimiter": "Account" // Searched value ends just before this end_delimter
"value_found": null // Save here the value we found
},
"account": {
"line_no_start": null, // This means is unknown and will be ignored by our text parsers
"line_no_end": null, // This means is unknown and will be ignored by our text parsers
"start_delimiter": "Account No", // Searched value starts immediatedly after this start_delimiters
"end_delimiter": null // Extract everything untill the end of current line
"value_found": null // Save here the value we found
},
"description": {
// apply same logic as above
},
"due_date" {
// apply same logic as above
},
"invoice_number" {
// apply same logic as above
},
"amount" {
// apply same logic as above
},
},
"bill_template_2": {
"vendor":{
"line_no_start": 0, // Extract data from line zero
"line_no_end": 0, // Extract data untill line zero
"start_delimiter": null, // Ignore this, because our delimiter is a complete line
"end_delimiter": null // Ignore this, because our delimiter is a complete line
"value_found": null // Save here the value we found
},
"account": {
"line_no_start": null, // This means is unknown and will be ignored by our text parsers
"line_no_end": null, // This means is unknown and will be ignored by our text parsers
"start_delimiter": "Account", // Searched value starts immediatedly after this start_delimiters
"end_delimiter": "Due date" // Searched value ends just before this end_delimter
"value_found": null // Save here the value we found
},
"description": {
"line_no_start": 6, // Extract data from line zero
"line_no_end": 99999, // Extract data untill line 99999 (a very big number which means EOF)
"start_delimiter": null, // Ignore this, because our delimiter is a complete line
"end_delimiter": null // Ignore this, because our delimiter is a complete line
"value_found": null // Save here the value we found
},
"due_date" {
// apply same logic as above
},
"invoice_number" {
// apply same logic as above
},
"amount" {
// apply same logic as above
},
}
}
// ALGORITHM
// 1. convert into an array the TEXT_FROM_OCR variable (each index, means a new line in file)
// in JavaScript we would do something like this:
TEXT_FROM_OCR = TEXT_FROM_OCR.split("\r\n");
var MAXIMUM_SCORE = 6; // we are looking to extract 6 values, out of 6
foreach TEMPLATES as TEMPLATE_TO_PARSE => PARSE_METADATA{
SCORE = 0; // for each field we find, we increment score
foreach PARSE_METADATA as SEARCHED_FIELD_NAME => DELIMITERS_METADATA{
// Search by line first
if (DELIMITERS_METADATA['line_no_start'] !== NULL && DELIMITERS_METADATA['line_no_end'] !== NULL){
// Initiate value with an empty string
DELIMITERS_METADATA['value_found'] = '';
// Concatenate the value found across these lines
for (LINE_NO = DELIMITERS_METADATA['line_no_start']; LINE_NO <= DELIMITERS_METADATA['line_no_end']; LINE_NO++){
// Add line, one by one as defined by your delimiters
DELIMITERS_METADATA['value_found'] += TEXT_FROM_OCR[ LINE_NO ];
}
// We have found a good value, continue to next field
SCORE++;
continue;
}
// Search by text delimiters
if (DELIMITERS_METADATA['start_delimiter'] !== NULL){
// Search for text inside each line of the file
foreach TEXT_FROM_OCR as LINE_CONTENT{
// If we found start_delimiter on this line, then let's parse it
if (LINE_CONTENT.indexOf(DELIMITERS_METADATA['start_delimiter']) > -1){
// START POSITION OF OUR SEARCHED VALUE IS THE OFFSET WE FOUND + THE TOTAL LENGTH OF START DELIMITER
START_POSITION = LINE_CONTENT.indexOf(DELIMITERS_METADATA['start_delimiter']) + LENGTH( DELIMITERS_METADATA['start_delimiter'] );
// by default we try to extract all data from START_POSITION untill the end of current line
END_POSITION = 999999999999; // till the end of line
// HOWEVER, IF THERE IS AN END DELIMITER DEFINED, WE WILL USE THAT
if (DELIMITERS_METADATA['end_delimiter'] !== NULL){
// IF WE FOUND THE END DELIMITER ON THIS LINE, WE WILL USE ITS OFFSET as END_POSITION
if (LINE_CONTENT.indexOf(DELIMITERS_METADATA['end_delimiter']) > -1){
END_POSITION = LINE_CONTENT.indexOf(DELIMITERS_METADATA['end_delimiter']);
}
}
// SUBSTRACT THE VALUE WE FOUND
DELIMITERS_METADATA['value_found'] = LINE_CONTENT.substr(START_POSITION, END_POSITION);
// We have found a good value earlier, increment the score
SCORE++;
// break this foreach as we found a good value, and we need to move to next field
break;
}
}
}
}
print(TEMPLATE_TO_PARSE obtained a score of SCORE out of MAXIMUM_SCORE):
}
At the end you will know which template extracted most of the data, and based on this which one to use for that bill. Feel free to ask anything in comments. If I stayed 45 minute to write this answer, I'll surely answer to your comments as well. :)

tabulator table doesn't render some column values. wondering why?

i got a temporary table which is created from procedure. i'm assigning it to the variable which is then converted to json and passed to the view of my laravel project. when i load my page table is rendered but some column values are missing.i wonder why?
i've run it in MySQL Workbench and all the data shows up normally. here is the HTML result of it: https://jsfiddle.net/iBek23/z5pdsjob/
controller:
public function tabulator(){
DB::select("CALL report1a_respublika('2000/01/01', '2023/01/01')");
$one = DB::select("SELECT * FROM test");
$one = json_encode($one);
return view ('reports.tabulator', compact('one'));
view:
{{-- tabulator tables --}}
<div style='display:none' id="tableZero"></div>
<div id="tableOne" style="margin-top:15px"></div>
{{-- tabulator script --}}
<script>
//define data for table1
var tabledata1 = {!!$one!!}
var table0 = new Tabulator("#tableZero", {
layout: "fitDataFill", //fit columns to width of table (optional)
autoColumns: true,
});
//define table1
var table1 = new Tabulator("#tableOne", {
data: tabledata1,
autoColumns:true,
layout:"fitDataFill",
movableColumns:true,
selectable:true,
clipboard:true,
});
//trigger download of data.xlsx file
var sheets = {
"sheetA": table1,
};
downloadXlxs = function(){
table0.download("xlsx", "report.xlsx", {sheets:sheets});
};
</script>
here is what i get: https://jsfiddle.net/iBek23/wf0xernz/5/
columns 5,6 and 7 (counting from left to right) don't have any values.
Replace Period '.' with .
see this jsfiddle you can see first item, repeat this
.\u0411\u0438\u0448\u043a\u0435\u043a":12,

How to add legend for a bar chart with different colors in dc.js?

Below is the code snippet for a barchart with colored bars:
var Dim2 = ndx.dimension(function(d){return [d.SNo, d.something ]});
var Group2 = Dim2.group().reduceSum(function(d){ return d.someId; });
var someColors = d3.scale.ordinal().domain(["a1","a2","a3","a4","a5","a6","a7","a8"])
.range(["#2980B9","#00FFFF","#008000","#FFC300","#FF5733","#D1AEF1","#C0C0C0","#000000"]);
barChart2
.height(250)
.width(1000)
.brushOn(false)
.mouseZoomable(true)
.x(d3.scale.linear().domain([600,800]))
.elasticY(false)
.dimension(Dim2)
.group(Group2)
.keyAccessor(function(d){ return d.key[0]; })
.valueAccessor(function(d){return d.value; })
.colors(someColors)
.colorAccessor(function(d){return d.key[1]; });
How do I add a legend to this chart?
Using composite keys in crossfilter is really tricky, and I don't recommend it unless you really need it.
Crossfilter only understands scalars, so even though you can produce dimension and group keys which are arrays, and retrieve them correctly, crossfilter is going to coerce those arrays to strings, and that can cause trouble.
Here, what is happening is that Group2.all() iterates over your data in string order, so you get keys in the order
[1, "a1"], [10, "a3"], [11, "a4"], [12, "a5"], [2, "a3"], ...
Without changing the shape of your data, one way around this is to sort the data in your legendables function:
barChart2.legendables = function() {
return Group2.all().sort((a,b) => a.key[0] - b.key[0])
.map(function(kv) {
return {
chart: barChart2,
name: kv.key[1],
color: barChart2.colors()(kv.key[1]) }; }) };
An unrelated problem is that dc.js takes the X domain very literally, so even though [1,12] contains all the values, the last bar was not shown because the right side ends right at 12 and the bar is drawn between 12 and 13.
So:
.x(d3.scale.linear().domain([1,13]))
Now the legend matches the data!
Fork of your fiddle (also with dc.css).
EDIT: Of course, you want the legend items unique, too. You can define uniq like this:
function uniq(a, kf) {
var seen = [];
return a.filter(x => seen[kf(x)] ? false : (seen[kf(x)] = true));
}
Adding a step to legendables:
barChart2.legendables = function() {
var vals = uniq(Group2.all(), kv => kv.key[1]),
sorted = vals.sort((a,b) => a.key[1] > b.key[1] ? 1 : -1);
// or in X order: sorted = vals.sort((a,b) => a.key[0] - b.key[0]);
return sorted.map(function(kv) {
return {
chart: barChart2,
name: kv.key[1],
color: barChart2.colors()(kv.key[1]) }; }) };
Note that we're sorting by the string value of d.something which lands in key[1]. As shown in the comment, sorting by x order (d.SNo, key[0]) is possible too. I wouldn't recommend sorting by y since that's a reduceSum.
Result, sorted and uniq'd:
New fiddle.

how to update html page only when database changes

JS, which returns the first name:
$.ajax({
url: 'exm1.php',
type: 'get',
dataType: 'json',
success: function(data){
if( t<data.length){
for(var i=0;i<data.length;i++){
$("#output").html(data[i].fn);
}
}
},
error: function() {
// TODO: do error handling here
console.log('An error has occurred while fetching lat/lon.');
}
});
There are two things which I would like to sort out first:
It always overwrites the output div tag. I don't know how I can prevent this
setInterval should only run when there is a change in the database, or maybe when data.length changes, is there any way I could store the previous value of data.length and then compare with new data.length
If #output contains the first name, then you can target and save it into a variable on load
Example:
var firstname = $('#output').html();
And then all you have to do is compare the AJAX output with the variable.
Example:
if ( firstname != data )
$('#output').html(firstname);
To prevent overwriting your div tag, use the following.
$("#output").append(data[i].fn);
I don't understand what you mean by setinterval, please clarify in your code.
You can keep the html inside of the div by using this (vanilla javascript)
var inTheOutPutDiv = document.getElementById("output");
inputTheOutPutDiv.html = inputTheOutPutDiv.html + data; // whatever
// or use inputTheOutPutDiv.html+= data;
but sense you are using Jquery you can use append
$("#output").append(data);
You can instantiate a variable outside of your function that holds the length of your database query (scope). Or you can return a database value that says if the info has been updated (a value of 1 if changed, a value of 0 if the same). Then use a conditional statement to perform an action on the response. You have many options.
Unfortunately you can't call setInterval only when the data changes, but you can run something at a set interval and only update the #output html if the result is different.
The reason #output is getting overwritten is because calling $.html("...") will replace the existing html with the argument. I'd recommend simply putting what you want to remain static on the page in a different div.
<div id='oldOutput'>
Static Text Here: firstName?
<span id='output'></span>
</div>
Now your ajax return will overwrite the element and your static text will remain. However, you still have an issue in that every iteration of your loop will remove the string from before. If you want each loop to render cleanly, i'd recommend something along these lines:
success: function(data){
if( t<data.length){
// if you want to flush the #output each time
$("#output").html("");
for(var i=0;i<data.length;i++){
$("#output").append(data[i].fn);
// or you could put each entry in a div for later use
// $("#output").append("<div id='div_"+ i +"'>" + data[i].fn + "</div>");
}
}
}
In this case (you're flushing the div each time) then if the data is the same you shouldn't notice it update. If you don't flush the div then the data will keep appending on to the end.
If this is what you want, but only when there is new data, then you can count the number of child divs in the #output span - if you wrap each item from the output in a div as I have above, you should be able to compare this to data.length:
success: function(data){
if (t<data.length){
// gets direction descendants of #output that are divs,
// and counts the length of the array returned
var numItems = $("#output > div").length;
if(numItems != data.length){
loop goes here..
}
}
This will sort of work, it will only update when the number of items returned is different to the number of items you already have. However, if your script is returning a set of data and you need to filter it to see what's old and what's new, it's slightly different. You'll need to do include some sort of identifier (other than the data itself) to embed into the to check if it exists later.
if ( $("[data-id=" + data[i].identifier +"]").length > 0 ) {
// already exists, do nothing
} else {
$("#output").append("<div data-id='"+ data[i].identifier +"' etc...
}

Example: Tablesorter Output widget difference between download and popup special characters

I am using the output widget of tablesorter to get my table as csv (for excel). The table has no problems with special chars. If I export the data as output its all fine. If I use download option special characters like & are shown as & (seen in Notepad++) so Excel decides to separate it as there is a ;
Any help?
UPDATE:
http://jsfiddle.net/abkNM/6503/
Thanks!
Ok, I ended up adding a new callback function output_formatContent*. Use it as follows (demo):
output_formatContent: function (config, widgetOptions, data) {
// data.isHeader (boolean) = true if processing a header cell
// data.$cell = jQuery object of the cell currently being processed
// data.content = processed cell content
// (spaces trimmed, quotes added/replaced, etc)
// **********
// use data.$cell.html() to get the original cell content
return data.content.replace(/&/g, '&');
}
if you want to replace all HTML codes, then check out Mathias Bynens he which would work as follows:
output_formatContent : function( c, wo, data ) {
// replace all HTML shortcut codes
// (e.g. 'foo © bar ≠ baz 𝌆 qux' becomes 'foo © bar ≠ baz 𝌆 qux' )
return he.decode( data.content );
}
* Note: the new output widget callback is currently only available in the master branch of the tablesorter repository. It will be included in the next update.