forked from Open-CT/openct-tasks
232 lines
6.1 KiB
JavaScript
232 lines
6.1 KiB
JavaScript
/*
|
|
|
|
step1: validate csv
|
|
step2: diff with valid data
|
|
|
|
*/
|
|
function CSVTextEditor(params) {
|
|
|
|
var defaults = {
|
|
width: '100%',
|
|
height: '200px',
|
|
tab_size: 4,
|
|
font_size: 16,
|
|
validation_interval: 200,
|
|
csv_separator: ',',
|
|
content: null,
|
|
labels: {},
|
|
onChange: null
|
|
}
|
|
params = Object.assign({}, defaults, params);
|
|
|
|
|
|
// parse valid_data
|
|
var d = Papa.parse(params.valid_data, {
|
|
delimiter: params.csv_separator
|
|
});
|
|
if(d.errors.length) {
|
|
console.error('CSV Editor Error: Invalid valid_data parameter', d.errors)
|
|
params.valid_data = [[]];
|
|
} else {
|
|
params.valid_data = d.data;
|
|
}
|
|
|
|
|
|
var wrapper = $('<div class="csv-text-editor"/>')
|
|
params.parent.append(wrapper);
|
|
|
|
function addLabel(section) {
|
|
if(section in params.labels) {
|
|
var label = $('<div class="label"/>');
|
|
label.html(params.labels[section]);
|
|
wrapper.append(label);
|
|
}
|
|
}
|
|
|
|
// onChange handler
|
|
function handleOnChange() {
|
|
if(!params.onChange) {
|
|
return;
|
|
}
|
|
params.onChange(getContent());
|
|
}
|
|
|
|
|
|
// ace editor
|
|
addLabel('editor');
|
|
var editor_wrapper = $('<div class="editor"/>').css('width', params.width).css('height', params.height);
|
|
wrapper.append(editor_wrapper);
|
|
|
|
var editor = ace.edit(editor_wrapper[0]);
|
|
editor.$blockScrolling = Infinity;
|
|
editor.setFontSize(params.font_size);
|
|
editor.getSession().setOptions({
|
|
mode: 'ace/mode/plain_text',
|
|
tabSize: params.tab_size,
|
|
useSoftTabs: true
|
|
})
|
|
var Range = ace.require('ace/range').Range;
|
|
var mistake_marker;
|
|
|
|
var on_change_timeout;
|
|
editor.getSession().on('change', function(e) {
|
|
clearTimeout(on_change_timeout);
|
|
on_change_timeout = setTimeout(
|
|
handleOnChange,
|
|
params.validation_interval
|
|
);
|
|
});
|
|
|
|
|
|
function handleOnChange() {
|
|
params.onChange && params.onChange(
|
|
getContent()
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
// content
|
|
|
|
function getContent() {
|
|
return editor.getValue();
|
|
}
|
|
|
|
function setContent(content) {
|
|
editor.setValue(content);
|
|
}
|
|
|
|
|
|
|
|
// validation
|
|
|
|
var mistake;
|
|
var table;
|
|
|
|
function diff(data, valid_data, silent) {
|
|
|
|
function getValidValue(row, col) {
|
|
if(row < valid_data.length && col < valid_data[row].length) {
|
|
return valid_data[row][col];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function getDataValue(row, col) {
|
|
if(row < data.length && col < data[row].length) {
|
|
return data[row][col];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function formatCell(value, valid) {
|
|
return '<td' + (valid ? '' : ' class="mistake"') + '>' +
|
|
(v1 === false ? ' ' : '<pre>' + value + '</pre>') +
|
|
'</td>';
|
|
}
|
|
|
|
|
|
mistake = false;
|
|
var res = true;
|
|
if(data.length != params.valid_data.length) {
|
|
mistake = {
|
|
tag: data.length < params.valid_data.length ? 'rows_lack' : 'rows_excess'
|
|
}
|
|
res = false;
|
|
}
|
|
|
|
var html = '';
|
|
var rows = Math.max(data.length, params.valid_data.length);
|
|
var v1, v2, valid, cols;
|
|
for(var i=0; i<rows; i++) {
|
|
html += '<tr>';
|
|
if(i < params.valid_data.length && i < data.length && data[i].length != params.valid_data[i].length) {
|
|
mistake = {
|
|
tag: data[i].length < params.valid_data[i].length ? 'cols_lack' : 'cols_excess'
|
|
}
|
|
res = false;
|
|
}
|
|
var cols = Math.max(i < data.length ? data[i].length : 0, i < params.valid_data.length ? params.valid_data[i].length : 0);
|
|
for(var j=0; j<cols; j++) {
|
|
v1 = getDataValue(i, j);
|
|
v2 = getValidValue(i, j);
|
|
valid = v1 === v2 && v1 !== false && v2 !== false;
|
|
res = res && valid;
|
|
html += formatCell(v1, valid);
|
|
}
|
|
html += '</tr>';
|
|
}
|
|
if(!silent) {
|
|
if(!table) {
|
|
table = $('<table>');
|
|
addLabel('preview');
|
|
wrapper.append(table);
|
|
}
|
|
table.html(html);
|
|
}
|
|
if(!res && !mistake) {
|
|
mistake = {
|
|
tag: 'incorrect_data'
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
function parseEditorContent(silent) {
|
|
if(!silent && mistake_marker) {
|
|
editor.getSession().removeMarker(mistake_marker);
|
|
mistake_marker = false;
|
|
}
|
|
|
|
var content = getContent(); // .trim(); //TODO
|
|
var d = Papa.parse(content, {
|
|
delimiter: params.csv_separator
|
|
});
|
|
if(d.errors.length) {
|
|
var err = d.errors[0];
|
|
mistake = {
|
|
tag: err.code
|
|
}
|
|
if(!silent) {
|
|
editor.getSelection().setRange(new Range(0, 0, 0, 0)); // important string :)
|
|
var range = new Range(err.row, err.index, err.row, err.index + 1);
|
|
mistake_marker = editor.getSession().addMarker(range, 'mistake', 'text');
|
|
}
|
|
return null;
|
|
}
|
|
return d.data;
|
|
}
|
|
|
|
|
|
function validate(valid_csv, silent) {
|
|
table && table.html('');
|
|
mistake = null;
|
|
var data = parseEditorContent(silent);
|
|
if(data) {
|
|
return diff(data, params.valid_data, silent);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
if('content' in params && typeof params.content == 'string') {
|
|
setContent(params.content);
|
|
}
|
|
|
|
return {
|
|
setContent: setContent,
|
|
getContent: getContent,
|
|
validate: validate,
|
|
getMistake: function() {
|
|
return mistake;
|
|
},
|
|
destroy: function() {
|
|
clearTimeout(on_change_timeout);
|
|
editor.destroy();
|
|
wrapper.remove();
|
|
}
|
|
}
|
|
|
|
} |