openct-tasks/_common/modules/pemFioi/components/csv-text-editor/editor.js

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 ? '&nbsp;' : '<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();
}
}
}