/*
* Project Name : Visual Python
* Description : GUI-based Python code generator
* File Name : Instance.js
* Author : Black Logic
* Note : Apps > Instance
* License : GNU GPLv3 with Visual Python special exception
* Date : 2021. 11. 18
* Change Date :
*/
//============================================================================
// [CLASS] Instance
//============================================================================
define([
__VP_TEXT_LOADER__('vp_base/html/m_apps/instance.html'), // INTEGRATION: unified version of text loader
__VP_CSS_LOADER__('vp_base/css/m_apps/instance'), // INTEGRATION: unified version of css loader
'vp_base/js/com/com_String',
'vp_base/js/com/com_util',
'vp_base/js/com/component/PopupComponent',
'vp_base/js/com/component/InstanceEditor',
'vp_base/js/com/component/DataSelector',
'vp_base/js/m_apps/Subset'
], function(insHtml, insCss, com_String, com_util, PopupComponent, InstanceEditor, DataSelector, Subset) {
const MAX_STACK_SIZE = 20;
/**
* Instance
*/
class Instance extends PopupComponent {
_init() {
super._init();
/** Write codes executed before rendering */
this.config.dataview = false;
this.config.sizeLevel = 5;
this.config.checkModules = ['pd'];
this.state = {
target: '',
vp_instanceVariable: '',
variable: {
stack: []
},
selectedBox: 'variable',
allocate: '',
...this.state
}
this.pointer = this.state.variable;
this.subsetEditor = null;
this.insEditor = null;
this._addCodemirror('vp_instanceVariable', this.wrapSelector('#vp_instanceVariable'), 'readonly');
}
_bindEvent() {
super._bindEvent();
let that = this;
// target change
$(this.wrapSelector('#vp_instanceTarget')).on('change', function(event) {
let value = $(this).val();
that.updateValue(value);
that.reloadInsEditor();
});
// clear
$(this.wrapSelector('#vp_instanceClear')).on('click', function(event) {
that.addStack();
that.updateValue('');
that.reloadInsEditor();
});
// undo
$(this.wrapSelector('#vp_instanceUndo')).on('click', function(event) {
that.popStack();
that.reloadInsEditor();
});
// backspace
$(document).on('keyup', this.wrapSelector('.CodeMirror'), function(event) {
var keycode = event.keyCode
? event.keyCode
: event.which;
if (keycode == 8) {
// backspace
that.popStack();
that.reloadInsEditor();
}
});
// subset button clicked
$(document).on('click', this.wrapSelector('.vp-ds-button'), function(event) {
var insEditorType = $(this).closest('.vp-instance-box').hasClass('variable')? 'variable': 'allocate';
$(that.wrapSelector('.CodeMirror')).removeClass('selected');
if (insEditorType == 'variable') {
// variable
that.pointer = that.state.variable;
$(that.wrapSelector('.variable .CodeMirror')).addClass('selected');
} else if (insEditorType == 'allocate'){
// allocate
that.pointer = that.state.allocate;
$(that.wrapSelector('.allocate .CodeMirror')).addClass('selected');
} else {
// that.state.variable.insEditor.hide();
// that.state.allocate.insEditor.hide();
}
});
// subset applied - variable
$(document).on('change apps_run', this.wrapSelector('#vp_instanceVariable'), function(event) {
var val = $(this).val();
that.addStack();
that.updateValue(val);
});
// codemirror clicked
$(document).on('click', this.wrapSelector('.CodeMirror'), function(event) {
$(that.wrapSelector('.CodeMirror')).removeClass('selected');
$(this).addClass('selected');
// show/hide insEditor
var insEditorType = $(this).closest('.vp-instance-box').hasClass('variable')? 'variable': 'allocate';
if (insEditorType == 'variable') {
// variable
that.state.selectedBox = 'variable';
that.pointer = that.state.variable;
} else if (insEditorType == 'allocate'){
// allocate
that.state.selectedBox = 'allocate';
that.pointer = that.state.allocate;
} else {
that.state.selectedBox = '';
}
});
$(document).on('focus', this.wrapSelector('.CodeMirror'), function(event) {
$(this).trigger('click');
});
// instance_editor_selected - variable
$(document).on('instance_editor_selected', this.wrapSelector('#vp_instanceVariable'), function(event) {
that.addStack();
let cmObj = that.getCodemirror('vp_instanceVariable');
var nowCode = (cmObj && cmObj.cm)?cmObj.cm.getValue():'';
if (nowCode != '') {
nowCode += '.'
}
var selectedVariable = event.varName;
let fullCode = nowCode + selectedVariable;
that.updateValue(fullCode);
that.reloadInsEditor();
});
// instance_editor_replaced - variable
$(document).on('instance_editor_replaced', this.wrapSelector('#vp_instanceVariable'), function(event) {
that.addStack();
var newCode = event.newCode;
that.updateValue(newCode);
that.reloadInsEditor();
});
// co-op with Subset
// $(this.wrapSelector('#vp_instanceVariable')).on('remove_option_page', function(evt) {
// let component = evt.component;
// component.close();
// });
// $(this.wrapSelector('#vp_instanceVariable')).on('close_option_page', function(evt) {
// let component = evt.component;
// component.close();
// });
// $(this.wrapSelector('#vp_instanceVariable')).on('focus_option_page', function(evt) {
// let component = evt.component;
// component.focus();
// });
// $(this.wrapSelector('#vp_instanceVariable')).on('apply_option_page', function(evt) {
// let component = evt.component;
// // apply its value
// let code = component.generateCode();
// component.close();
// that.addStack();
// that.state.subsetEditor.state.pandasObject = code;
// that.updateValue(code);
// });
}
templateForBody() {
let that = this;
let page = $(insHtml);
$(page).find('#vp_instanceVariable').val(this.state.vp_instanceVariable);
let targetSelector = new DataSelector({
pageThis: this, id: 'vp_instanceTarget', placeholder: 'Select variable',
allowDataType: [
'module', 'DataFrame', 'Series', 'dict', 'list', 'int'
],
allowModule: true,
finish: function(value, dtype) {
$(that.wrapSelector('#vp_instanceTarget')).trigger({type: 'change', value: value});
that.updateValue(value);
that.reloadInsEditor();
},
select: function(value, dtype) {
$(that.wrapSelector('#vp_instanceTarget')).trigger({type: 'change', value: value});
that.updateValue(value);
that.reloadInsEditor();
}
});
$(page).find('#vp_instanceTarget').replaceWith(targetSelector.toTagString());
// Removed dataselector for Allocation input
// let allocateSelector = new DataSelector({
// pageThis: this, id: 'vp_instanceAllocate', placeholder: 'Variable name'
// });
// $(page).find('#vp_instanceAllocate').replaceWith(allocateSelector.toTagString());
return page;
}
render() {
super.render();
let that = this;
// vpSubsetEditor
this.subsetEditor = new Subset({ pandasObject: '', config: { name: 'Subset', category: this.name } },
{
useInputVariable: true,
targetSelector: this.wrapSelector('#vp_instanceVariable'),
pageThis: this,
finish: function(code) {
that.addStack();
that.subsetEditor.state.pandasObject = code;
that.updateValue(code);
}
});
this.subsetEditor.disableButton();
this.ALLOW_SUBSET_TYPES = this.subsetEditor.getAllowSubsetTypes();
// vpInstanceEditor
this.insEditor = new InstanceEditor(this, "vp_instanceVariable", 'vp_variableInsEditContainer', { targetType: 'outside' });
this.insEditor.show();
// variable load
this.reloadInsEditor();
}
loadState() {
super.loadState();
// load metadata
let { vp_instanceVariable, allocate } = this.state;
this.updateValue(vp_instanceVariable);
$(this.wrapSelector('#vp_instanceAllocate')).val(allocate);
}
generateCode() {
var sbCode = new com_String();
var leftCode = $(this.wrapSelector('#vp_instanceAllocate')).val();
let cmObj = this.getCodemirror('vp_instanceVariable');
let rightCode = (cmObj && cmObj.cm)?cmObj.cm.getValue():'';
if (leftCode && leftCode != '') {
sbCode.appendFormatLine('{0} = {1}', leftCode, rightCode);
sbCode.append(leftCode); // show allocation (from version 2.4.10)
} else {
sbCode.appendFormat('{0}', rightCode);
}
return sbCode.toString();
}
hide() {
super.hide();
this.subsetEditor && this.subsetEditor.hide();
}
close() {
super.close();
this.subsetEditor && this.subsetEditor.close();
}
remove() {
super.remove();
this.subsetEditor && this.subsetEditor.remove();
}
updateValue(value) {
let cmObj = this.getCodemirror('vp_instanceVariable');
if (cmObj && cmObj.cm) {
let cm = cmObj.cm;
cm.setValue(value);
cm.save();
cm.focus();
cm.setCursor({ line: 0, ch: value.length});
}
this.state.vp_instanceVariable = value;
// show preview
this.loadPreview(value);
}
loadPreview(code) {
let that = this;
if (!code || code === '') {
$(that.wrapSelector('#instancePreview')).html('');
return;
}
// show variable information on clicking variable
vpKernel.execute(code).then(function(resultObj) {
let { result, type, msg } = resultObj;
if (msg.content.data) {
var textResult = msg.content.data["text/plain"];
var htmlResult = msg.content.data["text/html"];
var imgResult = msg.content.data["image/png"];
$(that.wrapSelector('#instancePreview')).html('');
if (htmlResult != undefined) {
// 1. HTML tag
$(that.wrapSelector('#instancePreview')).append(htmlResult);
} else if (imgResult != undefined) {
// 2. Image data (base64)
var imgTag = '';
$(that.wrapSelector('#instancePreview')).append(imgTag);
} else if (textResult != undefined) {
// 3. Text data
var preTag = document.createElement('pre');
$(preTag).text(textResult);
$(that.wrapSelector('#instancePreview')).html(preTag);
}
} else {
var errorContent = '';
if (msg.content && msg.content.ename) {
errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue, msg.content.detail);
}
$(that.wrapSelector('#instancePreview')).html(errorContent);
vpLog.display(VP_LOG_TYPE.ERROR, msg.content?.ename, msg.content?.evalue, msg.content);
}
}).catch(function(resultObj) {
let { msg, ename, evalue, status } = resultObj;
var errorContent = '';
if (msg && msg.content && msg.content.ename) {
// NOTEBOOK: notebook error FIXME: migrate it on com_Kernel.execute
errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue, msg.content.detail);
vpLog.display(VP_LOG_TYPE.ERROR, msg.content?.ename, msg.content?.evalue, msg.content);
} else {
// LAB: lab error FIXME: migrate it on com_Kernel.execute
errorContent = com_util.templateForErrorBox(ename, evalue, '');
vpLog.display(VP_LOG_TYPE.ERROR, ename, evalue);
}
$(that.wrapSelector('#instancePreview')).html(errorContent);
});
}
addStack() {
let cmObj = this.getCodemirror('vp_instanceVariable');
var currentValue = (cmObj && cmObj.cm)?cmObj.cm.getValue():'';
this.pointer.stack.push(currentValue);
// if stack over MAX_STACK_SIZE
if (this.pointer.stack.length > MAX_STACK_SIZE) {
this.pointer.stack.splice(0, 1);
}
// console.log('add stack', currentValue, this.pointer.stack);
}
popStack(replace=true) {
var lastValue = this.pointer.stack.pop();
if (!lastValue) {
lastValue = '';
}
if (replace) {
this.updateValue(lastValue);
}
// console.log('pop stack', lastValue, this.pointer.stack);
return lastValue;
}
reloadInsEditor() {
var that = this;
var tempPointer = this.pointer;
this.subsetEditor.disableButton();
var callbackFunction = function (varObj) {
var varType = varObj.type;
if (that.ALLOW_SUBSET_TYPES.includes(varType)) {
that.subsetEditor.state.dataType = varType;
let cmObj = that.getCodemirror('vp_instanceVariable');
let nowCode = (cmObj && cmObj.cm)?cmObj.cm.getValue():'';
that.subsetEditor.state.pandasObject = nowCode;
that.subsetEditor.enableButton();
}
};
this.insEditor.reload(callbackFunction);
}
}
return Instance;
});