Remove all traces of Backbone, Underscore. #5493

pull/5566/head
Aditya Toshniwal 2022-11-21 10:54:15 +05:30 committed by GitHub
parent 5dca51637c
commit 8d74de09cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 77 additions and 2348 deletions

View File

@ -200,6 +200,7 @@ babel-plugin-styled-components 1.13.3
babel-plugin-syntax-jsx 6.18.0 MIT https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-jsx
babelify 10.0.0 MIT https://github.com/babel/babelify.git
backbone 1.4.0 MIT https://github.com/jashkenas/backbone.git
backo2 1.0.2 MIT https://github.com/mokesmokes/backo.git
balanced-match 1.0.2 MIT git://github.com/juliangruber/balanced-match.git
base64-arraybuffer 0.2.0 MIT https://github.com/niklasvh/base64-arraybuffer
base64-arraybuffer 1.0.1 MIT https://github.com/niklasvh/base64-arraybuffer
@ -516,7 +517,6 @@ to-fast-properties 2.0.0
to-regex-range 5.0.1 MIT https://github.com/micromatch/to-regex-range.git
trim-newlines 4.0.2 MIT https://github.com/sindresorhus/trim-newlines.git
unc-path-regex 0.1.2 MIT https://github.com/regexhq/unc-path-regex.git
underscore 1.13.1 MIT git://github.com/jashkenas/underscore.git
uniqs 2.0.0 MIT git://github.com/fgnass/uniqs.git
url-loader 1.1.2 MIT https://github.com/webpack-contrib/url-loader.git
util-deprecate 1.0.2 MIT git://github.com/TooTallNate/util-deprecate.git

View File

@ -207,8 +207,8 @@ Front End
pgAdmin uses javascript extensively for the front-end implementation. It uses
require.js to allow the lazy loading (or, say load only when required),
bootstrap for UI look and feel, Backbone for data manipulation of a node,
React for generating properties/create dialog for selected node. We have
bootstrap and MaterialUI for UI look and feel, React for generating
properties/create dialog for selected node. We have
divided each module in small chunks as much as possible. Not all javascript
modules are required to be loaded (i.e. loading a javascript module for
database will make sense only when a server node is loaded completely.) Please

View File

@ -80,9 +80,12 @@ following options (in alphabetical order):
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
| *View Data* | Click to access a context menu that provides several options for viewing data (see below). |
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
| *Generate ERD* | Click to open the ERD tool with automatically generated diagram for the database selected. |
| *ERD For Database* | Click to open the ERD tool with automatically generated diagram for the database selected. |
| | This option is available only when a database is selected. Options are displayed on the flyout menu. |
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
| *ERD For Table* | Click to open the ERD tool with automatically generated diagram for the table selected. |
| | This option is available only when a table is selected. Options are displayed on the flyout menu. |
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
The Tools Menu
**************

View File

@ -101,7 +101,6 @@
"aspen-decorations": "^1.0.2",
"axios": "^0.21.1",
"babelify": "~10.0.0",
"backbone": "1.4.0",
"bignumber.js": "^9.0.1",
"bootstrap": "^4.3.1",
"bootstrap-datepicker": "^1.8.0",
@ -169,7 +168,6 @@
"styled-components": "^5.2.1",
"tempusdominus-bootstrap-4": "^5.1.2",
"tempusdominus-core": "^5.19.3",
"underscore": "^1.13.1",
"valid-filename": "^2.0.1",
"webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#3df8aac825ee2892f4d824de273b779cc6dbcad8",
"wkx": "^0.5.0",

View File

@ -92,10 +92,6 @@ define('pgadmin.node.extension', [
]);
},
/*
* Define model for the Node and specify the properties
* of the model in schema.
*/
getSchema: (treeNodeInfo, itemNodeData)=>{
let nodeObj = pgAdmin.Browser.Nodes['extension'];
return new ExtensionsSchema(

View File

@ -48,19 +48,6 @@ define('pgadmin.node.edbfunc', [
},
canDrop: false,
canDropCascade: false,
model: pgBrowser.Node.Model.extend({
idAttribute: 'oid',
schema: [{
id: 'name', label: gettext('Name'), cell: 'string',
type: 'text', mode: ['properties'],
},{
id: 'oid', label: gettext('OID'), cell: 'string',
type: 'text' , mode: ['properties'],
},{
id: 'funcowner', label: gettext('Owner'), cell: 'string',
type: 'text', readonly: true,
}]
}),
getSchema: () => {
return new EDBFuncSchema(
{}, {

View File

@ -8,15 +8,14 @@
//////////////////////////////////////////////////////////////
import EDBFuncSchema from './edbfunc.ui';
import _ from 'lodash';
/* Create and Register Procedure Collection and Node. */
define('pgadmin.node.edbproc', [
'sources/gettext', 'sources/url_for',
'sources/pgadmin', 'pgadmin.browser',
'pgadmin.node.edbfunc', 'pgadmin.browser.collection',
'pgadmin.browser.collection',
], function(
gettext, url_for, pgAdmin, pgBrowser, EdbFunction
gettext, url_for, pgAdmin, pgBrowser
) {
if (!pgBrowser.Nodes['coll-edbproc']) {
@ -54,24 +53,6 @@ define('pgadmin.node.edbproc', [
},
canDrop: false,
canDropCascade: false,
model: EdbFunction.model.extend({
defaults: _.extend({},
EdbFunction.model.prototype.defaults,
{
lanname: 'edbspl',
}
),
isVisible: function() {
if (this.name == 'sysfunc') { return false; }
else if (this.name == 'sysproc') { return true; }
return false;
},
validate: function()
{
return null;
},
}
),
getSchema: () => {
return new EDBFuncSchema(
{}, {

View File

@ -128,15 +128,15 @@ define('pgadmin.node.table', [
}
]);
pgBrowser.Events.on(
'pgadmin:browser:node:table:updated', this.onTableUpdated, this
'pgadmin:browser:node:table:updated', this.onTableUpdated.bind(this)
);
pgBrowser.Events.on(
'pgadmin:browser:node:type:cache_cleared',
this.handle_cache, this
this.handle_cache.bind(this)
);
pgBrowser.Events.on(
'pgadmin:browser:node:domain:cache_cleared',
this.handle_cache, this
this.handle_cache.bind(this)
);
},
callbacks: {
@ -305,50 +305,6 @@ define('pgadmin.node.table', [
getSchema: function(treeNodeInfo, itemNodeData) {
return getNodeTableSchema(treeNodeInfo, itemNodeData, pgBrowser);
},
model: pgBrowser.Node.Model.extend({
idAttribute: 'oid',
defaults: {
name: undefined,
oid: undefined,
relowner: undefined,
description: undefined,
is_partitioned: false,
},
schema: [{
id: 'name', label: gettext('Name'), type: 'text',
mode: ['properties', 'create', 'edit'], disabled: 'inSchema',
},{
id: 'oid', label: gettext('OID'), type: 'text', mode: ['properties'],
},{
id: 'relowner', label: gettext('Owner'), type: 'text', node: 'role',
mode: ['properties', 'create', 'edit'], select2: {allowClear: false},
disabled: 'inSchema', control: 'node-list-by-name',
},{
id: 'is_partitioned', label:gettext('Partitioned table?'), cell: 'switch',
type: 'switch', mode: ['properties', 'create', 'edit'],
visible: 'isVersionGreaterThan96',
readonly: function(m) {
return !m.isNew();
},
},{
id: 'description', label: gettext('Comment'), type: 'multiline',
mode: ['properties', 'create', 'edit'], disabled: 'inSchema',
}],
sessChanged: function() {
/* If only custom autovacuum option is enabled the check if the options table is also changed. */
if(_.size(this.sessAttrs) == 2 && this.sessAttrs['autovacuum_custom'] && this.sessAttrs['toast_autovacuum']) {
return this.get('vacuum_table').sessChanged() || this.get('vacuum_toast').sessChanged();
}
if(_.size(this.sessAttrs) == 1 && (this.sessAttrs['autovacuum_custom'] || this.sessAttrs['toast_autovacuum'])) {
return this.get('vacuum_table').sessChanged() || this.get('vacuum_toast').sessChanged();
}
return pgBrowser.DataModel.prototype.sessChanged.apply(this);
},
// We will disable everything if we are under catalog node
inSchema: function() {
return this.node_info && 'catalog' in this.node_info;
},
}),
// Check to whether table has disable trigger(s)
canCreate_with_trigger_enable: function(itemData, item, data) {
return itemData.tigger_count > 0 &&

View File

@ -442,7 +442,7 @@ define('pgadmin.node.database', [
} else {
Notify.success(res.info);
}
obj.trigger('connected', obj, _item, _data);
// obj.trigger('connected', obj, _item, _data);
pgBrowser.Events.trigger(
'pgadmin:database:connected', _item, _data
);

View File

@ -66,22 +66,6 @@ define('pgadmin.node.pga_schedule', [
getSchema: function() {
return new PgaJobScheduleSchema();
},
model: pgBrowser.Node.Model.extend({
idAttribute: 'jscid',
schema: [{
id: 'jscid', label: gettext('ID'), type: 'int',
cellHeaderClasses: 'width_percent_5', mode: ['properties'],
},{
id: 'jscname', label: gettext('Name'), type: 'text',
cellHeaderClasses: 'width_percent_45',
disabled: function() { return false; },
},{
id: 'jscenabled', label: gettext('Enabled?'), type: 'switch',
disabled: function() { return false; },
cellHeaderClasses: 'width_percent_5',
}],
}),
});
}

View File

@ -83,31 +83,6 @@ define('pgadmin.node.pga_job', [
);
},
model: pgBrowser.Node.Model.extend({
idAttribute: 'jobid',
schema: [{
id: 'jobname', label: gettext('Name'), type: 'text',
cellHeaderClasses: 'width_percent_30',
},{
id: 'jobid', label: gettext('ID'), mode: ['properties'],
type: 'int',
},{
id: 'jobenabled', label: gettext('Enabled?'), type: 'switch',
cellHeaderClasses: 'width_percent_5',
},{
id: 'jobnextrun', type: 'text', mode: ['properties'],
label: gettext('Next run'), cellHeaderClasses: 'width_percent_20',
},{
id: 'joblastrun', type: 'text', mode: ['properties'],
label: gettext('Last run'), cellHeaderClasses: 'width_percent_20',
},{
id: 'jlgstatus', type: 'text', label: gettext('Last result'),
cellHeaderClasses: 'width_percent_5', mode: ['properties'],
},{
id: 'jobdesc', label: gettext('Comment'), type: 'multiline',
cellHeaderClasses: 'width_percent_15',
}],
}),
/* Run pgagent job now */
run_pga_job_now: function(args) {
let input = args || {},

View File

@ -8,7 +8,6 @@
//////////////////////////////////////////////////////////////
import { getNodePgaJobStepSchema } from './pga_jobstep.ui';
import _ from 'lodash';
define('pgadmin.node.pga_jobstep', [
'sources/gettext', 'sources/url_for', 'pgadmin.browser',
@ -74,25 +73,6 @@ define('pgadmin.node.pga_jobstep', [
return getNodePgaJobStepSchema(treeNodeInfo, itemNodeData);
},
model: pgBrowser.Node.Model.extend({
initialize: function() {
pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
if (this.isNew() && this.get('jstconntype')) {
let args = arguments.length > 1 && arguments[1];
if (args) {
if (!_.isUndefined(args['node_info']) ||
!_.isUndefined(args.collection.top['node_info'])) {
this.set(
'jstdbname',
(args['node_info'] || args.collection.top['node_info'])['server']['db']
);
}
}
}
},
idAttribute: 'jstid',
}),
});
}

View File

@ -81,22 +81,6 @@ define('pgadmin.node.resource_group', [
getSchema: ()=>{
return new ResourceGroupSchema();
},
// Defining model for resource group node
model: pgBrowser.Node.Model.extend({
idAttribute: 'oid',
// Defining schema for the resource group node
schema: [{
id: 'name', label: gettext('Name'), cell: 'string',
type: 'text',
}, {
id: 'cpu_rate_limit', label: gettext('CPU rate limit (percentage)'), cell: 'string',
type: 'numeric', min:0, max:16777216,
}, {
id: 'dirty_rate_limit', label: gettext('Dirty rate limit (KB)'), cell: 'string',
type: 'numeric', min:0, max:16777216,
}],
}),
});
}

View File

@ -682,7 +682,7 @@ define('pgadmin.node.server', [
// Check the database server against supported version.
checkSupportedVersion(_data.version, res.info);
obj.trigger('connected', obj, _item, _data);
// obj.trigger('connected', obj, _item, _data);
// Generate the event that server is connected
pgBrowser.Events.trigger(

View File

@ -11,7 +11,6 @@ import { getNodeListByName } from '../../../../../static/js/node_ajax';
import { getNodePrivilegeRoleSchema } from '../../../static/js/privilege.ui';
import { getNodeVariableSchema } from '../../../static/js/variable.ui';
import TablespaceSchema from './tablespace.ui';
import _ from 'lodash';
define('pgadmin.node.tablespace', [
'sources/gettext', 'sources/url_for',
@ -96,35 +95,6 @@ define('pgadmin.node.tablespace', [
}
);
},
model: pgBrowser.Node.Model.extend({
idAttribute: 'oid',
// Default values!
initialize: function(attrs, args) {
let isNew = (_.size(attrs) === 0);
if (isNew) {
let userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
this.set({'spcuser': userInfo.name}, {silent: true});
}
pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
},
schema: [
{
id: 'name', label: gettext('Name'), cell: 'string',
type: 'text',
}, {
id: 'spcuser', label: gettext('Owner'), cell: 'string',
type: 'text', control: 'node-list-by-name', node: 'role',
select2: {allowClear: false},
}, {
id: 'description', label: gettext('Comment'), cell: 'string',
type: 'multiline',
},
],
}),
});
}

View File

@ -554,11 +554,11 @@ define('pgadmin.browser', [
obj.set_master_password('');
obj.check_corrupted_db_file();
obj.Events.on('pgadmin:browser:tree:add', obj.onAddTreeNode, obj);
obj.Events.on('pgadmin:browser:tree:update', obj.onUpdateTreeNode, obj);
obj.Events.on('pgadmin:browser:tree:refresh', obj.onRefreshTreeNodeReact, obj);
obj.Events.on('pgadmin-browser:tree:loadfail', obj.onLoadFailNode, obj);
obj.Events.on('pgadmin-browser:panel-browser:' + wcDocker.EVENT.RESIZE_ENDED, obj.onResizeEnded, obj);
obj.Events.on('pgadmin:browser:tree:add', obj.onAddTreeNode.bind(obj));
obj.Events.on('pgadmin:browser:tree:update', obj.onUpdateTreeNode.bind(obj));
obj.Events.on('pgadmin:browser:tree:refresh', obj.onRefreshTreeNodeReact.bind(obj));
obj.Events.on('pgadmin-browser:tree:loadfail', obj.onLoadFailNode.bind(obj));
obj.Events.on('pgadmin-browser:panel-browser:' + wcDocker.EVENT.RESIZE_ENDED, obj.onResizeEnded.bind(obj));
obj.bind_beforeunload();
/* User UI activity */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import EventBus from '../../../static/js/helpers/EventBus';
import pgAdmin from 'sources/pgadmin';
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
pgBrowser.Events = new EventBus();
export default pgBrowser;

View File

@ -13,22 +13,15 @@ import _ from 'lodash';
define('pgadmin.browser.node', [
'sources/gettext', 'jquery', 'sources/pgadmin',
'backbone', 'pgadmin.browser.datamodel',
'sources/browser/generate_url', 'pgadmin.help', 'sources/utils',
'pgadmin.browser.utils',
'sources/browser/generate_url', 'sources/utils',
'pgadmin.browser.utils', 'pgadmin.browser.events',
], function(
gettext, $, pgAdmin,
Backbone, pgBrowser,
generateUrl, help,
commonUtils
gettext, $, pgAdmin, generateUrl, commonUtils
) {
let wcDocker = window.wcDocker,
keyCode = {
ENTER: 13,
ESCAPE: 27,
F1: 112,
};
let wcDocker = window.wcDocker;
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
// It has already been defined.
// Avoid running this script again.
@ -45,7 +38,6 @@ define('pgadmin.browser.node', [
// Uses a hash of class properties to be extended.
//
// It is unlikely - we will instantiate an object for this class.
// (Inspired by Backbone.extend function)
pgBrowser.Node.extend = function(props, initialize) {
let parent = this;
let child;
@ -83,7 +75,7 @@ define('pgadmin.browser.node', [
return child;
};
_.extend(pgAdmin.Browser.Node, Backbone.Events, {
_.extend(pgAdmin.Browser.Node, {
// Node type
type: undefined,
// Label
@ -332,20 +324,6 @@ define('pgadmin.browser.node', [
return new_panel;
},
onEventResizeEnded: function() {
let $container = this.$container.find('.obj_properties').first(),
v = $container.data('obj-view');
if (v && v.model && v.model) {
v.model.trigger(
'pg-browser-resized', {
'view': v,
'panel': this,
'container': $container,
});
}
},
registerDockerPanel: function(docker, name, params) {
let w = docker || pgBrowser.docker,
p = w.findPanels(name);
@ -373,7 +351,6 @@ define('pgadmin.browser.node', [
return;
let events = {};
events[wcDocker.EVENT.RESIZE_ENDED] = this.onEventResizeEnded;
p = new pgBrowser.Panel({
name: 'utility_props',
@ -399,7 +376,6 @@ define('pgadmin.browser.node', [
return;
let events = {};
events[wcDocker.EVENT.RESIZE_ENDED] = this.onEventResizeEnded;
p = new pgBrowser.Panel({
name: 'node_props',
@ -974,55 +950,10 @@ define('pgadmin.browser.node', [
**/
showProperties: function(item, data, panel, action) {
let that = this,
tree = pgAdmin.Browser.tree,
j = panel.$container.find('.obj_properties').first(),
view = j.data('obj-view'),
confirm_close = true;
// Handle key press events for Cancel, save and help button
let handleKeyDown = function(event, context) {
// If called on panel other than node_props, return
if (panel && panel['_type'] !== 'node_props') return;
switch (event.which) {
case keyCode.ESCAPE:
closePanel(true);
break;
case keyCode.ENTER:
// Return if event is fired from child element
if (event.target !== context) return;
if (view && view.model && view.model.sessChanged()) {
let btn = $(event.target).closest('.obj_properties')
.find('.pg-prop-btn-group')
.find('button.btn-primary');
onSave.call(this, view, btn);
}
break;
case keyCode.F1:
onDialogHelp();
break;
default:
break;
}
}.bind(panel);
setTimeout(function() {
// Register key press events with panel element
panel.$container.find('.backform-tab').on('keydown', function(event) {
handleKeyDown(event, this);
});
}, 200); // wait for panel tab to render
j = panel.$container.find('.obj_properties').first();
// Callback to show object properties
let properties = function() {
// Avoid unnecessary reloads
let i = tree.selected(),
treeHierarchy = tree.getTreeNodeHierarchy(i);
// Cache the current IDs for next time
$(this).data('node-prop', treeHierarchy);
let properties = function() {
/* Remove any dom rendered by getNodeView */
removeNodeView(j[0]);
let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item);
@ -1032,99 +963,6 @@ define('pgadmin.browser.node', [
return;
}.bind(panel),
onDialogHelp = function() {
window.open(that.dialogHelp, 'pgadmin_help');
}.bind(panel),
warnBeforeChangesLost = function(warn_text, yes_callback) {
let $props = this.$container.find('.obj_properties').first(),
objview = $props && $props.data('obj-view'),
self = this;
let confirm_on_properties_close = pgBrowser.get_preferences_for_module('browser').confirm_on_properties_close;
if (confirm_on_properties_close && confirm_close && objview && objview.model) {
if(objview.model.sessChanged()){
Notify.confirm(
gettext('Warning'),
warn_text,
function() {
setTimeout(function(){
yes_callback();
}.bind(self), 50);
return true;
},
function() {
return true;
}
);
} else {
return true;
}
} else {
yes_callback();
return true;
}
}.bind(panel),
onSave = function(_view, saveBtn) {
let m = _view.model,
d = m.toJSON(true),
// Generate a timer for the request
timer = setTimeout(function() {
$('.obj_properties').addClass('show_progress');
}, 1000);
// Prevent subsequent save operation by disabling Save button
if (saveBtn)
$(saveBtn).prop('disabled', true);
if (d && !_.isEmpty(d)) {
m.save({}, {
attrs: d,
validate: false,
cache: false,
wait: true,
success: function() {
onSaveFunc.call();
// Hide progress cursor
$('.obj_properties').removeClass('show_progress');
clearTimeout(timer);
// Removing the node-prop property of panel
// so that we show updated data on panel
let pnlProperties = pgBrowser.docker.findPanels('properties')[0],
pnlSql = pgBrowser.docker.findPanels('sql')[0],
pnlStats = pgBrowser.docker.findPanels('statistics')[0],
pnlDependencies = pgBrowser.docker.findPanels('dependencies')[0],
pnlDependents = pgBrowser.docker.findPanels('dependents')[0];
if (pnlProperties)
$(pnlProperties).removeData('node-prop');
if (pnlSql)
$(pnlSql).removeData('node-prop');
if (pnlStats)
$(pnlStats).removeData('node-prop');
if (pnlDependencies)
$(pnlDependencies).removeData('node-prop');
if (pnlDependents)
$(pnlDependents).removeData('node-prop');
},
error: function(_m, jqxhr) {
Notify.pgNotifier(
'error', jqxhr,
gettext('Error saving properties')
);
// Hide progress cursor
$('.obj_properties').removeClass('show_progress');
clearTimeout(timer);
if (saveBtn)
$(saveBtn).prop('disabled', false);
},
});
}
}.bind(panel),
editFunc = function() {
let self = this;
if (action && action == 'properties') {
@ -1146,25 +984,6 @@ define('pgadmin.browser.node', [
(nodeData)=>{
if(nodeData.node) {
onSaveFunc(nodeData.node, treeNodeInfo);
// Removing the node-prop property of panel
// so that we show updated data on panel
let pnlProperties = pgBrowser.docker.findPanels('properties')[0],
pnlSql = pgBrowser.docker.findPanels('sql')[0],
pnlStats = pgBrowser.docker.findPanels('statistics')[0],
pnlDependencies = pgBrowser.docker.findPanels('dependencies')[0],
pnlDependents = pgBrowser.docker.findPanels('dependents')[0];
if (pnlProperties)
$(pnlProperties).removeData('node-prop');
if (pnlSql)
$(pnlSql).removeData('node-prop');
if (pnlStats)
$(pnlStats).removeData('node-prop');
if (pnlDependencies)
$(pnlDependencies).removeData('node-prop');
if (pnlDependents)
$(pnlDependents).removeData('node-prop');
if(nodeData.success === 0) {
Notify.alert(gettext('Error'),
gettext(nodeData.errormsg)
@ -1176,17 +995,11 @@ define('pgadmin.browser.node', [
return;
}.bind(panel),
closePanel = function(confirm_close_flag) {
if(!_.isUndefined(confirm_close_flag)) {
confirm_close = confirm_close_flag;
}
// Closing this panel
this.close();
}.bind(panel),
updateTreeItem = function(obj, tnode, node_info) {
let _old = data,
_new = tnode || _.clone(view.model.tnode),
info = node_info || _.clone(view.model.node_info);
_new = tnode,
info = node_info;
// Clear the cache for this node now.
setTimeout(function() {
@ -1208,25 +1021,22 @@ define('pgadmin.browser.node', [
},
}
);
closePanel(false);
this.close();
},
saveNewNode = function(obj, tnode, node_info) {
let $props = this.$container.find('.obj_properties').first(),
objview = $props.data('obj-view');
// Clear the cache for this node now.
setTimeout(function() {
obj.clear_cache.apply(obj, item);
}, 0);
try {
pgBrowser.Events.trigger(
'pgadmin:browser:tree:add', _.clone(tnode || objview.model.tnode),
_.clone(node_info || objview.model.node_info)
'pgadmin:browser:tree:add', _.clone(tnode),
_.clone(node_info)
);
} catch (e) {
console.warn(e.stack || e);
}
closePanel(false);
this.close();
}.bind(panel, that),
editInNewPanel = function() {
// Open edit in separate panel
@ -1255,30 +1065,6 @@ define('pgadmin.browser.node', [
onEdit = editInNewPanel.bind(panel);
properties();
}
if (panel.closeable() && !that.getSchema) {
panel.on(wcDocker.EVENT.CLOSING, warnBeforeChangesLost.bind(
panel,
gettext('Changes will be lost. Are you sure you want to close the dialog?'),
function() {
panel.off(wcDocker.EVENT.CLOSING);
panel.close();
}
));
let onCloseFunc = function() {
let $props = this.$container.find('.obj_properties').first(),
objview = $props && $props.data('obj-view');
if (objview) {
objview.remove({
data: true,
internal: true,
silent: true,
});
}
}.bind(panel);
panel.on(wcDocker.EVENT.CLOSED, onCloseFunc);
}
},
_find_parent_node: function(t, i, d) {
if (this.parent_type) {
@ -1368,10 +1154,6 @@ define('pgadmin.browser.node', [
return generateUrl.generate_url(pgBrowser.URL, treeInfo, actionType, self.type, nodePickFunction, itemID);
},
// Base class for Node Data Collection
Collection: pgBrowser.DataCollection,
// Base class for Node Data Model
Model: pgBrowser.DataModel,
cache: function(url, node_info, level, data) {
let cached = this.cached = this.cached || {},
hash = url,

View File

@ -49,7 +49,7 @@ _.extend(pgBrowser, {
get_preference: function(module, preference){
const self = this;
return _.findWhere(
return _.find(
self.preferences_cache, {'module': module, 'name': preference}
);
},
@ -58,8 +58,8 @@ _.extend(pgBrowser, {
get_preferences_for_module: function(module) {
let self = this;
let preferences = {};
_.each(
_.where(self.preferences_cache, {'module': module}),
_.forEach(
_.filter(self.preferences_cache, {'module': module}),
(preference) => {
preferences[preference.name] = preference.value;
}
@ -72,8 +72,8 @@ _.extend(pgBrowser, {
/* Get preference of an id, id is numeric */
get_preference_for_id : function(id) {
let self = this;
/* findWhere returns undefined if not found */
return _.findWhere(self.preferences_cache, {'id': id});
/* find returns undefined if not found */
return _.find(self.preferences_cache, {'id': id});
},
// Get and cache the preferences

View File

@ -8,7 +8,6 @@
//////////////////////////////////////////////////////////////
import $ from 'jquery';
import Backbone from 'backbone';
import axios from 'axios';
export function setPGCSRFToken(header, token) {
@ -17,28 +16,6 @@ export function setPGCSRFToken(header, token) {
throw new Error('csrf-token meta tag has not been set');
}
// Configure Backbone.sync to set CSRF-Token-header request header for
// every requests except GET.
let origBackboneSync = Backbone.sync;
Backbone.sync = function(method, model, options) {
options.beforeSend = function(xhr) {
xhr.setRequestHeader(header, token);
};
return origBackboneSync(method, model, options);
};
// Configure Backbone.get to set 'X-CSRFToken' request header for
// GET requests.
let origBackboneGet = Backbone.get;
Backbone.get = function(method, model, options) {
options.beforeSend = function(xhr) {
xhr.setRequestHeader(header, token);
};
return origBackboneGet(method, model, options);
};
// Configure jquery.ajax to set 'X-CSRFToken' request header for
// every requests.
$.ajaxSetup({

View File

@ -14,6 +14,10 @@ export default class EventBus {
});
}
on(...args) {
this.registerListener(...args);
}
deregisterListener(event, callback) {
if(callback) {
this._eventListeners = this._eventListeners.filter((e)=>{
@ -27,6 +31,10 @@ export default class EventBus {
}
}
off(...args) {
this.deregisterListener(...args);
}
fireEvent(event, ...args) {
let self = this;
Promise.resolve(0).then(()=>{
@ -43,4 +51,8 @@ export default class EventBus {
}
});
}
trigger(...args) {
this.fireEvent(...args);
}
}

View File

@ -1,101 +0,0 @@
import 'select2';
import $ from 'jquery';
import _ from 'underscore';
export default function (options) {
if(options.showOnScroll) {
let Utils = $.fn.select2.amd.require('select2/utils');
/* Define on scroll showing of dropdown items.
* This also requires ajax option of select2 to be set.
* The trick is, ajax: {} will also work even if you're actually not
* using ajax.
*/
let ScrollDataAdapter = function ($element, dropdownOptions) {
this.$element = $element;
this.options = dropdownOptions;
this._dataToConvert = dropdownOptions.get('data') || [];
};
let BaseAdapter = null;
if(options.data != null) {
BaseAdapter = $.fn.select2.amd.require('select2/data/array');
} else {
BaseAdapter = $.fn.select2.amd.require('select2/data/select');
}
Utils.Extend(ScrollDataAdapter, BaseAdapter);
ScrollDataAdapter.prototype.query = function (params, callback) {
let data = [];
let self = this;
if (!params.page) {
params.page = 1;
}
let pageSize = 20;
let $options = this.$element.children();
$options.each(function () {
let $option = $(this);
if (!$option.is('option') && !$option.is('optgroup')) {
return;
}
let option = self.item($option);
let matches = self.matches(params, option);
if (matches !== null) {
data.push(matches);
}
});
callback({
results: data.slice((params.page - 1) * pageSize, params.page * pageSize),
pagination: {
more: data.length >= params.page * pageSize,
},
});
};
if (options.minimumInputLength > 0) {
ScrollDataAdapter = Utils.Decorate(
ScrollDataAdapter,
$.fn.select2.amd.require('select2/data/minimumInputLength')
);
}
if (options.maximumInputLength > 0) {
ScrollDataAdapter = Utils.Decorate(
ScrollDataAdapter,
$.fn.select2.amd.require('select2/data/maximumInputLength')
);
}
if (options.maximumSelectionLength > 0) {
ScrollDataAdapter = Utils.Decorate(
ScrollDataAdapter,
$.fn.select2.amd.require('select2/data/maximumSelectionLength')
);
}
if (options.tags) {
ScrollDataAdapter = Utils.Decorate(ScrollDataAdapter, $.fn.select2.amd.require('select2/data/tags'));
}
if (options.tokenSeparators != null || options.tokenizer != null) {
ScrollDataAdapter = Utils.Decorate(
ScrollDataAdapter,
$.fn.select2.amd.require('select2/data/tokenizer')
);
}
options.dataAdapter = ScrollDataAdapter;
/* Setting empty ajax option will enable infinite scrolling. */
if(_.isUndefined(options.ajax)) {
options.ajax = {};
}
}
return options;
}

View File

@ -1,95 +0,0 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import Notify from '../helpers/Notifier';
define(['sources/gettext'], function (gettext) {
return {
copyTextToClipboard: function (text) {
let textArea = document.createElement('textarea');
//
// *** This styling is an extra step which is likely not required. ***
//
// Why is it here? To ensure:
// 1. the element is able to have focus and selection.
// 2. if element was to flash render it has minimal visual impact.
// 3. less flakyness with selection and copying which **might** occur if
// the textarea element is not visible.
//
// The likelihood is the element won't even render, not even a flash,
// so some of these are just precautions. However in IE the element
// is visible whilst the popup box asking the user for permission for
// the web page to copy to the clipboard.
//
// Place in top-left corner of screen regardless of scroll position.
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
// Ensure it has a small width and height. Setting to 1px / 1em
// doesn't work as this gives a negative w/h on some browsers.
textArea.style.width = '2em';
textArea.style.height = '2em';
// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = 0;
// Clean up any borders.
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
// Avoid flash of white box if rendered for any reason.
textArea.style.background = 'transparent';
document.body.appendChild(textArea);
textArea.textContent = text;
textArea.select();
let copyTextToClipboardHandler = function(e) {
/* Remove oncopy event listener from document as we add listener for
* oncopy event on each copy operation.
* Also we don't want this listener to be persistent; Otherwise it'll get
* called for each copy operation performed on any input/textarea from
* this document.
*/
document.removeEventListener('copy', copyTextToClipboardHandler);
let clipboardData = e.clipboardData || window.clipboardData;
if (clipboardData) {
clipboardData.setData('text', text);
// As there no uniform way to read data from clipboard
// storing copied data into main window object, so it is accessible from anywhere in the application
window.parent.window.clipboardData = text;
// We want our data, not data from any selection, to be written to the clipboard
e.preventDefault();
}
};
document.addEventListener('copy', copyTextToClipboardHandler);
try {
// just perform copy on empty textarea so that copy event will be
// triggered on document and then we can set clipboardData.
document.execCommand('copy');
} catch (err) {
Notify.alert(
gettext('Error'),
gettext('Oops, unable to copy to clipboard'));
}
document.body.removeChild(textArea);
},
getTextFromClipboard: function() {
return window.parent.window.clipboardData || '';
},
};
});

View File

@ -1,170 +0,0 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
define(['sources/selection/range_selection_helper', 'json-bignumber'],
function (RangeSelectionHelper, JSONBigNumber) {
return {
getUnion: function (allRanges) {
if (_.isEmpty(allRanges)) {
return [];
}
allRanges.sort(firstElementNumberComparator);
let unionedRanges = [allRanges[0]];
allRanges.forEach(function (range) {
let maxBeginningOfRange = _.last(unionedRanges);
if (isStartInsideRange(range, maxBeginningOfRange)) {
if (!isEndInsideRange(range, maxBeginningOfRange)) {
maxBeginningOfRange[1] = range[1];
}
} else {
unionedRanges.push(range);
}
});
return unionedRanges;
function firstElementNumberComparator(a, b) {
return a[0] - b[0];
}
function isStartInsideRange(range, surroundingRange) {
return range[0] <= surroundingRange[1] + 1;
}
function isEndInsideRange(range, surroundingRange) {
return range[1] <= surroundingRange[1];
}
},
mapDimensionBoundaryUnion: function (unionedDimensionBoundaries, iteratee) {
let mapResult = [];
unionedDimensionBoundaries.forEach(function (subrange) {
for (let index = subrange[0]; index <= subrange[1]; index += 1) {
mapResult.push(iteratee(index));
}
});
return mapResult;
},
mapOver2DArray: function (rowRangeBounds, colRangeBounds, processCell, rowCollector) {
let unionedRowRanges = this.getUnion(rowRangeBounds);
let unionedColRanges = this.getUnion(colRangeBounds);
return this.mapDimensionBoundaryUnion(unionedRowRanges, function (rowId) {
let rowData = this.mapDimensionBoundaryUnion(unionedColRanges, function (colId) {
return processCell(rowId, colId);
});
return rowCollector(rowData);
}.bind(this));
},
getHeaderData: function (columnDefinitions, CSVOptions) {
let headerData = [],
field_separator = CSVOptions.field_separator || '\t',
quote_char = CSVOptions.quote_char || '"';
_.each(columnDefinitions, function(col) {
if(col.display_name && col.selected) {
headerData.push(quote_char + col.display_name + quote_char);
}
});
return headerData.join(field_separator);
},
rangesToCsv: function (data, columnDefinitions, selectedRanges, CSVOptions, copyWithHeader) {
let rowRangeBounds = selectedRanges.map(function (range) {
return [range.fromRow, range.toRow];
});
let colRangeBounds = selectedRanges.map(function (range) {
return [range.fromCell, range.toCell];
});
if (!RangeSelectionHelper.isFirstColumnData(columnDefinitions)) {
colRangeBounds = this.removeFirstColumn(colRangeBounds);
}
let csvRows = this.mapOver2DArray(rowRangeBounds, colRangeBounds, this.csvCell.bind(this, data, columnDefinitions, CSVOptions), function (rowData) {
let field_separator = CSVOptions.field_separator || '\t';
return rowData.join(field_separator);
});
if (copyWithHeader) {
let headerData = '';
headerData = this.getHeaderData(columnDefinitions, CSVOptions);
return headerData + '\n' + csvRows.join('\n');
}
return csvRows.join('\n');
},
removeFirstColumn: function (colRangeBounds) {
let unionedColRanges = this.getUnion(colRangeBounds);
if(unionedColRanges.length == 0) {
return [];
}
let firstSubrangeStartsAt0 = function () {
return unionedColRanges[0][0] == 0;
};
function firstSubrangeIsJustFirstColumn() {
return unionedColRanges[0][1] == 0;
}
if (firstSubrangeStartsAt0()) {
if (firstSubrangeIsJustFirstColumn()) {
unionedColRanges.shift();
} else {
unionedColRanges[0][0] = 1;
}
}
return unionedColRanges;
},
csvCell: function (data, columnDefinitions, CSVOptions, rowId, colId) {
let val = data[rowId][columnDefinitions[colId].field],
cell_type = columnDefinitions[colId].cell || '',
quoting = CSVOptions.quoting || 'strings',
quote_char = CSVOptions.quote_char || '"';
const escape = (iStr) => {
return (quote_char == '"') ?
iStr.replace(/\"/g, '""') : iStr.replace(/\'/g, '\'\'');
};
if (quoting == 'all') {
if (val && _.isObject(val)) {
val = quote_char + JSONBigNumber.stringify(val) + quote_char;
} else if (val) {
val = quote_char + escape(val.toString()) + quote_char;
} else if (_.isNull(val) || _.isUndefined(val)) {
val = '';
}
}
else if(quoting == 'strings') {
if (val && _.isObject(val)) {
val = quote_char + JSONBigNumber.stringify(val) + quote_char;
} else if (val && cell_type != 'number' && cell_type != 'boolean') {
val = quote_char + escape(val.toString()) + quote_char;
} else if (cell_type == 'string' && _.isNull(val)){
val = null;
} else if (_.isNull(val) || _.isUndefined(val)) {
val = '';
}
}
return val;
},
};
});

View File

@ -1,106 +0,0 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
define(
[
'jquery',
'underscore',
'sources/selection/range_selection_helper',
],
function ($, _, RangeSelectionHelper) {
function disableButton(selector) {
$(selector).prop('disabled', true);
}
function enableButton(selector) {
$(selector).prop('disabled', false);
}
function getRowPrimaryKeyValuesToStage(selectedRows, primaryKeys, dataView, client_primary_key) {
return _.reduce(selectedRows, function (primaryKeyValuesToStage, dataGridRowIndex) {
let gridRow = dataView.getItem(dataGridRowIndex);
if (isRowMissingPrimaryKeys(gridRow, primaryKeys)) {
return primaryKeyValuesToStage;
}
let tempPK = gridRow[client_primary_key];
primaryKeyValuesToStage[tempPK] = getSingleRowPrimaryKeyValueToStage(primaryKeys, gridRow);
return primaryKeyValuesToStage;
}, {});
}
function isRowMissingPrimaryKeys(gridRow, primaryKeys) {
if (_.isUndefined(gridRow)) {
return true;
}
return !_.isUndefined(
_.find(primaryKeys , function (pk) {
return _.isUndefined(gridRow[pk]);
})
);
}
function getSingleRowPrimaryKeyValueToStage(primaryKeys, gridRow) {
let rowToStage = {};
if (primaryKeys && primaryKeys.length) {
_.each(_.keys(gridRow), function (columnNames) {
if (_.contains(primaryKeys, columnNames))
rowToStage[columnNames] = gridRow[columnNames];
});
}
return rowToStage;
}
function getPrimaryKeysForSelectedRows(self, selectedRows) {
let dataView = self.grid.getData();
return getRowPrimaryKeyValuesToStage(selectedRows, _.keys(self.keys), dataView, self.client_primary_key);
}
return function () {
let self = this;
function setStagedRows(rowsToStage) {
self.editor.handler.data_store.staged_rows = rowsToStage;
}
function isEditMode() {
return self.editor.handler.can_edit;
}
disableButton('#btn-delete-row');
disableButton('#btn-copy-row');
function areAllSelectionsEntireRows() {
return RangeSelectionHelper.areAllRangesCompleteRows(self.grid,
self.selection.getSelectedRanges());
}
let selectedRanges = this.selection.getSelectedRanges();
if (selectedRanges.length > 0) {
enableButton('#btn-copy-row');
}
if (areAllSelectionsEntireRows()) {
let selectedRows = RangeSelectionHelper.getIndexesOfCompleteRows(this.grid, this.selection.getSelectedRanges());
let stagedRows = getPrimaryKeysForSelectedRows(self, selectedRows);
setStagedRows(stagedRows);
if (_.isEmpty(stagedRows)) {
this.selection.setSelectedRows([]);
}
if (isEditMode() && !_.isEmpty(stagedRows)) {
enableButton('#btn-delete-row');
}
} else {
setStagedRows({});
}
};
}
);

View File

@ -66,11 +66,11 @@ _.extend(pgBrowser.browserTreeState, {
this.fetch_state.apply(this);
pgBrowser.Events.on('pgadmin:browser:tree:expand-from-previous-tree-state',
this.expand_from_previous_state, this);
this.expand_from_previous_state.bind(this));
pgBrowser.Events.on('pgadmin:browser:tree:remove-from-tree-state',
this.remove_from_cache, this);
this.remove_from_cache.bind(this));
pgBrowser.Events.on('pgadmin:browser:tree:update-tree-state',
this.update_cache, this);
this.update_cache.bind(this));
} else if (!_.isUndefined(save_tree_state_period)) {
$.ajax({
url: url_for('settings.reset_tree_state'),

View File

@ -54,9 +54,6 @@ function manageTreeEvents(event, eventName, item) {
/* Raise tree events for the nodes */
try {
node.trigger(
'browser-node.' + eventName, node, item, d
);
obj.Events.trigger(
'pgadmin-browser:tree:' + eventName, item, d, node
);

View File

@ -80,7 +80,7 @@ def make_json_response(
def make_response(response=None, status=200):
"""Create a JSON response handled by the backbone models."""
"""Create a JSON response"""
return Response(
response=json.dumps(
response, cls=DataTypeJSONEncoder, separators=(',', ':')),

View File

@ -41,8 +41,7 @@ const providePlugin = new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
_: 'underscore',
Backbone: 'backbone',
_: 'lodash',
pgAdmin: 'pgadmin',
'moment': 'moment',
'window.moment':'moment',
@ -599,7 +598,7 @@ module.exports = [{
minChunks: 2,
enforce: true,
test(module) {
return webpackShimConfig.matchModules(module, ['wcdocker', 'backbone', 'jquery', 'bootstrap', 'popper']);
return webpackShimConfig.matchModules(module, ['wcdocker', 'jquery', 'bootstrap', 'popper']);
},
},
vendor_others: {

View File

@ -16,9 +16,6 @@ let webpackShimConfig = {
'moment': {
exports: 'moment',
},
'underscore': {
exports: '_',
},
'jquery': {
'exports': '$',
},
@ -41,13 +38,6 @@ let webpackShimConfig = {
deps: ['jquery', 'bootstrap'],
'exports': '$.fn.bootstrapToggle',
},
'backbone': {
exports: 'Backbone', // Once loaded, use the global 'Backbone' as the module value.
deps: [
'underscore', // just make sure that underscore is loaded before (uses it's global value)
'jquery:$', // Provide jquery as dependency with name $
],
},
'jquery.event.drag': {
'deps': ['jquery'], 'exports': 'jQuery.fn.drag',
},
@ -75,9 +65,6 @@ let webpackShimConfig = {
'wcdocker': {
'deps': ['jquery.contextmenu'],
},
'pgadmin.browser.messages': {
'deps': ['pgadmin.browser.datamodel'],
},
},
// Map module id to file path used in 'define(['baseurl', 'misc']). It is
@ -126,9 +113,6 @@ let webpackShimConfig = {
//socket
'socketio': path.join(__dirname, './node_modules/socket.io-client/dist/socket.io.js'),
// Backbone
'backbone': path.join(__dirname, './node_modules/backbone/backbone'),
'backbone.undo': path.join(__dirname, './node_modules/backbone-undo/Backbone.Undo'),
'bootstrap.datetimepicker': path.join(__dirname, './node_modules/tempusdominus-bootstrap-4/build/js/tempusdominus-bootstrap-4.min'),
'bootstrap.toggle': path.join(__dirname, './node_modules/bootstrap4-toggle/js/bootstrap4-toggle.min'),
'select2': path.join(__dirname, './node_modules/select2/dist/js/select2.full'),
@ -138,7 +122,7 @@ let webpackShimConfig = {
'pgadmin.browser': path.join(__dirname, './pgadmin/browser/static/js/browser'),
'pgadmin.browser.bgprocessmanager': path.join(__dirname, './pgadmin/misc/bgprocess/static/js'),
'pgadmin.browser.collection': path.join(__dirname, './pgadmin/browser/static/js/collection'),
'pgadmin.browser.datamodel': path.join(__dirname, './pgadmin/browser/static/js/datamodel'),
'pgadmin.browser.events': path.join(__dirname, './pgadmin/browser/static/js/events'),
'pgadmin.browser.endpoints': '/browser/js/endpoints',
'pgadmin.browser.constants': '/browser/js/constants',
'pgadmin.browser.error': path.join(__dirname, './pgadmin/browser/static/js/error'),
@ -255,7 +239,7 @@ let webpackShimConfig = {
pgLibs: [
'pgadmin.browser.error',
'pgadmin.browser.collection',
'pgadmin.browser.datamodel', 'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin',
'pgadmin.browser.events', 'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin',
'pgadmin.browser.frame', 'pgadmin.browser',
'pgadmin.browser.node',
'pgadmin.settings', 'pgadmin.preferences', 'pgadmin.sqlfoldcode',

View File

@ -21,7 +21,7 @@ module.exports = {
plugins: [
new webpack.ProvidePlugin({
jQuery: 'jquery',
_: 'underscore',
_: 'lodash',
'window.jQuery': 'jquery',
'moment': 'moment',
'window.moment':'moment',
@ -126,7 +126,6 @@ module.exports = {
'bignumber': path.join(__dirname, './node_modules/bignumber.js/bignumber'),
'bootstrap.datetimepicker': path.join(__dirname, './node_modules/tempusdominus-bootstrap-4/build/js/tempusdominus-bootstrap-4.min'),
'bootstrap.toggle': path.join(__dirname, './node_modules/bootstrap4-toggle/js/bootstrap4-toggle.min'),
'backbone': path.join(__dirname, './node_modules/backbone/backbone'),
'react': path.join(__dirname, 'node_modules/react'),
'react-dom': path.join(__dirname, 'node_modules/react-dom'),
'socketio': path.join(__dirname, './node_modules/socket.io-client/dist/socket.io.js'),

View File

@ -3448,13 +3448,6 @@ babylon@^6.18.0:
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
backbone@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.4.0.tgz#54db4de9df7c3811c3f032f34749a4cd27f3bd12"
integrity sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ==
dependencies:
underscore ">=1.8.3"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@ -11237,7 +11230,7 @@ undeclared-identifiers@^1.1.2:
simple-concat "^1.0.0"
xtend "^4.0.1"
underscore@>=1.8.3, underscore@^1.13.1, underscore@^1.9.1:
underscore@^1.9.1:
version "1.13.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"
integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==