pgadmin4/web/pgadmin/browser/static/js/node.ui.js
Ashesh Vashi 845025db8f Some cosmetic changes.
- Loading 'pgadmin' as 'sources/pgadmin', as found under the 'sources'
  reference directory to be consistent with other files.
- Removed the 'pgadmin' reference from the base.html template.
- Renamed 'pgadmin.slickgrid.editors.js', and
  'pgadmin.slickgrid.formatters.js' as 'editors.js', and 'formatters.js'
  respectively, as they're already in the 'pgadmin/static/js/slickgrid'
  directory.
- Removed the duplicate entry of 'translations' from the webpack.shim.js
2017-08-09 16:52:12 +05:30

553 lines
18 KiB
JavaScript

define([
'sources/gettext', 'jquery', 'underscore', 'sources/pgadmin', 'backbone', 'backform',
'alertify', 'backgrid', 'select2', 'pgadmin.browser.node'
],
function(gettext, $, _, pgAdmin, Backbone, Backform, Alertify, Backgrid) {
var pgBrowser = pgAdmin.Browser;
/*
* Define the selectAll adapter for select2.
*
* Reference:
* https://github.com/select2/select2/issues/195#issuecomment-240130634
*/
$.fn.select2.amd.define('select2/selectAllAdapter', [
'select2/utils',
'select2/dropdown',
'select2/dropdown/attachBody'
], function (Utils, Dropdown, AttachBody) {
function SelectAll() { }
SelectAll.prototype.render = function (decorated) {
var self = this,
$rendered = decorated.call(this),
$selectAll = $([
'<button class="btn btn-xs btn-default" type="button"',
' style="width: 49%;margin: 0 0.5%;">',
'<i class="fa fa-check-square-o"></i>',
'<span style="padding: 0px 5px;">',
gettext("Select All"),
'</span></button>'
].join('')),
$unselectAll = $([
'<button class="btn btn-xs btn-default" type="button"',
' style="width: 49%;margin: 0 0.5%;">',
'<i class="fa fa-square-o"></i><span style="padding: 0px 5px;">',
gettext("Unselect All"),
'</span></button>'
].join('')),
$btnContainer = $(
'<div style="padding: 3px 0px; background-color: #2C76B4; margin-bottom: 3px;">'
).append($selectAll).append($unselectAll);
if (!this.$element.prop("multiple")) {
// this isn't a multi-select -> don't add the buttons!
return $rendered;
}
$rendered.find('.select2-dropdown').prepend($btnContainer);
$selectAll.on('click', function (e) {
var $results = $rendered.find('.select2-results__option[aria-selected=false]');
$results.each(function () {
self.trigger('select', {
data: $(this).data('data')
});
});
self.trigger('close');
});
$unselectAll.on('click', function (e) {
var $results = $rendered.find('.select2-results__option[aria-selected=true]');
$results.each(function () {
self.trigger('unselect', {
data: $(this).data('data')
});
});
self.trigger('close');
});
return $rendered;
};
return Utils.Decorate(
Utils.Decorate(
Dropdown,
AttachBody
),
SelectAll
);
});
/*
* NodeAjaxOptionsControl
* This control will fetch the options required to render the select
* control, from the url specific to the pgAdmin.Browser node object.
*
* In order to use this properly, schema require to set the 'url' property,
* which exposes the data for this node.
*
* In case the url is not providing the data in proper format, we can
* specify the 'transform' function too, which will convert the fetched
* data to proper 'label', 'value' format.
*/
var NodeAjaxOptionsControl = Backform.NodeAjaxOptionsControl =
Backform.Select2Control.extend({
defaults: _.extend(Backform.Select2Control.prototype.defaults, {
url: undefined,
transform: undefined,
url_with_id: false,
select2: {
allowClear: true,
placeholder: 'Select from the list',
width: 'style'
}
}),
initialize: function() {
/*
* Initialization from the original control.
*/
Backform.Select2Control.prototype.initialize.apply(this, arguments);
/*
* We're about to fetch the options required for this control.
*/
var self = this,
url = self.field.get('url') || self.defaults.url,
m = self.model.top || self.model;
// Hmm - we found the url option.
// That means - we needs to fetch the options from that node.
if (url) {
var node = this.field.get('schema_node'),
node_info = this.field.get('node_info'),
with_id = this.field.get('url_with_id') || false,
full_url = node.generate_url.apply(
node, [
null, url, this.field.get('node_data'), with_id, node_info
]),
cache_level,
cache_node = this.field.get('cache_node');
cache_node = (cache_node && pgAdmin.Browser.Nodes[cache_node]) || node;
if (this.field.has('cache_level')) {
cache_level = this.field.get('cache_level');
} else {
cache_level = cache_node.cache_level(node_info, with_id);
}
/*
* We needs to check, if we have already cached data for this url.
* If yes - use that, and do not bother about fetching it again,
* and use it.
*/
var data = cache_node.cache(node.type + '#' + url, node_info, cache_level);
if (this.field.get('version_compatible') &&
(_.isUndefined(data) || _.isNull(data))) {
m.trigger('pgadmin:view:fetching', m, self.field);
$.ajax({
async: false,
url: full_url,
success: function(res) {
/*
* We will cache this data for short period of time for avoiding
* same calls.
*/
data = cache_node.cache(node.type + '#' + url, node_info, cache_level, res.data);
},
error: function() {
m.trigger('pgadmin:view:fetch:error', m, self.field);
}
});
m.trigger('pgadmin:view:fetched', m, self.field);
}
// To fetch only options from cache, we do not need time from 'at'
// attribute but only options.
//
// It is feasible that the data may not have been fetched.
data = (data && data.data) || [];
/*
* Transform the data
*/
var transform = this.field.get('transform') || self.defaults.transform;
if (transform && _.isFunction(transform)) {
// We will transform the data later, when rendering.
// It will allow us to generate different data based on the
// dependencies.
self.field.set('options', transform.bind(self, data));
} else {
self.field.set('options', data);
}
}
}
});
var formatNode = function(opt) {
if (!opt.id) {
return opt.text;
}
var optimage = $(opt.element).data('image');
if(!optimage){
return opt.text;
} else {
return $('<span></span>').append(
$('<span></span>', {class: "wcTabIcon " + optimage})
).append(
$('<span></span>').text(opt.text)
);
}
};
var NodeListByIdControl = Backform.NodeListByIdControl = NodeAjaxOptionsControl.extend({
controlClassName: 'pgadmin-node-select form-control',
defaults: _.extend({}, NodeAjaxOptionsControl.prototype.defaults, {
url: 'nodes',
filter: undefined,
transform: function(rows) {
var self = this,
node = self.field.get('schema_node'),
res = [],
filter = self.field.get('filter') || function() { return true; };
filter = filter.bind(self);
_.each(rows, function(r) {
if (filter(r)) {
var l = (_.isFunction(node['node_label']) ?
(node['node_label']).apply(node, [r, self.model, self]) :
r.label),
image= (_.isFunction(node['node_image']) ?
(node['node_image']).apply(
node, [r, self.model, self]
) :
(node['node_image'] || ('icon-' + node.type)));
res.push({
'value': r._id,
'image': image,
'label': l
});
}
});
return res;
},
select2: {
allowClear: true,
placeholder: 'Select from the list',
width: 'style',
templateResult: formatNode,
templateSelection: formatNode
}
})
});
var NodeListByNameControl = Backform.NodeListByNameControl = NodeListByIdControl.extend({
defaults: _.extend({}, NodeListByIdControl.prototype.defaults, {
transform: function(rows) {
var self = this,
node = self.field.get('schema_node'),
res = [],
filter = self.field.get('filter') || function() { return true; };
filter = filter.bind(self);
_.each(rows, function(r) {
if (filter(r)) {
var l = (_.isFunction(node['node_label']) ?
(node['node_label']).apply(node, [r, self.model, self]) :
r.label),
image = (_.isFunction(node['node_image']) ?
(node['node_image']).apply(
node, [r, self.model, self]
) :
(node['node_image'] || ('icon-' + node.type)));
res.push({
'value': r.label,
'image': image,
'label': l
});
}
});
return res;
}
})
});
/*
* Global function to make visible particular dom element in it's parent
* with given class.
*/
$.fn.pgMakeVisible = function( cls ) {
return this.each(function() {
if (!this || !$(this.length))
return;
var top, p = $(this), hasScrollbar = function(j) {
if (j && j.length > 0) {
return j.get(0).scrollHeight > j.height();
}
return false;
};
// check if p is not empty
while(p && p.length > 0) {
top = p.get(0).offsetTop + p.height();
p = p.parent();
if (hasScrollbar(p)) {
p.scrollTop(top);
}
if (p.hasClass(cls)) //'backform-tab'
return;
}
});
};
/*
* NodeAjaxOptionsCell
* This cell will fetch the options required to render the select
* cell, from the url specific to the pgAdmin.Browser node object.
*
* In order to use this properly, schema require to set the 'url' property,
* which exposes the data for this node.
*
* In case the url is not providing the data in proper format, we can
* specify the 'transform' function too, which will convert the fetched
* data to proper 'label', 'value' format.
*/
var NodeAjaxOptionsCell = Backgrid.Extension.NodeAjaxOptionsCell = Backgrid.Extension.Select2Cell.extend({
defaults: _.extend({}, Backgrid.Extension.Select2Cell.prototype.defaults, {
url: undefined,
transform: undefined,
url_with_id: false,
select2: {
allowClear: true,
placeholder: 'Select from the list',
width: 'style'
},
opt: {
label: null,
value: null,
image: null,
selected: false
}
}),
template: _.template(
'<option <% if (image) { %> data-image=<%= image %> <% } %> value="<%- value %>" <%= selected ? \'selected="selected"\' : "" %>><%- label %></option>'
),
initialize: function () {
Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments);
var url = this.column.get('url') || this.defaults.url,
options_cached = this.column.get('options_cached');
// Hmm - we found the url option.
// That means - we needs to fetch the options from that node.
if (url && !options_cached) {
var self = this,
m = this.model, column = this.column,
eventHandler = m.top || m,
node = column.get('schema_node'),
node_info = column.get('node_info'),
with_id = column.get('url_with_id') || false,
full_url = node.generate_url.apply(
node, [
null, url, column.get('node_data'), with_id, node_info
]),
cache_level,
cache_node = column.get('cache_node');
cache_node = (cache_node && pgAdmin.Browser.Nodes['cache_node']) || node;
if (column.has('cache_level')) {
cache_level = column.get('cache_level');
} else {
cache_level = cache_node.cache_level(node_info, with_id);
}
/*
* We needs to check, if we have already cached data for this url.
* If yes - use that, and do not bother about fetching it again,
* and use it.
*/
var data = cache_node.cache(node.type + '#' + url, node_info, cache_level);
if (column.get('version_compatible') &&
(_.isUndefined(data) || _.isNull(data))) {
eventHandler.trigger('pgadmin:view:fetching', m, column);
$.ajax({
async: false,
url: full_url,
success: function(res) {
/*
* We will cache this data for short period of time for avoiding
* same calls.
*/
data = cache_node.cache(node.type + '#' + url, node_info, cache_level, res.data);
},
error: function() {
eventHandler.trigger('pgadmin:view:fetch:error', m, column);
}
});
eventHandler.trigger('pgadmin:view:fetched', m, column);
}
// To fetch only options from cache, we do not need time from 'at'
// attribute but only options.
//
// It is feasible that the data may not have been fetched.
data = (data && data.data) || [];
/*
* Transform the data
*/
var transform = column.get('transform') || self.defaults.transform;
if (transform && _.isFunction(transform)) {
// We will transform the data later, when rendering.
// It will allow us to generate different data based on the
// dependencies.
column.set('options', transform.bind(column, data));
} else {
column.set('options', data);
}
column.set('options_cached', true);
}
}
});
var NodeListByIdCell = Backgrid.Extension.NodeListByIdCell = NodeAjaxOptionsCell.extend({
controlClassName: 'pgadmin-node-select backgrid-cell',
defaults: _.extend({}, NodeAjaxOptionsCell.prototype.defaults, {
url: 'nodes',
filter: undefined,
transform: function(rows, control) {
var self = control || this,
node = self.column.get('schema_node'),
res = [],
filter = self.column.get('filter') || function() { return true; };
filter = filter.bind(self);
_.each(rows, function(r) {
if (filter(r)) {
var l = (_.isFunction(node['node_label']) ?
(node['node_label']).apply(node, [r, self.model, self]) :
r.label),
image= (_.isFunction(node['node_image']) ?
(node['node_image']).apply(
node, [r, self.model, self]
) :
(node['node_image'] || ('icon-' + node.type)));
res.push({
'value': r._id,
'image': image,
'label': l
});
}
});
return res;
},
select2: {
placeholder: 'Select from the list',
width: 'style',
templateResult: formatNode,
templateSelection: formatNode
}
})
});
var NodeListByNameCell = Backgrid.Extension.NodeListByNameCell = NodeAjaxOptionsCell.extend({
controlClassName: 'pgadmin-node-select backgrid-cell',
defaults: _.extend({}, NodeAjaxOptionsCell.prototype.defaults, {
url: 'nodes',
filter: undefined,
transform: function(rows, control) {
var self = control || this,
node = self.column.get('schema_node'),
res = [],
filter = self.column.get('filter') || function() { return true; };
filter = filter.bind(self);
_.each(rows, function(r) {
if (filter(r)) {
var l = (_.isFunction(node['node_label']) ?
(node['node_label']).apply(node, [r, self.model, self]) :
r.label),
image= (_.isFunction(node['node_image']) ?
(node['node_image']).apply(
node, [r, self.model, self]
) :
(node['node_image'] || ('icon-' + node.type)));
res.push({
'value': r.label,
'image': image,
'label': l
});
}
});
return res;
},
select2: {
placeholder: 'Select from the list',
width: 'style',
templateResult: formatNode,
templateSelection: formatNode
}
})
});
// Extend the browser's node model class to create a option/value pair
var MultiSelectAjaxCell = Backgrid.Extension.MultiSelectAjaxCell = Backgrid.Extension.NodeAjaxOptionsCell.extend({
defaults: _.extend({}, NodeAjaxOptionsCell.prototype.defaults, {
transform: undefined,
url_with_id: false,
select2: {
allowClear: true,
placeholder: 'Select from the list',
width: 'style',
multiple: true
},
opt: {
label: null,
value: null,
image: null,
selected: false
}
}),
getValueFromDOM: function() {
var res = [];
this.$el.find("select").find(':selected').each(function() {
res.push($(this).attr('value'));
});
return res;
},
});
/*
* Control to select multiple columns.
*/
var MultiSelectAjaxControl = Backform.MultiSelectAjaxControl = NodeAjaxOptionsControl.extend({
defaults: _.extend({}, NodeAjaxOptionsControl.prototype.defaults, {
select2: {
multiple: true,
allowClear: true,
width: 'style'
}
})
});
return Backform;
});