define('misc.depends', [ 'sources/gettext', 'underscore', 'underscore.string', 'jquery', 'backbone', 'pgadmin.browser', 'pgadmin.alertifyjs', 'pgadmin.backgrid', ], function(gettext, _, S, $, Backbone, pgBrowser, Alertify, Backgrid) { if (pgBrowser.ShowNodeDepends) return pgBrowser.ShowNodeDepends; var wcDocker = window.wcDocker; pgBrowser.ShowNodeDepends = pgBrowser.ShowNodeDepends || {}; _.extend(pgBrowser.ShowNodeDepends, { init: function() { if (this.initialized) { return; } this.initialized = true; /* Parameter is used to set the proper label of the * backgrid header cell. */ _.bindAll(this, 'showDependents', 'dependentsPanelVisibilityChanged', 'showDependencies', 'dependenciesPanelVisibilityChanged', '__updateCollection' ); // We will listened to the visibility change of the Dependencies and Dependents panel pgBrowser.Events.on('pgadmin-browser:panel-dependencies:' + wcDocker.EVENT.VISIBILITY_CHANGED, this.dependenciesPanelVisibilityChanged); pgBrowser.Events.on('pgadmin-browser:panel-dependents:' + wcDocker.EVENT.VISIBILITY_CHANGED, this.dependentsPanelVisibilityChanged); // Defining Backbone Model for Dependencies and Dependents. var Model = Backbone.Model.extend({ defaults: { icon: 'icon-unknown', type: undefined, name: undefined, /* field contains 'Database Name' for 'Tablespace and Role node', * for other node it contains 'Restriction'. */ field: undefined, }, // This function is used to fetch/set the icon for the type(Function, Role, Database, ....) parse: function(res) { var node = pgBrowser.Nodes[res.type]; res.icon = node ? (_.isFunction(node['node_image']) ? (node['node_image']).apply(node, [null, null]) : (node['node_image'] || ('icon-' + res.type))) : ('icon-' + res.type); res.type = S.titleize(res.type.replace(/_/g, ' '), true); return res; }, }); // Defining Backbone Collection for Dependents. this.dependentCollection = new(Backbone.Collection.extend({ model: Model, }))(null); // Defining Backbone Collection for Dependencies. this.dependenciesCollection = new(Backbone.Collection.extend({ model: Model, }))(null); var self = this; /* Function is used to create and render backgrid with * empty collection. We just want to add backgrid into the * panel only once. */ var appendGridToPanel = function(collection, panel, is_dependent) { var $container = panel[0].layout().scene().find('.pg-panel-content'), $gridContainer = $container.find('.pg-panel-depends-container'), grid = new Backgrid.Grid({ columns: [{ name: 'type', label: gettext('Type'), // Extend it to render the icon as per the type. cell: Backgrid.Cell.extend({ render: function() { Backgrid.Cell.prototype.render.apply(this, arguments); this.$el.prepend($('', { class: 'wcTabIcon ' + this.model.get('icon'), })); return this; }, }), editable: false, }, { name: 'name', label: gettext('Name'), cell: 'string', editable: false, }, { name: 'field', label: '', // label kept blank, it will change dynamically cell: 'string', editable: false, }, ], collection: collection, className: 'backgrid presentation table backgrid-striped table-bordered table-hover', }); // Condition is used to save grid object to change the label of the header. if (is_dependent) self.dependentGrid = grid; else self.dependenciesGrid = grid; $gridContainer.append(grid.render().el); return true; }; // We will listened to the visibility change of the Dependencies and Dependents panel pgBrowser.Events.on('pgadmin-browser:panel-dependencies:' + wcDocker.EVENT.VISIBILITY_CHANGED, this.dependenciesPanelVisibilityChanged); pgBrowser.Events.on('pgadmin-browser:panel-dependents:' + wcDocker.EVENT.VISIBILITY_CHANGED, this.dependentsPanelVisibilityChanged); pgBrowser.Events.on( 'pgadmin:browser:node:updated', function() { if (this.dependenciesPanels && this.dependenciesPanels.length) { $(this.dependenciesPanels[0]).data('node-prop', ''); this.dependenciesPanelVisibilityChanged(this.dependenciesPanels[0]); } if (this.dependentsPanels && this.dependentsPanels.length) { $(this.dependentsPanels[0]).data('node-prop', ''); this.dependentsPanelVisibilityChanged(this.dependentsPanels[0]); } }, this ); // We will render the grid objects in the panel after some time, because - // it is possible, it is not yet available. // Find the panels to render the grid. var dependenciesPanels = this.dependenciesPanels = pgBrowser.docker.findPanels('dependencies'); var dependentsPanels = this.dependentsPanels = pgBrowser.docker.findPanels('dependents'); if (dependenciesPanels.length == 0) { pgBrowser.Events.on( 'pgadmin-browser:panel-dependencies:' + wcDocker.EVENT.INIT, function() { this.dependenciesPanels = pgBrowser.docker.findPanels('dependencies'); appendGridToPanel(this.dependenciesCollection, this.dependenciesPanels, false); // If Dependencies panel exists and is focused then we need to listen the browser tree selection events. if ((dependenciesPanels[0].isVisible()) || dependenciesPanels.length != 1) { pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependencies); } }.bind(this) ); } else { appendGridToPanel(this.dependenciesCollection, this.dependenciesPanels, false); // If Dependencies panel exists and is focused then we need to listen the browser tree selection events. if ((dependenciesPanels[0].isVisible()) || dependenciesPanels.length != 1) { pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependencies); } } if (dependentsPanels.length == 0) { pgBrowser.Events.on( 'pgadmin-browser:panel-dependents:' + wcDocker.EVENT.INIT, function() { this.dependentsPanels = pgBrowser.docker.findPanels('dependents'); appendGridToPanel(this.dependentCollection, this.dependentsPanels, true); // If Dependents panel exists and is focused then we need to listen the browser tree selection events. if ((dependentsPanels[0].isVisible()) || dependentsPanels.length != 1) { pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependents); } }.bind(this) ); } else { appendGridToPanel(this.dependentCollection, this.dependentsPanels, true); // If Dependents panel exists and is focused then we need to listen the browser tree selection events. if ((dependentsPanels[0].isVisible()) || dependentsPanels.length != 1) { pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependents); } } }, // Fetch the actual data and update the collection __updateCollection: function(collection, panel, url, messages, node, item, type) { var msg = messages[0], $container = panel[0].layout().scene().find('.pg-panel-content'), $msgContainer = $container.find('.pg-panel-depends-message'), $gridContainer = $container.find('.pg-panel-depends-container'), treeHierarchy = node.getTreeNodeHierarchy(item), n_type = type; // Avoid unnecessary reloads if (_.isEqual($(panel[0]).data('node-prop'), treeHierarchy)) { return; } // Cache the current IDs for next time $(panel[0]).data('node-prop', treeHierarchy); // Hide the grid container and show the default message container if (!$gridContainer.hasClass('hidden')) $gridContainer.addClass('hidden'); $msgContainer.removeClass('hidden'); if (node) { msg = messages[1]; /* We fetch the Dependencies and Dependents tab only for * those node who set the parameter hasDepends to true. */ if (node.hasDepends) { /* Set the message because ajax request may take time to * fetch the information from the server. */ msg = messages[2]; $msgContainer.text(msg); /* Updating the label for the 'field' type of the backbone model. * Label should be "Database" if the node type is tablespace or role * and dependent tab is selected. For other nodes and dependencies tab * it should be 'Restriction'. */ if (this.dependent && (node.type == 'tablespace' || node.type == 'role')) this.dependentGrid.columns.models[2].set({ 'label': gettext('Database'), }); else { this.dependenciesGrid.columns.models[2].set({ 'label': gettext('Restriction'), }); this.dependentGrid.columns.models[2].set({ 'label': gettext('Restriction'), }); } // Hide message container and show grid container. $msgContainer.addClass('hidden'); $gridContainer.removeClass('hidden'); var timer = setTimeout(function() { // notify user if request is taking longer than 1 second $msgContainer.text(gettext('Retrieving data from the server...')); $msgContainer.removeClass('hidden'); if ($gridContainer) { $gridContainer.addClass('hidden'); } }, 1000); // Set the url, fetch the data and update the collection collection.url = url; collection.fetch({ reset: true, success: function() { clearTimeout(timer); $gridContainer.removeClass('hidden'); if (!$msgContainer.hasClass('hidden')) { $msgContainer.addClass('hidden'); } }, error: function(coll, xhr, error, message) { var _label = treeHierarchy[n_type].label; pgBrowser.Events.trigger( 'pgadmin:node:retrieval:error', 'depends', xhr, error, message ); if (!Alertify.pgHandleItemError(xhr, error, message, { item: item, info: treeHierarchy, })) { Alertify.pgNotifier( error, xhr, S( gettext('Error retrieving the information - %s') ).sprintf(message || _label).value(), function() { console.warn(arguments); } ); } // show failed message. $msgContainer.text(gettext('Failed to retrieve data from the server.')); }, }); } } if (msg != '') { $msgContainer.text(msg); } }, showDependents: function(item, data, node) { /** * We can't start fetching the Dependents immediately, it is possible the user * is just using the keyboard to select the node, and just traversing * through. We will wait for some time before fetching the Dependents **/ var self = this; if (!node) { return; } self.dependent = true; if (self.timeout) { clearTimeout(self.timeout); } self.timeout = setTimeout( self.__updateCollection( self.dependentCollection, self.dependentsPanels, node.generate_url(item, 'dependent', data, true), [ gettext('No object selected.'), gettext('No dependent information is available for the current object.'), gettext('Fetching dependent information from the server...'), ], node, item, data._type ), 400 ); }, dependentsPanelVisibilityChanged: function(panel) { if (panel.isVisible()) { var t = pgBrowser.tree, i = t.selected(), d = i && t.itemData(i), n = i && d && pgBrowser.Nodes[d._type]; pgBrowser.ShowNodeDepends.showDependents.apply(pgBrowser.ShowNodeDepends, [i, d, n]); // We will start listening the tree selection event. pgBrowser.Events.on('pgadmin-browser:tree:selected', pgBrowser.ShowNodeDepends.showDependents); } else { // We don't need to listen the tree item selection event. pgBrowser.Events.off('pgadmin-browser:tree:selected', pgBrowser.ShowNodeDepends.showDependents); } }, showDependencies: function(item, data, node) { /** * We can't start fetching the Dependencies immediately, it is possible the user * is just using the keyboard to select the node, and just traversing * through. We will wait for some time before fetching the Dependencies **/ var self = this; if (!node) { return; } self.dependent = false; if (self.timeout) { clearTimeout(self.timeout); } self.timeout = setTimeout( self.__updateCollection( self.dependenciesCollection, self.dependenciesPanels, node.generate_url(item, 'dependency', data, true), [gettext('Please select an object in the tree view.'), gettext('No dependency information is available for the current object.'), gettext('Fetching dependency information from the server...'), ], node, item, data._type ), 400 ); }, dependenciesPanelVisibilityChanged: function(panel) { if (panel.isVisible()) { var t = pgBrowser.tree, i = t.selected(), d = i && t.itemData(i), n = i && d && pgBrowser.Nodes[d._type]; pgBrowser.ShowNodeDepends.showDependencies.apply(pgBrowser.ShowNodeDepends, [i, d, n]); // We will start listening the tree selection event. pgBrowser.Events.on('pgadmin-browser:tree:selected', pgBrowser.ShowNodeDepends.showDependencies); } else { // We don't need to listen the tree item selection event. pgBrowser.Events.off('pgadmin-browser:tree:selected', pgBrowser.ShowNodeDepends.showDependencies); } }, }); return pgBrowser.ShowNodeDepends; });