TableSorter : csv export and accented characters - tablesorter

With TableSorter, when I export my table in a CSV file, the accented characters doesn't appear correctly.
How to solve that ?

In version 2.16.4, the output_encoding option was added to the output widget.
The demo now has a select dropdown in which you can choose "utf8 BOM" required to make the csv file work properly in Excel.
You can set this option to include a BOM by default as follows:
output_encoding : 'data:text/csv;charset=utf8,%EF%BB%BF'

With version 2.30.6, the solution proposed above didn't work for me.
I read an article saying the unicode character \uFEFF should be specified at the beginning of the CSV data.
So, my code below insert this character only once and I reset the boolean immediately to do it only once.
Dont pay attention about scroller_* widget options if you don't use the widget 'scroller' (same for 'columnSelector' and 'filter' widgets).
Here to show UTF8 data in Excel, use the callback function output_formatContent and see my code below.
var insertBOM = true;
var $tablesorterScroll = $(".tablesorter-scroll");
$tablesorterScroll.tablesorter({
sortInitialOrder: 'desc',
widthFixed : false,
showProcessing: true,
widgets: ['filter', 'columnSelector', 'scroller', 'output'],
widgetOptions: {
// Set the max-height property of the table for the scroller widget.
scroller_height : 412, // sized with table CompanyBoard statistics (fits like that without scrollbar and finishes at the end of the row)
// Delay in milliseconds before the filter widget starts searching; This option prevents searching for
// every character while typing and should make searching large tables faster.
filter_searchDelay : 200,
// target the column selector markup
columnSelector_container : $('#columnSelector'),
// column status, true = display, false = hide
// disable = do not display on list
columnSelector_columns : {
//0: 'disable' /* set to disabled; not allowed to unselect it */
},
// remember selected columns (requires $.tablesorter.storage)
columnSelector_saveColumns: true,
// container layout
columnSelector_layout : '<label><input type="checkbox">{name}</label>',
// data attribute containing column name to use in the selector container
columnSelector_name : 'data-selector-name',
/* Responsive Media Query settings */
// enable/disable mediaquery breakpoints
columnSelector_mediaquery: false,
// toggle checkbox name
columnSelector_mediaqueryName: 'Auto: ',
// breakpoints checkbox initial setting
columnSelector_mediaqueryState: false,
// hide columnSelector false columns while in auto mode
columnSelector_mediaqueryHidden: false,
// set the maximum and/or minimum number of visible columns; use null to disable
columnSelector_maxVisible: null,
columnSelector_minVisible: null,
// responsive table hides columns with priority 1-6 at these breakpoints
// see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint
// *** set to false to disable ***
columnSelector_breakpoints : [ '20em', '30em', '40em', '50em', '60em', '70em' ],
// data attribute containing column priority
// duplicates how jQuery mobile uses priorities:
// http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/
columnSelector_priority : 'data-priority',
// class name added to checked checkboxes - this fixes an issue with Chrome not updating FontAwesome
// applied icons; use this class name (input.checked) instead of input:checked
columnSelector_cssChecked : 'checked',
output_saveFileName : 'TableExport.csv',
output_separator: ';', // Excel recognize it and shows data in separated column without doing "Text to columns" Excel option.
output_replaceQuote: '\'',
output_delivery: 'd', // (p)opup, (d)ownload
output_saveRows: 'v', // (a)ll, (v)isible, (f)iltered, jQuery filter selector (string only) or filter function
output_encoding: "data:text/csv;charset=utf-8",
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
var BOM = "";
// Add BOM at file starting
if (insertBOM) {
BOM = "\uFEFF"; // set Excel enconding UTF-8
insertBOM = false;
}
return BOM + data.content.replace(/&/g, '&'); // data.content is HTML text converted so '&' has been converted to & which is the HTML reprensetation of the '&' character. Convert it back to show '&' in the CSV.
}
}
});
EDIT (15.10.2020)
My initial solution proposed above doesn't work when output_headerRows: true AND if the first header row has a rowspan. A colspan is ok.
To handle that situation, I found a way :
Before: The BOM was inserted by the function output_formatContentwhich is raised row after row (and a condition to apply the BOM only once)
Now: Use the output_callback function that is raised only once at the end of the data processed, so can insert the BOM.
IMPORTANT: You need tablesorter v2.25.1+ to be able to return data instead of true as before.
output_callback : function(config, data, url) {
BOM = "\uFEFF"; // The BOM character to force Excel opens CSV as UTF-8
return BOM + data;
}

Related

jquery querybuilder: editing the Rule's structure in the file query-builder.standalone

I created a custom plugin for Jquery Querybuilder but it only works if I edit the Rule's structure from the file querybuilder-standalone itself.
The plugin I've created adds a button like the one from the plugin not-group inside each rule. The function of this plugin is to transform an input like this 'field = "value"' into this 'UPPER( field ) = "VALUE"'.
The problem with this solution is that, in order to reuse this query I need to modify the structure of the Rule itself in the query-builder.
The edits I would need to make are the following:
You can find the original code at line 2242 of the file query-builder.standalone.js
if (model.filter) {
model.filter.to_upper = item.to_upper; /* <---This is the line I added myself */
model.operator = self.getOperatorByType(item.operator, !options.allow_invalid);
if (!model.operator) {
model.operator = self.getOperators(model.filter)[0];
}
}
And this:
Original code between line 6215 and 6232
var id = self.getSQLFieldID(field, value);
//The following line us the code I need
var to_upper = data.to_upper;
/**
* Modifies the rule generated from the SQL expression
* #event changer:sqlToRule
* #memberof module:plugins.SqlSupport
* #param {object} rule
* #param {object} AST
* #returns {object}
*/
var rule = self.change('sqlToRule', {
id: id,
field: field,
operator: opVal.op,
value: opVal.val,
//The following line us the code I need
to_upper: to_upper
}, data);
'to_upper' is a property I set to false by default and to true when its related button it's clicked.
How could I do this without adding these code to query-builder itself?
Here you can find more details about my issue and the helpful answer I received from the creator of this UI component.
https://github.com/mistic100/jQuery-QueryBuilder/issues/931

Aspose: Text after Ampersand(&) not seen while setting the page header

I encountered a problem with setting the page header text containing ampersand like ‘a&b’. The text after ‘&’ disappears in the pdf maybe because it is the reserved key in Aspose. My code looks like this:
PageSetup pageSetup = workbook.getWorksheets().get(worksheetName).getPageSetup();
//calling the function
setHeaderFooter(pageSetup, parameters, criteria)
//function for setting header and footer
def setHeaderFooter(PageSetup pageSetup, parameters, criteria = [:])
{
def selectedLoa=getSelectedLoa(parameters)
if(selectedLoa.length()>110){
String firstLine = selectedLoa.substring(0,110);
String secondLine = selectedLoa.substring(110);
if(secondLine.length()>120){
secondLine = secondLine.substring(0,122)+"...."
}
selectedLoa = firstLine+"\n"+secondLine.trim();
}
def periodInfo=getPeriodInfo(parameters, criteria)
def reportingInfo=periodInfo[0]
def comparisonInfo=periodInfo[1]
def benchmarkName=getBenchmark(parameters)
def isNonComparison = criteria.isNonComparison?
criteria.isNonComparison:false
def footerInfo="&BReporting Period:&B " + reportingInfo+"\n"
if (comparisonInfo && !isNonComparison){
footerInfo=footerInfo+"&BComparison Period:&B " +comparisonInfo+"\n"
}
if (benchmarkName){
footerInfo+="&BBenchmark:&B "+benchmarkName
}
//where I encounterd the issue,selectedLoa contains string with ampersand
pageSetup.setHeader(0, pageSetup.getHeader(0) + "\n&\"Lucida Sans,Regular\"&8&K02-074&BPopulation:&B "+selectedLoa)
//Insertion of footer
pageSetup.setFooter(0,"&\"Lucida Sans,Regular\"&8&K02-074"+footerInfo)
def downloadDate = new Date().format("MMMM dd, yyyy")
pageSetup.setFooter(2,"&\"Lucida Sans,Regular\"&8&K02-074" + downloadDate)
//Insertion of logo
try{
def bucketName = parameters.containsKey('printedRLBucketName')?parameters.get('printedRLBucketName'):null
def filePath = parameters.containsKey('printedReportLogo')?parameters.get('printedReportLogo'): null
// Declaring a byte array
byte[] binaryData
if(!filePath || filePath.contains("null") || filePath.endsWith("null")){
filePath = root+"/images/defaultExportLogo.png"
InputStream is = new FileInputStream(new File(filePath))
binaryData = is.getBytes()
}else {
AmazonS3Client s3client = amazonClientService.getAmazonS3Client()
S3Object object = s3client.getObject(bucketName, filePath)
// Getting the bytes out of input stream of S3 object
binaryData = object.getObjectContent().getBytes()
}
// Setting the logo/picture in the right section (2) of the page header
pageSetup.setHeaderPicture(2, binaryData);
// Setting the script for the logo/picture
pageSetup.setHeader(2, "&G");
// Scaling the picture to correct size
Picture pic = pageSetup.getPicture(true, 2);
pic.setLockAspectRatio(true)
pic.setRelativeToOriginalPictureSize(true)
pic.setHeight(35)
pic.setWidth(Math.abs(pic.getWidth() * (pic.getHeightScale() / 100)).intValue());
}catch (Exception e){
e.printStackTrace()
}
}
In this case, I get only ‘a’ in the pdf header all other text after ampersand gets disappeared. Please suggest me with a solution for this. I am using aspose 18.2
We have added header on a PDF page with below code snippet but we did not notice any problem when ampersand sign is included in header text.
// open document
Document document = new Document(dataDir + "input.pdf");
// create text stamp
TextStamp textStamp = new TextStamp("a&bcdefg");
// set properties of the stamp
textStamp.setTopMargin(10);
textStamp.setHorizontalAlignment(HorizontalAlignment.Center);
textStamp.setVerticalAlignment(VerticalAlignment.Top);
// set text properties
textStamp.getTextState().setFont(new FontRepository().findFont("Arial"));
textStamp.getTextState().setFontSize(14.0F);
textStamp.getTextState().setFontStyle(FontStyles.Bold);
textStamp.getTextState().setFontStyle(FontStyles.Italic);
textStamp.getTextState().setForegroundColor(Color.getGreen());
// iterate through all pages of PDF file
for (int Page_counter = 1; Page_counter <= document.getPages().size(); Page_counter++) {
// add stamp to all pages of PDF file
document.getPages().get_Item(Page_counter).addStamp(textStamp);
}
// save output document
document.save(dataDir + "TextStamp_18.8.pdf");
Please ensure using Aspose.PDF for Java 18.8 in your environment. For further information on adding page header, you may visit Add Text Stamp in the Header or Footer section.
In case you face any problem while adding header, then please share your code snippet and generated PDF document with us via Google Drive, Dropbox etc. so that we may investigate it to help you out.
PS: I work with Aspose as Developer Evangelist.
Well, yes, "&" is a reserved word when inserting headers/footers in MS Excel spreadsheet via Aspose.Cells APIs. To cope with your issue, you got to place another ampersand to paste the "& (ampersand)" in the header string. See the sample code for your reference:
e.g
Sample code:
Workbook wb = new Workbook();
Worksheet ws = wb.getWorksheets().get(0);
ws.getCells().get("A1").putValue("testin..");
String headerText="a&&bcdefg";
PageSetup pageSetup = ws.getPageSetup();
pageSetup.setHeader(0, headerText);
wb.save("f:\\files\\out1.xlsx");
wb.save("f:\\files\\out2.pdf");
Hope this helps a bit.
I am working as Support developer/ Evangelist at Aspose.

Using the Performance Timing Plugin

I'm tyring to implementing the timingPerformance plugin through Adobe DTM. Everything is configured (at least to my knowledge), but there aren't any events triggering.
Here's a snippet of code that's set up within DTM. Note that I've removed all the respected plugins so that it Isn't such a big post. But the necessary functions have been added.
When I jump to a secondary page to test the values, I would expect to see the listed events and eVar, but this is not the case. All I see is the eVar that is collecting my previous page name value, but no events.
s.pte = 'event110,event111,event112,event113,event114,event115,event116,event117,event118,event119'
//[--------------------------- 1 to 8 ---------------------------][-- 9 --][- 10 -]
s.ptc = false;
/*****Plugin Section*******/
s.usePlugins = true
function s_doPlugins(s) {
_satellite.notify("doPlugins fired:" + document.readyState);
/* Previous Page Name */
s.prop55 = s.eVar77 = s.getPreviousValue(s.pageName, 's_ppn');
/* Percent Page Viewed */
/* Pre-requisite: Previous Page Name */
var ppv = s.getPercentPageViewed(s.pageName); //get array of data on prev page % viewed
if (ppv && typeof ppv == 'object' && ppv[0] == s.prop55) { //if ppv array returned and prev page id matches prev page name
s.prop56 = s.eVar78 = ppv[1] + '|' + ppv[2];
}
/* Time Parting Tracking */
var tp = s.getTimeParting('n', '-7');
s.prop44 = s.eVar55 = tp;
/* Performance Timing */
s.eVar77 = s.getPreviousValue(s.pageName, 'gpv_v77', ''); //Record the previous page name in the designated eVar of your choice
//s.performanceTiming('list2') //List variable if one is neededd
/* Pre-requisite: Previous Page Name */
}
Am I missing something or not calling something correctly?
As indicated in the comments the performance timing plugin uses the s.performanceTiming(); to trigger the actual plugin. With that being said here is how it should be configured in order to properly work.
The s.pte is the call for which determines whether or not to execute the actual plugin.
`s.pte = 'event10,event11,event12,event13,event14,event15,event16,event17,event18,event19'
//[--------------------------- 1 to 8 ---------------------------][-- 9 --][- 10 -]
s.ptc = false;`
Next is to initialize the plugin that is set within the doPlugins section of the s_code.
/* Performance Timing */
s.eVar9 = s.getPreviousValue(s.pageName,'gpv_v9',''); //Record the previous page name in the designated eVar of your choice
s.performanceTiming('')
Note: Adobe Analytics documentation references a "list" option within the s.perfomaceTiming('list2)` call itself. This list is optional and is not required; therefore, removing "list" will work for a standard call.

insert numerical sequence in large text file

I need to create a file in this format :
item0000001
item0000002
item0000003
item0000004
item0000005
I was doing this with UltraEdit which has column mode including insert number ( start + increment including leading zeros ).
Unfortunately, UltraEdit bombs out above 1 million rows.
Does anyone know of a text editor with large file capacity that has a similar operation?
BaltoStar has not written which version of UltraEdit was used and how exactly he has tried to create the file.
However, here is an UltraEdit script which can be used to create a file with lines containing an incrementing number with leading zeros according to last number.
To use that script with UltraEdit v14.20 or UEStudio v9.00 or any later version, copy the code block of the script and paste it into a new ASCII file with DOS line terminations in UltraEdit/UEStudio. Save the file for example as CreateLinesWithIncrementingNumber.js into your preferred directory of UE/UES scripts.
Now run the script by clicking on menu item Run Active Script in menu Scripting.
The script prompts the user for first and last value of the incrementing number, and for strings left and right of the incrementing number which can be both also empty strings.
Then lean back and see how the script writes the lines with the incrementing number into a new file in blocks. I created a file with more than 150 MB with an incrementing number from 0 to 5.000.000 within a few seconds using this UltraEdit script.
if (typeof(UltraEdit.clipboardContent) == "string")
{
// Write in blocks of not more than 4 MB into the file. Do not increase
// this value too much as during the script execution much more free
// RAM in a continous block is necessary than the value used here for
// joining the lines in user clipboard 9. A too large value results
// in a memory exception during script execution and the user of the
// script also does not see for several seconds what is going on.
var nBlockSize = 4194304;
// Create a new file and make sure it uses DOS/Windows line terminations
// independent on the user configuration for line endings of new files.
UltraEdit.newFile();
UltraEdit.activeDocument.unixMacToDos();
var sLineTerm = "\r\n"; // Type of line termination is DOS/Windows.
// Ask user of script for the first value to write into the file.
do
{
var nFirstNumber = UltraEdit.getValue("Please enter first value of incrementing number:",1);
if (nFirstNumber < 0)
{
UltraEdit.messageBox("Sorry, but first value cannot be negative.");
}
}
while (nFirstNumber < 0);
// Ask user of script for the last value to write into the file.
do
{
var nLastNumber = UltraEdit.getValue("Please enter last value of incrementing number:",1);
if (nFirstNumber >= nLastNumber)
{
UltraEdit.messageBox("Sorry, but last value must be greater than "+nFirstNumber.toString(10)+".");
}
}
while (nFirstNumber >= nLastNumber);
var sBeforeNumber = UltraEdit.getString("Please enter string left of the incrementing number:",1);
var sAfterNumber = UltraEdit.getString("Please enter string right of the incrementing number:",1);
// http://stackoverflow.com/questions/16378849/ultraedit-how-do-i-pad-out-a-string-with-leading-blanks
// Convert the highest number to a decimal string and get a copy
// of this string with every character replaced by character '0'.
// With last number being 39428 the created string is "00000".
var sLeadingZeros = nLastNumber.toString(10).replace(/./g,"0");
// Instead of writing the string with the incrementing number line
// by line to file which would be very slow and which would create
// lots of undo records, the lines are collected first in an array of
// strings whereby the number of strings in the array is determined
// by value of variable nBlockSize. The lines in the array are
// concatenated into user clipboard 9 and written as block to the
// file using paste command. That is much faster and produces just
// a few undo records even on very large files.
UltraEdit.selectClipboard(9);
UltraEdit.clearClipboard();
// Calculate number of lines per block which depends on the
// lengths of the 4 strings which build a line in the file.
var nLineLength = sBeforeNumber.length + sLeadingZeros.length +
sAfterNumber.length + sLineTerm.length;
var nRemainder = nBlockSize % nLineLength;
var nLinesPerBlock = (nBlockSize - nRemainder) / nLineLength;
var asLines = [];
var nCurrentNumber = nFirstNumber;
while (nLastNumber >= nCurrentNumber)
{
// Convert integer number to decimal string.
var sNumber = nCurrentNumber.toString(10);
// Has the decimal string of the current number less
// characters than the decimal string of the last number?
if (sNumber.length < sLeadingZeros.length)
{
// Build decimal string new with X zeros from the alignment string
// and concatenate this leading zero string with the number string.
sNumber = sLeadingZeros.substr(0,sLeadingZeros.length-sNumber.length) + sNumber;
}
asLines.push(sBeforeNumber + sNumber + sAfterNumber);
if (asLines.length >= nLinesPerBlock)
{
asLines.push(""); // Results in a line termination at block end.
UltraEdit.clipboardContent = asLines.join(sLineTerm);
UltraEdit.activeDocument.paste();
UltraEdit.clearClipboard();
asLines = [];
}
nCurrentNumber++;
}
// Output also the last block.
if (asLines.length)
{
asLines.push("");
UltraEdit.clipboardContent = asLines.join(sLineTerm);
UltraEdit.activeDocument.paste();
UltraEdit.clearClipboard();
}
// Reselect the system clipboard and move caret to top of new file.
UltraEdit.selectClipboard(0);
UltraEdit.activeDocument.top();
}
else if(UltraEdit.messageBox)
{
UltraEdit.messageBox("Sorry, but you need a newer version of UltraEdit/UEStudio for this script.");
}
else
{
UltraEdit.newFile();
UltraEdit.activeDocument.write("Sorry, but you need a newer version of UltraEdit/UEStudio for this script.");
}

Prevent CKEditor filtering Radiant Tags (non-valid HTML tags)

I'm using CKEditor with refinerycms (rails CMS) I've also added basic support for radius tags (they are the tags used in Radiant, another rails CMS) so I'm able to list some elements from the model in the page just inserting a code.
The problem is that the radius tags mimic html:
<r:product_listing category="products" list_as="grid"/>
When using CKEditor to modify the page contents it thinks the radius tags are invalid HTML, which is correct and the expected behaviour, but I can't find the way to tell CKEditor to just ignore those tags.
Any ideas?
Thanks in advance
EDIT: Turned out that the tag was being filtered by the sanitize method in rails being called by RefineryCMS.
What kind of issues do you have with custom tags? And on which browsers?
I checked that CKEditor preserves this tag, but wraps entire content with it. To avoid that you have to edit CKEDITOR.dtd, namely:
CKEDITOR.dtd.$empty[ 'r:product_listing' ] = 1;
But that still may not be enough. To have better support you'd need to make more changes in this object - especially important is to define what can be its parents and that it's an inline tag. For example:
CKEDITOR.dtd.p[ 'r:product_listing' ] = 1; // it is allowed in <p> tag
CKEDITOR.dtd.$inline[ 'r:product_listing' ] = 1;
This still may not be enough - for example you'll most likely won't have copy&paste support.
So, if you need more reliable support I'd try a little bit different way. Using CKEDITOR.dataProcessor you can transform this tag into some normal one when data are loaded into editor and when data are retrieved transform it back to that tag.
Example solution:
// We still need this, because this tag has to be parsed correctly.
CKEDITOR.dtd.p[ 'r:product_listing' ] = 1;
CKEDITOR.dtd.$inline[ 'r:product_listing' ] = 1;
CKEDITOR.dtd.$empty[ 'r:product_listing' ] = 1;
CKEDITOR.replace( 'editor1', {
on: {
instanceReady: function( evt ) {
var editor = evt.editor;
// Add filter for html->data transformation.
editor.dataProcessor.dataFilter.addRules( {
elements: {
'r:product_listing': function( element ) {
// Span isn't self closing element - change that.
element.isEmpty = false;
// Save original element name in data-saved-name attribute.
element.attributes[ 'data-saved-name' ] = element.name;
// Change name to span.
element.name = 'span';
// Push zero width space, because empty span would be removed.
element.children.push( new CKEDITOR.htmlParser.text( '\u200b' ) );
}
}
} );
// Add filter for data->html transformation.
editor.dataProcessor.htmlFilter.addRules( {
elements: {
span: function( element ) {
// Restore everything.
if ( element.attributes[ 'data-saved-name' ] ) {
element.isEmpty = true;
element.children = [];
element.name = element.attributes[ 'data-saved-name' ];
delete element.attributes[ 'data-saved-name' ]
}
}
}
} );
}
}
} );
Now r:product_listing element will be transformed into span with zero-width space inside. Inside editor there will be a normal span, but in source mode and in data got by editor#getData() method you'll see original r:product_listing tag.
I think that this solution should be the safest one. E.g. copy and pasting works.
u can also add as protected source, so no filtering or parsing will be done.
config.protectedSource.push(/<r:product_listing[\s\S]*?\/>/g);
just add these line to your config.js ([\s\S]*? is for any random content)
check out http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-protectedSource

Resources