Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
utils.js644 linesDownload Raw Back to js
1//////////////////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////////////////9 10import _ from 'lodash';11import gettext from 'sources/gettext';12import { hasTrojanSource } from 'anti-trojan-source';13import convert from 'convert-units';14import getApiInstance from './api_instance';15import usePreferences from '../../preferences/static/js/store';16import pgAdmin from 'sources/pgadmin';17import { isMac } from './keyboard_shortcuts';18 19export function parseShortcutValue(obj) {20  let shortcut = '';21  if (!obj){22    return null;23  }24  if (obj.alt) { shortcut += 'alt+'; }25  if (obj.shift) { shortcut += 'shift+'; }26  if (obj.control) { shortcut += 'ctrl+'; }27  shortcut += obj?.key.char?.toLowerCase();28  return shortcut;29}30 31export function isShortcutValue(obj) {32  if(!obj) return false;33  return [obj.alt, obj.control, obj?.key, obj?.key?.char].every((k)=>!_.isUndefined(k));34}35 36// Convert shortcut obj to codemirror key format37export function toCodeMirrorKey(obj) {38  let shortcut = '';39  if (!obj){40    return shortcut;41  }42  if (obj.alt) { shortcut += 'Alt-'; }43  if (obj.shift) { shortcut += 'Shift-'; }44  if (obj.control) {45    if(isMac() && obj.ctrl_is_meta) {46      shortcut += 'Meta-';47    } else {48      shortcut += 'Ctrl-';49    }50  }51  if(obj?.key.char?.length == 1) {52    shortcut += obj?.key.char?.toLowerCase();53  } else {54    shortcut += obj?.key.char;55  }56  return shortcut;57}58 59export function getEpoch(inp_date) {60  let date_obj = inp_date || new Date();61  return parseInt(date_obj.getTime()/1000);62}63 64/* Eucladian GCD */65export function getGCD(inp_arr) {66  let gcd_for_two = (a, b) => {67    return a == 0?b:gcd_for_two(b % a, a);68  };69 70  let inp_len = inp_arr.length;71  if(inp_len <= 2) {72    return gcd_for_two(inp_arr[0], inp_arr[1]);73  }74 75  let result = inp_arr[0];76  for(let i=1; i<inp_len; i++) {77    result = gcd_for_two(inp_arr[i], result);78  }79 80  return result;81}82 83export function getMod(no, divisor) {84  return ((no % divisor) + divisor) % divisor;85}86 87export function parseFuncParams(label) {88  let paramArr = [],89    funcName = '',90    paramStr = '';91 92  if(label.endsWith('()')) {93    funcName = label.substring(0, label.length-2);94  } else if(!label.endsWith(')')) {95    funcName = label;96  } else if(!label.endsWith('()') && label.endsWith(')')) {97    let i = 0,98      startBracketPos = label.length;99 100    /* Parse through the characters in reverse to find the param start bracket */101    i = label.length-2;102    while(i >= 0) {103      if(label[i] == '(') {104        startBracketPos = i;105        break;106      } else if(label[i] == '"') {107        /* If quotes, skip all the chars till next quote */108        i--;109        while(label[i] != '"') i--;110      }111      i--;112    }113 114    funcName = label.substring(0, startBracketPos);115    paramStr = label.substring(startBracketPos+1, label.length-1);116 117    let paramStart = 0,118      paramName = '',119      paramModes = ['IN', 'OUT', 'INOUT', 'VARIADIC'];120 121    i = 0;122    while(i < paramStr.length) {123      if(paramStr[i] == '"') {124        /* If quotes, skip all the chars till next quote */125        i++;126        while(paramStr[i] != '"') i++;127      } else if (paramStr[i] == ' ') {128        /* if paramName is already set, ignore till comma129         * Or if paramName is parsed as one of the modes, reset.130         */131        if(paramName == '' || paramModes.indexOf(paramName) > -1 ) {132          paramName = paramStr.substring(paramStart, i);133          paramStart = i+1;134        }135      }136      else if (paramStr[i] == ',') {137        paramArr.push([paramName, paramStr.substring(paramStart, i)]);138        paramName = '';139        paramStart = i+1;140      }141      i++;142    }143    paramArr.push([paramName, paramStr.substring(paramStart)]);144  }145 146  return {147    'func_name': funcName,148    'param_string': paramStr,149    'params': paramArr,150  };151}152 153export function quote_ident(value) {154  /* check if the string is number or not */155  let quoteIt = false;156  if (!isNaN(parseInt(value))){157    quoteIt = true;158  }159 160  if(value.search(/[^a-z0-9_]/g) > -1) {161    /* escape double quotes */162    value = value.replace(/"/g, '""');163    quoteIt = true;164  }165 166  if(quoteIt) {167    return `"${value}"`;168  } else {169    return value;170  }171}172 173export function fully_qualify(pgBrowser, data, item) {174  const parentData = pgBrowser.tree.getTreeNodeHierarchy(item);175  let namespace = '';176 177  if (parentData.schema !== undefined) {178    namespace = quote_ident(parentData.schema._label);179  }180  else if (parentData.view !== undefined) {181    namespace = quote_ident(parentData.view._label);182  }183  else if (parentData.catalog !== undefined) {184    namespace = quote_ident(parentData.catalog._label);185  }186 187  if (parentData.package !== undefined && data._type != 'package') {188    if(namespace == '') {189      namespace = quote_ident(parentData.package._label);190    } else {191      namespace += '.' + quote_ident(parentData.package._label);192    }193  }194 195  if(namespace != '') {196    return namespace + '.' + quote_ident(data._label);197  } else {198    return quote_ident(data._label);199  }200}201 202export function getRandomInt(min, max) {203  const intArray = new Uint32Array(1);204  crypto.getRandomValues(intArray);205 206  let range = max - min + 1;207  return min + (intArray[0] % range);208}209 210export function titleize(i_str) {211  if(i_str === '' || i_str === null) return i_str;212  return i_str.split(' ')213    .map(w => w[0].toUpperCase() + w.substr(1).toLowerCase())214    .join(' ');215}216 217export function sprintf(i_str) {218  try {219    let replaceArgs = arguments;220    return i_str.split('%s')221      .map(function(w, i) {222        if(i > 0) {223          if(i < replaceArgs.length) {224            return [replaceArgs[i], w].join('');225          } else {226            return ['%s', w].join('');227          }228        } else {229          return w;230        }231      })232      .join('');233  } catch(e) {234    console.error(e);235    return i_str;236  }237}238 239// Modified ref: http://stackoverflow.com/a/1293163/2343 to suite pgAdmin.240// This will parse a delimited string into an array of arrays.241export function CSVToArray(strData, strDelimiter, quoteChar){242  strDelimiter = strDelimiter || ',';243  quoteChar = quoteChar || '"';244 245  // Create a regular expression to parse the CSV values.246  let objPattern = new RegExp(247    (248    // Delimiters.249      '(\\' + strDelimiter + '|\\r?\\n|\\r|^)' +250            // Quoted fields.251            (quoteChar == '"' ? '(?:"([^"]*(?:""[^"]*)*)"|' : '(?:\'([^\']*(?:\'\'[^\']*)*)\'|') +252            // Standard fields.253            (quoteChar == '"' ? '([^"\\' + strDelimiter + '\\r\\n]*))': '([^\'\\' + strDelimiter + '\\r\\n]*))')254    ),255    'gi'256  );257 258  // Create an array to hold our data. Give the array259  // a default empty first row.260  let arrData = [[]];261 262  // The regex doesn't handle and skips start value if263  // string starts with delimiter264  if(strData.startsWith(strDelimiter)) {265    arrData[ arrData.length - 1 ].push(null);266  }267 268  // Create an array to hold our individual pattern269  // matching groups.270  let arrMatches = null;271 272  // Keep looping over the regular expression matches273  // until we can no longer find a match.274  while ((arrMatches = objPattern.exec( strData ))){275    // Get the delimiter that was found.276    let strMatchedDelimiter = arrMatches[ 1 ];277 278    // Check to see if the given delimiter has a length279    // (is not the start of string) and if it matches280    // field delimiter. If id does not, then we know281    // that this delimiter is a row delimiter.282    if (strMatchedDelimiter.length && strMatchedDelimiter !== strDelimiter){283      // Since we have reached a new row of data,284      // add an empty row to our data array.285      arrData.push( [] );286    }287 288    let strMatchedValue;289 290    // Now that we have our delimiter out of the way,291    // let's check to see which kind of value we292    // captured (quoted or unquoted).293    if (arrMatches[ 2 ]){294      // We found a quoted value. When we capture295      // this value, unescape any quotes.296      strMatchedValue = arrMatches[ 2 ].replace(new RegExp( quoteChar+quoteChar, 'g' ), quoteChar);297    } else {298      // We found a non-quoted value.299      strMatchedValue = arrMatches[ 3 ];300    }301    // Now that we have our value string, let's add302    // it to the data array.303    arrData[ arrData.length - 1 ].push( strMatchedValue );304  }305  // Return the parsed data.306  return arrData;307}308 309export function hasBinariesConfiguration(pgBrowser, serverInformation) {310  const module = 'paths';311  let preference_name = 'pg_bin_dir';312  let msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');313 314  if ((serverInformation.type && serverInformation.type === 'ppas') ||315    serverInformation.server_type === 'ppas') {316    preference_name = 'ppas_bin_dir';317    msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');318  }319 320  const preference = usePreferences.getState().getPreferences(module, preference_name);321 322  if (preference) {323    if (_.isUndefined(preference.value) || !checkBinaryPathExists(preference.value, serverInformation.version)) {324      pgAdmin.Browser.notifier.alert(gettext('Configuration required'), msg);325      return false;326    }327  } else {328    pgAdmin.Browser.notifier.alert(329      gettext('Preferences Error'),330      gettext('Failed to load preference %s of module %s', preference_name, module)331    );332    return false;333  }334  return true;335}336 337function checkBinaryPathExists(binaryPathArray, selectedServerVersion) {338  let foundDefaultPath = false,339    serverSpecificPathExist = false,340    binPathArray = JSON.parse(binaryPathArray);341 342  _.each(binPathArray, function(binPath) {343    if (selectedServerVersion >= binPath.version && selectedServerVersion < binPath.next_major_version) {344      if (!_.isUndefined(binPath.binaryPath) && !_.isNull(binPath.binaryPath) && binPath.binaryPath.trim() !== '')345        serverSpecificPathExist = true;346    }347 348    // Check for default path349    if (binPath.isDefault) {350      foundDefaultPath = true;351    }352  });353 354  return (serverSpecificPathExist | foundDefaultPath);355}356 357/* If a function, then evaluate */358export function evalFunc(obj, func, ...param) {359  if(_.isFunction(func)) {360    return func.apply(obj, [...param]);361  }362  return func;363}364 365export function getBrowser() {366  let ua=navigator.userAgent,tem,M=ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];367  if(/trident/i.test(M[1])) {368    tem=/\brv[ :]+(\d+)/g.exec(ua) || [];369    return {name:'IE', version:(tem[1]||'')};370  }371  if(ua.startsWith('Nwjs')) {372    let nwjs = ua.split('-')[0]?.split(':');373    return {name:nwjs[0], version: nwjs[1]};374  }375 376  if(M[1]==='Chrome') {377    tem=ua.match(/\bOPR|Edge\/(\d+)/);378    if(tem!=null) {return {name:tem[0], version:tem[1]};}379  }380 381  M=M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];382  if((tem=ua.match(/version\/(\d+)/i))!=null) {M.splice(1,1,tem[1]);}383  return {384    name: M[0],385    version: M[1],386  };387}388 389export function checkTrojanSource(content, isPasteEvent) {390  // Call the hasTrojanSource function of 'anti-trojan-source' package391  if (hasTrojanSource({ sourceText: content})) {392    let msg = gettext('The file opened contains bidirectional Unicode characters which could be interpreted differently than what is displayed. If this is unexpected it is recommended that you review the text in an application that can display hidden Unicode characters before proceeding.');393    if (isPasteEvent) {394      msg = gettext('The pasted text contains bidirectional Unicode characters which could be interpreted differently than what is displayed. If this is unexpected it is recommended that you review the text in an application that can display hidden Unicode characters before proceeding.');395    }396    pgAdmin.Browser.notifier.alert(gettext('Trojan Source Warning'), msg);397  }398}399 400export function downloadBlob(blob, fileName) {401  let urlCreator = window.URL || window.webkitURL,402    downloadUrl = urlCreator.createObjectURL(blob),403    link = document.createElement('a');404 405  document.body.appendChild(link);406 407  if (getBrowser() == 'IE' && window.navigator.msSaveBlob) {408  // IE10+ : (has Blob, but not a[download] or URL)409    window.navigator.msSaveBlob(blob, fileName);410  } else {411    link.setAttribute('href', downloadUrl);412    link.setAttribute('download', fileName);413    link.click();414  }415  document.body.removeChild(link);416}417 418export function toPrettySize(rawSize, from='B') {419  try {420    //if the integer need to be converted to K for thousands, M for millions , B for billions only421    if (from == '') {422      return Intl.NumberFormat('en', {notation: 'compact'}).format(rawSize);423    }424    let conVal = convert(rawSize).from(from).toBest();425    conVal.val = Math.round(conVal.val * 100) / 100;426    return `${conVal.val} ${conVal.unit}`;427  }428  catch {429    return '';430  }431}432 433export function compareSizeVals(val1, val2) {434  const parseAndConvert = (val)=>{435    try {436      let [size, unit] = val.split(' ');437      return convert(size).from(unit.toUpperCase()).to('B');438    } catch {439      return -1;440    }441  };442  val1 = parseAndConvert(val1);443  val2 = parseAndConvert(val2);444  if(val1 > val2) return 1;445  return (val1 < val2 ? -1 : 0);446}447 448export function calcFontSize(fontSize) {449  if(fontSize) {450    fontSize = parseFloat((Math.round(parseFloat(fontSize + 'e+2')) + 'e-2'));451    let rounded = Number(fontSize);452    if(rounded > 0) {453      return rounded + 'em';454    }455  }456  return '1em';457}458 459export function pgHandleItemError(error, args) {460  let pgBrowser = window.pgAdmin.Browser;461 462  if (!error || !pgBrowser) {463    return;464  }465 466  if(error.response.headers['content-type'] == 'application/json') {467    let jsonResp = error.response.data;468    if (469      jsonResp && (470        error.response.status == 503 ? (471          jsonResp.info == 'CONNECTION_LOST' &&472          'server' in args.info && jsonResp.data.sid >= 0 &&473          jsonResp.data.sid == args.info.server._id474        ) : (475          error.response.status == 428 &&476          jsonResp.errormsg &&477          jsonResp.errormsg == gettext('Connection to the server has been lost.')478        )479      )480    ) {481      if (482        args.preHandleConnectionLost &&483        typeof(args.preHandleConnectionLost) == 'function'484      ) {485        args.preHandleConnectionLost.apply(this, arguments);486      }487 488      // Check the status of the maintenance server connection.489      let server = pgBrowser.Nodes['server'],490        ctx = {491          resp: jsonResp,492          error: error,493          args: args,494        },495        reconnectServer = function() {496          let ctx_local = this,497            onServerConnect = function(_sid, _i, _d) {498              // Yay - server is reconnected.499              if (this.args.info.server._id == _sid) {500                pgBrowser.Events.off(501                  'pgadmin:server:connected', onServerConnect502                );503                pgBrowser.Events.off(504                  'pgadmin:server:connect:cancelled', onConnectCancel505                );506 507                // Do we need to connect the disconnected server now?508                if (509                  this.resp.data.database &&510                  this.resp.data.database != _d.db511                ) {512                  // Server is connected now, we will need to inform the513                  // database to connect it now.514                  pgBrowser.Events.trigger(515                    'pgadmin:database:connection:lost', this.args.item,516                    this.resp, true517                  );518                }519              }520            }.bind(ctx_local),521            onConnectCancel = function(_sid, _item, _data) {522              // User has cancelled the operation in between.523              if (_sid == this.args.info.server.id) {524                pgBrowser.Events.off('pgadmin:server:connected', onServerConnect);525                pgBrowser.Events.off('pgadmin:server:connect:cancelled', onConnectCancel);526 527                // Connection to the database will also be cancelled528                pgBrowser.Events.trigger(529                  'pgadmin:database:connect:cancelled', _sid,530                  this.resp.data.database || _data.db, _item, _data531                );532              }533            }.bind(ctx_local);534 535          pgBrowser.Events.on('pgadmin:server:connected', onServerConnect);536          pgBrowser.Events.on('pgadmin:server:connect:cancelled', onConnectCancel);537 538          // Connection to the server has been lost, we need to inform the539          // server first to take the action first.540          pgBrowser.Events.trigger(541            'pgadmin:server:connection:lost', this.args.item, this.resp542          );543        }.bind(ctx);544 545      getApiInstance().get(server.generate_url(546        null, 'connect', args.info.server, true, args.info547      ))548        .then(({data: res})=>{549          if (res.success && 'connected' in res.data) {550            if (res.data.connected) {551              // Server is connected, but - the connection with the552              // particular database has been lost.553              pgBrowser.Events.trigger(554                'pgadmin:database:connection:lost', args.item, jsonResp555              );556              return;557            }558          }559 560          // Server was not connected, we should first try to connect561          // the server.562          reconnectServer();563        })564        .catch(()=>{565          reconnectServer();566        });567      return true;568    } else if (jsonResp && jsonResp.info == 'CRYPTKEY_MISSING' && error.response.status == 503) {569      /* Suppress the error here and handle in pgNotifier wherever570       * required, as it has callback option571       */572      return false;573    }574  }575 576  return false;577}578 579export function fullHexColor(shortHex) {580  if(shortHex?.length == 4) {581    return shortHex.replace(RegExp('#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])'), '#$1$1$2$2$3$3').toUpperCase();582  }583  return shortHex;584}585 586export function gettextForTranslation(translations, ...replaceArgs) {587  const text = replaceArgs[0];588  let rawTranslation = translations[text] ? translations[text] : text;589 590  if(arguments.length == 2) {591    return rawTranslation;592  }593 594  try {595    return rawTranslation.split('%s')596      .map(function(w, i) {597        if(i > 0) {598          if(i < replaceArgs.length) {599            return [replaceArgs[i], w].join('');600          } else {601            return ['%s', w].join('');602          }603        } else {604          return w;605        }606      })607      .join('');608  } catch(e) {609    console.error(e);610    return rawTranslation;611  }612}613 614// https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame615const requestAnimationFrame =616  window.requestAnimationFrame ||617  window.mozRequestAnimationFrame ||618  window.webkitRequestAnimationFrame ||619  window.msRequestAnimationFrame;620 621const cancelAnimationFrame =622  window.cancelAnimationFrame || window.mozCancelAnimationFrame;623 624/* Usefull in focussing an element after it appears on the screen */625export function requestAnimationAndFocus(ele) {626  if(!ele) return;627 628  const animateId = requestAnimationFrame(()=>{629    ele?.focus?.();630    cancelAnimationFrame(animateId);631  });632}633 634 635export function scrollbarWidth() {636  // thanks too https://davidwalsh.name/detect-scrollbar-width637  const scrollDiv = document.createElement('div');638  scrollDiv.setAttribute('style', 'width: 100px; height: 100px; overflow: scroll; position:absolute; top:-9999px;');639  document.body.appendChild(scrollDiv);640  const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;641  document.body.removeChild(scrollDiv);642  return scrollbarWidth;643}644 
codekingpro/portable-devtools · Team Ai