pgadmin4/web/pgadmin/static/js/slickgrid/editors.js

1086 lines
29 KiB
JavaScript
Raw Normal View History

2019-01-02 04:24:12 -06:00
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
2022-01-04 02:24:25 -06:00
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
2019-01-02 04:24:12 -06:00
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
/***
* Contains JSON SlickGrid editors.
* @module Editors
* @namespace Slick
*/
import JSONBigNumberLib from 'json-bignumber';
import gettext from 'sources/gettext';
import Notify from '../../../static/js/helpers/Notifier';
(function($, JSONBigNumber) {
// register namespace
$.extend(true, window, {
'Slick': {
'Editors': {
'pgText': pgTextEditor,
'JsonText': JsonTextEditor,
'CustomNumber': CustomNumberEditor,
'Checkbox': pgCheckboxEditor,
// Below editor will read only editors, Just to display data
'ReadOnlyText': ReadOnlyTextEditor,
'ReadOnlyCheckbox': ReadOnlyCheckboxEditor,
'ReadOnlypgText': ReadOnlypgTextEditor,
'ReadOnlyJsonText': ReadOnlyJsonTextEditor,
},
},
});
// return wrapper element
function getWrapper() {
return $('<div class=\'pg-text-editor\' />');
}
// return textarea element
function getTextArea() {
return $('<textarea class=\'pg-textarea text-12\' hidefocus rows=5\'>');
}
// return json editor element
function getJsonEditor() {
return $('<div id=\'pg-json-editor\' hidefocus>');
}
// Generate and return editor buttons
function getButtons(editable) {
var $buttons = $('<div class=\'pg_buttons\' />'),
label = editable ? gettext('Cancel') : gettext('OK'),
Fixed following: - Base font size changed from 0.815rem to 0.875rem, for navbar from 0.875rem to 0.925rem. - Dialog sizes made consistent throughout the application. Now there are 3 size options for width and height each - sm, md, lg. Combination of any of these to be used hereafter - Alignment fix for controls of Node properties dialogs which includes showing text and label in one line without dialog size change, checkbox alignment, switch control alignment at places and other minor improvements in other dialogs - Error message design change in dialogs validation - SQL Editor data grid editor popup design changes which were missed - Design change for dashboard server activity grid - Login page language dropdown color fix - Properties accordion collapse design fix - Help, Info icon fixed across all dialogs which were not working if clicked exactly on the text - Added missing icon with buttons at few places - Shadow behind the dialogs is increased to make it look clearly separated and depth. - Control Alignment fix in maintenance dialog - Min height of alertify dialogs set for better UX - File dialog design fix when no files found - Grant wizard fixes - Scroll bar visibility on first page, use full space for SQL generated on the last page - Browser toolbar buttons changed to sync with SQL editor toolbar buttons - Rounded corners for docker floating dialog (no properties) - Renaming file in file dialog should show original file name - SQL data grid text edit popup buttons behaviour was swapped. This is fixed. - Import/Export dialog changes as per new design.
2019-01-02 03:35:15 -06:00
button_type = editable ? 'btn-secondary' : 'btn-primary',
button_icon = editable ? 'fa-times' : 'fa-check';
$('<button class=\'btn ' + button_type + ' long_text_editor pg-alertify-button\' data-label="'+label+'">' +
'<span class="fa '+ button_icon +' pg-alertify-button"></span>&nbsp;'+ label +
'</button>')
.appendTo($buttons);
if (editable) {
$('<button class=\'btn btn-primary long_text_editor\' data-label="' + gettext('OK') + '">'+
'<span class="fa fa-check pg-alertify-button"></span>&nbsp;' + gettext('OK') +
'</button>')
.appendTo($buttons);
}
return $buttons;
}
function is_valid_array(val) {
val = $.trim(val);
return !(val != '' && (val.charAt(0) != '{' || val.charAt(val.length - 1) != '}'));
}
/*
* This function handles the [default] and [null] values for cells
* if row is copied, otherwise returns the editor value.
* @param {args} editor object
* @param {item} row cell values
* @param {state} entered value
* @param {column_type} type of column
*/
function setValue(args, item, state, column_type) {
// declare a 2-d array which tracks the status of each updated cell
// If a cell is edited for the 1st time and state is null,
// set cell value to [default] and update its status [row][cell] to 1.
// If same cell is edited again, and kept blank, set cell value to [null]
// If a row is copied
var grid = args.grid;
if (item.is_row_copied) {
if (!grid.copied_rows) {
grid.copied_rows = [
[],
];
}
var active_cell = grid.getActiveCell(),
row = active_cell['row'],
cell = active_cell['cell'],
last_value = item[args.column.pos];
last_value = (column_type === 'number') ?
(_.isEmpty(last_value) || last_value) : last_value;
item[args.column.field] = state;
if (last_value && _.isNull(state) &&
(_.isUndefined(grid.copied_rows[row]) ||
_.isUndefined(grid.copied_rows[row][cell]))
) {
item[args.column.field] = undefined;
if (grid.copied_rows[row] == undefined) grid.copied_rows[row] = [];
grid.copied_rows[row][cell] = 1;
}
} else {
if(column_type === 'jsonb' && state != null) {
item[args.column.field] = JSONBigNumber.stringify(JSONBigNumber.parse(state));
} else {
item[args.column.field] = state;
}
}
}
function calculateEditorPosition(position, $wrapper) {
var $edit_grid = $wrapper.parent().find('#datagrid');
position.top = position.top - $wrapper.height();
var grid_width = $edit_grid.width(),
popup_width = $wrapper.width() + 32;
popup_width += position.left;
if (popup_width > grid_width) {
position.left -= (popup_width - grid_width);
}
return position;
}
// Text data type editor
function pgTextEditor(args) {
var $input, $wrapper, $buttons;
var defaultValue;
var scope = this;
this.init = function() {
var $container = $('body');
$wrapper = getWrapper().appendTo($container);
$input = getTextArea().appendTo($wrapper);
$buttons = getButtons(true).appendTo($wrapper);
Fixed following: - Base font size changed from 0.815rem to 0.875rem, for navbar from 0.875rem to 0.925rem. - Dialog sizes made consistent throughout the application. Now there are 3 size options for width and height each - sm, md, lg. Combination of any of these to be used hereafter - Alignment fix for controls of Node properties dialogs which includes showing text and label in one line without dialog size change, checkbox alignment, switch control alignment at places and other minor improvements in other dialogs - Error message design change in dialogs validation - SQL Editor data grid editor popup design changes which were missed - Design change for dashboard server activity grid - Login page language dropdown color fix - Properties accordion collapse design fix - Help, Info icon fixed across all dialogs which were not working if clicked exactly on the text - Added missing icon with buttons at few places - Shadow behind the dialogs is increased to make it look clearly separated and depth. - Control Alignment fix in maintenance dialog - Min height of alertify dialogs set for better UX - File dialog design fix when no files found - Grant wizard fixes - Scroll bar visibility on first page, use full space for SQL generated on the last page - Browser toolbar buttons changed to sync with SQL editor toolbar buttons - Rounded corners for docker floating dialog (no properties) - Renaming file in file dialog should show original file name - SQL data grid text edit popup buttons behaviour was swapped. This is fixed. - Import/Export dialog changes as per new design.
2019-01-02 03:35:15 -06:00
$buttons.find('button:first').on('click', this.cancel);
$buttons.find('button:last').on('click', this.save);
2018-07-10 04:59:53 -05:00
$input.on('keydown', this.handleKeyDown);
scope.position(args.position);
2018-07-10 04:59:53 -05:00
$input.trigger('focus').trigger('select');
};
this.handleKeyDown = function(e) {
if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) {
scope.save();
} else if (e.which == $.ui.keyCode.ESCAPE) {
e.preventDefault();
scope.cancel();
} else if (e.which == $.ui.keyCode.TAB && e.shiftKey) {
e.preventDefault();
args.grid.navigatePrev();
} else if (e.which == $.ui.keyCode.TAB) {
e.preventDefault();
args.grid.navigateNext();
}
};
this.save = function() {
args.commitChanges();
};
this.cancel = function() {
$input.val(defaultValue);
args.cancelChanges();
};
this.hide = function() {
$wrapper.hide();
};
this.show = function() {
$wrapper.show();
};
this.position = function(position) {
calculateEditorPosition(position, $wrapper);
$wrapper
.css('top', position.top)
.css('left', position.left);
};
this.destroy = function() {
$wrapper.remove();
};
this.focus = function() {
2018-07-10 04:59:53 -05:00
$input.trigger('focus');
};
// When text editor opens
this.loadValue = function(item) {
if (
_.isUndefined(item[args.column.field]) ||
_.isNull(item[args.column.field])
) {
$input.val(defaultValue = '');
return;
}
if (!args.column.is_array) {
if (item[args.column.field] === '') {
$input.val(defaultValue = '\'\'');
$input.trigger('select');
} else if (item[args.column.field] === '\'\'') {
$input.val(defaultValue = '\\\'\\\'');
} else if (item[args.column.field] === '""') {
$input.val(defaultValue = '\\"\\"');
} else {
$input.val(defaultValue = item[args.column.field]);
2018-07-10 04:59:53 -05:00
$input.trigger('select');
}
} else {
$input.val(defaultValue = item[args.column.field]);
2018-07-10 04:59:53 -05:00
$input.trigger('select');
}
};
this.serializeValue = function() {
var value = $input.val();
// If empty return null
if (value === '') {
return null;
}
if (!args.column.is_array) {
if (value === '\'\'' || value === '""') {
return '';
} else if (value === '\\\'\\\'') {
return '\'\'';
} else if (value === '\\"\\"') {
return '""';
} else {
return value;
}
} else {
return $.trim(value);
}
};
this.applyValue = function(item, state) {
setValue(args, item, state, 'text');
};
this.isValueChanged = function() {
// Use _.isNull(value) for comparison for null instead of
// defaultValue == null, because it returns true for undefined value.
if ($input.val() == '' && _.isUndefined(defaultValue)) {
return false;
} else {
return (!($input.val() == '' && _.isNull(defaultValue))) &&
($input.val() !== defaultValue);
}
};
this.validate = function() {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
if (!validationResults.valid) {
return validationResults;
}
}
if (args.column.is_array && !is_valid_array($input.val())) {
return {
valid: false,
msg: gettext('Arrays must start with "{" and end with "}"'),
};
}
return {
valid: true,
msg: null,
};
};
this.init();
}
// JSON data type editor
function JsonTextEditor(args) {
var $input, $wrapper, $buttons, $editor;
var defaultValue, tmpdata;
var scope = this;
var editorInitialized = false;
this.init = function() {
var $container = $('body');
$wrapper = getWrapper().appendTo($container);
$input = getJsonEditor().appendTo($wrapper);
$buttons = getButtons(true).appendTo($wrapper);
$buttons.find('button:first').on('click', this.cancel);
$buttons.find('button:last').on('click', this.save);
2018-07-10 04:59:53 -05:00
$input.on('keydown', this.handleKeyDown);
scope.position(args.position);
};
this.handleKeyDown = function(e) {
if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) {
scope.save();
} else if (e.which == $.ui.keyCode.ESCAPE) {
e.preventDefault();
scope.cancel();
}
};
this.resizeJsoneditorObserver = new ResizeObserver(() => {
if ($editor){
$editor.resize();
}
});
this.save = function() {
args.commitChanges();
};
this.cancel = function() {
args.cancelChanges();
};
this.hide = function() {
$wrapper.hide();
};
this.show = function() {
$wrapper.show();
};
this.position = function(position) {
calculateEditorPosition(position, $wrapper);
position.top = Math.max(position.top, 0);
$wrapper
.css('top', position.top)
.css('left', position.left);
};
this.destroy = function() {
this.resizeJsoneditorObserver.unobserve(document.getElementById('pg-json-editor'));
$editor.destroy();
$wrapper.remove();
};
this.focus = function() {
$editor.focus();
};
this.loadValue = function(item) {
var data = defaultValue = item[args.column.field];
/* Can be useful until JSON editor loads */
tmpdata = data;
/* If jsonb or array */
if(args.column.column_type_internal === 'jsonb' && !Array.isArray(data) && data != null) {
data = JSONBigNumber.stringify(JSONBigNumber.parse(data), null, 2);
} else if (Array.isArray(data)) {
var temp = [];
$.each(data, function(i, val) {
if (typeof val === 'object') {
temp.push(JSONBigNumber.stringify(val, null, 2));
} else {
temp.push(val);
}
});
data = '[' + temp.join() + ']';
}
/* set editor content to empty if value is null*/
if (_.isNull(data)){
defaultValue = '';
data = '';
}
/* Create editor if required & set data*/
if ($editor){
$editor.setText(data);
$editor.focus();
}else{
editorInitialized = true;
require.ensure(['jsoneditor'], function(require) {
var JSONEditor = require('jsoneditor');
var jsonContainer = document.getElementById('pg-json-editor');
var options = {
modes: ['code', 'form', 'tree','preview'],
onError: function (error){
var msg = 'Invalid Json: ' + error.message.split(':')[0];
Notify.error(gettext(msg));
}
};
$editor = new JSONEditor(jsonContainer, options);
$editor.setText(data);
$editor.focus();
}, function(error){
throw(error);
}, 'jsoneditorchunk');
}
this.resizeJsoneditorObserver.observe(document.getElementById('pg-json-editor'));
};
this.serializeValue = function() {
/* Create editor if data is null/new data entry */
if( !editorInitialized) {
require.ensure(['jsoneditor'], function(require) {
var JSONEditor = require('jsoneditor');
var jsonContainer = document.getElementById('pg-json-editor');
var options = {
modes: ['code', 'form', 'tree','preview'],
onError: function (error){
var msg = 'Invalid Json: ' + error.message.split(':')[0];
Notify.error(gettext(msg));
}
};
if(jsonContainer) {
$editor = new JSONEditor(jsonContainer, options);
var data = '';
$editor.setText(data);
$editor.focus();
return null;
}
}, function(error){
throw(error);
}, 'jsoneditorchunk');}
if($editor){
let data = $editor.getText();
if (data === '') {
return null;
}
return data;
}else{
// The loader is not loaded yet ?
return tmpdata;
}
};
this.applyValue = function(item, state){
if(args.column.column_type_internal === 'jsonb') {
setValue(args, item, state, 'jsonb');
} else {
setValue(args, item, state, 'text');
}
};
this.isValueChanged = function() {
let data = $editor.getText();
if (data == '' && (_.isUndefined(defaultValue) || _.isNull(defaultValue) )) {
return false;
} else {
if( args.column.column_type_internal === 'jsonb' && (! _.isUndefined(defaultValue) && defaultValue != '')){
defaultValue = JSON.stringify(JSON.parse(defaultValue), null,2);
}
return (!( data == '' && _.isNull(defaultValue)) && (data != defaultValue));
}
};
this.validate = function() {
if(args.column.column_type_internal === 'jsonb' ||
args.column.column_type_internal === 'json') {
let data = $editor.getText();
try {
if(data != ''){
JSON.parse(data);
}
} catch(e) {
$input.addClass('pg-text-invalid');
return {
valid: false,
msg: e.message,
};
}
}
return {
valid: true,
msg: null,
};
};
this.init();
}
// Text data type editor
function ReadOnlypgTextEditor(args) {
var $input, $wrapper, $buttons;
var defaultValue;
var scope = this;
this.init = function() {
var $container = $('body');
$wrapper = getWrapper().appendTo($container);
$input = getTextArea().appendTo($wrapper);
$buttons = getButtons(false).appendTo($wrapper);
$buttons.find('button:first').on('click', this.cancel);
2018-07-10 04:59:53 -05:00
$input.on('keydown', this.handleKeyDown);
scope.position(args.position);
2018-07-10 04:59:53 -05:00
$input.trigger('focus').trigger('select');
};
this.handleKeyDown = function(e) {
if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) {
scope.cancel();
} else if (e.which == $.ui.keyCode.ESCAPE) {
e.preventDefault();
scope.cancel();
} else if (e.which == $.ui.keyCode.TAB && e.shiftKey) {
scope.cancel();
e.preventDefault();
args.grid.navigatePrev();
} else if (e.which == $.ui.keyCode.TAB) {
scope.cancel();
e.preventDefault();
args.grid.navigateNext();
}
};
this.cancel = function() {
$input.val(defaultValue);
args.cancelChanges();
};
this.hide = function() {
$wrapper.hide();
};
this.show = function() {
$wrapper.show();
};
this.position = function(position) {
calculateEditorPosition(position, $wrapper);
$wrapper
.css('top', position.top)
.css('left', position.left);
};
this.destroy = function() {
$wrapper.remove();
};
this.focus = function() {
2018-07-10 04:59:53 -05:00
$input.trigger('focus');
};
this.loadValue = function(item) {
$input.val(defaultValue = item[args.column.field]);
2018-07-10 04:59:53 -05:00
$input.trigger('select');
};
this.serializeValue = function() {
return $input.val();
};
this.applyValue = function(item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function() {
return (!($input.val() == '' && defaultValue == null)) && ($input.val() != defaultValue);
};
this.validate = function() {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null,
};
};
this.init();
}
// JSON data type editor
function ReadOnlyJsonTextEditor(args) {
var $input, $wrapper, $buttons, $editor;
var defaultValue;
var scope = this;
var tmpdata;
this.init = function() {
var $container = $('body');
$wrapper = getWrapper().appendTo($container);
$input = getJsonEditor().appendTo($wrapper);
$buttons = getButtons(false).appendTo($wrapper);
$buttons.find('button:first').on('click', this.cancel);
2018-07-10 04:59:53 -05:00
$input.on('keydown', this.handleKeyDown);
scope.position(args.position);
};
this.handleKeyDown = function(e) {
if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) {
scope.cancel();
} else if (e.which == $.ui.keyCode.ESCAPE) {
e.preventDefault();
scope.cancel();
}
};
this.cancel = function() {
$input.val(defaultValue);
args.cancelChanges();
};
this.hide = function() {
$wrapper.hide();
};
this.show = function() {
$wrapper.show();
};
this.position = function(position) {
calculateEditorPosition(position, $wrapper);
position.top = Math.max(position.top, 0);
$wrapper
.css('top', position.top)
.css('left', position.left);
};
this.destroy = function() {
this.resizeJsoneditorObserver.unobserve(document.getElementById('pg-json-editor'));
$editor.destroy();
$wrapper.remove();
};
this.focus = function() {
$editor.focus();
};
// listen to resize event for json editor
this.resizeJsoneditorObserver = new ResizeObserver(() => {
if ($editor){
$editor.resize();
}
});
this.loadValue = function(item) {
var data = defaultValue = item[args.column.field];
tmpdata = data;
if(args.column.column_type_internal === 'jsonb' && !Array.isArray(data) && data != null) {
data = JSONBigNumber.stringify(JSONBigNumber.parse(data), null, 2);
} else if (Array.isArray(data)) {
var temp = [];
$.each(data, function(i, val) {
if (typeof val === 'object') {
temp.push(JSONBigNumber.stringify(val, null,2));
} else {
temp.push(val);
}
});
data = '[' + temp.join() + ']';
}
/* set editor content to empty if value is null*/
if (_.isNull(data)){
defaultValue = '';
data = '';
}
/* Create editor if required & set data*/
require.ensure(['jsoneditor'], function(require) {
var JSONEditor = require('jsoneditor');
var jsonContainer = document.getElementById('pg-json-editor');
jsonContainer.setAttribute('readonly', true);
let options = {
modes: ['code', 'form', 'tree', 'preview'],
onEditable: function() {
return false;
}
};
if(jsonContainer) {
$editor = new JSONEditor(jsonContainer, options);
$editor.setText(data);
}
}, function(error){
throw(error);
}, 'jsoneditorchunk');
this.resizeJsoneditorObserver.observe(document.getElementById('pg-json-editor'));
};
this.serializeValue = function() {
if($editor) {
let data = $editor.getText();
if (data === '') {
return null;
}
return data;
} else {
// The loader is not loaded yet ?
return tmpdata;
}
};
this.applyValue = function(item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function() {
let data = $editor.getText();
return (!(data == '' && defaultValue == null)) && (data != defaultValue);
};
this.validate = function() {
if (args.column.validator) {
var validationResults = args.column.validator($editor.getText());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null,
};
};
this.init();
}
function ReadOnlyTextEditor(args) {
var $input;
var defaultValue;
this.init = function() {
$input = $('<input type=text class=\'editor-text\' readonly/>')
.appendTo(args.container)
2018-07-10 04:59:53 -05:00
.on('keydown.nav', function(e) {
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
e.stopImmediatePropagation();
}
})
2018-07-10 04:59:53 -05:00
.trigger('focus')
.trigger('select');
};
this.destroy = function() {
$input.remove();
};
this.focus = function() {
2018-07-10 04:59:53 -05:00
$input.trigger('focus');
};
this.getValue = function() {
return $input.val();
};
this.loadValue = function(item) {
var value = item[args.column.field];
// Check if value is null or undefined
if (value === null || typeof value === 'undefined') {
value = '';
}
defaultValue = value;
$input.val(defaultValue);
$input[0].defaultValue = defaultValue;
2018-07-10 04:59:53 -05:00
$input.trigger('select');
};
this.serializeValue = function() {
return $input.val();
};
this.applyValue = function(item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function() {
return (!($input.val() == '' && defaultValue == null)) && ($input.val() != defaultValue);
};
this.validate = function() {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null,
};
};
this.init();
}
function ReadOnlyCheckboxEditor(args) {
var $select;
var defaultValue;
this.init = function() {
$select = $('<input type=checkbox value=\'true\' class=\'editor-checkbox\' hideFocus disabled>');
$select.appendTo(args.container);
2018-07-10 04:59:53 -05:00
$select.trigger('focus');
};
this.destroy = function() {
$select.remove();
};
this.focus = function() {
2018-07-10 04:59:53 -05:00
$select.trigger('focus');
};
this.loadValue = function(item) {
defaultValue = item[args.column.pos];
if (_.isNull(defaultValue) || _.isUndefined(defaultValue)) {
$select.prop('indeterminate', true);
$select.data('checked', 2);
} else {
defaultValue = !!item[args.column.pos];
if (defaultValue) {
$select.prop('checked', true);
$select.data('checked', 0);
} else {
$select.prop('checked', false);
$select.data('checked', 1);
}
}
};
this.serializeValue = function() {
if ($select.prop('indeterminate')) {
return null;
}
return $select.prop('checked');
};
this.applyValue = function(item, state) {
item[args.column.pos] = state;
};
this.isValueChanged = function() {
var select_value = $select.data('checked');
return (!(select_value === 2 && (defaultValue == null || defaultValue == undefined))) &&
(select_value !== defaultValue);
};
this.validate = function() {
return {
valid: true,
msg: null,
};
};
this.init();
}
function CustomNumberEditor(args) {
var $input;
var defaultValue;
this.init = function() {
$input = $('<input type=text class=\'editor-text\' />');
2018-07-10 04:59:53 -05:00
$input.on('keydown.nav', function(e) {
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
e.stopImmediatePropagation();
}
});
$input.appendTo(args.container);
2018-07-10 04:59:53 -05:00
$input.trigger('focus').trigger('select');
};
this.destroy = function() {
$input.remove();
};
this.focus = function() {
2018-07-10 04:59:53 -05:00
$input.trigger('focus');
};
this.loadValue = function(item) {
defaultValue = item[args.column.field];
if (Array.isArray(defaultValue) && !_.isNull(defaultValue) && !_.isUndefined(defaultValue)) {
$input.val('{' + defaultValue.join() + '}');
} else {
$input.val(defaultValue);
}
$input[0].defaultValue = defaultValue;
2018-07-10 04:59:53 -05:00
$input.trigger('select');
};
this.serializeValue = function() {
var value = $input.val();
if (value === '') {
return null;
}
return value;
};
this.applyValue = function(item, state) {
setValue(args, item, state, 'number');
};
this.isValueChanged = function() {
if ($input.val() == '' && _.isUndefined(defaultValue)) {
return false;
} else if ($input.val() == '' && defaultValue == '') {
return true;
} else {
return (!($input.val() == '' && _.isNull(defaultValue))) &&
($input.val() != defaultValue);
}
};
this.validate = function() {
var value = $input.val();
if (!args.column.is_array && isNaN(value)) {
return {
valid: false,
msg: gettext('Please enter a valid number'),
};
}
if (args.column.validator) {
var validationResults = args.column.validator(value);
if (!validationResults.valid) {
return validationResults;
}
}
if (args.column.is_array) {
if (!is_valid_array(value)) {
return {
valid: false,
msg: gettext('Arrays must start with "{" and end with "}"'),
};
}
var val = value.trim().slice(1, -1),
arr;
if (val == '') {
arr = [];
} else {
arr = val.split(',');
}
for (var k in arr) {
if (isNaN(arr[k])) {
return {
valid: false,
msg: gettext('Please enter a valid number'),
};
}
}
}
return {
valid: true,
msg: null,
};
};
this.init();
}
// Custom checkbox editor, We need it for runtime as it does not render
// indeterminate checkbox state
function pgCheckboxEditor(args) {
var $select;
var defaultValue, previousState;
this.init = function() {
$select = $('<div class=\'multi-checkbox\' tabindex="0"><span class=\'check\' hideFocus></span></div>');
$select.appendTo(args.container);
2018-07-10 04:59:53 -05:00
$select.trigger('focus');
$select.on('click', this.changeValue);
$select.on('keydown', (e) => {
if (e.which == $.ui.keyCode.SPACE) {
e.preventDefault();
this.changeValue(e);
}
});
};
this.changeValue = function() {
// The following code is taken from https://css-tricks.com/indeterminate-checkboxes/
var states = ['unchecked', 'partial', 'checked'];
var curState = $select.find('.check').data('state') || 0;
curState++;
$select.find('.check')
.removeClass('unchecked partial checked')
.addClass(states[curState % states.length])
.data('state', curState % states.length);
};
this.destroy = function() {
$select.remove();
};
this.focus = function() {
2018-07-10 04:59:53 -05:00
$select.trigger('focus');
};
this.loadValue = function(item) {
defaultValue = item[args.column.field];
previousState = 1;
if (_.isNull(defaultValue) || _.isUndefined(defaultValue)) {
$select.find('.check').data('state', 1).addClass('partial');
} else {
defaultValue = !!item[args.column.field];
if (defaultValue) {
$select.find('.check').data('state', 2).addClass('checked');
previousState = 2;
} else {
$select.find('.check').data('state', 0).addClass('unchecked');
previousState = 0;
}
}
};
this.serializeValue = function() {
if ($select.find('.check').data('state') == 1) {
return null;
}
return $select.find('.check').data('state') == 2 ? true : false;
};
this.applyValue = function(item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function() {
var currentState = $select.find('.check').data('state');
return currentState !== previousState;
};
this.validate = function() {
if (args.column.validator) {
var validationResults = args.column.validator(this.serializeValue());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null,
};
};
this.init();
}
})(window.jQuery, JSONBigNumberLib);