pgadmin4/web/pgadmin/static/js/utils.js

447 lines
14 KiB
JavaScript

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////////////////
import _ from 'underscore';
import $ from 'jquery';
import gettext from 'sources/gettext';
import 'wcdocker';
import Notify from './helpers/Notifier';
var wcDocker = window.wcDocker;
export function parseShortcutValue(obj) {
var shortcut = '';
if (obj.alt) { shortcut += 'alt+'; }
if (obj.shift) { shortcut += 'shift+'; }
if (obj.control) { shortcut += 'ctrl+'; }
shortcut += obj.key.char.toLowerCase();
return shortcut;
}
export function handleKeyNavigation(event) {
let wizardHeader = $(event.currentTarget).find('.wizard-header');
let wizardFooter = $(event.currentTarget).find('.wizard-footer');
let gridElement = $(event.currentTarget).find('.select-row-cell:first');
let gridElementLast = $(event.currentTarget).find('.select-row-cell:last');
let firstWizardHeaderButton = $(wizardHeader).find('button:enabled:first');
let lastWizardHeaderButton = $(wizardHeader).find('button:enabled:last');
let lastWizardFooterBtn = $(wizardFooter).find('button:enabled:last');
let firstWizardFooterBtn = $(wizardFooter).find('button:enabled:first');
if (event.shiftKey && event.keyCode === 9) {
// Move backwards
if(firstWizardHeaderButton && $(firstWizardHeaderButton).is($(event.target))) {
if (lastWizardFooterBtn) {
$(lastWizardFooterBtn).focus();
event.preventDefault();
event.stopPropagation();
}
}
else if ($(firstWizardFooterBtn).is($(event.target))){
if ($(gridElement).find('.custom-control-input').is(':visible')){
$(gridElementLast).find('.custom-control-input').focus();
event.preventDefault();
event.stopPropagation();
}else if ($(event.currentTarget).find('.wizard-content').find('.CodeMirror-scroll').is(':visible')){
$(lastWizardHeaderButton).focus();
}
}
} else if (event.keyCode === 9) {
// Move forwards
// If taget is last button then goto first element
if(lastWizardFooterBtn && $(lastWizardFooterBtn).is($(event.target))) {
$(firstWizardHeaderButton).focus();
event.preventDefault();
event.stopPropagation();
}else if (event.target.innerText == 'Name'){
if ($(gridElement).find('.custom-control-input').is(':visible')){
$(gridElement).find('.custom-control-input').focus();
}else {
$(firstWizardFooterBtn).focus();
}
event.preventDefault();
event.stopPropagation();
} else if(event.target.tagName == 'DIV') {
$(event.currentTarget).find('.custom-control-input:first').trigger('focus');
event.preventDefault();
event.stopPropagation();
} else if(event.target.tagName == 'TEXTAREA'){
$(firstWizardFooterBtn).focus();
}
} else if (event.keyCode === 27){
//close the wizard when esc key is pressed
$(wizardHeader).find('button.ajs-close').click();
}
}
export function findAndSetFocus(container) {
if (container.length == 0) {
return;
}
setTimeout(function() {
var first_el = container
.find('button.fa-plus:first');
/* Adding the tabindex condition makes sure that
* when testing accessibility it works consistently across all
* browser. For eg, in safari focus() works only when element has
* tabindex="0", whereas in Chrome it works in any case
*/
if (first_el.length == 0) {
first_el = container
.find(`
.pgadmin-controls:first .btn:not(.toggle),
.pgadmin-controls:first,
.ajs-commands:first,
.CodeMirror-scroll,
.pgadmin-wizard`)
.find('*[tabindex]:not([tabindex="-1"]),input:enabled');
}
if(first_el.length > 0) {
first_el[0].focus();
} else {
container[0].focus();
}
}, 200);
}
let isValidData = (data) => (!_.isUndefined(data) && !_.isNull(data));
let isFunction = (fn) => (_.isFunction(fn));
let isString = (str) => (_.isString(str));
export {
isValidData, isFunction, isString,
};
export function getEpoch(inp_date) {
let date_obj = inp_date ? inp_date : new Date();
return parseInt(date_obj.getTime()/1000);
}
/* Eucladian GCD */
export function getGCD(inp_arr) {
let gcd_for_two = (a, b) => {
return a == 0?b:gcd_for_two(b % a, a);
};
let inp_len = inp_arr.length;
if(inp_len <= 2) {
return gcd_for_two(inp_arr[0], inp_arr[1]);
}
let result = inp_arr[0];
for(let i=1; i<inp_len; i++) {
result = gcd_for_two(inp_arr[i], result);
}
return result;
}
export function getMod(no, divisor) {
return ((no % divisor) + divisor) % divisor;
}
export function parseFuncParams(label) {
let paramArr = [],
funcName = '',
paramStr = '';
if(label.endsWith('()')) {
funcName = label.substring(0, label.length-2);
} else if(!label.endsWith(')')) {
funcName = label;
} else if(!label.endsWith('()') && label.endsWith(')')) {
let i = 0,
startBracketPos = label.length;
/* Parse through the characters in reverse to find the param start bracket */
i = label.length-2;
while(i >= 0) {
if(label[i] == '(') {
startBracketPos = i;
break;
} else if(label[i] == '"') {
/* If quotes, skip all the chars till next quote */
i--;
while(label[i] != '"') i--;
}
i--;
}
funcName = label.substring(0, startBracketPos);
paramStr = label.substring(startBracketPos+1, label.length-1);
let paramStart = 0,
paramName = '',
paramModes = ['IN', 'OUT', 'INOUT', 'VARIADIC'];
i = 0;
while(i < paramStr.length) {
if(paramStr[i] == '"') {
/* If quotes, skip all the chars till next quote */
i++;
while(paramStr[i] != '"') i++;
} else if (paramStr[i] == ' ') {
/* if paramName is already set, ignore till comma
* Or if paramName is parsed as one of the modes, reset.
*/
if(paramName == '' || paramModes.indexOf(paramName) > -1 ) {
paramName = paramStr.substring(paramStart, i);
paramStart = i+1;
}
}
else if (paramStr[i] == ',') {
paramArr.push([paramName, paramStr.substring(paramStart, i)]);
paramName = '';
paramStart = i+1;
}
i++;
}
paramArr.push([paramName, paramStr.substring(paramStart)]);
}
return {
'func_name': funcName,
'param_string': paramStr,
'params': paramArr,
};
}
export function quote_ident(value) {
/* check if the string is number or not */
let quoteIt = false;
if (!isNaN(parseInt(value))){
quoteIt = true;
}
if(value.search(/[^a-z0-9_]/g) > -1) {
/* escape double quotes */
value = value.replace(/"/g, '""');
quoteIt = true;
}
if(quoteIt) {
return `"${value}"`;
} else {
return value;
}
}
export function fully_qualify(pgBrowser, data, item) {
const parentData = pgBrowser.tree.getTreeNodeHierarchy(item);
let namespace = '';
if (parentData.schema !== undefined) {
namespace = quote_ident(parentData.schema._label);
}
else if (parentData.view !== undefined) {
namespace = quote_ident(parentData.view._label);
}
else if (parentData.catalog !== undefined) {
namespace = quote_ident(parentData.catalog._label);
}
if (parentData.package !== undefined && data._type != 'package') {
if(namespace == '') {
namespace = quote_ident(parentData.package._label);
} else {
namespace += '.' + quote_ident(parentData.package._label);
}
}
if(namespace != '') {
return namespace + '.' + quote_ident(data._label);
} else {
return quote_ident(data._label);
}
}
export function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
export function titleize(i_str) {
if(i_str === '' || i_str === null) return i_str;
return i_str.split(' ')
.map(w => w[0].toUpperCase() + w.substr(1).toLowerCase())
.join(' ');
}
export function sprintf(i_str) {
try {
let replaceArgs = arguments;
return i_str.split('%s')
.map(function(w, i) {
if(i > 0) {
if(i < replaceArgs.length) {
return [replaceArgs[i], w].join('');
} else {
return ['%s', w].join('');
}
} else {
return w;
}
})
.join('');
} catch(e) {
console.error(e);
return i_str;
}
}
// Modified ref: http://stackoverflow.com/a/1293163/2343 to suite pgAdmin.
// This will parse a delimited string into an array of arrays.
export function CSVToArray( strData, strDelimiter, quoteChar){
strDelimiter = strDelimiter || ',';
quoteChar = quoteChar || '"';
// Create a regular expression to parse the CSV values.
var objPattern = new RegExp(
(
// Delimiters.
'(\\' + strDelimiter + '|\\r?\\n|\\r|^)' +
// Quoted fields.
(quoteChar == '"' ? '(?:"([^"]*(?:""[^"]*)*)"|' : '(?:\'([^\']*(?:\'\'[^\']*)*)\'|') +
// Standard fields.
(quoteChar == '"' ? '([^"\\' + strDelimiter + '\\r\\n]*))': '([^\'\\' + strDelimiter + '\\r\\n]*))')
),
'gi'
);
// Create an array to hold our data. Give the array
// a default empty first row.
var arrData = [[]];
// Create an array to hold our individual pattern
// matching groups.
var arrMatches = null;
// Keep looping over the regular expression matches
// until we can no longer find a match.
while ((arrMatches = objPattern.exec( strData ))){
// Get the delimiter that was found.
var strMatchedDelimiter = arrMatches[ 1 ];
// Check to see if the given delimiter has a length
// (is not the start of string) and if it matches
// field delimiter. If id does not, then we know
// that this delimiter is a row delimiter.
if (strMatchedDelimiter.length && strMatchedDelimiter !== strDelimiter){
// Since we have reached a new row of data,
// add an empty row to our data array.
arrData.push( [] );
}
var strMatchedValue;
// Now that we have our delimiter out of the way,
// let's check to see which kind of value we
// captured (quoted or unquoted).
if (arrMatches[ 2 ]){
// We found a quoted value. When we capture
// this value, unescape any quotes.
strMatchedValue = arrMatches[ 2 ].replace(new RegExp( quoteChar+quoteChar, 'g' ), quoteChar);
} else {
// We found a non-quoted value.
strMatchedValue = arrMatches[ 3 ];
}
// Now that we have our value string, let's add
// it to the data array.
arrData[ arrData.length - 1 ].push( strMatchedValue );
}
// Return the parsed data.
return arrData;
}
export function hasBinariesConfiguration(pgBrowser, serverInformation) {
const module = 'paths';
let preference_name = 'pg_bin_dir';
let msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
if ((serverInformation.type && serverInformation.type === 'ppas') ||
serverInformation.server_type === 'ppas') {
preference_name = 'ppas_bin_dir';
msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
}
const preference = pgBrowser.get_preference(module, preference_name);
if (preference) {
if (_.isUndefined(preference.value) || !checkBinaryPathExists(preference.value, serverInformation.version)) {
Notify.alert(gettext('Configuration required'), msg);
return false;
}
} else {
Notify.alert(
gettext('Preferences Error'),
gettext('Failed to load preference %s of module %s', preference_name, module)
);
return false;
}
return true;
}
function checkBinaryPathExists(binaryPathArray, selectedServerVersion) {
let foundDefaultPath = false,
serverSpecificPathExist = false,
binPathArray = JSON.parse(binaryPathArray);
_.each(binPathArray, function(binPath) {
if (selectedServerVersion >= binPath.version && selectedServerVersion < binPath.next_major_version) {
if (!_.isUndefined(binPath.binaryPath) && !_.isNull(binPath.binaryPath) && binPath.binaryPath.trim() !== '')
serverSpecificPathExist = true;
}
// Check for default path
if (binPath.isDefault) {
foundDefaultPath = true;
}
});
return (serverSpecificPathExist | foundDefaultPath);
}
/* If a function, then evaluate */
export function evalFunc(obj, func, param) {
if(_.isFunction(func)) {
return func.apply(obj, [param]);
}
return func;
}
export function registerDetachEvent(panel){
panel.on(wcDocker.EVENT.DETACHED, function() {
$((this.$container)[0].ownerDocument).find('.wcIFrameFloating').attr({
style: 'z-index: 1200'
});
});
panel.on(wcDocker.EVENT.ORDER_CHANGED, function() {
$((this.$container)[0].ownerDocument).find('.wcIFrameFloating').attr({
style: 'z-index: 1200'
});
});
panel.on(wcDocker.EVENT.ORDER_CHANGED, function() {
var docker = this.docker(this._panel);
var dockerPos = docker.$container.offset();
var pos = this.$container.offset();
var width = this.$container.width();
var height = this.$container.height();
$((this.$container)[0].ownerDocument).find('.wcIFrameFloating').css('top', pos.top - dockerPos.top);
$((this.$container)[0].ownerDocument).find('.wcIFrameFloating').css('left', pos.left - dockerPos.left);
$((this.$container)[0].ownerDocument).find('.wcIFrameFloating').css('width', width);
$((this.$container)[0].ownerDocument).find('.wcIFrameFloating').find('.wcIFrameFloating').css('height', height);
});
}